* [PATCH 0/5] spi: uniphier: Introduce DMA transfer mode @ 2019-12-24 0:58 Kunihiko Hayashi 2019-12-24 0:58 ` [PATCH 1/5] spi: uniphier: Fix FIFO threshold Kunihiko Hayashi ` (4 more replies) 0 siblings, 5 replies; 12+ messages in thread From: Kunihiko Hayashi @ 2019-12-24 0:58 UTC (permalink / raw) To: Mark Brown, linux-spi Cc: linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar, Kunihiko Hayashi This series introduces DMA transfer mode into UniPhier SPI driver. This only supports UniPhier XDMAC implemented in the SoC. The DMA transfer mode uses FIFO threshold as a trigger to start DMA transfer, however, the FIFO is limited to one word width due to alignment restiction of the DMA controller. Kunihiko Hayashi (5): spi: uniphier: Fix FIFO threshold spi: uniphier: Change argument of irq functions to private structure spi: uniphier: Add handle_err callback function spi: uniphier: Add SPI_LOOP to the capabilities spi: uniphier: Add DMA transfer mode support drivers/spi/spi-uniphier.c | 261 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 241 insertions(+), 20 deletions(-) -- 2.7.4 ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/5] spi: uniphier: Fix FIFO threshold 2019-12-24 0:58 [PATCH 0/5] spi: uniphier: Introduce DMA transfer mode Kunihiko Hayashi @ 2019-12-24 0:58 ` Kunihiko Hayashi 2019-12-25 0:09 ` Applied "spi: uniphier: Fix FIFO threshold" to the spi tree Mark Brown 2019-12-24 0:58 ` [PATCH 2/5] spi: uniphier: Change argument of irq functions to private structure Kunihiko Hayashi ` (3 subsequent siblings) 4 siblings, 1 reply; 12+ messages in thread From: Kunihiko Hayashi @ 2019-12-24 0:58 UTC (permalink / raw) To: Mark Brown, linux-spi Cc: linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar, Kunihiko Hayashi Rx threshold means the value to inform the receiver when the number of words in Rx FIFO is equal to or more than the value. Similarly, Tx threshold means the value to inform the sender when the number of words in Tx FIFO is equal to or less than the value. The controller triggers the driver to start the transfer. In case of Rx, the driver wants to detect that the specified number of words N are in Rx FIFO, so the value of Rx threshold should be N. In case of Tx, the driver wants to detect that the same number of spaces as Rx are in Tx FIFO, so the value of Tx threshold should be (FIFO size - N). For example, in order for the driver to receive at least 3 words from Rx FIFO, set 3 to Rx threshold. +-+-+-+-+-+-+-+-+ | | | | | |*|*|*| +-+-+-+-+-+-+-+-+ In order for the driver to send at least 3 words to Tx FIFO, because it needs at least 3 spaces, set 8(FIFO size) - 3 = 5 to Tx threshold. +-+-+-+-+-+-+-+-+ |*|*|*|*|*| | | | +-+-+-+-+-+-+-+-+ This adds new function uniphier_spi_set_fifo_threshold() to set threshold value to the register. And more, FIFO counts by 'words', so this renames 'fill_bytes' with 'fill_words', and fixes the calculation using bytes_per_words. Fixes: 37ffab817098 ("spi: uniphier: introduce polling mode") Cc: Keiji Hayashibara <hayashibara.keiji@socionext.com> Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> --- drivers/spi/spi-uniphier.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 47cde186..ce9b301 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -290,25 +290,32 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv) } } -static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) +static void uniphier_spi_set_fifo_threshold(struct uniphier_spi_priv *priv, + unsigned int threshold) { - unsigned int fifo_threshold, fill_bytes; u32 val; - fifo_threshold = DIV_ROUND_UP(priv->rx_bytes, - bytes_per_word(priv->bits_per_word)); - fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); - - fill_bytes = fifo_threshold - (priv->rx_bytes - priv->tx_bytes); - - /* set fifo threshold */ val = readl(priv->base + SSI_FC); val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); - val |= FIELD_PREP(SSI_FC_TXFTH_MASK, fifo_threshold); - val |= FIELD_PREP(SSI_FC_RXFTH_MASK, fifo_threshold); + val |= FIELD_PREP(SSI_FC_TXFTH_MASK, SSI_FIFO_DEPTH - threshold); + val |= FIELD_PREP(SSI_FC_RXFTH_MASK, threshold); writel(val, priv->base + SSI_FC); +} + +static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) +{ + unsigned int fifo_threshold, fill_words; + unsigned int bpw = bytes_per_word(priv->bits_per_word); + + fifo_threshold = DIV_ROUND_UP(priv->rx_bytes, bpw); + fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); + + uniphier_spi_set_fifo_threshold(priv, fifo_threshold); + + fill_words = fifo_threshold - + DIV_ROUND_UP(priv->rx_bytes - priv->tx_bytes, bpw); - while (fill_bytes--) + while (fill_words--) uniphier_spi_send(priv); } -- 2.7.4 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Applied "spi: uniphier: Fix FIFO threshold" to the spi tree 2019-12-24 0:58 ` [PATCH 1/5] spi: uniphier: Fix FIFO threshold Kunihiko Hayashi @ 2019-12-25 0:09 ` Mark Brown 0 siblings, 0 replies; 12+ messages in thread From: Mark Brown @ 2019-12-25 0:09 UTC (permalink / raw) To: Kunihiko Hayashi Cc: Jassi Brar, Keiji Hayashibara, linux-arm-kernel, linux-kernel, linux-spi, Mark Brown, Masahiro Yamada, Masami Hiramatsu The patch spi: uniphier: Fix FIFO threshold has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-5.5 All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From 9cd34efbd3012171c102910ce17ee632a3cccb44 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Date: Tue, 24 Dec 2019 09:58:23 +0900 Subject: [PATCH] spi: uniphier: Fix FIFO threshold Rx threshold means the value to inform the receiver when the number of words in Rx FIFO is equal to or more than the value. Similarly, Tx threshold means the value to inform the sender when the number of words in Tx FIFO is equal to or less than the value. The controller triggers the driver to start the transfer. In case of Rx, the driver wants to detect that the specified number of words N are in Rx FIFO, so the value of Rx threshold should be N. In case of Tx, the driver wants to detect that the same number of spaces as Rx are in Tx FIFO, so the value of Tx threshold should be (FIFO size - N). For example, in order for the driver to receive at least 3 words from Rx FIFO, set 3 to Rx threshold. +-+-+-+-+-+-+-+-+ | | | | | |*|*|*| +-+-+-+-+-+-+-+-+ In order for the driver to send at least 3 words to Tx FIFO, because it needs at least 3 spaces, set 8(FIFO size) - 3 = 5 to Tx threshold. +-+-+-+-+-+-+-+-+ |*|*|*|*|*| | | | +-+-+-+-+-+-+-+-+ This adds new function uniphier_spi_set_fifo_threshold() to set threshold value to the register. And more, FIFO counts by 'words', so this renames 'fill_bytes' with 'fill_words', and fixes the calculation using bytes_per_words. Fixes: 37ffab817098 ("spi: uniphier: introduce polling mode") Cc: Keiji Hayashibara <hayashibara.keiji@socionext.com> Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Link: https://lore.kernel.org/r/1577149107-30670-2-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-uniphier.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 47cde1864630..ce9b30112e26 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -290,25 +290,32 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv) } } -static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) +static void uniphier_spi_set_fifo_threshold(struct uniphier_spi_priv *priv, + unsigned int threshold) { - unsigned int fifo_threshold, fill_bytes; u32 val; - fifo_threshold = DIV_ROUND_UP(priv->rx_bytes, - bytes_per_word(priv->bits_per_word)); - fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); - - fill_bytes = fifo_threshold - (priv->rx_bytes - priv->tx_bytes); - - /* set fifo threshold */ val = readl(priv->base + SSI_FC); val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); - val |= FIELD_PREP(SSI_FC_TXFTH_MASK, fifo_threshold); - val |= FIELD_PREP(SSI_FC_RXFTH_MASK, fifo_threshold); + val |= FIELD_PREP(SSI_FC_TXFTH_MASK, SSI_FIFO_DEPTH - threshold); + val |= FIELD_PREP(SSI_FC_RXFTH_MASK, threshold); writel(val, priv->base + SSI_FC); +} + +static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) +{ + unsigned int fifo_threshold, fill_words; + unsigned int bpw = bytes_per_word(priv->bits_per_word); + + fifo_threshold = DIV_ROUND_UP(priv->rx_bytes, bpw); + fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); + + uniphier_spi_set_fifo_threshold(priv, fifo_threshold); + + fill_words = fifo_threshold - + DIV_ROUND_UP(priv->rx_bytes - priv->tx_bytes, bpw); - while (fill_bytes--) + while (fill_words--) uniphier_spi_send(priv); } -- 2.20.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/5] spi: uniphier: Change argument of irq functions to private structure 2019-12-24 0:58 [PATCH 0/5] spi: uniphier: Introduce DMA transfer mode Kunihiko Hayashi 2019-12-24 0:58 ` [PATCH 1/5] spi: uniphier: Fix FIFO threshold Kunihiko Hayashi @ 2019-12-24 0:58 ` Kunihiko Hayashi 2019-12-25 0:09 ` Applied "spi: uniphier: Change argument of irq functions to private structure" to the spi tree Mark Brown 2019-12-24 0:58 ` [PATCH 3/5] spi: uniphier: Add handle_err callback function Kunihiko Hayashi ` (2 subsequent siblings) 4 siblings, 1 reply; 12+ messages in thread From: Kunihiko Hayashi @ 2019-12-24 0:58 UTC (permalink / raw) To: Mark Brown, linux-spi Cc: linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar, Kunihiko Hayashi This changes each argument of functions uniphier_irq_{enable,disable}() to uniphier_spi_priv because these functions are used not only for spi_device but also for the entire controller. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> --- drivers/spi/spi-uniphier.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index ce9b301..3859649 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -93,9 +93,9 @@ static inline unsigned int bytes_per_word(unsigned int bits) return bits <= 8 ? 1 : (bits <= 16 ? 2 : 4); } -static inline void uniphier_spi_irq_enable(struct spi_device *spi, u32 mask) +static inline void uniphier_spi_irq_enable(struct uniphier_spi_priv *priv, + u32 mask) { - struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); u32 val; val = readl(priv->base + SSI_IE); @@ -103,9 +103,9 @@ static inline void uniphier_spi_irq_enable(struct spi_device *spi, u32 mask) writel(val, priv->base + SSI_IE); } -static inline void uniphier_spi_irq_disable(struct spi_device *spi, u32 mask) +static inline void uniphier_spi_irq_disable(struct uniphier_spi_priv *priv, + u32 mask) { - struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); u32 val; val = readl(priv->base + SSI_IE); @@ -346,12 +346,12 @@ static int uniphier_spi_transfer_one_irq(struct spi_master *master, uniphier_spi_fill_tx_fifo(priv); - uniphier_spi_irq_enable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + uniphier_spi_irq_enable(priv, SSI_IE_RCIE | SSI_IE_RORIE); time_left = wait_for_completion_timeout(&priv->xfer_done, msecs_to_jiffies(SSI_TIMEOUT_MS)); - uniphier_spi_irq_disable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + uniphier_spi_irq_disable(priv, SSI_IE_RCIE | SSI_IE_RORIE); if (!time_left) { dev_err(dev, "transfer timeout.\n"); -- 2.7.4 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Applied "spi: uniphier: Change argument of irq functions to private structure" to the spi tree 2019-12-24 0:58 ` [PATCH 2/5] spi: uniphier: Change argument of irq functions to private structure Kunihiko Hayashi @ 2019-12-25 0:09 ` Mark Brown 0 siblings, 0 replies; 12+ messages in thread From: Mark Brown @ 2019-12-25 0:09 UTC (permalink / raw) To: Kunihiko Hayashi Cc: Jassi Brar, Keiji Hayashibara, linux-arm-kernel, linux-kernel, linux-spi, Mark Brown, Masahiro Yamada, Masami Hiramatsu The patch spi: uniphier: Change argument of irq functions to private structure has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-5.6 All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From 9859db51387df8a7e8564a211158ff8bf263b0a8 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Date: Tue, 24 Dec 2019 09:58:24 +0900 Subject: [PATCH] spi: uniphier: Change argument of irq functions to private structure This changes each argument of functions uniphier_irq_{enable,disable}() to uniphier_spi_priv because these functions are used not only for spi_device but also for the entire controller. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Link: https://lore.kernel.org/r/1577149107-30670-3-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-uniphier.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 47cde1864630..a44a1a5fb7b0 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -93,9 +93,9 @@ static inline unsigned int bytes_per_word(unsigned int bits) return bits <= 8 ? 1 : (bits <= 16 ? 2 : 4); } -static inline void uniphier_spi_irq_enable(struct spi_device *spi, u32 mask) +static inline void uniphier_spi_irq_enable(struct uniphier_spi_priv *priv, + u32 mask) { - struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); u32 val; val = readl(priv->base + SSI_IE); @@ -103,9 +103,9 @@ static inline void uniphier_spi_irq_enable(struct spi_device *spi, u32 mask) writel(val, priv->base + SSI_IE); } -static inline void uniphier_spi_irq_disable(struct spi_device *spi, u32 mask) +static inline void uniphier_spi_irq_disable(struct uniphier_spi_priv *priv, + u32 mask) { - struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); u32 val; val = readl(priv->base + SSI_IE); @@ -339,12 +339,12 @@ static int uniphier_spi_transfer_one_irq(struct spi_master *master, uniphier_spi_fill_tx_fifo(priv); - uniphier_spi_irq_enable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + uniphier_spi_irq_enable(priv, SSI_IE_RCIE | SSI_IE_RORIE); time_left = wait_for_completion_timeout(&priv->xfer_done, msecs_to_jiffies(SSI_TIMEOUT_MS)); - uniphier_spi_irq_disable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + uniphier_spi_irq_disable(priv, SSI_IE_RCIE | SSI_IE_RORIE); if (!time_left) { dev_err(dev, "transfer timeout.\n"); -- 2.20.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/5] spi: uniphier: Add handle_err callback function 2019-12-24 0:58 [PATCH 0/5] spi: uniphier: Introduce DMA transfer mode Kunihiko Hayashi 2019-12-24 0:58 ` [PATCH 1/5] spi: uniphier: Fix FIFO threshold Kunihiko Hayashi 2019-12-24 0:58 ` [PATCH 2/5] spi: uniphier: Change argument of irq functions to private structure Kunihiko Hayashi @ 2019-12-24 0:58 ` Kunihiko Hayashi 2019-12-25 0:09 ` Applied "spi: uniphier: Add handle_err callback function" to the spi tree Mark Brown 2019-12-24 0:58 ` [PATCH 4/5] spi: uniphier: Add SPI_LOOP to the capabilities Kunihiko Hayashi 2019-12-24 0:58 ` [PATCH 5/5] spi: uniphier: Add DMA transfer mode support Kunihiko Hayashi 4 siblings, 1 reply; 12+ messages in thread From: Kunihiko Hayashi @ 2019-12-24 0:58 UTC (permalink / raw) To: Mark Brown, linux-spi Cc: linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar, Kunihiko Hayashi This adds master->handle_err() callback function to stop transfer due to error. The function also resets FIFOs and disables interrupt. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> --- drivers/spi/spi-uniphier.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 3859649..c4e3b96 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -432,6 +432,22 @@ static int uniphier_spi_unprepare_transfer_hardware(struct spi_master *master) return 0; } +static void uniphier_spi_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + u32 val; + + /* stop running spi transfer */ + writel(0, priv->base + SSI_CTL); + + /* reset FIFOs */ + val = SSI_FC_TXFFL | SSI_FC_RXFFL; + writel(val, priv->base + SSI_FC); + + uniphier_spi_irq_disable(priv, SSI_IE_RCIE | SSI_IE_RORIE); +} + static irqreturn_t uniphier_spi_handler(int irq, void *dev_id) { struct uniphier_spi_priv *priv = dev_id; @@ -538,6 +554,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) = uniphier_spi_prepare_transfer_hardware; master->unprepare_transfer_hardware = uniphier_spi_unprepare_transfer_hardware; + master->handle_err = uniphier_spi_handle_err; master->num_chipselect = 1; ret = devm_spi_register_master(&pdev->dev, master); -- 2.7.4 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Applied "spi: uniphier: Add handle_err callback function" to the spi tree 2019-12-24 0:58 ` [PATCH 3/5] spi: uniphier: Add handle_err callback function Kunihiko Hayashi @ 2019-12-25 0:09 ` Mark Brown 0 siblings, 0 replies; 12+ messages in thread From: Mark Brown @ 2019-12-25 0:09 UTC (permalink / raw) To: Kunihiko Hayashi Cc: Jassi Brar, Keiji Hayashibara, linux-arm-kernel, linux-kernel, linux-spi, Mark Brown, Masahiro Yamada, Masami Hiramatsu The patch spi: uniphier: Add handle_err callback function has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-5.6 All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From 11299c5cd8868479e9acfc525ba3c2e882c7ba0c Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Date: Tue, 24 Dec 2019 09:58:25 +0900 Subject: [PATCH] spi: uniphier: Add handle_err callback function This adds master->handle_err() callback function to stop transfer due to error. The function also resets FIFOs and disables interrupt. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Link: https://lore.kernel.org/r/1577149107-30670-4-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-uniphier.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index a44a1a5fb7b0..0b255597fdcb 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -425,6 +425,22 @@ static int uniphier_spi_unprepare_transfer_hardware(struct spi_master *master) return 0; } +static void uniphier_spi_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + u32 val; + + /* stop running spi transfer */ + writel(0, priv->base + SSI_CTL); + + /* reset FIFOs */ + val = SSI_FC_TXFFL | SSI_FC_RXFFL; + writel(val, priv->base + SSI_FC); + + uniphier_spi_irq_disable(priv, SSI_IE_RCIE | SSI_IE_RORIE); +} + static irqreturn_t uniphier_spi_handler(int irq, void *dev_id) { struct uniphier_spi_priv *priv = dev_id; @@ -531,6 +547,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) = uniphier_spi_prepare_transfer_hardware; master->unprepare_transfer_hardware = uniphier_spi_unprepare_transfer_hardware; + master->handle_err = uniphier_spi_handle_err; master->num_chipselect = 1; ret = devm_spi_register_master(&pdev->dev, master); -- 2.20.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 4/5] spi: uniphier: Add SPI_LOOP to the capabilities 2019-12-24 0:58 [PATCH 0/5] spi: uniphier: Introduce DMA transfer mode Kunihiko Hayashi ` (2 preceding siblings ...) 2019-12-24 0:58 ` [PATCH 3/5] spi: uniphier: Add handle_err callback function Kunihiko Hayashi @ 2019-12-24 0:58 ` Kunihiko Hayashi 2019-12-25 18:01 ` Mark Brown 2019-12-24 0:58 ` [PATCH 5/5] spi: uniphier: Add DMA transfer mode support Kunihiko Hayashi 4 siblings, 1 reply; 12+ messages in thread From: Kunihiko Hayashi @ 2019-12-24 0:58 UTC (permalink / raw) To: Mark Brown, linux-spi Cc: linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar, Kunihiko Hayashi Add SPI_LOOP to the capabilities to support loopback mode. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> --- drivers/spi/spi-uniphier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index c4e3b96..caf0446 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -543,7 +543,8 @@ static int uniphier_spi_probe(struct platform_device *pdev) master->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER); master->min_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MAX_CLK_DIVIDER); - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | + SPI_LOOP; master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); -- 2.7.4 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 4/5] spi: uniphier: Add SPI_LOOP to the capabilities 2019-12-24 0:58 ` [PATCH 4/5] spi: uniphier: Add SPI_LOOP to the capabilities Kunihiko Hayashi @ 2019-12-25 18:01 ` Mark Brown 2019-12-26 1:55 ` Kunihiko Hayashi 0 siblings, 1 reply; 12+ messages in thread From: Mark Brown @ 2019-12-25 18:01 UTC (permalink / raw) To: Kunihiko Hayashi Cc: linux-spi, linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar [-- Attachment #1: Type: text/plain, Size: 527 bytes --] On Tue, Dec 24, 2019 at 09:58:26AM +0900, Kunihiko Hayashi wrote: > Add SPI_LOOP to the capabilities to support loopback mode. > master->min_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MAX_CLK_DIVIDER); > - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; > + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | > + SPI_LOOP; I'd expect to see a matching change that configures the hardware in loopback mode when the driver is in SPI_MODE? There's nothing in the existing driver. [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 4/5] spi: uniphier: Add SPI_LOOP to the capabilities 2019-12-25 18:01 ` Mark Brown @ 2019-12-26 1:55 ` Kunihiko Hayashi 0 siblings, 0 replies; 12+ messages in thread From: Kunihiko Hayashi @ 2019-12-26 1:55 UTC (permalink / raw) To: Mark Brown Cc: linux-spi, linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar Hi Mark, Thanks for your comment. On Wed, 25 Dec 2019 18:01:42 +0000 <broonie@kernel.org> wrote: > On Tue, Dec 24, 2019 at 09:58:26AM +0900, Kunihiko Hayashi wrote: > > Add SPI_LOOP to the capabilities to support loopback mode. > > > master->min_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MAX_CLK_DIVIDER); > > - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; > > + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | > > + SPI_LOOP; > > I'd expect to see a matching change that configures the hardware > in loopback mode when the driver is in SPI_MODE? There's nothing > in the existing driver. I see. When doing loopback test using spi-loopback-test.c, I thought 'loop_req=1' option was needed, however, the controller doesn't need it. We can ignore this patch. Thank you, --- Best Regards, Kunihiko Hayashi ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 5/5] spi: uniphier: Add DMA transfer mode support 2019-12-24 0:58 [PATCH 0/5] spi: uniphier: Introduce DMA transfer mode Kunihiko Hayashi ` (3 preceding siblings ...) 2019-12-24 0:58 ` [PATCH 4/5] spi: uniphier: Add SPI_LOOP to the capabilities Kunihiko Hayashi @ 2019-12-24 0:58 ` Kunihiko Hayashi 2019-12-25 18:22 ` Applied "spi: uniphier: Add DMA transfer mode support" to the spi tree Mark Brown 4 siblings, 1 reply; 12+ messages in thread From: Kunihiko Hayashi @ 2019-12-24 0:58 UTC (permalink / raw) To: Mark Brown, linux-spi Cc: linux-arm-kernel, linux-kernel, Masahiro Yamada, Keiji Hayashibara, Masami Hiramatsu, Jassi Brar, Kunihiko Hayashi This adds DMA transfer mode support for UniPhier SPI controller. Since this controller requires simulteaneous transmission and reception, this indicates SPI_CONTROLLER_MUST_RX and SPI_CONTROLLER_MUST_TX. Because the supported dma controller has alignment restiction, there is also a restriction that 'maxburst' parameters in dma_slave_config corresponds to one word width. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> --- drivers/spi/spi-uniphier.c | 200 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index caf0446..bf3af5b 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -8,6 +8,7 @@ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/dmaengine.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -23,6 +24,7 @@ struct uniphier_spi_priv { void __iomem *base; + dma_addr_t base_dma_addr; struct clk *clk; struct spi_master *master; struct completion xfer_done; @@ -32,6 +34,7 @@ struct uniphier_spi_priv { unsigned int rx_bytes; const u8 *tx_buf; u8 *rx_buf; + atomic_t dma_busy; bool is_save_param; u8 bits_per_word; @@ -61,11 +64,16 @@ struct uniphier_spi_priv { #define SSI_FPS_FSTRT BIT(14) #define SSI_SR 0x14 +#define SSI_SR_BUSY BIT(7) #define SSI_SR_RNE BIT(0) #define SSI_IE 0x18 +#define SSI_IE_TCIE BIT(4) #define SSI_IE_RCIE BIT(3) +#define SSI_IE_TXRE BIT(2) +#define SSI_IE_RXRE BIT(1) #define SSI_IE_RORIE BIT(0) +#define SSI_IE_ALL_MASK GENMASK(4, 0) #define SSI_IS 0x1c #define SSI_IS_RXRS BIT(9) @@ -87,6 +95,10 @@ struct uniphier_spi_priv { #define SSI_RXDR 0x24 #define SSI_FIFO_DEPTH 8U +#define SSI_FIFO_BURST_NUM 1 + +#define SSI_DMA_RX_BUSY BIT(1) +#define SSI_DMA_TX_BUSY BIT(0) static inline unsigned int bytes_per_word(unsigned int bits) { @@ -334,6 +346,128 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) writel(val, priv->base + SSI_FPS); } +static bool uniphier_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + unsigned int bpw = bytes_per_word(priv->bits_per_word); + + if ((!master->dma_tx && !master->dma_rx) + || (!master->dma_tx && t->tx_buf) + || (!master->dma_rx && t->rx_buf)) + return false; + + return DIV_ROUND_UP(t->len, bpw) > SSI_FIFO_DEPTH; +} + +static void uniphier_spi_dma_rxcb(void *data) +{ + struct spi_master *master = data; + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int state = atomic_fetch_andnot(SSI_DMA_RX_BUSY, &priv->dma_busy); + + uniphier_spi_irq_disable(priv, SSI_IE_RXRE); + + if (!(state & SSI_DMA_TX_BUSY)) + spi_finalize_current_transfer(master); +} + +static void uniphier_spi_dma_txcb(void *data) +{ + struct spi_master *master = data; + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int state = atomic_fetch_andnot(SSI_DMA_TX_BUSY, &priv->dma_busy); + + uniphier_spi_irq_disable(priv, SSI_IE_TXRE); + + if (!(state & SSI_DMA_RX_BUSY)) + spi_finalize_current_transfer(master); +} + +static int uniphier_spi_transfer_one_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + struct dma_async_tx_descriptor *rxdesc = NULL, *txdesc = NULL; + int buswidth; + + atomic_set(&priv->dma_busy, 0); + + uniphier_spi_set_fifo_threshold(priv, SSI_FIFO_BURST_NUM); + + if (priv->bits_per_word <= 8) + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (priv->bits_per_word <= 16) + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + else + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + + if (priv->rx_buf) { + struct dma_slave_config rxconf = { + .direction = DMA_DEV_TO_MEM, + .src_addr = priv->base_dma_addr + SSI_RXDR, + .src_addr_width = buswidth, + .src_maxburst = SSI_FIFO_BURST_NUM, + }; + + dmaengine_slave_config(master->dma_rx, &rxconf); + + rxdesc = dmaengine_prep_slave_sg( + master->dma_rx, + t->rx_sg.sgl, t->rx_sg.nents, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + goto out_err_prep; + + rxdesc->callback = uniphier_spi_dma_rxcb; + rxdesc->callback_param = master; + + uniphier_spi_irq_enable(priv, SSI_IE_RXRE); + atomic_or(SSI_DMA_RX_BUSY, &priv->dma_busy); + + dmaengine_submit(rxdesc); + dma_async_issue_pending(master->dma_rx); + } + + if (priv->tx_buf) { + struct dma_slave_config txconf = { + .direction = DMA_MEM_TO_DEV, + .dst_addr = priv->base_dma_addr + SSI_TXDR, + .dst_addr_width = buswidth, + .dst_maxburst = SSI_FIFO_BURST_NUM, + }; + + dmaengine_slave_config(master->dma_tx, &txconf); + + txdesc = dmaengine_prep_slave_sg( + master->dma_tx, + t->tx_sg.sgl, t->tx_sg.nents, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + goto out_err_prep; + + txdesc->callback = uniphier_spi_dma_txcb; + txdesc->callback_param = master; + + uniphier_spi_irq_enable(priv, SSI_IE_TXRE); + atomic_or(SSI_DMA_TX_BUSY, &priv->dma_busy); + + dmaengine_submit(txdesc); + dma_async_issue_pending(master->dma_tx); + } + + /* signal that we need to wait for completion */ + return (priv->tx_buf || priv->rx_buf); + +out_err_prep: + if (rxdesc) + dmaengine_terminate_sync(master->dma_rx); + + return -EINVAL; +} + static int uniphier_spi_transfer_one_irq(struct spi_master *master, struct spi_device *spi, struct spi_transfer *t) @@ -395,6 +529,7 @@ static int uniphier_spi_transfer_one(struct spi_master *master, { struct uniphier_spi_priv *priv = spi_master_get_devdata(master); unsigned long threshold; + bool use_dma; /* Terminate and return success for 0 byte length transfer */ if (!t->len) @@ -402,6 +537,10 @@ static int uniphier_spi_transfer_one(struct spi_master *master, uniphier_spi_setup_transfer(spi, t); + use_dma = master->can_dma ? master->can_dma(master, spi, t) : false; + if (use_dma) + return uniphier_spi_transfer_one_dma(master, spi, t); + /* * If the transfer operation will take longer than * SSI_POLL_TIMEOUT_US, it should use irq. @@ -445,7 +584,17 @@ static void uniphier_spi_handle_err(struct spi_master *master, val = SSI_FC_TXFFL | SSI_FC_RXFFL; writel(val, priv->base + SSI_FC); - uniphier_spi_irq_disable(priv, SSI_IE_RCIE | SSI_IE_RORIE); + uniphier_spi_irq_disable(priv, SSI_IE_ALL_MASK); + + if (atomic_read(&priv->dma_busy) & SSI_DMA_TX_BUSY) { + dmaengine_terminate_async(master->dma_tx); + atomic_andnot(SSI_DMA_TX_BUSY, &priv->dma_busy); + } + + if (atomic_read(&priv->dma_busy) & SSI_DMA_RX_BUSY) { + dmaengine_terminate_async(master->dma_rx); + atomic_andnot(SSI_DMA_RX_BUSY, &priv->dma_busy); + } } static irqreturn_t uniphier_spi_handler(int irq, void *dev_id) @@ -493,6 +642,9 @@ static int uniphier_spi_probe(struct platform_device *pdev) { struct uniphier_spi_priv *priv; struct spi_master *master; + struct resource *res; + struct dma_slave_caps caps; + u32 dma_tx_burst = 0, dma_rx_burst = 0; unsigned long clk_rate; int irq; int ret; @@ -507,11 +659,13 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->master = master; priv->is_save_param = false; - priv->base = devm_platform_ioremap_resource(pdev, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto out_master_put; } + priv->base_dma_addr = res->start; priv->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { @@ -556,7 +710,44 @@ static int uniphier_spi_probe(struct platform_device *pdev) master->unprepare_transfer_hardware = uniphier_spi_unprepare_transfer_hardware; master->handle_err = uniphier_spi_handle_err; + master->can_dma = uniphier_spi_can_dma; + master->num_chipselect = 1; + master->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + + master->dma_tx = dma_request_chan(&pdev->dev, "tx"); + if (IS_ERR_OR_NULL(master->dma_tx)) { + if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) + goto out_disable_clk; + master->dma_tx = NULL; + dma_tx_burst = INT_MAX; + } else { + ret = dma_get_slave_caps(master->dma_tx, &caps); + if (ret) { + dev_err(&pdev->dev, "failed to get TX DMA capacities: %d\n", + ret); + goto out_disable_clk; + } + dma_tx_burst = caps.max_burst; + } + + master->dma_rx = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR_OR_NULL(master->dma_rx)) { + if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) + goto out_disable_clk; + master->dma_rx = NULL; + dma_rx_burst = INT_MAX; + } else { + ret = dma_get_slave_caps(master->dma_rx, &caps); + if (ret) { + dev_err(&pdev->dev, "failed to get RX DMA capacities: %d\n", + ret); + goto out_disable_clk; + } + dma_rx_burst = caps.max_burst; + } + + master->max_dma_len = min(dma_tx_burst, dma_rx_burst); ret = devm_spi_register_master(&pdev->dev, master); if (ret) @@ -576,6 +767,11 @@ static int uniphier_spi_remove(struct platform_device *pdev) { struct uniphier_spi_priv *priv = platform_get_drvdata(pdev); + if (priv->master->dma_tx) + dma_release_channel(priv->master->dma_tx); + if (priv->master->dma_rx) + dma_release_channel(priv->master->dma_rx); + clk_disable_unprepare(priv->clk); return 0; -- 2.7.4 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Applied "spi: uniphier: Add DMA transfer mode support" to the spi tree 2019-12-24 0:58 ` [PATCH 5/5] spi: uniphier: Add DMA transfer mode support Kunihiko Hayashi @ 2019-12-25 18:22 ` Mark Brown 0 siblings, 0 replies; 12+ messages in thread From: Mark Brown @ 2019-12-25 18:22 UTC (permalink / raw) To: Kunihiko Hayashi Cc: Jassi Brar, Keiji Hayashibara, linux-arm-kernel, linux-kernel, linux-spi, Mark Brown, Masahiro Yamada, Masami Hiramatsu The patch spi: uniphier: Add DMA transfer mode support has been applied to the spi tree at https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-5.6 All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark From 28d1dddc59f6b7fc085093e7c1e978b33f0caf4c Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Date: Tue, 24 Dec 2019 09:58:27 +0900 Subject: [PATCH] spi: uniphier: Add DMA transfer mode support This adds DMA transfer mode support for UniPhier SPI controller. Since this controller requires simulteaneous transmission and reception, this indicates SPI_CONTROLLER_MUST_RX and SPI_CONTROLLER_MUST_TX. Because the supported dma controller has alignment restiction, there is also a restriction that 'maxburst' parameters in dma_slave_config corresponds to one word width. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Link: https://lore.kernel.org/r/1577149107-30670-6-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-uniphier.c | 200 ++++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index c4e3b96b1038..0fa50979644d 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -8,6 +8,7 @@ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/dmaengine.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -23,6 +24,7 @@ struct uniphier_spi_priv { void __iomem *base; + dma_addr_t base_dma_addr; struct clk *clk; struct spi_master *master; struct completion xfer_done; @@ -32,6 +34,7 @@ struct uniphier_spi_priv { unsigned int rx_bytes; const u8 *tx_buf; u8 *rx_buf; + atomic_t dma_busy; bool is_save_param; u8 bits_per_word; @@ -61,11 +64,16 @@ struct uniphier_spi_priv { #define SSI_FPS_FSTRT BIT(14) #define SSI_SR 0x14 +#define SSI_SR_BUSY BIT(7) #define SSI_SR_RNE BIT(0) #define SSI_IE 0x18 +#define SSI_IE_TCIE BIT(4) #define SSI_IE_RCIE BIT(3) +#define SSI_IE_TXRE BIT(2) +#define SSI_IE_RXRE BIT(1) #define SSI_IE_RORIE BIT(0) +#define SSI_IE_ALL_MASK GENMASK(4, 0) #define SSI_IS 0x1c #define SSI_IS_RXRS BIT(9) @@ -87,6 +95,10 @@ struct uniphier_spi_priv { #define SSI_RXDR 0x24 #define SSI_FIFO_DEPTH 8U +#define SSI_FIFO_BURST_NUM 1 + +#define SSI_DMA_RX_BUSY BIT(1) +#define SSI_DMA_TX_BUSY BIT(0) static inline unsigned int bytes_per_word(unsigned int bits) { @@ -334,6 +346,128 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) writel(val, priv->base + SSI_FPS); } +static bool uniphier_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + unsigned int bpw = bytes_per_word(priv->bits_per_word); + + if ((!master->dma_tx && !master->dma_rx) + || (!master->dma_tx && t->tx_buf) + || (!master->dma_rx && t->rx_buf)) + return false; + + return DIV_ROUND_UP(t->len, bpw) > SSI_FIFO_DEPTH; +} + +static void uniphier_spi_dma_rxcb(void *data) +{ + struct spi_master *master = data; + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int state = atomic_fetch_andnot(SSI_DMA_RX_BUSY, &priv->dma_busy); + + uniphier_spi_irq_disable(priv, SSI_IE_RXRE); + + if (!(state & SSI_DMA_TX_BUSY)) + spi_finalize_current_transfer(master); +} + +static void uniphier_spi_dma_txcb(void *data) +{ + struct spi_master *master = data; + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int state = atomic_fetch_andnot(SSI_DMA_TX_BUSY, &priv->dma_busy); + + uniphier_spi_irq_disable(priv, SSI_IE_TXRE); + + if (!(state & SSI_DMA_RX_BUSY)) + spi_finalize_current_transfer(master); +} + +static int uniphier_spi_transfer_one_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + struct dma_async_tx_descriptor *rxdesc = NULL, *txdesc = NULL; + int buswidth; + + atomic_set(&priv->dma_busy, 0); + + uniphier_spi_set_fifo_threshold(priv, SSI_FIFO_BURST_NUM); + + if (priv->bits_per_word <= 8) + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (priv->bits_per_word <= 16) + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + else + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + + if (priv->rx_buf) { + struct dma_slave_config rxconf = { + .direction = DMA_DEV_TO_MEM, + .src_addr = priv->base_dma_addr + SSI_RXDR, + .src_addr_width = buswidth, + .src_maxburst = SSI_FIFO_BURST_NUM, + }; + + dmaengine_slave_config(master->dma_rx, &rxconf); + + rxdesc = dmaengine_prep_slave_sg( + master->dma_rx, + t->rx_sg.sgl, t->rx_sg.nents, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + goto out_err_prep; + + rxdesc->callback = uniphier_spi_dma_rxcb; + rxdesc->callback_param = master; + + uniphier_spi_irq_enable(priv, SSI_IE_RXRE); + atomic_or(SSI_DMA_RX_BUSY, &priv->dma_busy); + + dmaengine_submit(rxdesc); + dma_async_issue_pending(master->dma_rx); + } + + if (priv->tx_buf) { + struct dma_slave_config txconf = { + .direction = DMA_MEM_TO_DEV, + .dst_addr = priv->base_dma_addr + SSI_TXDR, + .dst_addr_width = buswidth, + .dst_maxburst = SSI_FIFO_BURST_NUM, + }; + + dmaengine_slave_config(master->dma_tx, &txconf); + + txdesc = dmaengine_prep_slave_sg( + master->dma_tx, + t->tx_sg.sgl, t->tx_sg.nents, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + goto out_err_prep; + + txdesc->callback = uniphier_spi_dma_txcb; + txdesc->callback_param = master; + + uniphier_spi_irq_enable(priv, SSI_IE_TXRE); + atomic_or(SSI_DMA_TX_BUSY, &priv->dma_busy); + + dmaengine_submit(txdesc); + dma_async_issue_pending(master->dma_tx); + } + + /* signal that we need to wait for completion */ + return (priv->tx_buf || priv->rx_buf); + +out_err_prep: + if (rxdesc) + dmaengine_terminate_sync(master->dma_rx); + + return -EINVAL; +} + static int uniphier_spi_transfer_one_irq(struct spi_master *master, struct spi_device *spi, struct spi_transfer *t) @@ -395,6 +529,7 @@ static int uniphier_spi_transfer_one(struct spi_master *master, { struct uniphier_spi_priv *priv = spi_master_get_devdata(master); unsigned long threshold; + bool use_dma; /* Terminate and return success for 0 byte length transfer */ if (!t->len) @@ -402,6 +537,10 @@ static int uniphier_spi_transfer_one(struct spi_master *master, uniphier_spi_setup_transfer(spi, t); + use_dma = master->can_dma ? master->can_dma(master, spi, t) : false; + if (use_dma) + return uniphier_spi_transfer_one_dma(master, spi, t); + /* * If the transfer operation will take longer than * SSI_POLL_TIMEOUT_US, it should use irq. @@ -445,7 +584,17 @@ static void uniphier_spi_handle_err(struct spi_master *master, val = SSI_FC_TXFFL | SSI_FC_RXFFL; writel(val, priv->base + SSI_FC); - uniphier_spi_irq_disable(priv, SSI_IE_RCIE | SSI_IE_RORIE); + uniphier_spi_irq_disable(priv, SSI_IE_ALL_MASK); + + if (atomic_read(&priv->dma_busy) & SSI_DMA_TX_BUSY) { + dmaengine_terminate_async(master->dma_tx); + atomic_andnot(SSI_DMA_TX_BUSY, &priv->dma_busy); + } + + if (atomic_read(&priv->dma_busy) & SSI_DMA_RX_BUSY) { + dmaengine_terminate_async(master->dma_rx); + atomic_andnot(SSI_DMA_RX_BUSY, &priv->dma_busy); + } } static irqreturn_t uniphier_spi_handler(int irq, void *dev_id) @@ -493,6 +642,9 @@ static int uniphier_spi_probe(struct platform_device *pdev) { struct uniphier_spi_priv *priv; struct spi_master *master; + struct resource *res; + struct dma_slave_caps caps; + u32 dma_tx_burst = 0, dma_rx_burst = 0; unsigned long clk_rate; int irq; int ret; @@ -507,11 +659,13 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->master = master; priv->is_save_param = false; - priv->base = devm_platform_ioremap_resource(pdev, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto out_master_put; } + priv->base_dma_addr = res->start; priv->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { @@ -555,7 +709,44 @@ static int uniphier_spi_probe(struct platform_device *pdev) master->unprepare_transfer_hardware = uniphier_spi_unprepare_transfer_hardware; master->handle_err = uniphier_spi_handle_err; + master->can_dma = uniphier_spi_can_dma; + master->num_chipselect = 1; + master->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + + master->dma_tx = dma_request_chan(&pdev->dev, "tx"); + if (IS_ERR_OR_NULL(master->dma_tx)) { + if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) + goto out_disable_clk; + master->dma_tx = NULL; + dma_tx_burst = INT_MAX; + } else { + ret = dma_get_slave_caps(master->dma_tx, &caps); + if (ret) { + dev_err(&pdev->dev, "failed to get TX DMA capacities: %d\n", + ret); + goto out_disable_clk; + } + dma_tx_burst = caps.max_burst; + } + + master->dma_rx = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR_OR_NULL(master->dma_rx)) { + if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) + goto out_disable_clk; + master->dma_rx = NULL; + dma_rx_burst = INT_MAX; + } else { + ret = dma_get_slave_caps(master->dma_rx, &caps); + if (ret) { + dev_err(&pdev->dev, "failed to get RX DMA capacities: %d\n", + ret); + goto out_disable_clk; + } + dma_rx_burst = caps.max_burst; + } + + master->max_dma_len = min(dma_tx_burst, dma_rx_burst); ret = devm_spi_register_master(&pdev->dev, master); if (ret) @@ -575,6 +766,11 @@ static int uniphier_spi_remove(struct platform_device *pdev) { struct uniphier_spi_priv *priv = platform_get_drvdata(pdev); + if (priv->master->dma_tx) + dma_release_channel(priv->master->dma_tx); + if (priv->master->dma_rx) + dma_release_channel(priv->master->dma_rx); + clk_disable_unprepare(priv->clk); return 0; -- 2.20.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2019-12-26 1:55 UTC | newest] Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-12-24 0:58 [PATCH 0/5] spi: uniphier: Introduce DMA transfer mode Kunihiko Hayashi 2019-12-24 0:58 ` [PATCH 1/5] spi: uniphier: Fix FIFO threshold Kunihiko Hayashi 2019-12-25 0:09 ` Applied "spi: uniphier: Fix FIFO threshold" to the spi tree Mark Brown 2019-12-24 0:58 ` [PATCH 2/5] spi: uniphier: Change argument of irq functions to private structure Kunihiko Hayashi 2019-12-25 0:09 ` Applied "spi: uniphier: Change argument of irq functions to private structure" to the spi tree Mark Brown 2019-12-24 0:58 ` [PATCH 3/5] spi: uniphier: Add handle_err callback function Kunihiko Hayashi 2019-12-25 0:09 ` Applied "spi: uniphier: Add handle_err callback function" to the spi tree Mark Brown 2019-12-24 0:58 ` [PATCH 4/5] spi: uniphier: Add SPI_LOOP to the capabilities Kunihiko Hayashi 2019-12-25 18:01 ` Mark Brown 2019-12-26 1:55 ` Kunihiko Hayashi 2019-12-24 0:58 ` [PATCH 5/5] spi: uniphier: Add DMA transfer mode support Kunihiko Hayashi 2019-12-25 18:22 ` Applied "spi: uniphier: Add DMA transfer mode support" to the spi tree Mark Brown
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).