From 5b177234e9fde7d4208e8163debc109b86e3f68d Mon Sep 17 00:00:00 2001 From: Guochun Mao Date: Tue, 18 Jan 2022 22:28:18 +0800 Subject: spi: spi-mtk-nor: improve device table for adding more capabilities Define a structure for adding more capabilities. Add a item extra_dummy_bit for new SoCs, due to design changed. Signed-off-by: Guochun Mao Signed-off-by: Zhen Zhang Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220118142820.2729-3-guochun.mao@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 48 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 5c93730615f8..f5ff01f61f42 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -95,6 +95,17 @@ #define CLK_TO_US(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000) +struct mtk_nor_caps { + u8 dma_bits; + + /* extra_dummy_bit is adding for the IP of new SoCs. + * Some new SoCs modify the timing of fetching registers' values + * and IDs of nor flash, they need a extra_dummy_bit which can add + * more clock cycles for fetching data. + */ + u8 extra_dummy_bit; +}; + struct mtk_nor { struct spi_controller *ctlr; struct device *dev; @@ -109,6 +120,7 @@ struct mtk_nor { bool has_irq; bool high_dma; struct completion op_done; + const struct mtk_nor_caps *caps; }; static inline void mtk_nor_rmw(struct mtk_nor *sp, u32 reg, u32 set, u32 clr) @@ -554,7 +566,12 @@ static int mtk_nor_spi_mem_prg(struct mtk_nor *sp, const struct spi_mem_op *op) } // trigger op - writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT); + if (rx_len) + writel(prg_len * BITS_PER_BYTE + sp->caps->extra_dummy_bit, + sp->base + MTK_NOR_REG_PRG_CNT); + else + writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT); + ret = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_PROGRAM, prg_len * BITS_PER_BYTE); if (ret) @@ -743,9 +760,19 @@ static const struct spi_controller_mem_ops mtk_nor_mem_ops = { .exec_op = mtk_nor_exec_op }; +const struct mtk_nor_caps mtk_nor_caps_mt8173 = { + .dma_bits = 32, + .extra_dummy_bit = 0, +}; + +const struct mtk_nor_caps mtk_nor_caps_mt8192 = { + .dma_bits = 36, + .extra_dummy_bit = 0, +}; + static const struct of_device_id mtk_nor_match[] = { - { .compatible = "mediatek,mt8192-nor", .data = (void *)36 }, - { .compatible = "mediatek,mt8173-nor", .data = (void *)32 }, + { .compatible = "mediatek,mt8173-nor", .data = &mtk_nor_caps_mt8173 }, + { .compatible = "mediatek,mt8192-nor", .data = &mtk_nor_caps_mt8192 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_nor_match); @@ -754,10 +781,10 @@ static int mtk_nor_probe(struct platform_device *pdev) { struct spi_controller *ctlr; struct mtk_nor *sp; + struct mtk_nor_caps *caps; void __iomem *base; struct clk *spi_clk, *ctlr_clk, *axi_clk; int ret, irq; - unsigned long dma_bits; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -775,10 +802,12 @@ static int mtk_nor_probe(struct platform_device *pdev) if (IS_ERR(axi_clk)) return PTR_ERR(axi_clk); - dma_bits = (unsigned long)of_device_get_match_data(&pdev->dev); - if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(dma_bits))) { - dev_err(&pdev->dev, "failed to set dma mask(%lu)\n", dma_bits); - return -EINVAL; + caps = (struct mtk_nor_caps *)of_device_get_match_data(&pdev->dev); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(caps->dma_bits)); + if (ret) { + dev_err(&pdev->dev, "failed to set dma mask(%u)\n", caps->dma_bits); + return ret; } ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*sp)); @@ -808,7 +837,8 @@ static int mtk_nor_probe(struct platform_device *pdev) sp->spi_clk = spi_clk; sp->ctlr_clk = ctlr_clk; sp->axi_clk = axi_clk; - sp->high_dma = (dma_bits > 32); + sp->caps = caps; + sp->high_dma = caps->dma_bits > 32; sp->buffer = dmam_alloc_coherent(&pdev->dev, MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN, &sp->buffer_dma, GFP_KERNEL); -- cgit From 4e8bfe5cdf77621cb4e7b196448ceeff20d9d6a6 Mon Sep 17 00:00:00 2001 From: Guochun Mao Date: Tue, 18 Jan 2022 22:28:19 +0800 Subject: spi: spi-mtk-nor: add new soc mt8186 support Add compatible mediatek,mt8186-nor implementation. Signed-off-by: Guochun Mao Signed-off-by: Zhen Zhang Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220118142820.2729-4-guochun.mao@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index f5ff01f61f42..e44fdf7c9e4b 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -765,6 +765,11 @@ const struct mtk_nor_caps mtk_nor_caps_mt8173 = { .extra_dummy_bit = 0, }; +const struct mtk_nor_caps mtk_nor_caps_mt8186 = { + .dma_bits = 32, + .extra_dummy_bit = 1, +}; + const struct mtk_nor_caps mtk_nor_caps_mt8192 = { .dma_bits = 36, .extra_dummy_bit = 0, @@ -772,6 +777,7 @@ const struct mtk_nor_caps mtk_nor_caps_mt8192 = { static const struct of_device_id mtk_nor_match[] = { { .compatible = "mediatek,mt8173-nor", .data = &mtk_nor_caps_mt8173 }, + { .compatible = "mediatek,mt8186-nor", .data = &mtk_nor_caps_mt8186 }, { .compatible = "mediatek,mt8192-nor", .data = &mtk_nor_caps_mt8192 }, { /* sentinel */ } }; -- cgit From 58b0a653b8dac40bbeb01a2c8a230aa8f84a7530 Mon Sep 17 00:00:00 2001 From: Guochun Mao Date: Tue, 18 Jan 2022 22:28:20 +0800 Subject: spi: spi-mtk-nor: add axi_s clock for mt8186 MT8186 needs axi_s clock for DMA feature. Signed-off-by: Guochun Mao Signed-off-by: Zhen Zhang Acked-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220118142820.2729-5-guochun.mao@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index e44fdf7c9e4b..455b4dcb26e9 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -115,6 +115,7 @@ struct mtk_nor { struct clk *spi_clk; struct clk *ctlr_clk; struct clk *axi_clk; + struct clk *axi_s_clk; unsigned int spi_freq; bool wbuf_en; bool has_irq; @@ -691,6 +692,7 @@ static void mtk_nor_disable_clk(struct mtk_nor *sp) clk_disable_unprepare(sp->spi_clk); clk_disable_unprepare(sp->ctlr_clk); clk_disable_unprepare(sp->axi_clk); + clk_disable_unprepare(sp->axi_s_clk); } static int mtk_nor_enable_clk(struct mtk_nor *sp) @@ -714,6 +716,14 @@ static int mtk_nor_enable_clk(struct mtk_nor *sp) return ret; } + ret = clk_prepare_enable(sp->axi_s_clk); + if (ret) { + clk_disable_unprepare(sp->spi_clk); + clk_disable_unprepare(sp->ctlr_clk); + clk_disable_unprepare(sp->axi_clk); + return ret; + } + return 0; } @@ -789,7 +799,7 @@ static int mtk_nor_probe(struct platform_device *pdev) struct mtk_nor *sp; struct mtk_nor_caps *caps; void __iomem *base; - struct clk *spi_clk, *ctlr_clk, *axi_clk; + struct clk *spi_clk, *ctlr_clk, *axi_clk, *axi_s_clk; int ret, irq; base = devm_platform_ioremap_resource(pdev, 0); @@ -808,6 +818,10 @@ static int mtk_nor_probe(struct platform_device *pdev) if (IS_ERR(axi_clk)) return PTR_ERR(axi_clk); + axi_s_clk = devm_clk_get_optional(&pdev->dev, "axi_s"); + if (IS_ERR(axi_s_clk)) + return PTR_ERR(axi_s_clk); + caps = (struct mtk_nor_caps *)of_device_get_match_data(&pdev->dev); ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(caps->dma_bits)); @@ -843,6 +857,7 @@ static int mtk_nor_probe(struct platform_device *pdev) sp->spi_clk = spi_clk; sp->ctlr_clk = ctlr_clk; sp->axi_clk = axi_clk; + sp->axi_s_clk = axi_s_clk; sp->caps = caps; sp->high_dma = caps->dma_bits > 32; sp->buffer = dmam_alloc_coherent(&pdev->dev, -- cgit From a45cf3cc72dd9cfde9db8af32cdf9c431f53f9bc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 19 Jan 2022 00:09:15 +0100 Subject: spi: s3c64xx: Convert to use GPIO descriptors Convert the S3C64xx SPI host to use GPIO descriptors. Provide GPIO descriptor tables for the one user with CS 0 and 1. Cc: linux-samsung-soc@vger.kernel.org Cc: Sylwester Nawrocki Reviewed-by: Krzysztof Kozlowski Reviewed-by: Sam Protsenko Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220118230915.157797-3-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 53 ++++++++++++----------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 8755cd85e83c..3e42cdb19d27 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -13,10 +13,8 @@ #include #include #include -#include #include #include -#include #include @@ -656,7 +654,11 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master, struct s3c64xx_spi_csinfo *cs = spi->controller_data; /* Configure feedback delay */ - writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); + if (!cs) + /* No delay if not defined */ + writel(0, sdd->regs + S3C64XX_SPI_FB_CLK); + else + writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); return 0; } @@ -830,34 +832,16 @@ static int s3c64xx_spi_setup(struct spi_device *spi) if (spi->dev.of_node) { cs = s3c64xx_get_slave_ctrldata(spi); spi->controller_data = cs; - } else if (cs) { - /* On non-DT platforms the SPI core will set spi->cs_gpio - * to -ENOENT. The GPIO pin used to drive the chip select - * is defined by using platform data so spi->cs_gpio value - * has to be override to have the proper GPIO pin number. - */ - spi->cs_gpio = cs->line; } - if (IS_ERR_OR_NULL(cs)) { + /* NULL is fine, we just avoid using the FB delay (=0) */ + if (IS_ERR(cs)) { dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select); return -ENODEV; } - if (!spi_get_ctldata(spi)) { - if (gpio_is_valid(spi->cs_gpio)) { - err = gpio_request_one(spi->cs_gpio, GPIOF_OUT_INIT_HIGH, - dev_name(&spi->dev)); - if (err) { - dev_err(&spi->dev, - "Failed to get /CS gpio [%d]: %d\n", - spi->cs_gpio, err); - goto err_gpio_req; - } - } - + if (!spi_get_ctldata(spi)) spi_set_ctldata(spi, cs); - } pm_runtime_get_sync(&sdd->pdev->dev); @@ -909,11 +893,9 @@ setup_exit: /* setup() returns with device de-selected */ s3c64xx_spi_set_cs(spi, false); - if (gpio_is_valid(spi->cs_gpio)) - gpio_free(spi->cs_gpio); spi_set_ctldata(spi, NULL); -err_gpio_req: + /* This was dynamically allocated on the DT path */ if (spi->dev.of_node) kfree(cs); @@ -924,19 +906,9 @@ static void s3c64xx_spi_cleanup(struct spi_device *spi) { struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi); - if (gpio_is_valid(spi->cs_gpio)) { - gpio_free(spi->cs_gpio); - if (spi->dev.of_node) - kfree(cs); - else { - /* On non-DT platforms, the SPI core sets - * spi->cs_gpio to -ENOENT and .setup() - * overrides it with the GPIO pin value - * passed using platform data. - */ - spi->cs_gpio = -ENOENT; - } - } + /* This was dynamically allocated on the DT path */ + if (spi->dev.of_node) + kfree(cs); spi_set_ctldata(spi, NULL); } @@ -1131,6 +1103,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->prepare_message = s3c64xx_spi_prepare_message; master->transfer_one = s3c64xx_spi_transfer_one; master->num_chipselect = sci->num_cs; + master->use_gpio_descriptors = true; master->dma_alignment = 8; master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | SPI_BPW_MASK(8); -- cgit From 7f2a3cf4e6077a1525092f114be7819e505773a1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 19 Jan 2022 01:09:14 +0100 Subject: spi: s3c24xx: Convert to GPIO descriptors This driver has a bunch of custom oldstyle GPIO number-passing fields and a custom set-up callback. The good thing is: nothing in the kernel is using it. Convert the driver to use GPIO descriptors with a SPI_MASTER_GPIO_SS flag so that the local CS callback also get invoked as the hardware needs this. New users of this driver can provide GPIO descriptor tables like the other converted drivers. Cc: linux-samsung-soc@vger.kernel.org Cc: Krzysztof Kozlowski Cc: Sylwester Nawrocki Signed-off-by: Linus Walleij Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220119000914.192553-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 47 +++-------------------------------------------- 1 file changed, 3 insertions(+), 44 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index d6f51695ca5b..660aa866af06 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -62,9 +61,6 @@ struct s3c24xx_spi { unsigned char fiq_inuse; unsigned char fiq_claimed; - void (*set_cs)(struct s3c2410_spi_info *spi, - int cs, int pol); - /* data buffers */ const unsigned char *tx; unsigned char *rx; @@ -84,29 +80,21 @@ static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev) return spi_master_get_devdata(sdev->master); } -static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol) -{ - gpio_set_value(spi->pin_cs, pol); -} - static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) { struct s3c24xx_spi_devstate *cs = spi->controller_state; struct s3c24xx_spi *hw = to_hw(spi); - unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; /* change the chipselect state and the state of the spi engine clock */ switch (value) { case BITBANG_CS_INACTIVE: - hw->set_cs(hw->pdata, spi->chip_select, cspol^1); writeb(cs->spcon, hw->regs + S3C2410_SPCON); break; case BITBANG_CS_ACTIVE: writeb(cs->spcon | S3C2410_SPCON_ENSCK, hw->regs + S3C2410_SPCON); - hw->set_cs(hw->pdata, spi->chip_select, cspol); break; } } @@ -452,14 +440,6 @@ static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw) writeb(0xff, hw->regs + S3C2410_SPPRE); writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN); writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON); - - if (hw->pdata) { - if (hw->set_cs == s3c24xx_spi_gpiocs) - gpio_direction_output(hw->pdata->pin_cs, 1); - - if (hw->pdata->gpio_setup) - hw->pdata->gpio_setup(hw->pdata, 1); - } } static int s3c24xx_spi_probe(struct platform_device *pdev) @@ -502,6 +482,9 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) master->num_chipselect = hw->pdata->num_cs; master->bus_num = pdata->bus_num; master->bits_per_word_mask = SPI_BPW_MASK(8); + /* we need to call the local chipselect callback */ + master->flags = SPI_MASTER_GPIO_SS; + master->use_gpio_descriptors = true; /* setup the state for the bitbang driver */ @@ -541,27 +524,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) goto err_no_pdata; } - /* setup any gpio we can */ - - if (!pdata->set_cs) { - if (pdata->pin_cs < 0) { - dev_err(&pdev->dev, "No chipselect pin\n"); - err = -EINVAL; - goto err_register; - } - - err = devm_gpio_request(&pdev->dev, pdata->pin_cs, - dev_name(&pdev->dev)); - if (err) { - dev_err(&pdev->dev, "Failed to get gpio for cs\n"); - goto err_register; - } - - hw->set_cs = s3c24xx_spi_gpiocs; - gpio_direction_output(pdata->pin_cs, 1); - } else - hw->set_cs = pdata->set_cs; - s3c24xx_spi_initialsetup(hw); /* register our spi controller */ @@ -604,9 +566,6 @@ static int s3c24xx_spi_suspend(struct device *dev) if (ret) return ret; - if (hw->pdata && hw->pdata->gpio_setup) - hw->pdata->gpio_setup(hw->pdata, 0); - clk_disable(hw->clk); return 0; } -- cgit From f62ca4e2a863033d9b3b5a00a0d897557c9da6c5 Mon Sep 17 00:00:00 2001 From: Li-hao Kuo Date: Tue, 18 Jan 2022 16:42:38 +0800 Subject: spi: Add spi driver for Sunplus SP7021 Add spi driver for Sunplus SP7021. Signed-off-by: Li-hao Kuo Link: https://lore.kernel.org/r/37998e515d561e762ee30d0ac4fca25a948e0c5c.1642494310.git.lhjeff911@gmail.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-sunplus-sp7021.c | 602 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 614 insertions(+) create mode 100644 drivers/spi/spi-sunplus-sp7021.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b2a8821971e1..203f4ec32119 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -866,6 +866,17 @@ config SPI_SUN6I help This enables using the SPI controller on the Allwinner A31 SoCs. +config SPI_SUNPLUS_SP7021 + tristate "Sunplus SP7021 SPI controller" + depends on SOC_SP7021 || COMPILE_TEST + help + This enables Sunplus SP7021 SPI controller driver on the SP7021 SoCs. + This driver can also be built as a module. If so, the module will be + called as spi-sunplus-sp7021. + + If you have a Sunplus SP7021 platform say Y here. + If unsure, say N. + config SPI_SYNQUACER tristate "Socionext's SynQuacer HighSpeed SPI controller" depends on ARCH_SYNQUACER || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index dd7393a6046f..b455eafd5367 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o +obj-$(CONFIG_SPI_SUNPLUS_SP7021) += spi-sunplus-sp7021.o obj-$(CONFIG_SPI_SYNQUACER) += spi-synquacer.o obj-$(CONFIG_SPI_TEGRA210_QUAD) += spi-tegra210-quad.o obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c new file mode 100644 index 000000000000..627b9c3024e9 --- /dev/null +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -0,0 +1,602 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021 Sunplus Inc. +// Author: Li-hao Kuo + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SP7021_DATA_RDY_REG 0x0044 +#define SP7021_SLAVE_DMA_CTRL_REG 0x0048 +#define SP7021_SLAVE_DMA_LENGTH_REG 0x004c +#define SP7021_SLAVE_DMA_ADDR_REG 0x004c + +#define SP7021_SLAVE_DATA_RDY BIT(0) +#define SP7021_SLAVE_SW_RST BIT(1) +#define SP7021_SLA_DMA_W_INT BIT(8) +#define SP7021_SLAVE_CLR_INT BIT(8) +#define SP7021_SLAVE_DMA_EN BIT(0) +#define SP7021_SLAVE_DMA_RW BIT(6) +#define SP7021_SLAVE_DMA_CMD GENMASK(3, 2) + +#define SP7021_FIFO_REG 0x0034 +#define SP7021_SPI_STATUS_REG 0x0038 +#define SP7021_SPI_CONFIG_REG 0x003c +#define SP7021_INT_BUSY_REG 0x004c +#define SP7021_DMA_CTRL_REG 0x0050 + +#define SP7021_SPI_START_FD BIT(0) +#define SP7021_FD_SW_RST BIT(1) +#define SP7021_TX_EMP_FLAG BIT(2) +#define SP7021_RX_EMP_FLAG BIT(4) +#define SP7021_RX_FULL_FLAG BIT(5) +#define SP7021_FINISH_FLAG BIT(6) + +#define SP7021_TX_CNT_MASK GENMASK(11, 8) +#define SP7021_RX_CNT_MASK GENMASK(15, 12) +#define SP7021_TX_LEN_MASK GENMASK(23, 16) +#define SP7021_GET_LEN_MASK GENMASK(31, 24) +#define SP7021_SET_TX_LEN GENMASK(23, 16) +#define SP7021_SET_XFER_LEN GENMASK(31, 24) + +#define SP7021_CPOL_FD BIT(0) +#define SP7021_CPHA_R BIT(1) +#define SP7021_CPHA_W BIT(2) +#define SP7021_LSB_SEL BIT(4) +#define SP7021_CS_POR BIT(5) +#define SP7021_FD_SEL BIT(6) + +#define SP7021_RX_UNIT GENMASK(8, 7) +#define SP7021_TX_UNIT GENMASK(10, 9) +#define SP7021_TX_EMP_FLAG_MASK BIT(11) +#define SP7021_RX_FULL_FLAG_MASK BIT(14) +#define SP7021_FINISH_FLAG_MASK BIT(15) +#define SP7021_CLEAN_RW_BYTE GENMASK(10, 7) +#define SP7021_CLEAN_FLUG_MASK GENMASK(15, 11) +#define SP7021_CLK_MASK GENMASK(31, 16) + +#define SP7021_INT_BYPASS BIT(3) +#define SP7021_CLR_MASTER_INT BIT(6) + +#define SP7021_SPI_DATA_SIZE (255) +#define SP7021_FIFO_DATA_LEN (16) + +enum SP_SPI_MODE { + SP7021_SLAVE_READ = 0, + SP7021_SLAVE_WRITE = 1, + SP7021_SPI_IDLE = 2, +}; + +enum { + SP7021_MASTER_MODE = 0, + SP7021_SLAVE_MODE = 1, +}; + +struct sp7021_spi_ctlr { + struct device *dev; + struct spi_controller *ctlr; + void __iomem *m_base; + void __iomem *s_base; + u32 xfer_conf; + int mode; + int m_irq; + int s_irq; + struct clk *spi_clk; + struct reset_control *rstc; + // irq spin lock + spinlock_t lock; + // data xfer lock + struct mutex buf_lock; + struct completion isr_done; + struct completion slave_isr; + unsigned int rx_cur_len; + unsigned int tx_cur_len; + unsigned int data_unit; + const u8 *tx_buf; + u8 *rx_buf; +}; + +static irqreturn_t sp7021_spi_slave_irq(int irq, void *dev) +{ + struct sp7021_spi_ctlr *pspim = dev; + unsigned int data_status; + + data_status = readl(pspim->s_base + SP7021_DATA_RDY_REG); + writel(data_status | SP7021_SLAVE_CLR_INT, pspim->s_base + SP7021_DATA_RDY_REG); + complete(&pspim->slave_isr); + return IRQ_HANDLED; +} + +static int sp7021_spi_slave_abort(struct spi_controller *ctlr) +{ + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + + complete(&pspim->slave_isr); + complete(&pspim->isr_done); + return 0; +} + +int sp7021_spi_slave_tx(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller); + + reinit_completion(&pspim->slave_isr); + writel(SP7021_SLAVE_DMA_EN | SP7021_SLAVE_DMA_RW | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3), + pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); + writel(xfer->len, pspim->s_base + SP7021_SLAVE_DMA_LENGTH_REG); + writel(xfer->tx_dma, pspim->s_base + SP7021_SLAVE_DMA_ADDR_REG); + writel(readl(pspim->s_base + SP7021_DATA_RDY_REG) | SP7021_SLAVE_DATA_RDY, + pspim->s_base + SP7021_DATA_RDY_REG); + if (wait_for_completion_interruptible(&pspim->isr_done)) { + dev_err(&spi->dev, "%s() wait_for_completion err\n", __func__); + return -EINTR; + } + return 0; +} + +int sp7021_spi_slave_rx(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller); + int ret = 0; + + reinit_completion(&pspim->isr_done); + writel(SP7021_SLAVE_DMA_EN | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3), + pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); + writel(xfer->len, pspim->s_base + SP7021_SLAVE_DMA_LENGTH_REG); + writel(xfer->rx_dma, pspim->s_base + SP7021_SLAVE_DMA_ADDR_REG); + if (wait_for_completion_interruptible(&pspim->isr_done)) { + dev_err(&spi->dev, "%s() wait_for_completion err\n", __func__); + return -EINTR; + } + writel(SP7021_SLAVE_SW_RST, pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); + return ret; +} + +void sp7021_spi_master_rb(struct sp7021_spi_ctlr *pspim, unsigned int len) +{ + int i; + + for (i = 0; i < len; i++) { + pspim->rx_buf[pspim->rx_cur_len] = + readl(pspim->m_base + SP7021_FIFO_REG); + pspim->rx_cur_len++; + } +} + +void sp7021_spi_master_wb(struct sp7021_spi_ctlr *pspim, unsigned int len) +{ + int i; + + for (i = 0; i < len; i++) { + writel(pspim->tx_buf[pspim->tx_cur_len], + pspim->m_base + SP7021_FIFO_REG); + pspim->tx_cur_len++; + } +} + +static irqreturn_t sp7021_spi_master_irq(int irq, void *dev) +{ + struct sp7021_spi_ctlr *pspim = dev; + unsigned int tx_cnt, total_len; + unsigned int tx_len, rx_cnt; + unsigned int fd_status; + unsigned long flags; + bool isrdone = false; + u32 value; + + fd_status = readl(pspim->m_base + SP7021_SPI_STATUS_REG); + tx_cnt = FIELD_GET(SP7021_TX_CNT_MASK, fd_status); + tx_len = FIELD_GET(SP7021_TX_LEN_MASK, fd_status); + total_len = FIELD_GET(SP7021_GET_LEN_MASK, fd_status); + + if ((fd_status & SP7021_TX_EMP_FLAG) && (fd_status & SP7021_RX_EMP_FLAG) && total_len == 0) + return IRQ_NONE; + + if (tx_len == 0 && total_len == 0) + return IRQ_NONE; + + spin_lock_irqsave(&pspim->lock, flags); + + rx_cnt = FIELD_GET(SP7021_RX_CNT_MASK, fd_status); + if (fd_status & SP7021_RX_FULL_FLAG) + rx_cnt = pspim->data_unit; + + tx_cnt = min(tx_len - pspim->tx_cur_len, pspim->data_unit - tx_cnt); + dev_dbg(pspim->dev, "fd_st=0x%x rx_c:%d tx_c:%d tx_l:%d", + fd_status, rx_cnt, tx_cnt, tx_len); + + if (rx_cnt > 0) + sp7021_spi_master_rb(pspim, rx_cnt); + if (tx_cnt > 0) + sp7021_spi_master_wb(pspim, tx_cnt); + + fd_status = readl(pspim->m_base + SP7021_SPI_STATUS_REG); + tx_len = FIELD_GET(SP7021_TX_LEN_MASK, fd_status); + total_len = FIELD_GET(SP7021_GET_LEN_MASK, fd_status); + + if (fd_status & SP7021_FINISH_FLAG || tx_len == pspim->tx_cur_len) { + while (total_len != pspim->rx_cur_len) { + fd_status = readl(pspim->m_base + SP7021_SPI_STATUS_REG); + total_len = FIELD_GET(SP7021_GET_LEN_MASK, fd_status); + if (fd_status & SP7021_RX_FULL_FLAG) + rx_cnt = pspim->data_unit; + else + rx_cnt = FIELD_GET(SP7021_RX_CNT_MASK, fd_status); + + if (rx_cnt > 0) + sp7021_spi_master_rb(pspim, rx_cnt); + } + value = readl(pspim->m_base + SP7021_INT_BUSY_REG); + value |= SP7021_CLR_MASTER_INT; + writel(value, pspim->m_base + SP7021_INT_BUSY_REG); + writel(SP7021_FINISH_FLAG, pspim->m_base + SP7021_SPI_STATUS_REG); + isrdone = true; + } + + if (isrdone) + complete(&pspim->isr_done); + spin_unlock_irqrestore(&pspim->lock, flags); + return IRQ_HANDLED; +} + +static void sp7021_prep_transfer(struct spi_controller *ctlr, struct spi_device *spi) +{ + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + + pspim->tx_cur_len = 0; + pspim->rx_cur_len = 0; + pspim->data_unit = SP7021_FIFO_DATA_LEN; +} + +// preliminary set CS, CPOL, CPHA and LSB +static int sp7021_spi_controller_prepare_message(struct spi_controller *ctlr, + struct spi_message *msg) +{ + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + struct spi_device *s = msg->spi; + u32 valus, rs = 0; + + valus = readl(pspim->m_base + SP7021_SPI_STATUS_REG); + valus |= SP7021_FD_SW_RST; + writel(valus, pspim->m_base + SP7021_SPI_STATUS_REG); + rs |= SP7021_FD_SEL; + if (s->mode & SPI_CPOL) + rs |= SP7021_CPOL_FD; + + if (s->mode & SPI_LSB_FIRST) + rs |= SP7021_LSB_SEL; + + if (s->mode & SPI_CS_HIGH) + rs |= SP7021_CS_POR; + + if (s->mode & SPI_CPHA) + rs |= SP7021_CPHA_R; + else + rs |= SP7021_CPHA_W; + + rs |= FIELD_PREP(SP7021_TX_UNIT, 0) | FIELD_PREP(SP7021_RX_UNIT, 0); + pspim->xfer_conf = rs; + if (pspim->xfer_conf & SP7021_CPOL_FD) + writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG); + + return 0; +} + +static void sp7021_spi_setup_clk(struct spi_controller *ctlr, struct spi_transfer *xfer) +{ + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + u32 clk_rate, clk_sel, div; + + clk_rate = clk_get_rate(pspim->spi_clk); + div = clk_rate / xfer->speed_hz; + if (div < 2) + div = 2; + clk_sel = (div / 2) - 1; + pspim->xfer_conf &= SP7021_CLK_MASK; + pspim->xfer_conf |= FIELD_PREP(SP7021_CLK_MASK, clk_sel); + writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG); +} + +static int sp7021_spi_master_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + unsigned long timeout = msecs_to_jiffies(1000); + unsigned int xfer_cnt, xfer_len, last_len; + unsigned int i, len_temp; + u32 reg_temp; + int ret; + + xfer_cnt = xfer->len / SP7021_SPI_DATA_SIZE; + last_len = xfer->len % SP7021_SPI_DATA_SIZE; + + for (i = 0; i <= xfer_cnt; i++) { + mutex_lock(&pspim->buf_lock); + sp7021_prep_transfer(ctlr, spi); + sp7021_spi_setup_clk(ctlr, xfer); + reinit_completion(&pspim->isr_done); + + if (i == xfer_cnt) + xfer_len = last_len; + else + xfer_len = SP7021_SPI_DATA_SIZE; + + pspim->tx_buf = xfer->tx_buf + i * SP7021_SPI_DATA_SIZE; + pspim->rx_buf = xfer->rx_buf + i * SP7021_SPI_DATA_SIZE; + + if (pspim->tx_cur_len < xfer_len) { + len_temp = min(pspim->data_unit, xfer_len); + sp7021_spi_master_wb(pspim, len_temp); + } + reg_temp = readl(pspim->m_base + SP7021_SPI_CONFIG_REG); + reg_temp &= ~SP7021_CLEAN_RW_BYTE; + reg_temp &= ~SP7021_CLEAN_FLUG_MASK; + reg_temp |= SP7021_FD_SEL | SP7021_FINISH_FLAG_MASK | + SP7021_TX_EMP_FLAG_MASK | SP7021_RX_FULL_FLAG_MASK | + FIELD_PREP(SP7021_TX_UNIT, 0) | FIELD_PREP(SP7021_RX_UNIT, 0); + writel(reg_temp, pspim->m_base + SP7021_SPI_CONFIG_REG); + + reg_temp = FIELD_PREP(SP7021_SET_TX_LEN, xfer_len) | + FIELD_PREP(SP7021_SET_XFER_LEN, xfer_len) | + SP7021_SPI_START_FD; + writel(reg_temp, pspim->m_base + SP7021_SPI_STATUS_REG); + + if (!wait_for_completion_interruptible_timeout(&pspim->isr_done, timeout)) { + dev_err(&spi->dev, "wait_for_completion err\n"); + return -ETIMEDOUT; + } + + reg_temp = readl(pspim->m_base + SP7021_SPI_STATUS_REG); + if (reg_temp & SP7021_FINISH_FLAG) { + writel(SP7021_FINISH_FLAG, pspim->m_base + SP7021_SPI_STATUS_REG); + writel(readl(pspim->m_base + SP7021_SPI_CONFIG_REG) & + SP7021_CLEAN_FLUG_MASK, pspim->m_base + SP7021_SPI_CONFIG_REG); + } + + if (pspim->xfer_conf & SP7021_CPOL_FD) + writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG); + + mutex_unlock(&pspim->buf_lock); + ret = 0; + } + return ret; +} + +static int sp7021_spi_slave_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + struct device *dev = pspim->dev; + int mode, ret = 0; + + mode = SP7021_SPI_IDLE; + if (xfer->tx_buf && xfer->rx_buf) { + dev_dbg(&ctlr->dev, "%s() wrong command\n", __func__); + ret = -EINVAL; + } else if (xfer->tx_buf) { + xfer->tx_dma = dma_map_single(dev, (void *)xfer->tx_buf, + xfer->len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, xfer->tx_dma)) + return -ENOMEM; + mode = SP7021_SLAVE_WRITE; + } else if (xfer->rx_buf) { + xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, xfer->rx_dma)) + return -ENOMEM; + mode = SP7021_SLAVE_READ; + } + + switch (mode) { + case SP7021_SLAVE_WRITE: + ret = sp7021_spi_slave_tx(spi, xfer); + break; + case SP7021_SLAVE_READ: + ret = sp7021_spi_slave_rx(spi, xfer); + break; + default: + break; + } + if (xfer->tx_buf) + dma_unmap_single(dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE); + if (xfer->rx_buf) + dma_unmap_single(dev, xfer->rx_dma, xfer->len, DMA_FROM_DEVICE); + + spi_finalize_current_transfer(ctlr); + return ret; +} + +static void sp7021_spi_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static void sp7021_spi_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + +static int sp7021_spi_controller_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sp7021_spi_ctlr *pspim; + struct spi_controller *ctlr; + int mode, ret; + + pdev->id = of_alias_get_id(pdev->dev.of_node, "sp_spi"); + + if (device_property_read_bool(dev, "spi-slave")) + mode = SP7021_SLAVE_MODE; + else + mode = SP7021_MASTER_MODE; + + if (mode == SP7021_SLAVE_MODE) + ctlr = devm_spi_alloc_slave(dev, sizeof(*pspim)); + else + ctlr = devm_spi_alloc_master(dev, sizeof(*pspim)); + if (!ctlr) + return -ENOMEM; + device_set_node(&ctlr->dev, pdev->dev.fwnode); + ctlr->bus_num = pdev->id; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + ctlr->auto_runtime_pm = true; + ctlr->prepare_message = sp7021_spi_controller_prepare_message; + if (mode == SP7021_SLAVE_MODE) { + ctlr->transfer_one = sp7021_spi_slave_transfer_one; + ctlr->slave_abort = sp7021_spi_slave_abort; + ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX; + } else { + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->min_speed_hz = 40000; + ctlr->max_speed_hz = 25000000; + ctlr->use_gpio_descriptors = true; + ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + ctlr->transfer_one = sp7021_spi_master_transfer_one; + } + platform_set_drvdata(pdev, ctlr); + pspim = spi_controller_get_devdata(ctlr); + pspim->mode = mode; + pspim->ctlr = ctlr; + pspim->dev = dev; + spin_lock_init(&pspim->lock); + mutex_init(&pspim->buf_lock); + init_completion(&pspim->isr_done); + init_completion(&pspim->slave_isr); + + pspim->m_base = devm_platform_ioremap_resource_byname(pdev, "master"); + if (IS_ERR(pspim->m_base)) + return dev_err_probe(dev, PTR_ERR(pspim->m_base), "m_base get fail\n"); + + pspim->s_base = devm_platform_ioremap_resource_byname(pdev, "slave"); + if (IS_ERR(pspim->s_base)) + return dev_err_probe(dev, PTR_ERR(pspim->s_base), "s_base get fail\n"); + + pspim->m_irq = platform_get_irq_byname(pdev, "master_risc"); + if (pspim->m_irq < 0) + return pspim->m_irq; + + pspim->s_irq = platform_get_irq_byname(pdev, "slave_risc"); + if (pspim->s_irq < 0) + return pspim->s_irq; + + ret = devm_request_irq(dev, pspim->m_irq, sp7021_spi_master_irq, + IRQF_TRIGGER_RISING, pdev->name, pspim); + if (ret) + return ret; + + ret = devm_request_irq(dev, pspim->s_irq, sp7021_spi_slave_irq, + IRQF_TRIGGER_RISING, pdev->name, pspim); + if (ret) + return ret; + + pspim->spi_clk = devm_clk_get(dev, NULL); + if (IS_ERR(pspim->spi_clk)) + return dev_err_probe(dev, PTR_ERR(pspim->spi_clk), "clk get fail\n"); + + pspim->rstc = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(pspim->rstc)) + return dev_err_probe(dev, PTR_ERR(pspim->rstc), "rst get fail\n"); + + ret = clk_prepare_enable(pspim->spi_clk); + if (ret) + return dev_err_probe(dev, ret, "failed to enable clk\n"); + + ret = devm_add_action_or_reset(dev, sp7021_spi_disable_unprepare, pspim->spi_clk); + if (ret) + return ret; + + ret = reset_control_deassert(pspim->rstc); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert reset\n"); + + ret = devm_add_action_or_reset(dev, sp7021_spi_reset_control_assert, pspim->rstc); + if (ret) + return ret; + + pm_runtime_enable(dev); + ret = spi_register_controller(ctlr); + if (ret) { + pm_runtime_disable(dev); + return dev_err_probe(dev, ret, "spi_register_master fail\n"); + } + return 0; +} + +static int sp7021_spi_controller_remove(struct platform_device *pdev) +{ + struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev); + + spi_unregister_controller(ctlr); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + return 0; +} + +static int __maybe_unused sp7021_spi_controller_suspend(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + + return reset_control_assert(pspim->rstc); +} + +static int __maybe_unused sp7021_spi_controller_resume(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + + reset_control_deassert(pspim->rstc); + return clk_prepare_enable(pspim->spi_clk); +} + +static int sp7021_spi_runtime_suspend(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + + return reset_control_assert(pspim->rstc); +} + +static int sp7021_spi_runtime_resume(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); + + return reset_control_deassert(pspim->rstc); +} + +static const struct dev_pm_ops sp7021_spi_pm_ops = { + SET_RUNTIME_PM_OPS(sp7021_spi_runtime_suspend, + sp7021_spi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(sp7021_spi_controller_suspend, + sp7021_spi_controller_resume) +}; + +static const struct of_device_id sp7021_spi_controller_ids[] = { + { .compatible = "sunplus,sp7021-spi" }, + {} +}; +MODULE_DEVICE_TABLE(of, sp7021_spi_controller_ids); + +static struct platform_driver sp7021_spi_controller_driver = { + .probe = sp7021_spi_controller_probe, + .remove = sp7021_spi_controller_remove, + .driver = { + .name = "sunplus,sp7021-spi-controller", + .of_match_table = sp7021_spi_controller_ids, + .pm = &sp7021_spi_pm_ops, + }, +}; +module_platform_driver(sp7021_spi_controller_driver); + +MODULE_AUTHOR("Li-hao Kuo "); +MODULE_DESCRIPTION("Sunplus SPI controller driver"); +MODULE_LICENSE("GPL"); -- cgit From 474fc2e6395d62758e80b9ea65f61339296355fc Mon Sep 17 00:00:00 2001 From: Guochun Mao Date: Wed, 26 Jan 2022 17:11:59 +0800 Subject: spi: spi-mtk-nor: make some internal variables static Variables mtk_nor_caps_mt8173, mtk_nor_caps_mt8186 and mtk_nor_caps_mt8192 are not declared. Make them static. Fixes: 5b177234e9fd ("spi: spi-mtk-nor: improve device table for adding more capabilities") Signed-off-by: Guochun Mao Reported-by: kernel test robot Link: https://lore.kernel.org/r/20220126091159.27513-1-guochun.mao@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 455b4dcb26e9..94fb09696677 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -770,17 +770,17 @@ static const struct spi_controller_mem_ops mtk_nor_mem_ops = { .exec_op = mtk_nor_exec_op }; -const struct mtk_nor_caps mtk_nor_caps_mt8173 = { +static const struct mtk_nor_caps mtk_nor_caps_mt8173 = { .dma_bits = 32, .extra_dummy_bit = 0, }; -const struct mtk_nor_caps mtk_nor_caps_mt8186 = { +static const struct mtk_nor_caps mtk_nor_caps_mt8186 = { .dma_bits = 32, .extra_dummy_bit = 1, }; -const struct mtk_nor_caps mtk_nor_caps_mt8192 = { +static const struct mtk_nor_caps mtk_nor_caps_mt8192 = { .dma_bits = 36, .extra_dummy_bit = 0, }; -- cgit From 20dc69ca1023b7e4c4af3c3495aa5a91e1a9be39 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 27 Jan 2022 19:58:15 +0800 Subject: spi: Fix missing unlock on error in sp7021_spi_master_transfer_one() Add the missing unlock before return from sp7021_spi_master_transfer_one() in the error handling case. Fixes: f62ca4e2a863 ("spi: Add spi driver for Sunplus SP7021") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20220127115815.3148950-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-sunplus-sp7021.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c index 627b9c3024e9..cbbb1664017e 100644 --- a/drivers/spi/spi-sunplus-sp7021.c +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -351,6 +351,7 @@ static int sp7021_spi_master_transfer_one(struct spi_controller *ctlr, struct sp if (!wait_for_completion_interruptible_timeout(&pspim->isr_done, timeout)) { dev_err(&spi->dev, "wait_for_completion err\n"); + mutex_unlock(&pspim->buf_lock); return -ETIMEDOUT; } -- cgit From 31455bbda2081af83f72bb4636348b12b82c37c1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 25 Jan 2022 01:58:36 +0100 Subject: spi: pxa2xx_spi: Convert to use GPIO descriptors This converts the PXA2xx SPI driver to use GPIO descriptors exclusively to retrieve GPIO chip select lines. The device tree and ACPI paths of the driver already use descriptors, hence ->use_gpio_descriptors is already set and this codepath is well tested. Convert all the PXA boards providing chip select GPIOs as platform data and drop the old GPIO chipselect handling in favor of the core managing it exclusively. Cc: Marek Vasut Cc: Daniel Mack Cc: Haojian Zhuang Cc: Robert Jarzmik Cc: linux-arm-kernel@lists.infradead.org Acked-by: Jonathan Cameron Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220125005836.494807-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 63 +----------------------------------------------- 1 file changed, 1 insertion(+), 62 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e88f86274eeb..abb9f0ffd377 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -1163,57 +1162,6 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_controller *controller) return 0; } -static void cleanup_cs(struct spi_device *spi) -{ - if (!gpio_is_valid(spi->cs_gpio)) - return; - - gpio_free(spi->cs_gpio); - spi->cs_gpio = -ENOENT; -} - -static int setup_cs(struct spi_device *spi, struct chip_data *chip, - struct pxa2xx_spi_chip *chip_info) -{ - struct driver_data *drv_data = spi_controller_get_devdata(spi->controller); - - if (chip == NULL) - return 0; - - if (chip_info == NULL) - return 0; - - if (drv_data->ssp_type == CE4100_SSP) - return 0; - - /* - * NOTE: setup() can be called multiple times, possibly with - * different chip_info, release previously requested GPIO. - */ - cleanup_cs(spi); - - if (gpio_is_valid(chip_info->gpio_cs)) { - int gpio = chip_info->gpio_cs; - int err; - - err = gpio_request(gpio, "SPI_CS"); - if (err) { - dev_err(&spi->dev, "failed to request chip select GPIO%d\n", gpio); - return err; - } - - err = gpio_direction_output(gpio, !(spi->mode & SPI_CS_HIGH)); - if (err) { - gpio_free(gpio); - return err; - } - - spi->cs_gpio = gpio; - } - - return 0; -} - static int setup(struct spi_device *spi) { struct pxa2xx_spi_chip *chip_info; @@ -1222,7 +1170,6 @@ static int setup(struct spi_device *spi) struct driver_data *drv_data = spi_controller_get_devdata(spi->controller); uint tx_thres, tx_hi_thres, rx_thres; - int err; switch (drv_data->ssp_type) { case QUARK_X1000_SSP: @@ -1365,21 +1312,13 @@ static int setup(struct spi_device *spi) spi_set_ctldata(spi, chip); - if (drv_data->ssp_type == CE4100_SSP) - return 0; - - err = setup_cs(spi, chip, chip_info); - if (err) - kfree(chip); - - return err; + return 0; } static void cleanup(struct spi_device *spi) { struct chip_data *chip = spi_get_ctldata(spi); - cleanup_cs(spi); kfree(chip); } -- cgit From 1a5a87d541b442293dfcc2253b652bc7b7e02d09 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 22 Jan 2022 01:33:02 +0100 Subject: spi: mt65xx: Convert to GPIO descriptors The MT65xx driver was already relying on the core to get some GPIO line numbers so it can be (hopefully) trivially converted to use descriptors instead. Cc: Dafna Hirschfeld Cc: Mason Zhang Cc: Guenter Roeck Cc: Peter Hess Cc: Leilk Liu Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220122003302.374304-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index a15de10ee286..4f49b2e93ca7 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -605,8 +605,9 @@ static int mtk_spi_setup(struct spi_device *spi) if (!spi->controller_data) spi->controller_data = (void *)&mtk_default_chip_info; - if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio)) - gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); + if (mdata->dev_comp->need_pad_sel && spi->cs_gpiod) + /* CS de-asserted, gpiolib will handle inversion */ + gpiod_direction_output(spi->cs_gpiod, 0); return 0; } @@ -730,6 +731,7 @@ static int mtk_spi_probe(struct platform_device *pdev) master->can_dma = mtk_spi_can_dma; master->setup = mtk_spi_setup; master->set_cs_timing = mtk_spi_set_hw_cs_timing; + master->use_gpio_descriptors = true; of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); if (!of_id) { @@ -853,25 +855,12 @@ static int mtk_spi_probe(struct platform_device *pdev) goto err_disable_runtime_pm; } - if (!master->cs_gpios && master->num_chipselect > 1) { + if (!master->cs_gpiods && master->num_chipselect > 1) { dev_err(&pdev->dev, "cs_gpios not specified and num_chipselect > 1\n"); ret = -EINVAL; goto err_disable_runtime_pm; } - - if (master->cs_gpios) { - for (i = 0; i < master->num_chipselect; i++) { - ret = devm_gpio_request(&pdev->dev, - master->cs_gpios[i], - dev_name(&pdev->dev)); - if (ret) { - dev_err(&pdev->dev, - "can't get CS GPIO %i\n", i); - goto err_disable_runtime_pm; - } - } - } } if (mdata->dev_comp->dma_ext) -- cgit From 2818824ced4be5abc22c450340d548702f166d9a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 20 Jan 2022 01:26:00 +0100 Subject: spi: mpc512x-psc: Convert to use GPIO descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver is already relying on the core to provide valid GPIO numbers in spi->cs_gpio through of_spi_get_gpio_numbers(), so we can switch to letting the core use GPIO descriptors instead. The driver was assigning a local function to the custom chipselect callback, but I chose to just open code the gpiod setting instead, this is easier to read. The only platform that overrides the cs_control callback is the mpc832x_rdb. Cc: Uwe Kleine-König Cc: Anatolij Gustschin Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220120002600.216667-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-mpc512x-psc.c | 46 +++++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 78a9bca8cc68..8a488d8e4c1b 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include enum { @@ -128,17 +127,28 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) out_be32(psc_addr(mps, ccr), ccr); mps->bits_per_word = cs->bits_per_word; - if (mps->cs_control && gpio_is_valid(spi->cs_gpio)) - mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0); + if (cs->gpiod) { + if (mps->cs_control) + /* boardfile override */ + mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0); + else + /* gpiolib will deal with the inversion */ + gpiod_set_value(spi->cs_gpiod, 1); + } } static void mpc512x_psc_spi_deactivate_cs(struct spi_device *spi) { struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master); - if (mps->cs_control && gpio_is_valid(spi->cs_gpio)) - mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1); - + if (spi->cs_gpiod) { + if (mps->cs_control) + /* boardfile override */ + mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1); + else + /* gpiolib will deal with the inversion */ + gpiod_set_value(spi->cs_gpiod, 0); + } } /* extract and scale size field in txsz or rxsz */ @@ -373,18 +383,6 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi) if (!cs) return -ENOMEM; - if (gpio_is_valid(spi->cs_gpio)) { - ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); - if (ret) { - dev_err(&spi->dev, "can't get CS gpio: %d\n", - ret); - kfree(cs); - return ret; - } - gpio_direction_output(spi->cs_gpio, - spi->mode & SPI_CS_HIGH ? 0 : 1); - } - spi->controller_state = cs; } @@ -396,8 +394,6 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi) static void mpc512x_psc_spi_cleanup(struct spi_device *spi) { - if (gpio_is_valid(spi->cs_gpio)) - gpio_free(spi->cs_gpio); kfree(spi->controller_state); } @@ -476,11 +472,6 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id) return IRQ_NONE; } -static void mpc512x_spi_cs_control(struct spi_device *spi, bool onoff) -{ - gpio_set_value(spi->cs_gpio, onoff); -} - static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, u32 size, unsigned int irq) { @@ -500,9 +491,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, mps->type = (int)of_device_get_match_data(dev); mps->irq = irq; - if (pdata == NULL) { - mps->cs_control = mpc512x_spi_cs_control; - } else { + if (pdata) { mps->cs_control = pdata->cs_control; master->bus_num = pdata->bus_num; master->num_chipselect = pdata->max_chipselect; @@ -513,6 +502,7 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, master->prepare_transfer_hardware = mpc512x_psc_spi_prep_xfer_hw; master->transfer_one_message = mpc512x_psc_spi_msg_xfer; master->unprepare_transfer_hardware = mpc512x_psc_spi_unprep_xfer_hw; + master->use_gpio_descriptors = true; master->cleanup = mpc512x_psc_spi_cleanup; master->dev.of_node = dev->of_node; -- cgit From 99407f11b5657cd66625c6b55a73d38b67803a8c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 22 Jan 2022 01:48:46 +0100 Subject: spi: pic32: Convert to use GPIO descriptors The driver already relies on the core looking up GPIO lines from the core, so this is trivial to switch over to using GPIO descriptors. Cc: Purna Chandra Mandal Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220122004846.374930-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-pic32.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index f86433b29260..7e5c09a7d489 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -591,18 +591,16 @@ static int pic32_spi_setup(struct spi_device *spi) * unreliable/erroneous SPI transactions. * To avoid that we will always handle /CS by toggling GPIO. */ - if (!gpio_is_valid(spi->cs_gpio)) + if (!spi->cs_gpiod) return -EINVAL; - gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); - return 0; } static void pic32_spi_cleanup(struct spi_device *spi) { - /* de-activate cs-gpio */ - gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); + /* de-activate cs-gpio, gpiolib will handle inversion */ + gpiod_direction_output(spi->cs_gpiod, 0); } static int pic32_spi_dma_prep(struct pic32_spi *pic32s, struct device *dev) @@ -784,6 +782,7 @@ static int pic32_spi_probe(struct platform_device *pdev) master->unprepare_message = pic32_spi_unprepare_message; master->prepare_transfer_hardware = pic32_spi_prepare_hardware; master->unprepare_transfer_hardware = pic32_spi_unprepare_hardware; + master->use_gpio_descriptors = true; /* optional DMA support */ ret = pic32_spi_dma_prep(pic32s, &pdev->dev); -- cgit From 6938e02f8658734209fc1f68dc3a6cd355f4f737 Mon Sep 17 00:00:00 2001 From: 郭力豪 Date: Fri, 28 Jan 2022 22:47:41 +0800 Subject: spi: sp7201: Fix compiler warnings Fix compiler warming for kernel test Fixes: f62ca4e2a863 ("spi: Add spi driver for Sunplus SP7021") Signed-off-by: Li-hao Kuo Link: https://lore.kernel.org/r/CAGcXWkzM6wbhNFLbYoijq7iS_76nYVod1ySFEDu-BRgnBokEQA@mail.gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sunplus-sp7021.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c index cbbb1664017e..e5bdeb3eba45 100644 --- a/drivers/spi/spi-sunplus-sp7021.c +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -124,7 +124,7 @@ static int sp7021_spi_slave_abort(struct spi_controller *ctlr) return 0; } -int sp7021_spi_slave_tx(struct spi_device *spi, struct spi_transfer *xfer) +static int sp7021_spi_slave_tx(struct spi_device *spi, struct spi_transfer *xfer) { struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller); @@ -142,7 +142,7 @@ int sp7021_spi_slave_tx(struct spi_device *spi, struct spi_transfer *xfer) return 0; } -int sp7021_spi_slave_rx(struct spi_device *spi, struct spi_transfer *xfer) +static int sp7021_spi_slave_rx(struct spi_device *spi, struct spi_transfer *xfer) { struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller); int ret = 0; @@ -160,7 +160,7 @@ int sp7021_spi_slave_rx(struct spi_device *spi, struct spi_transfer *xfer) return ret; } -void sp7021_spi_master_rb(struct sp7021_spi_ctlr *pspim, unsigned int len) +static void sp7021_spi_master_rb(struct sp7021_spi_ctlr *pspim, unsigned int len) { int i; @@ -171,7 +171,7 @@ void sp7021_spi_master_rb(struct sp7021_spi_ctlr *pspim, unsigned int len) } } -void sp7021_spi_master_wb(struct sp7021_spi_ctlr *pspim, unsigned int len) +static void sp7021_spi_master_wb(struct sp7021_spi_ctlr *pspim, unsigned int len) { int i; @@ -558,6 +558,7 @@ static int __maybe_unused sp7021_spi_controller_resume(struct device *dev) return clk_prepare_enable(pspim->spi_clk); } +#ifdef CONFIG_PM static int sp7021_spi_runtime_suspend(struct device *dev) { struct spi_controller *ctlr = dev_get_drvdata(dev); @@ -573,6 +574,7 @@ static int sp7021_spi_runtime_resume(struct device *dev) return reset_control_deassert(pspim->rstc); } +#endif static const struct dev_pm_ops sp7021_spi_pm_ops = { SET_RUNTIME_PM_OPS(sp7021_spi_runtime_suspend, -- cgit From 321599693213c81b2ce8530abb27e39528e969f9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 1 Feb 2022 02:26:54 +0100 Subject: spi: st-ssc4: Covert to use GPIO descriptors This switches the ST SSC SPI controller to use GPIO descriptors from the core instead of GPIO numbers. It is already using the core parsing of GPIO numbers so the switch is pretty straight-forward. Cc: Lee Jones Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220201012654.562578-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-st-ssc4.c | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index 6c44dda9ee8c..843be803696b 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -171,11 +170,6 @@ static int spi_st_transfer_one(struct spi_master *master, return t->len; } -static void spi_st_cleanup(struct spi_device *spi) -{ - gpio_free(spi->cs_gpio); -} - /* the spi->mode bits understood by this driver: */ #define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH) static int spi_st_setup(struct spi_device *spi) @@ -183,29 +177,17 @@ static int spi_st_setup(struct spi_device *spi) struct spi_st *spi_st = spi_master_get_devdata(spi->master); u32 spi_st_clk, sscbrg, var; u32 hz = spi->max_speed_hz; - int cs = spi->cs_gpio; - int ret; if (!hz) { dev_err(&spi->dev, "max_speed_hz unspecified\n"); return -EINVAL; } - if (!gpio_is_valid(cs)) { - dev_err(&spi->dev, "%d is not a valid gpio\n", cs); + if (!spi->cs_gpiod) { + dev_err(&spi->dev, "no valid gpio assigned\n"); return -EINVAL; } - ret = gpio_request(cs, dev_name(&spi->dev)); - if (ret) { - dev_err(&spi->dev, "could not request gpio:%d\n", cs); - return ret; - } - - ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); - if (ret) - goto out_free_gpio; - spi_st_clk = clk_get_rate(spi_st->clk); /* Set SSC_BRF */ @@ -213,8 +195,7 @@ static int spi_st_setup(struct spi_device *spi) if (sscbrg < 0x07 || sscbrg > BIT(16)) { dev_err(&spi->dev, "baudrate %d outside valid range %d\n", sscbrg, hz); - ret = -EINVAL; - goto out_free_gpio; + return -EINVAL; } spi_st->baud = spi_st_clk / (2 * sscbrg); @@ -263,10 +244,6 @@ static int spi_st_setup(struct spi_device *spi) readl_relaxed(spi_st->base + SSC_RBUF); return 0; - -out_free_gpio: - gpio_free(cs); - return ret; } /* Interrupt fired when TX shift register becomes empty */ @@ -309,11 +286,11 @@ static int spi_st_probe(struct platform_device *pdev) master->dev.of_node = np; master->mode_bits = MODEBITS; master->setup = spi_st_setup; - master->cleanup = spi_st_cleanup; master->transfer_one = spi_st_transfer_one; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->auto_runtime_pm = true; master->bus_num = pdev->id; + master->use_gpio_descriptors = true; spi_st = spi_master_get_devdata(master); spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); -- cgit From b651d1da86aa525c5a5b2bd61f528353c28d589d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 1 Feb 2022 02:29:56 +0100 Subject: spi: bcm2835aux: Convert to use GPIO descriptors This one is pretty straight forward to switch over, the driver already relies on inspecting cs_gpio just check cs_gpiod instead and stop the special handling of requesting the GPIO and stuff the core will take care of. Cc: Lukas Wunner Cc: Martin Sperl Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220201012956.563272-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835aux.c | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index 7d709a8c833b..e28521922330 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -445,25 +444,12 @@ static void bcm2835aux_spi_handle_err(struct spi_master *master, static int bcm2835aux_spi_setup(struct spi_device *spi) { - int ret; - /* sanity check for native cs */ if (spi->mode & SPI_NO_CS) return 0; - if (gpio_is_valid(spi->cs_gpio)) { - /* with gpio-cs set the GPIO to the correct level - * and as output (in case the dt has the gpio not configured - * as output but native cs) - */ - ret = gpio_direction_output(spi->cs_gpio, - (spi->mode & SPI_CS_HIGH) ? 0 : 1); - if (ret) - dev_err(&spi->dev, - "could not set gpio %i as output: %i\n", - spi->cs_gpio, ret); - - return ret; - } + + if (spi->cs_gpiod) + return 0; /* for dt-backwards compatibility: only support native on CS0 * known things not supported with broken native CS: @@ -519,6 +505,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) master->prepare_message = bcm2835aux_spi_prepare_message; master->unprepare_message = bcm2835aux_spi_unprepare_message; master->dev.of_node = pdev->dev.of_node; + master->use_gpio_descriptors = true; bs = spi_master_get_devdata(master); -- cgit From e3dc1399506f894110667ee5c66a6a70f06f3348 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:23 +0000 Subject: spi: Make spi_alloc_device and spi_add_device public again This functions were previously made private since they were not used. However, these functions will be needed again. Partial revert of commit da21fde0fdb3 ("spi: Make several public functions private to spi.c") Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-2-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4599b121d744..1eb84101c4ad 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -532,7 +532,7 @@ static DEFINE_MUTEX(board_lock); * * Return: a pointer to the new device, or NULL. */ -static struct spi_device *spi_alloc_device(struct spi_controller *ctlr) +struct spi_device *spi_alloc_device(struct spi_controller *ctlr) { struct spi_device *spi; @@ -557,6 +557,7 @@ static struct spi_device *spi_alloc_device(struct spi_controller *ctlr) device_initialize(&spi->dev); return spi; } +EXPORT_SYMBOL_GPL(spi_alloc_device); static void spi_dev_set_name(struct spi_device *spi) { @@ -652,7 +653,7 @@ static int __spi_add_device(struct spi_device *spi) * * Return: 0 on success; negative errno on failure */ -static int spi_add_device(struct spi_device *spi) +int spi_add_device(struct spi_device *spi) { struct spi_controller *ctlr = spi->controller; struct device *dev = ctlr->dev.parent; @@ -673,6 +674,7 @@ static int spi_add_device(struct spi_device *spi) mutex_unlock(&ctlr->add_lock); return status; } +EXPORT_SYMBOL_GPL(spi_add_device); static int spi_add_device_locked(struct spi_device *spi) { -- cgit From 000bee0ed70af79e610444096fb453430220960f Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:24 +0000 Subject: spi: Create helper API to lookup ACPI info for spi device This can then be used to find a spi resource inside an ACPI node, and allocate a spi device. Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-3-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1eb84101c4ad..13f4701f0694 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2410,8 +2410,18 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) return 1; } -static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, - struct acpi_device *adev) +/** + * acpi_spi_device_alloc - Allocate a spi device, and fill it in with ACPI information + * @ctlr: controller to which the spi device belongs + * @adev: ACPI Device for the spi device + * + * This should be used to allocate a new spi device from and ACPI Node. + * The caller is responsible for calling spi_add_device to register the spi device. + * + * Return: a pointer to the new device, or ERR_PTR on error. + */ +struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, + struct acpi_device *adev) { acpi_handle parent_handle = NULL; struct list_head resource_list; @@ -2419,10 +2429,6 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, struct spi_device *spi; int ret; - if (acpi_bus_get_status(adev) || !adev->status.present || - acpi_device_enumerated(adev)) - return AE_OK; - lookup.ctlr = ctlr; lookup.irq = -1; @@ -2433,7 +2439,7 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, if (ret < 0) /* found SPI in _CRS but it points to another controller */ - return AE_OK; + return ERR_PTR(-ENODEV); if (!lookup.max_speed_hz && ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) && @@ -2443,16 +2449,15 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, } if (!lookup.max_speed_hz) - return AE_OK; + return ERR_PTR(-ENODEV); spi = spi_alloc_device(ctlr); if (!spi) { dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", dev_name(&adev->dev)); - return AE_NO_MEMORY; + return ERR_PTR(-ENOMEM); } - ACPI_COMPANION_SET(&spi->dev, adev); spi->max_speed_hz = lookup.max_speed_hz; spi->mode |= lookup.mode; @@ -2460,6 +2465,27 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, spi->bits_per_word = lookup.bits_per_word; spi->chip_select = lookup.chip_select; + return spi; +} +EXPORT_SYMBOL_GPL(acpi_spi_device_alloc); + +static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, + struct acpi_device *adev) +{ + struct spi_device *spi; + + if (acpi_bus_get_status(adev) || !adev->status.present || + acpi_device_enumerated(adev)) + return AE_OK; + + spi = acpi_spi_device_alloc(ctlr, adev); + if (IS_ERR(spi)) { + if (PTR_ERR(spi) == -ENOMEM) + return AE_NO_MEMORY; + else + return AE_OK; + } + acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias, sizeof(spi->modalias)); -- cgit From 87e59b36e5e26122efd55d77adb9fac827987db0 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:25 +0000 Subject: spi: Support selection of the index of the ACPI Spi Resource before alloc If a node contains more than one SPI resource it may be necessary to use an index to select which one you want to allocate a spi device for. Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-4-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 13f4701f0694..06c0a308b38b 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2320,6 +2320,8 @@ struct acpi_spi_lookup { int irq; u8 bits_per_word; u8 chip_select; + int n; + int index; }; static void acpi_spi_parse_apple_properties(struct acpi_device *dev, @@ -2351,6 +2353,8 @@ static void acpi_spi_parse_apple_properties(struct acpi_device *dev, lookup->mode |= SPI_CPHA; } +static struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev); + static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) { struct acpi_spi_lookup *lookup = data; @@ -2364,14 +2368,35 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) sb = &ares->data.spi_serial_bus; if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) { + if (lookup->index != -1 && lookup->n++ != lookup->index) + return 1; + + if (lookup->index == -1 && !ctlr) + return -ENODEV; + status = acpi_get_handle(NULL, sb->resource_source.string_ptr, &parent_handle); - if (ACPI_FAILURE(status) || - ACPI_HANDLE(ctlr->dev.parent) != parent_handle) + if (ACPI_FAILURE(status)) return -ENODEV; + if (ctlr) { + if (ACPI_HANDLE(ctlr->dev.parent) != parent_handle) + return -ENODEV; + } else { + struct acpi_device *adev; + + if (acpi_bus_get_device(parent_handle, &adev)) + return -ENODEV; + + ctlr = acpi_spi_find_controller_by_adev(adev); + if (!ctlr) + return -ENODEV; + + lookup->ctlr = ctlr; + } + /* * ACPI DeviceSelection numbering is handled by the * host controller driver in Windows and can vary @@ -2414,14 +2439,21 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) * acpi_spi_device_alloc - Allocate a spi device, and fill it in with ACPI information * @ctlr: controller to which the spi device belongs * @adev: ACPI Device for the spi device + * @index: Index of the spi resource inside the ACPI Node * * This should be used to allocate a new spi device from and ACPI Node. * The caller is responsible for calling spi_add_device to register the spi device. * + * If ctlr is set to NULL, the Controller for the spi device will be looked up + * using the resource. + * If index is set to -1, index is not used. + * Note: If index is -1, ctlr must be set. + * * Return: a pointer to the new device, or ERR_PTR on error. */ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, - struct acpi_device *adev) + struct acpi_device *adev, + int index) { acpi_handle parent_handle = NULL; struct list_head resource_list; @@ -2429,8 +2461,13 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, struct spi_device *spi; int ret; + if (!ctlr && index == -1) + return ERR_PTR(-EINVAL); + lookup.ctlr = ctlr; lookup.irq = -1; + lookup.index = index; + lookup.n = 0; INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, @@ -2443,7 +2480,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, if (!lookup.max_speed_hz && ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) && - ACPI_HANDLE(ctlr->dev.parent) == parent_handle) { + ACPI_HANDLE(lookup.ctlr->dev.parent) == parent_handle) { /* Apple does not use _CRS but nested devices for SPI slaves */ acpi_spi_parse_apple_properties(adev, &lookup); } @@ -2451,9 +2488,9 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, if (!lookup.max_speed_hz) return ERR_PTR(-ENODEV); - spi = spi_alloc_device(ctlr); + spi = spi_alloc_device(lookup.ctlr); if (!spi) { - dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", + dev_err(&lookup.ctlr->dev, "failed to allocate SPI device for %s\n", dev_name(&adev->dev)); return ERR_PTR(-ENOMEM); } @@ -2478,7 +2515,7 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, acpi_device_enumerated(adev)) return AE_OK; - spi = acpi_spi_device_alloc(ctlr, adev); + spi = acpi_spi_device_alloc(ctlr, adev, -1); if (IS_ERR(spi)) { if (PTR_ERR(spi) == -ENOMEM) return AE_NO_MEMORY; -- cgit From e612af7acef2459f1afd885f4107748995a05963 Mon Sep 17 00:00:00 2001 From: Stefan Binding Date: Fri, 21 Jan 2022 17:24:26 +0000 Subject: spi: Add API to count spi acpi resources Some ACPI nodes may have more than one Spi Resource. To be able to handle these case, its necessary to have a way of counting these resources. Signed-off-by: Stefan Binding Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20220121172431.6876-5-sbinding@opensource.cirrus.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 06c0a308b38b..ec9f2ed579e3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2324,6 +2324,46 @@ struct acpi_spi_lookup { int index; }; +static int acpi_spi_count(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_spi_serialbus *sb; + int *count = data; + + if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) + return 1; + + sb = &ares->data.spi_serial_bus; + if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_SPI) + return 1; + + *count = *count + 1; + + return 1; +} + +/** + * acpi_spi_count_resources - Count the number of SpiSerialBus resources + * @adev: ACPI device + * + * Returns the number of SpiSerialBus resources in the ACPI-device's + * resource-list; or a negative error code. + */ +int acpi_spi_count_resources(struct acpi_device *adev) +{ + LIST_HEAD(r); + int count = 0; + int ret; + + ret = acpi_dev_get_resources(adev, &r, acpi_spi_count, &count); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&r); + + return count; +} +EXPORT_SYMBOL_GPL(acpi_spi_count_resources); + static void acpi_spi_parse_apple_properties(struct acpi_device *dev, struct acpi_spi_lookup *lookup) { -- cgit From 7030c428fae100c339436f5cb6f9e7c0574097ad Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 1 Feb 2022 21:05:59 +0100 Subject: spi: Replace acpi_bus_get_device() Replace acpi_bus_get_device() that is going to be dropped with acpi_fetch_acpi_dev(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/2231987.ElGaqSPkdT@kreacher Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ec9f2ed579e3..c98c55f44ee6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2585,10 +2585,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, void *data, void **return_value) { + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); struct spi_controller *ctlr = data; - struct acpi_device *adev; - if (acpi_bus_get_device(handle, &adev)) + if (!adev) return AE_OK; return acpi_register_spi_device(ctlr, adev); -- cgit From 8d37f2710f022837635d9f97db3ac8c853e86979 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 2 Feb 2022 00:45:35 +0100 Subject: spi: mpc512x-psc: Fix compile errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My patch created compilation bugs in the MPC512x-PSC driver. Fix them up. Cc: Uwe Kleine-König Cc: Anatolij Gustschin Cc: linuxppc-dev@lists.ozlabs.org Reported-by: kernel test robot Fixes: 2818824ced4b (" spi: mpc512x-psc: Convert to use GPIO descriptors") Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220201234535.569973-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-mpc512x-psc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 8a488d8e4c1b..03630359ce70 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -127,7 +127,7 @@ static void mpc512x_psc_spi_activate_cs(struct spi_device *spi) out_be32(psc_addr(mps, ccr), ccr); mps->bits_per_word = cs->bits_per_word; - if (cs->gpiod) { + if (spi->cs_gpiod) { if (mps->cs_control) /* boardfile override */ mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0); @@ -373,7 +373,6 @@ static int mpc512x_psc_spi_unprep_xfer_hw(struct spi_master *master) static int mpc512x_psc_spi_setup(struct spi_device *spi) { struct mpc512x_psc_spi_cs *cs = spi->controller_state; - int ret; if (spi->bits_per_word % 8) return -EINVAL; -- cgit From 833026ad56f76d1a1035d6511fd5aeed308465fd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Jan 2022 09:52:02 +0300 Subject: spi: spidev: prevent spidev->speed_hz from being zero A zero value for spi->max_speed_hz or spidev->speed_hz does not make sense and trying to set that can lead to divide by zero crashes in a some of the drivers. drivers/spi/spi-s3c64xx.c:874 s3c64xx_spi_setup() error: potential divide by zero bug '/ spi->max_speed_hz'. drivers/spi/spi-fsl-dspi.c:613 hz_to_spi_baud() error: potential divide by zero bug '/ speed_hz'. drivers/spi/spi-xlp.c:146 xlp_spi_setup() error: potential divide by zero bug '/ (spi->max_speed_hz)'. drivers/spi/spi-orion.c:162 orion_spi_baudrate_set() error: potential divide by zero bug '/ speed'. Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20220125065202.GA8807@kili Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index a5cceca8b82b..dd824db63fbe 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -453,22 +453,29 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) dev_dbg(&spi->dev, "%d bits per word\n", tmp); } break; - case SPI_IOC_WR_MAX_SPEED_HZ: + case SPI_IOC_WR_MAX_SPEED_HZ: { + u32 save; + retval = get_user(tmp, (__u32 __user *)arg); - if (retval == 0) { - u32 save = spi->max_speed_hz; + if (retval) + break; + if (tmp == 0) { + retval = -EINVAL; + break; + } - spi->max_speed_hz = tmp; - retval = spi_setup(spi); - if (retval == 0) { - spidev->speed_hz = tmp; - dev_dbg(&spi->dev, "%d Hz (max)\n", - spidev->speed_hz); - } - spi->max_speed_hz = save; + save = spi->max_speed_hz; + + spi->max_speed_hz = tmp; + retval = spi_setup(spi); + if (retval == 0) { + spidev->speed_hz = tmp; + dev_dbg(&spi->dev, "%d Hz (max)\n", spidev->speed_hz); } - break; + spi->max_speed_hz = save; + break; + } default: /* segmented and/or full-duplex I/O request */ /* Check message and copy into scratch area */ -- cgit From 47e8fe57a66f72c5734b981b21557c732b9a5eb6 Mon Sep 17 00:00:00 2001 From: Li-hao Kuo Date: Mon, 7 Feb 2022 09:57:22 +0800 Subject: spi: Modify irq request position and modify parameters - Change irq request position to the back. - Add temporary varilable and setting (as suggested by Mr. Andy Shevchenko) Fixes: f62ca4e2a863 ("spi: Add spi driver for Sunplus SP7021") Signed-off-by: Li-hao Kuo Link: https://lore.kernel.org/r/a94e3b123773fe303221d2bd2e4ce36ffa905a1c.1644198957.git.lhjeff911@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sunplus-sp7021.c | 63 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 32 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c index e5bdeb3eba45..ba5ed9f7277a 100644 --- a/drivers/spi/spi-sunplus-sp7021.c +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -110,7 +110,8 @@ static irqreturn_t sp7021_spi_slave_irq(int irq, void *dev) unsigned int data_status; data_status = readl(pspim->s_base + SP7021_DATA_RDY_REG); - writel(data_status | SP7021_SLAVE_CLR_INT, pspim->s_base + SP7021_DATA_RDY_REG); + data_status |= SP7021_SLAVE_CLR_INT; + writel(data_status , pspim->s_base + SP7021_DATA_RDY_REG); complete(&pspim->slave_isr); return IRQ_HANDLED; } @@ -127,14 +128,16 @@ static int sp7021_spi_slave_abort(struct spi_controller *ctlr) static int sp7021_spi_slave_tx(struct spi_device *spi, struct spi_transfer *xfer) { struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller); + u32 value; reinit_completion(&pspim->slave_isr); - writel(SP7021_SLAVE_DMA_EN | SP7021_SLAVE_DMA_RW | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3), - pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); + value = SP7021_SLAVE_DMA_EN | SP7021_SLAVE_DMA_RW | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3); + writel(value, pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); writel(xfer->len, pspim->s_base + SP7021_SLAVE_DMA_LENGTH_REG); writel(xfer->tx_dma, pspim->s_base + SP7021_SLAVE_DMA_ADDR_REG); - writel(readl(pspim->s_base + SP7021_DATA_RDY_REG) | SP7021_SLAVE_DATA_RDY, - pspim->s_base + SP7021_DATA_RDY_REG); + value = readl(pspim->s_base + SP7021_DATA_RDY_REG); + value |= SP7021_SLAVE_DATA_RDY; + writel(value, pspim->s_base + SP7021_DATA_RDY_REG); if (wait_for_completion_interruptible(&pspim->isr_done)) { dev_err(&spi->dev, "%s() wait_for_completion err\n", __func__); return -EINTR; @@ -145,11 +148,11 @@ static int sp7021_spi_slave_tx(struct spi_device *spi, struct spi_transfer *xfer static int sp7021_spi_slave_rx(struct spi_device *spi, struct spi_transfer *xfer) { struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(spi->controller); - int ret = 0; + u32 value; reinit_completion(&pspim->isr_done); - writel(SP7021_SLAVE_DMA_EN | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3), - pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); + value = SP7021_SLAVE_DMA_EN | FIELD_PREP(SP7021_SLAVE_DMA_CMD, 3); + writel(value, pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); writel(xfer->len, pspim->s_base + SP7021_SLAVE_DMA_LENGTH_REG); writel(xfer->rx_dma, pspim->s_base + SP7021_SLAVE_DMA_ADDR_REG); if (wait_for_completion_interruptible(&pspim->isr_done)) { @@ -157,7 +160,7 @@ static int sp7021_spi_slave_rx(struct spi_device *spi, struct spi_transfer *xfer return -EINTR; } writel(SP7021_SLAVE_SW_RST, pspim->s_base + SP7021_SLAVE_DMA_CTRL_REG); - return ret; + return 0; } static void sp7021_spi_master_rb(struct sp7021_spi_ctlr *pspim, unsigned int len) @@ -188,7 +191,6 @@ static irqreturn_t sp7021_spi_master_irq(int irq, void *dev) unsigned int tx_cnt, total_len; unsigned int tx_len, rx_cnt; unsigned int fd_status; - unsigned long flags; bool isrdone = false; u32 value; @@ -203,7 +205,7 @@ static irqreturn_t sp7021_spi_master_irq(int irq, void *dev) if (tx_len == 0 && total_len == 0) return IRQ_NONE; - spin_lock_irqsave(&pspim->lock, flags); + spin_lock_irq(&pspim->lock); rx_cnt = FIELD_GET(SP7021_RX_CNT_MASK, fd_status); if (fd_status & SP7021_RX_FULL_FLAG) @@ -243,7 +245,7 @@ static irqreturn_t sp7021_spi_master_irq(int irq, void *dev) if (isrdone) complete(&pspim->isr_done); - spin_unlock_irqrestore(&pspim->lock, flags); + spin_unlock_irq(&pspim->lock); return IRQ_HANDLED; } @@ -296,11 +298,10 @@ static void sp7021_spi_setup_clk(struct spi_controller *ctlr, struct spi_transfe u32 clk_rate, clk_sel, div; clk_rate = clk_get_rate(pspim->spi_clk); - div = clk_rate / xfer->speed_hz; - if (div < 2) - div = 2; + div = max(2U, clk_rate / xfer->speed_hz); + clk_sel = (div / 2) - 1; - pspim->xfer_conf &= SP7021_CLK_MASK; + pspim->xfer_conf &= ~SP7021_CLK_MASK; pspim->xfer_conf |= FIELD_PREP(SP7021_CLK_MASK, clk_sel); writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG); } @@ -313,7 +314,6 @@ static int sp7021_spi_master_transfer_one(struct spi_controller *ctlr, struct sp unsigned int xfer_cnt, xfer_len, last_len; unsigned int i, len_temp; u32 reg_temp; - int ret; xfer_cnt = xfer->len / SP7021_SPI_DATA_SIZE; last_len = xfer->len % SP7021_SPI_DATA_SIZE; @@ -366,9 +366,8 @@ static int sp7021_spi_master_transfer_one(struct spi_controller *ctlr, struct sp writel(pspim->xfer_conf, pspim->m_base + SP7021_SPI_CONFIG_REG); mutex_unlock(&pspim->buf_lock); - ret = 0; } - return ret; + return 0; } static int sp7021_spi_slave_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, @@ -376,12 +375,12 @@ static int sp7021_spi_slave_transfer_one(struct spi_controller *ctlr, struct spi { struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); struct device *dev = pspim->dev; - int mode, ret = 0; + int mode, ret; mode = SP7021_SPI_IDLE; if (xfer->tx_buf && xfer->rx_buf) { dev_dbg(&ctlr->dev, "%s() wrong command\n", __func__); - ret = -EINVAL; + return -EINVAL; } else if (xfer->tx_buf) { xfer->tx_dma = dma_map_single(dev, (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); @@ -445,7 +444,7 @@ static int sp7021_spi_controller_probe(struct platform_device *pdev) ctlr = devm_spi_alloc_master(dev, sizeof(*pspim)); if (!ctlr) return -ENOMEM; - device_set_node(&ctlr->dev, pdev->dev.fwnode); + device_set_node(&ctlr->dev, dev_fwnode(dev)); ctlr->bus_num = pdev->id; ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; ctlr->auto_runtime_pm = true; @@ -488,16 +487,6 @@ static int sp7021_spi_controller_probe(struct platform_device *pdev) if (pspim->s_irq < 0) return pspim->s_irq; - ret = devm_request_irq(dev, pspim->m_irq, sp7021_spi_master_irq, - IRQF_TRIGGER_RISING, pdev->name, pspim); - if (ret) - return ret; - - ret = devm_request_irq(dev, pspim->s_irq, sp7021_spi_slave_irq, - IRQF_TRIGGER_RISING, pdev->name, pspim); - if (ret) - return ret; - pspim->spi_clk = devm_clk_get(dev, NULL); if (IS_ERR(pspim->spi_clk)) return dev_err_probe(dev, PTR_ERR(pspim->spi_clk), "clk get fail\n"); @@ -522,6 +511,16 @@ static int sp7021_spi_controller_probe(struct platform_device *pdev) if (ret) return ret; + ret = devm_request_irq(dev, pspim->m_irq, sp7021_spi_master_irq, + IRQF_TRIGGER_RISING, pdev->name, pspim); + if (ret) + return ret; + + ret = devm_request_irq(dev, pspim->s_irq, sp7021_spi_slave_irq, + IRQF_TRIGGER_RISING, pdev->name, pspim); + if (ret) + return ret; + pm_runtime_enable(dev); ret = spi_register_controller(ctlr); if (ret) { -- cgit From d08de0259dfe172caf073b921c6b27ff089605a9 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Sat, 29 Jan 2022 01:04:53 -0300 Subject: spi: ath79: add mem_ops for fast-read Reading from memory is 3x faster than bit-bang read operation. Also, for tl-wr2543nd, the bit-bang read was sporadically returning random data, possibly a HW defect, while fast-read works as expected. Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20220129040453.8476-1-luizluca@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-ath79.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index d1e287d2d9cd..607e7a49fb89 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -133,6 +134,38 @@ static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs, return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS); } +static int ath79_exec_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct ath79_spi *sp = ath79_spidev_to_sp(mem->spi); + + /* Ensures that reading is performed on device connected to hardware cs0 */ + if (mem->spi->chip_select || mem->spi->cs_gpiod) + return -ENOTSUPP; + + /* Only use for fast-read op. */ + if (op->cmd.opcode != 0x0b || op->data.dir != SPI_MEM_DATA_IN || + op->addr.nbytes != 3 || op->dummy.nbytes != 1) + return -ENOTSUPP; + + /* disable GPIO mode */ + ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); + + memcpy_fromio(op->data.buf.in, sp->base + op->addr.val, op->data.nbytes); + + /* enable GPIO mode */ + ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); + + /* restore IOC register */ + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); + + return 0; +} + +static const struct spi_controller_mem_ops ath79_mem_ops = { + .exec_op = ath79_exec_mem_op, +}; + static int ath79_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -154,6 +187,7 @@ static int ath79_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->flags = SPI_MASTER_GPIO_SS; master->num_chipselect = 3; + master->mem_ops = &ath79_mem_ops; sp->bitbang.master = master; sp->bitbang.chipselect = ath79_spi_chipselect; -- cgit From 4f92724d4b92c024e721063f520d66e11ca4b54b Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Fri, 28 Jan 2022 16:52:38 +0000 Subject: spi: tegra114: Add missing IRQ check in tegra_spi_probe This func misses checking for platform_get_irq()'s call and may passes the negative error codes to request_threaded_irq(), which takes unsigned IRQ #, causing it to fail with -EINVAL, overriding an original error code. Stop calling request_threaded_irq() with invalid IRQ #s. Fixes: f333a331adfa ("spi/tegra114: add spi driver") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220128165238.25615-1-linmq006@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index e9de1d958bbd..8f345247a8c3 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1352,6 +1352,10 @@ static int tegra_spi_probe(struct platform_device *pdev) tspi->phys = r->start; spi_irq = platform_get_irq(pdev, 0); + if (spi_irq < 0) { + ret = spi_irq; + goto exit_free_master; + } tspi->irq = spi_irq; tspi->clk = devm_clk_get(&pdev->dev, "spi"); -- cgit From 47c3e06ed95aa9b74932dbc6b23b544f644faf84 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Fri, 28 Jan 2022 16:59:56 +0000 Subject: spi: tegra210-quad: Fix missin IRQ check in tegra_qspi_probe This func misses checking for platform_get_irq()'s call and may passes the negative error codes to request_threaded_irq(), which takes unsigned IRQ #, causing it to fail with -EINVAL, overriding an original error code. Stop calling request_threaded_irq() with invalid IRQ #s. Fixes: 921fc1838fb0 ("spi: tegra210-quad: Add support for Tegra210 QSPI controller") Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20220128165956.27821-1-linmq006@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index ce1bdb4767ea..cb00ac2fc7d8 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -1240,6 +1240,8 @@ static int tegra_qspi_probe(struct platform_device *pdev) tqspi->phys = r->start; qspi_irq = platform_get_irq(pdev, 0); + if (qspi_irq < 0) + return qspi_irq; tqspi->irq = qspi_irq; tqspi->clk = devm_clk_get(&pdev->dev, "qspi"); -- cgit From a0386bba70934d42f586eaf68b21d5eeaffa7bd0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 23 Jan 2022 18:52:01 +0100 Subject: spi: make remove callback a void function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value returned by an spi driver's remove function is mostly ignored. (Only an error message is printed if the value is non-zero that the error is ignored.) So change the prototype of the remove function to return no value. This way driver authors are not tempted to assume that passing an error to the upper layer is a good idea. All drivers are adapted accordingly. There is no intended change of behaviour, all callbacks were prepared to return 0 before. Signed-off-by: Uwe Kleine-König Acked-by: Marc Kleine-Budde Acked-by: Andy Shevchenko Reviewed-by: Geert Uytterhoeven Acked-by: Jérôme Pouiller Acked-by: Miquel Raynal Acked-by: Jonathan Cameron Acked-by: Claudius Heine Acked-by: Stefan Schmidt Acked-by: Alexandre Belloni Acked-by: Ulf Hansson # For MMC Acked-by: Marcus Folkesson Acked-by: Łukasz Stelmach Acked-by: Lee Jones Link: https://lore.kernel.org/r/20220123175201.34839-6-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 6 ++---- drivers/spi/spi-slave-system-control.c | 3 +-- drivers/spi/spi-slave-time.c | 3 +-- drivers/spi/spi-tle62x0.c | 3 +-- drivers/spi/spi.c | 11 ++--------- drivers/spi/spidev.c | 4 +--- 6 files changed, 8 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 37f4443ce9a0..e9d83d65873b 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -854,15 +854,13 @@ static int spi_mem_probe(struct spi_device *spi) return memdrv->probe(mem); } -static int spi_mem_remove(struct spi_device *spi) +static void spi_mem_remove(struct spi_device *spi) { struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); struct spi_mem *mem = spi_get_drvdata(spi); if (memdrv->remove) - return memdrv->remove(mem); - - return 0; + memdrv->remove(mem); } static void spi_mem_shutdown(struct spi_device *spi) diff --git a/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c index 169f3d595f60..d37cfe995a63 100644 --- a/drivers/spi/spi-slave-system-control.c +++ b/drivers/spi/spi-slave-system-control.c @@ -132,13 +132,12 @@ static int spi_slave_system_control_probe(struct spi_device *spi) return 0; } -static int spi_slave_system_control_remove(struct spi_device *spi) +static void spi_slave_system_control_remove(struct spi_device *spi) { struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi); spi_slave_abort(spi); wait_for_completion(&priv->finished); - return 0; } static struct spi_driver spi_slave_system_control_driver = { diff --git a/drivers/spi/spi-slave-time.c b/drivers/spi/spi-slave-time.c index f2e07a392d68..f56c1afb8534 100644 --- a/drivers/spi/spi-slave-time.c +++ b/drivers/spi/spi-slave-time.c @@ -106,13 +106,12 @@ static int spi_slave_time_probe(struct spi_device *spi) return 0; } -static int spi_slave_time_remove(struct spi_device *spi) +static void spi_slave_time_remove(struct spi_device *spi) { struct spi_slave_time_priv *priv = spi_get_drvdata(spi); spi_slave_abort(spi); wait_for_completion(&priv->finished); - return 0; } static struct spi_driver spi_slave_time_driver = { diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index f8ad0709d015..a565352f6381 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -288,7 +288,7 @@ static int tle62x0_probe(struct spi_device *spi) return ret; } -static int tle62x0_remove(struct spi_device *spi) +static void tle62x0_remove(struct spi_device *spi) { struct tle62x0_state *st = spi_get_drvdata(spi); int ptr; @@ -298,7 +298,6 @@ static int tle62x0_remove(struct spi_device *spi) device_remove_file(&spi->dev, &dev_attr_status_show); kfree(st); - return 0; } static struct spi_driver tle62x0_driver = { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4599b121d744..ead9a132dcb9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -404,15 +404,8 @@ static void spi_remove(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); - if (sdrv->remove) { - int ret; - - ret = sdrv->remove(to_spi_device(dev)); - if (ret) - dev_warn(dev, - "Failed to unbind driver (%pe), ignoring\n", - ERR_PTR(ret)); - } + if (sdrv->remove) + sdrv->remove(to_spi_device(dev)); dev_pm_domain_detach(dev, true); } diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index a5cceca8b82b..9468f74308bd 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -803,7 +803,7 @@ static int spidev_probe(struct spi_device *spi) return status; } -static int spidev_remove(struct spi_device *spi) +static void spidev_remove(struct spi_device *spi) { struct spidev_data *spidev = spi_get_drvdata(spi); @@ -820,8 +820,6 @@ static int spidev_remove(struct spi_device *spi) if (spidev->users == 0) kfree(spidev); mutex_unlock(&device_list_lock); - - return 0; } static struct spi_driver spidev_spi_driver = { -- cgit From 715bea3568e78b80b1d127d8452eac0e3cb6f299 Mon Sep 17 00:00:00 2001 From: André Almeida Date: Fri, 11 Feb 2022 11:31:53 -0300 Subject: spi: amd: Use iopoll for busy waiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of implementing a custom IO busy wait function, just use readl_poll_timeout(). Signed-off-by: André Almeida Link: https://lore.kernel.org/r/20220211143155.75513-2-andrealmeid@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 4b3ac7aceaf6..899b8d90ff61 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -12,6 +12,7 @@ #include #include #include +#include #define AMD_SPI_CTRL0_REG 0x00 #define AMD_SPI_EXEC_CMD BIT(16) @@ -103,16 +104,10 @@ static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count) static int amd_spi_busy_wait(struct amd_spi *amd_spi) { - int timeout = 100000; + u32 val; - /* poll for SPI bus to become idle */ - while (amd_spi_readreg32(amd_spi, AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) { - usleep_range(10, 20); - if (timeout-- < 0) - return -ETIMEDOUT; - } - - return 0; + return readl_poll_timeout(amd_spi->io_remap_addr + AMD_SPI_CTRL0_REG, + val, !(val & AMD_SPI_BUSY), 20, 2000000); } static int amd_spi_execute_opcode(struct amd_spi *amd_spi) -- cgit From fbc71367288cf1902822618e00c7c1cf6f35348d Mon Sep 17 00:00:00 2001 From: André Almeida Date: Fri, 11 Feb 2022 11:31:54 -0300 Subject: spi: amd: Remove needless rom_addr variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rom_addr is not used in the code, so we can just drop it from struct amd_spi. Signed-off-by: André Almeida Link: https://lore.kernel.org/r/20220211143155.75513-3-andrealmeid@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 899b8d90ff61..417ce14a21c6 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; }; static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx) -- cgit From 209043554915d7c51ac112a668ad1a255e1bea61 Mon Sep 17 00:00:00 2001 From: André Almeida Date: Fri, 11 Feb 2022 11:31:55 -0300 Subject: spi: amd: Add support for version AMDI0062 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the AMD SPI controller version AMDI0062. Do this in a modular way where's easy to add new versions. Signed-off-by: André Almeida Link: https://lore.kernel.org/r/20220211143155.75513-4-andrealmeid@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 81 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 417ce14a21c6..d909afac6e21 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -19,6 +19,10 @@ #define AMD_SPI_FIFO_CLEAR BIT(20) #define AMD_SPI_BUSY BIT(31) +#define AMD_SPI_OPCODE_REG 0x45 +#define AMD_SPI_CMD_TRIGGER_REG 0x47 +#define AMD_SPI_TRIGGER_CMD BIT(7) + #define AMD_SPI_OPCODE_MASK 0xFF #define AMD_SPI_ALT_CS_REG 0x1D @@ -35,9 +39,15 @@ #define AMD_SPI_XFER_TX 1 #define AMD_SPI_XFER_RX 2 +enum amd_spi_versions { + AMD_SPI_V1 = 1, /* AMDI0061 */ + AMD_SPI_V2, /* AMDI0062 */ +}; + struct amd_spi { void __iomem *io_remap_addr; unsigned long io_base_addr; + enum amd_spi_versions version; }; static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx) @@ -81,14 +91,29 @@ static void amd_spi_select_chip(struct amd_spi *amd_spi, u8 cs) amd_spi_setclear_reg8(amd_spi, AMD_SPI_ALT_CS_REG, cs, AMD_SPI_ALT_CS_MASK); } +static inline void amd_spi_clear_chip(struct amd_spi *amd_spi, u8 chip_select) +{ + amd_spi_writereg8(amd_spi, AMD_SPI_ALT_CS_REG, chip_select & ~AMD_SPI_ALT_CS_MASK); +} + static void amd_spi_clear_fifo_ptr(struct amd_spi *amd_spi) { amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, AMD_SPI_FIFO_CLEAR); } -static void amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode) +static int amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode) { - amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode, AMD_SPI_OPCODE_MASK); + switch (amd_spi->version) { + case AMD_SPI_V1: + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode, + AMD_SPI_OPCODE_MASK); + return 0; + case AMD_SPI_V2: + amd_spi_writereg8(amd_spi, AMD_SPI_OPCODE_REG, cmd_opcode); + return 0; + default: + return -ENODEV; + } } static inline void amd_spi_set_rx_count(struct amd_spi *amd_spi, u8 rx_count) @@ -104,9 +129,21 @@ static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count) static int amd_spi_busy_wait(struct amd_spi *amd_spi) { u32 val; + int reg; + + switch (amd_spi->version) { + case AMD_SPI_V1: + reg = AMD_SPI_CTRL0_REG; + break; + case AMD_SPI_V2: + reg = AMD_SPI_STATUS_REG; + break; + default: + return -ENODEV; + } - return readl_poll_timeout(amd_spi->io_remap_addr + AMD_SPI_CTRL0_REG, - val, !(val & AMD_SPI_BUSY), 20, 2000000); + return readl_poll_timeout(amd_spi->io_remap_addr + reg, val, + !(val & AMD_SPI_BUSY), 20, 2000000); } static int amd_spi_execute_opcode(struct amd_spi *amd_spi) @@ -117,10 +154,20 @@ static int amd_spi_execute_opcode(struct amd_spi *amd_spi) if (ret) return ret; - /* Set ExecuteOpCode bit in the CTRL0 register */ - amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, AMD_SPI_EXEC_CMD); - - return 0; + switch (amd_spi->version) { + case AMD_SPI_V1: + /* Set ExecuteOpCode bit in the CTRL0 register */ + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, + AMD_SPI_EXEC_CMD); + return 0; + case AMD_SPI_V2: + /* Trigger the command execution */ + amd_spi_setclear_reg8(amd_spi, AMD_SPI_CMD_TRIGGER_REG, + AMD_SPI_TRIGGER_CMD, AMD_SPI_TRIGGER_CMD); + return 0; + default: + return -ENODEV; + } } static int amd_spi_master_setup(struct spi_device *spi) @@ -190,6 +237,17 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, message->actual_length = tx_len + rx_len + 1; /* complete the transaction */ message->status = 0; + + switch (amd_spi->version) { + case AMD_SPI_V1: + break; + case AMD_SPI_V2: + amd_spi_clear_chip(amd_spi, message->spi->chip_select); + break; + default: + return -ENODEV; + } + spi_finalize_current_message(master); return 0; @@ -235,6 +293,8 @@ static int amd_spi_probe(struct platform_device *pdev) } dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); + amd_spi->version = (enum amd_spi_versions) device_get_match_data(dev); + /* Initialize the spi_master fields */ master->bus_num = 0; master->num_chipselect = 4; @@ -260,7 +320,8 @@ err_free_master: #ifdef CONFIG_ACPI static const struct acpi_device_id spi_acpi_match[] = { - { "AMDI0061", 0 }, + { "AMDI0061", AMD_SPI_V1 }, + { "AMDI0062", AMD_SPI_V2 }, {}, }; MODULE_DEVICE_TABLE(acpi, spi_acpi_match); @@ -269,7 +330,7 @@ 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), + .acpi_match_table = spi_acpi_match, }, .probe = amd_spi_probe, }; -- cgit From e23e5a05d1fd9479586c40ffbcc056b3e34ef816 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 9 Feb 2022 15:27:05 +0300 Subject: mtd: spi-nor: intel-spi: Convert to SPI MEM The preferred way to implement SPI-NOR controller drivers is through SPI subsubsystem utilizing the SPI MEM core functions. This converts the Intel SPI flash controller driver over the SPI MEM by moving the driver from SPI-NOR subsystem to SPI subsystem and in one go make it use the SPI MEM functions. The driver name will be changed from intel-spi to spi-intel to match the convention used in the SPI subsystem. Signed-off-by: Mika Westerberg Reviewed-by: Andy Shevchenko Reviewed-by: Mauro Lima Reviewed-by: Boris Brezillon Acked-by: Lee Jones Acked-by: Pratyush Yadav Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20220209122706.42439-3-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 39 ++ drivers/spi/Makefile | 3 + drivers/spi/spi-intel-pci.c | 94 +++ drivers/spi/spi-intel-platform.c | 39 ++ drivers/spi/spi-intel.c | 1250 ++++++++++++++++++++++++++++++++++++++ drivers/spi/spi-intel.h | 19 + 6 files changed, 1444 insertions(+) create mode 100644 drivers/spi/spi-intel-pci.c create mode 100644 drivers/spi/spi-intel-platform.c create mode 100644 drivers/spi/spi-intel.c create mode 100644 drivers/spi/spi-intel.h (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b2a8821971e1..0201257511fb 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -427,6 +427,45 @@ config SPI_INGENIC To compile this driver as a module, choose M here: the module will be called spi-ingenic. +config SPI_INTEL + tristate + +config SPI_INTEL_PCI + tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)" + depends on PCI + depends on X86 || COMPILE_TEST + depends on SPI_MEM + select SPI_INTEL + help + This enables PCI support for the Intel PCH/PCU SPI controller in + master mode. This controller is present in modern Intel hardware + and is used to hold BIOS and other persistent settings. Using + this driver it is possible to upgrade BIOS directly from Linux. + + Say N here unless you know what you are doing. Overwriting the + SPI flash may render the system unbootable. + + To compile this driver as a module, choose M here: the module + will be called spi-intel-pci. + +config SPI_INTEL_PLATFORM + tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)" + depends on X86 || COMPILE_TEST + depends on SPI_MEM + select SPI_INTEL + help + This enables platform support for the Intel PCH/PCU SPI + controller in master mode. This controller is present in modern + Intel hardware and is used to hold BIOS and other persistent + settings. Using this driver it is possible to upgrade BIOS + directly from Linux. + + Say N here unless you know what you are doing. Overwriting the + SPI flash may render the system unbootable. + + To compile this driver as a module, choose M here: the module + will be called spi-intel-platform. + config SPI_JCORE tristate "J-Core SPI Master" depends on OF && (SUPERH || COMPILE_TEST) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index dd7393a6046f..36b2045f08d2 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -61,6 +61,9 @@ obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o obj-$(CONFIG_SPI_IMX) += spi-imx.o obj-$(CONFIG_SPI_INGENIC) += spi-ingenic.o +obj-$(CONFIG_SPI_INTEL) += spi-intel.o +obj-$(CONFIG_SPI_INTEL_PCI) += spi-intel-pci.o +obj-$(CONFIG_SPI_INTEL_PLATFORM) += spi-intel-platform.o obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o obj-$(CONFIG_SPI_JCORE) += spi-jcore.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c new file mode 100644 index 000000000000..a9cb4d77ffe3 --- /dev/null +++ b/drivers/spi/spi-intel-pci.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel PCH/PCU SPI flash PCI driver. + * + * Copyright (C) 2016 - 2022, Intel Corporation + * Author: Mika Westerberg + */ + +#include +#include + +#include "spi-intel.h" + +#define BCR 0xdc +#define BCR_WPD BIT(0) + +static bool intel_spi_pci_set_writeable(void __iomem *base, void *data) +{ + struct pci_dev *pdev = data; + u32 bcr; + + /* Try to make the chip read/write */ + pci_read_config_dword(pdev, BCR, &bcr); + if (!(bcr & BCR_WPD)) { + bcr |= BCR_WPD; + pci_write_config_dword(pdev, BCR, bcr); + pci_read_config_dword(pdev, BCR, &bcr); + } + + return bcr & BCR_WPD; +} + +static const struct intel_spi_boardinfo bxt_info = { + .type = INTEL_SPI_BXT, + .set_writeable = intel_spi_pci_set_writeable, +}; + +static const struct intel_spi_boardinfo cnl_info = { + .type = INTEL_SPI_CNL, + .set_writeable = intel_spi_pci_set_writeable, +}; + +static int intel_spi_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct intel_spi_boardinfo *info; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + info = devm_kmemdup(&pdev->dev, (void *)id->driver_data, sizeof(*info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->data = pdev; + return intel_spi_probe(&pdev->dev, &pdev->resource[0], info); +} + +static const struct pci_device_id intel_spi_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x02a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x06a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x18e0), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0xa324), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0xa3a4), (unsigned long)&bxt_info }, + { }, +}; +MODULE_DEVICE_TABLE(pci, intel_spi_pci_ids); + +static struct pci_driver intel_spi_pci_driver = { + .name = "intel-spi", + .id_table = intel_spi_pci_ids, + .probe = intel_spi_pci_probe, +}; + +module_pci_driver(intel_spi_pci_driver); + +MODULE_DESCRIPTION("Intel PCH/PCU SPI flash PCI driver"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-intel-platform.c b/drivers/spi/spi-intel-platform.c new file mode 100644 index 000000000000..2ef09fa35661 --- /dev/null +++ b/drivers/spi/spi-intel-platform.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel PCH/PCU SPI flash platform driver. + * + * Copyright (C) 2016 - 2022, Intel Corporation + * Author: Mika Westerberg + */ + +#include +#include + +#include "spi-intel.h" + +static int intel_spi_platform_probe(struct platform_device *pdev) +{ + struct intel_spi_boardinfo *info; + struct resource *mem; + + info = dev_get_platdata(&pdev->dev); + if (!info) + return -EINVAL; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + return intel_spi_probe(&pdev->dev, mem, info); +} + +static struct platform_driver intel_spi_platform_driver = { + .probe = intel_spi_platform_probe, + .driver = { + .name = "intel-spi", + }, +}; + +module_platform_driver(intel_spi_platform_driver); + +MODULE_DESCRIPTION("Intel PCH/PCU SPI flash platform driver"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:intel-spi"); diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c new file mode 100644 index 000000000000..e937cfe85559 --- /dev/null +++ b/drivers/spi/spi-intel.c @@ -0,0 +1,1250 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel PCH/PCU SPI flash driver. + * + * Copyright (C) 2016 - 2022, Intel Corporation + * Author: Mika Westerberg + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include "spi-intel.h" + +/* Offsets are from @ispi->base */ +#define BFPREG 0x00 + +#define HSFSTS_CTL 0x04 +#define HSFSTS_CTL_FSMIE BIT(31) +#define HSFSTS_CTL_FDBC_SHIFT 24 +#define HSFSTS_CTL_FDBC_MASK (0x3f << HSFSTS_CTL_FDBC_SHIFT) + +#define HSFSTS_CTL_FCYCLE_SHIFT 17 +#define HSFSTS_CTL_FCYCLE_MASK (0x0f << HSFSTS_CTL_FCYCLE_SHIFT) +/* HW sequencer opcodes */ +#define HSFSTS_CTL_FCYCLE_READ (0x00 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_WRITE (0x02 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_ERASE (0x03 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_ERASE_64K (0x04 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_RDID (0x06 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_WRSR (0x07 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_RDSR (0x08 << HSFSTS_CTL_FCYCLE_SHIFT) + +#define HSFSTS_CTL_FGO BIT(16) +#define HSFSTS_CTL_FLOCKDN BIT(15) +#define HSFSTS_CTL_FDV BIT(14) +#define HSFSTS_CTL_SCIP BIT(5) +#define HSFSTS_CTL_AEL BIT(2) +#define HSFSTS_CTL_FCERR BIT(1) +#define HSFSTS_CTL_FDONE BIT(0) + +#define FADDR 0x08 +#define DLOCK 0x0c +#define FDATA(n) (0x10 + ((n) * 4)) + +#define FRACC 0x50 + +#define FREG(n) (0x54 + ((n) * 4)) +#define FREG_BASE_MASK 0x3fff +#define FREG_LIMIT_SHIFT 16 +#define FREG_LIMIT_MASK (0x03fff << FREG_LIMIT_SHIFT) + +/* Offset is from @ispi->pregs */ +#define PR(n) ((n) * 4) +#define PR_WPE BIT(31) +#define PR_LIMIT_SHIFT 16 +#define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT) +#define PR_RPE BIT(15) +#define PR_BASE_MASK 0x3fff + +/* Offsets are from @ispi->sregs */ +#define SSFSTS_CTL 0x00 +#define SSFSTS_CTL_FSMIE BIT(23) +#define SSFSTS_CTL_DS BIT(22) +#define SSFSTS_CTL_DBC_SHIFT 16 +#define SSFSTS_CTL_SPOP BIT(11) +#define SSFSTS_CTL_ACS BIT(10) +#define SSFSTS_CTL_SCGO BIT(9) +#define SSFSTS_CTL_COP_SHIFT 12 +#define SSFSTS_CTL_FRS BIT(7) +#define SSFSTS_CTL_DOFRS BIT(6) +#define SSFSTS_CTL_AEL BIT(4) +#define SSFSTS_CTL_FCERR BIT(3) +#define SSFSTS_CTL_FDONE BIT(2) +#define SSFSTS_CTL_SCIP BIT(0) + +#define PREOP_OPTYPE 0x04 +#define OPMENU0 0x08 +#define OPMENU1 0x0c + +#define OPTYPE_READ_NO_ADDR 0 +#define OPTYPE_WRITE_NO_ADDR 1 +#define OPTYPE_READ_WITH_ADDR 2 +#define OPTYPE_WRITE_WITH_ADDR 3 + +/* CPU specifics */ +#define BYT_PR 0x74 +#define BYT_SSFSTS_CTL 0x90 +#define BYT_FREG_NUM 5 +#define BYT_PR_NUM 5 + +#define LPT_PR 0x74 +#define LPT_SSFSTS_CTL 0x90 +#define LPT_FREG_NUM 5 +#define LPT_PR_NUM 5 + +#define BXT_PR 0x84 +#define BXT_SSFSTS_CTL 0xa0 +#define BXT_FREG_NUM 12 +#define BXT_PR_NUM 6 + +#define CNL_PR 0x84 +#define CNL_FREG_NUM 6 +#define CNL_PR_NUM 5 + +#define LVSCC 0xc4 +#define UVSCC 0xc8 +#define ERASE_OPCODE_SHIFT 8 +#define ERASE_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT) +#define ERASE_64K_OPCODE_SHIFT 16 +#define ERASE_64K_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT) + +#define INTEL_SPI_TIMEOUT 5000 /* ms */ +#define INTEL_SPI_FIFO_SZ 64 + +/** + * struct intel_spi - Driver private data + * @dev: Device pointer + * @info: Pointer to board specific info + * @base: Beginning of MMIO space + * @pregs: Start of protection registers + * @sregs: Start of software sequencer registers + * @master: Pointer to the SPI controller structure + * @nregions: Maximum number of regions + * @pr_num: Maximum number of protected range registers + * @locked: Is SPI setting locked + * @swseq_reg: Use SW sequencer in register reads/writes + * @swseq_erase: Use SW sequencer in erase operation + * @atomic_preopcode: Holds preopcode when atomic sequence is requested + * @opcodes: Opcodes which are supported. This are programmed by BIOS + * before it locks down the controller. + * @mem_ops: Pointer to SPI MEM ops supported by the controller + */ +struct intel_spi { + struct device *dev; + const struct intel_spi_boardinfo *info; + void __iomem *base; + void __iomem *pregs; + void __iomem *sregs; + struct spi_controller *master; + size_t nregions; + size_t pr_num; + bool locked; + bool swseq_reg; + bool swseq_erase; + u8 atomic_preopcode; + u8 opcodes[8]; + const struct intel_spi_mem_op *mem_ops; +}; + +struct intel_spi_mem_op { + struct spi_mem_op mem_op; + u32 replacement_op; + int (*exec_op)(struct intel_spi *ispi, + const struct intel_spi_mem_op *iop, + const struct spi_mem_op *op); +}; + +static bool writeable; +module_param(writeable, bool, 0); +MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)"); + +static void intel_spi_dump_regs(struct intel_spi *ispi) +{ + u32 value; + int i; + + dev_dbg(ispi->dev, "BFPREG=0x%08x\n", readl(ispi->base + BFPREG)); + + value = readl(ispi->base + HSFSTS_CTL); + dev_dbg(ispi->dev, "HSFSTS_CTL=0x%08x\n", value); + if (value & HSFSTS_CTL_FLOCKDN) + dev_dbg(ispi->dev, "-> Locked\n"); + + dev_dbg(ispi->dev, "FADDR=0x%08x\n", readl(ispi->base + FADDR)); + dev_dbg(ispi->dev, "DLOCK=0x%08x\n", readl(ispi->base + DLOCK)); + + for (i = 0; i < 16; i++) + dev_dbg(ispi->dev, "FDATA(%d)=0x%08x\n", + i, readl(ispi->base + FDATA(i))); + + dev_dbg(ispi->dev, "FRACC=0x%08x\n", readl(ispi->base + FRACC)); + + for (i = 0; i < ispi->nregions; i++) + dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i, + readl(ispi->base + FREG(i))); + for (i = 0; i < ispi->pr_num; i++) + dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i, + readl(ispi->pregs + PR(i))); + + if (ispi->sregs) { + value = readl(ispi->sregs + SSFSTS_CTL); + dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value); + dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n", + readl(ispi->sregs + PREOP_OPTYPE)); + dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", + readl(ispi->sregs + OPMENU0)); + dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", + readl(ispi->sregs + OPMENU1)); + } + + dev_dbg(ispi->dev, "LVSCC=0x%08x\n", readl(ispi->base + LVSCC)); + dev_dbg(ispi->dev, "UVSCC=0x%08x\n", readl(ispi->base + UVSCC)); + + dev_dbg(ispi->dev, "Protected regions:\n"); + for (i = 0; i < ispi->pr_num; i++) { + u32 base, limit; + + value = readl(ispi->pregs + PR(i)); + if (!(value & (PR_WPE | PR_RPE))) + continue; + + limit = (value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT; + base = value & PR_BASE_MASK; + + dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x [%c%c]\n", + i, base << 12, (limit << 12) | 0xfff, + value & PR_WPE ? 'W' : '.', value & PR_RPE ? 'R' : '.'); + } + + dev_dbg(ispi->dev, "Flash regions:\n"); + for (i = 0; i < ispi->nregions; i++) { + u32 region, base, limit; + + region = readl(ispi->base + FREG(i)); + base = region & FREG_BASE_MASK; + limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT; + + if (base >= limit || (i > 0 && limit == 0)) + dev_dbg(ispi->dev, " %02d disabled\n", i); + else + dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x\n", + i, base << 12, (limit << 12) | 0xfff); + } + + dev_dbg(ispi->dev, "Using %cW sequencer for register access\n", + ispi->swseq_reg ? 'S' : 'H'); + dev_dbg(ispi->dev, "Using %cW sequencer for erase operation\n", + ispi->swseq_erase ? 'S' : 'H'); +} + +/* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */ +static int intel_spi_read_block(struct intel_spi *ispi, void *buf, size_t size) +{ + size_t bytes; + int i = 0; + + if (size > INTEL_SPI_FIFO_SZ) + return -EINVAL; + + while (size > 0) { + bytes = min_t(size_t, size, 4); + memcpy_fromio(buf, ispi->base + FDATA(i), bytes); + size -= bytes; + buf += bytes; + i++; + } + + return 0; +} + +/* Writes max INTEL_SPI_FIFO_SZ bytes to the device fifo */ +static int intel_spi_write_block(struct intel_spi *ispi, const void *buf, + size_t size) +{ + size_t bytes; + int i = 0; + + if (size > INTEL_SPI_FIFO_SZ) + return -EINVAL; + + while (size > 0) { + bytes = min_t(size_t, size, 4); + memcpy_toio(ispi->base + FDATA(i), buf, bytes); + size -= bytes; + buf += bytes; + i++; + } + + return 0; +} + +static int intel_spi_wait_hw_busy(struct intel_spi *ispi) +{ + u32 val; + + return readl_poll_timeout(ispi->base + HSFSTS_CTL, val, + !(val & HSFSTS_CTL_SCIP), 0, + INTEL_SPI_TIMEOUT * 1000); +} + +static int intel_spi_wait_sw_busy(struct intel_spi *ispi) +{ + u32 val; + + return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val, + !(val & SSFSTS_CTL_SCIP), 0, + INTEL_SPI_TIMEOUT * 1000); +} + +static bool intel_spi_set_writeable(struct intel_spi *ispi) +{ + if (!ispi->info->set_writeable) + return false; + + return ispi->info->set_writeable(ispi->base, ispi->info->data); +} + +static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype) +{ + int i; + int preop; + + if (ispi->locked) { + for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) + if (ispi->opcodes[i] == opcode) + return i; + + return -EINVAL; + } + + /* The lock is off, so just use index 0 */ + writel(opcode, ispi->sregs + OPMENU0); + preop = readw(ispi->sregs + PREOP_OPTYPE); + writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE); + + return 0; +} + +static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len) +{ + u32 val, status; + int ret; + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK); + + switch (opcode) { + case SPINOR_OP_RDID: + val |= HSFSTS_CTL_FCYCLE_RDID; + break; + case SPINOR_OP_WRSR: + val |= HSFSTS_CTL_FCYCLE_WRSR; + break; + case SPINOR_OP_RDSR: + val |= HSFSTS_CTL_FCYCLE_RDSR; + break; + default: + return -EINVAL; + } + + if (len > INTEL_SPI_FIFO_SZ) + return -EINVAL; + + val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT; + val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= HSFSTS_CTL_FGO; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + return -EIO; + else if (status & HSFSTS_CTL_AEL) + return -EACCES; + + return 0; +} + +static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len, + int optype) +{ + u32 val = 0, status; + u8 atomic_preopcode; + int ret; + + ret = intel_spi_opcode_index(ispi, opcode, optype); + if (ret < 0) + return ret; + + if (len > INTEL_SPI_FIFO_SZ) + return -EINVAL; + + /* + * Always clear it after each SW sequencer operation regardless + * of whether it is successful or not. + */ + atomic_preopcode = ispi->atomic_preopcode; + ispi->atomic_preopcode = 0; + + /* Only mark 'Data Cycle' bit when there is data to be transferred */ + if (len > 0) + val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS; + val |= ret << SSFSTS_CTL_COP_SHIFT; + val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE; + val |= SSFSTS_CTL_SCGO; + if (atomic_preopcode) { + u16 preop; + + switch (optype) { + case OPTYPE_WRITE_NO_ADDR: + case OPTYPE_WRITE_WITH_ADDR: + /* Pick matching preopcode for the atomic sequence */ + preop = readw(ispi->sregs + PREOP_OPTYPE); + if ((preop & 0xff) == atomic_preopcode) + ; /* Do nothing */ + else if ((preop >> 8) == atomic_preopcode) + val |= SSFSTS_CTL_SPOP; + else + return -EINVAL; + + /* Enable atomic sequence */ + val |= SSFSTS_CTL_ACS; + break; + + default: + return -EINVAL; + } + } + writel(val, ispi->sregs + SSFSTS_CTL); + + ret = intel_spi_wait_sw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->sregs + SSFSTS_CTL); + if (status & SSFSTS_CTL_FCERR) + return -EIO; + else if (status & SSFSTS_CTL_AEL) + return -EACCES; + + return 0; +} + +static int intel_spi_read_reg(struct intel_spi *ispi, + const struct intel_spi_mem_op *iop, + const struct spi_mem_op *op) +{ + size_t nbytes = op->data.nbytes; + u8 opcode = op->cmd.opcode; + int ret; + + /* Address of the first chip */ + writel(0, ispi->base + FADDR); + + if (ispi->swseq_reg) + ret = intel_spi_sw_cycle(ispi, opcode, nbytes, + OPTYPE_READ_NO_ADDR); + else + ret = intel_spi_hw_cycle(ispi, opcode, nbytes); + + if (ret) + return ret; + + return intel_spi_read_block(ispi, op->data.buf.in, nbytes); +} + +static int intel_spi_write_reg(struct intel_spi *ispi, + const struct intel_spi_mem_op *iop, + const struct spi_mem_op *op) +{ + size_t nbytes = op->data.nbytes; + u8 opcode = op->cmd.opcode; + int ret; + + /* + * This is handled with atomic operation and preop code in Intel + * controller so we only verify that it is available. If the + * controller is not locked, program the opcode to the PREOP + * register for later use. + * + * When hardware sequencer is used there is no need to program + * any opcodes (it handles them automatically as part of a command). + */ + if (opcode == SPINOR_OP_WREN) { + u16 preop; + + if (!ispi->swseq_reg) + return 0; + + preop = readw(ispi->sregs + PREOP_OPTYPE); + if ((preop & 0xff) != opcode && (preop >> 8) != opcode) { + if (ispi->locked) + return -EINVAL; + writel(opcode, ispi->sregs + PREOP_OPTYPE); + } + + /* + * This enables atomic sequence on next SW sycle. Will + * be cleared after next operation. + */ + ispi->atomic_preopcode = opcode; + return 0; + } + + /* + * We hope that HW sequencer will do the right thing automatically and + * with the SW sequencer we cannot use preopcode anyway, so just ignore + * the Write Disable operation and pretend it was completed + * successfully. + */ + if (opcode == SPINOR_OP_WRDI) + return 0; + + writel(0, ispi->base + FADDR); + + /* Write the value beforehand */ + ret = intel_spi_write_block(ispi, op->data.buf.out, nbytes); + if (ret) + return ret; + + if (ispi->swseq_reg) + return intel_spi_sw_cycle(ispi, opcode, nbytes, + OPTYPE_WRITE_NO_ADDR); + return intel_spi_hw_cycle(ispi, opcode, nbytes); +} + +static int intel_spi_read(struct intel_spi *ispi, + const struct intel_spi_mem_op *iop, + const struct spi_mem_op *op) +{ + void *read_buf = op->data.buf.in; + size_t block_size, nbytes = op->data.nbytes; + u32 addr = op->addr.val; + u32 val, status; + int ret; + + /* + * Atomic sequence is not expected with HW sequencer reads. Make + * sure it is cleared regardless. + */ + if (WARN_ON_ONCE(ispi->atomic_preopcode)) + ispi->atomic_preopcode = 0; + + while (nbytes > 0) { + block_size = min_t(size_t, nbytes, INTEL_SPI_FIFO_SZ); + + /* Read cannot cross 4K boundary */ + block_size = min_t(loff_t, addr + block_size, + round_up(addr + 1, SZ_4K)) - addr; + + writel(addr, ispi->base + FADDR); + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK); + val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT; + val |= HSFSTS_CTL_FCYCLE_READ; + val |= HSFSTS_CTL_FGO; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + ret = -EIO; + else if (status & HSFSTS_CTL_AEL) + ret = -EACCES; + + if (ret < 0) { + dev_err(ispi->dev, "read error: %x: %#x\n", addr, status); + return ret; + } + + ret = intel_spi_read_block(ispi, read_buf, block_size); + if (ret) + return ret; + + nbytes -= block_size; + addr += block_size; + read_buf += block_size; + } + + return 0; +} + +static int intel_spi_write(struct intel_spi *ispi, + const struct intel_spi_mem_op *iop, + const struct spi_mem_op *op) +{ + size_t block_size, nbytes = op->data.nbytes; + const void *write_buf = op->data.buf.out; + u32 addr = op->addr.val; + u32 val, status; + int ret; + + /* Not needed with HW sequencer write, make sure it is cleared */ + ispi->atomic_preopcode = 0; + + while (nbytes > 0) { + block_size = min_t(size_t, nbytes, INTEL_SPI_FIFO_SZ); + + /* Write cannot cross 4K boundary */ + block_size = min_t(loff_t, addr + block_size, + round_up(addr + 1, SZ_4K)) - addr; + + writel(addr, ispi->base + FADDR); + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK); + val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT; + val |= HSFSTS_CTL_FCYCLE_WRITE; + + ret = intel_spi_write_block(ispi, write_buf, block_size); + if (ret) { + dev_err(ispi->dev, "failed to write block\n"); + return ret; + } + + /* Start the write now */ + val |= HSFSTS_CTL_FGO; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) { + dev_err(ispi->dev, "timeout\n"); + return ret; + } + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + ret = -EIO; + else if (status & HSFSTS_CTL_AEL) + ret = -EACCES; + + if (ret < 0) { + dev_err(ispi->dev, "write error: %x: %#x\n", addr, status); + return ret; + } + + nbytes -= block_size; + addr += block_size; + write_buf += block_size; + } + + return 0; +} + +static int intel_spi_erase(struct intel_spi *ispi, + const struct intel_spi_mem_op *iop, + const struct spi_mem_op *op) +{ + u8 opcode = op->cmd.opcode; + u32 addr = op->addr.val; + u32 val, status; + int ret; + + writel(addr, ispi->base + FADDR); + + if (ispi->swseq_erase) + return intel_spi_sw_cycle(ispi, opcode, 0, + OPTYPE_WRITE_WITH_ADDR); + + /* Not needed with HW sequencer erase, make sure it is cleared */ + ispi->atomic_preopcode = 0; + + val = readl(ispi->base + HSFSTS_CTL); + val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK); + val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; + val |= HSFSTS_CTL_FGO; + val |= iop->replacement_op; + writel(val, ispi->base + HSFSTS_CTL); + + ret = intel_spi_wait_hw_busy(ispi); + if (ret) + return ret; + + status = readl(ispi->base + HSFSTS_CTL); + if (status & HSFSTS_CTL_FCERR) + return -EIO; + if (status & HSFSTS_CTL_AEL) + return -EACCES; + + return 0; +} + +static bool intel_spi_cmp_mem_op(const struct intel_spi_mem_op *iop, + const struct spi_mem_op *op) +{ + if (iop->mem_op.cmd.nbytes != op->cmd.nbytes || + iop->mem_op.cmd.buswidth != op->cmd.buswidth || + iop->mem_op.cmd.dtr != op->cmd.dtr || + iop->mem_op.cmd.opcode != op->cmd.opcode) + return false; + + if (iop->mem_op.addr.nbytes != op->addr.nbytes || + iop->mem_op.addr.dtr != op->addr.dtr) + return false; + + if (iop->mem_op.data.dir != op->data.dir || + iop->mem_op.data.dtr != op->data.dtr) + return false; + + if (iop->mem_op.data.dir != SPI_MEM_NO_DATA) { + if (iop->mem_op.data.buswidth != op->data.buswidth) + return false; + } + + return true; +} + +static const struct intel_spi_mem_op * +intel_spi_match_mem_op(struct intel_spi *ispi, const struct spi_mem_op *op) +{ + const struct intel_spi_mem_op *iop; + + for (iop = ispi->mem_ops; iop->mem_op.cmd.opcode; iop++) { + if (intel_spi_cmp_mem_op(iop, op)) + break; + } + + return iop->mem_op.cmd.opcode ? iop : NULL; +} + +static bool intel_spi_supports_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct intel_spi *ispi = spi_master_get_devdata(mem->spi->master); + const struct intel_spi_mem_op *iop; + + iop = intel_spi_match_mem_op(ispi, op); + if (!iop) { + dev_dbg(ispi->dev, "%#x not supported\n", op->cmd.opcode); + return false; + } + + /* + * For software sequencer check that the opcode is actually + * present in the opmenu if it is locked. + */ + if (ispi->swseq_reg && ispi->locked) { + int i; + + /* Check if it is in the locked opcodes list */ + for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) { + if (ispi->opcodes[i] == op->cmd.opcode) + return true; + } + + dev_dbg(ispi->dev, "%#x not supported\n", op->cmd.opcode); + return false; + } + + return true; +} + +static int intel_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct intel_spi *ispi = spi_master_get_devdata(mem->spi->master); + const struct intel_spi_mem_op *iop; + + iop = intel_spi_match_mem_op(ispi, op); + if (!iop) + return -EOPNOTSUPP; + + return iop->exec_op(ispi, iop, op); +} + +static const char *intel_spi_get_name(struct spi_mem *mem) +{ + const struct intel_spi *ispi = spi_master_get_devdata(mem->spi->master); + + /* + * Return name of the flash controller device to be compatible + * with the MTD version. + */ + return dev_name(ispi->dev); +} + +static const struct spi_controller_mem_ops intel_spi_mem_ops = { + .supports_op = intel_spi_supports_mem_op, + .exec_op = intel_spi_exec_mem_op, + .get_name = intel_spi_get_name, +}; + +#define INTEL_SPI_OP_ADDR(__nbytes) \ + { \ + .nbytes = __nbytes, \ + } + +#define INTEL_SPI_OP_NO_DATA \ + { \ + .dir = SPI_MEM_NO_DATA, \ + } + +#define INTEL_SPI_OP_DATA_IN(__buswidth) \ + { \ + .dir = SPI_MEM_DATA_IN, \ + .buswidth = __buswidth, \ + } + +#define INTEL_SPI_OP_DATA_OUT(__buswidth) \ + { \ + .dir = SPI_MEM_DATA_OUT, \ + .buswidth = __buswidth, \ + } + +#define INTEL_SPI_MEM_OP(__cmd, __addr, __data, __exec_op) \ + { \ + .mem_op = { \ + .cmd = __cmd, \ + .addr = __addr, \ + .data = __data, \ + }, \ + .exec_op = __exec_op, \ + } + +#define INTEL_SPI_MEM_OP_REPL(__cmd, __addr, __data, __exec_op, __repl) \ + { \ + .mem_op = { \ + .cmd = __cmd, \ + .addr = __addr, \ + .data = __data, \ + }, \ + .exec_op = __exec_op, \ + .replacement_op = __repl, \ + } + +/* + * The controller handles pretty much everything internally based on the + * SFDP data but we want to make sure we only support the operations + * actually possible. Only check buswidth and transfer direction, the + * core validates data. + */ +#define INTEL_SPI_GENERIC_OPS \ + /* Status register operations */ \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), \ + SPI_MEM_OP_NO_ADDR, \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read_reg), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), \ + SPI_MEM_OP_NO_ADDR, \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read_reg), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), \ + SPI_MEM_OP_NO_ADDR, \ + INTEL_SPI_OP_DATA_OUT(1), \ + intel_spi_write_reg), \ + /* Normal read */ \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_IN(2), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_IN(4), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(2), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(4), \ + intel_spi_read), \ + /* Fast read */ \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_IN(2), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_IN(4), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(2), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(4), \ + intel_spi_read), \ + /* Read with 4-byte address opcode */ \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(2), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(4), \ + intel_spi_read), \ + /* Fast read with 4-byte address opcode */ \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(2), \ + intel_spi_read), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_FAST_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_IN(4), \ + intel_spi_read), \ + /* Write operations */ \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_PP, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_OUT(1), \ + intel_spi_write), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_PP, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_OUT(1), \ + intel_spi_write), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_PP_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + INTEL_SPI_OP_DATA_OUT(1), \ + intel_spi_write), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DATA, \ + intel_spi_write_reg), \ + INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DATA, \ + intel_spi_write_reg), \ + /* Erase operations */ \ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_BE_4K, 1), \ + INTEL_SPI_OP_ADDR(3), \ + SPI_MEM_OP_NO_DATA, \ + intel_spi_erase, \ + HSFSTS_CTL_FCYCLE_ERASE), \ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_BE_4K, 1), \ + INTEL_SPI_OP_ADDR(4), \ + SPI_MEM_OP_NO_DATA, \ + intel_spi_erase, \ + HSFSTS_CTL_FCYCLE_ERASE), \ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_BE_4K_4B, 1), \ + INTEL_SPI_OP_ADDR(4), \ + SPI_MEM_OP_NO_DATA, \ + intel_spi_erase, \ + HSFSTS_CTL_FCYCLE_ERASE) \ + +static const struct intel_spi_mem_op generic_mem_ops[] = { + INTEL_SPI_GENERIC_OPS, + { }, +}; + +static const struct intel_spi_mem_op erase_64k_mem_ops[] = { + INTEL_SPI_GENERIC_OPS, + /* 64k sector erase operations */ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_SE, 1), + INTEL_SPI_OP_ADDR(3), + SPI_MEM_OP_NO_DATA, + intel_spi_erase, + HSFSTS_CTL_FCYCLE_ERASE_64K), + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_SE, 1), + INTEL_SPI_OP_ADDR(4), + SPI_MEM_OP_NO_DATA, + intel_spi_erase, + HSFSTS_CTL_FCYCLE_ERASE_64K), + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_SE_4B, 1), + INTEL_SPI_OP_ADDR(4), + SPI_MEM_OP_NO_DATA, + intel_spi_erase, + HSFSTS_CTL_FCYCLE_ERASE_64K), + { }, +}; + +static int intel_spi_init(struct intel_spi *ispi) +{ + u32 opmenu0, opmenu1, lvscc, uvscc, val; + bool erase_64k = false; + int i; + + switch (ispi->info->type) { + case INTEL_SPI_BYT: + ispi->sregs = ispi->base + BYT_SSFSTS_CTL; + ispi->pregs = ispi->base + BYT_PR; + ispi->nregions = BYT_FREG_NUM; + ispi->pr_num = BYT_PR_NUM; + ispi->swseq_reg = true; + break; + + case INTEL_SPI_LPT: + ispi->sregs = ispi->base + LPT_SSFSTS_CTL; + ispi->pregs = ispi->base + LPT_PR; + ispi->nregions = LPT_FREG_NUM; + ispi->pr_num = LPT_PR_NUM; + ispi->swseq_reg = true; + break; + + case INTEL_SPI_BXT: + ispi->sregs = ispi->base + BXT_SSFSTS_CTL; + ispi->pregs = ispi->base + BXT_PR; + ispi->nregions = BXT_FREG_NUM; + ispi->pr_num = BXT_PR_NUM; + erase_64k = true; + break; + + case INTEL_SPI_CNL: + ispi->sregs = NULL; + ispi->pregs = ispi->base + CNL_PR; + ispi->nregions = CNL_FREG_NUM; + ispi->pr_num = CNL_PR_NUM; + break; + + default: + return -EINVAL; + } + + /* Try to disable write protection if user asked to do so */ + if (writeable && !intel_spi_set_writeable(ispi)) { + dev_warn(ispi->dev, "can't disable chip write protection\n"); + writeable = false; + } + + /* Disable #SMI generation from HW sequencer */ + val = readl(ispi->base + HSFSTS_CTL); + val &= ~HSFSTS_CTL_FSMIE; + writel(val, ispi->base + HSFSTS_CTL); + + /* + * Determine whether erase operation should use HW or SW sequencer. + * + * The HW sequencer has a predefined list of opcodes, with only the + * erase opcode being programmable in LVSCC and UVSCC registers. + * If these registers don't contain a valid erase opcode, erase + * cannot be done using HW sequencer. + */ + lvscc = readl(ispi->base + LVSCC); + uvscc = readl(ispi->base + UVSCC); + if (!(lvscc & ERASE_OPCODE_MASK) || !(uvscc & ERASE_OPCODE_MASK)) + ispi->swseq_erase = true; + /* SPI controller on Intel BXT supports 64K erase opcode */ + if (ispi->info->type == INTEL_SPI_BXT && !ispi->swseq_erase) + if (!(lvscc & ERASE_64K_OPCODE_MASK) || + !(uvscc & ERASE_64K_OPCODE_MASK)) + erase_64k = false; + + if (!ispi->sregs && (ispi->swseq_reg || ispi->swseq_erase)) { + dev_err(ispi->dev, "software sequencer not supported, but required\n"); + return -EINVAL; + } + + /* + * Some controllers can only do basic operations using hardware + * sequencer. All other operations are supposed to be carried out + * using software sequencer. + */ + if (ispi->swseq_reg) { + /* Disable #SMI generation from SW sequencer */ + val = readl(ispi->sregs + SSFSTS_CTL); + val &= ~SSFSTS_CTL_FSMIE; + writel(val, ispi->sregs + SSFSTS_CTL); + } + + /* Check controller's lock status */ + val = readl(ispi->base + HSFSTS_CTL); + ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN); + + if (ispi->locked && ispi->sregs) { + /* + * BIOS programs allowed opcodes and then locks down the + * register. So read back what opcodes it decided to support. + * That's the set we are going to support as well. + */ + opmenu0 = readl(ispi->sregs + OPMENU0); + opmenu1 = readl(ispi->sregs + OPMENU1); + + if (opmenu0 && opmenu1) { + for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) { + ispi->opcodes[i] = opmenu0 >> i * 8; + ispi->opcodes[i + 4] = opmenu1 >> i * 8; + } + } + } + + if (erase_64k) { + dev_dbg(ispi->dev, "Using erase_64k memory operations"); + ispi->mem_ops = erase_64k_mem_ops; + } else { + dev_dbg(ispi->dev, "Using generic memory operations"); + ispi->mem_ops = generic_mem_ops; + } + + intel_spi_dump_regs(ispi); + return 0; +} + +static bool intel_spi_is_protected(const struct intel_spi *ispi, + unsigned int base, unsigned int limit) +{ + int i; + + for (i = 0; i < ispi->pr_num; i++) { + u32 pr_base, pr_limit, pr_value; + + pr_value = readl(ispi->pregs + PR(i)); + if (!(pr_value & (PR_WPE | PR_RPE))) + continue; + + pr_limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT; + pr_base = pr_value & PR_BASE_MASK; + + if (pr_base >= base && pr_limit <= limit) + return true; + } + + return false; +} + +/* + * There will be a single partition holding all enabled flash regions. We + * call this "BIOS". + */ +static void intel_spi_fill_partition(struct intel_spi *ispi, + struct mtd_partition *part) +{ + u64 end; + int i; + + memset(part, 0, sizeof(*part)); + + /* Start from the mandatory descriptor region */ + part->size = 4096; + part->name = "BIOS"; + + /* + * Now try to find where this partition ends based on the flash + * region registers. + */ + for (i = 1; i < ispi->nregions; i++) { + u32 region, base, limit; + + region = readl(ispi->base + FREG(i)); + base = region & FREG_BASE_MASK; + limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT; + + if (base >= limit || limit == 0) + continue; + + /* + * If any of the regions have protection bits set, make the + * whole partition read-only to be on the safe side. + * + * Also if the user did not ask the chip to be writeable + * mask the bit too. + */ + if (!writeable || intel_spi_is_protected(ispi, base, limit)) + part->mask_flags |= MTD_WRITEABLE; + + end = (limit << 12) + 4096; + if (end > part->size) + part->size = end; + } +} + +static int intel_spi_populate_chip(struct intel_spi *ispi) +{ + struct flash_platform_data *pdata; + struct spi_board_info chip; + + pdata = devm_kzalloc(ispi->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->nr_parts = 1; + pdata->parts = devm_kcalloc(ispi->dev, sizeof(*pdata->parts), + pdata->nr_parts, GFP_KERNEL); + if (!pdata->parts) + return -ENOMEM; + + intel_spi_fill_partition(ispi, pdata->parts); + + memset(&chip, 0, sizeof(chip)); + snprintf(chip.modalias, 8, "spi-nor"); + chip.platform_data = pdata; + + return spi_new_device(ispi->master, &chip) ? 0 : -ENODEV; +} + +/** + * intel_spi_probe() - Probe the Intel SPI flash controller + * @dev: Pointer to the parent device + * @mem: MMIO resource + * @info: Platform spefific information + * + * Probes Intel SPI flash controller and creates the flash chip device. + * Returns %0 on success and negative errno in case of failure. + */ +int intel_spi_probe(struct device *dev, struct resource *mem, + const struct intel_spi_boardinfo *info) +{ + struct spi_controller *master; + struct intel_spi *ispi; + int ret; + + master = devm_spi_alloc_master(dev, sizeof(*ispi)); + if (!master) + return -ENOMEM; + + master->mem_ops = &intel_spi_mem_ops; + + ispi = spi_master_get_devdata(master); + + ispi->base = devm_ioremap_resource(dev, mem); + if (IS_ERR(ispi->base)) + return PTR_ERR(ispi->base); + + ispi->dev = dev; + ispi->master = master; + ispi->info = info; + + ret = intel_spi_init(ispi); + if (ret) + return ret; + + ret = devm_spi_register_master(dev, master); + if (ret) + return ret; + + return intel_spi_populate_chip(ispi); +} +EXPORT_SYMBOL_GPL(intel_spi_probe); + +MODULE_DESCRIPTION("Intel PCH/PCU SPI flash core driver"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-intel.h b/drivers/spi/spi-intel.h new file mode 100644 index 000000000000..a4f0327a46ff --- /dev/null +++ b/drivers/spi/spi-intel.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Intel PCH/PCU SPI flash driver. + * + * Copyright (C) 2016 - 2022, Intel Corporation + * Author: Mika Westerberg + */ + +#ifndef SPI_INTEL_H +#define SPI_INTEL_H + +#include + +struct resource; + +int intel_spi_probe(struct device *dev, struct resource *mem, + const struct intel_spi_boardinfo *info); + +#endif /* SPI_INTEL_H */ -- cgit From 5790597d7113faabb1714d3d1efa268e36eb4811 Mon Sep 17 00:00:00 2001 From: Li-hao Kuo Date: Mon, 14 Feb 2022 10:20:11 +0800 Subject: spi: Fix warning for Clang build and simplify code Clang build fails with spi-sunplus-sp7021.c:405:2: error: variable 'ret' is used uninitialized whenever switch default is taken default: simplify code Restore initializing ret. and add return error at default Fixes: 47e8fe57a66f ("spi: Modify irq request position and modify parameters") Reported-by: Tom Rix Reported-by: kernel test robot Reported-by: Nathan Chancellor Reported-by: Mark Brown Signed-off-by: Li-hao Kuo Link: https://lore.kernel.org/r/7d91e6ce29f9a8df2c53a47b4b977664020e237a.1644805060.git.lhjeff911@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-sunplus-sp7021.c | 38 +++++++++----------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c index ba5ed9f7277a..ade7a0fca8cb 100644 --- a/drivers/spi/spi-sunplus-sp7021.c +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -69,12 +69,6 @@ #define SP7021_SPI_DATA_SIZE (255) #define SP7021_FIFO_DATA_LEN (16) -enum SP_SPI_MODE { - SP7021_SLAVE_READ = 0, - SP7021_SLAVE_WRITE = 1, - SP7021_SPI_IDLE = 2, -}; - enum { SP7021_MASTER_MODE = 0, SP7021_SLAVE_MODE = 1, @@ -375,40 +369,26 @@ static int sp7021_spi_slave_transfer_one(struct spi_controller *ctlr, struct spi { struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(ctlr); struct device *dev = pspim->dev; - int mode, ret; + int ret; - mode = SP7021_SPI_IDLE; - if (xfer->tx_buf && xfer->rx_buf) { - dev_dbg(&ctlr->dev, "%s() wrong command\n", __func__); - return -EINVAL; - } else if (xfer->tx_buf) { + if (xfer->tx_buf && !xfer->rx_buf) { xfer->tx_dma = dma_map_single(dev, (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); if (dma_mapping_error(dev, xfer->tx_dma)) return -ENOMEM; - mode = SP7021_SLAVE_WRITE; - } else if (xfer->rx_buf) { + ret = sp7021_spi_slave_tx(spi, xfer); + dma_unmap_single(dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE); + } else if (xfer->rx_buf && !xfer->tx_buf) { xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); if (dma_mapping_error(dev, xfer->rx_dma)) return -ENOMEM; - mode = SP7021_SLAVE_READ; - } - - switch (mode) { - case SP7021_SLAVE_WRITE: - ret = sp7021_spi_slave_tx(spi, xfer); - break; - case SP7021_SLAVE_READ: ret = sp7021_spi_slave_rx(spi, xfer); - break; - default: - break; - } - if (xfer->tx_buf) - dma_unmap_single(dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE); - if (xfer->rx_buf) dma_unmap_single(dev, xfer->rx_dma, xfer->len, DMA_FROM_DEVICE); + } else { + dev_dbg(&ctlr->dev, "%s() wrong command\n", __func__); + return -EINVAL; + } spi_finalize_current_transfer(ctlr); return ret; -- cgit From f48dc6b9664963107e500aecfc2f4df27dc5afb6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 11 Feb 2022 00:19:54 +0100 Subject: spi: Retire legacy GPIO handling All drivers using GPIOs as chip select have been rewritten to use GPIO descriptors passing the ->use_gpio_descriptors flag. Retire the code and fields used by the legacy GPIO API. Do not drop the ->use_gpio_descriptors flag: it now only indicates that we want to use GPIOs in addition to native chip selects. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20220210231954.807904-1-linus.walleij@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi.c | 125 ++++++++++++------------------------------------------ 1 file changed, 28 insertions(+), 97 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d9832d8e9f44..85f8ae4cc0c0 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -542,7 +541,6 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr) spi->dev.parent = &ctlr->dev; spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; - spi->cs_gpio = -ENOENT; spi->mode = ctlr->buswidth_override_bits; spin_lock_init(&spi->statistics.lock); @@ -606,11 +604,8 @@ static int __spi_add_device(struct spi_device *spi) return -ENODEV; } - /* Descriptors take precedence */ if (ctlr->cs_gpiods) spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select]; - else if (ctlr->cs_gpios) - spi->cs_gpio = ctlr->cs_gpios[spi->chip_select]; /* * Drivers may modify this initial i/o setup, but will @@ -940,39 +935,30 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) spi->controller->last_cs_enable = enable; spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; - if ((spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) || - !spi->controller->set_cs_timing) && !activate) { + if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) { spi_delay_exec(&spi->cs_hold, NULL); } if (spi->mode & SPI_CS_HIGH) enable = !enable; - if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio)) { + if (spi->cs_gpiod) { if (!(spi->mode & SPI_NO_CS)) { - if (spi->cs_gpiod) { - /* - * Historically ACPI has no means of the GPIO polarity and - * thus the SPISerialBus() resource defines it on the per-chip - * basis. In order to avoid a chain of negations, the GPIO - * polarity is considered being Active High. Even for the cases - * when _DSD() is involved (in the updated versions of ACPI) - * the GPIO CS polarity must be defined Active High to avoid - * ambiguity. That's why we use enable, that takes SPI_CS_HIGH - * into account. - */ - if (has_acpi_companion(&spi->dev)) - gpiod_set_value_cansleep(spi->cs_gpiod, !enable); - else - /* Polarity handled by GPIO library */ - gpiod_set_value_cansleep(spi->cs_gpiod, activate); - } else { - /* - * Invert the enable line, as active low is - * default for SPI. - */ - gpio_set_value_cansleep(spi->cs_gpio, !enable); - } + /* + * Historically ACPI has no means of the GPIO polarity and + * thus the SPISerialBus() resource defines it on the per-chip + * basis. In order to avoid a chain of negations, the GPIO + * polarity is considered being Active High. Even for the cases + * when _DSD() is involved (in the updated versions of ACPI) + * the GPIO CS polarity must be defined Active High to avoid + * ambiguity. That's why we use enable, that takes SPI_CS_HIGH + * into account. + */ + if (has_acpi_companion(&spi->dev)) + gpiod_set_value_cansleep(spi->cs_gpiod, !enable); + else + /* Polarity handled by GPIO library */ + gpiod_set_value_cansleep(spi->cs_gpiod, activate); } /* Some SPI masters need both GPIO CS & slave_select */ if ((spi->controller->flags & SPI_MASTER_GPIO_SS) && @@ -982,8 +968,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) spi->controller->set_cs(spi, !enable); } - if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio) || - !spi->controller->set_cs_timing) { + if (spi->cs_gpiod || !spi->controller->set_cs_timing) { if (activate) spi_delay_exec(&spi->cs_setup, NULL); else @@ -2827,46 +2812,6 @@ struct spi_controller *__devm_spi_alloc_controller(struct device *dev, } EXPORT_SYMBOL_GPL(__devm_spi_alloc_controller); -#ifdef CONFIG_OF -static int of_spi_get_gpio_numbers(struct spi_controller *ctlr) -{ - int nb, i, *cs; - struct device_node *np = ctlr->dev.of_node; - - if (!np) - return 0; - - nb = of_gpio_named_count(np, "cs-gpios"); - ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect); - - /* Return error only for an incorrectly formed cs-gpios property */ - if (nb == 0 || nb == -ENOENT) - return 0; - else if (nb < 0) - return nb; - - cs = devm_kcalloc(&ctlr->dev, ctlr->num_chipselect, sizeof(int), - GFP_KERNEL); - ctlr->cs_gpios = cs; - - if (!ctlr->cs_gpios) - return -ENOMEM; - - for (i = 0; i < ctlr->num_chipselect; i++) - cs[i] = -ENOENT; - - for (i = 0; i < nb; i++) - cs[i] = of_get_named_gpio(np, "cs-gpios", i); - - return 0; -} -#else -static int of_spi_get_gpio_numbers(struct spi_controller *ctlr) -{ - return 0; -} -#endif - /** * spi_get_gpio_descs() - grab chip select GPIOs for the master * @ctlr: The SPI master to grab GPIO descriptors for @@ -3051,22 +2996,15 @@ int spi_register_controller(struct spi_controller *ctlr) */ dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num); - if (!spi_controller_is_slave(ctlr)) { - if (ctlr->use_gpio_descriptors) { - status = spi_get_gpio_descs(ctlr); - if (status) - goto free_bus_id; - /* - * A controller using GPIO descriptors always - * supports SPI_CS_HIGH if need be. - */ - ctlr->mode_bits |= SPI_CS_HIGH; - } else { - /* Legacy code path for GPIOs from DT */ - status = of_spi_get_gpio_numbers(ctlr); - if (status) - goto free_bus_id; - } + if (!spi_controller_is_slave(ctlr) && ctlr->use_gpio_descriptors) { + status = spi_get_gpio_descs(ctlr); + if (status) + goto free_bus_id; + /* + * A controller using GPIO descriptors always + * supports SPI_CS_HIGH if need be. + */ + ctlr->mode_bits |= SPI_CS_HIGH; } /* @@ -3555,12 +3493,6 @@ int spi_setup(struct spi_device *spi) */ bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD | SPI_NO_TX | SPI_NO_RX); - /* - * Nothing prevents from working with active-high CS in case if it - * is driven by GPIO. - */ - if (gpio_is_valid(spi->cs_gpio)) - bad_bits &= ~SPI_CS_HIGH; ugly_bits = bad_bits & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL); @@ -3686,8 +3618,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) * cs_change is set for each transfer. */ if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) || - spi->cs_gpiod || - gpio_is_valid(spi->cs_gpio))) { + spi->cs_gpiod)) { size_t maxsize; int ret; -- cgit From 47b34f495b8b75475952f12c521c4c1fc2fa09b4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 15 Feb 2022 15:51:39 +0200 Subject: spi: intel-pci: Add support for Intel Ice Lake-N SPI serial flash Intel Ice Lake-N has the same SPI serial flash controller as Ice Lake-LP. Add Ice Lake-N PCI ID to the driver list of supported devices. The device can be found on MacBookPro16,2 [1]. [1]: https://linux-hardware.org/?probe=f1c5cf0c43 Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Link: https://lore.kernel.org/r/20220215135139.4328-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-intel-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c index a9cb4d77ffe3..a5ef7a526a7f 100644 --- a/drivers/spi/spi-intel-pci.c +++ b/drivers/spi/spi-intel-pci.c @@ -66,6 +66,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x1bca), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x38a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, -- cgit From 2b993ab79b5dc83eb699e747bfac6c04f4f5fc70 Mon Sep 17 00:00:00 2001 From: André Almeida Date: Wed, 16 Feb 2022 13:27:19 -0300 Subject: spi: amd: Fix building without ACPI enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 209043554915 ("spi: amd: Add support for version AMDI0062") removed the cast ACPI_PTR() for no good reason. This wrapper is important to make sure that the driver can be compiled with or without CONFIG_ACPI enabled, useful for compiling test. Give back the cast so compilation works again. Fixes: 209043554915 ("spi: amd: Add support for version AMDI0062") Signed-off-by: André Almeida Link: https://lore.kernel.org/r/20220216162719.116062-1-andrealmeid@collabora.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 d909afac6e21..cba6a4486c24 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -330,7 +330,7 @@ MODULE_DEVICE_TABLE(acpi, spi_acpi_match); static struct platform_driver amd_spi_driver = { .driver = { .name = "amd_spi", - .acpi_match_table = spi_acpi_match, + .acpi_match_table = ACPI_PTR(spi_acpi_match), }, .probe = amd_spi_probe, }; -- cgit From 54d0fd06e2bd52d3b17648de787157a7c0625adb Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 16 Feb 2022 11:13:17 +0200 Subject: spi: pxa2xx: Add support for Intel Raptor Lake PCH-S Add support for LPSS SPI on Intel Raptor Lake PCH-S. It has four controllers each having two chip selects. Signed-off-by: Jarkko Nikula Link: https://lore.kernel.org/r/20220216091317.1302254-1-jarkko.nikula@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index abb9f0ffd377..edb42d08857d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1394,6 +1394,11 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac6), LPSS_BXT_SSP }, + /* RPL-S */ + { PCI_VDEVICE(INTEL, 0x7a2a), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x7a2b), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x7a79), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x7a7b), LPSS_CNL_SSP }, /* ADL-S */ { PCI_VDEVICE(INTEL, 0x7aaa), LPSS_CNL_SSP }, { PCI_VDEVICE(INTEL, 0x7aab), LPSS_CNL_SSP }, -- cgit From 869f2c94db92f0f1d6acd0dff1c1ebb8160f5e29 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Wed, 16 Feb 2022 09:40:25 +0800 Subject: spi: rockchip: Stop spi slave dma receiver when cs inactive The spi which's version is higher than ver 2 will automatically enable this feature. If the length of master transmission is uncertain, the RK spi slave is better to automatically stop after cs inactive instead of waiting for xfer_completion forever. Signed-off-by: Jon Lin Link: https://lore.kernel.org/r/20220216014028.8123-4-jon.lin@rock-chips.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 81 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index c6a1bb09be05..5ecd0692cca1 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -133,7 +133,8 @@ #define INT_TF_OVERFLOW (1 << 1) #define INT_RF_UNDERFLOW (1 << 2) #define INT_RF_OVERFLOW (1 << 3) -#define INT_RF_FULL (1 << 4) +#define INT_RF_FULL (1 << 4) +#define INT_CS_INACTIVE (1 << 6) /* Bit fields in ICR, 4bit */ #define ICR_MASK 0x0f @@ -194,6 +195,8 @@ struct rockchip_spi { bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; bool slave_abort; + bool cs_inactive; /* spi slave tansmition stop when cs inactive */ + struct spi_transfer *xfer; /* Store xfer temporarily */ }; static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) @@ -343,6 +346,15 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) struct spi_controller *ctlr = dev_id; struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + /* When int_cs_inactive comes, spi slave abort */ + if (rs->cs_inactive && readl_relaxed(rs->regs + ROCKCHIP_SPI_IMR) & INT_CS_INACTIVE) { + ctlr->slave_abort(ctlr); + writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); + writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR); + + return IRQ_HANDLED; + } + if (rs->tx_left) rockchip_spi_pio_writer(rs); @@ -350,6 +362,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); + writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR); spi_finalize_current_transfer(ctlr); } @@ -357,14 +370,18 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) } static int rockchip_spi_prepare_irq(struct rockchip_spi *rs, - struct spi_transfer *xfer) + struct spi_controller *ctlr, + struct spi_transfer *xfer) { rs->tx = xfer->tx_buf; rs->rx = xfer->rx_buf; rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0; rs->rx_left = xfer->len / rs->n_bytes; - writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR); + if (rs->cs_inactive) + writel_relaxed(INT_RF_FULL | INT_CS_INACTIVE, rs->regs + ROCKCHIP_SPI_IMR); + else + writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR); spi_enable_chip(rs, true); if (rs->tx_left) @@ -383,6 +400,9 @@ static void rockchip_spi_dma_rxcb(void *data) if (state & TXDMA && !rs->slave_abort) return; + if (rs->cs_inactive) + writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); + spi_enable_chip(rs, false); spi_finalize_current_transfer(ctlr); } @@ -423,14 +443,16 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, atomic_set(&rs->state, 0); + rs->tx = xfer->tx_buf; + rs->rx = xfer->rx_buf; + rxdesc = NULL; if (xfer->rx_buf) { struct dma_slave_config rxconf = { .direction = DMA_DEV_TO_MEM, .src_addr = rs->dma_addr_rx, .src_addr_width = rs->n_bytes, - .src_maxburst = rockchip_spi_calc_burst_size(xfer->len / - rs->n_bytes), + .src_maxburst = rockchip_spi_calc_burst_size(xfer->len / rs->n_bytes), }; dmaengine_slave_config(ctlr->dma_rx, &rxconf); @@ -474,10 +496,13 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, /* rx must be started before tx due to spi instinct */ if (rxdesc) { atomic_or(RXDMA, &rs->state); - dmaengine_submit(rxdesc); + ctlr->dma_rx->cookie = dmaengine_submit(rxdesc); dma_async_issue_pending(ctlr->dma_rx); } + if (rs->cs_inactive) + writel_relaxed(INT_CS_INACTIVE, rs->regs + ROCKCHIP_SPI_IMR); + spi_enable_chip(rs, true); if (txdesc) { @@ -584,7 +609,42 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi) static int rockchip_spi_slave_abort(struct spi_controller *ctlr) { struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + u32 rx_fifo_left; + struct dma_tx_state state; + enum dma_status status; + + /* Get current dma rx point */ + if (atomic_read(&rs->state) & RXDMA) { + dmaengine_pause(ctlr->dma_rx); + status = dmaengine_tx_status(ctlr->dma_rx, ctlr->dma_rx->cookie, &state); + if (status == DMA_ERROR) { + rs->rx = rs->xfer->rx_buf; + rs->xfer->len = 0; + rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR); + for (; rx_fifo_left; rx_fifo_left--) + readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR); + goto out; + } else { + rs->rx += rs->xfer->len - rs->n_bytes * state.residue; + } + } + /* Get the valid data left in rx fifo and set rs->xfer->len real rx size */ + if (rs->rx) { + rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR); + for (; rx_fifo_left; rx_fifo_left--) { + u32 rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR); + + if (rs->n_bytes == 1) + *(u8 *)rs->rx = (u8)rxw; + else + *(u16 *)rs->rx = (u16)rxw; + rs->rx += rs->n_bytes; + } + rs->xfer->len = (unsigned int)(rs->rx - rs->xfer->rx_buf); + } + +out: if (atomic_read(&rs->state) & RXDMA) dmaengine_terminate_sync(ctlr->dma_rx); if (atomic_read(&rs->state) & TXDMA) @@ -626,7 +686,7 @@ static int rockchip_spi_transfer_one( } rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2; - + rs->xfer = xfer; use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false; ret = rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave); @@ -636,7 +696,7 @@ static int rockchip_spi_transfer_one( if (use_dma) return rockchip_spi_prepare_dma(rs, ctlr, xfer); - return rockchip_spi_prepare_irq(rs, xfer); + return rockchip_spi_prepare_irq(rs, ctlr, xfer); } static bool rockchip_spi_can_dma(struct spi_controller *ctlr, @@ -815,8 +875,13 @@ static int rockchip_spi_probe(struct platform_device *pdev) switch (readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION)) { case ROCKCHIP_SPI_VER2_TYPE2: ctlr->mode_bits |= SPI_CS_HIGH; + if (ctlr->can_dma && slave_mode) + rs->cs_inactive = true; + else + rs->cs_inactive = false; break; default: + rs->cs_inactive = false; break; } -- cgit From 3a4bf922d42efa4e9a3dc803d1fd786d43e8a501 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Wed, 16 Feb 2022 09:40:26 +0800 Subject: spi: rockchip: Preset cs-high and clk polarity in setup progress After power up, the cs and clock is in default status, and the cs-high and clock polarity dts property configuration will take no effect until the calling of rockchip_spi_config in the first transmission. So preset them to make sure a correct voltage before the first transmission coming. Signed-off-by: Jon Lin Link: https://lore.kernel.org/r/20220216014028.8123-5-jon.lin@rock-chips.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 5ecd0692cca1..83da8fdb3c02 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -713,6 +713,29 @@ static bool rockchip_spi_can_dma(struct spi_controller *ctlr, return xfer->len / bytes_per_word >= rs->fifo_len; } +static int rockchip_spi_setup(struct spi_device *spi) +{ + struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller); + u32 cr0; + + pm_runtime_get_sync(rs->dev); + + cr0 = readl_relaxed(rs->regs + ROCKCHIP_SPI_CTRLR0); + + cr0 &= ~(0x3 << CR0_SCPH_OFFSET); + cr0 |= ((spi->mode & 0x3) << CR0_SCPH_OFFSET); + if (spi->mode & SPI_CS_HIGH && spi->chip_select <= 1) + cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET; + else if (spi->chip_select <= 1) + cr0 &= ~(BIT(spi->chip_select) << CR0_SOI_OFFSET); + + writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0); + + pm_runtime_put(rs->dev); + + return 0; +} + static int rockchip_spi_probe(struct platform_device *pdev) { int ret; @@ -840,6 +863,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); + ctlr->setup = rockchip_spi_setup; ctlr->set_cs = rockchip_spi_set_cs; ctlr->transfer_one = rockchip_spi_transfer_one; ctlr->max_transfer_size = rockchip_spi_max_transfer_size; -- cgit From e882575efc771f130a24322377dc1033551da11d Mon Sep 17 00:00:00 2001 From: shengfei Xu Date: Wed, 16 Feb 2022 09:40:27 +0800 Subject: spi: rockchip: Suspend and resume the bus during NOIRQ_SYSTEM_SLEEP_PM ops the wakeup interrupt handler which is guaranteed not to run while @resume noirq() is being executed. the patch can help to avoid the wakeup source try to access spi when the spi is in suspend mode. Signed-off-by: shengfei Xu Signed-off-by: Jon Lin Link: https://lore.kernel.org/r/20220216014028.8123-6-jon.lin@rock-chips.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 83da8fdb3c02..8b4d56ee2193 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -964,14 +964,14 @@ static int rockchip_spi_suspend(struct device *dev) { int ret; struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); ret = spi_controller_suspend(ctlr); if (ret < 0) return ret; - ret = pm_runtime_force_suspend(dev); - if (ret < 0) - return ret; + clk_disable_unprepare(rs->spiclk); + clk_disable_unprepare(rs->apb_pclk); pinctrl_pm_select_sleep_state(dev); @@ -986,10 +986,14 @@ static int rockchip_spi_resume(struct device *dev) pinctrl_pm_select_default_state(dev); - ret = pm_runtime_force_resume(dev); + ret = clk_prepare_enable(rs->apb_pclk); if (ret < 0) return ret; + ret = clk_prepare_enable(rs->spiclk); + if (ret < 0) + clk_disable_unprepare(rs->apb_pclk); + ret = spi_controller_resume(ctlr); if (ret < 0) { clk_disable_unprepare(rs->spiclk); @@ -1031,7 +1035,7 @@ static int rockchip_spi_runtime_resume(struct device *dev) #endif /* CONFIG_PM */ static const struct dev_pm_ops rockchip_spi_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume) SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend, rockchip_spi_runtime_resume, NULL) }; -- cgit From 2fcdde56c44fe1cd13ce328128f509bbda2cdb41 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Wed, 16 Feb 2022 09:40:28 +0800 Subject: spi: rockchip: clear interrupt status in error handler The interrupt status bit of the previous error data transmition will affect the next operation and cause continuous SPI transmission failure. Signed-off-by: Jon Lin Link: https://lore.kernel.org/r/20220216014028.8123-7-jon.lin@rock-chips.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 8b4d56ee2193..cdc16eecaf6b 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -278,8 +278,9 @@ static void rockchip_spi_handle_err(struct spi_controller *ctlr, */ spi_enable_chip(rs, false); - /* make sure all interrupts are masked */ + /* make sure all interrupts are masked and status cleared */ writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); + writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR); if (atomic_read(&rs->state) & TXDMA) dmaengine_terminate_async(ctlr->dma_tx); -- cgit From 07025ceaac9f4f7a9e1a3285c3216469bf066320 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Thu, 17 Feb 2022 09:00:24 +0800 Subject: spi: clean up some inconsistent indenting Eliminate the follow smatch warning: drivers/spi/spi-sunplus-sp7021.c:379 sp7021_spi_slave_transfer_one() warn: inconsistent indenting Reported-by: Abaci Robot Signed-off-by: Yang Li Link: https://lore.kernel.org/r/20220217010024.111904-1-yang.lee@linux.alibaba.com Signed-off-by: Mark Brown --- drivers/spi/spi-sunplus-sp7021.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c index ade7a0fca8cb..f989f7b99296 100644 --- a/drivers/spi/spi-sunplus-sp7021.c +++ b/drivers/spi/spi-sunplus-sp7021.c @@ -376,8 +376,8 @@ static int sp7021_spi_slave_transfer_one(struct spi_controller *ctlr, struct spi xfer->len, DMA_TO_DEVICE); if (dma_mapping_error(dev, xfer->tx_dma)) return -ENOMEM; - ret = sp7021_spi_slave_tx(spi, xfer); - dma_unmap_single(dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE); + ret = sp7021_spi_slave_tx(spi, xfer); + dma_unmap_single(dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE); } else if (xfer->rx_buf && !xfer->tx_buf) { xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); -- cgit From 043786303b175977e515d4e99cf6b5f886b136dc Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 18 Feb 2022 14:58:35 +0100 Subject: spi: use sysfs_emit() for printing statistics and add trailing newline Use dedicated function sysfs_emit() that does some extra checking, e.g. to ensure that no more than PAGESIZE bytes are written. In addition add a trailing newline to the output, that makes it better readable from the console. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/56e1588d-d53b-73e9-fdc8-7fe30bf91f11@gmail.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 85f8ae4cc0c0..cd4dc3131e17 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -143,7 +143,7 @@ static ssize_t spi_statistics_##name##_show(struct spi_statistics *stat, \ unsigned long flags; \ ssize_t len; \ spin_lock_irqsave(&stat->lock, flags); \ - len = sprintf(buf, format_string, stat->field); \ + len = sysfs_emit(buf, format_string "\n", stat->field); \ spin_unlock_irqrestore(&stat->lock, flags); \ return len; \ } \ -- cgit From c5a3106aa4923bec979c2a76667a493cb5d134fd Mon Sep 17 00:00:00 2001 From: "Minghao Chi (CGEL ZTE)" Date: Mon, 21 Feb 2022 02:02:33 +0000 Subject: spi: Use of_device_get_match_data() Use of_device_get_match_data() to simplify the code. Reported-by: Zeal Robot Signed-off-by: Minghao Chi (CGEL ZTE) Link: https://lore.kernel.org/r/20220221020233.1925154-1-chi.minghao@zte.com.cn Signed-off-by: Mark Brown --- drivers/spi/spi-lantiq-ssc.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c index bcb52601804a..aae26f62ea87 100644 --- a/drivers/spi/spi-lantiq-ssc.c +++ b/drivers/spi/spi-lantiq-ssc.c @@ -906,17 +906,11 @@ static int lantiq_ssc_probe(struct platform_device *pdev) struct spi_master *master; struct lantiq_ssc_spi *spi; const struct lantiq_ssc_hwcfg *hwcfg; - const struct of_device_id *match; u32 id, supports_dma, revision; unsigned int num_cs; int err; - match = of_match_device(lantiq_ssc_match, dev); - if (!match) { - dev_err(dev, "no device match\n"); - return -EINVAL; - } - hwcfg = match->data; + hwcfg = of_device_get_match_data(dev); master = spi_alloc_master(dev, sizeof(struct lantiq_ssc_spi)); if (!master) -- cgit From 5741150c808b2bbeb1017609f3029daf6651b7d5 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Tue, 1 Feb 2022 12:51:41 +0100 Subject: spi: stm32: ignore Rx queue not empty in stm32f4 Tx only mode STM32F4_SPI_SR_RXNE and STM32F4_SPI_SR_OVR are distinct bits in the same status register. ~STM32F4_SPI_SR_OVR | STM32F4_SPI_SR_RXNE is thus equal to ~STM32F4_SPI_SR_OVR. The original intention was likely for transmission-only transfers to ignore interrupts both for when the Rx queue has bytes (RXNE) as well as when these bytes haven't been read in time (OVR). Fix the typo by adding the missing parenthesis. Signed-off-by: Ahmad Fatoum Link: https://lore.kernel.org/r/20220201115142.3999860-1-a.fatoum@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 7fc24505a72c..a6adc20f6862 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -763,7 +763,7 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id) if (!spi->cur_usedma && (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)) { /* OVR flag shouldn't be handled for TX only mode */ - sr &= ~STM32F4_SPI_SR_OVR | STM32F4_SPI_SR_RXNE; + sr &= ~(STM32F4_SPI_SR_OVR | STM32F4_SPI_SR_RXNE); mask |= STM32F4_SPI_SR_TXE; } -- cgit From 1847e3046c528bd85bd51e2860f4139bd9052d6c Mon Sep 17 00:00:00 2001 From: Andreas Färber Date: Sat, 19 Feb 2022 14:15:48 +0100 Subject: spi: gpio: Implement LSB First bitbang support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for slave DT property spi-lsb-first, i.e., SPI_LSB_FIRST mode. Duplicate the inline helpers bitbang_txrx_be_cpha{0,1} as LE versions. Conditionally call them from all the spi-gpio txrx_word callbacks. Some alternatives to this implementation approach were discussed back then [0], but eventually it was considered reasonable. [0] https://lore.kernel.org/linux-arm-kernel/20191212033952.5967-8-afaerber@suse.de/ Signed-off-by: Andreas Färber Signed-off-by: Heiner Kallweit Tested-by: Christian Hewitt Link: https://lore.kernel.org/r/feac3377-4ad1-77d8-9a18-3588d80fb909@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-bitbang-txrx.h | 66 ++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spi-gpio.c | 42 +++++++++++++++++++++------ 2 files changed, 99 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bitbang-txrx.h b/drivers/spi/spi-bitbang-txrx.h index ae61d72c7d28..267342dfa738 100644 --- a/drivers/spi/spi-bitbang-txrx.h +++ b/drivers/spi/spi-bitbang-txrx.h @@ -41,6 +41,8 @@ * chips need ... there may be several reasons you'd need to tweak timings * in these routines, not just to make it faster or slower to match a * particular CPU clock rate. + * + * ToDo: Maybe the bitrev macros can be used to improve the code? */ static inline u32 @@ -106,3 +108,67 @@ bitbang_txrx_be_cpha1(struct spi_device *spi, } return word; } + +static inline u32 +bitbang_txrx_le_cpha0(struct spi_device *spi, + unsigned int nsecs, unsigned int cpol, unsigned int flags, + u32 word, u8 bits) +{ + /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */ + + u32 oldbit = !(word & 1); + /* clock starts at inactive polarity */ + for (; likely(bits); bits--) { + + /* setup LSB (to slave) on trailing edge */ + if ((flags & SPI_MASTER_NO_TX) == 0) { + if ((word & 1) != oldbit) { + setmosi(spi, word & 1); + oldbit = word & 1; + } + } + spidelay(nsecs); /* T(setup) */ + + setsck(spi, !cpol); + spidelay(nsecs); + + /* sample LSB (from slave) on leading edge */ + word >>= 1; + if ((flags & SPI_MASTER_NO_RX) == 0) + word |= getmiso(spi) << (bits - 1); + setsck(spi, cpol); + } + return word; +} + +static inline u32 +bitbang_txrx_le_cpha1(struct spi_device *spi, + unsigned int nsecs, unsigned int cpol, unsigned int flags, + u32 word, u8 bits) +{ + /* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */ + + u32 oldbit = !(word & 1); + /* clock starts at inactive polarity */ + for (; likely(bits); bits--) { + + /* setup LSB (to slave) on leading edge */ + setsck(spi, !cpol); + if ((flags & SPI_MASTER_NO_TX) == 0) { + if ((word & 1) != oldbit) { + setmosi(spi, word & 1); + oldbit = word & 1; + } + } + spidelay(nsecs); /* T(setup) */ + + setsck(spi, cpol); + spidelay(nsecs); + + /* sample LSB (from slave) on trailing edge */ + word >>= 1; + if ((flags & SPI_MASTER_NO_RX) == 0) + word |= getmiso(spi) << (bits - 1); + } + return word; +} diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 0584f4d2fde2..4b12c4964a66 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -135,25 +135,37 @@ static inline int getmiso(const struct spi_device *spi) static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits); + else + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits); + else + return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits); + else + return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits); + else + return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } /* @@ -170,28 +182,40 @@ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { flags = spi->master->flags; - return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha0(spi, nsecs, 0, flags, word, bits); + else + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { flags = spi->master->flags; - return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha1(spi, nsecs, 0, flags, word, bits); + else + return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { flags = spi->master->flags; - return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha0(spi, nsecs, 1, flags, word, bits); + else + return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags) { flags = spi->master->flags; - return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); + if (unlikely(spi->mode & SPI_LSB_FIRST)) + return bitbang_txrx_le_cpha1(spi, nsecs, 1, flags, word, bits); + else + return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } /*----------------------------------------------------------------------*/ @@ -378,7 +402,7 @@ static int spi_gpio_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL | - SPI_CS_HIGH; + SPI_CS_HIGH | SPI_LSB_FIRST; if (!spi_gpio->mosi) { /* HW configuration without MOSI pin * -- cgit From 4d986ffa036a773456476f70bd0fde2fb1330b7d Mon Sep 17 00:00:00 2001 From: Wang Qing Date: Mon, 14 Feb 2022 18:00:07 -0800 Subject: spi: add missing pci_dev_put() before return pci_get_slot() increases its reference count, the caller must decrement the reference count by calling pci_dev_put() Signed-off-by: Wang Qing Link: https://lore.kernel.org/r/1644890407-65167-1-git-send-email-wangqing@vivo.com Signed-off-by: Mark Brown --- drivers/spi/spi-topcliff-pch.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 8c4615b76339..8e1cc345810a 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -877,7 +877,7 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw) dev_err(&data->master->dev, "ERROR: dma_request_channel FAILS(Tx)\n"); data->use_dma = 0; - return; + goto out; } dma->chan_tx = chan; @@ -894,9 +894,12 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw) dma_release_channel(dma->chan_tx); dma->chan_tx = NULL; data->use_dma = 0; - return; + goto out; } dma->chan_rx = chan; + +out: + pci_dev_put(dma_dev); } static void pch_spi_release_dma(struct pch_spi_data *data) -- cgit From a586f944f3a30cfffdbda081aa094bc6845f5ba9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 23 Feb 2022 21:19:48 +0200 Subject: spi: pxa2xx-pci: Do not dereference fwnode in struct device In order to make the underneath API easier to change in the future, prevent users from dereferencing fwnode from struct device. Instead, use the specific dev_fwnode() API for that. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220223191948.31325-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 2e134eb4bd2c..47c8cb56a4d0 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -261,7 +261,7 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, return PTR_ERR(ssp->clk); memset(&pi, 0, sizeof(pi)); - pi.fwnode = dev->dev.fwnode; + pi.fwnode = dev_fwnode(&dev->dev); pi.parent = &dev->dev; pi.name = "pxa2xx-spi"; pi.id = ssp->port_id; -- cgit From 609d7ffdc42199a0ec949db057e3b4be6745d6c5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 23 Feb 2022 21:16:37 +0200 Subject: spi: pxa2xx-pci: Balance reference count for PCI DMA device The pci_get_slot() increases its reference count, the caller must decrement the reference count by calling pci_dev_put(). Fixes: 743485ea3bee ("spi: pxa2xx-pci: Do a specific setup in a separate function") Fixes: 25014521603f ("spi: pxa2xx-pci: Enable DMA for Intel Merrifield") Reported-by: Wang Qing Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220223191637.31147-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 47c8cb56a4d0..6d60972e4e20 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -76,14 +76,23 @@ static bool lpss_dma_filter(struct dma_chan *chan, void *param) return true; } +static void lpss_dma_put_device(void *dma_dev) +{ + pci_dev_put(dma_dev); +} + static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { struct pci_dev *dma_dev; + int ret; c->num_chipselect = 1; c->max_clk_rate = 50000000; dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + ret = devm_add_action_or_reset(&dev->dev, lpss_dma_put_device, dma_dev); + if (ret) + return ret; if (c->tx_param) { struct dw_dma_slave *slave = c->tx_param; @@ -107,8 +116,9 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { - struct pci_dev *dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0)); struct dw_dma_slave *tx, *rx; + struct pci_dev *dma_dev; + int ret; switch (PCI_FUNC(dev->devfn)) { case 0: @@ -133,6 +143,11 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return -ENODEV; } + dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0)); + ret = devm_add_action_or_reset(&dev->dev, lpss_dma_put_device, dma_dev); + if (ret) + return ret; + tx = c->tx_param; tx->dma_dev = &dma_dev->dev; -- cgit From ac982578e7d340dc4f4fd243f4a4b24787d28c3f Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Tue, 22 Feb 2022 23:26:07 +0530 Subject: spi: tegra210-quad: use device_reset method Use device_reset api to replace duplicate code in driver to call reset_control_get api with reset handle. Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/20220222175611.58051-2-kyarlagadda@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index cb00ac2fc7d8..a353f2a9abd4 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -137,7 +137,6 @@ struct tegra_qspi { spinlock_t lock; struct clk *clk; - struct reset_control *rst; void __iomem *base; phys_addr_t phys; unsigned int irq; @@ -948,9 +947,8 @@ static void tegra_qspi_handle_error(struct tegra_qspi *tqspi) dev_err(tqspi->dev, "error in transfer, fifo status 0x%08x\n", tqspi->status_reg); tegra_qspi_dump_regs(tqspi); tegra_qspi_flush_fifos(tqspi, true); - reset_control_assert(tqspi->rst); - udelay(2); - reset_control_deassert(tqspi->rst); + if (device_reset(tqspi->dev) < 0) + dev_warn_once(tqspi->dev, "device reset failed\n"); } static void tegra_qspi_transfer_end(struct spi_device *spi) @@ -1251,13 +1249,6 @@ static int tegra_qspi_probe(struct platform_device *pdev) return ret; } - tqspi->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(tqspi->rst)) { - ret = PTR_ERR(tqspi->rst); - dev_err(&pdev->dev, "failed to get reset control: %d\n", ret); - return ret; - } - tqspi->max_buf_size = QSPI_FIFO_DEPTH << 2; tqspi->dma_buf_size = DEFAULT_QSPI_DMA_BUF_LEN; @@ -1279,9 +1270,8 @@ static int tegra_qspi_probe(struct platform_device *pdev) goto exit_pm_disable; } - reset_control_assert(tqspi->rst); - udelay(2); - reset_control_deassert(tqspi->rst); + if (device_reset(tqspi->dev) < 0) + dev_warn_once(tqspi->dev, "device reset failed\n"); tqspi->def_command1_reg = QSPI_M_S | QSPI_CS_SW_HW | QSPI_CS_SW_VAL; tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1); -- cgit From ea23f0e148b82e5bcbc6c814926f53133552f0f3 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Tue, 22 Feb 2022 23:26:09 +0530 Subject: spi: tegra210-quad: add new chips to compatible Add support for Tegra234 and soc data to select capabilities. Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/20220222175611.58051-4-kyarlagadda@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index a353f2a9abd4..3725ee5331ae 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -125,6 +125,10 @@ #define QSPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) #define DEFAULT_QSPI_DMA_BUF_LEN (64 * 1024) +struct tegra_qspi_soc_data { + bool has_dma; +}; + struct tegra_qspi_client_data { int tx_clk_tap_delay; int rx_clk_tap_delay; @@ -184,6 +188,7 @@ struct tegra_qspi { u32 *tx_dma_buf; dma_addr_t tx_dma_phys; struct dma_async_tx_descriptor *tx_dma_desc; + const struct tegra_qspi_soc_data *soc_data; }; static inline u32 tegra_qspi_readl(struct tegra_qspi *tqspi, unsigned long offset) @@ -1191,10 +1196,32 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data) return handle_dma_based_xfer(tqspi); } +static struct tegra_qspi_soc_data tegra210_qspi_soc_data = { + .has_dma = true, +}; + +static struct tegra_qspi_soc_data tegra186_qspi_soc_data = { + .has_dma = true, +}; + +static struct tegra_qspi_soc_data tegra234_qspi_soc_data = { + .has_dma = false, +}; + static const struct of_device_id tegra_qspi_of_match[] = { - { .compatible = "nvidia,tegra210-qspi", }, - { .compatible = "nvidia,tegra186-qspi", }, - { .compatible = "nvidia,tegra194-qspi", }, + { + .compatible = "nvidia,tegra210-qspi", + .data = &tegra210_qspi_soc_data, + }, { + .compatible = "nvidia,tegra186-qspi", + .data = &tegra186_qspi_soc_data, + }, { + .compatible = "nvidia,tegra194-qspi", + .data = &tegra186_qspi_soc_data, + }, { + .compatible = "nvidia,tegra234-qspi", + .data = &tegra234_qspi_soc_data, + }, {} }; -- cgit From 78e27f970f73a4ee57dc050a6233e09a56963391 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:40 +0200 Subject: spi: pxa2xx-pci: Refactor CE4100 to use ->setup() Refactor CE4100 handling code to use ->setup() instead of spreading potentially confusing conditional. Besides that, it will allow to refactor further to avoid intermediate storage for the used configuration parameters. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 6d60972e4e20..bd20379d9342 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -30,7 +30,7 @@ enum { struct pxa_spi_info { enum pxa_ssp_type type; int port_id; - int num_chipselect; + unsigned int num_chipselect; unsigned long max_clk_rate; /* DMA channel request parameters */ @@ -114,6 +114,14 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return 0; } +static int ce4100_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +{ + c->num_chipselect = dev->devfn; + c->max_clk_rate = 3686400; + + return 0; +} + static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { struct dw_dma_slave *tx, *rx; @@ -163,8 +171,7 @@ static struct pxa_spi_info spi_info_configs[] = { [PORT_CE4100] = { .type = PXA25x_SSP, .port_id = -1, - .num_chipselect = -1, - .max_clk_rate = 3686400, + .setup = ce4100_spi_setup, }, [PORT_BYT] = { .type = LPSS_BYT_SSP, @@ -248,7 +255,7 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, } memset(&spi_pdata, 0, sizeof(spi_pdata)); - spi_pdata.num_chipselect = (c->num_chipselect > 0) ? c->num_chipselect : dev->devfn; + spi_pdata.num_chipselect = c->num_chipselect; spi_pdata.dma_filter = c->dma_filter; spi_pdata.tx_param = c->tx_param; spi_pdata.rx_param = c->rx_param; -- cgit From 71ea0e3ac70a50b0c56105e116ed903f8e504e8f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:41 +0200 Subject: spi: pxa2xx-pci: Refactor Quark X1000 to use ->setup() Refactor Quark X1000 handling code to use ->setup() instead of using the configuration data structure directly. It will allow to refactor further to avoid intermediate storage for the used configuration parameters. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index bd20379d9342..4d617ad72bca 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -167,6 +167,14 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return 0; } +static int qrk_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +{ + c->num_chipselect = 1; + c->max_clk_rate = 50000000; + + return 0; +} + static struct pxa_spi_info spi_info_configs[] = { [PORT_CE4100] = { .type = PXA25x_SSP, @@ -209,8 +217,7 @@ static struct pxa_spi_info spi_info_configs[] = { [PORT_QUARK_X1000] = { .type = QUARK_X1000_SSP, .port_id = -1, - .num_chipselect = 1, - .max_clk_rate = 50000000, + .setup = qrk_spi_setup, }, [PORT_LPT0] = { .type = LPSS_LPT_SSP, -- cgit From 1d9d62959f1b52eb939df38b9fda8beea455c751 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:42 +0200 Subject: spi: pxa2xx-pci: Drop redundant NULL check in ->probe() Since all platforms are using ->setup() function, drop unneeded check. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-3-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 4d617ad72bca..90b95e49a164 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -255,11 +255,9 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, return ret; c = &spi_info_configs[ent->driver_data]; - if (c->setup) { - ret = c->setup(dev, c); - if (ret) - return ret; - } + ret = c->setup(dev, c); + if (ret) + return ret; memset(&spi_pdata, 0, sizeof(spi_pdata)); spi_pdata.num_chipselect = c->num_chipselect; -- cgit From 108607ce4e39a51caca51aa97c44c31041a597d1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:43 +0200 Subject: spi: pxa2xx-pci: Move port_id assignment to ->setup() Instead of using conditional, move port_id to the corresponding ->setup() functions. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-4-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 90b95e49a164..87629da3e544 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -29,7 +29,7 @@ enum { struct pxa_spi_info { enum pxa_ssp_type type; - int port_id; + unsigned int port_id; unsigned int num_chipselect; unsigned long max_clk_rate; @@ -116,6 +116,7 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) static int ce4100_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { + c->port_id = dev->devfn; c->num_chipselect = dev->devfn; c->max_clk_rate = 3686400; @@ -169,6 +170,7 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) static int qrk_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { + c->port_id = dev->devfn; c->num_chipselect = 1; c->max_clk_rate = 50000000; @@ -178,7 +180,6 @@ static int qrk_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) static struct pxa_spi_info spi_info_configs[] = { [PORT_CE4100] = { .type = PXA25x_SSP, - .port_id = -1, .setup = ce4100_spi_setup, }, [PORT_BYT] = { @@ -216,7 +217,6 @@ static struct pxa_spi_info spi_info_configs[] = { }, [PORT_QUARK_X1000] = { .type = QUARK_X1000_SSP, - .port_id = -1, .setup = qrk_spi_setup, }, [PORT_LPT0] = { @@ -271,8 +271,8 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, ssp->dev = &dev->dev; ssp->phys_base = pci_resource_start(dev, 0); ssp->mmio_base = pcim_iomap_table(dev)[0]; - ssp->port_id = (c->port_id >= 0) ? c->port_id : dev->devfn; ssp->type = c->type; + ssp->port_id = c->port_id; pci_set_master(dev); -- cgit From bd2e24de10da015147b02f8c2c4b8ebea8fa9574 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:44 +0200 Subject: spi: pxa2xx-pci: Move dma_burst_size assignment to ->setup() Instead of using conditional, move dma_burst_size to the corresponding ->setup() function. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-5-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 87629da3e544..c2cbb002784a 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -38,7 +38,7 @@ struct pxa_spi_info { void *tx_param; void *rx_param; - int dma_burst_size; + unsigned int dma_burst_size; int (*setup)(struct pci_dev *pdev, struct pxa_spi_info *c); }; @@ -111,6 +111,7 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) } c->dma_filter = lpss_dma_filter; + c->dma_burst_size = 1; return 0; } @@ -265,7 +266,7 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, spi_pdata.tx_param = c->tx_param; spi_pdata.rx_param = c->rx_param; spi_pdata.enable_dma = c->rx_param && c->tx_param; - spi_pdata.dma_burst_size = c->dma_burst_size ? c->dma_burst_size : 1; + spi_pdata.dma_burst_size = c->dma_burst_size; ssp = &spi_pdata.ssp; ssp->dev = &dev->dev; -- cgit From 03f8e04e9f9be8d28c52ae801f37d49988f02ce4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:45 +0200 Subject: spi: pxa2xx-pci: Move max_clk_rate assignment to ->setup() Move max_clk_rate to the corresponding ->setup() function to unify with the rest. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-6-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index c2cbb002784a..5ac1487c9b3f 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -153,6 +153,8 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return -ENODEV; } + c->max_clk_rate = 25000000; + dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0)); ret = devm_add_action_or_reset(&dev->dev, lpss_dma_put_device, dma_dev); if (ret) @@ -213,7 +215,6 @@ static struct pxa_spi_info spi_info_configs[] = { }, [PORT_MRFLD] = { .type = MRFLD_SSP, - .max_clk_rate = 25000000, .setup = mrfld_spi_setup, }, [PORT_QUARK_X1000] = { -- cgit From 7e425c3c3d15241aa5b6c442a83f11b8bc4fee91 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:46 +0200 Subject: spi: pxa2xx-pci: Replace enum with direct use of PCI IDs Instead of creating an abstraction on top of PCI IDs, just use them directly. The corresponding enum can be dropped. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-7-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 161 ++++++++++++++++++++++--------------------- 1 file changed, 83 insertions(+), 78 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 5ac1487c9b3f..a0f24e811e9f 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -15,17 +15,17 @@ #include #include -enum { - PORT_QUARK_X1000, - PORT_BYT, - PORT_MRFLD, - PORT_BSW0, - PORT_BSW1, - PORT_BSW2, - PORT_CE4100, - PORT_LPT0, - PORT_LPT1, -}; +#define PCI_DEVICE_ID_INTEL_QUARK_X1000 0x0935 +#define PCI_DEVICE_ID_INTEL_BYT 0x0f0e +#define PCI_DEVICE_ID_INTEL_MRFLD 0x1194 +#define PCI_DEVICE_ID_INTEL_BSW0 0x228e +#define PCI_DEVICE_ID_INTEL_BSW1 0x2290 +#define PCI_DEVICE_ID_INTEL_BSW2 0x22ac +#define PCI_DEVICE_ID_INTEL_CE4100 0x2e6a +#define PCI_DEVICE_ID_INTEL_LPT0_0 0x9c65 +#define PCI_DEVICE_ID_INTEL_LPT0_1 0x9c66 +#define PCI_DEVICE_ID_INTEL_LPT1_0 0x9ce5 +#define PCI_DEVICE_ID_INTEL_LPT1_1 0x9ce6 struct pxa_spi_info { enum pxa_ssp_type type; @@ -86,6 +86,49 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) struct pci_dev *dma_dev; int ret; + switch (dev->device) { + case PCI_DEVICE_ID_INTEL_BYT: + c->type = LPSS_BYT_SSP; + c->port_id = 0; + c->tx_param = &byt_tx_param; + c->rx_param = &byt_rx_param; + break; + case PCI_DEVICE_ID_INTEL_BSW0: + c->type = LPSS_BSW_SSP; + c->port_id = 0; + c->tx_param = &bsw0_tx_param; + c->rx_param = &bsw0_rx_param; + break; + case PCI_DEVICE_ID_INTEL_BSW1: + c->type = LPSS_BSW_SSP; + c->port_id = 1; + c->tx_param = &bsw1_tx_param; + c->rx_param = &bsw1_rx_param; + break; + case PCI_DEVICE_ID_INTEL_BSW2: + c->type = LPSS_BSW_SSP; + c->port_id = 2; + c->tx_param = &bsw2_tx_param; + c->rx_param = &bsw2_rx_param; + break; + case PCI_DEVICE_ID_INTEL_LPT0_0: + case PCI_DEVICE_ID_INTEL_LPT1_0: + c->type = LPSS_LPT_SSP; + c->port_id = 0; + c->tx_param = &lpt0_tx_param; + c->rx_param = &lpt0_rx_param; + break; + case PCI_DEVICE_ID_INTEL_LPT0_1: + case PCI_DEVICE_ID_INTEL_LPT1_1: + c->type = LPSS_LPT_SSP; + c->port_id = 1; + c->tx_param = &lpt1_tx_param; + c->rx_param = &lpt1_rx_param; + break; + default: + return -ENODEV; + } + c->num_chipselect = 1; c->max_clk_rate = 50000000; @@ -115,8 +158,13 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return 0; } +static struct pxa_spi_info lpss_info_config = { + .setup = lpss_spi_setup, +}; + static int ce4100_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { + c->type = PXA25x_SSP; c->port_id = dev->devfn; c->num_chipselect = dev->devfn; c->max_clk_rate = 3686400; @@ -124,6 +172,10 @@ static int ce4100_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return 0; } +static struct pxa_spi_info ce4100_info_config = { + .setup = ce4100_spi_setup, +}; + static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { struct dw_dma_slave *tx, *rx; @@ -153,6 +205,7 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return -ENODEV; } + c->type = MRFLD_SSP; c->max_clk_rate = 25000000; dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0)); @@ -171,8 +224,13 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return 0; } +static struct pxa_spi_info mrfld_info_config = { + .setup = mrfld_spi_setup, +}; + static int qrk_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { + c->type = QUARK_X1000_SSP; c->port_id = dev->devfn; c->num_chipselect = 1; c->max_clk_rate = 50000000; @@ -180,61 +238,8 @@ static int qrk_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return 0; } -static struct pxa_spi_info spi_info_configs[] = { - [PORT_CE4100] = { - .type = PXA25x_SSP, - .setup = ce4100_spi_setup, - }, - [PORT_BYT] = { - .type = LPSS_BYT_SSP, - .port_id = 0, - .setup = lpss_spi_setup, - .tx_param = &byt_tx_param, - .rx_param = &byt_rx_param, - }, - [PORT_BSW0] = { - .type = LPSS_BSW_SSP, - .port_id = 0, - .setup = lpss_spi_setup, - .tx_param = &bsw0_tx_param, - .rx_param = &bsw0_rx_param, - }, - [PORT_BSW1] = { - .type = LPSS_BSW_SSP, - .port_id = 1, - .setup = lpss_spi_setup, - .tx_param = &bsw1_tx_param, - .rx_param = &bsw1_rx_param, - }, - [PORT_BSW2] = { - .type = LPSS_BSW_SSP, - .port_id = 2, - .setup = lpss_spi_setup, - .tx_param = &bsw2_tx_param, - .rx_param = &bsw2_rx_param, - }, - [PORT_MRFLD] = { - .type = MRFLD_SSP, - .setup = mrfld_spi_setup, - }, - [PORT_QUARK_X1000] = { - .type = QUARK_X1000_SSP, - .setup = qrk_spi_setup, - }, - [PORT_LPT0] = { - .type = LPSS_LPT_SSP, - .port_id = 0, - .setup = lpss_spi_setup, - .tx_param = &lpt0_tx_param, - .rx_param = &lpt0_rx_param, - }, - [PORT_LPT1] = { - .type = LPSS_LPT_SSP, - .port_id = 1, - .setup = lpss_spi_setup, - .tx_param = &lpt1_tx_param, - .rx_param = &lpt1_rx_param, - }, +static struct pxa_spi_info qrk_info_config = { + .setup = qrk_spi_setup, }; static int pxa2xx_spi_pci_probe(struct pci_dev *dev, @@ -256,7 +261,7 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, if (ret) return ret; - c = &spi_info_configs[ent->driver_data]; + c = (struct pxa_spi_info *)ent->driver_data; ret = c->setup(dev, c); if (ret) return ret; @@ -320,17 +325,17 @@ static void pxa2xx_spi_pci_remove(struct pci_dev *dev) } static const struct pci_device_id pxa2xx_spi_pci_devices[] = { - { PCI_VDEVICE(INTEL, 0x0935), PORT_QUARK_X1000 }, - { PCI_VDEVICE(INTEL, 0x0f0e), PORT_BYT }, - { PCI_VDEVICE(INTEL, 0x1194), PORT_MRFLD }, - { PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 }, - { PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 }, - { PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 }, - { PCI_VDEVICE(INTEL, 0x2e6a), PORT_CE4100 }, - { PCI_VDEVICE(INTEL, 0x9c65), PORT_LPT0 }, - { PCI_VDEVICE(INTEL, 0x9c66), PORT_LPT1 }, - { PCI_VDEVICE(INTEL, 0x9ce5), PORT_LPT0 }, - { PCI_VDEVICE(INTEL, 0x9ce6), PORT_LPT1 }, + { PCI_DEVICE_DATA(INTEL, QUARK_X1000, &qrk_info_config) }, + { PCI_DEVICE_DATA(INTEL, BYT, &lpss_info_config) }, + { PCI_DEVICE_DATA(INTEL, MRFLD, &mrfld_info_config) }, + { PCI_DEVICE_DATA(INTEL, BSW0, &lpss_info_config) }, + { PCI_DEVICE_DATA(INTEL, BSW1, &lpss_info_config) }, + { PCI_DEVICE_DATA(INTEL, BSW2, &lpss_info_config) }, + { PCI_DEVICE_DATA(INTEL, CE4100, &ce4100_info_config) }, + { PCI_DEVICE_DATA(INTEL, LPT0_0, &lpss_info_config) }, + { PCI_DEVICE_DATA(INTEL, LPT0_1, &lpss_info_config) }, + { PCI_DEVICE_DATA(INTEL, LPT1_0, &lpss_info_config) }, + { PCI_DEVICE_DATA(INTEL, LPT1_1, &lpss_info_config) }, { } }; MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices); -- cgit From cb50f3f32a044ea45192a43e756b26048d35ba95 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:47 +0200 Subject: spi: pxa2xx-pci: Drop unneeded checks in lpss_spi_setup() All of the LPSS devices are using DMA and set the parameters up, hence no need to test for that. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-8-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index a0f24e811e9f..c041a9288d0c 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -83,6 +83,7 @@ static void lpss_dma_put_device(void *dma_dev) static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) { + struct dw_dma_slave *tx, *rx; struct pci_dev *dma_dev; int ret; @@ -137,21 +138,15 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) if (ret) return ret; - if (c->tx_param) { - struct dw_dma_slave *slave = c->tx_param; - - slave->dma_dev = &dma_dev->dev; - slave->m_master = 0; - slave->p_master = 1; - } - - if (c->rx_param) { - struct dw_dma_slave *slave = c->rx_param; + tx = c->tx_param; + tx->dma_dev = &dma_dev->dev; + tx->m_master = 0; + tx->p_master = 1; - slave->dma_dev = &dma_dev->dev; - slave->m_master = 0; - slave->p_master = 1; - } + rx = c->rx_param; + rx->dma_dev = &dma_dev->dev; + rx->m_master = 0; + rx->p_master = 1; c->dma_filter = lpss_dma_filter; c->dma_burst_size = 1; -- cgit From c3f4fc096b37bc2e4535f16ac3d65d517bbc14eb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:48 +0200 Subject: spi: pxa2xx-pci: Extract pxa2xx_spi_pci_clk_register() Extract pxa2xx_spi_pci_clk_register() from ->probe() in order to reuse it later on for getting rid of max_clk_rate temporary storage. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-9-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index c041a9288d0c..2dbe08034ad0 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -65,6 +65,24 @@ static struct dw_dma_slave lpt1_rx_param = { .src_id = 1 }; static struct dw_dma_slave lpt0_tx_param = { .dst_id = 2 }; static struct dw_dma_slave lpt0_rx_param = { .src_id = 3 }; +static void pxa2xx_spi_pci_clk_unregister(void *clk) +{ + clk_unregister(clk); +} + +static int pxa2xx_spi_pci_clk_register(struct pci_dev *dev, struct ssp_device *ssp, + unsigned long rate) +{ + char buf[40]; + + snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id); + ssp->clk = clk_register_fixed_rate(&dev->dev, buf, NULL, 0, rate); + if (IS_ERR(ssp->clk)) + return PTR_ERR(ssp->clk); + + return devm_add_action_or_reset(&dev->dev, pxa2xx_spi_pci_clk_unregister, ssp->clk); +} + static bool lpss_dma_filter(struct dma_chan *chan, void *param) { struct dw_dma_slave *dws = param; @@ -246,7 +264,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, struct pxa2xx_spi_controller spi_pdata; struct ssp_device *ssp; struct pxa_spi_info *c; - char buf[40]; ret = pcim_enable_device(dev); if (ret) @@ -283,11 +300,9 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, return ret; ssp->irq = pci_irq_vector(dev, 0); - snprintf(buf, sizeof(buf), "pxa2xx-spi.%d", ssp->port_id); - ssp->clk = clk_register_fixed_rate(&dev->dev, buf, NULL, 0, - c->max_clk_rate); - if (IS_ERR(ssp->clk)) - return PTR_ERR(ssp->clk); + ret = pxa2xx_spi_pci_clk_register(dev, ssp, c->max_clk_rate); + if (ret) + return ret; memset(&pi, 0, sizeof(pi)); pi.fwnode = dev_fwnode(&dev->dev); @@ -298,10 +313,8 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, pi.size_data = sizeof(spi_pdata); pdev = platform_device_register_full(&pi); - if (IS_ERR(pdev)) { - clk_unregister(ssp->clk); + if (IS_ERR(pdev)) return PTR_ERR(pdev); - } pci_set_drvdata(dev, pdev); @@ -311,12 +324,8 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, static void pxa2xx_spi_pci_remove(struct pci_dev *dev) { struct platform_device *pdev = pci_get_drvdata(dev); - struct pxa2xx_spi_controller *spi_pdata; - - spi_pdata = dev_get_platdata(&pdev->dev); platform_device_unregister(pdev); - clk_unregister(spi_pdata->ssp.clk); } static const struct pci_device_id pxa2xx_spi_pci_devices[] = { -- cgit From ba8d1353d9c2d9190a523860e37bd7cb7b9de31b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:49 +0200 Subject: spi: pxa2xx-pci: Drop temporary storage use for a handful of members Instead of using temporary storage, assign the values directly to the corresponding struct pxa2xx_spi_controller members. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-10-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 110 +++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 61 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 2dbe08034ad0..3c5d14affa95 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -28,19 +28,7 @@ #define PCI_DEVICE_ID_INTEL_LPT1_1 0x9ce6 struct pxa_spi_info { - enum pxa_ssp_type type; - unsigned int port_id; - unsigned int num_chipselect; - unsigned long max_clk_rate; - - /* DMA channel request parameters */ - bool (*dma_filter)(struct dma_chan *chan, void *param); - void *tx_param; - void *rx_param; - - unsigned int dma_burst_size; - - int (*setup)(struct pci_dev *pdev, struct pxa_spi_info *c); + int (*setup)(struct pci_dev *pdev, struct pxa2xx_spi_controller *c); }; static struct dw_dma_slave byt_tx_param = { .dst_id = 0 }; @@ -99,48 +87,49 @@ static void lpss_dma_put_device(void *dma_dev) pci_dev_put(dma_dev); } -static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +static int lpss_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c) { + struct ssp_device *ssp = &c->ssp; struct dw_dma_slave *tx, *rx; struct pci_dev *dma_dev; int ret; switch (dev->device) { case PCI_DEVICE_ID_INTEL_BYT: - c->type = LPSS_BYT_SSP; - c->port_id = 0; + ssp->type = LPSS_BYT_SSP; + ssp->port_id = 0; c->tx_param = &byt_tx_param; c->rx_param = &byt_rx_param; break; case PCI_DEVICE_ID_INTEL_BSW0: - c->type = LPSS_BSW_SSP; - c->port_id = 0; + ssp->type = LPSS_BSW_SSP; + ssp->port_id = 0; c->tx_param = &bsw0_tx_param; c->rx_param = &bsw0_rx_param; break; case PCI_DEVICE_ID_INTEL_BSW1: - c->type = LPSS_BSW_SSP; - c->port_id = 1; + ssp->type = LPSS_BSW_SSP; + ssp->port_id = 1; c->tx_param = &bsw1_tx_param; c->rx_param = &bsw1_rx_param; break; case PCI_DEVICE_ID_INTEL_BSW2: - c->type = LPSS_BSW_SSP; - c->port_id = 2; + ssp->type = LPSS_BSW_SSP; + ssp->port_id = 2; c->tx_param = &bsw2_tx_param; c->rx_param = &bsw2_rx_param; break; case PCI_DEVICE_ID_INTEL_LPT0_0: case PCI_DEVICE_ID_INTEL_LPT1_0: - c->type = LPSS_LPT_SSP; - c->port_id = 0; + ssp->type = LPSS_LPT_SSP; + ssp->port_id = 0; c->tx_param = &lpt0_tx_param; c->rx_param = &lpt0_rx_param; break; case PCI_DEVICE_ID_INTEL_LPT0_1: case PCI_DEVICE_ID_INTEL_LPT1_1: - c->type = LPSS_LPT_SSP; - c->port_id = 1; + ssp->type = LPSS_LPT_SSP; + ssp->port_id = 1; c->tx_param = &lpt1_tx_param; c->rx_param = &lpt1_rx_param; break; @@ -149,7 +138,10 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) } c->num_chipselect = 1; - c->max_clk_rate = 50000000; + + ret = pxa2xx_spi_pci_clk_register(dev, ssp, 50000000); + if (ret) + return ret; dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); ret = devm_add_action_or_reset(&dev->dev, lpss_dma_put_device, dma_dev); @@ -168,6 +160,7 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) c->dma_filter = lpss_dma_filter; c->dma_burst_size = 1; + c->enable_dma = 1; return 0; } @@ -175,41 +168,45 @@ static struct pxa_spi_info lpss_info_config = { .setup = lpss_spi_setup, }; -static int ce4100_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +static int ce4100_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c) { - c->type = PXA25x_SSP; - c->port_id = dev->devfn; + struct ssp_device *ssp = &c->ssp; + + ssp->type = PXA25x_SSP; + ssp->port_id = dev->devfn; c->num_chipselect = dev->devfn; - c->max_clk_rate = 3686400; - return 0; + return pxa2xx_spi_pci_clk_register(dev, ssp, 3686400); } static struct pxa_spi_info ce4100_info_config = { .setup = ce4100_spi_setup, }; -static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +static int mrfld_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c) { + struct ssp_device *ssp = &c->ssp; struct dw_dma_slave *tx, *rx; struct pci_dev *dma_dev; int ret; + ssp->type = MRFLD_SSP; + switch (PCI_FUNC(dev->devfn)) { case 0: - c->port_id = 3; + ssp->port_id = 3; c->num_chipselect = 1; c->tx_param = &mrfld3_tx_param; c->rx_param = &mrfld3_rx_param; break; case 1: - c->port_id = 5; + ssp->port_id = 5; c->num_chipselect = 4; c->tx_param = &mrfld5_tx_param; c->rx_param = &mrfld5_rx_param; break; case 2: - c->port_id = 6; + ssp->port_id = 6; c->num_chipselect = 1; c->tx_param = &mrfld6_tx_param; c->rx_param = &mrfld6_rx_param; @@ -218,8 +215,9 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) return -ENODEV; } - c->type = MRFLD_SSP; - c->max_clk_rate = 25000000; + ret = pxa2xx_spi_pci_clk_register(dev, ssp, 25000000); + if (ret) + return ret; dma_dev = pci_get_slot(dev->bus, PCI_DEVFN(21, 0)); ret = devm_add_action_or_reset(&dev->dev, lpss_dma_put_device, dma_dev); @@ -234,6 +232,7 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) c->dma_filter = lpss_dma_filter; c->dma_burst_size = 8; + c->enable_dma = 1; return 0; } @@ -241,14 +240,15 @@ static struct pxa_spi_info mrfld_info_config = { .setup = mrfld_spi_setup, }; -static int qrk_spi_setup(struct pci_dev *dev, struct pxa_spi_info *c) +static int qrk_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c) { - c->type = QUARK_X1000_SSP; - c->port_id = dev->devfn; + struct ssp_device *ssp = &c->ssp; + + ssp->type = QUARK_X1000_SSP; + ssp->port_id = dev->devfn; c->num_chipselect = 1; - c->max_clk_rate = 50000000; - return 0; + return pxa2xx_spi_pci_clk_register(dev, ssp, 50000000); } static struct pxa_spi_info qrk_info_config = { @@ -262,8 +262,8 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, int ret; struct platform_device *pdev; struct pxa2xx_spi_controller spi_pdata; + struct pxa_spi_info *info; struct ssp_device *ssp; - struct pxa_spi_info *c; ret = pcim_enable_device(dev); if (ret) @@ -273,25 +273,17 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, if (ret) return ret; - c = (struct pxa_spi_info *)ent->driver_data; - ret = c->setup(dev, c); - if (ret) - return ret; - memset(&spi_pdata, 0, sizeof(spi_pdata)); - spi_pdata.num_chipselect = c->num_chipselect; - spi_pdata.dma_filter = c->dma_filter; - spi_pdata.tx_param = c->tx_param; - spi_pdata.rx_param = c->rx_param; - spi_pdata.enable_dma = c->rx_param && c->tx_param; - spi_pdata.dma_burst_size = c->dma_burst_size; ssp = &spi_pdata.ssp; ssp->dev = &dev->dev; ssp->phys_base = pci_resource_start(dev, 0); ssp->mmio_base = pcim_iomap_table(dev)[0]; - ssp->type = c->type; - ssp->port_id = c->port_id; + + info = (struct pxa_spi_info *)ent->driver_data; + ret = info->setup(dev, &spi_pdata); + if (ret) + return ret; pci_set_master(dev); @@ -300,10 +292,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, return ret; ssp->irq = pci_irq_vector(dev, 0); - ret = pxa2xx_spi_pci_clk_register(dev, ssp, c->max_clk_rate); - if (ret) - return ret; - memset(&pi, 0, sizeof(pi)); pi.fwnode = dev_fwnode(&dev->dev); pi.parent = &dev->dev; -- cgit From fcaaf76ed5f3bbf346db9e49d9d9c0978d8f8dce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 25 Feb 2022 19:23:50 +0200 Subject: spi: pxa2xx-pci: Constify struct pxa_spi_info variables Now when there are no dynamical changes required, we may constify struct pxa_spi_info variables. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220225172350.69797-11-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-pci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index 3c5d14affa95..861b21c63504 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -164,7 +164,7 @@ static int lpss_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c) return 0; } -static struct pxa_spi_info lpss_info_config = { +static const struct pxa_spi_info lpss_info_config = { .setup = lpss_spi_setup, }; @@ -179,7 +179,7 @@ static int ce4100_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c return pxa2xx_spi_pci_clk_register(dev, ssp, 3686400); } -static struct pxa_spi_info ce4100_info_config = { +static const struct pxa_spi_info ce4100_info_config = { .setup = ce4100_spi_setup, }; @@ -236,7 +236,7 @@ static int mrfld_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c) return 0; } -static struct pxa_spi_info mrfld_info_config = { +static const struct pxa_spi_info mrfld_info_config = { .setup = mrfld_spi_setup, }; @@ -251,18 +251,18 @@ static int qrk_spi_setup(struct pci_dev *dev, struct pxa2xx_spi_controller *c) return pxa2xx_spi_pci_clk_register(dev, ssp, 50000000); } -static struct pxa_spi_info qrk_info_config = { +static const struct pxa_spi_info qrk_info_config = { .setup = qrk_spi_setup, }; static int pxa2xx_spi_pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { + const struct pxa_spi_info *info; struct platform_device_info pi; int ret; struct platform_device *pdev; struct pxa2xx_spi_controller spi_pdata; - struct pxa_spi_info *info; struct ssp_device *ssp; ret = pcim_enable_device(dev); -- cgit From 6bb477df04366e0f69dd2f49e1ae1099069326bc Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Thu, 17 Feb 2022 22:12:34 +0800 Subject: spi: use specific last_cs instead of last_cs_enable Commit d40f0b6f2e21 instroduced last_cs_enable to avoid setting chipselect if it's not necessary, but it also introduces a bug. The chipselect may not be set correctly on multi-device SPI busses. The reason is that we can't judge the chipselect by bool last_cs_enable, since chipselect may be modified after other devices were accessed. So we should record the specific state of chipselect in case of confusion. Signed-off-by: Yun Zhou Link: https://lore.kernel.org/r/20220217141234.72737-1-yun.zhou@windriver.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index cd4dc3131e17..6326e592fcfd 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -926,13 +926,14 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) * Avoid calling into the driver (or doing delays) if the chip select * isn't actually changing from the last time this was called. */ - if (!force && (spi->controller->last_cs_enable == enable) && + if (!force && ((enable && spi->controller->last_cs == spi->chip_select) || + (!enable && spi->controller->last_cs != spi->chip_select)) && (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH))) return; trace_spi_set_cs(spi, activate); - spi->controller->last_cs_enable = enable; + spi->controller->last_cs = enable ? spi->chip_select : -1; spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) { @@ -3016,6 +3017,9 @@ int spi_register_controller(struct spi_controller *ctlr) goto free_bus_id; } + /* setting last_cs to -1 means no chip selected */ + ctlr->last_cs = -1; + status = device_add(&ctlr->dev); if (status < 0) goto free_bus_id; -- cgit From 13262fc26c1837c51a5131dbbdd67a2387f8bfc7 Mon Sep 17 00:00:00 2001 From: Jiasheng Jiang Date: Wed, 2 Mar 2022 17:20:51 +0800 Subject: spi: spi-zynqmp-gqspi: Handle error for dma_set_mask As the potential failure of the dma_set_mask(), it should be better to check it and return error if fails. Fixes: 126bdb606fd2 ("spi: spi-zynqmp-gqspi: return -ENOMEM if dma_map_single fails") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20220302092051.121343-1-jiasheng@iscas.ac.cn Signed-off-by: Mark Brown --- drivers/spi/spi-zynqmp-gqspi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 328b6559bb19..2b5afae8ff7f 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -1172,7 +1172,10 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto clk_dis_all; } - dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(44)); + if (ret) + goto clk_dis_all; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); ctlr->num_chipselect = GQSPI_DEFAULT_NUM_CS; ctlr->mem_ops = &zynqmp_qspi_mem_ops; -- cgit From dc8fea13f98ace0ae8815dd44d1e60c184f3f930 Mon Sep 17 00:00:00 2001 From: "Minghao Chi (CGEL ZTE)" Date: Thu, 3 Mar 2022 09:21:31 +0000 Subject: spi: Use of_device_get_match_data() Use of_device_get_match_data() to simplify the code. Reported-by: Zeal Robot Signed-off-by: Minghao Chi (CGEL ZTE) Link: https://lore.kernel.org/r/20220303092131.2060044-1-chi.minghao@zte.com.cn Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-fiu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c index b62471ab6d7f..71ed75bf0a20 100644 --- a/drivers/spi/spi-npcm-fiu.c +++ b/drivers/spi/spi-npcm-fiu.c @@ -671,7 +671,6 @@ static const struct of_device_id npcm_fiu_dt_ids[] = { static int npcm_fiu_probe(struct platform_device *pdev) { const struct fiu_data *fiu_data_match; - const struct of_device_id *match; struct device *dev = &pdev->dev; struct spi_controller *ctrl; struct npcm_fiu_spi *fiu; @@ -685,13 +684,12 @@ static int npcm_fiu_probe(struct platform_device *pdev) fiu = spi_controller_get_devdata(ctrl); - match = of_match_device(npcm_fiu_dt_ids, dev); - if (!match || !match->data) { + fiu_data_match = of_device_get_match_data(dev); + if (!fiu_data_match) { dev_err(dev, "No compatible OF match\n"); return -ENODEV; } - fiu_data_match = match->data; id = of_alias_get_id(dev->of_node, "fiu"); if (id < 0 || id >= fiu_data_match->fiu_max) { dev_err(dev, "Invalid platform device id: %d\n", id); -- cgit From 7db7a24657c969291921d6580d62748b1320dd2f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 24 Jan 2022 09:23:47 +0100 Subject: spi: s3c64xx: Allow controller-data to be optional The Samsung SoC SPI driver requires to provide controller-data node for each of SPI peripheral device nodes. Make this controller-data node optional, so DTS could be simpler. Suggested-by: Rob Herring Signed-off-by: Krzysztof Kozlowski Reviewed-by: Sam Protsenko Reviewed-by: Andi Shyti Reviwed-by: Mark Brown Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220124082347.32747-5-krzysztof.kozlowski@canonical.com --- drivers/spi/spi-s3c64xx.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 8755cd85e83c..386550fca81c 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -796,16 +796,14 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( return ERR_PTR(-EINVAL); } - data_np = of_get_child_by_name(slave_np, "controller-data"); - if (!data_np) { - dev_err(&spi->dev, "child node 'controller-data' not found\n"); - return ERR_PTR(-EINVAL); - } - cs = kzalloc(sizeof(*cs), GFP_KERNEL); - if (!cs) { - of_node_put(data_np); + if (!cs) return ERR_PTR(-ENOMEM); + + data_np = of_get_child_by_name(slave_np, "controller-data"); + if (!data_np) { + dev_info(&spi->dev, "feedback delay set to default (0)\n"); + return cs; } of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay); -- cgit From c59dbc642d4e76187516960780b6cd26e7f2c943 Mon Sep 17 00:00:00 2001 From: Yihao Han Date: Thu, 3 Mar 2022 04:50:54 -0800 Subject: spi: cadence: fix platform_get_irq.cocci warning Remove dev_err() messages after platform_get_irq*() failures. platform_get_irq() already prints an error. Generated by: scripts/coccinelle/api/platform_get_irq.cocci Signed-off-by: Yihao Han Link: https://lore.kernel.org/r/20220303125054.3574-1-hanyihao@vivo.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-xspi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 4bc1b93fc276..3ab19be83095 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -578,10 +578,8 @@ static int cdns_xspi_probe(struct platform_device *pdev) } cdns_xspi->irq = platform_get_irq(pdev, 0); - if (cdns_xspi->irq < 0) { - dev_err(dev, "Failed to get IRQ\n"); + if (cdns_xspi->irq < 0) return -ENXIO; - } ret = devm_request_irq(dev, cdns_xspi->irq, cdns_xspi_irq_handler, IRQF_SHARED, pdev->name, cdns_xspi); -- cgit From fa0f3db49e10ac61774e1c90167ec79429d6fd56 Mon Sep 17 00:00:00 2001 From: Xingbang Liu Date: Wed, 2 Mar 2022 15:15:21 +0800 Subject: spi: qup: replace spin_lock_irqsave by spin_lock in hard IRQ The code has been in a irq-disabled context since it is hard IRQ. There is no necessity to do it again. Signed-off-by: Xingbang Liu Link: https://lore.kernel.org/r/20220302071521.6638-1-liu.airalert@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index d39dec6d1c91..00d6084306b4 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -593,7 +593,6 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) { struct spi_qup *controller = dev_id; u32 opflags, qup_err, spi_err; - unsigned long flags; int error = 0; qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); @@ -625,10 +624,10 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) error = -EIO; } - spin_lock_irqsave(&controller->lock, flags); + spin_lock(&controller->lock); if (!controller->error) controller->error = error; - spin_unlock_irqrestore(&controller->lock, flags); + spin_unlock(&controller->lock); if (spi_qup_is_dma_xfer(controller->mode)) { writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); -- cgit From b15e3bc76925eb1366348483fca89f115c8cde31 Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Sun, 6 Mar 2022 15:23:12 +0100 Subject: spi: npcm-fiu: Fix typo ("npxm") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The platform is called NPCM, not NPXM. Signed-off-by: Jonathan Neuschäfer Link: https://lore.kernel.org/r/20220306142312.109017-1-j.neuschaefer@gmx.net Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-fiu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c index 71ed75bf0a20..ba67dbed9fb8 100644 --- a/drivers/spi/spi-npcm-fiu.c +++ b/drivers/spi/spi-npcm-fiu.c @@ -201,7 +201,7 @@ struct fiu_data { int fiu_max; }; -static const struct npcm_fiu_info npxm7xx_fiu_info[] = { +static const struct npcm_fiu_info npcm7xx_fiu_info[] = { {.name = "FIU0", .fiu_id = FIU0, .max_map_size = MAP_SIZE_128MB, .max_cs = 2}, {.name = "FIU3", .fiu_id = FIU3, @@ -209,8 +209,8 @@ static const struct npcm_fiu_info npxm7xx_fiu_info[] = { {.name = "FIUX", .fiu_id = FIUX, .max_map_size = MAP_SIZE_16MB, .max_cs = 2} }; -static const struct fiu_data npxm7xx_fiu_data = { - .npcm_fiu_data_info = npxm7xx_fiu_info, +static const struct fiu_data npcm7xx_fiu_data = { + .npcm_fiu_data_info = npcm7xx_fiu_info, .fiu_max = 3, }; @@ -664,7 +664,7 @@ static const struct spi_controller_mem_ops npcm_fiu_mem_ops = { }; static const struct of_device_id npcm_fiu_dt_ids[] = { - { .compatible = "nuvoton,npcm750-fiu", .data = &npxm7xx_fiu_data }, + { .compatible = "nuvoton,npcm750-fiu", .data = &npcm7xx_fiu_data }, { /* sentinel */ } }; -- cgit From 75a1b44a54bd97500e524cf42e8c81cc632672b3 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Mon, 7 Mar 2022 22:25:17 +0530 Subject: spi: tegra210-quad: add acpi support Add ACPI ID for Tegra QUAD SPI. Switch to common device property calls. Skip clock calls that are not updated in ACPI boot. Runtime PM support is not yet enabled with ACPI boot. Signed-off-by: Krishna Yarlagadda Link: https://lore.kernel.org/r/20220307165519.38380-2-kyarlagadda@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 52 ++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 3725ee5331ae..517a348d1c19 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #define QSPI_COMMAND1 0x000 #define QSPI_BIT_LENGTH(x) (((x) & 0x1f) << 0) @@ -771,7 +773,7 @@ static u32 tegra_qspi_setup_transfer_one(struct spi_device *spi, struct spi_tran u32 tx_tap = 0, rx_tap = 0; int req_mode; - if (speed != tqspi->cur_speed) { + if (!has_acpi_companion(tqspi->dev) && speed != tqspi->cur_speed) { clk_set_rate(tqspi->clk, speed); tqspi->cur_speed = speed; } @@ -879,16 +881,16 @@ static int tegra_qspi_start_transfer_one(struct spi_device *spi, static struct tegra_qspi_client_data *tegra_qspi_parse_cdata_dt(struct spi_device *spi) { struct tegra_qspi_client_data *cdata; - struct device_node *slave_np = spi->dev.of_node; cdata = devm_kzalloc(&spi->dev, sizeof(*cdata), GFP_KERNEL); if (!cdata) return NULL; - of_property_read_u32(slave_np, "nvidia,tx-clk-tap-delay", - &cdata->tx_clk_tap_delay); - of_property_read_u32(slave_np, "nvidia,rx-clk-tap-delay", - &cdata->rx_clk_tap_delay); + device_property_read_u32(&spi->dev, "nvidia,tx-clk-tap-delay", + &cdata->tx_clk_tap_delay); + device_property_read_u32(&spi->dev, "nvidia,rx-clk-tap-delay", + &cdata->rx_clk_tap_delay); + return cdata; } @@ -1227,6 +1229,24 @@ static const struct of_device_id tegra_qspi_of_match[] = { MODULE_DEVICE_TABLE(of, tegra_qspi_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id tegra_qspi_acpi_match[] = { + { + .id = "NVDA1213", + .driver_data = (kernel_ulong_t)&tegra210_qspi_soc_data, + }, { + .id = "NVDA1313", + .driver_data = (kernel_ulong_t)&tegra186_qspi_soc_data, + }, { + .id = "NVDA1413", + .driver_data = (kernel_ulong_t)&tegra234_qspi_soc_data, + }, + {} +}; + +MODULE_DEVICE_TABLE(acpi, tegra_qspi_acpi_match); +#endif + static int tegra_qspi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -1269,11 +1289,14 @@ static int tegra_qspi_probe(struct platform_device *pdev) return qspi_irq; tqspi->irq = qspi_irq; - tqspi->clk = devm_clk_get(&pdev->dev, "qspi"); - if (IS_ERR(tqspi->clk)) { - ret = PTR_ERR(tqspi->clk); - dev_err(&pdev->dev, "failed to get clock: %d\n", ret); - return ret; + if (!has_acpi_companion(tqspi->dev)) { + tqspi->clk = devm_clk_get(&pdev->dev, "qspi"); + if (IS_ERR(tqspi->clk)) { + ret = PTR_ERR(tqspi->clk); + dev_err(&pdev->dev, "failed to get clock: %d\n", ret); + return ret; + } + } tqspi->max_buf_size = QSPI_FIFO_DEPTH << 2; @@ -1377,6 +1400,9 @@ static int __maybe_unused tegra_qspi_runtime_suspend(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct tegra_qspi *tqspi = spi_master_get_devdata(master); + /* Runtime pm disabled with ACPI */ + if (has_acpi_companion(tqspi->dev)) + return 0; /* flush all write which are in PPSB queue by reading back */ tegra_qspi_readl(tqspi, QSPI_COMMAND1); @@ -1391,6 +1417,9 @@ static int __maybe_unused tegra_qspi_runtime_resume(struct device *dev) struct tegra_qspi *tqspi = spi_master_get_devdata(master); int ret; + /* Runtime pm disabled with ACPI */ + if (has_acpi_companion(tqspi->dev)) + return 0; ret = clk_prepare_enable(tqspi->clk); if (ret < 0) dev_err(tqspi->dev, "failed to enable clock: %d\n", ret); @@ -1408,6 +1437,7 @@ static struct platform_driver tegra_qspi_driver = { .name = "tegra-qspi", .pm = &tegra_qspi_pm_ops, .of_match_table = tegra_qspi_of_match, + .acpi_match_table = ACPI_PTR(tegra_qspi_acpi_match), }, .probe = tegra_qspi_probe, .remove = tegra_qspi_remove, -- cgit From 1b8342cc4a387933780c50f0cf51c94455be7d11 Mon Sep 17 00:00:00 2001 From: Krishna Yarlagadda Date: Mon, 7 Mar 2022 22:25:18 +0530 Subject: spi: tegra210-quad: combined sequence mode Add combined sequence mode supported by Tegra QSPI controller. For commands which contain cmd, addr, data parts to it, controller can accept all 3 transfers at once and avoid interrupt for each transfer. This would improve read & write performance. Signed-off-by: Krishna Yarlagadda Reported-by: kernel test robot Reported-by: Dan Carpenter Link: https://lore.kernel.org/r/20220307165519.38380-3-kyarlagadda@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 238 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 233 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 517a348d1c19..66f647f32876 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -121,14 +121,39 @@ #define QSPI_NUM_DUMMY_CYCLE(x) (((x) & 0xff) << 0) #define QSPI_DUMMY_CYCLES_MAX 0xff +#define QSPI_CMB_SEQ_CMD 0x19c +#define QSPI_COMMAND_VALUE_SET(X) (((x) & 0xFF) << 0) + +#define QSPI_CMB_SEQ_CMD_CFG 0x1a0 +#define QSPI_COMMAND_X1_X2_X4(x) (((x) & 0x3) << 13) +#define QSPI_COMMAND_X1_X2_X4_MASK (0x03 << 13) +#define QSPI_COMMAND_SDR_DDR BIT(12) +#define QSPI_COMMAND_SIZE_SET(x) (((x) & 0xFF) << 0) + +#define QSPI_GLOBAL_CONFIG 0X1a4 +#define QSPI_CMB_SEQ_EN BIT(0) + +#define QSPI_CMB_SEQ_ADDR 0x1a8 +#define QSPI_ADDRESS_VALUE_SET(X) (((x) & 0xFFFF) << 0) + +#define QSPI_CMB_SEQ_ADDR_CFG 0x1ac +#define QSPI_ADDRESS_X1_X2_X4(x) (((x) & 0x3) << 13) +#define QSPI_ADDRESS_X1_X2_X4_MASK (0x03 << 13) +#define QSPI_ADDRESS_SDR_DDR BIT(12) +#define QSPI_ADDRESS_SIZE_SET(x) (((x) & 0xFF) << 0) + #define DATA_DIR_TX BIT(0) #define DATA_DIR_RX BIT(1) #define QSPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) #define DEFAULT_QSPI_DMA_BUF_LEN (64 * 1024) +#define CMD_TRANSFER 0 +#define ADDR_TRANSFER 1 +#define DATA_TRANSFER 2 struct tegra_qspi_soc_data { bool has_dma; + bool cmb_xfer_capable; }; struct tegra_qspi_client_data { @@ -912,7 +937,6 @@ static int tegra_qspi_setup(struct spi_device *spi) cdata = tegra_qspi_parse_cdata_dt(spi); spi->controller_data = cdata; } - spin_lock_irqsave(&tqspi->lock, flags); /* keep default cs state to inactive */ @@ -971,19 +995,179 @@ static void tegra_qspi_transfer_end(struct spi_device *spi) tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1); } -static int tegra_qspi_transfer_one_message(struct spi_master *master, struct spi_message *msg) +static u32 tegra_qspi_cmd_config(bool is_ddr, u8 bus_width, u8 len) +{ + u32 cmd_config = 0; + + /* Extract Command configuration and value */ + if (is_ddr) + cmd_config |= QSPI_COMMAND_SDR_DDR; + else + cmd_config &= ~QSPI_COMMAND_SDR_DDR; + + cmd_config |= QSPI_COMMAND_X1_X2_X4(bus_width); + cmd_config |= QSPI_COMMAND_SIZE_SET((len * 8) - 1); + + return cmd_config; +} + +static u32 tegra_qspi_addr_config(bool is_ddr, u8 bus_width, u8 len) +{ + u32 addr_config = 0; + + /* Extract Address configuration and value */ + is_ddr = 0; //Only SDR mode supported + bus_width = 0; //X1 mode + + if (is_ddr) + addr_config |= QSPI_ADDRESS_SDR_DDR; + else + addr_config &= ~QSPI_ADDRESS_SDR_DDR; + + addr_config |= QSPI_ADDRESS_X1_X2_X4(bus_width); + addr_config |= QSPI_ADDRESS_SIZE_SET((len * 8) - 1); + + return addr_config; +} + +static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, + struct spi_message *msg) +{ + bool is_first_msg = true; + struct spi_transfer *xfer; + struct spi_device *spi = msg->spi; + u8 transfer_phase = 0; + u32 cmd1 = 0, dma_ctl = 0; + int ret = 0; + u32 address_value = 0; + u32 cmd_config = 0, addr_config = 0; + u8 cmd_value = 0, val = 0; + + /* Enable Combined sequence mode */ + val = tegra_qspi_readl(tqspi, QSPI_GLOBAL_CONFIG); + val |= QSPI_CMB_SEQ_EN; + tegra_qspi_writel(tqspi, val, QSPI_GLOBAL_CONFIG); + /* Process individual transfer list */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + switch (transfer_phase) { + case CMD_TRANSFER: + /* X1 SDR mode */ + cmd_config = tegra_qspi_cmd_config(false, 0, + xfer->len); + cmd_value = *((const u8 *)(xfer->tx_buf)); + break; + case ADDR_TRANSFER: + /* X1 SDR mode */ + addr_config = tegra_qspi_addr_config(false, 0, + xfer->len); + address_value = *((const u32 *)(xfer->tx_buf)); + break; + case DATA_TRANSFER: + /* Program Command, Address value in register */ + tegra_qspi_writel(tqspi, cmd_value, QSPI_CMB_SEQ_CMD); + tegra_qspi_writel(tqspi, address_value, + QSPI_CMB_SEQ_ADDR); + /* Program Command and Address config in register */ + tegra_qspi_writel(tqspi, cmd_config, + QSPI_CMB_SEQ_CMD_CFG); + tegra_qspi_writel(tqspi, addr_config, + QSPI_CMB_SEQ_ADDR_CFG); + + reinit_completion(&tqspi->xfer_completion); + cmd1 = tegra_qspi_setup_transfer_one(spi, xfer, + is_first_msg); + ret = tegra_qspi_start_transfer_one(spi, xfer, + cmd1); + + if (ret < 0) { + dev_err(tqspi->dev, "Failed to start transfer-one: %d\n", + ret); + return ret; + } + + is_first_msg = false; + ret = wait_for_completion_timeout + (&tqspi->xfer_completion, + QSPI_DMA_TIMEOUT); + + if (WARN_ON(ret == 0)) { + dev_err(tqspi->dev, "QSPI Transfer failed with timeout: %d\n", + ret); + if (tqspi->is_curr_dma_xfer && + (tqspi->cur_direction & DATA_DIR_TX)) + dmaengine_terminate_all + (tqspi->tx_dma_chan); + + if (tqspi->is_curr_dma_xfer && + (tqspi->cur_direction & DATA_DIR_RX)) + dmaengine_terminate_all + (tqspi->rx_dma_chan); + + /* Abort transfer by resetting pio/dma bit */ + if (!tqspi->is_curr_dma_xfer) { + cmd1 = tegra_qspi_readl + (tqspi, + QSPI_COMMAND1); + cmd1 &= ~QSPI_PIO; + tegra_qspi_writel + (tqspi, cmd1, + QSPI_COMMAND1); + } else { + dma_ctl = tegra_qspi_readl + (tqspi, + QSPI_DMA_CTL); + dma_ctl &= ~QSPI_DMA_EN; + tegra_qspi_writel(tqspi, dma_ctl, + QSPI_DMA_CTL); + } + + /* Reset controller if timeout happens */ + if (device_reset(tqspi->dev) < 0) + dev_warn_once(tqspi->dev, + "device reset failed\n"); + ret = -EIO; + goto exit; + } + + if (tqspi->tx_status || tqspi->rx_status) { + dev_err(tqspi->dev, "QSPI Transfer failed\n"); + tqspi->tx_status = 0; + tqspi->rx_status = 0; + ret = -EIO; + goto exit; + } + break; + default: + ret = -EINVAL; + goto exit; + } + msg->actual_length += xfer->len; + transfer_phase++; + } + +exit: + msg->status = ret; + + return ret; +} + +static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi, + struct spi_message *msg) { - struct tegra_qspi *tqspi = spi_master_get_devdata(master); struct spi_device *spi = msg->spi; struct spi_transfer *transfer; bool is_first_msg = true; - int ret; + int ret = 0, val = 0; msg->status = 0; msg->actual_length = 0; tqspi->tx_status = 0; tqspi->rx_status = 0; + /* Disable Combined sequence mode */ + val = tegra_qspi_readl(tqspi, QSPI_GLOBAL_CONFIG); + val &= ~QSPI_CMB_SEQ_EN; + tegra_qspi_writel(tqspi, val, QSPI_GLOBAL_CONFIG); list_for_each_entry(transfer, &msg->transfers, transfer_list) { struct spi_transfer *xfer = transfer; u8 dummy_bytes = 0; @@ -1021,7 +1205,6 @@ static int tegra_qspi_transfer_one_message(struct spi_master *master, struct spi goto complete_xfer; } - is_first_msg = false; ret = wait_for_completion_timeout(&tqspi->xfer_completion, QSPI_DMA_TIMEOUT); if (WARN_ON(ret == 0)) { @@ -1066,7 +1249,48 @@ complete_xfer: ret = 0; exit: msg->status = ret; + + return ret; +} + +static bool tegra_qspi_validate_cmb_seq(struct tegra_qspi *tqspi, + struct spi_message *msg) +{ + int transfer_count = 0; + struct spi_transfer *xfer; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + transfer_count++; + } + if (!tqspi->soc_data->cmb_xfer_capable || transfer_count != 3) + return false; + xfer = list_first_entry(&msg->transfers, typeof(*xfer), + transfer_list); + if (xfer->len > 2) + return false; + xfer = list_next_entry(xfer, transfer_list); + if (xfer->len > 4 || xfer->len < 3) + return false; + xfer = list_next_entry(xfer, transfer_list); + if (!tqspi->soc_data->has_dma || xfer->len > (QSPI_FIFO_DEPTH << 2)) + return false; + + return true; +} + +static int tegra_qspi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct tegra_qspi *tqspi = spi_master_get_devdata(master); + int ret; + + if (tegra_qspi_validate_cmb_seq(tqspi, msg)) + ret = tegra_qspi_combined_seq_xfer(tqspi, msg); + else + ret = tegra_qspi_non_combined_seq_xfer(tqspi, msg); + spi_finalize_current_message(master); + return ret; } @@ -1200,14 +1424,17 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data) static struct tegra_qspi_soc_data tegra210_qspi_soc_data = { .has_dma = true, + .cmb_xfer_capable = false, }; static struct tegra_qspi_soc_data tegra186_qspi_soc_data = { .has_dma = true, + .cmb_xfer_capable = true, }; static struct tegra_qspi_soc_data tegra234_qspi_soc_data = { .has_dma = false, + .cmb_xfer_capable = true, }; static const struct of_device_id tegra_qspi_of_match[] = { @@ -1278,6 +1505,7 @@ static int tegra_qspi_probe(struct platform_device *pdev) tqspi->dev = &pdev->dev; spin_lock_init(&tqspi->lock); + tqspi->soc_data = device_get_match_data(&pdev->dev); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); tqspi->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(tqspi->base)) -- cgit From 80ab9012bbf1011f57c06b3c6e4ac3816c4a86f5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Mar 2022 19:37:40 +0200 Subject: spi: topcliff-pch: Prevent usage of potentially stale DMA device DMA device is expected to be available while SPI transfer is ongoing. Prevent usage of potentially stale DMA device by keeping reference count till the end of the transfer. Fixes: 4d986ffa036a ("spi: add missing pci_dev_put() before return") Reported-by: Christophe JAILLET Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220307173740.80996-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-topcliff-pch.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 8e1cc345810a..dfaa1d79a78b 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -103,6 +103,7 @@ static int use_dma = 1; struct pch_spi_dma_ctrl { + struct pci_dev *dma_dev; struct dma_async_tx_descriptor *desc_tx; struct dma_async_tx_descriptor *desc_rx; struct pch_dma_slave param_tx; @@ -876,7 +877,6 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw) if (!chan) { dev_err(&data->master->dev, "ERROR: dma_request_channel FAILS(Tx)\n"); - data->use_dma = 0; goto out; } dma->chan_tx = chan; @@ -893,13 +893,15 @@ static void pch_spi_request_dma(struct pch_spi_data *data, int bpw) "ERROR: dma_request_channel FAILS(Rx)\n"); dma_release_channel(dma->chan_tx); dma->chan_tx = NULL; - data->use_dma = 0; goto out; } dma->chan_rx = chan; + dma->dma_dev = dma_dev; + return; out: pci_dev_put(dma_dev); + data->use_dma = 0; } static void pch_spi_release_dma(struct pch_spi_data *data) @@ -915,6 +917,8 @@ static void pch_spi_release_dma(struct pch_spi_data *data) dma_release_channel(dma->chan_rx); dma->chan_rx = NULL; } + + pci_dev_put(dma->dma_dev); } static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) -- cgit From 4ebb15a15799da4954f1d4926fcd3263ea46e417 Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Tue, 8 Mar 2022 17:46:40 +0530 Subject: spi: s3c64xx: Add spi port configuration for Tesla FSD SoC Add compatible and port configuration for spi controller for Tesla Full Self-Driving SoC. Cc: linux-fsd@tesla.com Signed-off-by: Aswani Reddy Signed-off-by: Alim Akhtar Reviewed-by: Krzysztof Kozlowski Reviewed-by: Andi Shyti Link: https://lore.kernel.org/r/20220308121640.27344-2-alim.akhtar@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 386550fca81c..423518bf0270 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1440,6 +1440,16 @@ static const struct s3c64xx_spi_port_config exynos5433_spi_port_config = { .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, }; +static struct s3c64xx_spi_port_config fsd_spi_port_config = { + .fifo_lvl_mask = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f}, + .rx_lvl_offset = 15, + .tx_st_done = 25, + .high_speed = true, + .clk_from_cmu = true, + .clk_ioclk = false, + .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, +}; + static const struct platform_device_id s3c64xx_spi_driver_ids[] = { { .name = "s3c2443-spi", @@ -1470,6 +1480,9 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = { { .compatible = "samsung,exynos5433-spi", .data = (void *)&exynos5433_spi_port_config, }, + { .compatible = "tesla,fsd-spi", + .data = (void *)&fsd_spi_port_config, + }, { }, }; MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match); -- cgit From 2cfdf0b4441aa918c3b18142740c92407b3c35a2 Mon Sep 17 00:00:00 2001 From: Yihao Han Date: Thu, 10 Mar 2022 01:48:06 -0800 Subject: spi: rockchip-sfc: fix platform_get_irq.cocci warning Remove dev_err() messages after platform_get_irq*() failures. platform_get_irq() already prints an error. Generated by: scripts/coccinelle/api/platform_get_irq.cocci Signed-off-by: Yihao Han Link: https://lore.kernel.org/r/20220310094806.13734-1-hanyihao@vivo.com Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip-sfc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index a46b38544027..bd87d3c92dd3 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -624,10 +624,8 @@ static int rockchip_sfc_probe(struct platform_device *pdev) /* Find the irq */ ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "Failed to get the irq\n"); + if (ret < 0) goto err_irq; - } ret = devm_request_irq(dev, ret, rockchip_sfc_irq_handler, 0, pdev->name, sfc); -- cgit From 03b1be379dcee2e9c866c2a455a1a4a9581b3efd Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Tue, 15 Mar 2022 11:24:06 +0800 Subject: spi: mediatek: support tick_delay without enhance_timing this patch support tick_delay bit[31:30] without enhance_timing feature. Fixes: f84d866ab43f("spi: mediatek: add tick_delay support") Signed-off-by: Leilk Liu Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220315032411.2826-2-leilk.liu@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index bbfeb8046c17..3fd89548ec3c 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -43,8 +43,11 @@ #define SPI_CFG1_PACKET_LOOP_OFFSET 8 #define SPI_CFG1_PACKET_LENGTH_OFFSET 16 #define SPI_CFG1_GET_TICK_DLY_OFFSET 29 +#define SPI_CFG1_GET_TICK_DLY_OFFSET_V1 30 #define SPI_CFG1_GET_TICK_DLY_MASK 0xe0000000 +#define SPI_CFG1_GET_TICK_DLY_MASK_V1 0xc0000000 + #define SPI_CFG1_CS_IDLE_MASK 0xff #define SPI_CFG1_PACKET_LOOP_MASK 0xff00 #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 @@ -346,9 +349,15 @@ static int mtk_spi_prepare_message(struct spi_master *master, /* tick delay */ reg_val = readl(mdata->base + SPI_CFG1_REG); - reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; - reg_val |= ((chip_config->tick_delay & 0x7) - << SPI_CFG1_GET_TICK_DLY_OFFSET); + if (mdata->dev_comp->enhance_timing) { + reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; + reg_val |= ((chip_config->tick_delay & 0x7) + << SPI_CFG1_GET_TICK_DLY_OFFSET); + } else { + reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1; + reg_val |= ((chip_config->tick_delay & 0x3) + << SPI_CFG1_GET_TICK_DLY_OFFSET_V1); + } writel(reg_val, mdata->base + SPI_CFG1_REG); /* set hw cs timing */ -- cgit From 2002c13243d595e211c0dad6b8e2e87f906f474b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 14 Mar 2022 12:53:45 +0100 Subject: spi: sun4i: fix typos in comments Various spelling mistakes in comments. Detected with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20220314115354.144023-22-Julia.Lawall@inria.fr Signed-off-by: Mark Brown --- drivers/spi/spi-sun4i.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 1fdfc6e6691d..6000d0761206 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -280,7 +280,7 @@ static int sun4i_spi_transfer_one(struct spi_master *master, * SPI_CLK = MOD_CLK / (2 ^ (cdr + 1)) * Or we can use CDR2, which is calculated with the formula: * SPI_CLK = MOD_CLK / (2 * (cdr + 1)) - * Wether we use the former or the latter is set through the + * Whether we use the former or the latter is set through the * DRS bit. * * First try CDR2, and if we can't reach the expected -- cgit From 7e963fb2a33ce488e65258ab5be38a4855923033 Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Tue, 15 Mar 2022 11:24:08 +0800 Subject: spi: mediatek: add ipm design support for MT7986 this patch add the support of ipm design. Signed-off-by: Leilk Liu Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20220315032411.2826-4-leilk.liu@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 102 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 3fd89548ec3c..1a0b3208dfca 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -31,6 +31,7 @@ #define SPI_CFG2_REG 0x0028 #define SPI_TX_SRC_REG_64 0x002c #define SPI_RX_DST_REG_64 0x0030 +#define SPI_CFG3_IPM_REG 0x0040 #define SPI_CFG0_SCK_HIGH_OFFSET 0 #define SPI_CFG0_SCK_LOW_OFFSET 8 @@ -51,6 +52,7 @@ #define SPI_CFG1_CS_IDLE_MASK 0xff #define SPI_CFG1_PACKET_LOOP_MASK 0xff00 #define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000 +#define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16) #define SPI_CFG2_SCK_HIGH_OFFSET 0 #define SPI_CFG2_SCK_LOW_OFFSET 16 @@ -71,7 +73,13 @@ #define SPI_CMD_TX_ENDIAN BIT(15) #define SPI_CMD_FINISH_IE BIT(16) #define SPI_CMD_PAUSE_IE BIT(17) +#define SPI_CMD_IPM_NONIDLE_MODE BIT(19) +#define SPI_CMD_IPM_SPIM_LOOP BIT(21) +#define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22 +#define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22) +#define SPI_CFG3_IPM_HALF_DUPLEX_DIR BIT(2) +#define SPI_CFG3_IPM_HALF_DUPLEX_EN BIT(3) #define MT8173_SPI_MAX_PAD_SEL 3 #define MTK_SPI_PAUSE_INT_STATUS 0x2 @@ -81,6 +89,7 @@ #define MTK_SPI_MAX_FIFO_SIZE 32U #define MTK_SPI_PACKET_SIZE 1024 +#define MTK_SPI_IPM_PACKET_SIZE SZ_64K #define MTK_SPI_32BITS_MASK (0xffffffff) #define DMA_ADDR_EXT_BITS (36) @@ -96,6 +105,9 @@ struct mtk_spi_compatible { bool dma_ext; /* some IC no need unprepare SPI clk */ bool no_need_unprepare; + /* IPM design adjust and extend register to support more features */ + bool ipm_design; + }; struct mtk_spi { @@ -119,6 +131,12 @@ static const struct mtk_spi_compatible mt2712_compat = { .must_tx = true, }; +static const struct mtk_spi_compatible mtk_ipm_compat = { + .enhance_timing = true, + .dma_ext = true, + .ipm_design = true, +}; + static const struct mtk_spi_compatible mt6765_compat = { .need_pad_sel = true, .must_tx = true, @@ -160,6 +178,9 @@ static const struct mtk_chip_config mtk_default_chip_info = { }; static const struct of_device_id mtk_spi_of_match[] = { + { .compatible = "mediatek,spi-ipm", + .data = (void *)&mtk_ipm_compat, + }, { .compatible = "mediatek,mt2701-spi", .data = (void *)&mtk_common_compat, }, @@ -278,12 +299,11 @@ static int mtk_spi_set_hw_cs_timing(struct spi_device *spi) return 0; } -static int mtk_spi_prepare_message(struct spi_master *master, - struct spi_message *msg) +static int mtk_spi_hw_init(struct spi_master *master, + struct spi_device *spi) { u16 cpha, cpol; u32 reg_val; - struct spi_device *spi = msg->spi; struct mtk_chip_config *chip_config = spi->controller_data; struct mtk_spi *mdata = spi_master_get_devdata(master); @@ -291,6 +311,15 @@ static int mtk_spi_prepare_message(struct spi_master *master, cpol = spi->mode & SPI_CPOL ? 1 : 0; reg_val = readl(mdata->base + SPI_CMD_REG); + if (mdata->dev_comp->ipm_design) { + /* SPI transfer without idle time until packet length done */ + reg_val |= SPI_CMD_IPM_NONIDLE_MODE; + if (spi->mode & SPI_LOOP) + reg_val |= SPI_CMD_IPM_SPIM_LOOP; + else + reg_val &= ~SPI_CMD_IPM_SPIM_LOOP; + } + if (cpha) reg_val |= SPI_CMD_CPHA; else @@ -348,23 +377,39 @@ static int mtk_spi_prepare_message(struct spi_master *master, mdata->base + SPI_PAD_SEL_REG); /* tick delay */ - reg_val = readl(mdata->base + SPI_CFG1_REG); if (mdata->dev_comp->enhance_timing) { - reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; - reg_val |= ((chip_config->tick_delay & 0x7) - << SPI_CFG1_GET_TICK_DLY_OFFSET); + if (mdata->dev_comp->ipm_design) { + reg_val = readl(mdata->base + SPI_CMD_REG); + reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; + reg_val |= ((chip_config->tick_delay & 0x7) + << SPI_CMD_IPM_GET_TICKDLY_OFFSET); + writel(reg_val, mdata->base + SPI_CMD_REG); + } else { + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; + reg_val |= ((chip_config->tick_delay & 0x7) + << SPI_CFG1_GET_TICK_DLY_OFFSET); + writel(reg_val, mdata->base + SPI_CFG1_REG); + } } else { + reg_val = readl(mdata->base + SPI_CFG1_REG); reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1; reg_val |= ((chip_config->tick_delay & 0x3) << SPI_CFG1_GET_TICK_DLY_OFFSET_V1); + writel(reg_val, mdata->base + SPI_CFG1_REG); } - writel(reg_val, mdata->base + SPI_CFG1_REG); /* set hw cs timing */ mtk_spi_set_hw_cs_timing(spi); return 0; } +static int mtk_spi_prepare_message(struct spi_master *master, + struct spi_message *msg) +{ + return mtk_spi_hw_init(master, msg->spi); +} + static void mtk_spi_set_cs(struct spi_device *spi, bool enable) { u32 reg_val; @@ -386,13 +431,13 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable) } static void mtk_spi_prepare_transfer(struct spi_master *master, - struct spi_transfer *xfer) + u32 speed_hz) { u32 div, sck_time, reg_val; struct mtk_spi *mdata = spi_master_get_devdata(master); - if (xfer->speed_hz < mdata->spi_clk_hz / 2) - div = DIV_ROUND_UP(mdata->spi_clk_hz, xfer->speed_hz); + if (speed_hz < mdata->spi_clk_hz / 2) + div = DIV_ROUND_UP(mdata->spi_clk_hz, speed_hz); else div = 1; @@ -423,12 +468,24 @@ static void mtk_spi_setup_packet(struct spi_master *master) u32 packet_size, packet_loop, reg_val; struct mtk_spi *mdata = spi_master_get_devdata(master); - packet_size = min_t(u32, mdata->xfer_len, MTK_SPI_PACKET_SIZE); + if (mdata->dev_comp->ipm_design) + packet_size = min_t(u32, + mdata->xfer_len, + MTK_SPI_IPM_PACKET_SIZE); + else + packet_size = min_t(u32, + mdata->xfer_len, + MTK_SPI_PACKET_SIZE); + packet_loop = mdata->xfer_len / packet_size; reg_val = readl(mdata->base + SPI_CFG1_REG); - reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK); + if (mdata->dev_comp->ipm_design) + reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK; + else + reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK; reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET; + reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK; reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET; writel(reg_val, mdata->base + SPI_CFG1_REG); } @@ -523,7 +580,7 @@ static int mtk_spi_fifo_transfer(struct spi_master *master, mdata->cur_transfer = xfer; mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len); mdata->num_xfered = 0; - mtk_spi_prepare_transfer(master, xfer); + mtk_spi_prepare_transfer(master, xfer->speed_hz); mtk_spi_setup_packet(master); if (xfer->tx_buf) { @@ -556,7 +613,7 @@ static int mtk_spi_dma_transfer(struct spi_master *master, mdata->cur_transfer = xfer; mdata->num_xfered = 0; - mtk_spi_prepare_transfer(master, xfer); + mtk_spi_prepare_transfer(master, xfer->speed_hz); cmd = readl(mdata->base + SPI_CMD_REG); if (xfer->tx_buf) @@ -591,6 +648,19 @@ static int mtk_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + u32 reg_val = 0; + + /* prepare xfer direction and duplex mode */ + if (mdata->dev_comp->ipm_design) { + if (!xfer->tx_buf || !xfer->rx_buf) { + reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN; + if (xfer->rx_buf) + reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR; + } + writel(reg_val, mdata->base + SPI_CFG3_IPM_REG); + } + if (master->can_dma(master, spi, xfer)) return mtk_spi_dma_transfer(master, spi, xfer); else @@ -757,6 +827,8 @@ static int mtk_spi_probe(struct platform_device *pdev) if (mdata->dev_comp->must_tx) master->flags = SPI_MASTER_MUST_TX; + if (mdata->dev_comp->ipm_design) + master->mode_bits |= SPI_LOOP; if (mdata->dev_comp->need_pad_sel) { mdata->pad_num = of_property_count_u32_elems( -- cgit From c9839acfcbe20ce43d363c2a9d0772472d9921c0 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Tue, 15 Mar 2022 02:31:38 +0000 Subject: spi: tegra20: Use of_device_get_match_data() Use of_device_get_match_data() to simplify the code. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Link: https://lore.kernel.org/r/20220315023138.2118293-1-chi.minghao@zte.com.cn Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 2a03739a0c60..80c3787deea9 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1006,14 +1006,8 @@ static int tegra_slink_probe(struct platform_device *pdev) struct resource *r; int ret, spi_irq; const struct tegra_slink_chip_data *cdata = NULL; - const struct of_device_id *match; - match = of_match_device(tegra_slink_of_match, &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "Error: No device match found\n"); - return -ENODEV; - } - cdata = match->data; + cdata = of_device_get_match_data(&pdev->dev); master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); if (!master) { -- cgit From ebc4cb43ea5ada3db46c80156fca58a54b9bbca8 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 16 Mar 2022 17:53:17 +0000 Subject: spi: Fix erroneous sgs value with min_t() While computing sgs in spi_map_buf(), the data type used in min_t() for max_seg_size is 'unsigned int' where as that of ctlr->max_dma_len is 'size_t'. min_t(unsigned int,x,y) gives wrong results if one of x/y is 'size_t' Consider the below examples on a 64-bit machine (ie size_t is 64-bits, and unsigned int is 32-bit). case 1) min_t(unsigned int, 5, 0x100000001); case 2) min_t(size_t, 5, 0x100000001); Case 1 returns '1', where as case 2 returns '5'. As you can see the result from case 1 is wrong. This patch fixes the above issue by using the data type of the parameters that are used in min_t with maximum data length. Fixes: commit 1a4e53d2fc4f68aa ("spi: Fix invalid sgs value") Reported-by: Linus Torvalds Suggested-by: Geert Uytterhoeven Signed-off-by: Biju Das Reviewed-by: Lad Prabhakar Link: https://lore.kernel.org/r/20220316175317.465-1-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 6937cf2d59e0..c4dd1200fe99 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1000,10 +1000,10 @@ int spi_map_buf(struct spi_controller *ctlr, struct device *dev, int i, ret; if (vmalloced_buf || kmap_buf) { - desc_len = min_t(unsigned int, max_seg_size, PAGE_SIZE); + desc_len = min_t(unsigned long, max_seg_size, PAGE_SIZE); sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len); } else if (virt_addr_valid(buf)) { - desc_len = min_t(unsigned int, max_seg_size, ctlr->max_dma_len); + desc_len = min_t(size_t, max_seg_size, ctlr->max_dma_len); sgs = DIV_ROUND_UP(len, desc_len); } else { return -EINVAL; -- cgit From 89b35e3f28514087d3f1e28e8f5634fbfd07c554 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Thu, 17 Mar 2022 16:14:26 -0500 Subject: spi: fsi: Implement a timeout for polling status The data transfer routines must poll the status register to determine when more data can be shifted in or out. If the hardware gets into a bad state, these polling loops may never exit. Prevent this by returning an error if a timeout is exceeded. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20220317211426.38940-1-eajames@linux.ibm.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index b6c7467f0b59..d403a7a3021d 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -25,6 +25,7 @@ #define SPI_FSI_BASE 0x70000 #define SPI_FSI_INIT_TIMEOUT_MS 1000 +#define SPI_FSI_STATUS_TIMEOUT_MS 100 #define SPI_FSI_MAX_RX_SIZE 8 #define SPI_FSI_MAX_TX_SIZE 40 @@ -299,6 +300,7 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, struct spi_transfer *transfer) { int rc = 0; + unsigned long end; u64 status = 0ULL; if (transfer->tx_buf) { @@ -315,10 +317,14 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, if (rc) return rc; + end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS); do { rc = fsi_spi_status(ctx, &status, "TX"); if (rc) return rc; + + if (time_after(jiffies, end)) + return -ETIMEDOUT; } while (status & SPI_FSI_STATUS_TDR_FULL); sent += nb; @@ -329,10 +335,14 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, u8 *rx = transfer->rx_buf; while (transfer->len > recv) { + end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS); do { rc = fsi_spi_status(ctx, &status, "RX"); if (rc) return rc; + + if (time_after(jiffies, end)) + return -ETIMEDOUT; } while (!(status & SPI_FSI_STATUS_RDR_FULL)); rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in); -- cgit