From cb75b0c4dee881fe9d486e725b778395ce0b0426 Mon Sep 17 00:00:00 2001 From: Aishwarya R Date: Tue, 7 Apr 2020 18:25:57 +0530 Subject: spi: spi-fsl-lpspi: Fix indentation and open brace should be on the previous line Resolved open brace { should be on the previous line checkpatch error and fix the indentation Signed-off-by: Aishwarya R Link: https://lore.kernel.org/r/20200407125557.6520-1-aishwaryarj100@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 8b41b70f6f5c..511211b82430 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -186,14 +186,13 @@ static bool fsl_lpspi_can_dma(struct spi_controller *controller, bytes_per_word = fsl_lpspi_bytes_per_word(transfer->bits_per_word); - switch (bytes_per_word) - { - case 1: - case 2: - case 4: - break; - default: - return false; + switch (bytes_per_word) { + case 1: + case 2: + case 4: + break; + default: + return false; } return true; -- cgit v1.2.3-59-g8ed1b From 7cb88afb42a49e5a6d51d458ffbb3d02e2c0755c Mon Sep 17 00:00:00 2001 From: Aishwarya R Date: Tue, 7 Apr 2020 17:58:55 +0530 Subject: spi: spi-fsl-spi: Fix checkpatch error "foo * bar" should be "foo *bar" This patch fixes checkpatch error "foo * bar" should be "foo *bar" Signed-off-by: Aishwarya R Link: https://lore.kernel.org/r/20200407122855.5531-1-aishwaryarj100@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 3b81772fea0d..67f022b8c81d 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -588,7 +588,7 @@ static void fsl_spi_grlib_probe(struct device *dev) pdata->cs_control = fsl_spi_grlib_cs_control; } -static struct spi_master * fsl_spi_probe(struct device *dev, +static struct spi_master *fsl_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); -- cgit v1.2.3-59-g8ed1b From 49686df5b874e5ed6249bc622b73ad9a9e71cd99 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 10 Apr 2020 13:23:15 +0100 Subject: spi: remove redundant assignment to variable ms The variable ms is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Signed-off-by: Colin Ian King Reviewed-by: Geert Uytterhoeven Addresses-Coverity: ("Unused value") Link: https://lore.kernel.org/r/20200410122315.17523-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c92c89467e7e..5446b07dcd3a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1075,7 +1075,7 @@ static int spi_transfer_wait(struct spi_controller *ctlr, { struct spi_statistics *statm = &ctlr->statistics; struct spi_statistics *stats = &msg->spi->statistics; - unsigned long long ms = 1; + unsigned long long ms; if (spi_controller_is_slave(ctlr)) { if (wait_for_completion_interruptible(&ctlr->xfer_completion)) { -- cgit v1.2.3-59-g8ed1b From bb4bf8d2f4e0fdf64e43bb3a3186e04891b8a8e1 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 13 Apr 2020 14:30:25 +0800 Subject: spi: sprd: adi: Use IS_ENABLED() to validate configs If the Spreadtrum wachdog is loaded as a module, we still need set default watchdog reboot mode in case the rebooting is caused by watchdog. But now we can not set the watchdog reboot mode by using '#ifdef' to validate the watchdog configuration, thus we can change to use IS_ENABLED() to fix this issue. Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/e38807eadd5550add8eb90dd3f8fbe2cfc39cc13.1586759322.git.baolin.wang7@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sprd-adi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index 87dadb6b8ebf..88e6543648cb 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -319,7 +319,7 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr, static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi) { -#ifdef CONFIG_SPRD_WATCHDOG +#if IS_ENABLED(CONFIG_SPRD_WATCHDOG) u32 val; /* Set default watchdog reboot mode */ -- cgit v1.2.3-59-g8ed1b From afedb4b7282b4c532d8aea105943e71c0efb3a6b Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Thu, 9 Apr 2020 16:50:09 +0800 Subject: spi: spi-mtk-nor: make mtk_nor_exec_op() statuc Fix the following sparse warning: drivers/spi/spi-mtk-nor.c:394:5: warning: symbol 'mtk_nor_exec_op' was not declared. Should it be static? Reported-by: Hulk Robot Signed-off-by: Jason Yan Link: https://lore.kernel.org/r/20200409085009.44971-1-yanaijie@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index c15a9910549f..7bc302b50396 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -391,7 +391,7 @@ static int mtk_nor_pp_unbuffered(struct mtk_nor *sp, return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE, 6 * BITS_PER_BYTE); } -int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); int ret; -- cgit v1.2.3-59-g8ed1b From 21fb1f41bcdf436f6969f4651dce0df62985f69a Mon Sep 17 00:00:00 2001 From: Aishwarya R Date: Mon, 6 Apr 2020 21:23:01 +0530 Subject: spi: spi-sh-msiof: Fix checkpatch error Complex macros should use () Fixed checkpatch error "Macros with complex values should be enclosed in parentheses" Signed-off-by: Aishwarya R Link: https://lore.kernel.org/r/20200406155301.21768-1-raishwar@visteon.com Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 1c11a00a2c36..b2579af0e3eb 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1398,7 +1398,7 @@ static int sh_msiof_spi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend, sh_msiof_spi_resume); -#define DEV_PM_OPS &sh_msiof_spi_pm_ops +#define DEV_PM_OPS (&sh_msiof_spi_pm_ops) #else #define DEV_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ -- cgit v1.2.3-59-g8ed1b From 66ec7b3bc9c043518da828cd84aefe6242943a97 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 16 Apr 2020 12:18:34 +0200 Subject: spi: spidev: Add support for Octal mode data transfers Include the flags for Octal mode data transfers in the mask, so userspace can set them. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200416101835.14573-2-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 80dd1025b953..d753df700e9e 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -62,7 +62,8 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS); #define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \ | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \ | SPI_NO_CS | SPI_READY | SPI_TX_DUAL \ - | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD) + | SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \ + | SPI_RX_QUAD | SPI_RX_OCTAL) struct spidev_data { dev_t devt; -- cgit v1.2.3-59-g8ed1b From ae9e6ac4d8542d1b16fad4bd3c3e447632437623 Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Thu, 16 Apr 2020 13:09:15 +0200 Subject: spi: dw: remove unused dw_spi_chip handling The path of code using this struct is unused since there is no more user of this. Remove code and struct definition. Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200416110916.22633-1-cleger@kalray.eu Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 16 ---------------- drivers/spi/spi-dw.h | 12 ------------ 2 files changed, 28 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 31e3f866d11a..fda8d433a769 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -399,7 +399,6 @@ static void dw_spi_handle_err(struct spi_controller *master, /* This may be called twice for each spi dev */ static int dw_spi_setup(struct spi_device *spi) { - struct dw_spi_chip *chip_info = NULL; struct chip_data *chip; /* Only alloc on first setup */ @@ -411,21 +410,6 @@ static int dw_spi_setup(struct spi_device *spi) spi_set_ctldata(spi, chip); } - /* - * Protocol drivers may change the chip settings, so... - * if chip_info exists, use it - */ - chip_info = spi->controller_data; - - /* chip_info doesn't always exist */ - if (chip_info) { - if (chip_info->cs_control) - chip->cs_control = chip_info->cs_control; - - chip->poll_mode = chip_info->poll_mode; - chip->type = chip_info->type; - } - chip->tmode = SPI_TMOD_TR; return 0; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 1bf5713e047d..44ef18187c15 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -235,18 +235,6 @@ static inline void spi_shutdown_chip(struct dw_spi *dws) spi_set_clk(dws, 0); } -/* - * Each SPI slave device to work with dw_api controller should - * has such a structure claiming its working mode (poll or PIO/DMA), - * which can be save in the "controller_data" member of the - * struct spi_device. - */ -struct dw_spi_chip { - u8 poll_mode; /* 1 for controller polling mode */ - u8 type; /* SPI/SSP/MicroWire */ - void (*cs_control)(u32 command); -}; - extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); -- cgit v1.2.3-59-g8ed1b From 33e8fd4bfbd7eb0921eb55caceed54e51912a4e6 Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Thu, 16 Apr 2020 13:09:16 +0200 Subject: spi: dw: remove cs_control and poll_mode members from chip_data Since these members were initialized only with previous dw_spi_chip struct members and that there is no user anymore, remove them. Along this removal, remove code path which were using these members. Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200416110916.22633-2-cleger@kalray.eu Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index fda8d433a769..2b79c5a983c0 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -24,11 +24,8 @@ struct chip_data { u8 tmode; /* TR/TO/RO/EEPROM */ u8 type; /* SPI/SSP/MicroWire */ - u8 poll_mode; /* 1 means use poll mode */ - u16 clk_div; /* baud rate divider */ u32 speed_hz; /* baud rate */ - void (*cs_control)(u32 command); }; #ifdef CONFIG_DEBUG_FS @@ -127,11 +124,6 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) void dw_spi_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_controller_get_devdata(spi->controller); - struct chip_data *chip = spi_get_ctldata(spi); - - /* Chip select logic is inverted from spi_set_cs() */ - if (chip && chip->cs_control) - chip->cs_control(!enable); if (!enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); @@ -265,18 +257,6 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) return dws->transfer_handler(dws); } -/* Must be called inside pump_transfers() */ -static int poll_transfer(struct dw_spi *dws) -{ - do { - dw_writer(dws); - dw_reader(dws); - cpu_relax(); - } while (dws->rx_end > dws->rx); - - return 0; -} - static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { @@ -324,22 +304,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) | (chip->tmode << SPI_TMOD_OFFSET); - /* - * Adjust transfer mode if necessary. Requires platform dependent - * chipselect mechanism. - */ - if (chip->cs_control) { - if (dws->rx && dws->tx) - chip->tmode = SPI_TMOD_TR; - else if (dws->rx) - chip->tmode = SPI_TMOD_RO; - else - chip->tmode = SPI_TMOD_TO; - - cr0 &= ~SPI_TMOD_MASK; - cr0 |= (chip->tmode << SPI_TMOD_OFFSET); - } - dw_writel(dws, DW_SPI_CTRL0, cr0); /* Check if current transfer is a DMA transaction */ @@ -359,7 +323,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_enable_chip(dws, 1); return ret; } - } else if (!chip->poll_mode) { + } else { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); dw_writel(dws, DW_SPI_TXFLTR, txlevel); @@ -379,9 +343,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, return ret; } - if (chip->poll_mode) - return poll_transfer(dws); - return 1; } -- cgit v1.2.3-59-g8ed1b From 858e26a515c28df3ef542d9c09493b54a329d6cf Mon Sep 17 00:00:00 2001 From: Ashish Kumar Date: Thu, 16 Apr 2020 17:13:19 +0530 Subject: spi: spi-fsl-qspi: Reduce devm_ioremap size to 4 times AHB buffer size Reduce devm_ioremap size to (4 * AHB_BUFER_SIZE) rather than mapping complete QSPI-Memmory as driver is now independent of flash size. Flash of any size can be accessed. Issue was reported on platform where devm_ioremap failure is observed with size > 256M. Error log on LS1021ATWR : fsl-quadspi 1550000.spi: ioremap failed for resource [mem 0x40000000-0x7fffffff] fsl-quadspi 1550000.spi: Freescale QuadSPI probe failed fsl-quadspi: probe of 1550000.spi failed with error -12 This change was also suggested previously: https://patchwork.kernel.org/patch/10508753/#22166385 Suggested-by: Boris Brezillon Signed-off-by: Kuldeep Singh Signed-off-by: Ashish Kumar Reviewed-by: Boris Brezillon Reviewed-by: Frieder Schrempf Link: https://lore.kernel.org/r/1587037399-18672-1-git-send-email-Ashish.Kumar@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-qspi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index 02e5cba0a5bb..83eb8a484faf 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -876,14 +876,15 @@ static int fsl_qspi_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI-memory"); - q->ahb_addr = devm_ioremap_resource(dev, res); + q->memmap_phy = res->start; + /* Since there are 4 cs, map size required is 4 times ahb_buf_size */ + q->ahb_addr = devm_ioremap(dev, q->memmap_phy, + (q->devtype_data->ahb_buf_size * 4)); if (IS_ERR(q->ahb_addr)) { ret = PTR_ERR(q->ahb_addr); goto err_put_ctrl; } - q->memmap_phy = res->start; - /* find the clocks */ q->clk_en = devm_clk_get(dev, "qspi_en"); if (IS_ERR(q->clk_en)) { -- cgit v1.2.3-59-g8ed1b From 9d282c17b023a97516a63255ce7f7ee65c39bec1 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 17 Apr 2020 14:12:41 +0200 Subject: spi: stm32-qspi: Add pm_runtime support By default, STM32_AUTOSUSPEND_DELAY is set to -1 which has for effect to prevent runtime suspends. Runtime suspends can be activated by setting autosuspend_delay_ms using sysfs entry : echo {delay_in_ms} > /sys/devices/platform/soc/58003000.spi/power/autosusp end_delay_ms) Signed-off-by: Christophe Kerello Signed-off-by: Patrice Chotard Signed-off-by: Benjamin Gaignard Link: https://lore.kernel.org/r/20200417121241.6473-1-patrice.chotard@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 57 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index d066f5144c3e..2f2ea2c42d6e 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,7 @@ #define STM32_BUSY_TIMEOUT_US 100000 #define STM32_ABT_TIMEOUT_US 100000 #define STM32_COMP_TIMEOUT_MS 1000 +#define STM32_AUTOSUSPEND_DELAY -1 struct stm32_qspi_flash { struct stm32_qspi *qspi; @@ -431,10 +433,17 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master); int ret; + ret = pm_runtime_get_sync(qspi->dev); + if (ret < 0) + return ret; + mutex_lock(&qspi->lock); ret = stm32_qspi_send(mem, op); mutex_unlock(&qspi->lock); + pm_runtime_mark_last_busy(qspi->dev); + pm_runtime_put_autosuspend(qspi->dev); + return ret; } @@ -444,6 +453,7 @@ static int stm32_qspi_setup(struct spi_device *spi) struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl); struct stm32_qspi_flash *flash; u32 presc; + int ret; if (ctrl->busy) return -EBUSY; @@ -451,6 +461,10 @@ static int stm32_qspi_setup(struct spi_device *spi) if (!spi->max_speed_hz) return -EINVAL; + ret = pm_runtime_get_sync(qspi->dev); + if (ret < 0) + return ret; + presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1; flash = &qspi->flash[spi->chip_select]; @@ -467,6 +481,9 @@ static int stm32_qspi_setup(struct spi_device *spi) writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); mutex_unlock(&qspi->lock); + pm_runtime_mark_last_busy(qspi->dev); + pm_runtime_put_autosuspend(qspi->dev); + return 0; } @@ -643,9 +660,20 @@ static int stm32_qspi_probe(struct platform_device *pdev) ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP; ctrl->dev.of_node = dev->of_node; + pm_runtime_set_autosuspend_delay(dev, STM32_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_noresume(dev); + ret = devm_spi_register_master(dev, ctrl); - if (!ret) - return 0; + if (ret) + goto err_qspi_release; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; err_qspi_release: stm32_qspi_release(qspi); @@ -660,14 +688,28 @@ static int stm32_qspi_remove(struct platform_device *pdev) struct stm32_qspi *qspi = platform_get_drvdata(pdev); stm32_qspi_release(qspi); + return 0; } -static int __maybe_unused stm32_qspi_suspend(struct device *dev) +static int __maybe_unused stm32_qspi_runtime_suspend(struct device *dev) { struct stm32_qspi *qspi = dev_get_drvdata(dev); clk_disable_unprepare(qspi->clk); + + return 0; +} + +static int __maybe_unused stm32_qspi_runtime_resume(struct device *dev) +{ + struct stm32_qspi *qspi = dev_get_drvdata(dev); + + return clk_prepare_enable(qspi->clk); +} + +static int __maybe_unused stm32_qspi_suspend(struct device *dev) +{ pinctrl_pm_select_sleep_state(dev); return 0; @@ -683,10 +725,17 @@ static int __maybe_unused stm32_qspi_resume(struct device *dev) writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR); writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); + pm_runtime_mark_last_busy(qspi->dev); + pm_runtime_put_autosuspend(qspi->dev); + return 0; } -static SIMPLE_DEV_PM_OPS(stm32_qspi_pm_ops, stm32_qspi_suspend, stm32_qspi_resume); +static const struct dev_pm_ops stm32_qspi_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_qspi_runtime_suspend, + stm32_qspi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(stm32_qspi_suspend, stm32_qspi_resume) +}; static const struct of_device_id stm32_qspi_match[] = { {.compatible = "st,stm32f469-qspi"}, -- cgit v1.2.3-59-g8ed1b From 59fc9ad5cb108bce18043281c7cf67f2b425d55d Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 17 Apr 2020 15:48:27 +0800 Subject: spi: hisi-sfc-v3xx: add error check after per operation The controller may receive instructions of accessing protected address, or may perform failed page program. These operations will not succeed and the controller will receive interrupts when such failure occur. Previously we don't check the interrupts and return 0 even if such operation fails. Check the interrupts after per command and inform the user if there is an error. Signed-off-by: Yicong Yang Acked-by: John Garry Link: https://lore.kernel.org/r/1587109707-23597-1-git-send-email-yangyicong@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-sfc-v3xx.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index e3b57252d075..64a18d08a4d9 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -17,6 +17,11 @@ #define HISI_SFC_V3XX_VERSION (0x1f8) +#define HISI_SFC_V3XX_INT_STAT (0x120) +#define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2) +#define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5) +#define HISI_SFC_V3XX_INT_CLR (0x12c) +#define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff) #define HISI_SFC_V3XX_CMD_CFG (0x300) #define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17) #define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17) @@ -163,7 +168,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, u8 chip_select) { int ret, len = op->data.nbytes; - u32 config = 0; + u32 int_stat, config = 0; if (op->addr.nbytes) config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; @@ -228,6 +233,25 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, if (ret) return ret; + /* + * The interrupt status register indicates whether an error occurs + * after per operation. Check it, and clear the interrupts for + * next time judgement. + */ + int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); + writel(HISI_SFC_V3XX_INT_CLR_CLEAR, + host->regbase + HISI_SFC_V3XX_INT_CLR); + + if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) { + dev_err(host->dev, "fail to access protected address\n"); + return -EIO; + } + + if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) { + dev_err(host->dev, "page program operation failed\n"); + return -EIO; + } + if (op->data.dir == SPI_MEM_DATA_IN) hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len); -- cgit v1.2.3-59-g8ed1b From 66eb228988cf82abb5f633a7d585120923308caf Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 15 Apr 2020 19:56:13 +0200 Subject: spi: orion: Convert to use GPIO descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This converts the Orion SPI master to use GPIO descriptors. The SPI core will obtain and manage the CS GPIOs, if any are defined. I make one sematic change: when a certain chip select is using a GPIO line instead of the native CS I simply just enable the 1:1 mapped native CS that would have been used if the GPIO was not there. As we set the SPI_MASTER_GPIO_SS the .set_cs() callback will be called for all chip selects whether native or not, and the important thing for the driver is that the previous native chip select (if any) is deasserted, which other chip select is asserted instead does not really matter. The previous code went to great lengths to ascertain that the first hw CS which was hiding behind a GPIO line was used for all cases when the line is not using native chip select but this should not matter at all, just use the one "underneath" the GPIO at all times. When a GPIO is used for CS, the SPI_CS_HIGH flag is enforced, so the native chip select is also inverted. But that should not matter since we are not using it anyways. Signed-off-by: Linus Walleij Cc: Gregory CLEMENT Cc: Tomas Paukrt Cc: Jan Kundrát Link: https://lore.kernel.org/r/20200415175613.220767-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 70 +++++++++++-------------------------------------- 1 file changed, 16 insertions(+), 54 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 1f59beb7d27e..43f73db22f21 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -17,10 +17,8 @@ #include #include #include -#include #include #include -#include #include #define DRIVER_NAME "orion_spi" @@ -98,7 +96,6 @@ struct orion_spi { struct clk *clk; struct clk *axi_clk; const struct orion_spi_dev *devdata; - int unused_hw_gpio; struct orion_child_options child[ORION_NUM_CHIPSELECTS]; }; @@ -325,20 +322,27 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) static void orion_spi_set_cs(struct spi_device *spi, bool enable) { struct orion_spi *orion_spi; - int cs; orion_spi = spi_master_get_devdata(spi->master); - if (gpio_is_valid(spi->cs_gpio)) - cs = orion_spi->unused_hw_gpio; - else - cs = spi->chip_select; - + /* + * If this line is using a GPIO to control chip select, this internal + * .set_cs() function will still be called, so we clear any previous + * chip select. The CS we activate will not have any elecrical effect, + * as it is handled by a GPIO, but that doesn't matter. What we need + * is to deassert the old chip select and assert some other chip select. + */ orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK); orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, - ORION_SPI_CS(cs)); + ORION_SPI_CS(spi->chip_select)); - /* Chip select logic is inverted from spi_set_cs */ + /* + * Chip select logic is inverted from spi_set_cs(). For lines using a + * GPIO to do chip select SPI_CS_HIGH is enforced and inversion happens + * in the GPIO library, but we don't care about that, because in those + * cases we are dealing with an unused native CS anyways so the polarity + * doesn't matter. + */ if (!enable) orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); else @@ -503,9 +507,6 @@ static int orion_spi_transfer_one(struct spi_master *master, static int orion_spi_setup(struct spi_device *spi) { - if (gpio_is_valid(spi->cs_gpio)) { - gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); - } return orion_spi_setup_transfer(spi, NULL); } @@ -622,13 +623,13 @@ static int orion_spi_probe(struct platform_device *pdev) master->setup = orion_spi_setup; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->auto_runtime_pm = true; + master->use_gpio_descriptors = true; master->flags = SPI_MASTER_GPIO_SS; platform_set_drvdata(pdev, master); spi = spi_master_get_devdata(master); spi->master = master; - spi->unused_hw_gpio = -1; of_id = of_match_device(orion_spi_of_match_table, &pdev->dev); devdata = (of_id) ? of_id->data : &orion_spi_dev_data; @@ -683,7 +684,6 @@ static int orion_spi_probe(struct platform_device *pdev) for_each_available_child_of_node(pdev->dev.of_node, np) { struct orion_direct_acc *dir_acc; u32 cs; - int cs_gpio; /* Get chip-select number from the "reg" property */ status = of_property_read_u32(np, "reg", &cs); @@ -694,44 +694,6 @@ static int orion_spi_probe(struct platform_device *pdev) continue; } - /* - * Initialize the CS GPIO: - * - properly request the actual GPIO signal - * - de-assert the logical signal so that all GPIO CS lines - * are inactive when probing for slaves - * - find an unused physical CS which will be driven for any - * slave which uses a CS GPIO - */ - cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", cs); - if (cs_gpio > 0) { - char *gpio_name; - int cs_flags; - - if (spi->unused_hw_gpio == -1) { - dev_info(&pdev->dev, - "Selected unused HW CS#%d for any GPIO CSes\n", - cs); - spi->unused_hw_gpio = cs; - } - - gpio_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "%s-CS%d", dev_name(&pdev->dev), cs); - if (!gpio_name) { - status = -ENOMEM; - goto out_rel_axi_clk; - } - - cs_flags = of_property_read_bool(np, "spi-cs-high") ? - GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; - status = devm_gpio_request_one(&pdev->dev, cs_gpio, - cs_flags, gpio_name); - if (status) { - dev_err(&pdev->dev, - "Can't request GPIO for CS %d\n", cs); - goto out_rel_axi_clk; - } - } - /* * Check if an address is configured for this SPI device. If * not, the MBus mapping via the 'ranges' property in the 'soc' -- cgit v1.2.3-59-g8ed1b From 3a01f04d74ef7597104f3ace8a053761cb2ac349 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 20 Apr 2020 15:08:47 -0400 Subject: spi: bcm-qspi: Handle lack of MSPI_REV offset Older MIPS chips have a QSPI/MSPI controller that does not have the MSPI_REV offset, reading from that offset will cause a bus error. Match their compatible string and do not perform a read from that register in that case. Signed-off-by: Florian Fainelli Signed-off-by: Kamal Dasu Link: https://lore.kernel.org/r/20200420190853.45614-4-kdasu.kdev@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 50 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 23d295f36c80..1f157c35a3a3 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -91,6 +91,7 @@ #define MSPI_MSPI_STATUS 0x020 #define MSPI_CPTQP 0x024 #define MSPI_SPCR3 0x028 +#define MSPI_REV 0x02c #define MSPI_TXRAM 0x040 #define MSPI_RXRAM 0x0c0 #define MSPI_CDRAM 0x140 @@ -217,6 +218,8 @@ struct bcm_qspi { struct bcm_qspi_dev_id *dev_ids; struct completion mspi_done; struct completion bspi_done; + u8 mspi_maj_rev; + u8 mspi_min_rev; }; static inline bool has_bspi(struct bcm_qspi *qspi) @@ -1190,8 +1193,35 @@ static const struct spi_controller_mem_ops bcm_qspi_mem_ops = { .exec_op = bcm_qspi_exec_mem_op, }; +struct bcm_qspi_data { + bool has_mspi_rev; +}; + +static const struct bcm_qspi_data bcm_qspi_no_rev_data = { + .has_mspi_rev = false, +}; + +static const struct bcm_qspi_data bcm_qspi_rev_data = { + .has_mspi_rev = true, +}; + static const struct of_device_id bcm_qspi_of_match[] = { - { .compatible = "brcm,spi-bcm-qspi" }, + { + .compatible = "brcm,spi-bcm7425-qspi", + .data = &bcm_qspi_no_rev_data, + }, + { + .compatible = "brcm,spi-bcm7429-qspi", + .data = &bcm_qspi_no_rev_data, + }, + { + .compatible = "brcm,spi-bcm7435-qspi", + .data = &bcm_qspi_no_rev_data, + }, + { + .compatible = "brcm,spi-bcm-qspi", + .data = &bcm_qspi_rev_data, + }, {}, }; MODULE_DEVICE_TABLE(of, bcm_qspi_of_match); @@ -1199,12 +1229,15 @@ MODULE_DEVICE_TABLE(of, bcm_qspi_of_match); int bcm_qspi_probe(struct platform_device *pdev, struct bcm_qspi_soc_intc *soc_intc) { + const struct of_device_id *of_id = NULL; + const struct bcm_qspi_data *data; struct device *dev = &pdev->dev; struct bcm_qspi *qspi; struct spi_master *master; struct resource *res; int irq, ret = 0, num_ints = 0; u32 val; + u32 rev = 0; const char *name = NULL; int num_irqs = ARRAY_SIZE(qspi_irq_tab); @@ -1212,9 +1245,12 @@ int bcm_qspi_probe(struct platform_device *pdev, if (!dev->of_node) return -ENODEV; - if (!of_match_node(bcm_qspi_of_match, dev->of_node)) + of_id = of_match_node(bcm_qspi_of_match, dev->of_node); + if (!of_id) return -ENODEV; + data = of_id->data; + master = spi_alloc_master(dev, sizeof(struct bcm_qspi)); if (!master) { dev_err(dev, "error allocating spi_master\n"); @@ -1351,6 +1387,16 @@ int bcm_qspi_probe(struct platform_device *pdev, qspi->base_clk = clk_get_rate(qspi->clk); qspi->max_speed_hz = qspi->base_clk / (QSPI_SPBR_MIN * 2); + if (data->has_mspi_rev) { + rev = bcm_qspi_read(qspi, MSPI, MSPI_REV); + /* some older revs do not have a MSPI_REV register */ + if ((rev & 0xff) == 0xff) + rev = 0; + } + + qspi->mspi_maj_rev = (rev >> 4) & 0xf; + qspi->mspi_min_rev = rev & 0xf; + bcm_qspi_hw_init(qspi); init_completion(&qspi->mspi_done); init_completion(&qspi->bspi_done); -- cgit v1.2.3-59-g8ed1b From d9576ae5dcefe75ac8e1655916fa51ba50f9e90b Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Mon, 20 Apr 2020 15:08:51 -0400 Subject: spi: bcm-qspi: Use fastbr setting to allow faster MSPI speeds Setting MSPI_SPCR3.fastbr=1 allows using clock divider (SPBR) values of 1-7, while the default value prohibits these values and requires a minimum clock divider value of 8. Signed-off-by: Kamal Dasu Link: https://lore.kernel.org/r/20200420190853.45614-8-kdasu.kdev@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 1f157c35a3a3..94ff9964a3c7 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -107,13 +107,15 @@ #define MSPI_SPCR2_SPE BIT(6) #define MSPI_SPCR2_CONT_AFTER_CMD BIT(7) +#define MSPI_SPCR3_FASTBR BIT(0) +#define MSPI_SPCR3_FASTDT BIT(1) + #define MSPI_MSPI_STATUS_SPIF BIT(0) #define INTR_BASE_BIT_SHIFT 0x02 #define INTR_COUNT 0x07 #define NUM_CHIPSELECT 4 -#define QSPI_SPBR_MIN 8U #define QSPI_SPBR_MAX 255U #define OPCODE_DIOR 0xBB @@ -227,6 +229,25 @@ static inline bool has_bspi(struct bcm_qspi *qspi) return qspi->bspi_mode; } +/* hardware supports spcr3 and fast baud-rate */ +static inline bool bcm_qspi_has_fastbr(struct bcm_qspi *qspi) +{ + if (!has_bspi(qspi) && + ((qspi->mspi_maj_rev >= 1) && + (qspi->mspi_min_rev >= 5))) + return true; + + return false; +} + +static inline int bcm_qspi_spbr_min(struct bcm_qspi *qspi) +{ + if (bcm_qspi_has_fastbr(qspi)) + return 1; + else + return 8; +} + /* Read qspi controller register*/ static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type, unsigned int offset) @@ -534,7 +555,7 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, if (xp->speed_hz) spbr = qspi->base_clk / (2 * xp->speed_hz); - spcr = clamp_val(spbr, QSPI_SPBR_MIN, QSPI_SPBR_MAX); + spcr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX); bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr); spcr = MSPI_MASTER_BIT; @@ -544,6 +565,14 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, spcr |= xp->mode & 3; bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr); + if (bcm_qspi_has_fastbr(qspi)) { + spcr = 0; + + /* enable fastbr */ + spcr |= MSPI_SPCR3_FASTBR; + bcm_qspi_write(qspi, MSPI, MSPI_SPCR3, spcr); + } + qspi->last_parms = *xp; } @@ -1385,7 +1414,6 @@ int bcm_qspi_probe(struct platform_device *pdev, } qspi->base_clk = clk_get_rate(qspi->clk); - qspi->max_speed_hz = qspi->base_clk / (QSPI_SPBR_MIN * 2); if (data->has_mspi_rev) { rev = bcm_qspi_read(qspi, MSPI, MSPI_REV); @@ -1397,6 +1425,8 @@ int bcm_qspi_probe(struct platform_device *pdev, qspi->mspi_maj_rev = (rev >> 4) & 0xf; qspi->mspi_min_rev = rev & 0xf; + qspi->max_speed_hz = qspi->base_clk / (bcm_qspi_spbr_min(qspi) * 2); + bcm_qspi_hw_init(qspi); init_completion(&qspi->mspi_done); init_completion(&qspi->bspi_done); -- cgit v1.2.3-59-g8ed1b From 43613a77b8d5f2bb36f840afab49d34544fa3b4e Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Mon, 20 Apr 2020 15:08:52 -0400 Subject: spi: bcm-qspi: add support for MSPI sys clk 108Mhz Adding support for MSPI sys clk 108Mhz available on 7216 and 7278 BRCMSTB SoCs. Signed-off-by: Kamal Dasu Link: https://lore.kernel.org/r/20200420190853.45614-9-kdasu.kdev@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 94ff9964a3c7..4b139717ff9e 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -109,6 +109,11 @@ #define MSPI_SPCR3_FASTBR BIT(0) #define MSPI_SPCR3_FASTDT BIT(1) +#define MSPI_SPCR3_SYSCLKSEL_MASK GENMASK(11, 10) +#define MSPI_SPCR3_SYSCLKSEL_27 (MSPI_SPCR3_SYSCLKSEL_MASK & \ + ~(BIT(10) | BIT(11))) +#define MSPI_SPCR3_SYSCLKSEL_108 (MSPI_SPCR3_SYSCLKSEL_MASK & \ + BIT(11)) #define MSPI_MSPI_STATUS_SPIF BIT(0) @@ -117,6 +122,7 @@ #define NUM_CHIPSELECT 4 #define QSPI_SPBR_MAX 255U +#define MSPI_BASE_FREQ 27000000UL #define OPCODE_DIOR 0xBB #define OPCODE_QIOR 0xEB @@ -222,6 +228,7 @@ struct bcm_qspi { struct completion bspi_done; u8 mspi_maj_rev; u8 mspi_min_rev; + bool mspi_spcr3_sysclk; }; static inline bool has_bspi(struct bcm_qspi *qspi) @@ -240,6 +247,17 @@ static inline bool bcm_qspi_has_fastbr(struct bcm_qspi *qspi) return false; } +/* hardware supports sys clk 108Mhz */ +static inline bool bcm_qspi_has_sysclk_108(struct bcm_qspi *qspi) +{ + if (!has_bspi(qspi) && (qspi->mspi_spcr3_sysclk || + ((qspi->mspi_maj_rev >= 1) && + (qspi->mspi_min_rev >= 6)))) + return true; + + return false; +} + static inline int bcm_qspi_spbr_min(struct bcm_qspi *qspi) { if (bcm_qspi_has_fastbr(qspi)) @@ -570,6 +588,15 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, /* enable fastbr */ spcr |= MSPI_SPCR3_FASTBR; + + if (bcm_qspi_has_sysclk_108(qspi)) { + /* SYSCLK_108 */ + spcr |= MSPI_SPCR3_SYSCLKSEL_108; + qspi->base_clk = MSPI_BASE_FREQ * 4; + /* Change spbr as we changed sysclk */ + bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, 4); + } + bcm_qspi_write(qspi, MSPI, MSPI_SPCR3, spcr); } @@ -1224,14 +1251,22 @@ static const struct spi_controller_mem_ops bcm_qspi_mem_ops = { struct bcm_qspi_data { bool has_mspi_rev; + bool has_spcr3_sysclk; }; static const struct bcm_qspi_data bcm_qspi_no_rev_data = { .has_mspi_rev = false, + .has_spcr3_sysclk = false, }; static const struct bcm_qspi_data bcm_qspi_rev_data = { .has_mspi_rev = true, + .has_spcr3_sysclk = false, +}; + +static const struct bcm_qspi_data bcm_qspi_spcr3_data = { + .has_mspi_rev = true, + .has_spcr3_sysclk = true, }; static const struct of_device_id bcm_qspi_of_match[] = { @@ -1251,6 +1286,14 @@ static const struct of_device_id bcm_qspi_of_match[] = { .compatible = "brcm,spi-bcm-qspi", .data = &bcm_qspi_rev_data, }, + { + .compatible = "brcm,spi-bcm7216-qspi", + .data = &bcm_qspi_spcr3_data, + }, + { + .compatible = "brcm,spi-bcm7278-qspi", + .data = &bcm_qspi_spcr3_data, + }, {}, }; MODULE_DEVICE_TABLE(of, bcm_qspi_of_match); @@ -1424,6 +1467,7 @@ int bcm_qspi_probe(struct platform_device *pdev, qspi->mspi_maj_rev = (rev >> 4) & 0xf; qspi->mspi_min_rev = rev & 0xf; + qspi->mspi_spcr3_sysclk = data->has_spcr3_sysclk; qspi->max_speed_hz = qspi->base_clk / (bcm_qspi_spbr_min(qspi) * 2); -- cgit v1.2.3-59-g8ed1b From 2f5f5302c569f773f58d7f31db2c9e8bbbacc6a7 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Mon, 20 Apr 2020 15:08:53 -0400 Subject: spi: bcm-qspi: MSPI_SPCR0_MSB MSTR bit exists only on legacy controllers Set MASTER bit on the MSPI_SPCR0_MSB only for legacy MSPI and HIF_MSPI controllers. Fixes: fa236a7ef240 ("spi: bcm-qspi: Add Broadcom MSPI driver") Signed-off-by: Kamal Dasu Link: https://lore.kernel.org/r/20200420190853.45614-10-kdasu.kdev@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 4b139717ff9e..23cad49e7405 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -576,11 +576,17 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, spcr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX); bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr); - spcr = MSPI_MASTER_BIT; + if (!qspi->mspi_maj_rev) + /* legacy controller */ + spcr = MSPI_MASTER_BIT; + else + spcr = 0; + /* for 16 bit the data should be zero */ if (xp->bits_per_word != 16) spcr |= xp->bits_per_word << 2; spcr |= xp->mode & 3; + bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr); if (bcm_qspi_has_fastbr(qspi)) { -- cgit v1.2.3-59-g8ed1b From f58dcab68aa57e74c12c9bec7ff1bb73e0b35143 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 22 Apr 2020 01:45:43 +0000 Subject: spi: spi-fsl-qspi: Fix return value check of devm_ioremap() in probe In case of error, the function devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Fixes: 858e26a515c2 ("spi: spi-fsl-qspi: Reduce devm_ioremap size to 4 times AHB buffer size") Signed-off-by: Wei Yongjun Reviewed-by: Ashish Kumar Acked-by: Han Xu Link: https://lore.kernel.org/r/20200422014543.111070-1-weiyongjun1@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-qspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index 83eb8a484faf..6766262d7e75 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -880,8 +880,8 @@ static int fsl_qspi_probe(struct platform_device *pdev) /* Since there are 4 cs, map size required is 4 times ahb_buf_size */ q->ahb_addr = devm_ioremap(dev, q->memmap_phy, (q->devtype_data->ahb_buf_size * 4)); - if (IS_ERR(q->ahb_addr)) { - ret = PTR_ERR(q->ahb_addr); + if (!q->ahb_addr) { + ret = -ENOMEM; goto err_put_ctrl; } -- cgit v1.2.3-59-g8ed1b From 80300a7d5f2d7178335652f41d2e55ba898b4ec1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 16 Apr 2020 12:14:18 +0200 Subject: spi: spi-mem: Fix Dual/Quad modes on Octal-capable devices Currently buswidths 2 and 4 are rejected for a device that advertises Octal capabilities. Allow these buswidths, just like is done for buswidth 2 and Quad-capable devices. Fixes: b12a084c8729ef42 ("spi: spi-mem: add support for octal mode I/O data transfer") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200416101418.14379-1-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index adaa0c49f966..9a86cc27fcc0 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -108,15 +108,17 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx) return 0; case 2: - if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) || - (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD)))) + if ((tx && + (mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL))) || + (!tx && + (mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL)))) return 0; break; case 4: - if ((tx && (mode & SPI_TX_QUAD)) || - (!tx && (mode & SPI_RX_QUAD))) + if ((tx && (mode & (SPI_TX_QUAD | SPI_TX_OCTAL))) || + (!tx && (mode & (SPI_RX_QUAD | SPI_RX_OCTAL)))) return 0; break; -- cgit v1.2.3-59-g8ed1b From dc234825997ec6ff05980ca9e2204f4ac3f8d695 Mon Sep 17 00:00:00 2001 From: Peng Ma Date: Fri, 24 Apr 2020 14:12:16 +0800 Subject: spi: spi-fsl-dspi: Adding shutdown hook We need to ensure dspi controller could be stopped in order for kexec to start the next kernel. So add the shutdown operation support. Signed-off-by: Peng Ma Link: https://lore.kernel.org/r/20200424061216.27445-1-peng.ma@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 50e41f66a2d7..685afdf9e807 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ // // Copyright 2013 Freescale Semiconductor, Inc. +// Copyright 2020 NXP // // Freescale DSPI driver // This file contains a driver for the Freescale DSPI @@ -26,6 +27,9 @@ #define SPI_MCR_CLR_TXF BIT(11) #define SPI_MCR_CLR_RXF BIT(10) #define SPI_MCR_XSPI BIT(3) +#define SPI_MCR_DIS_TXF BIT(13) +#define SPI_MCR_DIS_RXF BIT(12) +#define SPI_MCR_HALT BIT(0) #define SPI_TCR 0x08 #define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16) @@ -1417,6 +1421,24 @@ static int dspi_remove(struct platform_device *pdev) return 0; } +static void dspi_shutdown(struct platform_device *pdev) +{ + struct spi_controller *ctlr = platform_get_drvdata(pdev); + struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr); + + /* Disable RX and TX */ + regmap_update_bits(dspi->regmap, SPI_MCR, + SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF, + SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF); + + /* Stop Running */ + regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_HALT, SPI_MCR_HALT); + + dspi_release_dma(dspi); + clk_disable_unprepare(dspi->clk); + spi_unregister_controller(dspi->ctlr); +} + static struct platform_driver fsl_dspi_driver = { .driver.name = DRIVER_NAME, .driver.of_match_table = fsl_dspi_dt_ids, @@ -1424,6 +1446,7 @@ static struct platform_driver fsl_dspi_driver = { .driver.pm = &dspi_pm, .probe = dspi_probe, .remove = dspi_remove, + .shutdown = dspi_shutdown, }; module_platform_driver(fsl_dspi_driver); -- cgit v1.2.3-59-g8ed1b From bbb336f39efcb1b5498f65f59f9af5773ad6dc2b Mon Sep 17 00:00:00 2001 From: Sanjay R Mehta Date: Sat, 25 Apr 2020 14:59:48 -0500 Subject: spi: spi-amd: Add AMD SPI controller driver support This driver supports SPI Controller for AMD SOCs.This driver supports SPI operations using FIFO mode of transfer. Signed-off-by: Sanjay R Mehta Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/1587844788-33997-1-git-send-email-sanju.mehta@amd.com Signed-off-by: Mark Brown --- MAINTAINERS | 5 + drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-amd.c | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 345 insertions(+) create mode 100644 drivers/spi/spi-amd.c (limited to 'drivers/spi') diff --git a/MAINTAINERS b/MAINTAINERS index 26f281d9f32a..8ff2c0333b56 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -892,6 +892,11 @@ F: drivers/gpu/drm/amd/include/v9_structs.h F: drivers/gpu/drm/amd/include/vi_structs.h F: include/uapi/linux/kfd_ioctl.h +AMD SPI DRIVER +M: Sanjay R Mehta +S: Maintained +F: drivers/spi/spi-amd.c + AMD MP2 I2C DRIVER M: Elie Morisse M: Nehal Shah diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 741b9140992a..17e7e4afc0fc 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -910,6 +910,12 @@ config SPI_ZYNQMP_GQSPI help Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC. +config SPI_AMD + tristate "AMD SPI controller" + depends on SPI_MASTER || COMPILE_TEST + help + Enables SPI controller driver for AMD SoC. + # # Add new SPI master controllers in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 28f601327f8c..889368f4ad53 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -127,6 +127,7 @@ obj-$(CONFIG_SPI_XLP) += spi-xlp.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o +obj-$(CONFIG_SPI_AMD) += spi-amd.o # SPI slave protocol handlers obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c new file mode 100644 index 000000000000..0d9debe1386e --- /dev/null +++ b/drivers/spi/spi-amd.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// +// AMD SPI controller driver +// +// Copyright (c) 2020, Advanced Micro Devices, Inc. +// +// Author: Sanjay R Mehta + +#include +#include +#include +#include +#include +#include + +#define AMD_SPI_CTRL0_REG 0x00 +#define AMD_SPI_EXEC_CMD BIT(16) +#define AMD_SPI_FIFO_CLEAR BIT(20) +#define AMD_SPI_BUSY BIT(31) + +#define AMD_SPI_OPCODE_MASK 0xFF + +#define AMD_SPI_ALT_CS_REG 0x1D +#define AMD_SPI_ALT_CS_MASK 0x3 + +#define AMD_SPI_FIFO_BASE 0x80 +#define AMD_SPI_TX_COUNT_REG 0x48 +#define AMD_SPI_RX_COUNT_REG 0x4B +#define AMD_SPI_STATUS_REG 0x4C + +#define AMD_SPI_MEM_SIZE 200 + +/* M_CMD OP codes for SPI */ +#define AMD_SPI_XFER_TX 1 +#define AMD_SPI_XFER_RX 2 + +struct amd_spi { + void __iomem *io_remap_addr; + unsigned long io_base_addr; + u32 rom_addr; + struct spi_master *master; + u8 chip_select; +}; + +static inline u8 amd_spi_readreg8(struct spi_master *master, int idx) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx); +} + +static inline void amd_spi_writereg8(struct spi_master *master, int idx, + u8 val) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); +} + +static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx, + u8 set, u8 clear) +{ + u8 tmp = amd_spi_readreg8(master, idx); + + tmp = (tmp & ~clear) | set; + amd_spi_writereg8(master, idx, tmp); +} + +static inline u32 amd_spi_readreg32(struct spi_master *master, int idx) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx); +} + +static inline void amd_spi_writereg32(struct spi_master *master, int idx, + u32 val) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); +} + +static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx, + u32 set, u32 clear) +{ + u32 tmp = amd_spi_readreg32(master, idx); + + tmp = (tmp & ~clear) | set; + amd_spi_writereg32(master, idx, tmp); +} + +static void amd_spi_select_chip(struct spi_master *master) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + u8 chip_select = amd_spi->chip_select; + + amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select, + AMD_SPI_ALT_CS_MASK); +} + +static void amd_spi_clear_fifo_ptr(struct spi_master *master) +{ + amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, + AMD_SPI_FIFO_CLEAR); +} + +static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode) +{ + amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode, + AMD_SPI_OPCODE_MASK); +} + +static inline void amd_spi_set_rx_count(struct spi_master *master, + u8 rx_count) +{ + amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); +} + +static inline void amd_spi_set_tx_count(struct spi_master *master, + u8 tx_count) +{ + amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); +} + +static inline int amd_spi_busy_wait(struct amd_spi *amd_spi) +{ + bool spi_busy; + int timeout = 100000; + + /* poll for SPI bus to become idle */ + spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; + while (spi_busy) { + usleep_range(10, 20); + if (timeout-- < 0) + return -ETIMEDOUT; + + spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; + } + + return 0; +} + +static void amd_spi_execute_opcode(struct spi_master *master) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + /* Set ExecuteOpCode bit in the CTRL0 register */ + amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, + AMD_SPI_EXEC_CMD); + + amd_spi_busy_wait(amd_spi); +} + +static int amd_spi_master_setup(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + + amd_spi_clear_fifo_ptr(master); + + return 0; +} + +static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, + struct spi_message *message) +{ + struct spi_master *master = amd_spi->master; + struct spi_transfer *xfer = NULL; + u8 cmd_opcode, opcode = 0; + u8 *buf = NULL; + u32 m_cmd = 0; + u32 i = 0; + u32 tx_len = 0, rx_len = 0; + + list_for_each_entry(xfer, &message->transfers, + transfer_list) { + if (xfer->rx_buf) + m_cmd = AMD_SPI_XFER_RX; + if (xfer->tx_buf) + m_cmd = AMD_SPI_XFER_TX; + + if (m_cmd & AMD_SPI_XFER_TX) { + buf = (u8 *)xfer->tx_buf; + tx_len = xfer->len - 1; + cmd_opcode = *(u8 *)xfer->tx_buf; + buf++; + amd_spi_set_opcode(master, cmd_opcode); + + /* Write data into the FIFO. */ + for (i = 0; i < tx_len; i++) { + iowrite8(buf[i], + ((u8 __iomem *)amd_spi->io_remap_addr + + AMD_SPI_FIFO_BASE + i)); + } + + amd_spi_set_tx_count(master, tx_len); + amd_spi_clear_fifo_ptr(master); + /* Execute command */ + amd_spi_execute_opcode(master); + } + if (m_cmd & AMD_SPI_XFER_RX) { + /* + * Store no. of bytes to be received from + * FIFO + */ + rx_len = xfer->len; + buf = (u8 *)xfer->rx_buf; + amd_spi_set_rx_count(master, rx_len); + amd_spi_clear_fifo_ptr(master); + /* Execute command */ + amd_spi_execute_opcode(master); + /* Read data from FIFO to receive buffer */ + for (i = 0; i < rx_len; i++) + buf[i] = amd_spi_readreg8(master, + AMD_SPI_FIFO_BASE + + tx_len + i); + } + } + + /* Update statistics */ + message->actual_length = tx_len + rx_len + 1; + /* complete the transaction */ + message->status = 0; + spi_finalize_current_message(master); + + return 0; +} + +static int amd_spi_master_transfer(struct spi_master *master, + struct spi_message *msg) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + struct spi_device *spi = msg->spi; + + amd_spi->chip_select = spi->chip_select; + amd_spi_select_chip(master); + + /* + * Extract spi_transfers from the spi message and + * program the controller. + */ + amd_spi_fifo_xfer(amd_spi, msg); + + return 0; +} + +static int amd_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_master *master; + struct amd_spi *amd_spi; + struct resource *res; + int err = 0; + + /* Allocate storage for spi_master and driver private data */ + master = spi_alloc_master(dev, sizeof(struct amd_spi)); + if (!master) { + dev_err(dev, "Error allocating SPI master\n"); + return -ENOMEM; + } + + amd_spi = spi_master_get_devdata(master); + amd_spi->master = master; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + amd_spi->io_remap_addr = devm_ioremap_resource(&pdev->dev, res); + + if (!amd_spi->io_remap_addr) { + dev_err(dev, "error %d ioremap of SPI registers failed\n", err); + err = -ENOMEM; + goto err_free_master; + } + dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); + + /* Initialize the spi_master fields */ + master->bus_num = 0; + master->num_chipselect = 4; + master->mode_bits = 0; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->setup = amd_spi_master_setup; + master->transfer_one_message = amd_spi_master_transfer; + + /* Register the controller with SPI framework */ + err = spi_register_master(master); + if (err) { + dev_err(dev, "error %d registering SPI controller\n", err); + goto err_iounmap; + } + platform_set_drvdata(pdev, amd_spi); + + return 0; + +err_iounmap: + iounmap(amd_spi->io_remap_addr); +err_free_master: + spi_master_put(master); + + return 0; +} + +static int amd_spi_remove(struct platform_device *pdev) +{ + struct amd_spi *amd_spi = platform_get_drvdata(pdev); + + spi_unregister_master(amd_spi->master); + spi_master_put(amd_spi->master); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct acpi_device_id spi_acpi_match[] = { + { "AMDI0061", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, spi_acpi_match); + +static struct platform_driver amd_spi_driver = { + .driver = { + .name = "amd_spi", + .acpi_match_table = ACPI_PTR(spi_acpi_match), + }, + .probe = amd_spi_probe, + .remove = amd_spi_remove, +}; + +module_platform_driver(amd_spi_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Sanjay Mehta "); +MODULE_DESCRIPTION("AMD SPI Master Controller Driver"); -- cgit v1.2.3-59-g8ed1b From 68d047cb0a20f612506c6cf3a98e3600a7d4371f Mon Sep 17 00:00:00 2001 From: Sanjay R Mehta Date: Mon, 27 Apr 2020 23:56:41 -0500 Subject: spi: spi-amd: fix warning remove unused variable "opcode" Signed-off-by: Sanjay R Mehta Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/1588049801-37995-1-git-send-email-sanju.mehta@amd.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 0d9debe1386e..c5a16dd44fa3 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -168,7 +168,7 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, { struct spi_master *master = amd_spi->master; struct spi_transfer *xfer = NULL; - u8 cmd_opcode, opcode = 0; + u8 cmd_opcode; u8 *buf = NULL; u32 m_cmd = 0; u32 i = 0; -- cgit v1.2.3-59-g8ed1b From f84b604dbaf242d17135f527dbe81093a41226b3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 29 Apr 2020 02:54:26 +0000 Subject: spi: spi-amd: Fix a NULL vs IS_ERR() check in amd_spi_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Fixes: bbb336f39efc ("spi: spi-amd: Add AMD SPI controller driver support") Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20200429025426.167664-1-weiyongjun1@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index c5a16dd44fa3..c7cfc3dc20b1 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -266,10 +266,9 @@ static int amd_spi_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); amd_spi->io_remap_addr = devm_ioremap_resource(&pdev->dev, res); - - if (!amd_spi->io_remap_addr) { + if (IS_ERR(amd_spi->io_remap_addr)) { + err = PTR_ERR(amd_spi->io_remap_addr); dev_err(dev, "error %d ioremap of SPI registers failed\n", err); - err = -ENOMEM; goto err_free_master; } dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); -- cgit v1.2.3-59-g8ed1b From 18168291aa7dd48e9d9e3cbb1d4cd06e899357ca Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 29 Apr 2020 07:58:55 +0000 Subject: spi: uniphier: fix error return code in uniphier_spi_probe() Fix to return negative error code -EPROBE_DEFER from the DMA probe defer error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20200429075855.104487-1-weiyongjun1@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-uniphier.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 0fa50979644d..0457d3376873 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -716,8 +716,10 @@ static int uniphier_spi_probe(struct platform_device *pdev) 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) + if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; goto out_disable_clk; + } master->dma_tx = NULL; dma_tx_burst = INT_MAX; } else { @@ -732,8 +734,10 @@ static int uniphier_spi_probe(struct platform_device *pdev) 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) + if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; goto out_disable_clk; + } master->dma_rx = NULL; dma_rx_burst = INT_MAX; } else { -- cgit v1.2.3-59-g8ed1b From be6ef160840f23d9723d9bd008ca08e864ce4745 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 29 Apr 2020 12:26:25 +0200 Subject: spi: stm32-qspi: Fix unbalanced pm_runtime_enable issue Issue detected by unbinding/binding the stm32 qspi driver as following: root@stm32mp2:~# echo 40430000.spi > /sys/bus/platform/drivers/stm32-qspi/404300 00.spi/driver/unbind root@stm32mp2:~# echo 40430000.spi > /sys/bus/platform/drivers/stm32-qspi/bind [ 969.864021] stm32-qspi 40430000.spi: Unbalanced pm_runtime_enable! [ 970.225161] spi-nor spi0.0: mx66u51235f (65536 Kbytes) [ 970.935721] spi-nor spi0.1: mx66u51235f (65536 Kbytes) Fixes: 9d282c17b023 ("spi: stm32-qspi: Add pm_runtime support") Signed-off-by: Patrice Chotard Link: https://lore.kernel.org/r/20200429102625.25974-1-patrice.chotard@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 2f2ea2c42d6e..3c44bb2fd9b1 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -555,10 +555,15 @@ static const struct spi_controller_mem_ops stm32_qspi_mem_ops = { static void stm32_qspi_release(struct stm32_qspi *qspi) { + pm_runtime_get_sync(qspi->dev); /* disable qspi */ writel_relaxed(0, qspi->io_base + QSPI_CR); stm32_qspi_dma_free(qspi); mutex_destroy(&qspi->lock); + pm_runtime_put_noidle(qspi->dev); + pm_runtime_disable(qspi->dev); + pm_runtime_set_suspended(qspi->dev); + pm_runtime_dont_use_autosuspend(qspi->dev); clk_disable_unprepare(qspi->clk); } -- cgit v1.2.3-59-g8ed1b From 6eefaee4f2d366a389da0eb95e524ba82bf358c4 Mon Sep 17 00:00:00 2001 From: Evan Green Date: Mon, 27 Apr 2020 16:32:48 -0700 Subject: spi: pxa2xx: Apply CS clk quirk to BXT With a couple allies at Intel, and much badgering, I got confirmation from Intel that at least BXT suffers from the same SPI chip-select issue as Cannonlake (and beyond). The issue being that after going through runtime suspend/resume, toggling the chip-select line without also sending data does nothing. Add the quirk to BXT to briefly toggle dynamic clock gating off and on, forcing the fabric to wake up enough to notice the CS register change. Signed-off-by: Evan Green Cc: Shobhit Srivastava Cc: Andy Shevchenko Link: https://lore.kernel.org/r/20200427163238.1.Ib1faaabe236e37ea73be9b8dcc6aa034cb3c8804@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 73d2a65d0b6e..20dcbd35611a 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -150,6 +150,7 @@ static const struct lpss_config lpss_platforms[] = { .tx_threshold_hi = 48, .cs_sel_shift = 8, .cs_sel_mask = 3 << 8, + .cs_clk_stays_gated = true, }, { /* LPSS_CNL_SSP */ .offset = 0x200, -- cgit v1.2.3-59-g8ed1b From b68527dfa952b702142520724d29826b8eb29a31 Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Wed, 29 Apr 2020 23:57:23 +0100 Subject: spi: atmel: Add missing annotation for atmel_spi_next_xfer_dma_submit() Sparse reports a warning at atmel_spi_next_xfer_dma_submit() warning: context imbalance in atmel_spi_next_xfer_dma_submit() - unexpected unlock The root cause is the missing annotation at atmel_spi_next_xfer_dma_submit() Add the missing __must_hold(&as->lock) annotation Signed-off-by: Jules Irenge Link: https://lore.kernel.org/r/20200429225723.31258-3-jbi.octave@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 013458cabe3c..57ee8c3b7972 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -706,6 +706,7 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master, static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, struct spi_transfer *xfer, u32 *plen) + __must_hold(&as->lock) { struct atmel_spi *as = spi_master_get_devdata(master); struct dma_chan *rxchan = master->dma_rx; -- cgit v1.2.3-59-g8ed1b From 2b60c49f3ca0648c5b02b60dc0f5b9e2c274bfb5 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 4 May 2020 13:12:01 +0200 Subject: spi: amd: Fix duplicate iounmap in error path The AMD SPI driver uses devm_ioremap_resource() to map its registers, so they're automatically unmapped via device_release() when the last ref on the SPI controller is dropped. The additional iounmap() in the ->probe() error path is thus unnecessary. Signed-off-by: Lukas Wunner Link: https://lore.kernel.org/r/497cc38ae2beb7900ae05a1463eb83ff96e2770e.1588590210.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index c7cfc3dc20b1..65b53eee5be9 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -285,14 +285,12 @@ static int amd_spi_probe(struct platform_device *pdev) err = spi_register_master(master); if (err) { dev_err(dev, "error %d registering SPI controller\n", err); - goto err_iounmap; + goto err_free_master; } platform_set_drvdata(pdev, amd_spi); return 0; -err_iounmap: - iounmap(amd_spi->io_remap_addr); err_free_master: spi_master_put(master); -- cgit v1.2.3-59-g8ed1b From cc17fbec2e785926dafce65d014f8301847dff40 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 4 May 2020 13:12:02 +0200 Subject: spi: amd: Pass probe errors back to driver core If probing fails, the AMD SPI driver pretends success to the driver core by returning 0. Return the errno instead. Signed-off-by: Lukas Wunner Link: https://lore.kernel.org/r/689f29a359718dab4f5de9ee66c02ea97b3bd9e8.1588590210.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 65b53eee5be9..a4248b97b67e 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -294,7 +294,7 @@ static int amd_spi_probe(struct platform_device *pdev) err_free_master: spi_master_put(master); - return 0; + return err; } static int amd_spi_remove(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 4332ea8f40c80d51a534f194291bf3b7738a7beb Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 4 May 2020 13:12:03 +0200 Subject: spi: amd: Drop duplicate driver data assignments The AMD SPI driver calls platform_set_drvdata() on probe even though it's already been set by __spi_alloc_controller(). Likewise, it calls platform_set_drvdata() on remove even though it's going to be set by __device_release_driver(). Drop the duplicate assignments. Signed-off-by: Lukas Wunner Link: https://lore.kernel.org/r/499f8ad4759c2ff0f586e0459fb9a293faecff6d.1588590210.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index a4248b97b67e..d3e3516ef957 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -287,7 +287,6 @@ static int amd_spi_probe(struct platform_device *pdev) dev_err(dev, "error %d registering SPI controller\n", err); goto err_free_master; } - platform_set_drvdata(pdev, amd_spi); return 0; @@ -303,7 +302,6 @@ static int amd_spi_remove(struct platform_device *pdev) spi_unregister_master(amd_spi->master); spi_master_put(amd_spi->master); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3-59-g8ed1b From 7b9c94bd13cc9dc9c0c4932ebacf756ae612d52a Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 4 May 2020 13:12:04 +0200 Subject: spi: amd: Fix refcount underflow on remove The AMD SPI driver calls spi_master_put() in its ->remove() hook even though the preceding call to spi_unregister_master() already drops a ref, thus leading to a refcount underflow. Drop the superfluous call to spi_master_put(). This only leaves the call to spi_unregister_master() in the ->remove() hook, so it's safe to change the ->probe() hook to use the devm version of spi_register_master() and drop the ->remove() hook altogether. Signed-off-by: Lukas Wunner Link: https://lore.kernel.org/r/5e53ccdf1eecd4e015dba99d0d77389107f8a2e3.1588590210.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index d3e3516ef957..00f2f3405e08 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -282,7 +282,7 @@ static int amd_spi_probe(struct platform_device *pdev) master->transfer_one_message = amd_spi_master_transfer; /* Register the controller with SPI framework */ - err = spi_register_master(master); + err = devm_spi_register_master(dev, master); if (err) { dev_err(dev, "error %d registering SPI controller\n", err); goto err_free_master; @@ -296,16 +296,6 @@ err_free_master: return err; } -static int amd_spi_remove(struct platform_device *pdev) -{ - struct amd_spi *amd_spi = platform_get_drvdata(pdev); - - spi_unregister_master(amd_spi->master); - spi_master_put(amd_spi->master); - - return 0; -} - static const struct acpi_device_id spi_acpi_match[] = { { "AMDI0061", 0 }, {}, @@ -318,7 +308,6 @@ static struct platform_driver amd_spi_driver = { .acpi_match_table = ACPI_PTR(spi_acpi_match), }, .probe = amd_spi_probe, - .remove = amd_spi_remove, }; module_platform_driver(amd_spi_driver); -- cgit v1.2.3-59-g8ed1b From 36c72a58d472b4032e09f165ea064a0251c9df07 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 4 May 2020 13:12:05 +0200 Subject: spi: amd: Drop superfluous member from struct amd_spi The AMD SPI driver stores a pointer to the spi_master in struct amd_spi so that it can get from the latter to the former in amd_spi_fifo_xfer(). It's simpler to just pass the pointer from the sole caller amd_spi_master_transfer() and drop the pointer from struct amd_spi. Signed-off-by: Lukas Wunner Link: https://lore.kernel.org/r/a088b684ad292faf3bd036e51529e608e5c94638.1588590210.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 00f2f3405e08..d0aacd4de1b9 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -38,7 +38,6 @@ struct amd_spi { void __iomem *io_remap_addr; unsigned long io_base_addr; u32 rom_addr; - struct spi_master *master; u8 chip_select; }; @@ -164,9 +163,9 @@ static int amd_spi_master_setup(struct spi_device *spi) } static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, + struct spi_master *master, struct spi_message *message) { - struct spi_master *master = amd_spi->master; struct spi_transfer *xfer = NULL; u8 cmd_opcode; u8 *buf = NULL; @@ -241,7 +240,7 @@ static int amd_spi_master_transfer(struct spi_master *master, * Extract spi_transfers from the spi message and * program the controller. */ - amd_spi_fifo_xfer(amd_spi, msg); + amd_spi_fifo_xfer(amd_spi, master, msg); return 0; } @@ -262,7 +261,6 @@ static int amd_spi_probe(struct platform_device *pdev) } amd_spi = spi_master_get_devdata(master); - amd_spi->master = master; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); amd_spi->io_remap_addr = devm_ioremap_resource(&pdev->dev, res); -- cgit v1.2.3-59-g8ed1b From e37687c98ab9ee5872e672b81c44932cf5356d58 Mon Sep 17 00:00:00 2001 From: Jacko Dirks Date: Sun, 3 May 2020 22:00:33 +0200 Subject: spi: bcm2835: Fixes bare use of unsigned Signed-off-by: Jacko Dirks Link: https://lore.kernel.org/r/20200503200033.GA3256@vasteMachine Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 11c235879bb7..e10b8f3b4bab 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -191,12 +191,12 @@ static void bcm2835_debugfs_remove(struct bcm2835_spi *bs) } #endif /* CONFIG_DEBUG_FS */ -static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) +static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned int reg) { return readl(bs->regs + reg); } -static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val) +static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned int reg, u32 val) { writel(val, bs->regs + reg); } -- cgit v1.2.3-59-g8ed1b From 299cb65c9dd4791242a102f216583773d962c1ac Mon Sep 17 00:00:00 2001 From: Wan Ahmad Zainie Date: Tue, 5 May 2020 21:06:12 +0800 Subject: spi: dw: Fix typo in few registers name This patch will fix typo in the register name used in the source code, to be consistent with the register name used in the databook. Databook: DW_apb_ssi_databook.pdf version 4.01a Signed-off-by: Wan Ahmad Zainie Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200505130618.554-2-wan.ahmad.zainie.wan.mohamad@intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 18 +++++++++--------- drivers/spi/spi-dw.h | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 2b79c5a983c0..72a1c99ce9e6 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -47,9 +47,9 @@ static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf, len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, "=================================\n"); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRL0: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL0)); + "CTRLR0: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR0)); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRL1: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL1)); + "CTRLR1: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR1)); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, "SSIENR: \t0x%08x\n", dw_readl(dws, DW_SPI_SSIENR)); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, @@ -57,9 +57,9 @@ static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf, len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, "BAUDR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_BAUDR)); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFLTR)); + "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFTLR)); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFLTR)); + "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFTLR)); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, "TXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_TXFLR)); len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, @@ -304,7 +304,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) | (chip->tmode << SPI_TMOD_OFFSET); - dw_writel(dws, DW_SPI_CTRL0, cr0); + dw_writel(dws, DW_SPI_CTRLR0, cr0); /* Check if current transfer is a DMA transaction */ if (master->can_dma && master->can_dma(master, spi, transfer)) @@ -325,7 +325,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, } } else { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); - dw_writel(dws, DW_SPI_TXFLTR, txlevel); + dw_writel(dws, DW_SPI_TXFTLR, txlevel); /* Set the interrupt mask */ imask |= SPI_INT_TXEI | SPI_INT_TXOI | @@ -397,11 +397,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) u32 fifo; for (fifo = 1; fifo < 256; fifo++) { - dw_writel(dws, DW_SPI_TXFLTR, fifo); - if (fifo != dw_readl(dws, DW_SPI_TXFLTR)) + dw_writel(dws, DW_SPI_TXFTLR, fifo); + if (fifo != dw_readl(dws, DW_SPI_TXFTLR)) break; } - dw_writel(dws, DW_SPI_TXFLTR, 0); + dw_writel(dws, DW_SPI_TXFTLR, 0); dws->fifo_len = (fifo == 1) ? 0 : fifo; dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 44ef18187c15..6c34720b1b1d 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -6,14 +6,14 @@ #include /* Register offsets */ -#define DW_SPI_CTRL0 0x00 -#define DW_SPI_CTRL1 0x04 +#define DW_SPI_CTRLR0 0x00 +#define DW_SPI_CTRLR1 0x04 #define DW_SPI_SSIENR 0x08 #define DW_SPI_MWCR 0x0c #define DW_SPI_SER 0x10 #define DW_SPI_BAUDR 0x14 -#define DW_SPI_TXFLTR 0x18 -#define DW_SPI_RXFLTR 0x1c +#define DW_SPI_TXFTLR 0x18 +#define DW_SPI_RXFTLR 0x1c #define DW_SPI_TXFLR 0x20 #define DW_SPI_RXFLR 0x24 #define DW_SPI_SR 0x28 -- cgit v1.2.3-59-g8ed1b From c4eadee21fa9afd3dc9dd867c71b642177bf671f Mon Sep 17 00:00:00 2001 From: Wan Ahmad Zainie Date: Tue, 5 May 2020 21:06:13 +0800 Subject: spi: dw: Add update_cr0() callback to update CTRLR0 This patch adds update_cr0() callback, in struct dw_spi. Existing code that configure register CTRLR0 is moved into a new function, dw_spi_update_cr0(), and this will be the default. Suggested-by: Andy Shevchenko Signed-off-by: Wan Ahmad Zainie Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200505130618.554-3-wan.ahmad.zainie.wan.mohamad@intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 4 ++++ drivers/spi/spi-dw-mmio.c | 21 ++++++++++++++++++--- drivers/spi/spi-dw.c | 29 +++++++++++++++++++++-------- drivers/spi/spi-dw.h | 5 +++++ 4 files changed, 48 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 0d86c37e0aeb..9cc010e9737e 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -318,5 +318,9 @@ int dw_spi_mid_init(struct dw_spi *dws) dws->dma_rx = &mid_dma_rx; dws->dma_ops = &mid_dma_ops; #endif + + /* Register hook to configure CTRLR0 */ + dws->update_cr0 = dw_spi_update_cr0; + return 0; } diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 384a3ab6dc2d..a52f75e22109 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -106,6 +106,9 @@ static int dw_spi_mscc_init(struct platform_device *pdev, dwsmmio->dws.set_cs = dw_spi_mscc_set_cs; dwsmmio->priv = dwsmscc; + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + return 0; } @@ -128,6 +131,18 @@ static int dw_spi_alpine_init(struct platform_device *pdev, { dwsmmio->dws.cs_override = 1; + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + + return 0; +} + +static int dw_spi_dw_apb_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + return 0; } @@ -224,17 +239,17 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) } static const struct of_device_id dw_spi_mmio_of_match[] = { - { .compatible = "snps,dw-apb-ssi", }, + { .compatible = "snps,dw-apb-ssi", .data = dw_spi_dw_apb_init}, { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_ocelot_init}, { .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init}, { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, - { .compatible = "renesas,rzn1-spi", }, + { .compatible = "renesas,rzn1-spi", .data = dw_spi_dw_apb_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); static const struct acpi_device_id dw_spi_mmio_acpi_match[] = { - {"HISI0173", 0}, + {"HISI0173", (kernel_ulong_t)dw_spi_dw_apb_init}, {}, }; MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 72a1c99ce9e6..4905457641ef 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -257,6 +257,26 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) return dws->transfer_handler(dws); } +/* Configure CTRLR0 for DW_apb_ssi */ +u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + struct chip_data *chip = spi_get_ctldata(spi); + u32 cr0; + + /* Default SPI mode is SCPOL = 0, SCPH = 0 */ + cr0 = (transfer->bits_per_word - 1) + | (chip->type << SPI_FRF_OFFSET) + | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | + (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | + (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) + | (chip->tmode << SPI_TMOD_OFFSET); + + return cr0; +} +EXPORT_SYMBOL_GPL(dw_spi_update_cr0); + static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { @@ -296,14 +316,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); dws->dma_width = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); - /* Default SPI mode is SCPOL = 0, SCPH = 0 */ - cr0 = (transfer->bits_per_word - 1) - | (chip->type << SPI_FRF_OFFSET) - | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | - (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | - (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) - | (chip->tmode << SPI_TMOD_OFFSET); - + cr0 = dws->update_cr0(master, spi, transfer); dw_writel(dws, DW_SPI_CTRLR0, cr0); /* Check if current transfer is a DMA transaction */ diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 6c34720b1b1d..2745a7e7405c 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -114,6 +114,8 @@ struct dw_spi { u16 bus_num; u16 num_cs; /* supported slave numbers */ void (*set_cs)(struct spi_device *spi, bool enable); + u32 (*update_cr0)(struct spi_controller *master, struct spi_device *spi, + struct spi_transfer *transfer); /* Current message transfer state info */ size_t len; @@ -240,6 +242,9 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); extern int dw_spi_resume_host(struct dw_spi *dws); +extern u32 dw_spi_update_cr0(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer); /* platform related setup */ extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ -- cgit v1.2.3-59-g8ed1b From e539f435cb9c78c6984b75f16b65a2ece7867981 Mon Sep 17 00:00:00 2001 From: Wan Ahmad Zainie Date: Tue, 5 May 2020 21:06:14 +0800 Subject: spi: dw: Add support for DesignWare DWC_ssi This patch adds initial support for DesignWare DWC_ssi soft IP. DWC_ssi is the enhanced version of DW_apb_ssi, which is currently supported by this driver. Their registers are same, but the bit fields of register CTRLR0 are different. DWC_ssi has additional features compared to DW_apb_ssi. Major enhancements in DWC_ssi are hyper bus protocol, boot mode support and advanced XIP support. DWC_ssi is an AHB slave device, whilst DW_apb_ssi is an APB slave device. Register offset DW_ssi DW_apb_ssi CTRLR0 0x00 0x00 CTRLR1 0x04 0x04 SSIENR 0x08 0x08 MWCR 0x0c 0x0c SER 0x10 0x10 BAUDR 0x14 0x14 TXFTLR 0x18 0x18 RXFTLR 0x1c 0x1c TXFLR 0x20 0x20 RXFLR 0x24 0x24 SR 0x28 0x28 IMR 0x2c 0x2c ISR 0x30 0x30 RISR 0x34 0x34 TXOICR 0x38 0x38 RXOICR 0x3c 0x3c RXUICR 0x40 0x40 MSTICR 0x44 0x44 ICR 0x48 0x48 DMACR 0x4c 0x4c DMATDLR 0x50 0x50 DMARDLR 0x54 0x54 IDR 0x58 0x58 SSI_VERSION_ID 0x5c 0x5c DRx (0 to 35) 0x60+i*0x4 0x60+i*0x4 RX_SAMPLE_DLY 0xf0 0xf0 SPI_CTRLR0 0xf4 0xf4 TXD_DRIVE_EDGE 0xf8 0xf8 XIP_MODE_BITS 0xfc RSVD Register configuration - CTRLR0 DW_ssi DW_apb_ssi SPI_HYPERBUS_EN bit[24] NONE SPI_FRF bit[23:22] bit[22:21] DFS_32 NONE bit[20:16] CFS bit[19:16] bit[15:12] SSTE bit[14] bit[24] SRL bit[13] bit[11] SLV_OE bit[12] bit[10] TMOD bit[11:10] bit[9:8] SCPOL | SPHA bit[9:8] bit[7:6] FRF bit[7:6] bit[5:4] DFS bit[4:0] bit[3:0] The documents used are [1] DW_apb_ssi_databook.pdf version 4.01a (2016.10a). [2] DWC_ssi_databook.pdf version 1.01a. Signed-off-by: Wan Ahmad Zainie Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200505130618.554-4-wan.ahmad.zainie.wan.mohamad@intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 10 ++++++++++ drivers/spi/spi-dw.c | 33 +++++++++++++++++++++++++++++++++ drivers/spi/spi-dw.h | 12 ++++++++++++ 3 files changed, 55 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index a52f75e22109..1df6f3deee2c 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -146,6 +146,15 @@ static int dw_spi_dw_apb_init(struct platform_device *pdev, return 0; } +static int dw_spi_dwc_ssi_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a; + + return 0; +} + static int dw_spi_mmio_probe(struct platform_device *pdev) { int (*init_func)(struct platform_device *pdev, @@ -244,6 +253,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init}, { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, { .compatible = "renesas,rzn1-spi", .data = dw_spi_dw_apb_init}, + { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 4905457641ef..240a61b66a06 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -277,6 +277,39 @@ u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, } EXPORT_SYMBOL_GPL(dw_spi_update_cr0); +/* Configure CTRLR0 for DWC_ssi */ +u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + struct chip_data *chip = spi_get_ctldata(spi); + u32 cr0; + + /* CTRLR0[ 4: 0] Data Frame Size */ + cr0 = (transfer->bits_per_word - 1); + + /* CTRLR0[ 7: 6] Frame Format */ + cr0 |= chip->type << DWC_SSI_CTRLR0_FRF_OFFSET; + + /* + * SPI mode (SCPOL|SCPH) + * CTRLR0[ 8] Serial Clock Phase + * CTRLR0[ 9] Serial Clock Polarity + */ + cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET; + cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET; + + /* CTRLR0[11:10] Transfer Mode */ + cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; + + /* CTRLR0[13] Shift Register Loop */ + cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; + + return cr0; +} +EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a); + static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 2745a7e7405c..8fe724279d15 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -57,6 +57,15 @@ #define SPI_SRL_OFFSET 11 #define SPI_CFS_OFFSET 12 +/* Bit fields in CTRLR0 based on DWC_ssi_databook.pdf v1.01a */ +#define DWC_SSI_CTRLR0_SRL_OFFSET 13 +#define DWC_SSI_CTRLR0_TMOD_OFFSET 10 +#define DWC_SSI_CTRLR0_TMOD_MASK GENMASK(11, 10) +#define DWC_SSI_CTRLR0_SCPOL_OFFSET 9 +#define DWC_SSI_CTRLR0_SCPH_OFFSET 8 +#define DWC_SSI_CTRLR0_FRF_OFFSET 6 +#define DWC_SSI_CTRLR0_DFS_OFFSET 0 + /* Bit fields in SR, 7 bits */ #define SR_MASK 0x7f /* cover 7 bits */ #define SR_BUSY (1 << 0) @@ -245,6 +254,9 @@ extern int dw_spi_resume_host(struct dw_spi *dws); extern u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer); +extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer); /* platform related setup */ extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ -- cgit v1.2.3-59-g8ed1b From f42377916ed534649341777669628f22ef1edf59 Mon Sep 17 00:00:00 2001 From: Wan Ahmad Zainie Date: Tue, 5 May 2020 21:06:16 +0800 Subject: spi: dw: Add support for Intel Keem Bay SPI Add support for Intel Keem Bay SPI controller, which uses DesignWare DWC_ssi core. Bit 31 of CTRLR0 register is added for Keem Bay, to configure the device as a master or as a slave serial peripheral. Signed-off-by: Wan Ahmad Zainie Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200505130618.554-6-wan.ahmad.zainie.wan.mohamad@intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 1df6f3deee2c..abd3bb5e52db 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -44,6 +44,13 @@ struct dw_spi_mmio { #define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) #define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) +/* + * For Keem Bay, CTRLR0[31] is used to select controller mode. + * 0: SSI is slave + * 1: SSI is master + */ +#define KEEMBAY_CTRLR0_SSIC_IS_MST BIT(31) + struct dw_spi_mscc { struct regmap *syscon; void __iomem *spi_mst; @@ -155,6 +162,24 @@ static int dw_spi_dwc_ssi_init(struct platform_device *pdev, return 0; } +static u32 dw_spi_update_cr0_keembay(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + u32 cr0 = dw_spi_update_cr0_v1_01a(master, spi, transfer); + + return cr0 | KEEMBAY_CTRLR0_SSIC_IS_MST; +} + +static int dw_spi_keembay_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0_keembay; + + return 0; +} + static int dw_spi_mmio_probe(struct platform_device *pdev) { int (*init_func)(struct platform_device *pdev, @@ -254,6 +279,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, { .compatible = "renesas,rzn1-spi", .data = dw_spi_dw_apb_init}, { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init}, + { .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); -- cgit v1.2.3-59-g8ed1b From 5b684514af9052fdbaa60895620f75928a134196 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Wed, 6 May 2020 14:19:11 +0800 Subject: spi: a3700: make a3700_spi_init() return void Fix the following coccicheck warning: drivers/spi/spi-armada-3700.c:283:8-11: Unneeded variable: "ret". Return "0" on line 315 Signed-off-by: Jason Yan Link: https://lore.kernel.org/r/20200506061911.19923-1-yanaijie@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-armada-3700.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index e450ee17787f..fcde419e480c 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -276,11 +276,11 @@ static int a3700_spi_fifo_flush(struct a3700_spi *a3700_spi) return -ETIMEDOUT; } -static int a3700_spi_init(struct a3700_spi *a3700_spi) +static void a3700_spi_init(struct a3700_spi *a3700_spi) { struct spi_master *master = a3700_spi->master; u32 val; - int i, ret = 0; + int i; /* Reset SPI unit */ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); @@ -311,8 +311,6 @@ static int a3700_spi_init(struct a3700_spi *a3700_spi) /* Mask the interrupts and clear cause bits */ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0); spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U); - - return ret; } static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id) @@ -886,9 +884,7 @@ static int a3700_spi_probe(struct platform_device *pdev) master->min_speed_hz = DIV_ROUND_UP(clk_get_rate(spi->clk), A3700_SPI_MAX_PRESCALE); - ret = a3700_spi_init(spi); - if (ret) - goto error_clk; + a3700_spi_init(spi); ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0, dev_name(dev), master); -- cgit v1.2.3-59-g8ed1b From dd4441ab1fa1e2787a5f218f92d8ead1aa5ce6b5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 6 May 2020 12:56:07 +0000 Subject: spi: bcm2835: Fix error return code in bcm2835_dma_init() Fix to return negative error code -ENOMEM from the dma mapping error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20200506125607.90952-1-weiyongjun1@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index e10b8f3b4bab..cad3458e23ed 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -940,6 +940,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, if (dma_mapping_error(ctlr->dma_tx->device->dev, bs->fill_tx_addr)) { dev_err(dev, "cannot map zero page - not using DMA mode\n"); bs->fill_tx_addr = 0; + ret = -ENOMEM; goto err_release; } @@ -949,6 +950,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, DMA_MEM_TO_DEV, 0); if (!bs->fill_tx_desc) { dev_err(dev, "cannot prepare fill_tx_desc - not using DMA mode\n"); + ret = -ENOMEM; goto err_release; } @@ -979,6 +981,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, if (dma_mapping_error(ctlr->dma_rx->device->dev, bs->clear_rx_addr)) { dev_err(dev, "cannot map clear_rx_cs - not using DMA mode\n"); bs->clear_rx_addr = 0; + ret = -ENOMEM; goto err_release; } @@ -989,6 +992,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, DMA_MEM_TO_DEV, 0); if (!bs->clear_rx_desc[i]) { dev_err(dev, "cannot prepare clear_rx_desc - not using DMA mode\n"); + ret = -ENOMEM; goto err_release; } -- cgit v1.2.3-59-g8ed1b From 3cb97e223d277f84171cc4ccecab31e08b2ee7b5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2020 18:30:18 +0300 Subject: spi: dw: Zero DMA Tx and Rx configurations on stack Some DMA controller drivers do not tolerate non-zero values in the DMA configuration structures. Zero them to avoid issues with such DMA controller drivers. Even despite above this is a good practice per se. Fixes: 7063c0d942a1 ("spi/dw_spi: add DMA support") Signed-off-by: Andy Shevchenko Acked-by: Feng Tang Cc: Feng Tang Link: https://lore.kernel.org/r/20200506153025.21441-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 9cc010e9737e..86d9f79267f0 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -147,6 +147,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, if (!xfer->tx_buf) return NULL; + memset(&txconf, 0, sizeof(txconf)); txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = 16; @@ -193,6 +194,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, if (!xfer->rx_buf) return NULL; + memset(&rxconf, 0, sizeof(rxconf)); rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = 16; -- cgit v1.2.3-59-g8ed1b From d4dd6c0a404a2ed3843b4b685d6990c4438a64c0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2020 18:30:19 +0300 Subject: spi: dw: Remove unused variable in CR0 configuring hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After enabling new IP support in the driver couple of variables were left unused compiler is not happy about: .../spi-dw.c: In function ‘dw_spi_update_cr0’: .../spi-dw.c:264:17: warning: unused variable ‘dws’ [-Wunused-variable] 264 | struct dw_spi *dws = spi_controller_get_devdata(master); | ^~~ .../spi-dw.c: In function ‘dw_spi_update_cr0_v1_01a’: .../spi-dw.c:285:17: warning: unused variable ‘dws’ [-Wunused-variable] 285 | struct dw_spi *dws = spi_controller_get_devdata(master); | ^~~ Drop them for good. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200506153025.21441-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 240a61b66a06..6e56a64ccc55 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -261,7 +261,6 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { - struct dw_spi *dws = spi_controller_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); u32 cr0; @@ -282,7 +281,6 @@ u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { - struct dw_spi *dws = spi_controller_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); u32 cr0; -- cgit v1.2.3-59-g8ed1b From 0c2ce3fe4dd0b8f8dda07ea029f51ddf4c5190c2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2020 18:30:20 +0300 Subject: spi: dw: Move interrupt.h to spi-dw.h who is user of it The actual user of interrupt.h is spi-dw.h and not bus drivers. Move header there. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200506153025.21441-3-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 1 - drivers/spi/spi-dw-pci.c | 1 - drivers/spi/spi-dw.h | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index abd3bb5e52db..fc3577b07a1e 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 12c131b5fb4e..172a9f299631 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -5,7 +5,6 @@ * Copyright (c) 2009, 2014 Intel Corporation. */ -#include #include #include #include diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 8fe724279d15..aeed49b4a444 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -2,6 +2,7 @@ #ifndef DW_SPI_HEADER_H #define DW_SPI_HEADER_H +#include #include #include -- cgit v1.2.3-59-g8ed1b From e62a15d97b0adede829ba5b4f1c8e28f230bd8e8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2020 18:30:21 +0300 Subject: spi: dw: Downgrade interrupt.h to irqreturn.h where appropriate spi-dw-mid.c along with spi-dw.h are direct users of irqreturn.h and nothing else is being used from interrupt.h. So, switch them to use the former instead of latter one. While here, move the header under #ifdef CONFIG_SPI_DW_MID_DMA in spi-dw-mid.c. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200506153025.21441-4-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 2 +- drivers/spi/spi-dw.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 86d9f79267f0..02defb68618d 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -15,6 +14,7 @@ #include "spi-dw.h" #ifdef CONFIG_SPI_DW_MID_DMA +#include #include #include diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index aeed49b4a444..5e1e78210d8d 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -2,7 +2,7 @@ #ifndef DW_SPI_HEADER_H #define DW_SPI_HEADER_H -#include +#include #include #include -- cgit v1.2.3-59-g8ed1b From e7940952644558e680033ae0450978445e53b423 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2020 18:30:22 +0300 Subject: spi: dw: Move few headers under #ifdef CONFIG_SPI_DW_MID_DMA There is no user of few headers without CONFIG_SPI_DW_MID_DMA being set. Move them under condition. While at it, remove unused slab.h there. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200506153025.21441-5-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 02defb68618d..64523e68490d 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -5,15 +5,14 @@ * Copyright (c) 2009, 2014 Intel Corporation. */ -#include -#include -#include #include #include #include "spi-dw.h" #ifdef CONFIG_SPI_DW_MID_DMA +#include +#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 37aa8aa68492deb56f9e4c8b2d00aa5d9dae7da2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2020 18:30:23 +0300 Subject: spi: dw: Add 'mfld' suffix to Intel Medfield related routines In order to prepare driver for the extension to support newer hardware, add 'mfld' suffix to some related functions. While here, move DMA parameters assignment under existing #ifdef CONFIG_SPI_DW_MID_DMA. There is no functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200506153025.21441-6-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 24 ++++++++++++++---------- drivers/spi/spi-dw-pci.c | 4 ++-- drivers/spi/spi-dw.h | 3 ++- 3 files changed, 18 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 64523e68490d..13b548915c8f 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -34,7 +34,7 @@ static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) return true; } -static int mid_spi_dma_init(struct dw_spi *dws) +static int mid_spi_dma_init_mfld(struct dw_spi *dws) { struct pci_dev *dma_dev; struct dw_dma_slave *tx = dws->dma_tx; @@ -276,14 +276,23 @@ static void mid_spi_dma_stop(struct dw_spi *dws) } } -static const struct dw_spi_dma_ops mid_dma_ops = { - .dma_init = mid_spi_dma_init, +static const struct dw_spi_dma_ops mfld_dma_ops = { + .dma_init = mid_spi_dma_init_mfld, .dma_exit = mid_spi_dma_exit, .dma_setup = mid_spi_dma_setup, .can_dma = mid_spi_can_dma, .dma_transfer = mid_spi_dma_transfer, .dma_stop = mid_spi_dma_stop, }; + +static void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) +{ + dws->dma_tx = &mid_dma_tx; + dws->dma_rx = &mid_dma_rx; + dws->dma_ops = &mfld_dma_ops; +} +#else /* CONFIG_SPI_DW_MID_DMA */ +static inline void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) {} #endif /* Some specific info for SPI0 controller on Intel MID */ @@ -297,7 +306,7 @@ static const struct dw_spi_dma_ops mid_dma_ops = { #define CLK_SPI_CDIV_MASK 0x00000e00 #define CLK_SPI_DISABLE_OFFSET 8 -int dw_spi_mid_init(struct dw_spi *dws) +int dw_spi_mid_init_mfld(struct dw_spi *dws) { void __iomem *clk_reg; u32 clk_cdiv; @@ -314,14 +323,9 @@ int dw_spi_mid_init(struct dw_spi *dws) iounmap(clk_reg); -#ifdef CONFIG_SPI_DW_MID_DMA - dws->dma_tx = &mid_dma_tx; - dws->dma_rx = &mid_dma_rx; - dws->dma_ops = &mid_dma_ops; -#endif - /* Register hook to configure CTRLR0 */ dws->update_cr0 = dw_spi_update_cr0; + dw_spi_mid_setup_dma_mfld(dws); return 0; } diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 172a9f299631..dd59df5122ee 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -23,13 +23,13 @@ struct spi_pci_desc { }; static struct spi_pci_desc spi_pci_mid_desc_1 = { - .setup = dw_spi_mid_init, + .setup = dw_spi_mid_init_mfld, .num_cs = 5, .bus_num = 0, }; static struct spi_pci_desc spi_pci_mid_desc_2 = { - .setup = dw_spi_mid_init, + .setup = dw_spi_mid_init_mfld, .num_cs = 2, .bus_num = 1, }; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 5e1e78210d8d..b7e3f0ebba44 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -260,5 +260,6 @@ extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, struct spi_transfer *transfer); /* platform related setup */ -extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ +extern int dw_spi_mid_init_mfld(struct dw_spi *dws); + #endif /* DW_SPI_HEADER_H */ -- cgit v1.2.3-59-g8ed1b From 6370ababce81911576d7c96663ae64fb84820c7b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 6 May 2020 18:30:24 +0300 Subject: spi: dw: Propagate struct device pointer to ->dma_init() callback In some cases, one of which is coming soon, we would like to have a struct device pointer to request DMA channel. For this purpose propagate it to ->dma_init() callback in DMA ops. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200506153025.21441-7-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 2 +- drivers/spi/spi-dw.c | 2 +- drivers/spi/spi-dw.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 13b548915c8f..d73aa4ae644d 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -34,7 +34,7 @@ static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) return true; } -static int mid_spi_dma_init_mfld(struct dw_spi *dws) +static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) { struct pci_dev *dma_dev; struct dw_dma_slave *tx = dws->dma_tx; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 6e56a64ccc55..b9f651e9ca02 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -506,7 +506,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) spi_hw_init(dev, dws); if (dws->dma_ops && dws->dma_ops->dma_init) { - ret = dws->dma_ops->dma_init(dws); + ret = dws->dma_ops->dma_init(dev, dws); if (ret) { dev_warn(dev, "DMA init failed\n"); dws->dma_inited = 0; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index b7e3f0ebba44..642f0be642e5 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -100,7 +100,7 @@ enum dw_ssi_type { struct dw_spi; struct dw_spi_dma_ops { - int (*dma_init)(struct dw_spi *dws); + int (*dma_init)(struct device *dev, struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer); bool (*can_dma)(struct spi_controller *master, struct spi_device *spi, -- cgit v1.2.3-59-g8ed1b From 22d48ad7bfacda05900c3f7b43510fc4d40d5d53 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 6 May 2020 18:30:25 +0300 Subject: spi: dw: Add Elkhart Lake PSE DMA support Elkhart Lake PSE SPI is capable to utilize PSE DMA engine which is described in ACPI. With help of acpi-dma module the support becomes a generic one. Thus, add Elkhart Lake PSE DMA support and generic DMA hooks in SPI DesignWare driver. Signed-off-by: Jarkko Nikula Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200506153025.21441-8-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spi-dw-pci.c | 1 + drivers/spi/spi-dw.h | 1 + 3 files changed, 46 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index d73aa4ae644d..f3c85f92ef12 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -75,6 +75,24 @@ err_exit: return -EBUSY; } +static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) +{ + dws->rxchan = dma_request_slave_channel(dev, "rx"); + if (!dws->rxchan) + return -ENODEV; + dws->master->dma_rx = dws->rxchan; + + dws->txchan = dma_request_slave_channel(dev, "tx"); + if (!dws->txchan) { + dma_release_channel(dws->rxchan); + return -ENODEV; + } + dws->master->dma_tx = dws->txchan; + + dws->dma_inited = 1; + return 0; +} + static void mid_spi_dma_exit(struct dw_spi *dws) { if (!dws->dma_inited) @@ -291,8 +309,25 @@ static void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) dws->dma_rx = &mid_dma_rx; dws->dma_ops = &mfld_dma_ops; } + +static const struct dw_spi_dma_ops generic_dma_ops = { + .dma_init = mid_spi_dma_init_generic, + .dma_exit = mid_spi_dma_exit, + .dma_setup = mid_spi_dma_setup, + .can_dma = mid_spi_can_dma, + .dma_transfer = mid_spi_dma_transfer, + .dma_stop = mid_spi_dma_stop, +}; + +static void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) +{ + dws->dma_tx = &mid_dma_tx; + dws->dma_rx = &mid_dma_rx; + dws->dma_ops = &generic_dma_ops; +} #else /* CONFIG_SPI_DW_MID_DMA */ static inline void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) {} +static inline void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) {} #endif /* Some specific info for SPI0 controller on Intel MID */ @@ -329,3 +364,12 @@ int dw_spi_mid_init_mfld(struct dw_spi *dws) dw_spi_mid_setup_dma_mfld(dws); return 0; } + +int dw_spi_mid_init_generic(struct dw_spi *dws) +{ + /* Register hook to configure CTRLR0 */ + dws->update_cr0 = dw_spi_update_cr0; + + dw_spi_mid_setup_dma_generic(dws); + return 0; +} diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index dd59df5122ee..dde54a918b5d 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -35,6 +35,7 @@ static struct spi_pci_desc spi_pci_mid_desc_2 = { }; static struct spi_pci_desc spi_pci_ehl_desc = { + .setup = dw_spi_mid_init_generic, .num_cs = 2, .bus_num = -1, .max_freq = 100000000, diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 642f0be642e5..490cff260a3e 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -261,5 +261,6 @@ extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, /* platform related setup */ extern int dw_spi_mid_init_mfld(struct dw_spi *dws); +extern int dw_spi_mid_init_generic(struct dw_spi *dws); #endif /* DW_SPI_HEADER_H */ -- cgit v1.2.3-59-g8ed1b From 140e45e1e62dd56ed4c264db1443a5d4f5f40352 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 7 May 2020 14:54:48 +0300 Subject: spi: dw: Avoid useless assignments in generic DMA setup Generic DMA setup doesn't rely on certain type of DMA controller and thus shouldn't use Intel Medfield settings, although it's harmless in this case. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200507115449.8093-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index f3c85f92ef12..8b7b94c5a9cc 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -321,8 +321,6 @@ static const struct dw_spi_dma_ops generic_dma_ops = { static void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) { - dws->dma_tx = &mid_dma_tx; - dws->dma_rx = &mid_dma_rx; dws->dma_ops = &generic_dma_ops; } #else /* CONFIG_SPI_DW_MID_DMA */ -- cgit v1.2.3-59-g8ed1b From a041e672cb57201d152bfc314e52d41e7643375d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 7 May 2020 14:54:49 +0300 Subject: spi: dw: Get rid of dma_inited flag This flag is superfluous in all cases where it's being used, i.e. * ->can_dma() won't be called without dma_inited == 1 * DMA ->exit() callback can rely on txchan and rxchan variables So, get rid of dma_inited flag. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200507115449.8093-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 28 ++++++++++++++-------------- drivers/spi/spi-dw.c | 2 -- drivers/spi/spi-dw.h | 1 - 3 files changed, 14 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 8b7b94c5a9cc..177e1f5ec62b 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -57,20 +57,21 @@ static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx); if (!dws->rxchan) goto err_exit; - dws->master->dma_rx = dws->rxchan; /* 2. Init tx channel */ tx->dma_dev = &dma_dev->dev; dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx); if (!dws->txchan) goto free_rxchan; + + dws->master->dma_rx = dws->rxchan; dws->master->dma_tx = dws->txchan; - dws->dma_inited = 1; return 0; free_rxchan: dma_release_channel(dws->rxchan); + dws->rxchan = NULL; err_exit: return -EBUSY; } @@ -80,29 +81,31 @@ static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) dws->rxchan = dma_request_slave_channel(dev, "rx"); if (!dws->rxchan) return -ENODEV; - dws->master->dma_rx = dws->rxchan; dws->txchan = dma_request_slave_channel(dev, "tx"); if (!dws->txchan) { dma_release_channel(dws->rxchan); + dws->rxchan = NULL; return -ENODEV; } + + dws->master->dma_rx = dws->rxchan; dws->master->dma_tx = dws->txchan; - dws->dma_inited = 1; return 0; } static void mid_spi_dma_exit(struct dw_spi *dws) { - if (!dws->dma_inited) - return; - - dmaengine_terminate_sync(dws->txchan); - dma_release_channel(dws->txchan); + if (dws->txchan) { + dmaengine_terminate_sync(dws->txchan); + dma_release_channel(dws->txchan); + } - dmaengine_terminate_sync(dws->rxchan); - dma_release_channel(dws->rxchan); + if (dws->rxchan) { + dmaengine_terminate_sync(dws->rxchan); + dma_release_channel(dws->rxchan); + } } static irqreturn_t dma_transfer(struct dw_spi *dws) @@ -126,9 +129,6 @@ static bool mid_spi_can_dma(struct spi_controller *master, { struct dw_spi *dws = spi_controller_get_devdata(master); - if (!dws->dma_inited) - return false; - return xfer->len > dws->fifo_len; } diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index b9f651e9ca02..6de196df9c96 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -470,7 +470,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->master = master; dws->type = SSI_MOTO_SPI; - dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); spin_lock_init(&dws->buf_lock); @@ -509,7 +508,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ret = dws->dma_ops->dma_init(dev, dws); if (ret) { dev_warn(dev, "DMA init failed\n"); - dws->dma_inited = 0; } else { master->can_dma = dws->dma_ops->can_dma; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 490cff260a3e..e92d43b9a9e6 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -141,7 +141,6 @@ struct dw_spi { u32 current_freq; /* frequency in hz */ /* DMA info */ - int dma_inited; struct dma_chan *txchan; struct dma_chan *rxchan; unsigned long dma_chan_busy; -- cgit v1.2.3-59-g8ed1b From 6a091404bf95f1e17d4ad149f1c010a9360e88ba Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Mon, 11 May 2020 17:25:29 +0900 Subject: spi: uniphier: Depend on HAS_IOMEM The driver uses devm_ioremap_resource() which is only available when CONFIG_HAS_IOMEM is set, so the driver depends on this option. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1589185530-28170-1-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 17e7e4afc0fc..099d6a75a371 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -844,6 +844,7 @@ config SPI_TXX9 config SPI_UNIPHIER tristate "Socionext UniPhier SPI Controller" depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF + depends on HAS_IOMEM help This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller. -- cgit v1.2.3-59-g8ed1b From 5bc486156d840c15fb65c96da24f20687a07b2a4 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Mon, 11 May 2020 17:25:30 +0900 Subject: spi: uniphier: Use devm_platform_get_and_ioremap_resource() to simplify code Use devm_platform_get_and_ioremap_resource() to simplify code instead of platform_get_resource() and devm_ioremap_resource(). This also gets the resource that the following code uses. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1589185530-28170-2-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown --- drivers/spi/spi-uniphier.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 0457d3376873..6a9ef8ee3cc9 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -659,8 +659,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->master = master; priv->is_save_param = false; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto out_master_put; -- cgit v1.2.3-59-g8ed1b From 74750e0621cdcee53a7be72142a080bf40ed90c8 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Sun, 10 May 2020 21:53:30 -0700 Subject: spi: sun6i: Add support for GPIO chip select lines Set use_gpio_descriptors as true to support using generic GPIO lines for the chip select. Signed-off-by: Alistair Francis Link: https://lore.kernel.org/r/20200511045330.690507-1-alistair@alistair23.me Signed-off-by: Mark Brown --- drivers/spi/spi-sun6i.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index ec7967be9e2f..ecea15534c42 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -470,6 +470,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) master->max_speed_hz = 100 * 1000 * 1000; master->min_speed_hz = 3 * 1000; + master->use_gpio_descriptors = true; master->set_cs = sun6i_spi_set_cs; master->transfer_one = sun6i_spi_transfer_one; master->num_chipselect = 4; -- cgit v1.2.3-59-g8ed1b From 4dd227a55a9f88618b227eda407d58826bbf4df1 Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Sat, 9 May 2020 10:29:51 +0800 Subject: spi: dw-mmio: Do not add acpi modalias when CONFIG_ACPI is not enabled Reduce unnecessary static memory allocation when CONFIG_ACPI is not enabled. Signed-off-by: Jay Fang Link: https://lore.kernel.org/r/1588991392-24219-1-git-send-email-f.fangjian@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index fc3577b07a1e..c34fba2c0346 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -283,11 +283,13 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); +#ifdef CONFIG_ACPI static const struct acpi_device_id dw_spi_mmio_acpi_match[] = { {"HISI0173", (kernel_ulong_t)dw_spi_dw_apb_init}, {}, }; MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match); +#endif static struct platform_driver dw_spi_mmio_driver = { .probe = dw_spi_mmio_probe, -- cgit v1.2.3-59-g8ed1b From afb7f565249aca3d46954889f07b48e8caf860ce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 12 May 2020 14:03:15 +0300 Subject: spi: dw: Drop duplicate error message when remap resource devm_platform_ioremap_resource() will issue a message in the error case. Thus, no need to duplicate in the driver. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200512110315.58845-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index c34fba2c0346..398f7926cf92 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -197,10 +197,8 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) /* Get basic io resource and map it */ dws->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dws->regs)) { - dev_err(&pdev->dev, "SPI region map failed\n"); + if (IS_ERR(dws->regs)) return PTR_ERR(dws->regs); - } dws->irq = platform_get_irq(pdev, 0); if (dws->irq < 0) -- cgit v1.2.3-59-g8ed1b From 9aea644ca17b94f82ad7fa767cbc4509642f4420 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 15 May 2020 13:47:43 +0300 Subject: spi: dw: Fix native CS being unset Commit 6e0a32d6f376 ("spi: dw: Fix default polarity of native chipselect") attempted to fix the problem when GPIO active-high chip-select is utilized to communicate with some SPI slave. It fixed the problem, but broke the normal native CS support. At the same time the reversion commit ada9e3fcc175 ("spi: dw: Correct handling of native chipselect") didn't solve the problem either, since it just inverted the set_cs() polarity perception without taking into account that CS-high might be applicable. Here is what is done to finally fix the problem. DW SPI controller demands any native CS being set in order to proceed with data transfer. So in order to activate the SPI communications we must set any bit in the Slave Select DW SPI controller register no matter whether the platform requests the GPIO- or native CS. Preferably it should be the bit corresponding to the SPI slave CS number. But currently the dw_spi_set_cs() method activates the chip-select only if the second argument is false. Since the second argument of the set_cs callback is expected to be a boolean with "is-high" semantics (actual chip-select pin state value), the bit in the DW SPI Slave Select register will be set only if SPI core requests the driver to set the CS in the low state. So this will work for active-low GPIO-based CS case, and won't work for active-high CS setting the bit when SPI core actually needs to deactivate the CS. This commit fixes the problem for all described cases. So no matter whether an SPI slave needs GPIO- or native-based CS with active-high or low signal the corresponding bit will be set in SER. Signed-off-by: Serge Semin Fixes: ada9e3fcc175 ("spi: dw: Correct handling of native chipselect") Fixes: 6e0a32d6f376 ("spi: dw: Fix default polarity of native chipselect") Reviewed-by: Charles Keepax Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20200515104758.6934-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 6de196df9c96..450c8218caeb 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -124,8 +124,16 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) void dw_spi_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_controller_get_devdata(spi->controller); + bool cs_high = !!(spi->mode & SPI_CS_HIGH); - if (!enable) + /* + * DW SPI controller demands any native CS being set in order to + * proceed with data transfer. So in order to activate the SPI + * communications we must set a corresponding bit in the Slave + * Enable register no matter whether the SPI core is configured to + * support active-high or active-low CS level. + */ + if (cs_high == enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); else if (dws->cs_override) dw_writel(dws, DW_SPI_SER, 0); -- cgit v1.2.3-59-g8ed1b From 77810d484f4f28107391787dfa1c6c53d994c4fc Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 15 May 2020 13:47:50 +0300 Subject: spi: dw: Initialize paddr in DW SPI MMIO private data This field is used only for the DW SPI DMA code initialization, that's why there were no problems with it being uninitialized in Dw SPI MMIO driver. Since in a further patch we are going to introduce the DW SPI DMA support in the MMIO version of the driver, lets set the field with the physical address of the DW SPI controller registers region. Co-developed-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Georgy Vlasov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200515104758.6934-12-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 398f7926cf92..0894b4c09496 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -184,6 +184,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) int (*init_func)(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio); struct dw_spi_mmio *dwsmmio; + struct resource *mem; struct dw_spi *dws; int ret; int num_cs; @@ -196,10 +197,12 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dws = &dwsmmio->dws; /* Get basic io resource and map it */ - dws->regs = devm_platform_ioremap_resource(pdev, 0); + dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(dws->regs)) return PTR_ERR(dws->regs); + dws->paddr = mem->start; + dws->irq = platform_get_irq(pdev, 0); if (dws->irq < 0) return dws->irq; /* -ENXIO */ -- cgit v1.2.3-59-g8ed1b From 0327f0b881dc5645c7ba670331e822cdaa8c5e09 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 15 May 2020 13:47:42 +0300 Subject: spi: dw: Clear DMAC register when done or stopped If DMAC register is left uncleared any further DMAless transfers may cause the DMAC hardware handshaking interface getting activated. So the next DMA-based Rx/Tx transaction will be started right after the dma_async_issue_pending() method is invoked even if no DMATDLR/DMARDLR conditions are met. This at the same time may cause the Tx/Rx FIFO buffers underrun/overrun. In order to fix this we must clear DMAC register after a current DMA-based transaction is finished. Co-developed-by: Georgy Vlasov Signed-off-by: Georgy Vlasov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200515104758.6934-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 177e1f5ec62b..f9757a370699 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -106,6 +106,8 @@ static void mid_spi_dma_exit(struct dw_spi *dws) dmaengine_terminate_sync(dws->rxchan); dma_release_channel(dws->rxchan); } + + dw_writel(dws, DW_SPI_DMACR, 0); } static irqreturn_t dma_transfer(struct dw_spi *dws) @@ -152,6 +154,8 @@ static void dw_spi_dma_tx_done(void *arg) clear_bit(TX_BUSY, &dws->dma_chan_busy); if (test_bit(RX_BUSY, &dws->dma_chan_busy)) return; + + dw_writel(dws, DW_SPI_DMACR, 0); spi_finalize_current_transfer(dws->master); } @@ -199,6 +203,8 @@ static void dw_spi_dma_rx_done(void *arg) clear_bit(RX_BUSY, &dws->dma_chan_busy); if (test_bit(TX_BUSY, &dws->dma_chan_busy)) return; + + dw_writel(dws, DW_SPI_DMACR, 0); spi_finalize_current_transfer(dws->master); } @@ -292,6 +298,8 @@ static void mid_spi_dma_stop(struct dw_spi *dws) dmaengine_terminate_sync(dws->rxchan); clear_bit(RX_BUSY, &dws->dma_chan_busy); } + + dw_writel(dws, DW_SPI_DMACR, 0); } static const struct dw_spi_dma_ops mfld_dma_ops = { -- cgit v1.2.3-59-g8ed1b From d66571a20f68f153c045c9822bf4c288e7511168 Mon Sep 17 00:00:00 2001 From: Chris Ruehl Date: Mon, 11 May 2020 16:30:20 +0800 Subject: spi: spi-rockchip: cleanup use struct spi_controller Cleanup, move from the compatibily layer struct spi_master over to struct spi_controller, and rename the related function calls. Signed-off-by: Chris Ruehl Link: https://lore.kernel.org/r/20200511083022.23678-2-chris.ruehl@gtsys.com.hk Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 182 ++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 91 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 70ef63e0b6b8..c501d8a84414 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -219,8 +219,8 @@ static u32 get_fifo_len(struct rockchip_spi *rs) static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) { - struct spi_master *master = spi->master; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = spi->controller; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); bool cs_asserted = !enable; /* Return immediately for no-op */ @@ -244,10 +244,10 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) rs->cs_asserted[spi->chip_select] = cs_asserted; } -static void rockchip_spi_handle_err(struct spi_master *master, +static void rockchip_spi_handle_err(struct spi_controller *ctlr, struct spi_message *msg) { - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); /* stop running spi transfer * this also flushes both rx and tx fifos @@ -258,10 +258,10 @@ static void rockchip_spi_handle_err(struct spi_master *master, writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); if (atomic_read(&rs->state) & TXDMA) - dmaengine_terminate_async(master->dma_tx); + dmaengine_terminate_async(ctlr->dma_tx); if (atomic_read(&rs->state) & RXDMA) - dmaengine_terminate_async(master->dma_rx); + dmaengine_terminate_async(ctlr->dma_rx); } static void rockchip_spi_pio_writer(struct rockchip_spi *rs) @@ -319,8 +319,8 @@ static void rockchip_spi_pio_reader(struct rockchip_spi *rs) static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) { - struct spi_master *master = dev_id; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_id; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); if (rs->tx_left) rockchip_spi_pio_writer(rs); @@ -329,7 +329,7 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) if (!rs->rx_left) { spi_enable_chip(rs, false); writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); - spi_finalize_current_transfer(master); + spi_finalize_current_transfer(ctlr); } return IRQ_HANDLED; @@ -355,21 +355,21 @@ static int rockchip_spi_prepare_irq(struct rockchip_spi *rs, static void rockchip_spi_dma_rxcb(void *data) { - struct spi_master *master = data; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = data; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); int state = atomic_fetch_andnot(RXDMA, &rs->state); if (state & TXDMA) return; spi_enable_chip(rs, false); - spi_finalize_current_transfer(master); + spi_finalize_current_transfer(ctlr); } static void rockchip_spi_dma_txcb(void *data) { - struct spi_master *master = data; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = data; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); int state = atomic_fetch_andnot(TXDMA, &rs->state); if (state & RXDMA) @@ -379,11 +379,11 @@ static void rockchip_spi_dma_txcb(void *data) wait_for_idle(rs); spi_enable_chip(rs, false); - spi_finalize_current_transfer(master); + spi_finalize_current_transfer(ctlr); } static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, - struct spi_master *master, struct spi_transfer *xfer) + struct spi_controller *ctlr, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *rxdesc, *txdesc; @@ -398,17 +398,17 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, .src_maxburst = 1, }; - dmaengine_slave_config(master->dma_rx, &rxconf); + dmaengine_slave_config(ctlr->dma_rx, &rxconf); rxdesc = dmaengine_prep_slave_sg( - master->dma_rx, + ctlr->dma_rx, xfer->rx_sg.sgl, xfer->rx_sg.nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!rxdesc) return -EINVAL; rxdesc->callback = rockchip_spi_dma_rxcb; - rxdesc->callback_param = master; + rxdesc->callback_param = ctlr; } txdesc = NULL; @@ -420,27 +420,27 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, .dst_maxburst = rs->fifo_len / 4, }; - dmaengine_slave_config(master->dma_tx, &txconf); + dmaengine_slave_config(ctlr->dma_tx, &txconf); txdesc = dmaengine_prep_slave_sg( - master->dma_tx, + ctlr->dma_tx, xfer->tx_sg.sgl, xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txdesc) { if (rxdesc) - dmaengine_terminate_sync(master->dma_rx); + dmaengine_terminate_sync(ctlr->dma_rx); return -EINVAL; } txdesc->callback = rockchip_spi_dma_txcb; - txdesc->callback_param = master; + txdesc->callback_param = ctlr; } /* rx must be started before tx due to spi instinct */ if (rxdesc) { atomic_or(RXDMA, &rs->state); dmaengine_submit(rxdesc); - dma_async_issue_pending(master->dma_rx); + dma_async_issue_pending(ctlr->dma_rx); } spi_enable_chip(rs, true); @@ -448,7 +448,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, if (txdesc) { atomic_or(TXDMA, &rs->state); dmaengine_submit(txdesc); - dma_async_issue_pending(master->dma_tx); + dma_async_issue_pending(ctlr->dma_tx); } /* 1 means the transfer is in progress */ @@ -493,7 +493,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs, break; default: /* we only whitelist 4, 8 and 16 bit words in - * master->bits_per_word_mask, so this shouldn't + * ctlr->bits_per_word_mask, so this shouldn't * happen */ unreachable(); @@ -536,11 +536,11 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi) } static int rockchip_spi_transfer_one( - struct spi_master *master, + struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer) { - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); bool use_dma; WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) && @@ -558,21 +558,21 @@ static int rockchip_spi_transfer_one( rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2; - use_dma = master->can_dma ? master->can_dma(master, spi, xfer) : false; + use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false; rockchip_spi_config(rs, spi, xfer, use_dma); if (use_dma) - return rockchip_spi_prepare_dma(rs, master, xfer); + return rockchip_spi_prepare_dma(rs, ctlr, xfer); return rockchip_spi_prepare_irq(rs, xfer); } -static bool rockchip_spi_can_dma(struct spi_master *master, +static bool rockchip_spi_can_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer) { - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); unsigned int bytes_per_word = xfer->bits_per_word <= 8 ? 1 : 2; /* if the numbor of spi words to transfer is less than the fifo @@ -586,44 +586,44 @@ static int rockchip_spi_probe(struct platform_device *pdev) { int ret; struct rockchip_spi *rs; - struct spi_master *master; + struct spi_controller *ctlr; struct resource *mem; u32 rsd_nsecs; - master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi)); - if (!master) + ctlr = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi)); + if (!ctlr) return -ENOMEM; - platform_set_drvdata(pdev, master); + platform_set_drvdata(pdev, ctlr); - rs = spi_master_get_devdata(master); + rs = spi_controller_get_devdata(ctlr); /* Get basic io resource and map it */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); rs->regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(rs->regs)) { ret = PTR_ERR(rs->regs); - goto err_put_master; + goto err_put_ctlr; } rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk"); if (IS_ERR(rs->apb_pclk)) { dev_err(&pdev->dev, "Failed to get apb_pclk\n"); ret = PTR_ERR(rs->apb_pclk); - goto err_put_master; + goto err_put_ctlr; } rs->spiclk = devm_clk_get(&pdev->dev, "spiclk"); if (IS_ERR(rs->spiclk)) { dev_err(&pdev->dev, "Failed to get spi_pclk\n"); ret = PTR_ERR(rs->spiclk); - goto err_put_master; + goto err_put_ctlr; } ret = clk_prepare_enable(rs->apb_pclk); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable apb_pclk\n"); - goto err_put_master; + goto err_put_ctlr; } ret = clk_prepare_enable(rs->spiclk); @@ -639,7 +639,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_disable_spiclk; ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL, - IRQF_ONESHOT, dev_name(&pdev->dev), master); + IRQF_ONESHOT, dev_name(&pdev->dev), ctlr); if (ret) goto err_disable_spiclk; @@ -673,78 +673,78 @@ static int rockchip_spi_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - master->auto_runtime_pm = true; - master->bus_num = pdev->id; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST; - master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; - master->dev.of_node = pdev->dev.of_node; - master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); - master->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; - master->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); - - master->set_cs = rockchip_spi_set_cs; - master->transfer_one = rockchip_spi_transfer_one; - master->max_transfer_size = rockchip_spi_max_transfer_size; - master->handle_err = rockchip_spi_handle_err; - master->flags = SPI_MASTER_GPIO_SS; - - master->dma_tx = dma_request_chan(rs->dev, "tx"); - if (IS_ERR(master->dma_tx)) { + ctlr->auto_runtime_pm = true; + ctlr->bus_num = pdev->id; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST; + ctlr->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; + ctlr->dev.of_node = pdev->dev.of_node; + ctlr->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); + ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; + ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); + + ctlr->set_cs = rockchip_spi_set_cs; + ctlr->transfer_one = rockchip_spi_transfer_one; + ctlr->max_transfer_size = rockchip_spi_max_transfer_size; + ctlr->handle_err = rockchip_spi_handle_err; + ctlr->flags = SPI_MASTER_GPIO_SS; + + ctlr->dma_tx = dma_request_chan(rs->dev, "tx"); + if (IS_ERR(ctlr->dma_tx)) { /* Check tx to see if we need defer probing driver */ - if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) { + if (PTR_ERR(ctlr->dma_tx) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_disable_pm_runtime; } dev_warn(rs->dev, "Failed to request TX DMA channel\n"); - master->dma_tx = NULL; + ctlr->dma_tx = NULL; } - master->dma_rx = dma_request_chan(rs->dev, "rx"); - if (IS_ERR(master->dma_rx)) { - if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) { + ctlr->dma_rx = dma_request_chan(rs->dev, "rx"); + if (IS_ERR(ctlr->dma_rx)) { + if (PTR_ERR(ctlr->dma_rx) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_free_dma_tx; } dev_warn(rs->dev, "Failed to request RX DMA channel\n"); - master->dma_rx = NULL; + ctlr->dma_rx = NULL; } - if (master->dma_tx && master->dma_rx) { + if (ctlr->dma_tx && ctlr->dma_rx) { rs->dma_addr_tx = mem->start + ROCKCHIP_SPI_TXDR; rs->dma_addr_rx = mem->start + ROCKCHIP_SPI_RXDR; - master->can_dma = rockchip_spi_can_dma; + ctlr->can_dma = rockchip_spi_can_dma; } - ret = devm_spi_register_master(&pdev->dev, master); + ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret < 0) { - dev_err(&pdev->dev, "Failed to register master\n"); + dev_err(&pdev->dev, "Failed to register controller\n"); goto err_free_dma_rx; } return 0; err_free_dma_rx: - if (master->dma_rx) - dma_release_channel(master->dma_rx); + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); err_free_dma_tx: - if (master->dma_tx) - dma_release_channel(master->dma_tx); + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); err_disable_pm_runtime: pm_runtime_disable(&pdev->dev); err_disable_spiclk: clk_disable_unprepare(rs->spiclk); err_disable_apbclk: clk_disable_unprepare(rs->apb_pclk); -err_put_master: - spi_master_put(master); +err_put_ctlr: + spi_controller_put(ctlr); return ret; } static int rockchip_spi_remove(struct platform_device *pdev) { - struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = spi_controller_get(platform_get_drvdata(pdev)); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); pm_runtime_get_sync(&pdev->dev); @@ -755,12 +755,12 @@ static int rockchip_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - if (master->dma_tx) - dma_release_channel(master->dma_tx); - if (master->dma_rx) - dma_release_channel(master->dma_rx); + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); - spi_master_put(master); + spi_controller_put(ctlr); return 0; } @@ -769,9 +769,9 @@ static int rockchip_spi_remove(struct platform_device *pdev) static int rockchip_spi_suspend(struct device *dev) { int ret; - struct spi_master *master = dev_get_drvdata(dev); + struct spi_controller *ctlr = dev_get_drvdata(dev); - ret = spi_master_suspend(master); + ret = spi_controller_suspend(ctlr); if (ret < 0) return ret; @@ -787,8 +787,8 @@ static int rockchip_spi_suspend(struct device *dev) static int rockchip_spi_resume(struct device *dev) { int ret; - struct spi_master *master = dev_get_drvdata(dev); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); pinctrl_pm_select_default_state(dev); @@ -796,7 +796,7 @@ static int rockchip_spi_resume(struct device *dev) if (ret < 0) return ret; - ret = spi_master_resume(master); + ret = spi_controller_resume(ctlr); if (ret < 0) { clk_disable_unprepare(rs->spiclk); clk_disable_unprepare(rs->apb_pclk); @@ -809,8 +809,8 @@ static int rockchip_spi_resume(struct device *dev) #ifdef CONFIG_PM static int rockchip_spi_runtime_suspend(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); clk_disable_unprepare(rs->spiclk); clk_disable_unprepare(rs->apb_pclk); @@ -821,8 +821,8 @@ static int rockchip_spi_runtime_suspend(struct device *dev) static int rockchip_spi_runtime_resume(struct device *dev) { int ret; - struct spi_master *master = dev_get_drvdata(dev); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); ret = clk_prepare_enable(rs->apb_pclk); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From d065f41a3f0d0974593122e4864e158bb945696e Mon Sep 17 00:00:00 2001 From: Chris Ruehl Date: Mon, 11 May 2020 16:30:21 +0800 Subject: spi: spi-rockchip: add support for spi slave mode Add support for spi slave mode in spi-rockchip. The register map has an entry for it. If spi-slave is set in dts, set this corresponding bit and add to mode_bits the SPI_NO_CS, allow slave mode without explicit CS use. Slave abort function had been added. Signed-off-by: Chris Ruehl Link: https://lore.kernel.org/r/20200511083022.23678-3-chris.ruehl@gtsys.com.hk Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index c501d8a84414..f57a8faa6f31 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -183,6 +183,8 @@ struct rockchip_spi { u8 rsd; bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; + + bool slave_abort; }; static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) @@ -359,7 +361,7 @@ static void rockchip_spi_dma_rxcb(void *data) struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); int state = atomic_fetch_andnot(RXDMA, &rs->state); - if (state & TXDMA) + if (state & TXDMA && !rs->slave_abort) return; spi_enable_chip(rs, false); @@ -372,7 +374,7 @@ static void rockchip_spi_dma_txcb(void *data) struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); int state = atomic_fetch_andnot(TXDMA, &rs->state); - if (state & RXDMA) + if (state & RXDMA && !rs->slave_abort) return; /* Wait until the FIFO data completely. */ @@ -457,7 +459,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, static void rockchip_spi_config(struct rockchip_spi *rs, struct spi_device *spi, struct spi_transfer *xfer, - bool use_dma) + bool use_dma, bool slave_mode) { u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET | CR0_BHT_8BIT << CR0_BHT_OFFSET @@ -466,6 +468,10 @@ static void rockchip_spi_config(struct rockchip_spi *rs, u32 cr1; u32 dmacr = 0; + if (slave_mode) + cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET; + rs->slave_abort = false; + cr0 |= rs->rsd << CR0_RSD_OFFSET; cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET; if (spi->mode & SPI_LSB_FIRST) @@ -535,6 +541,16 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi) return ROCKCHIP_SPI_MAX_TRANLEN; } +static int rockchip_spi_slave_abort(struct spi_controller *ctlr) +{ + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + + rs->slave_abort = true; + complete(&ctlr->xfer_completion); + + return 0; +} + static int rockchip_spi_transfer_one( struct spi_controller *ctlr, struct spi_device *spi, @@ -560,7 +576,7 @@ static int rockchip_spi_transfer_one( use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false; - rockchip_spi_config(rs, spi, xfer, use_dma); + rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave); if (use_dma) return rockchip_spi_prepare_dma(rs, ctlr, xfer); @@ -588,15 +604,26 @@ static int rockchip_spi_probe(struct platform_device *pdev) struct rockchip_spi *rs; struct spi_controller *ctlr; struct resource *mem; + struct device_node *np = pdev->dev.of_node; u32 rsd_nsecs; + bool slave_mode; + + slave_mode = of_property_read_bool(np, "spi-slave"); + + if (slave_mode) + ctlr = spi_alloc_slave(&pdev->dev, + sizeof(struct rockchip_spi)); + else + ctlr = spi_alloc_master(&pdev->dev, + sizeof(struct rockchip_spi)); - ctlr = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi)); if (!ctlr) return -ENOMEM; platform_set_drvdata(pdev, ctlr); rs = spi_controller_get_devdata(ctlr); + ctlr->slave = slave_mode; /* Get basic io resource and map it */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -676,6 +703,12 @@ static int rockchip_spi_probe(struct platform_device *pdev) ctlr->auto_runtime_pm = true; ctlr->bus_num = pdev->id; ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST; + if (slave_mode) { + ctlr->mode_bits |= SPI_NO_CS; + ctlr->slave_abort = rockchip_spi_slave_abort; + } else { + ctlr->flags = SPI_MASTER_GPIO_SS; + } ctlr->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; ctlr->dev.of_node = pdev->dev.of_node; ctlr->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); @@ -686,7 +719,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) ctlr->transfer_one = rockchip_spi_transfer_one; ctlr->max_transfer_size = rockchip_spi_max_transfer_size; ctlr->handle_err = rockchip_spi_handle_err; - ctlr->flags = SPI_MASTER_GPIO_SS; ctlr->dma_tx = dma_request_chan(rs->dev, "tx"); if (IS_ERR(ctlr->dma_tx)) { -- cgit v1.2.3-59-g8ed1b From eb1262e3cc8b529218e085254888c90f096a5da5 Mon Sep 17 00:00:00 2001 From: Chris Ruehl Date: Mon, 11 May 2020 16:30:22 +0800 Subject: spi: spi-rockchip: use num-cs property and ctlr->enable_gpiods The original implementation set num_chipselect to ROCKCHIP_SPI_MAX_CS_NUM (2) which seems wrong here. spi0 has 2 native cs, all others just one. With enable and use of cs_gpiods / GPIO CS, its correct to set the num_chipselect from the num-cs property and set max_native_cs with the define. If num-cs is missing the default set to num_chipselect = 1. Signed-off-by: Chris Ruehl Link: https://lore.kernel.org/r/20200511083022.23678-4-chris.ruehl@gtsys.com.hk Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index f57a8faa6f31..9b8a5e1233c0 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -708,8 +708,15 @@ static int rockchip_spi_probe(struct platform_device *pdev) ctlr->slave_abort = rockchip_spi_slave_abort; } else { ctlr->flags = SPI_MASTER_GPIO_SS; + ctlr->max_native_cs = ROCKCHIP_SPI_MAX_CS_NUM; + /* + * rk spi0 has two native cs, spi1..5 one cs only + * if num-cs is missing in the dts, default to 1 + */ + if (of_property_read_u16(np, "num-cs", &ctlr->num_chipselect)) + ctlr->num_chipselect = 1; + ctlr->use_gpio_descriptors = true; } - ctlr->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; ctlr->dev.of_node = pdev->dev.of_node; ctlr->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; -- cgit v1.2.3-59-g8ed1b From 05897c710e8e095abf80937e23068deb600f7b0c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 15 May 2020 17:58:04 +0200 Subject: spi: bcm2835: Tear down DMA before turning off SPI controller On unbind of the BCM2835 SPI driver, the SPI controller is disabled first and the DMA channels are terminated and torn down afterwards. This seems backwards: In the theoretical case that DMA is active, it might try to fill the SPI FIFOs even after the controller has been disabled. Reverse the order, thereby mirroring what's done on ->probe(). Signed-off-by: Lukas Wunner Link: https://lore.kernel.org/r/ac79f1e3d6fd9a1f5e0cb4008c43b98ea70be3c2.1589557526.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 06d2782d38ec..20d8581fdf88 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -1380,14 +1380,14 @@ static int bcm2835_spi_remove(struct platform_device *pdev) spi_unregister_controller(ctlr); + bcm2835_dma_release(ctlr, bs); + /* Clear FIFOs, and disable the HW block */ bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); clk_disable_unprepare(bs->clk); - bcm2835_dma_release(ctlr, bs); - return 0; } -- cgit v1.2.3-59-g8ed1b From 43dba9f3f98c2b184a19f856f06fe22817bfd9e0 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 22 May 2020 03:07:51 +0300 Subject: spi: dw: Enable interrupts in accordance with DMA xfer mode It's pointless to track the Tx overrun interrupts if Rx-only SPI transfer is issued. Similarly there is no need in handling the Rx overrun/underrun interrupts if Tx-only SPI transfer is executed. So lets unmask the interrupts only if corresponding SPI transactions are implied. Co-developed-by: Georgy Vlasov Signed-off-by: Georgy Vlasov Signed-off-by: Serge Semin Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle Cc: Arnd Bergmann Cc: Andy Shevchenko Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200522000806.7381-3-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index f9757a370699..1578dfebc92b 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -243,19 +243,23 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { - u16 dma_ctrl = 0; + u16 imr = 0, dma_ctrl = 0; dw_writel(dws, DW_SPI_DMARDLR, 0xf); dw_writel(dws, DW_SPI_DMATDLR, 0x10); - if (xfer->tx_buf) + if (xfer->tx_buf) { dma_ctrl |= SPI_DMA_TDMAE; - if (xfer->rx_buf) + imr |= SPI_INT_TXOI; + } + if (xfer->rx_buf) { dma_ctrl |= SPI_DMA_RDMAE; + imr |= SPI_INT_RXUI | SPI_INT_RXOI; + } dw_writel(dws, DW_SPI_DMACR, dma_ctrl); /* Set the interrupt mask */ - spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI); + spi_umask_intr(dws, imr); dws->transfer_handler = dma_transfer; -- cgit v1.2.3-59-g8ed1b From 2afccbd283ae63072391ea17ac1d772ed9d33749 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 22 May 2020 03:07:52 +0300 Subject: spi: dw: Discard static DW DMA slave structures Having them declared is redundant since each struct dw_dma_chan has the same structure embedded and the structure from the passed dma_chan private pointer will be copied there as a result of the next calls chain: dma_request_channel() -> find_candidate() -> dma_chan_get() -> device_alloc_chan_resources() = dwc_alloc_chan_resources() -> dw_dma_filter(). So just remove the static dw_dma_chan structures and use a locally declared data instance with dst_id/src_id set to the same values as the static copies used to have. Co-developed-by: Georgy Vlasov Signed-off-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle Cc: Andy Shevchenko Cc: Arnd Bergmann Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200522000806.7381-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 19 ++++++++----------- drivers/spi/spi-dw.h | 2 -- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 1578dfebc92b..a39bde9996a4 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -20,9 +20,6 @@ #define RX_BUSY 0 #define TX_BUSY 1 -static struct dw_dma_slave mid_dma_tx = { .dst_id = 1 }; -static struct dw_dma_slave mid_dma_rx = { .src_id = 0 }; - static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) { struct dw_dma_slave *s = param; @@ -36,9 +33,11 @@ static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) { + struct dw_dma_slave slave = { + .src_id = 0, + .dst_id = 0 + }; struct pci_dev *dma_dev; - struct dw_dma_slave *tx = dws->dma_tx; - struct dw_dma_slave *rx = dws->dma_rx; dma_cap_mask_t mask; /* @@ -53,14 +52,14 @@ static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) dma_cap_set(DMA_SLAVE, mask); /* 1. Init rx channel */ - rx->dma_dev = &dma_dev->dev; - dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx); + slave.dma_dev = &dma_dev->dev; + dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); if (!dws->rxchan) goto err_exit; /* 2. Init tx channel */ - tx->dma_dev = &dma_dev->dev; - dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx); + slave.dst_id = 1; + dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); if (!dws->txchan) goto free_rxchan; @@ -317,8 +316,6 @@ static const struct dw_spi_dma_ops mfld_dma_ops = { static void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) { - dws->dma_tx = &mid_dma_tx; - dws->dma_rx = &mid_dma_rx; dws->dma_ops = &mfld_dma_ops; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index e92d43b9a9e6..e1b565438f23 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -146,8 +146,6 @@ struct dw_spi { unsigned long dma_chan_busy; dma_addr_t dma_addr; /* phy address of the Data register */ const struct dw_spi_dma_ops *dma_ops; - void *dma_tx; - void *dma_rx; /* Bus interface info */ void *priv; -- cgit v1.2.3-59-g8ed1b From 595c19d4543fed18384fd7d1edf6f381d4eed34e Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 22 May 2020 03:07:53 +0300 Subject: spi: dw: Discard unused void priv pointer Seeing the "void *priv" member of the dw_spi data structure is unused let's remove it. The glue-layers can embed the DW APB SSI controller descriptor into their private data object. MMIO driver for instance already utilizes that design pattern. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle Cc: Andy Shevchenko Cc: Arnd Bergmann Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200522000806.7381-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index e1b565438f23..0aa3a4be1046 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -147,8 +147,6 @@ struct dw_spi { dma_addr_t dma_addr; /* phy address of the Data register */ const struct dw_spi_dma_ops *dma_ops; - /* Bus interface info */ - void *priv; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; #endif -- cgit v1.2.3-59-g8ed1b From 4fdc03a9bc47b6cf35a8821e545075d9f5f24906 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 22 May 2020 03:07:54 +0300 Subject: spi: dw: Discard dma_width member of the dw_spi structure This member has exactly the same value as n_bytes of the DW SPI private data object, it's calculated at the same point of the transfer method, n_bytes isn't changed during the whole transfer, and they even serve for the same purpose - keep number of bytes per transfer word, though the dma_width is used only to calculate the DMA source/destination addresses width, which n_bytes could be also utilized for. Taking all of these into account let's replace the dma_width member usage with n_bytes one and remove the former. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Paul Burton Cc: Ralf Baechle Cc: Andy Shevchenko Cc: Arnd Bergmann Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200522000806.7381-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 10 +++++----- drivers/spi/spi-dw.c | 1 - drivers/spi/spi-dw.h | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index a39bde9996a4..b1710132b7b2 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -133,10 +133,10 @@ static bool mid_spi_can_dma(struct spi_controller *master, return xfer->len > dws->fifo_len; } -static enum dma_slave_buswidth convert_dma_width(u32 dma_width) { - if (dma_width == 1) +static enum dma_slave_buswidth convert_dma_width(u8 n_bytes) { + if (n_bytes == 1) return DMA_SLAVE_BUSWIDTH_1_BYTE; - else if (dma_width == 2) + else if (n_bytes == 2) return DMA_SLAVE_BUSWIDTH_2_BYTES; return DMA_SLAVE_BUSWIDTH_UNDEFINED; @@ -172,7 +172,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = 16; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - txconf.dst_addr_width = convert_dma_width(dws->dma_width); + txconf.dst_addr_width = convert_dma_width(dws->n_bytes); txconf.device_fc = false; dmaengine_slave_config(dws->txchan, &txconf); @@ -221,7 +221,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = 16; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - rxconf.src_addr_width = convert_dma_width(dws->dma_width); + rxconf.src_addr_width = convert_dma_width(dws->n_bytes); rxconf.device_fc = false; dmaengine_slave_config(dws->rxchan, &rxconf); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 450c8218caeb..1edb8cdd11ee 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -353,7 +353,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, } dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); - dws->dma_width = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); cr0 = dws->update_cr0(master, spi, transfer); dw_writel(dws, DW_SPI_CTRLR0, cr0); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 0aa3a4be1046..79782e93eb12 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -136,7 +136,6 @@ struct dw_spi { void *rx_end; int dma_mapped; u8 n_bytes; /* current is a 1/2 bytes op */ - u32 dma_width; irqreturn_t (*transfer_handler)(struct dw_spi *dws); u32 current_freq; /* frequency in hz */ -- cgit v1.2.3-59-g8ed1b From 678e5e1e42d74f77a6e2e9feb6f95ed72a996251 Mon Sep 17 00:00:00 2001 From: Christopher Hill Date: Thu, 21 May 2020 14:36:29 -0400 Subject: spi: rb4xx: null pointer bug fix This patch fixes a null pointer bug in the spi driver spi-rb4xx.c by moving the private data initialization to earlier in probe Signed-off-by: Christopher Hill Link: https://lore.kernel.org/r/20200521183631.37806-1-ch6574@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-rb4xx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 4c9620e0d18c..17e1a77dc132 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -158,6 +158,11 @@ static int rb4xx_spi_probe(struct platform_device *pdev) master->transfer_one = rb4xx_transfer_one; master->set_cs = rb4xx_set_cs; + rbspi = spi_master_get_devdata(master); + rbspi->base = spi_base; + rbspi->clk = ahb_clk; + platform_set_drvdata(pdev, rbspi); + err = devm_spi_register_master(&pdev->dev, master); if (err) { dev_err(&pdev->dev, "failed to register SPI master\n"); @@ -168,11 +173,6 @@ static int rb4xx_spi_probe(struct platform_device *pdev) if (err) return err; - rbspi = spi_master_get_devdata(master); - rbspi->base = spi_base; - rbspi->clk = ahb_clk; - platform_set_drvdata(pdev, rbspi); - /* Enable SPI */ rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); -- cgit v1.2.3-59-g8ed1b From 9a436c62fbb4c57c6f0be01e4fc368ed5da6b730 Mon Sep 17 00:00:00 2001 From: Christopher Hill Date: Thu, 21 May 2020 14:36:30 -0400 Subject: spi: rb4xx: update driver to be device tree aware This patch updates the spi driver spi-rb4xx.c to be device tree aware Signed-off-by: Christopher Hill Link: https://lore.kernel.org/r/20200521183631.37806-2-ch6574@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-rb4xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 17e1a77dc132..8aa51beb4ff3 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -150,6 +151,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev) if (IS_ERR(ahb_clk)) return PTR_ERR(ahb_clk); + master->dev.of_node = pdev->dev.of_node; master->bus_num = 0; master->num_chipselect = 3; master->mode_bits = SPI_TX_DUAL; @@ -188,11 +190,18 @@ static int rb4xx_spi_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id rb4xx_spi_dt_match[] = { + { .compatible = "mikrotik,rb4xx-spi" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rb4xx_spi_dt_match); + static struct platform_driver rb4xx_spi_drv = { .probe = rb4xx_spi_probe, .remove = rb4xx_spi_remove, .driver = { .name = "rb4xx-spi", + .of_match_table = of_match_ptr(rb4xx_spi_dt_match), }, }; -- cgit v1.2.3-59-g8ed1b From 8fede89f853a7bfc671f9d8f069ccfe4190061f3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 22 May 2020 16:50:05 +0100 Subject: spi: Make spi_delay_exec() warn if called from atomic context If the delay used is long enough the spi_delay_exec() will use a sleeping function to implement it. Add a might_sleep() here to help avoid callers using this from an atomic context and running into problems at runtime on other systems. Suggested-by: Serge Semin Signed-off-by: Mark Brown Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200522155005.46099-1-broonie@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d32bdc6cbf66..c765c966cc8e 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1160,6 +1160,8 @@ int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer) { int delay; + might_sleep(); + if (!_delay) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 61367d0b8f5edf5146059ba8b79ce4e4485340b2 Mon Sep 17 00:00:00 2001 From: dillon min Date: Mon, 25 May 2020 11:45:47 +0800 Subject: spi: stm32: Add 'SPI_SIMPLEX_RX', 'SPI_3WIRE_RX' support for stm32f4 in l3gd20 driver startup, there is a setup failed error return from stm32 spi driver " [ 2.687630] st-gyro-spi spi0.0: supply vdd not found, using dummy regulator [ 2.696869] st-gyro-spi spi0.0: supply vddio not found, using dummy regulator [ 2.706707] spi_stm32 40015000.spi: SPI transfer setup failed [ 2.713741] st-gyro-spi spi0.0: SPI transfer failed: -22 [ 2.721096] spi_master spi0: failed to transfer one message from queue [ 2.729268] iio iio:device0: failed to read Who-Am-I register. [ 2.737504] st-gyro-spi: probe of spi0.0 failed with error -22 " after debug into spi-stm32 driver, st-gyro-spi split two steps to read l3gd20 id first: send command to l3gd20 with read id command in tx_buf, rx_buf is null. second: read id with tx_buf is null, rx_buf not null. so, for second step, stm32 driver recongise this process as 'SPI_SIMPLE_RX' from stm32_spi_communication_type(), but there is no related process for this type in stm32f4_spi_set_mode(), then we get error from stm32_spi_transfer_one_setup(). we can use two method to fix this bug. 1, use stm32 spi's "In unidirectional receive-only mode (BIDIMODE=0 and RXONLY=1)". but as our code running in sdram, the read latency is too large to get so many receive overrun error in interrupts handler. 2, use stm32 spi's "In full-duplex (BIDIMODE=0 and RXONLY=0)", as tx_buf is null, so add flag 'SPI_MASTER_MUST_TX' to spi master. Change since V4: 1 remove dummy data sent out by stm32 spi driver 2 add flag 'SPI_MASTER_MUST_TX' to spi master Signed-off-by: dillon min Link: https://lore.kernel.org/r/1590378348-8115-8-git-send-email-dillon.minfei@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 44ac6eb3298d..4c643dfc7fbb 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -811,7 +811,9 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id) mask |= STM32F4_SPI_SR_TXE; } - if (!spi->cur_usedma && spi->cur_comm == SPI_FULL_DUPLEX) { + if (!spi->cur_usedma && (spi->cur_comm == SPI_FULL_DUPLEX || + spi->cur_comm == SPI_SIMPLEX_RX || + spi->cur_comm == SPI_3WIRE_RX)) { /* TXE flag is set and is handled when RXNE flag occurs */ sr &= ~STM32F4_SPI_SR_TXE; mask |= STM32F4_SPI_SR_RXNE | STM32F4_SPI_SR_OVR; @@ -850,7 +852,7 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id) stm32f4_spi_read_rx(spi); if (spi->rx_len == 0) end = true; - else /* Load data for discontinuous mode */ + else if (spi->tx_buf)/* Load data for discontinuous mode */ stm32f4_spi_write_tx(spi); } @@ -1151,7 +1153,9 @@ static int stm32f4_spi_transfer_one_irq(struct stm32_spi *spi) /* Enable the interrupts relative to the current communication mode */ if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) { cr2 |= STM32F4_SPI_CR2_TXEIE; - } else if (spi->cur_comm == SPI_FULL_DUPLEX) { + } else if (spi->cur_comm == SPI_FULL_DUPLEX || + spi->cur_comm == SPI_SIMPLEX_RX || + spi->cur_comm == SPI_3WIRE_RX) { /* In transmit-only mode, the OVR flag is set in the SR register * since the received data are never read. Therefore set OVR * interrupt only when rx buffer is available. @@ -1462,10 +1466,16 @@ static int stm32f4_spi_set_mode(struct stm32_spi *spi, unsigned int comm_type) stm32_spi_set_bits(spi, STM32F4_SPI_CR1, STM32F4_SPI_CR1_BIDIMODE | STM32F4_SPI_CR1_BIDIOE); - } else if (comm_type == SPI_FULL_DUPLEX) { + } else if (comm_type == SPI_FULL_DUPLEX || + comm_type == SPI_SIMPLEX_RX) { stm32_spi_clr_bits(spi, STM32F4_SPI_CR1, STM32F4_SPI_CR1_BIDIMODE | STM32F4_SPI_CR1_BIDIOE); + } else if (comm_type == SPI_3WIRE_RX) { + stm32_spi_set_bits(spi, STM32F4_SPI_CR1, + STM32F4_SPI_CR1_BIDIMODE); + stm32_spi_clr_bits(spi, STM32F4_SPI_CR1, + STM32F4_SPI_CR1_BIDIOE); } else { return -EINVAL; } @@ -1906,6 +1916,7 @@ static int stm32_spi_probe(struct platform_device *pdev) master->prepare_message = stm32_spi_prepare_msg; master->transfer_one = stm32_spi_transfer_one; master->unprepare_message = stm32_spi_unprepare_msg; + master->flags = SPI_MASTER_MUST_TX; spi->dma_tx = dma_request_chan(spi->dev, "tx"); if (IS_ERR(spi->dma_tx)) { -- cgit v1.2.3-59-g8ed1b From aee67fe879e5030a2f5e1d9af3cb5b2a1027e78a Mon Sep 17 00:00:00 2001 From: dillon min Date: Mon, 25 May 2020 11:45:48 +0800 Subject: spi: flags 'SPI_CONTROLLER_MUST_RX' and 'SPI_CONTROLLER_MUST_TX' can't be coexit with 'SPI_3WIRE' mode since chip spi driver need get the transfer direction by 'tx_buf' and 'rx_buf' of 'struct spi_transfer' in 'SPI_3WIRE' mode. so, we need bypass 'SPI_CONTROLLER_MUST_RX' and 'SPI_CONTROLLER_MUST_TX' feature in 'SPI_3WIRE' mode Signed-off-by: dillon min Link: https://lore.kernel.org/r/1590378348-8115-9-git-send-email-dillon.minfei@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c765c966cc8e..9d0b3c292e1e 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1023,7 +1023,8 @@ static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) void *tmp; unsigned int max_tx, max_rx; - if (ctlr->flags & (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX)) { + if ((ctlr->flags & (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX)) + && !(msg->spi->mode & SPI_3WIRE)) { max_tx = 0; max_rx = 0; -- cgit v1.2.3-59-g8ed1b From c373643b8688836c1627a805875994fe0012fc17 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 25 May 2020 14:31:20 +0100 Subject: spi: Remove note about transfer limit for spi_write_then_read() Originally spi_write_then_read() used a fixed statically allocated buffer which limited the maximum message size it could handle. This restriction was removed a while ago so that we could dynamically allocate a buffer if required but the kerneldoc was not updated to reflect this, do so. Reported-by: Marc Kleine-Budde Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20200525133120.57273-1-broonie@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 9d0b3c292e1e..8158e281f354 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3858,8 +3858,7 @@ static u8 *buf; * is zero for success, else a negative errno status code. * This call may only be used from a context that may sleep. * - * Parameters to this routine are always copied using a small buffer; - * portable code should never use this for more than 32 bytes. + * Parameters to this routine are always copied using a small buffer. * Performance-sensitive or bulk transfer code should instead use * spi_{async,sync}() calls with dma-safe buffers. * -- cgit v1.2.3-59-g8ed1b From 8d728808194a12186ce5af0b72c8a47b42476bc3 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 21:38:59 +0800 Subject: spi: spi-fsl-lpspi: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Link: https://lore.kernel.org/r/20200523133859.5625-1-dinghao.liu@zju.edu.cn Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 511211b82430..1552b28b9515 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -940,7 +940,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(fsl_lpspi->dev); if (ret < 0) { dev_err(fsl_lpspi->dev, "failed to enable clock\n"); - goto out_controller_put; + goto out_pm_get; } temp = readl(fsl_lpspi->base + IMX7ULP_PARAM); @@ -949,13 +949,15 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller); if (ret == -EPROBE_DEFER) - goto out_controller_put; + goto out_pm_get; if (ret < 0) dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret); return 0; +out_pm_get: + pm_runtime_put_noidle(fsl_lpspi->dev); out_controller_put: spi_controller_put(controller); -- cgit v1.2.3-59-g8ed1b From bcd8e7761ec9c128b9102b0833d9c7052ae2dbcf Mon Sep 17 00:00:00 2001 From: Robin Gong Date: Thu, 21 May 2020 04:34:17 +0800 Subject: spi: imx: fallback to PIO if dma setup failure Fallback to PIO in case dma setup failed. For example, sdma firmware not updated but ERR009165 workaroud added in kernel. Signed-off-by: Robin Gong Acked-by: Sascha Hauer Link: https://lore.kernel.org/r/1590006865-20900-6-git-send-email-yibin.gong@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index f4f28a400a96..b7a85e3fe1c1 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -71,6 +71,7 @@ struct spi_imx_devtype_data { void (*reset)(struct spi_imx_data *); void (*setup_wml)(struct spi_imx_data *); void (*disable)(struct spi_imx_data *); + void (*disable_dma)(struct spi_imx_data *); bool has_dmamode; bool has_slavemode; unsigned int fifo_size; @@ -485,6 +486,11 @@ static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MX51_ECSPI_CTRL); } +static void mx51_disable_dma(struct spi_imx_data *spi_imx) +{ + writel(0, spi_imx->base + MX51_ECSPI_DMA); +} + static void mx51_ecspi_disable(struct spi_imx_data *spi_imx) { u32 ctrl; @@ -987,6 +993,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { .rx_available = mx51_ecspi_rx_available, .reset = mx51_ecspi_reset, .setup_wml = mx51_setup_wml, + .disable_dma = mx51_disable_dma, .fifo_size = 64, .has_dmamode = true, .dynamic_burst = true, @@ -1001,6 +1008,7 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = { .prepare_transfer = mx51_ecspi_prepare_transfer, .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, + .disable_dma = mx51_disable_dma, .reset = mx51_ecspi_reset, .fifo_size = 64, .has_dmamode = true, @@ -1385,6 +1393,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { dmaengine_terminate_all(master->dma_tx); + dmaengine_terminate_all(master->dma_rx); return -EINVAL; } @@ -1498,6 +1507,7 @@ static int spi_imx_transfer(struct spi_device *spi, struct spi_transfer *transfer) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + int ret; /* flush rxfifo before transfer */ while (spi_imx->devtype_data->rx_available(spi_imx)) @@ -1506,10 +1516,23 @@ static int spi_imx_transfer(struct spi_device *spi, if (spi_imx->slave_mode) return spi_imx_pio_transfer_slave(spi, transfer); - if (spi_imx->usedma) - return spi_imx_dma_transfer(spi_imx, transfer); - else - return spi_imx_pio_transfer(spi, transfer); + /* + * fallback PIO mode if dma setup error happen, for example sdma + * firmware may not be updated as ERR009165 required. + */ + if (spi_imx->usedma) { + ret = spi_imx_dma_transfer(spi_imx, transfer); + if (ret != -EINVAL) + return ret; + + spi_imx->devtype_data->disable_dma(spi_imx); + + spi_imx->usedma = false; + spi_imx->dynamic_burst = spi_imx->devtype_data->dynamic_burst; + dev_dbg(&spi->dev, "Fallback to PIO mode\n"); + } + + return spi_imx_pio_transfer(spi, transfer); } static int spi_imx_setup(struct spi_device *spi) -- cgit v1.2.3-59-g8ed1b From cddc36f3fd706b1046a4d4608359c0003f72db32 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 20:57:04 +0800 Subject: spi: tegra114: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Link: https://lore.kernel.org/r/20200523125704.30300-1-dinghao.liu@zju.edu.cn Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 83edabdb41ad..c2c58871a947 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1398,6 +1398,7 @@ static int tegra_spi_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + pm_runtime_put_noidle(&pdev->dev); goto exit_pm_disable; } -- cgit v1.2.3-59-g8ed1b From faedcc17ad183acfa5d74758ebc4f21aef341f11 Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 20:29:09 +0800 Subject: spi: tegra20-slink: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Link: https://lore.kernel.org/r/20200523122909.25247-1-dinghao.liu@zju.edu.cn Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 7f4d932dade7..a07b72e9c344 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1118,6 +1118,7 @@ static int tegra_slink_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + pm_runtime_put_noidle(&pdev->dev); goto exit_pm_disable; } tspi->def_command_reg = SLINK_M_S; -- cgit v1.2.3-59-g8ed1b From 117858bd63fb2ebcdfcc27b09fd65cb44511ea4c Mon Sep 17 00:00:00 2001 From: Dinghao Liu Date: Sat, 23 May 2020 20:47:58 +0800 Subject: spi: tegra20-sflash: Fix runtime PM imbalance on error pm_runtime_get_sync() increments the runtime PM usage counter even when it returns an error code. Thus a pairing decrement is needed on the error handling path to keep the counter balanced. Signed-off-by: Dinghao Liu Link: https://lore.kernel.org/r/20200523124758.28604-1-dinghao.liu@zju.edu.cn Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-sflash.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 514429379206..02cf5f463ba6 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -491,6 +491,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + pm_runtime_put_noidle(&pdev->dev); goto exit_pm_disable; } -- cgit v1.2.3-59-g8ed1b From de4c2875a5ff2c886df60f2086c6affca83f890a Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:50 +0300 Subject: spi: dw: Set xfer effective_speed_hz Seeing DW APB SSI controller doesn't support setting the exactly requested SPI bus frequency, but only a rounded frequency determined by means of the odd-numbered half-worded reference clock divider, it would be good to tune the SPI core up and initialize the current transfer effective_speed_hz. By doing so the core will be able to execute the xfer-related delays with better accuracy. Signed-off-by: Serge Semin Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Andy Shevchenko Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-2-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 1edb8cdd11ee..e6fc0bcf214d 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -352,6 +352,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_set_clk(dws, chip->clk_div); } + transfer->effective_speed_hz = dws->max_freq / chip->clk_div; dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); cr0 = dws->update_cr0(master, spi, transfer); -- cgit v1.2.3-59-g8ed1b From f0410bbf7d0fb80149e3b17d11d31f5b5197873e Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:51 +0300 Subject: spi: dw: Return any value retrieved from the dma_transfer callback DW APB SSI DMA-part of the driver may need to perform the requested SPI-transfer synchronously. In that case the dma_transfer() callback will return 0 as a marker of the SPI transfer being finished so the SPI core doesn't need to wait and may proceed with the SPI message trasnfers pumping procedure. This will be needed to fix the problem when DMA transactions are finished, but there is still data left in the SPI Tx/Rx FIFOs being sent/received. But for now make dma_transfer to return 1 as the normal dw_spi_transfer_one() method. Signed-off-by: Serge Semin Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Andy Shevchenko Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-3-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 2 +- drivers/spi/spi-dw.c | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index b1710132b7b2..7ff1acaa55f8 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -288,7 +288,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) dma_async_issue_pending(dws->txchan); } - return 0; + return 1; } static void mid_spi_dma_stop(struct dw_spi *dws) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index e6fc0bcf214d..73af58dbcd6a 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -389,11 +389,8 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_enable_chip(dws, 1); - if (dws->dma_mapped) { - ret = dws->dma_ops->dma_transfer(dws, transfer); - if (ret < 0) - return ret; - } + if (dws->dma_mapped) + return dws->dma_ops->dma_transfer(dws, transfer); return 1; } -- cgit v1.2.3-59-g8ed1b From bdbdf0f06337d3661b64c0288c291cb06624065e Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:52 +0300 Subject: spi: dw: Locally wait for the DMA transfers completion In general each DMA-based SPI transfer can be split up into two stages: DMA data transmission/reception and SPI-bus transmission/reception. DMA asynchronous transactions completion can be tracked by means of the DMA async Tx-descriptor completion callback. But that callback being called indicates that the DMA transfer has been finished, it doesn't mean that SPI data transmission is also done. Moreover in fact it isn't for at least Tx-only SPI transfers. Upon DMA transfer completion some data is left in the Tx FIFO and being pushed out by the SPI controller. So in order to make sure that an SPI transfer is completely pushed to the SPI-bus, the driver has to wait for both DMA transaction and the SPI-bus transmission/reception are finished. Note if there is a way to asynchronously track the former event by means of the DMA async Tx callback, there isn't easy one for the later (IRQ-based solution won't work since SPI controller doesn't notify about Rx FIFO being empty). The DMA transfer completion callback isn't suitable to wait for the SPI controller activity finish either. The callback might (in case of DW DMAC it will) be called in the tasklet context. Waiting for the SPI controller to complete the transfer might take a considerable amount of time since SPI-bus might be pretty slow. In this case delaying the execution in the tasklet atomic context might cause significant system performance drop. So to speak the best option we've got to solve the problem is to consequently wait for both stages being finished in the locally implemented SPI transfer execution procedure even if it costs us of the local wait-function re-implementation. In this case we don't need to use the SPI-core transfer-wait functionality, but we'll make sure that all DMA and SPI-bus transactions are completely finished before the SPI-core transfer_one callback returns. In this commit we provide an implementation of the DMA-transfers completion wait functionality. The DW APB SSI DMA-specific SPI transfer_one function waits for both Tx and Rx DMA transfers being finished, and only then exits with zero returned signalling to the SPI core that the SPI transfer is finished. This implementation is fully equivalent to the currently used DMA-execution-SPI-core-wait algorithm. The SPI-bus transmission/reception wait methods will be added in the follow-up commits. Signed-off-by: Serge Semin Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Andy Shevchenko Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 44 ++++++++++++++++++++++++++++++++++++++++---- drivers/spi/spi-dw.h | 2 ++ 2 files changed, 42 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 7ff1acaa55f8..355b641c4483 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -11,9 +11,11 @@ #include "spi-dw.h" #ifdef CONFIG_SPI_DW_MID_DMA +#include #include #include #include +#include #include #include @@ -66,6 +68,8 @@ static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) dws->master->dma_rx = dws->rxchan; dws->master->dma_tx = dws->txchan; + init_completion(&dws->dma_completion); + return 0; free_rxchan: @@ -91,6 +95,8 @@ static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) dws->master->dma_rx = dws->rxchan; dws->master->dma_tx = dws->txchan; + init_completion(&dws->dma_completion); + return 0; } @@ -121,7 +127,7 @@ static irqreturn_t dma_transfer(struct dw_spi *dws) dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); dws->master->cur_msg->status = -EIO; - spi_finalize_current_transfer(dws->master); + complete(&dws->dma_completion); return IRQ_HANDLED; } @@ -142,6 +148,29 @@ static enum dma_slave_buswidth convert_dma_width(u8 n_bytes) { return DMA_SLAVE_BUSWIDTH_UNDEFINED; } +static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer) +{ + unsigned long long ms; + + ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE; + do_div(ms, xfer->effective_speed_hz); + ms += ms + 200; + + if (ms > UINT_MAX) + ms = UINT_MAX; + + ms = wait_for_completion_timeout(&dws->dma_completion, + msecs_to_jiffies(ms)); + + if (ms == 0) { + dev_err(&dws->master->cur_msg->spi->dev, + "DMA transaction timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + /* * dws->dma_chan_busy is set before the dma transfer starts, callback for tx * channel will clear a corresponding bit. @@ -155,7 +184,7 @@ static void dw_spi_dma_tx_done(void *arg) return; dw_writel(dws, DW_SPI_DMACR, 0); - spi_finalize_current_transfer(dws->master); + complete(&dws->dma_completion); } static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, @@ -204,7 +233,7 @@ static void dw_spi_dma_rx_done(void *arg) return; dw_writel(dws, DW_SPI_DMACR, 0); - spi_finalize_current_transfer(dws->master); + complete(&dws->dma_completion); } static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, @@ -260,6 +289,8 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) /* Set the interrupt mask */ spi_umask_intr(dws, imr); + reinit_completion(&dws->dma_completion); + dws->transfer_handler = dma_transfer; return 0; @@ -268,6 +299,7 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *txdesc, *rxdesc; + int ret; /* Prepare the TX dma transfer */ txdesc = dw_spi_dma_prepare_tx(dws, xfer); @@ -288,7 +320,11 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) dma_async_issue_pending(dws->txchan); } - return 1; + ret = dw_spi_dma_wait(dws, xfer); + if (ret) + return ret; + + return 0; } static void mid_spi_dma_stop(struct dw_spi *dws) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 79782e93eb12..9585d0c83a6d 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -2,6 +2,7 @@ #ifndef DW_SPI_HEADER_H #define DW_SPI_HEADER_H +#include #include #include #include @@ -145,6 +146,7 @@ struct dw_spi { unsigned long dma_chan_busy; dma_addr_t dma_addr; /* phy address of the Data register */ const struct dw_spi_dma_ops *dma_ops; + struct completion dma_completion; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; -- cgit v1.2.3-59-g8ed1b From 1ade2d8a72f9240825f6be050f0d49c840f7daeb Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:53 +0300 Subject: spi: dw: Add SPI Tx-done wait method to DMA-based transfer Since DMA transfers are performed asynchronously with actual SPI bus transfers, then even if DMA transactions are finished it doesn't mean all data is actually pushed to the SPI bus. Some data might still be in the controller FIFO. This is specifically true for Tx-only transfers. In this case if the next SPI transfer is recharged while a tail of the previous one is still in FIFO, we'll loose that tail data. In order to fix that problem let's add the wait procedure of the Tx SPI transfer completion after the DMA transactions are finished. Fixes: 7063c0d942a1 ("spi/dw_spi: add DMA support") Co-developed-by: Georgy Vlasov Signed-off-by: Georgy Vlasov Signed-off-by: Serge Semin Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Andy Shevchenko Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 355b641c4483..846e3db91329 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -19,6 +19,7 @@ #include #include +#define WAIT_RETRIES 5 #define RX_BUSY 0 #define TX_BUSY 1 @@ -171,6 +172,33 @@ static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer) return 0; } +static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws) +{ + return !(dw_readl(dws, DW_SPI_SR) & SR_TF_EMPT); +} + +static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + int retry = WAIT_RETRIES; + struct spi_delay delay; + u32 nents; + + nents = dw_readl(dws, DW_SPI_TXFLR); + delay.unit = SPI_DELAY_UNIT_SCK; + delay.value = nents * dws->n_bytes * BITS_PER_BYTE; + + while (dw_spi_dma_tx_busy(dws) && retry--) + spi_delay_exec(&delay, xfer); + + if (retry < 0) { + dev_err(&dws->master->dev, "Tx hanged up\n"); + return -EIO; + } + + return 0; +} + /* * dws->dma_chan_busy is set before the dma transfer starts, callback for tx * channel will clear a corresponding bit. @@ -324,6 +352,12 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) if (ret) return ret; + if (txdesc && dws->master->cur_msg->status == -EINPROGRESS) { + ret = dw_spi_dma_wait_tx_done(dws, xfer); + if (ret) + return ret; + } + return 0; } -- cgit v1.2.3-59-g8ed1b From 33726eff3d98e643f7d7a0940f4024844b430c82 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:54 +0300 Subject: spi: dw: Add SPI Rx-done wait method to DMA-based transfer Having any data left in the Rx FIFO after the DMA engine claimed it has finished all DMA transactions is an abnormal situation, since the DW SPI controller driver expects to have all the data being fetched and placed into the SPI Rx buffer at that moment. In case if that has happened we hopefully assume that the DMA engine may still be doing the data fetching, thus we give it sometime to finish. If after a short period of time the data is still left in the Rx FIFO, the driver will give up waiting and return an error indicating that the SPI controller/DMA engine must have hung up or failed at some point of doing their duties. Fixes: 7063c0d942a1 ("spi/dw_spi: add DMA support") Co-developed-by: Georgy Vlasov Signed-off-by: Georgy Vlasov Signed-off-by: Serge Semin Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Andy Shevchenko Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 846e3db91329..abd6955ad1f7 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -248,6 +248,49 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, return txdesc; } +static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws) +{ + return !!(dw_readl(dws, DW_SPI_SR) & SR_RF_NOT_EMPT); +} + +static int dw_spi_dma_wait_rx_done(struct dw_spi *dws) +{ + int retry = WAIT_RETRIES; + struct spi_delay delay; + unsigned long ns, us; + u32 nents; + + /* + * It's unlikely that DMA engine is still doing the data fetching, but + * if it's let's give it some reasonable time. The timeout calculation + * is based on the synchronous APB/SSI reference clock rate, on a + * number of data entries left in the Rx FIFO, times a number of clock + * periods normally needed for a single APB read/write transaction + * without PREADY signal utilized (which is true for the DW APB SSI + * controller). + */ + nents = dw_readl(dws, DW_SPI_RXFLR); + ns = 4U * NSEC_PER_SEC / dws->max_freq * nents; + if (ns <= NSEC_PER_USEC) { + delay.unit = SPI_DELAY_UNIT_NSECS; + delay.value = ns; + } else { + us = DIV_ROUND_UP(ns, NSEC_PER_USEC); + delay.unit = SPI_DELAY_UNIT_USECS; + delay.value = clamp_val(us, 0, USHRT_MAX); + } + + while (dw_spi_dma_rx_busy(dws) && retry--) + spi_delay_exec(&delay, NULL); + + if (retry < 0) { + dev_err(&dws->master->dev, "Rx hanged up\n"); + return -EIO; + } + + return 0; +} + /* * dws->dma_chan_busy is set before the dma transfer starts, callback for rx * channel will clear a corresponding bit. @@ -358,7 +401,10 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) return ret; } - return 0; + if (rxdesc && dws->master->cur_msg->status == -EINPROGRESS) + ret = dw_spi_dma_wait_rx_done(dws); + + return ret; } static void mid_spi_dma_stop(struct dw_spi *dws) -- cgit v1.2.3-59-g8ed1b From c534df9d6225314d1403e4330a22d68c35e0eb55 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:55 +0300 Subject: spi: dw: Parameterize the DMA Rx/Tx burst length It isn't good to have numeric literals in the code especially if there are multiple of them and they are related. Let's replace the Tx and Rx burst level literals with the corresponding constants. Co-developed-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Georgy Vlasov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-7-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index abd6955ad1f7..189b517f77fc 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -21,7 +21,9 @@ #define WAIT_RETRIES 5 #define RX_BUSY 0 +#define RX_BURST_LEVEL 16 #define TX_BUSY 1 +#define TX_BURST_LEVEL 16 static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) { @@ -227,7 +229,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, memset(&txconf, 0, sizeof(txconf)); txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; - txconf.dst_maxburst = 16; + txconf.dst_maxburst = TX_BURST_LEVEL; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; txconf.dst_addr_width = convert_dma_width(dws->n_bytes); txconf.device_fc = false; @@ -319,7 +321,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, memset(&rxconf, 0, sizeof(rxconf)); rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; - rxconf.src_maxburst = 16; + rxconf.src_maxburst = RX_BURST_LEVEL; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; rxconf.src_addr_width = convert_dma_width(dws->n_bytes); rxconf.device_fc = false; @@ -344,8 +346,8 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 imr = 0, dma_ctrl = 0; - dw_writel(dws, DW_SPI_DMARDLR, 0xf); - dw_writel(dws, DW_SPI_DMATDLR, 0x10); + dw_writel(dws, DW_SPI_DMARDLR, RX_BURST_LEVEL - 1); + dw_writel(dws, DW_SPI_DMATDLR, TX_BURST_LEVEL); if (xfer->tx_buf) { dma_ctrl |= SPI_DMA_TDMAE; -- cgit v1.2.3-59-g8ed1b From 0b2b66514fc9971b3a6002ba038d74f77705fd34 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:56 +0300 Subject: spi: dw: Use DMA max burst to set the request thresholds Each channel of DMA controller may have a limited length of burst transaction (number of IO operations performed at ones in a single DMA client request). This parameter can be used to setup the most optimal DMA Tx/Rx data level values. In order to avoid the Tx buffer overrun we can set the DMA Tx level to be of FIFO depth minus the maximum burst transactions length. To prevent the Rx buffer underflow the DMA Rx level should be set to the maximum burst transactions length. This commit setups the DMA channels and the DW SPI DMA Tx/Rx levels in accordance with these rules. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-8-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 37 +++++++++++++++++++++++++++++++++---- drivers/spi/spi-dw.h | 2 ++ 2 files changed, 35 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 189b517f77fc..1cf9e3ffe07b 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -36,6 +36,31 @@ static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) return true; } +static void mid_spi_maxburst_init(struct dw_spi *dws) +{ + struct dma_slave_caps caps; + u32 max_burst, def_burst; + int ret; + + def_burst = dws->fifo_len / 2; + + ret = dma_get_slave_caps(dws->rxchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = RX_BURST_LEVEL; + + dws->rxburst = min(max_burst, def_burst); + + ret = dma_get_slave_caps(dws->txchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = TX_BURST_LEVEL; + + dws->txburst = min(max_burst, def_burst); +} + static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) { struct dw_dma_slave slave = { @@ -73,6 +98,8 @@ static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) init_completion(&dws->dma_completion); + mid_spi_maxburst_init(dws); + return 0; free_rxchan: @@ -100,6 +127,8 @@ static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) init_completion(&dws->dma_completion); + mid_spi_maxburst_init(dws); + return 0; } @@ -229,7 +258,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, memset(&txconf, 0, sizeof(txconf)); txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; - txconf.dst_maxburst = TX_BURST_LEVEL; + txconf.dst_maxburst = dws->txburst; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; txconf.dst_addr_width = convert_dma_width(dws->n_bytes); txconf.device_fc = false; @@ -321,7 +350,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, memset(&rxconf, 0, sizeof(rxconf)); rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; - rxconf.src_maxburst = RX_BURST_LEVEL; + rxconf.src_maxburst = dws->rxburst; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; rxconf.src_addr_width = convert_dma_width(dws->n_bytes); rxconf.device_fc = false; @@ -346,8 +375,8 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 imr = 0, dma_ctrl = 0; - dw_writel(dws, DW_SPI_DMARDLR, RX_BURST_LEVEL - 1); - dw_writel(dws, DW_SPI_DMATDLR, TX_BURST_LEVEL); + dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1); + dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst); if (xfer->tx_buf) { dma_ctrl |= SPI_DMA_TDMAE; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 9585d0c83a6d..9247670fcdfb 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -142,7 +142,9 @@ struct dw_spi { /* DMA info */ struct dma_chan *txchan; + u32 txburst; struct dma_chan *rxchan; + u32 rxburst; unsigned long dma_chan_busy; dma_addr_t dma_addr; /* phy address of the Data register */ const struct dw_spi_dma_ops *dma_ops; -- cgit v1.2.3-59-g8ed1b From 46164fde6b7890e7a3982d54549947c8394c0192 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:57 +0300 Subject: spi: dw: Fix Rx-only DMA transfers Tx-only DMA transfers are working perfectly fine since in this case the code just ignores the Rx FIFO overflow interrupts. But it turns out the SPI Rx-only transfers are broken since nothing pushing any data to the shift registers, so the Rx FIFO is left empty and the SPI core subsystems just returns a timeout error. Since DW DMAC driver doesn't support something like cyclic write operations of a single byte to a device register, the only way to support the Rx-only SPI transfers is to fake it by using a dummy Tx-buffer. This is what we intend to fix in this commit by setting the SPI_CONTROLLER_MUST_TX flag for DMA-capable platform. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-9-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 73af58dbcd6a..1f1a3536b7cf 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -515,6 +515,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dev_warn(dev, "DMA init failed\n"); } else { master->can_dma = dws->dma_ops->can_dma; + master->flags |= SPI_CONTROLLER_MUST_TX; } } -- cgit v1.2.3-59-g8ed1b From 77ccff803d27279ccc100dc906c6f456c8fa515c Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:58 +0300 Subject: spi: dw: Add core suffix to the DW APB SSI core source file Generic DMA support is going to be part of the DW APB SSI core object. In order to preserve the kernel loadable module name as spi-dw.ko, let's add the "-core" suffix to the object with generic DW APB SSI code and build it into the target spi-dw.ko driver. Suggested-by: Andy Shevchenko Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Feng Tang Cc: Rob Herring Cc: Arnd Bergmann Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-10-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/Makefile | 1 + drivers/spi/spi-dw-core.c | 577 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spi-dw.c | 577 ---------------------------------------------- 3 files changed, 578 insertions(+), 577 deletions(-) create mode 100644 drivers/spi/spi-dw-core.c delete mode 100644 drivers/spi/spi-dw.c (limited to 'drivers/spi') diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 889368f4ad53..840a5a1c655e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o obj-$(CONFIG_SPI_DLN2) += spi-dln2.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o +spi-dw-y := spi-dw-core.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c new file mode 100644 index 000000000000..1f1a3536b7cf --- /dev/null +++ b/drivers/spi/spi-dw-core.c @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Designware SPI core controller driver (refer pxa2xx_spi.c) + * + * Copyright (c) 2009, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "spi-dw.h" + +#ifdef CONFIG_DEBUG_FS +#include +#endif + +/* Slave spi_dev related */ +struct chip_data { + u8 tmode; /* TR/TO/RO/EEPROM */ + u8 type; /* SPI/SSP/MicroWire */ + + u16 clk_div; /* baud rate divider */ + u32 speed_hz; /* baud rate */ +}; + +#ifdef CONFIG_DEBUG_FS +#define SPI_REGS_BUFSIZE 1024 +static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dw_spi *dws = file->private_data; + char *buf; + u32 len = 0; + ssize_t ret; + + buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL); + if (!buf) + return 0; + + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "%s registers:\n", dev_name(&dws->master->dev)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "=================================\n"); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "CTRLR0: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR0)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "CTRLR1: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR1)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "SSIENR: \t0x%08x\n", dw_readl(dws, DW_SPI_SSIENR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "SER: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SER)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "BAUDR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_BAUDR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFTLR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFTLR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "TXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_TXFLR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "RXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_RXFLR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "SR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "IMR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_IMR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "ISR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_ISR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "DMACR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_DMACR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "DMATDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMATDLR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "DMARDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMARDLR)); + len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, + "=================================\n"); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; +} + +static const struct file_operations dw_spi_regs_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = dw_spi_show_regs, + .llseek = default_llseek, +}; + +static int dw_spi_debugfs_init(struct dw_spi *dws) +{ + char name[32]; + + snprintf(name, 32, "dw_spi%d", dws->master->bus_num); + dws->debugfs = debugfs_create_dir(name, NULL); + if (!dws->debugfs) + return -ENOMEM; + + debugfs_create_file("registers", S_IFREG | S_IRUGO, + dws->debugfs, (void *)dws, &dw_spi_regs_ops); + return 0; +} + +static void dw_spi_debugfs_remove(struct dw_spi *dws) +{ + debugfs_remove_recursive(dws->debugfs); +} + +#else +static inline int dw_spi_debugfs_init(struct dw_spi *dws) +{ + return 0; +} + +static inline void dw_spi_debugfs_remove(struct dw_spi *dws) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +void dw_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct dw_spi *dws = spi_controller_get_devdata(spi->controller); + bool cs_high = !!(spi->mode & SPI_CS_HIGH); + + /* + * DW SPI controller demands any native CS being set in order to + * proceed with data transfer. So in order to activate the SPI + * communications we must set a corresponding bit in the Slave + * Enable register no matter whether the SPI core is configured to + * support active-high or active-low CS level. + */ + if (cs_high == enable) + dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); + else if (dws->cs_override) + dw_writel(dws, DW_SPI_SER, 0); +} +EXPORT_SYMBOL_GPL(dw_spi_set_cs); + +/* Return the max entries we can fill into tx fifo */ +static inline u32 tx_max(struct dw_spi *dws) +{ + u32 tx_left, tx_room, rxtx_gap; + + tx_left = (dws->tx_end - dws->tx) / dws->n_bytes; + tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR); + + /* + * Another concern is about the tx/rx mismatch, we + * though to use (dws->fifo_len - rxflr - txflr) as + * one maximum value for tx, but it doesn't cover the + * data which is out of tx/rx fifo and inside the + * shift registers. So a control from sw point of + * view is taken. + */ + rxtx_gap = ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx)) + / dws->n_bytes; + + return min3(tx_left, tx_room, (u32) (dws->fifo_len - rxtx_gap)); +} + +/* Return the max entries we should read out of rx fifo */ +static inline u32 rx_max(struct dw_spi *dws) +{ + u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes; + + return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR)); +} + +static void dw_writer(struct dw_spi *dws) +{ + u32 max; + u16 txw = 0; + + spin_lock(&dws->buf_lock); + max = tx_max(dws); + while (max--) { + /* Set the tx word if the transfer's original "tx" is not null */ + if (dws->tx_end - dws->len) { + if (dws->n_bytes == 1) + txw = *(u8 *)(dws->tx); + else + txw = *(u16 *)(dws->tx); + } + dw_write_io_reg(dws, DW_SPI_DR, txw); + dws->tx += dws->n_bytes; + } + spin_unlock(&dws->buf_lock); +} + +static void dw_reader(struct dw_spi *dws) +{ + u32 max; + u16 rxw; + + spin_lock(&dws->buf_lock); + max = rx_max(dws); + while (max--) { + rxw = dw_read_io_reg(dws, DW_SPI_DR); + /* Care rx only if the transfer's original "rx" is not null */ + if (dws->rx_end - dws->len) { + if (dws->n_bytes == 1) + *(u8 *)(dws->rx) = rxw; + else + *(u16 *)(dws->rx) = rxw; + } + dws->rx += dws->n_bytes; + } + spin_unlock(&dws->buf_lock); +} + +static void int_error_stop(struct dw_spi *dws, const char *msg) +{ + spi_reset_chip(dws); + + dev_err(&dws->master->dev, "%s\n", msg); + dws->master->cur_msg->status = -EIO; + spi_finalize_current_transfer(dws->master); +} + +static irqreturn_t interrupt_transfer(struct dw_spi *dws) +{ + u16 irq_status = dw_readl(dws, DW_SPI_ISR); + + /* Error handling */ + if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { + dw_readl(dws, DW_SPI_ICR); + int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); + return IRQ_HANDLED; + } + + dw_reader(dws); + if (dws->rx_end == dws->rx) { + spi_mask_intr(dws, SPI_INT_TXEI); + spi_finalize_current_transfer(dws->master); + return IRQ_HANDLED; + } + if (irq_status & SPI_INT_TXEI) { + spi_mask_intr(dws, SPI_INT_TXEI); + dw_writer(dws); + /* Enable TX irq always, it will be disabled when RX finished */ + spi_umask_intr(dws, SPI_INT_TXEI); + } + + return IRQ_HANDLED; +} + +static irqreturn_t dw_spi_irq(int irq, void *dev_id) +{ + struct spi_controller *master = dev_id; + struct dw_spi *dws = spi_controller_get_devdata(master); + u16 irq_status = dw_readl(dws, DW_SPI_ISR) & 0x3f; + + if (!irq_status) + return IRQ_NONE; + + if (!master->cur_msg) { + spi_mask_intr(dws, SPI_INT_TXEI); + return IRQ_HANDLED; + } + + return dws->transfer_handler(dws); +} + +/* Configure CTRLR0 for DW_apb_ssi */ +u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct chip_data *chip = spi_get_ctldata(spi); + u32 cr0; + + /* Default SPI mode is SCPOL = 0, SCPH = 0 */ + cr0 = (transfer->bits_per_word - 1) + | (chip->type << SPI_FRF_OFFSET) + | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | + (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | + (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) + | (chip->tmode << SPI_TMOD_OFFSET); + + return cr0; +} +EXPORT_SYMBOL_GPL(dw_spi_update_cr0); + +/* Configure CTRLR0 for DWC_ssi */ +u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct chip_data *chip = spi_get_ctldata(spi); + u32 cr0; + + /* CTRLR0[ 4: 0] Data Frame Size */ + cr0 = (transfer->bits_per_word - 1); + + /* CTRLR0[ 7: 6] Frame Format */ + cr0 |= chip->type << DWC_SSI_CTRLR0_FRF_OFFSET; + + /* + * SPI mode (SCPOL|SCPH) + * CTRLR0[ 8] Serial Clock Phase + * CTRLR0[ 9] Serial Clock Polarity + */ + cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET; + cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET; + + /* CTRLR0[11:10] Transfer Mode */ + cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; + + /* CTRLR0[13] Shift Register Loop */ + cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; + + return cr0; +} +EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a); + +static int dw_spi_transfer_one(struct spi_controller *master, + struct spi_device *spi, struct spi_transfer *transfer) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + struct chip_data *chip = spi_get_ctldata(spi); + unsigned long flags; + u8 imask = 0; + u16 txlevel = 0; + u32 cr0; + int ret; + + dws->dma_mapped = 0; + spin_lock_irqsave(&dws->buf_lock, flags); + dws->tx = (void *)transfer->tx_buf; + dws->tx_end = dws->tx + transfer->len; + dws->rx = transfer->rx_buf; + dws->rx_end = dws->rx + transfer->len; + dws->len = transfer->len; + spin_unlock_irqrestore(&dws->buf_lock, flags); + + /* Ensure dw->rx and dw->rx_end are visible */ + smp_mb(); + + spi_enable_chip(dws, 0); + + /* Handle per transfer options for bpw and speed */ + if (transfer->speed_hz != dws->current_freq) { + if (transfer->speed_hz != chip->speed_hz) { + /* clk_div doesn't support odd number */ + chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe; + chip->speed_hz = transfer->speed_hz; + } + dws->current_freq = transfer->speed_hz; + spi_set_clk(dws, chip->clk_div); + } + + transfer->effective_speed_hz = dws->max_freq / chip->clk_div; + dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); + + cr0 = dws->update_cr0(master, spi, transfer); + dw_writel(dws, DW_SPI_CTRLR0, cr0); + + /* Check if current transfer is a DMA transaction */ + if (master->can_dma && master->can_dma(master, spi, transfer)) + dws->dma_mapped = master->cur_msg_mapped; + + /* For poll mode just disable all interrupts */ + spi_mask_intr(dws, 0xff); + + /* + * Interrupt mode + * we only need set the TXEI IRQ, as TX/RX always happen syncronizely + */ + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_setup(dws, transfer); + if (ret < 0) { + spi_enable_chip(dws, 1); + return ret; + } + } else { + txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); + dw_writel(dws, DW_SPI_TXFTLR, txlevel); + + /* Set the interrupt mask */ + imask |= SPI_INT_TXEI | SPI_INT_TXOI | + SPI_INT_RXUI | SPI_INT_RXOI; + spi_umask_intr(dws, imask); + + dws->transfer_handler = interrupt_transfer; + } + + spi_enable_chip(dws, 1); + + if (dws->dma_mapped) + return dws->dma_ops->dma_transfer(dws, transfer); + + return 1; +} + +static void dw_spi_handle_err(struct spi_controller *master, + struct spi_message *msg) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + + if (dws->dma_mapped) + dws->dma_ops->dma_stop(dws); + + spi_reset_chip(dws); +} + +/* This may be called twice for each spi dev */ +static int dw_spi_setup(struct spi_device *spi) +{ + struct chip_data *chip; + + /* Only alloc on first setup */ + chip = spi_get_ctldata(spi); + if (!chip) { + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + if (!chip) + return -ENOMEM; + spi_set_ctldata(spi, chip); + } + + chip->tmode = SPI_TMOD_TR; + + return 0; +} + +static void dw_spi_cleanup(struct spi_device *spi) +{ + struct chip_data *chip = spi_get_ctldata(spi); + + kfree(chip); + spi_set_ctldata(spi, NULL); +} + +/* Restart the controller, disable all interrupts, clean rx fifo */ +static void spi_hw_init(struct device *dev, struct dw_spi *dws) +{ + spi_reset_chip(dws); + + /* + * Try to detect the FIFO depth if not set by interface driver, + * the depth could be from 2 to 256 from HW spec + */ + if (!dws->fifo_len) { + u32 fifo; + + for (fifo = 1; fifo < 256; fifo++) { + dw_writel(dws, DW_SPI_TXFTLR, fifo); + if (fifo != dw_readl(dws, DW_SPI_TXFTLR)) + break; + } + dw_writel(dws, DW_SPI_TXFTLR, 0); + + dws->fifo_len = (fifo == 1) ? 0 : fifo; + dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); + } + + /* enable HW fixup for explicit CS deselect for Amazon's alpine chip */ + if (dws->cs_override) + dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF); +} + +int dw_spi_add_host(struct device *dev, struct dw_spi *dws) +{ + struct spi_controller *master; + int ret; + + if (!dws) + return -EINVAL; + + master = spi_alloc_master(dev, 0); + if (!master) + return -ENOMEM; + + dws->master = master; + dws->type = SSI_MOTO_SPI; + dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); + spin_lock_init(&dws->buf_lock); + + spi_controller_set_devdata(master, dws); + + ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), + master); + if (ret < 0) { + dev_err(dev, "can not get IRQ\n"); + goto err_free_master; + } + + master->use_gpio_descriptors = true; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + master->bus_num = dws->bus_num; + master->num_chipselect = dws->num_cs; + master->setup = dw_spi_setup; + master->cleanup = dw_spi_cleanup; + master->set_cs = dw_spi_set_cs; + master->transfer_one = dw_spi_transfer_one; + master->handle_err = dw_spi_handle_err; + master->max_speed_hz = dws->max_freq; + master->dev.of_node = dev->of_node; + master->dev.fwnode = dev->fwnode; + master->flags = SPI_MASTER_GPIO_SS; + master->auto_runtime_pm = true; + + if (dws->set_cs) + master->set_cs = dws->set_cs; + + /* Basic HW init */ + spi_hw_init(dev, dws); + + if (dws->dma_ops && dws->dma_ops->dma_init) { + ret = dws->dma_ops->dma_init(dev, dws); + if (ret) { + dev_warn(dev, "DMA init failed\n"); + } else { + master->can_dma = dws->dma_ops->can_dma; + master->flags |= SPI_CONTROLLER_MUST_TX; + } + } + + ret = devm_spi_register_controller(dev, master); + if (ret) { + dev_err(&master->dev, "problem registering spi master\n"); + goto err_dma_exit; + } + + dw_spi_debugfs_init(dws); + return 0; + +err_dma_exit: + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); + spi_enable_chip(dws, 0); + free_irq(dws->irq, master); +err_free_master: + spi_controller_put(master); + return ret; +} +EXPORT_SYMBOL_GPL(dw_spi_add_host); + +void dw_spi_remove_host(struct dw_spi *dws) +{ + dw_spi_debugfs_remove(dws); + + if (dws->dma_ops && dws->dma_ops->dma_exit) + dws->dma_ops->dma_exit(dws); + + spi_shutdown_chip(dws); + + free_irq(dws->irq, dws->master); +} +EXPORT_SYMBOL_GPL(dw_spi_remove_host); + +int dw_spi_suspend_host(struct dw_spi *dws) +{ + int ret; + + ret = spi_controller_suspend(dws->master); + if (ret) + return ret; + + spi_shutdown_chip(dws); + return 0; +} +EXPORT_SYMBOL_GPL(dw_spi_suspend_host); + +int dw_spi_resume_host(struct dw_spi *dws) +{ + spi_hw_init(&dws->master->dev, dws); + return spi_controller_resume(dws->master); +} +EXPORT_SYMBOL_GPL(dw_spi_resume_host); + +MODULE_AUTHOR("Feng Tang "); +MODULE_DESCRIPTION("Driver for DesignWare SPI controller core"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c deleted file mode 100644 index 1f1a3536b7cf..000000000000 --- a/drivers/spi/spi-dw.c +++ /dev/null @@ -1,577 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Designware SPI core controller driver (refer pxa2xx_spi.c) - * - * Copyright (c) 2009, Intel Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "spi-dw.h" - -#ifdef CONFIG_DEBUG_FS -#include -#endif - -/* Slave spi_dev related */ -struct chip_data { - u8 tmode; /* TR/TO/RO/EEPROM */ - u8 type; /* SPI/SSP/MicroWire */ - - u16 clk_div; /* baud rate divider */ - u32 speed_hz; /* baud rate */ -}; - -#ifdef CONFIG_DEBUG_FS -#define SPI_REGS_BUFSIZE 1024 -static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct dw_spi *dws = file->private_data; - char *buf; - u32 len = 0; - ssize_t ret; - - buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL); - if (!buf) - return 0; - - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "%s registers:\n", dev_name(&dws->master->dev)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "=================================\n"); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRLR0: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR0)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRLR1: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR1)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SSIENR: \t0x%08x\n", dw_readl(dws, DW_SPI_SSIENR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SER: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SER)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "BAUDR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_BAUDR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFTLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFTLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "TXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_TXFLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "RXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_RXFLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "IMR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_IMR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "ISR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_ISR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMACR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_DMACR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMATDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMATDLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMARDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMARDLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "=================================\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return ret; -} - -static const struct file_operations dw_spi_regs_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = dw_spi_show_regs, - .llseek = default_llseek, -}; - -static int dw_spi_debugfs_init(struct dw_spi *dws) -{ - char name[32]; - - snprintf(name, 32, "dw_spi%d", dws->master->bus_num); - dws->debugfs = debugfs_create_dir(name, NULL); - if (!dws->debugfs) - return -ENOMEM; - - debugfs_create_file("registers", S_IFREG | S_IRUGO, - dws->debugfs, (void *)dws, &dw_spi_regs_ops); - return 0; -} - -static void dw_spi_debugfs_remove(struct dw_spi *dws) -{ - debugfs_remove_recursive(dws->debugfs); -} - -#else -static inline int dw_spi_debugfs_init(struct dw_spi *dws) -{ - return 0; -} - -static inline void dw_spi_debugfs_remove(struct dw_spi *dws) -{ -} -#endif /* CONFIG_DEBUG_FS */ - -void dw_spi_set_cs(struct spi_device *spi, bool enable) -{ - struct dw_spi *dws = spi_controller_get_devdata(spi->controller); - bool cs_high = !!(spi->mode & SPI_CS_HIGH); - - /* - * DW SPI controller demands any native CS being set in order to - * proceed with data transfer. So in order to activate the SPI - * communications we must set a corresponding bit in the Slave - * Enable register no matter whether the SPI core is configured to - * support active-high or active-low CS level. - */ - if (cs_high == enable) - dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); - else if (dws->cs_override) - dw_writel(dws, DW_SPI_SER, 0); -} -EXPORT_SYMBOL_GPL(dw_spi_set_cs); - -/* Return the max entries we can fill into tx fifo */ -static inline u32 tx_max(struct dw_spi *dws) -{ - u32 tx_left, tx_room, rxtx_gap; - - tx_left = (dws->tx_end - dws->tx) / dws->n_bytes; - tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR); - - /* - * Another concern is about the tx/rx mismatch, we - * though to use (dws->fifo_len - rxflr - txflr) as - * one maximum value for tx, but it doesn't cover the - * data which is out of tx/rx fifo and inside the - * shift registers. So a control from sw point of - * view is taken. - */ - rxtx_gap = ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx)) - / dws->n_bytes; - - return min3(tx_left, tx_room, (u32) (dws->fifo_len - rxtx_gap)); -} - -/* Return the max entries we should read out of rx fifo */ -static inline u32 rx_max(struct dw_spi *dws) -{ - u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes; - - return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR)); -} - -static void dw_writer(struct dw_spi *dws) -{ - u32 max; - u16 txw = 0; - - spin_lock(&dws->buf_lock); - max = tx_max(dws); - while (max--) { - /* Set the tx word if the transfer's original "tx" is not null */ - if (dws->tx_end - dws->len) { - if (dws->n_bytes == 1) - txw = *(u8 *)(dws->tx); - else - txw = *(u16 *)(dws->tx); - } - dw_write_io_reg(dws, DW_SPI_DR, txw); - dws->tx += dws->n_bytes; - } - spin_unlock(&dws->buf_lock); -} - -static void dw_reader(struct dw_spi *dws) -{ - u32 max; - u16 rxw; - - spin_lock(&dws->buf_lock); - max = rx_max(dws); - while (max--) { - rxw = dw_read_io_reg(dws, DW_SPI_DR); - /* Care rx only if the transfer's original "rx" is not null */ - if (dws->rx_end - dws->len) { - if (dws->n_bytes == 1) - *(u8 *)(dws->rx) = rxw; - else - *(u16 *)(dws->rx) = rxw; - } - dws->rx += dws->n_bytes; - } - spin_unlock(&dws->buf_lock); -} - -static void int_error_stop(struct dw_spi *dws, const char *msg) -{ - spi_reset_chip(dws); - - dev_err(&dws->master->dev, "%s\n", msg); - dws->master->cur_msg->status = -EIO; - spi_finalize_current_transfer(dws->master); -} - -static irqreturn_t interrupt_transfer(struct dw_spi *dws) -{ - u16 irq_status = dw_readl(dws, DW_SPI_ISR); - - /* Error handling */ - if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { - dw_readl(dws, DW_SPI_ICR); - int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); - return IRQ_HANDLED; - } - - dw_reader(dws); - if (dws->rx_end == dws->rx) { - spi_mask_intr(dws, SPI_INT_TXEI); - spi_finalize_current_transfer(dws->master); - return IRQ_HANDLED; - } - if (irq_status & SPI_INT_TXEI) { - spi_mask_intr(dws, SPI_INT_TXEI); - dw_writer(dws); - /* Enable TX irq always, it will be disabled when RX finished */ - spi_umask_intr(dws, SPI_INT_TXEI); - } - - return IRQ_HANDLED; -} - -static irqreturn_t dw_spi_irq(int irq, void *dev_id) -{ - struct spi_controller *master = dev_id; - struct dw_spi *dws = spi_controller_get_devdata(master); - u16 irq_status = dw_readl(dws, DW_SPI_ISR) & 0x3f; - - if (!irq_status) - return IRQ_NONE; - - if (!master->cur_msg) { - spi_mask_intr(dws, SPI_INT_TXEI); - return IRQ_HANDLED; - } - - return dws->transfer_handler(dws); -} - -/* Configure CTRLR0 for DW_apb_ssi */ -u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, - struct spi_transfer *transfer) -{ - struct chip_data *chip = spi_get_ctldata(spi); - u32 cr0; - - /* Default SPI mode is SCPOL = 0, SCPH = 0 */ - cr0 = (transfer->bits_per_word - 1) - | (chip->type << SPI_FRF_OFFSET) - | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | - (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | - (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) - | (chip->tmode << SPI_TMOD_OFFSET); - - return cr0; -} -EXPORT_SYMBOL_GPL(dw_spi_update_cr0); - -/* Configure CTRLR0 for DWC_ssi */ -u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, - struct spi_device *spi, - struct spi_transfer *transfer) -{ - struct chip_data *chip = spi_get_ctldata(spi); - u32 cr0; - - /* CTRLR0[ 4: 0] Data Frame Size */ - cr0 = (transfer->bits_per_word - 1); - - /* CTRLR0[ 7: 6] Frame Format */ - cr0 |= chip->type << DWC_SSI_CTRLR0_FRF_OFFSET; - - /* - * SPI mode (SCPOL|SCPH) - * CTRLR0[ 8] Serial Clock Phase - * CTRLR0[ 9] Serial Clock Polarity - */ - cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET; - cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET; - - /* CTRLR0[11:10] Transfer Mode */ - cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; - - /* CTRLR0[13] Shift Register Loop */ - cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; - - return cr0; -} -EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a); - -static int dw_spi_transfer_one(struct spi_controller *master, - struct spi_device *spi, struct spi_transfer *transfer) -{ - struct dw_spi *dws = spi_controller_get_devdata(master); - struct chip_data *chip = spi_get_ctldata(spi); - unsigned long flags; - u8 imask = 0; - u16 txlevel = 0; - u32 cr0; - int ret; - - dws->dma_mapped = 0; - spin_lock_irqsave(&dws->buf_lock, flags); - dws->tx = (void *)transfer->tx_buf; - dws->tx_end = dws->tx + transfer->len; - dws->rx = transfer->rx_buf; - dws->rx_end = dws->rx + transfer->len; - dws->len = transfer->len; - spin_unlock_irqrestore(&dws->buf_lock, flags); - - /* Ensure dw->rx and dw->rx_end are visible */ - smp_mb(); - - spi_enable_chip(dws, 0); - - /* Handle per transfer options for bpw and speed */ - if (transfer->speed_hz != dws->current_freq) { - if (transfer->speed_hz != chip->speed_hz) { - /* clk_div doesn't support odd number */ - chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe; - chip->speed_hz = transfer->speed_hz; - } - dws->current_freq = transfer->speed_hz; - spi_set_clk(dws, chip->clk_div); - } - - transfer->effective_speed_hz = dws->max_freq / chip->clk_div; - dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); - - cr0 = dws->update_cr0(master, spi, transfer); - dw_writel(dws, DW_SPI_CTRLR0, cr0); - - /* Check if current transfer is a DMA transaction */ - if (master->can_dma && master->can_dma(master, spi, transfer)) - dws->dma_mapped = master->cur_msg_mapped; - - /* For poll mode just disable all interrupts */ - spi_mask_intr(dws, 0xff); - - /* - * Interrupt mode - * we only need set the TXEI IRQ, as TX/RX always happen syncronizely - */ - if (dws->dma_mapped) { - ret = dws->dma_ops->dma_setup(dws, transfer); - if (ret < 0) { - spi_enable_chip(dws, 1); - return ret; - } - } else { - txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); - dw_writel(dws, DW_SPI_TXFTLR, txlevel); - - /* Set the interrupt mask */ - imask |= SPI_INT_TXEI | SPI_INT_TXOI | - SPI_INT_RXUI | SPI_INT_RXOI; - spi_umask_intr(dws, imask); - - dws->transfer_handler = interrupt_transfer; - } - - spi_enable_chip(dws, 1); - - if (dws->dma_mapped) - return dws->dma_ops->dma_transfer(dws, transfer); - - return 1; -} - -static void dw_spi_handle_err(struct spi_controller *master, - struct spi_message *msg) -{ - struct dw_spi *dws = spi_controller_get_devdata(master); - - if (dws->dma_mapped) - dws->dma_ops->dma_stop(dws); - - spi_reset_chip(dws); -} - -/* This may be called twice for each spi dev */ -static int dw_spi_setup(struct spi_device *spi) -{ - struct chip_data *chip; - - /* Only alloc on first setup */ - chip = spi_get_ctldata(spi); - if (!chip) { - chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); - if (!chip) - return -ENOMEM; - spi_set_ctldata(spi, chip); - } - - chip->tmode = SPI_TMOD_TR; - - return 0; -} - -static void dw_spi_cleanup(struct spi_device *spi) -{ - struct chip_data *chip = spi_get_ctldata(spi); - - kfree(chip); - spi_set_ctldata(spi, NULL); -} - -/* Restart the controller, disable all interrupts, clean rx fifo */ -static void spi_hw_init(struct device *dev, struct dw_spi *dws) -{ - spi_reset_chip(dws); - - /* - * Try to detect the FIFO depth if not set by interface driver, - * the depth could be from 2 to 256 from HW spec - */ - if (!dws->fifo_len) { - u32 fifo; - - for (fifo = 1; fifo < 256; fifo++) { - dw_writel(dws, DW_SPI_TXFTLR, fifo); - if (fifo != dw_readl(dws, DW_SPI_TXFTLR)) - break; - } - dw_writel(dws, DW_SPI_TXFTLR, 0); - - dws->fifo_len = (fifo == 1) ? 0 : fifo; - dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); - } - - /* enable HW fixup for explicit CS deselect for Amazon's alpine chip */ - if (dws->cs_override) - dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF); -} - -int dw_spi_add_host(struct device *dev, struct dw_spi *dws) -{ - struct spi_controller *master; - int ret; - - if (!dws) - return -EINVAL; - - master = spi_alloc_master(dev, 0); - if (!master) - return -ENOMEM; - - dws->master = master; - dws->type = SSI_MOTO_SPI; - dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); - spin_lock_init(&dws->buf_lock); - - spi_controller_set_devdata(master, dws); - - ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), - master); - if (ret < 0) { - dev_err(dev, "can not get IRQ\n"); - goto err_free_master; - } - - master->use_gpio_descriptors = true; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); - master->bus_num = dws->bus_num; - master->num_chipselect = dws->num_cs; - master->setup = dw_spi_setup; - master->cleanup = dw_spi_cleanup; - master->set_cs = dw_spi_set_cs; - master->transfer_one = dw_spi_transfer_one; - master->handle_err = dw_spi_handle_err; - master->max_speed_hz = dws->max_freq; - master->dev.of_node = dev->of_node; - master->dev.fwnode = dev->fwnode; - master->flags = SPI_MASTER_GPIO_SS; - master->auto_runtime_pm = true; - - if (dws->set_cs) - master->set_cs = dws->set_cs; - - /* Basic HW init */ - spi_hw_init(dev, dws); - - if (dws->dma_ops && dws->dma_ops->dma_init) { - ret = dws->dma_ops->dma_init(dev, dws); - if (ret) { - dev_warn(dev, "DMA init failed\n"); - } else { - master->can_dma = dws->dma_ops->can_dma; - master->flags |= SPI_CONTROLLER_MUST_TX; - } - } - - ret = devm_spi_register_controller(dev, master); - if (ret) { - dev_err(&master->dev, "problem registering spi master\n"); - goto err_dma_exit; - } - - dw_spi_debugfs_init(dws); - return 0; - -err_dma_exit: - if (dws->dma_ops && dws->dma_ops->dma_exit) - dws->dma_ops->dma_exit(dws); - spi_enable_chip(dws, 0); - free_irq(dws->irq, master); -err_free_master: - spi_controller_put(master); - return ret; -} -EXPORT_SYMBOL_GPL(dw_spi_add_host); - -void dw_spi_remove_host(struct dw_spi *dws) -{ - dw_spi_debugfs_remove(dws); - - if (dws->dma_ops && dws->dma_ops->dma_exit) - dws->dma_ops->dma_exit(dws); - - spi_shutdown_chip(dws); - - free_irq(dws->irq, dws->master); -} -EXPORT_SYMBOL_GPL(dw_spi_remove_host); - -int dw_spi_suspend_host(struct dw_spi *dws) -{ - int ret; - - ret = spi_controller_suspend(dws->master); - if (ret) - return ret; - - spi_shutdown_chip(dws); - return 0; -} -EXPORT_SYMBOL_GPL(dw_spi_suspend_host); - -int dw_spi_resume_host(struct dw_spi *dws) -{ - spi_hw_init(&dws->master->dev, dws); - return spi_controller_resume(dws->master); -} -EXPORT_SYMBOL_GPL(dw_spi_resume_host); - -MODULE_AUTHOR("Feng Tang "); -MODULE_DESCRIPTION("Driver for DesignWare SPI controller core"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 6c710c0cb6725bdbe647b958756685aed0295936 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:11:59 +0300 Subject: spi: dw: Move Non-DMA code to the DW PCIe-SPI driver This is a preparation patch before adding the DW DMA support into the DW SPI MMIO driver. We need to unpin the Non-DMA-specific code from the intended to be generic DW APB SSI DMA code. This isn't that hard, since the most part of the spi-dw-mid.c driver in fact implements a generic DMA interface for the DW SPI controller driver. The only Intel MID specifics concern getting the max frequency from the MRST Clock Control Unit and fetching the DMA controller channels from corresponding PCIe DMA controller. Since first one is related with the SPI interface configuration we moved it' implementation into the DW PCIe-SPI driver module. After that former spi-dw-mid.c file can be just renamed to be the DW SPI DMA module optionally compiled in to the DW APB SSI core driver. Co-developed-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Georgy Vlasov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-11-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 8 +- drivers/spi/Makefile | 4 +- drivers/spi/spi-dw-dma.c | 481 ++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spi-dw-mid.c | 529 ----------------------------------------------- drivers/spi/spi-dw-pci.c | 50 ++++- drivers/spi/spi-dw.h | 14 +- 6 files changed, 545 insertions(+), 541 deletions(-) create mode 100644 drivers/spi/spi-dw-dma.c delete mode 100644 drivers/spi/spi-dw-mid.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 099d6a75a371..73d508eb4871 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -226,14 +226,14 @@ config SPI_DESIGNWARE help general driver for SPI controller core from DesignWare +config SPI_DW_DMA + bool "DMA support for DW SPI controller" + depends on SPI_DESIGNWARE && DW_DMAC_PCI + config SPI_DW_PCI tristate "PCI interface driver for DW SPI core" depends on SPI_DESIGNWARE && PCI -config SPI_DW_MID_DMA - bool "DMA support for DW SPI controller on Intel MID platform" - depends on SPI_DW_PCI && DW_DMAC_PCI - config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" depends on SPI_DESIGNWARE diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 840a5a1c655e..d2e41d3d464a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -37,9 +37,9 @@ obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o obj-$(CONFIG_SPI_DLN2) += spi-dln2.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o spi-dw-y := spi-dw-core.o +spi-dw-$(CONFIG_SPI_DW_DMA) += spi-dw-dma.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o -obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o -spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o +obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o obj-$(CONFIG_SPI_EFM32) += spi-efm32.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o obj-$(CONFIG_SPI_FALCON) += spi-falcon.o diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c new file mode 100644 index 000000000000..7ae31682b5de --- /dev/null +++ b/drivers/spi/spi-dw-dma.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Special handling for DW DMA core + * + * Copyright (c) 2009, 2014 Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-dw.h" + +#define WAIT_RETRIES 5 +#define RX_BUSY 0 +#define RX_BURST_LEVEL 16 +#define TX_BUSY 1 +#define TX_BURST_LEVEL 16 + +static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_slave *s = param; + + if (s->dma_dev != chan->device->dev) + return false; + + chan->private = s; + return true; +} + +static void mid_spi_maxburst_init(struct dw_spi *dws) +{ + struct dma_slave_caps caps; + u32 max_burst, def_burst; + int ret; + + def_burst = dws->fifo_len / 2; + + ret = dma_get_slave_caps(dws->rxchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = RX_BURST_LEVEL; + + dws->rxburst = min(max_burst, def_burst); + + ret = dma_get_slave_caps(dws->txchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = TX_BURST_LEVEL; + + dws->txburst = min(max_burst, def_burst); +} + +static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) +{ + struct dw_dma_slave slave = { + .src_id = 0, + .dst_id = 0 + }; + struct pci_dev *dma_dev; + dma_cap_mask_t mask; + + /* + * Get pci device for DMA controller, currently it could only + * be the DMA controller of Medfield + */ + dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL); + if (!dma_dev) + return -ENODEV; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* 1. Init rx channel */ + slave.dma_dev = &dma_dev->dev; + dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); + if (!dws->rxchan) + goto err_exit; + + /* 2. Init tx channel */ + slave.dst_id = 1; + dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); + if (!dws->txchan) + goto free_rxchan; + + dws->master->dma_rx = dws->rxchan; + dws->master->dma_tx = dws->txchan; + + init_completion(&dws->dma_completion); + + mid_spi_maxburst_init(dws); + + return 0; + +free_rxchan: + dma_release_channel(dws->rxchan); + dws->rxchan = NULL; +err_exit: + return -EBUSY; +} + +static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) +{ + dws->rxchan = dma_request_slave_channel(dev, "rx"); + if (!dws->rxchan) + return -ENODEV; + + dws->txchan = dma_request_slave_channel(dev, "tx"); + if (!dws->txchan) { + dma_release_channel(dws->rxchan); + dws->rxchan = NULL; + return -ENODEV; + } + + dws->master->dma_rx = dws->rxchan; + dws->master->dma_tx = dws->txchan; + + init_completion(&dws->dma_completion); + + mid_spi_maxburst_init(dws); + + return 0; +} + +static void mid_spi_dma_exit(struct dw_spi *dws) +{ + if (dws->txchan) { + dmaengine_terminate_sync(dws->txchan); + dma_release_channel(dws->txchan); + } + + if (dws->rxchan) { + dmaengine_terminate_sync(dws->rxchan); + dma_release_channel(dws->rxchan); + } + + dw_writel(dws, DW_SPI_DMACR, 0); +} + +static irqreturn_t dma_transfer(struct dw_spi *dws) +{ + u16 irq_status = dw_readl(dws, DW_SPI_ISR); + + if (!irq_status) + return IRQ_NONE; + + dw_readl(dws, DW_SPI_ICR); + spi_reset_chip(dws); + + dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); + dws->master->cur_msg->status = -EIO; + complete(&dws->dma_completion); + return IRQ_HANDLED; +} + +static bool mid_spi_can_dma(struct spi_controller *master, + struct spi_device *spi, struct spi_transfer *xfer) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + + return xfer->len > dws->fifo_len; +} + +static enum dma_slave_buswidth convert_dma_width(u8 n_bytes) { + if (n_bytes == 1) + return DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (n_bytes == 2) + return DMA_SLAVE_BUSWIDTH_2_BYTES; + + return DMA_SLAVE_BUSWIDTH_UNDEFINED; +} + +static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer) +{ + unsigned long long ms; + + ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE; + do_div(ms, xfer->effective_speed_hz); + ms += ms + 200; + + if (ms > UINT_MAX) + ms = UINT_MAX; + + ms = wait_for_completion_timeout(&dws->dma_completion, + msecs_to_jiffies(ms)); + + if (ms == 0) { + dev_err(&dws->master->cur_msg->spi->dev, + "DMA transaction timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws) +{ + return !(dw_readl(dws, DW_SPI_SR) & SR_TF_EMPT); +} + +static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + int retry = WAIT_RETRIES; + struct spi_delay delay; + u32 nents; + + nents = dw_readl(dws, DW_SPI_TXFLR); + delay.unit = SPI_DELAY_UNIT_SCK; + delay.value = nents * dws->n_bytes * BITS_PER_BYTE; + + while (dw_spi_dma_tx_busy(dws) && retry--) + spi_delay_exec(&delay, xfer); + + if (retry < 0) { + dev_err(&dws->master->dev, "Tx hanged up\n"); + return -EIO; + } + + return 0; +} + +/* + * dws->dma_chan_busy is set before the dma transfer starts, callback for tx + * channel will clear a corresponding bit. + */ +static void dw_spi_dma_tx_done(void *arg) +{ + struct dw_spi *dws = arg; + + clear_bit(TX_BUSY, &dws->dma_chan_busy); + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) + return; + + dw_writel(dws, DW_SPI_DMACR, 0); + complete(&dws->dma_completion); +} + +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + struct dma_slave_config txconf; + struct dma_async_tx_descriptor *txdesc; + + if (!xfer->tx_buf) + return NULL; + + memset(&txconf, 0, sizeof(txconf)); + txconf.direction = DMA_MEM_TO_DEV; + txconf.dst_addr = dws->dma_addr; + txconf.dst_maxburst = dws->txburst; + txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = convert_dma_width(dws->n_bytes); + txconf.device_fc = false; + + dmaengine_slave_config(dws->txchan, &txconf); + + txdesc = dmaengine_prep_slave_sg(dws->txchan, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + return NULL; + + txdesc->callback = dw_spi_dma_tx_done; + txdesc->callback_param = dws; + + return txdesc; +} + +static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws) +{ + return !!(dw_readl(dws, DW_SPI_SR) & SR_RF_NOT_EMPT); +} + +static int dw_spi_dma_wait_rx_done(struct dw_spi *dws) +{ + int retry = WAIT_RETRIES; + struct spi_delay delay; + unsigned long ns, us; + u32 nents; + + /* + * It's unlikely that DMA engine is still doing the data fetching, but + * if it's let's give it some reasonable time. The timeout calculation + * is based on the synchronous APB/SSI reference clock rate, on a + * number of data entries left in the Rx FIFO, times a number of clock + * periods normally needed for a single APB read/write transaction + * without PREADY signal utilized (which is true for the DW APB SSI + * controller). + */ + nents = dw_readl(dws, DW_SPI_RXFLR); + ns = 4U * NSEC_PER_SEC / dws->max_freq * nents; + if (ns <= NSEC_PER_USEC) { + delay.unit = SPI_DELAY_UNIT_NSECS; + delay.value = ns; + } else { + us = DIV_ROUND_UP(ns, NSEC_PER_USEC); + delay.unit = SPI_DELAY_UNIT_USECS; + delay.value = clamp_val(us, 0, USHRT_MAX); + } + + while (dw_spi_dma_rx_busy(dws) && retry--) + spi_delay_exec(&delay, NULL); + + if (retry < 0) { + dev_err(&dws->master->dev, "Rx hanged up\n"); + return -EIO; + } + + return 0; +} + +/* + * dws->dma_chan_busy is set before the dma transfer starts, callback for rx + * channel will clear a corresponding bit. + */ +static void dw_spi_dma_rx_done(void *arg) +{ + struct dw_spi *dws = arg; + + clear_bit(RX_BUSY, &dws->dma_chan_busy); + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) + return; + + dw_writel(dws, DW_SPI_DMACR, 0); + complete(&dws->dma_completion); +} + +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + struct dma_slave_config rxconf; + struct dma_async_tx_descriptor *rxdesc; + + if (!xfer->rx_buf) + return NULL; + + memset(&rxconf, 0, sizeof(rxconf)); + rxconf.direction = DMA_DEV_TO_MEM; + rxconf.src_addr = dws->dma_addr; + rxconf.src_maxburst = dws->rxburst; + rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = convert_dma_width(dws->n_bytes); + rxconf.device_fc = false; + + dmaengine_slave_config(dws->rxchan, &rxconf); + + rxdesc = dmaengine_prep_slave_sg(dws->rxchan, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + return NULL; + + rxdesc->callback = dw_spi_dma_rx_done; + rxdesc->callback_param = dws; + + return rxdesc; +} + +static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) +{ + u16 imr = 0, dma_ctrl = 0; + + dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1); + dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst); + + if (xfer->tx_buf) { + dma_ctrl |= SPI_DMA_TDMAE; + imr |= SPI_INT_TXOI; + } + if (xfer->rx_buf) { + dma_ctrl |= SPI_DMA_RDMAE; + imr |= SPI_INT_RXUI | SPI_INT_RXOI; + } + dw_writel(dws, DW_SPI_DMACR, dma_ctrl); + + /* Set the interrupt mask */ + spi_umask_intr(dws, imr); + + reinit_completion(&dws->dma_completion); + + dws->transfer_handler = dma_transfer; + + return 0; +} + +static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) +{ + struct dma_async_tx_descriptor *txdesc, *rxdesc; + int ret; + + /* Prepare the TX dma transfer */ + txdesc = dw_spi_dma_prepare_tx(dws, xfer); + + /* Prepare the RX dma transfer */ + rxdesc = dw_spi_dma_prepare_rx(dws, xfer); + + /* rx must be started before tx due to spi instinct */ + if (rxdesc) { + set_bit(RX_BUSY, &dws->dma_chan_busy); + dmaengine_submit(rxdesc); + dma_async_issue_pending(dws->rxchan); + } + + if (txdesc) { + set_bit(TX_BUSY, &dws->dma_chan_busy); + dmaengine_submit(txdesc); + dma_async_issue_pending(dws->txchan); + } + + ret = dw_spi_dma_wait(dws, xfer); + if (ret) + return ret; + + if (txdesc && dws->master->cur_msg->status == -EINPROGRESS) { + ret = dw_spi_dma_wait_tx_done(dws, xfer); + if (ret) + return ret; + } + + if (rxdesc && dws->master->cur_msg->status == -EINPROGRESS) + ret = dw_spi_dma_wait_rx_done(dws); + + return ret; +} + +static void mid_spi_dma_stop(struct dw_spi *dws) +{ + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_sync(dws->txchan); + clear_bit(TX_BUSY, &dws->dma_chan_busy); + } + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_sync(dws->rxchan); + clear_bit(RX_BUSY, &dws->dma_chan_busy); + } + + dw_writel(dws, DW_SPI_DMACR, 0); +} + +static const struct dw_spi_dma_ops mfld_dma_ops = { + .dma_init = mid_spi_dma_init_mfld, + .dma_exit = mid_spi_dma_exit, + .dma_setup = mid_spi_dma_setup, + .can_dma = mid_spi_can_dma, + .dma_transfer = mid_spi_dma_transfer, + .dma_stop = mid_spi_dma_stop, +}; + +void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) +{ + dws->dma_ops = &mfld_dma_ops; +} +EXPORT_SYMBOL_GPL(dw_spi_mid_setup_dma_mfld); + +static const struct dw_spi_dma_ops generic_dma_ops = { + .dma_init = mid_spi_dma_init_generic, + .dma_exit = mid_spi_dma_exit, + .dma_setup = mid_spi_dma_setup, + .can_dma = mid_spi_can_dma, + .dma_transfer = mid_spi_dma_transfer, + .dma_stop = mid_spi_dma_stop, +}; + +void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) +{ + dws->dma_ops = &generic_dma_ops; +} +EXPORT_SYMBOL_GPL(dw_spi_mid_setup_dma_generic); diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c deleted file mode 100644 index 1cf9e3ffe07b..000000000000 --- a/drivers/spi/spi-dw-mid.c +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Special handling for DW core on Intel MID platform - * - * Copyright (c) 2009, 2014 Intel Corporation. - */ - -#include -#include - -#include "spi-dw.h" - -#ifdef CONFIG_SPI_DW_MID_DMA -#include -#include -#include -#include -#include -#include -#include - -#define WAIT_RETRIES 5 -#define RX_BUSY 0 -#define RX_BURST_LEVEL 16 -#define TX_BUSY 1 -#define TX_BURST_LEVEL 16 - -static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_slave *s = param; - - if (s->dma_dev != chan->device->dev) - return false; - - chan->private = s; - return true; -} - -static void mid_spi_maxburst_init(struct dw_spi *dws) -{ - struct dma_slave_caps caps; - u32 max_burst, def_burst; - int ret; - - def_burst = dws->fifo_len / 2; - - ret = dma_get_slave_caps(dws->rxchan, &caps); - if (!ret && caps.max_burst) - max_burst = caps.max_burst; - else - max_burst = RX_BURST_LEVEL; - - dws->rxburst = min(max_burst, def_burst); - - ret = dma_get_slave_caps(dws->txchan, &caps); - if (!ret && caps.max_burst) - max_burst = caps.max_burst; - else - max_burst = TX_BURST_LEVEL; - - dws->txburst = min(max_burst, def_burst); -} - -static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) -{ - struct dw_dma_slave slave = { - .src_id = 0, - .dst_id = 0 - }; - struct pci_dev *dma_dev; - dma_cap_mask_t mask; - - /* - * Get pci device for DMA controller, currently it could only - * be the DMA controller of Medfield - */ - dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL); - if (!dma_dev) - return -ENODEV; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - /* 1. Init rx channel */ - slave.dma_dev = &dma_dev->dev; - dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); - if (!dws->rxchan) - goto err_exit; - - /* 2. Init tx channel */ - slave.dst_id = 1; - dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); - if (!dws->txchan) - goto free_rxchan; - - dws->master->dma_rx = dws->rxchan; - dws->master->dma_tx = dws->txchan; - - init_completion(&dws->dma_completion); - - mid_spi_maxburst_init(dws); - - return 0; - -free_rxchan: - dma_release_channel(dws->rxchan); - dws->rxchan = NULL; -err_exit: - return -EBUSY; -} - -static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) -{ - dws->rxchan = dma_request_slave_channel(dev, "rx"); - if (!dws->rxchan) - return -ENODEV; - - dws->txchan = dma_request_slave_channel(dev, "tx"); - if (!dws->txchan) { - dma_release_channel(dws->rxchan); - dws->rxchan = NULL; - return -ENODEV; - } - - dws->master->dma_rx = dws->rxchan; - dws->master->dma_tx = dws->txchan; - - init_completion(&dws->dma_completion); - - mid_spi_maxburst_init(dws); - - return 0; -} - -static void mid_spi_dma_exit(struct dw_spi *dws) -{ - if (dws->txchan) { - dmaengine_terminate_sync(dws->txchan); - dma_release_channel(dws->txchan); - } - - if (dws->rxchan) { - dmaengine_terminate_sync(dws->rxchan); - dma_release_channel(dws->rxchan); - } - - dw_writel(dws, DW_SPI_DMACR, 0); -} - -static irqreturn_t dma_transfer(struct dw_spi *dws) -{ - u16 irq_status = dw_readl(dws, DW_SPI_ISR); - - if (!irq_status) - return IRQ_NONE; - - dw_readl(dws, DW_SPI_ICR); - spi_reset_chip(dws); - - dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); - dws->master->cur_msg->status = -EIO; - complete(&dws->dma_completion); - return IRQ_HANDLED; -} - -static bool mid_spi_can_dma(struct spi_controller *master, - struct spi_device *spi, struct spi_transfer *xfer) -{ - struct dw_spi *dws = spi_controller_get_devdata(master); - - return xfer->len > dws->fifo_len; -} - -static enum dma_slave_buswidth convert_dma_width(u8 n_bytes) { - if (n_bytes == 1) - return DMA_SLAVE_BUSWIDTH_1_BYTE; - else if (n_bytes == 2) - return DMA_SLAVE_BUSWIDTH_2_BYTES; - - return DMA_SLAVE_BUSWIDTH_UNDEFINED; -} - -static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer) -{ - unsigned long long ms; - - ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE; - do_div(ms, xfer->effective_speed_hz); - ms += ms + 200; - - if (ms > UINT_MAX) - ms = UINT_MAX; - - ms = wait_for_completion_timeout(&dws->dma_completion, - msecs_to_jiffies(ms)); - - if (ms == 0) { - dev_err(&dws->master->cur_msg->spi->dev, - "DMA transaction timed out\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws) -{ - return !(dw_readl(dws, DW_SPI_SR) & SR_TF_EMPT); -} - -static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, - struct spi_transfer *xfer) -{ - int retry = WAIT_RETRIES; - struct spi_delay delay; - u32 nents; - - nents = dw_readl(dws, DW_SPI_TXFLR); - delay.unit = SPI_DELAY_UNIT_SCK; - delay.value = nents * dws->n_bytes * BITS_PER_BYTE; - - while (dw_spi_dma_tx_busy(dws) && retry--) - spi_delay_exec(&delay, xfer); - - if (retry < 0) { - dev_err(&dws->master->dev, "Tx hanged up\n"); - return -EIO; - } - - return 0; -} - -/* - * dws->dma_chan_busy is set before the dma transfer starts, callback for tx - * channel will clear a corresponding bit. - */ -static void dw_spi_dma_tx_done(void *arg) -{ - struct dw_spi *dws = arg; - - clear_bit(TX_BUSY, &dws->dma_chan_busy); - if (test_bit(RX_BUSY, &dws->dma_chan_busy)) - return; - - dw_writel(dws, DW_SPI_DMACR, 0); - complete(&dws->dma_completion); -} - -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, - struct spi_transfer *xfer) -{ - struct dma_slave_config txconf; - struct dma_async_tx_descriptor *txdesc; - - if (!xfer->tx_buf) - return NULL; - - memset(&txconf, 0, sizeof(txconf)); - txconf.direction = DMA_MEM_TO_DEV; - txconf.dst_addr = dws->dma_addr; - txconf.dst_maxburst = dws->txburst; - txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - txconf.dst_addr_width = convert_dma_width(dws->n_bytes); - txconf.device_fc = false; - - dmaengine_slave_config(dws->txchan, &txconf); - - txdesc = dmaengine_prep_slave_sg(dws->txchan, - xfer->tx_sg.sgl, - xfer->tx_sg.nents, - DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!txdesc) - return NULL; - - txdesc->callback = dw_spi_dma_tx_done; - txdesc->callback_param = dws; - - return txdesc; -} - -static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws) -{ - return !!(dw_readl(dws, DW_SPI_SR) & SR_RF_NOT_EMPT); -} - -static int dw_spi_dma_wait_rx_done(struct dw_spi *dws) -{ - int retry = WAIT_RETRIES; - struct spi_delay delay; - unsigned long ns, us; - u32 nents; - - /* - * It's unlikely that DMA engine is still doing the data fetching, but - * if it's let's give it some reasonable time. The timeout calculation - * is based on the synchronous APB/SSI reference clock rate, on a - * number of data entries left in the Rx FIFO, times a number of clock - * periods normally needed for a single APB read/write transaction - * without PREADY signal utilized (which is true for the DW APB SSI - * controller). - */ - nents = dw_readl(dws, DW_SPI_RXFLR); - ns = 4U * NSEC_PER_SEC / dws->max_freq * nents; - if (ns <= NSEC_PER_USEC) { - delay.unit = SPI_DELAY_UNIT_NSECS; - delay.value = ns; - } else { - us = DIV_ROUND_UP(ns, NSEC_PER_USEC); - delay.unit = SPI_DELAY_UNIT_USECS; - delay.value = clamp_val(us, 0, USHRT_MAX); - } - - while (dw_spi_dma_rx_busy(dws) && retry--) - spi_delay_exec(&delay, NULL); - - if (retry < 0) { - dev_err(&dws->master->dev, "Rx hanged up\n"); - return -EIO; - } - - return 0; -} - -/* - * dws->dma_chan_busy is set before the dma transfer starts, callback for rx - * channel will clear a corresponding bit. - */ -static void dw_spi_dma_rx_done(void *arg) -{ - struct dw_spi *dws = arg; - - clear_bit(RX_BUSY, &dws->dma_chan_busy); - if (test_bit(TX_BUSY, &dws->dma_chan_busy)) - return; - - dw_writel(dws, DW_SPI_DMACR, 0); - complete(&dws->dma_completion); -} - -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, - struct spi_transfer *xfer) -{ - struct dma_slave_config rxconf; - struct dma_async_tx_descriptor *rxdesc; - - if (!xfer->rx_buf) - return NULL; - - memset(&rxconf, 0, sizeof(rxconf)); - rxconf.direction = DMA_DEV_TO_MEM; - rxconf.src_addr = dws->dma_addr; - rxconf.src_maxburst = dws->rxburst; - rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - rxconf.src_addr_width = convert_dma_width(dws->n_bytes); - rxconf.device_fc = false; - - dmaengine_slave_config(dws->rxchan, &rxconf); - - rxdesc = dmaengine_prep_slave_sg(dws->rxchan, - xfer->rx_sg.sgl, - xfer->rx_sg.nents, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!rxdesc) - return NULL; - - rxdesc->callback = dw_spi_dma_rx_done; - rxdesc->callback_param = dws; - - return rxdesc; -} - -static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) -{ - u16 imr = 0, dma_ctrl = 0; - - dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1); - dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst); - - if (xfer->tx_buf) { - dma_ctrl |= SPI_DMA_TDMAE; - imr |= SPI_INT_TXOI; - } - if (xfer->rx_buf) { - dma_ctrl |= SPI_DMA_RDMAE; - imr |= SPI_INT_RXUI | SPI_INT_RXOI; - } - dw_writel(dws, DW_SPI_DMACR, dma_ctrl); - - /* Set the interrupt mask */ - spi_umask_intr(dws, imr); - - reinit_completion(&dws->dma_completion); - - dws->transfer_handler = dma_transfer; - - return 0; -} - -static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) -{ - struct dma_async_tx_descriptor *txdesc, *rxdesc; - int ret; - - /* Prepare the TX dma transfer */ - txdesc = dw_spi_dma_prepare_tx(dws, xfer); - - /* Prepare the RX dma transfer */ - rxdesc = dw_spi_dma_prepare_rx(dws, xfer); - - /* rx must be started before tx due to spi instinct */ - if (rxdesc) { - set_bit(RX_BUSY, &dws->dma_chan_busy); - dmaengine_submit(rxdesc); - dma_async_issue_pending(dws->rxchan); - } - - if (txdesc) { - set_bit(TX_BUSY, &dws->dma_chan_busy); - dmaengine_submit(txdesc); - dma_async_issue_pending(dws->txchan); - } - - ret = dw_spi_dma_wait(dws, xfer); - if (ret) - return ret; - - if (txdesc && dws->master->cur_msg->status == -EINPROGRESS) { - ret = dw_spi_dma_wait_tx_done(dws, xfer); - if (ret) - return ret; - } - - if (rxdesc && dws->master->cur_msg->status == -EINPROGRESS) - ret = dw_spi_dma_wait_rx_done(dws); - - return ret; -} - -static void mid_spi_dma_stop(struct dw_spi *dws) -{ - if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { - dmaengine_terminate_sync(dws->txchan); - clear_bit(TX_BUSY, &dws->dma_chan_busy); - } - if (test_bit(RX_BUSY, &dws->dma_chan_busy)) { - dmaengine_terminate_sync(dws->rxchan); - clear_bit(RX_BUSY, &dws->dma_chan_busy); - } - - dw_writel(dws, DW_SPI_DMACR, 0); -} - -static const struct dw_spi_dma_ops mfld_dma_ops = { - .dma_init = mid_spi_dma_init_mfld, - .dma_exit = mid_spi_dma_exit, - .dma_setup = mid_spi_dma_setup, - .can_dma = mid_spi_can_dma, - .dma_transfer = mid_spi_dma_transfer, - .dma_stop = mid_spi_dma_stop, -}; - -static void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) -{ - dws->dma_ops = &mfld_dma_ops; -} - -static const struct dw_spi_dma_ops generic_dma_ops = { - .dma_init = mid_spi_dma_init_generic, - .dma_exit = mid_spi_dma_exit, - .dma_setup = mid_spi_dma_setup, - .can_dma = mid_spi_can_dma, - .dma_transfer = mid_spi_dma_transfer, - .dma_stop = mid_spi_dma_stop, -}; - -static void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) -{ - dws->dma_ops = &generic_dma_ops; -} -#else /* CONFIG_SPI_DW_MID_DMA */ -static inline void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) {} -static inline void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) {} -#endif - -/* Some specific info for SPI0 controller on Intel MID */ - -/* HW info for MRST Clk Control Unit, 32b reg per controller */ -#define MRST_SPI_CLK_BASE 100000000 /* 100m */ -#define MRST_CLK_SPI_REG 0xff11d86c -#define CLK_SPI_BDIV_OFFSET 0 -#define CLK_SPI_BDIV_MASK 0x00000007 -#define CLK_SPI_CDIV_OFFSET 9 -#define CLK_SPI_CDIV_MASK 0x00000e00 -#define CLK_SPI_DISABLE_OFFSET 8 - -int dw_spi_mid_init_mfld(struct dw_spi *dws) -{ - void __iomem *clk_reg; - u32 clk_cdiv; - - clk_reg = ioremap(MRST_CLK_SPI_REG, 16); - if (!clk_reg) - return -ENOMEM; - - /* Get SPI controller operating freq info */ - clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32)); - clk_cdiv &= CLK_SPI_CDIV_MASK; - clk_cdiv >>= CLK_SPI_CDIV_OFFSET; - dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); - - iounmap(clk_reg); - - /* Register hook to configure CTRLR0 */ - dws->update_cr0 = dw_spi_update_cr0; - - dw_spi_mid_setup_dma_mfld(dws); - return 0; -} - -int dw_spi_mid_init_generic(struct dw_spi *dws) -{ - /* Register hook to configure CTRLR0 */ - dws->update_cr0 = dw_spi_update_cr0; - - dw_spi_mid_setup_dma_generic(dws); - return 0; -} diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index dde54a918b5d..c13707b8493e 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -15,6 +15,15 @@ #define DRIVER_NAME "dw_spi_pci" +/* HW info for MRST Clk Control Unit, 32b reg per controller */ +#define MRST_SPI_CLK_BASE 100000000 /* 100m */ +#define MRST_CLK_SPI_REG 0xff11d86c +#define CLK_SPI_BDIV_OFFSET 0 +#define CLK_SPI_BDIV_MASK 0x00000007 +#define CLK_SPI_CDIV_OFFSET 9 +#define CLK_SPI_CDIV_MASK 0x00000e00 +#define CLK_SPI_DISABLE_OFFSET 8 + struct spi_pci_desc { int (*setup)(struct dw_spi *); u16 num_cs; @@ -22,20 +31,55 @@ struct spi_pci_desc { u32 max_freq; }; +static int spi_mid_init(struct dw_spi *dws) +{ + void __iomem *clk_reg; + u32 clk_cdiv; + + clk_reg = ioremap(MRST_CLK_SPI_REG, 16); + if (!clk_reg) + return -ENOMEM; + + /* Get SPI controller operating freq info */ + clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32)); + clk_cdiv &= CLK_SPI_CDIV_MASK; + clk_cdiv >>= CLK_SPI_CDIV_OFFSET; + dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); + + iounmap(clk_reg); + + /* Register hook to configure CTRLR0 */ + dws->update_cr0 = dw_spi_update_cr0; + + dw_spi_mid_setup_dma_mfld(dws); + + return 0; +} + +static int spi_generic_init(struct dw_spi *dws) +{ + /* Register hook to configure CTRLR0 */ + dws->update_cr0 = dw_spi_update_cr0; + + dw_spi_mid_setup_dma_generic(dws); + + return 0; +} + static struct spi_pci_desc spi_pci_mid_desc_1 = { - .setup = dw_spi_mid_init_mfld, + .setup = spi_mid_init, .num_cs = 5, .bus_num = 0, }; static struct spi_pci_desc spi_pci_mid_desc_2 = { - .setup = dw_spi_mid_init_mfld, + .setup = spi_mid_init, .num_cs = 2, .bus_num = 1, }; static struct spi_pci_desc spi_pci_ehl_desc = { - .setup = dw_spi_mid_init_generic, + .setup = spi_generic_init, .num_cs = 2, .bus_num = -1, .max_freq = 100000000, diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 9247670fcdfb..91608cf12636 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -257,8 +257,16 @@ extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer); -/* platform related setup */ -extern int dw_spi_mid_init_mfld(struct dw_spi *dws); -extern int dw_spi_mid_init_generic(struct dw_spi *dws); +#ifdef CONFIG_SPI_DW_DMA + +extern void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws); +extern void dw_spi_mid_setup_dma_generic(struct dw_spi *dws); + +#else + +static inline void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) {} +static inline void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) {} + +#endif /* !CONFIG_SPI_DW_DMA */ #endif /* DW_SPI_HEADER_H */ -- cgit v1.2.3-59-g8ed1b From 06cfadb8c51b05c6b91c2d43e0fe72b3d643dced Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:12:00 +0300 Subject: spi: dw: Remove DW DMA code dependency from DW_DMAC_PCI Since there is a generic method available to initialize the DW SPI DMA interface on any DT and ACPI-based platforms, which in general can be designed with not only DW DMAC but with any DMA engine on board, we can freely remove the CONFIG_DW_DMAC_PCI config from dependency list of CONFIG_SPI_DW_DMA. Especially seeing that we don't use anything DW DMAC specific in the new driver. Co-developed-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Georgy Vlasov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-12-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 73d508eb4871..a20995090ff9 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -228,7 +228,7 @@ config SPI_DESIGNWARE config SPI_DW_DMA bool "DMA support for DW SPI controller" - depends on SPI_DESIGNWARE && DW_DMAC_PCI + depends on SPI_DESIGNWARE config SPI_DW_PCI tristate "PCI interface driver for DW SPI core" -- cgit v1.2.3-59-g8ed1b From ecb3a67edfd353837dc23b538fb250d1dfd88e7b Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:12:01 +0300 Subject: spi: dw: Add DW SPI DMA/PCI/MMIO dependency on the DW SPI core Seeing all of the DW SPI driver components like DW SPI DMA/PCI/MMIO depend on the DW SPI core code it's better to use the if-endif conditional kernel config statement to signify that common dependency. Co-developed-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Georgy Vlasov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-13-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a20995090ff9..8f1f8fca79e3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -226,17 +226,20 @@ config SPI_DESIGNWARE help general driver for SPI controller core from DesignWare +if SPI_DESIGNWARE + config SPI_DW_DMA bool "DMA support for DW SPI controller" - depends on SPI_DESIGNWARE config SPI_DW_PCI tristate "PCI interface driver for DW SPI core" - depends on SPI_DESIGNWARE && PCI + depends on PCI config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" - depends on SPI_DESIGNWARE + depends on HAS_IOMEM + +endif config SPI_DLN2 tristate "Diolan DLN-2 USB SPI adapter" -- cgit v1.2.3-59-g8ed1b From 57784411728ff4d72ae051fdbba1e54fcb1f8d6f Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:12:02 +0300 Subject: spi: dw: Cleanup generic DW DMA code namings Since from now the former Intel MID platform layer is used as a generic DW SPI DMA module, let's alter the internal methods naming to be DMA-related instead of having the "mid_" prefix. Co-developed-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Georgy Vlasov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-14-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 85 ++++++++++++++++++++++++------------------------ drivers/spi/spi-dw-pci.c | 4 +-- drivers/spi/spi-dw.h | 8 ++--- 3 files changed, 49 insertions(+), 48 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 7ae31682b5de..1b96cec6d8cd 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -23,7 +23,7 @@ #define TX_BUSY 1 #define TX_BURST_LEVEL 16 -static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) +static bool dw_spi_dma_chan_filter(struct dma_chan *chan, void *param) { struct dw_dma_slave *s = param; @@ -34,7 +34,7 @@ static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) return true; } -static void mid_spi_maxburst_init(struct dw_spi *dws) +static void dw_spi_dma_maxburst_init(struct dw_spi *dws) { struct dma_slave_caps caps; u32 max_burst, def_burst; @@ -59,7 +59,7 @@ static void mid_spi_maxburst_init(struct dw_spi *dws) dws->txburst = min(max_burst, def_burst); } -static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) +static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) { struct dw_dma_slave slave = { .src_id = 0, @@ -81,13 +81,13 @@ static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) /* 1. Init rx channel */ slave.dma_dev = &dma_dev->dev; - dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); + dws->rxchan = dma_request_channel(mask, dw_spi_dma_chan_filter, &slave); if (!dws->rxchan) goto err_exit; /* 2. Init tx channel */ slave.dst_id = 1; - dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, &slave); + dws->txchan = dma_request_channel(mask, dw_spi_dma_chan_filter, &slave); if (!dws->txchan) goto free_rxchan; @@ -96,7 +96,7 @@ static int mid_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) init_completion(&dws->dma_completion); - mid_spi_maxburst_init(dws); + dw_spi_dma_maxburst_init(dws); return 0; @@ -107,7 +107,7 @@ err_exit: return -EBUSY; } -static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) +static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) { dws->rxchan = dma_request_slave_channel(dev, "rx"); if (!dws->rxchan) @@ -125,12 +125,12 @@ static int mid_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) init_completion(&dws->dma_completion); - mid_spi_maxburst_init(dws); + dw_spi_dma_maxburst_init(dws); return 0; } -static void mid_spi_dma_exit(struct dw_spi *dws) +static void dw_spi_dma_exit(struct dw_spi *dws) { if (dws->txchan) { dmaengine_terminate_sync(dws->txchan); @@ -145,7 +145,7 @@ static void mid_spi_dma_exit(struct dw_spi *dws) dw_writel(dws, DW_SPI_DMACR, 0); } -static irqreturn_t dma_transfer(struct dw_spi *dws) +static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws) { u16 irq_status = dw_readl(dws, DW_SPI_ISR); @@ -161,15 +161,16 @@ static irqreturn_t dma_transfer(struct dw_spi *dws) return IRQ_HANDLED; } -static bool mid_spi_can_dma(struct spi_controller *master, - struct spi_device *spi, struct spi_transfer *xfer) +static bool dw_spi_can_dma(struct spi_controller *master, + struct spi_device *spi, struct spi_transfer *xfer) { struct dw_spi *dws = spi_controller_get_devdata(master); return xfer->len > dws->fifo_len; } -static enum dma_slave_buswidth convert_dma_width(u8 n_bytes) { +static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes) +{ if (n_bytes == 1) return DMA_SLAVE_BUSWIDTH_1_BYTE; else if (n_bytes == 2) @@ -244,8 +245,8 @@ static void dw_spi_dma_tx_done(void *arg) complete(&dws->dma_completion); } -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, - struct spi_transfer *xfer) +static struct dma_async_tx_descriptor * +dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_slave_config txconf; struct dma_async_tx_descriptor *txdesc; @@ -258,7 +259,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = dws->txburst; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - txconf.dst_addr_width = convert_dma_width(dws->n_bytes); + txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); txconf.device_fc = false; dmaengine_slave_config(dws->txchan, &txconf); @@ -350,7 +351,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = dws->rxburst; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - rxconf.src_addr_width = convert_dma_width(dws->n_bytes); + rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); rxconf.device_fc = false; dmaengine_slave_config(dws->rxchan, &rxconf); @@ -369,7 +370,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, return rxdesc; } -static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 imr = 0, dma_ctrl = 0; @@ -391,12 +392,12 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) reinit_completion(&dws->dma_completion); - dws->transfer_handler = dma_transfer; + dws->transfer_handler = dw_spi_dma_transfer_handler; return 0; } -static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *txdesc, *rxdesc; int ret; @@ -436,7 +437,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) return ret; } -static void mid_spi_dma_stop(struct dw_spi *dws) +static void dw_spi_dma_stop(struct dw_spi *dws) { if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { dmaengine_terminate_sync(dws->txchan); @@ -450,32 +451,32 @@ static void mid_spi_dma_stop(struct dw_spi *dws) dw_writel(dws, DW_SPI_DMACR, 0); } -static const struct dw_spi_dma_ops mfld_dma_ops = { - .dma_init = mid_spi_dma_init_mfld, - .dma_exit = mid_spi_dma_exit, - .dma_setup = mid_spi_dma_setup, - .can_dma = mid_spi_can_dma, - .dma_transfer = mid_spi_dma_transfer, - .dma_stop = mid_spi_dma_stop, +static const struct dw_spi_dma_ops dw_spi_dma_mfld_ops = { + .dma_init = dw_spi_dma_init_mfld, + .dma_exit = dw_spi_dma_exit, + .dma_setup = dw_spi_dma_setup, + .can_dma = dw_spi_can_dma, + .dma_transfer = dw_spi_dma_transfer, + .dma_stop = dw_spi_dma_stop, }; -void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) +void dw_spi_dma_setup_mfld(struct dw_spi *dws) { - dws->dma_ops = &mfld_dma_ops; + dws->dma_ops = &dw_spi_dma_mfld_ops; } -EXPORT_SYMBOL_GPL(dw_spi_mid_setup_dma_mfld); - -static const struct dw_spi_dma_ops generic_dma_ops = { - .dma_init = mid_spi_dma_init_generic, - .dma_exit = mid_spi_dma_exit, - .dma_setup = mid_spi_dma_setup, - .can_dma = mid_spi_can_dma, - .dma_transfer = mid_spi_dma_transfer, - .dma_stop = mid_spi_dma_stop, +EXPORT_SYMBOL_GPL(dw_spi_dma_setup_mfld); + +static const struct dw_spi_dma_ops dw_spi_dma_generic_ops = { + .dma_init = dw_spi_dma_init_generic, + .dma_exit = dw_spi_dma_exit, + .dma_setup = dw_spi_dma_setup, + .can_dma = dw_spi_can_dma, + .dma_transfer = dw_spi_dma_transfer, + .dma_stop = dw_spi_dma_stop, }; -void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) +void dw_spi_dma_setup_generic(struct dw_spi *dws) { - dws->dma_ops = &generic_dma_ops; + dws->dma_ops = &dw_spi_dma_generic_ops; } -EXPORT_SYMBOL_GPL(dw_spi_mid_setup_dma_generic); +EXPORT_SYMBOL_GPL(dw_spi_dma_setup_generic); diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index c13707b8493e..2ea73809ca34 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -51,7 +51,7 @@ static int spi_mid_init(struct dw_spi *dws) /* Register hook to configure CTRLR0 */ dws->update_cr0 = dw_spi_update_cr0; - dw_spi_mid_setup_dma_mfld(dws); + dw_spi_dma_setup_mfld(dws); return 0; } @@ -61,7 +61,7 @@ static int spi_generic_init(struct dw_spi *dws) /* Register hook to configure CTRLR0 */ dws->update_cr0 = dw_spi_update_cr0; - dw_spi_mid_setup_dma_generic(dws); + dw_spi_dma_setup_generic(dws); return 0; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 91608cf12636..0b2cd7994513 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -259,13 +259,13 @@ extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, #ifdef CONFIG_SPI_DW_DMA -extern void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws); -extern void dw_spi_mid_setup_dma_generic(struct dw_spi *dws); +extern void dw_spi_dma_setup_mfld(struct dw_spi *dws); +extern void dw_spi_dma_setup_generic(struct dw_spi *dws); #else -static inline void dw_spi_mid_setup_dma_mfld(struct dw_spi *dws) {} -static inline void dw_spi_mid_setup_dma_generic(struct dw_spi *dws) {} +static inline void dw_spi_dma_setup_mfld(struct dw_spi *dws) {} +static inline void dw_spi_dma_setup_generic(struct dw_spi *dws) {} #endif /* !CONFIG_SPI_DW_DMA */ -- cgit v1.2.3-59-g8ed1b From 0fdad596d46b28d5c3e39d1897c5e3878b64d9a2 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:12:03 +0300 Subject: spi: dw: Add DMA support to the DW SPI MMIO driver Since the common code in the spi-dw-dma.c driver is ready to be used by the MMIO driver and now provides a method to generically (on any DT or ACPI-based platforms) retrieve the Tx/Rx DMA channel handlers, we can use it and a set of the common DW SPI DMA callbacks to enable DMA at least for generic "snps,dw-apb-ssi" and "snps,dwc-ssi-1.01a" devices. Co-developed-by: Georgy Vlasov Co-developed-by: Ramil Zaripov Signed-off-by: Georgy Vlasov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-15-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 0894b4c09496..e23d0c53a664 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -149,6 +149,8 @@ static int dw_spi_dw_apb_init(struct platform_device *pdev, /* Register hook to configure CTRLR0 */ dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + dw_spi_dma_setup_generic(&dwsmmio->dws); + return 0; } @@ -158,6 +160,8 @@ static int dw_spi_dwc_ssi_init(struct platform_device *pdev, /* Register hook to configure CTRLR0 */ dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a; + dw_spi_dma_setup_generic(&dwsmmio->dws); + return 0; } -- cgit v1.2.3-59-g8ed1b From 8378449d1f79add31be77e77fc7df9f639878e9c Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Fri, 29 May 2020 16:12:04 +0300 Subject: spi: dw: Use regset32 DebugFS method to create regdump file DebugFS kernel interface provides a dedicated method to create the registers dump file. Use it instead of creating a generic DebugFS file with manually written read callback function. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Cc: Georgy Vlasov Cc: Ramil Zaripov Cc: Alexey Malahov Cc: Thomas Bogendoerfer Cc: Arnd Bergmann Cc: Feng Tang Cc: Rob Herring Cc: linux-mips@vger.kernel.org Cc: devicetree@vger.kernel.org Link: https://lore.kernel.org/r/20200529131205.31838-16-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 86 ++++++++++++++--------------------------------- drivers/spi/spi-dw.h | 2 ++ 2 files changed, 28 insertions(+), 60 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 1f1a3536b7cf..caeece6681b3 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -29,66 +29,29 @@ struct chip_data { }; #ifdef CONFIG_DEBUG_FS -#define SPI_REGS_BUFSIZE 1024 -static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct dw_spi *dws = file->private_data; - char *buf; - u32 len = 0; - ssize_t ret; - - buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL); - if (!buf) - return 0; - - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "%s registers:\n", dev_name(&dws->master->dev)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "=================================\n"); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRLR0: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR0)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRLR1: \t0x%08x\n", dw_readl(dws, DW_SPI_CTRLR1)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SSIENR: \t0x%08x\n", dw_readl(dws, DW_SPI_SSIENR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SER: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SER)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "BAUDR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_BAUDR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFTLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFTLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "TXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_TXFLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "RXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_RXFLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "IMR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_IMR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "ISR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_ISR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMACR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_DMACR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMATDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMATDLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMARDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMARDLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "=================================\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return ret; + +#define DW_SPI_DBGFS_REG(_name, _off) \ +{ \ + .name = _name, \ + .offset = _off, \ } -static const struct file_operations dw_spi_regs_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = dw_spi_show_regs, - .llseek = default_llseek, +static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = { + DW_SPI_DBGFS_REG("CTRLR0", DW_SPI_CTRLR0), + DW_SPI_DBGFS_REG("CTRLR1", DW_SPI_CTRLR1), + DW_SPI_DBGFS_REG("SSIENR", DW_SPI_SSIENR), + DW_SPI_DBGFS_REG("SER", DW_SPI_SER), + DW_SPI_DBGFS_REG("BAUDR", DW_SPI_BAUDR), + DW_SPI_DBGFS_REG("TXFTLR", DW_SPI_TXFTLR), + DW_SPI_DBGFS_REG("RXFTLR", DW_SPI_RXFTLR), + DW_SPI_DBGFS_REG("TXFLR", DW_SPI_TXFLR), + DW_SPI_DBGFS_REG("RXFLR", DW_SPI_RXFLR), + DW_SPI_DBGFS_REG("SR", DW_SPI_SR), + DW_SPI_DBGFS_REG("IMR", DW_SPI_IMR), + DW_SPI_DBGFS_REG("ISR", DW_SPI_ISR), + DW_SPI_DBGFS_REG("DMACR", DW_SPI_DMACR), + DW_SPI_DBGFS_REG("DMATDLR", DW_SPI_DMATDLR), + DW_SPI_DBGFS_REG("DMARDLR", DW_SPI_DMARDLR), }; static int dw_spi_debugfs_init(struct dw_spi *dws) @@ -100,8 +63,11 @@ static int dw_spi_debugfs_init(struct dw_spi *dws) if (!dws->debugfs) return -ENOMEM; - debugfs_create_file("registers", S_IFREG | S_IRUGO, - dws->debugfs, (void *)dws, &dw_spi_regs_ops); + dws->regset.regs = dw_spi_dbgfs_regs; + dws->regset.nregs = ARRAY_SIZE(dw_spi_dbgfs_regs); + dws->regset.base = dws->regs; + debugfs_create_regset32("registers", 0400, dws->debugfs, &dws->regset); + return 0; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 0b2cd7994513..151ba316619e 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -3,6 +3,7 @@ #define DW_SPI_HEADER_H #include +#include #include #include #include @@ -152,6 +153,7 @@ struct dw_spi { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; + struct debugfs_regset32 regset; #endif }; -- cgit v1.2.3-59-g8ed1b From 118eb0e52eb74b899053a0f46dfe7e178788d23b Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 28 May 2020 12:06:05 -0700 Subject: spi: bcm2835: Implement shutdown callback Make sure we clear the FIFOs, stop the block, disable the clock and release the DMA channel. Signed-off-by: Florian Fainelli Link: https://lore.kernel.org/r/20200528190605.24850-1-f.fainelli@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 20d8581fdf88..237bd306c268 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -1391,6 +1391,15 @@ static int bcm2835_spi_remove(struct platform_device *pdev) return 0; } +static void bcm2835_spi_shutdown(struct platform_device *pdev) +{ + int ret; + + ret = bcm2835_spi_remove(pdev); + if (ret) + dev_err(&pdev->dev, "failed to shutdown\n"); +} + static const struct of_device_id bcm2835_spi_match[] = { { .compatible = "brcm,bcm2835-spi", }, {} @@ -1404,6 +1413,7 @@ static struct platform_driver bcm2835_spi_driver = { }, .probe = bcm2835_spi_probe, .remove = bcm2835_spi_remove, + .shutdown = bcm2835_spi_shutdown, }; module_platform_driver(bcm2835_spi_driver); -- cgit v1.2.3-59-g8ed1b From ecfbd3cf3b8bb73ac6a80ddf430b5912fd4402a6 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Thu, 28 May 2020 20:58:04 +0200 Subject: spi: bcm2835: Enable shared interrupt support bcm2711, Rasberry Pi 4's SoC, shares one interrupt for multiple instances of the bcm2835 SPI controller. So this enables shared interrupt support for them. The early bail out in the interrupt routine avoids messing with buffers of transfers being done by other means. Otherwise, the driver can handle receiving interrupts asserted by other controllers during an IRQ based transfer. Signed-off-by: Martin Sperl Signed-off-by: Nicolas Saenz Julienne Tested-by: Florian Fainelli Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20200528185805.28991-1-nsaenzjulienne@suse.de Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 237bd306c268..f3e41676e825 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -379,6 +379,10 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) if (bs->tx_len && cs & BCM2835_SPI_CS_DONE) bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); + /* check if we got interrupt enabled */ + if (!(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_INTR)) + return IRQ_NONE; + /* Read as many bytes as possible from FIFO */ bcm2835_rd_fifo(bs); /* Write as many bytes as possible to FIFO */ @@ -1344,8 +1348,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev) bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); - err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0, - dev_name(&pdev->dev), ctlr); + err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, + IRQF_SHARED, dev_name(&pdev->dev), ctlr); if (err) { dev_err(&pdev->dev, "could not request IRQ: %d\n", err); goto out_dma_release; -- cgit v1.2.3-59-g8ed1b From 7830c0ef26cb73b653c2db2f3ca7be08f44fa046 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 29 May 2020 10:58:05 -0500 Subject: spi: dw: add reset control Add mechanism to get the reset control and deassert it in order to bring the IP out of reset. Signed-off-by: Liang Jin J Signed-off-by: Dinh Nguyen Link: https://lore.kernel.org/r/20200529155806.16758-1-dinguyen@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 0894b4c09496..a1b87d4606ba 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -29,6 +30,7 @@ struct dw_spi_mmio { struct clk *clk; struct clk *pclk; void *priv; + struct reset_control *rstc; }; #define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 @@ -224,6 +226,14 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) if (ret) goto out_clk; + /* find an optional reset controller */ + dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi"); + if (IS_ERR(dwsmmio->rstc)) { + ret = PTR_ERR(dwsmmio->rstc); + goto out_clk; + } + reset_control_deassert(dwsmmio->rstc); + dws->bus_num = pdev->id; dws->max_freq = clk_get_rate(dwsmmio->clk); @@ -257,6 +267,8 @@ out: clk_disable_unprepare(dwsmmio->pclk); out_clk: clk_disable_unprepare(dwsmmio->clk); + reset_control_assert(dwsmmio->rstc); + return ret; } @@ -268,6 +280,7 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable_unprepare(dwsmmio->pclk); clk_disable_unprepare(dwsmmio->clk); + reset_control_assert(dwsmmio->rstc); return 0; } -- cgit v1.2.3-59-g8ed1b From d62069c22eda3caeca97f6713a9ed2662ef1346d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 29 May 2020 18:48:46 +0100 Subject: spi: bcm2835: Remove shared interrupt support This reverts commit ecfbd3cf3b8b since Lukas Wunner noticed that we start operating on the hardware before we check to see if this is a spurious interrupt. Reported-by: Lukas Wunner Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index f3e41676e825..237bd306c268 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -379,10 +379,6 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) if (bs->tx_len && cs & BCM2835_SPI_CS_DONE) bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); - /* check if we got interrupt enabled */ - if (!(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_INTR)) - return IRQ_NONE; - /* Read as many bytes as possible from FIFO */ bcm2835_rd_fifo(bs); /* Write as many bytes as possible to FIFO */ @@ -1348,8 +1344,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev) bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); - err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, - IRQF_SHARED, dev_name(&pdev->dev), ctlr); + err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0, + dev_name(&pdev->dev), ctlr); if (err) { dev_err(&pdev->dev, "could not request IRQ: %d\n", err); goto out_dma_release; -- cgit v1.2.3-59-g8ed1b From b3f82dc26c0d4e1348ef81e0189cb8838b8b0d22 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 29 May 2020 21:31:49 +0300 Subject: spi: dw: Make DMA request line assignments explicit for Intel Medfield The 2afccbd283ae ("spi: dw: Discard static DW DMA slave structures") did a clean up of global variables, which is fine, but messed up with the carefully provided information in the custom DMA slave structures. There reader can find an assignment of the DMA request lines in use. Partially revert the above mentioned commit to restore readability and maintainability of the code. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200529183150.44149-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 1b96cec6d8cd..53d5257662e8 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -61,10 +61,8 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws) static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) { - struct dw_dma_slave slave = { - .src_id = 0, - .dst_id = 0 - }; + struct dw_dma_slave dma_tx = { .dst_id = 1 }, *tx = &dma_tx; + struct dw_dma_slave dma_rx = { .src_id = 0 }, *rx = &dma_rx; struct pci_dev *dma_dev; dma_cap_mask_t mask; @@ -80,14 +78,14 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) dma_cap_set(DMA_SLAVE, mask); /* 1. Init rx channel */ - slave.dma_dev = &dma_dev->dev; - dws->rxchan = dma_request_channel(mask, dw_spi_dma_chan_filter, &slave); + rx->dma_dev = &dma_dev->dev; + dws->rxchan = dma_request_channel(mask, dw_spi_dma_chan_filter, rx); if (!dws->rxchan) goto err_exit; /* 2. Init tx channel */ - slave.dst_id = 1; - dws->txchan = dma_request_channel(mask, dw_spi_dma_chan_filter, &slave); + tx->dma_dev = &dma_dev->dev; + dws->txchan = dma_request_channel(mask, dw_spi_dma_chan_filter, tx); if (!dws->txchan) goto free_rxchan; -- cgit v1.2.3-59-g8ed1b From 3d7db0f11c7ad19979a1a01cac1d379ff040e886 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 29 May 2020 21:31:50 +0300 Subject: spi: dw: Refactor mid_spi_dma_setup() to separate DMA and IRQ config It's better to understand what bits are set for DMA and for IRQ handling in mid_spi_dma_setup() if they are grouped accordingly. Thus, refactor mid_spi_dma_setup() to separate DMA and IRQ configuration. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200529183150.44149-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 53d5257662e8..5986c520b196 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -375,17 +375,17 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1); dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst); - if (xfer->tx_buf) { + if (xfer->tx_buf) dma_ctrl |= SPI_DMA_TDMAE; - imr |= SPI_INT_TXOI; - } - if (xfer->rx_buf) { + if (xfer->rx_buf) dma_ctrl |= SPI_DMA_RDMAE; - imr |= SPI_INT_RXUI | SPI_INT_RXOI; - } dw_writel(dws, DW_SPI_DMACR, dma_ctrl); /* Set the interrupt mask */ + if (xfer->tx_buf) + imr |= SPI_INT_TXOI; + if (xfer->rx_buf) + imr |= SPI_INT_RXUI | SPI_INT_RXOI; spi_umask_intr(dws, imr); reinit_completion(&dws->dma_completion); -- cgit v1.2.3-59-g8ed1b From 263b81dc6c932c8bc550d5e7bfc178d2b3fc491e Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 29 May 2020 21:57:56 +0200 Subject: spi: spi-fsl-dspi: fix native data copy ColdFire is a big-endian cpu with a big-endian dspi hw module, so, it uses native access, but memcpy breaks the endianness. So, if i understand properly, by native copy we would mean be(cpu)->be(dspi) or le(cpu)->le(dspi) accesses, so my fix shouldn't break anything, but i couldn't test it on LS family, so every test is really appreciated. Fixes: 53fadb4d90c7 ("spi: spi-fsl-dspi: Simplify bytes_per_word gymnastics") Signed-off-by: Angelo Dureghello Tested-by: Vladimir Oltean Reviewed-by: Vladimir Oltean Link: https://lore.kernel.org/r/20200529195756.184677-1-angelo.dureghello@timesys.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 685afdf9e807..a35faced0456 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -250,13 +250,33 @@ struct fsl_dspi { static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata) { - memcpy(txdata, dspi->tx, dspi->oper_word_size); + switch (dspi->oper_word_size) { + case 1: + *txdata = *(u8 *)dspi->tx; + break; + case 2: + *txdata = *(u16 *)dspi->tx; + break; + case 4: + *txdata = *(u32 *)dspi->tx; + break; + } dspi->tx += dspi->oper_word_size; } static void dspi_native_dev_to_host(struct fsl_dspi *dspi, u32 rxdata) { - memcpy(dspi->rx, &rxdata, dspi->oper_word_size); + switch (dspi->oper_word_size) { + case 1: + *(u8 *)dspi->rx = rxdata; + break; + case 2: + *(u16 *)dspi->rx = rxdata; + break; + case 4: + *(u32 *)dspi->rx = rxdata; + break; + } dspi->rx += dspi->oper_word_size; } -- cgit v1.2.3-59-g8ed1b