From 66c1c4175bbdfcf1cb1411b1ea62e7e0b5571594 Mon Sep 17 00:00:00 2001 From: Hardevsinh Palaniya Date: Tue, 8 Oct 2024 11:25:45 +0530 Subject: [PATCH 01/16] spi: spi-fsl-dspi: Fix casting warnings Sparse warnings: drivers/spi/spi-fsl-dspi.c:283:17: warning: incorrect type in assignment (different base types) drivers/spi/spi-fsl-dspi.c:283:17: expected unsigned int [usertype] drivers/spi/spi-fsl-dspi.c:283:17: got restricted __be32 [usertype] drivers/spi/spi-fsl-dspi.c:289:28: warning: cast to restricted __be32 drivers/spi/spi-fsl-dspi.c:289:28: warning: cast to restricted __be32 drivers/spi/spi-fsl-dspi.c:289:28: warning: cast to restricted __be32 drivers/spi/spi-fsl-dspi.c:289:28: warning: cast to restricted __be32 drivers/spi/spi-fsl-dspi.c:289:28: warning: cast to restricted __be32 drivers/spi/spi-fsl-dspi.c:289:28: warning: cast to restricted __be32 drivers/spi/spi-fsl-dspi.c:295:17: warning: incorrect type in assignment (different base types) drivers/spi/spi-fsl-dspi.c:295:17: expected unsigned int [usertype] drivers/spi/spi-fsl-dspi.c:295:17: got restricted __be16 [usertype] drivers/spi/spi-fsl-dspi.c:301:28: warning: cast to restricted __be16 drivers/spi/spi-fsl-dspi.c:301:28: warning: cast to restricted __be16 drivers/spi/spi-fsl-dspi.c:301:28: warning: cast to restricted __be16 drivers/spi/spi-fsl-dspi.c:301:28: warning: cast to restricted __be16 Signed-off-by: Hardevsinh Palaniya Reviewed-by: Vladimir Oltean Tested-by: Vladimir Oltean Link: https://patch.msgid.link/20241008055644.4900-2-hardevsinh.palaniya@siliconsignals.io Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 72044d76d736..b52950a0f5f9 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -280,25 +280,25 @@ static void dspi_native_dev_to_host(struct fsl_dspi *dspi, u32 rxdata) static void dspi_8on32_host_to_dev(struct fsl_dspi *dspi, u32 *txdata) { - *txdata = cpu_to_be32(*(u32 *)dspi->tx); + *txdata = (__force u32)cpu_to_be32(*(u32 *)dspi->tx); dspi->tx += sizeof(u32); } static void dspi_8on32_dev_to_host(struct fsl_dspi *dspi, u32 rxdata) { - *(u32 *)dspi->rx = be32_to_cpu(rxdata); + *(u32 *)dspi->rx = be32_to_cpu((__force __be32)rxdata); dspi->rx += sizeof(u32); } static void dspi_8on16_host_to_dev(struct fsl_dspi *dspi, u32 *txdata) { - *txdata = cpu_to_be16(*(u16 *)dspi->tx); + *txdata = (__force u32)cpu_to_be16(*(u16 *)dspi->tx); dspi->tx += sizeof(u16); } static void dspi_8on16_dev_to_host(struct fsl_dspi *dspi, u32 rxdata) { - *(u16 *)dspi->rx = be16_to_cpu(rxdata); + *(u16 *)dspi->rx = be16_to_cpu((__force __be16)rxdata); dspi->rx += sizeof(u16); } -- 2.51.0 From f3a59ab98cfc18c7b2fb1d8164bedbb1569a7e76 Mon Sep 17 00:00:00 2001 From: Hardevsinh Palaniya Date: Tue, 8 Oct 2024 11:25:46 +0530 Subject: [PATCH 02/16] spi: spi-imx: Fix casting warnings Sparse warnings: drivers/spi/spi-imx.c:410:19: warning: cast to restricted __be32 drivers/spi/spi-imx.c:410:19: warning: cast to restricted __be32 drivers/spi/spi-imx.c:410:19: warning: cast to restricted __be32 drivers/spi/spi-imx.c:410:19: warning: cast to restricted __be32 drivers/spi/spi-imx.c:410:19: warning: cast to restricted __be32 drivers/spi/spi-imx.c:410:19: warning: cast to restricted __be32 drivers/spi/spi-imx.c:439:21: warning: incorrect type in assignment (different base types) drivers/spi/spi-imx.c:439:21: expected unsigned int [addressable] [usertype] val drivers/spi/spi-imx.c:439:21: got restricted __be32 [usertype] Signed-off-by: Hardevsinh Palaniya Reviewed-by: Frank Li Link: https://patch.msgid.link/20241008055644.4900-3-hardevsinh.palaniya@siliconsignals.io Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index adfd7d74a984..84c3da1a1e22 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -407,7 +407,7 @@ static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx) static void mx53_ecspi_rx_target(struct spi_imx_data *spi_imx) { - u32 val = be32_to_cpu(readl(spi_imx->base + MXC_CSPIRXDATA)); + u32 val = ioread32be(spi_imx->base + MXC_CSPIRXDATA); if (spi_imx->rx_buf) { int n_bytes = spi_imx->target_burst % sizeof(val); @@ -436,13 +436,12 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx) if (spi_imx->tx_buf) { memcpy(((u8 *)&val) + sizeof(val) - n_bytes, spi_imx->tx_buf, n_bytes); - val = cpu_to_be32(val); spi_imx->tx_buf += n_bytes; } spi_imx->count -= n_bytes; - writel(val, spi_imx->base + MXC_CSPITXDATA); + iowrite32be(val, spi_imx->base + MXC_CSPITXDATA); } /* MX51 eCSPI */ -- 2.51.0 From f7bc15211fc6946203dd7e57c123f1e387d7225b Mon Sep 17 00:00:00 2001 From: Dragan Simic Date: Sun, 29 Sep 2024 11:21:13 +0200 Subject: [PATCH 03/16] spi: rockchip: Perform trivial code cleanups Perform a few trivial code cleanups, to obey the reverse Christmas tree rule, to avoid unnecessary line wrapping by using the 100-column width better, to actually obey the 100-column width in one case, and to make the way a couple of wrapped function arguments are indented a bit more readable. No intended functional changes are introduced by these code cleanups. Reviewed-by: Heiko Stuebner Signed-off-by: Dragan Simic Link: https://patch.msgid.link/1b55380a0b9f0e8fe1a09611636b30e232b95d08.1727601608.git.dsimic@manjaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 0bb33c43b1b4..81046f7b7591 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -742,22 +742,20 @@ static int rockchip_spi_setup(struct spi_device *spi) static int rockchip_spi_probe(struct platform_device *pdev) { - int ret; - struct rockchip_spi *rs; + struct device_node *np = pdev->dev.of_node; struct spi_controller *ctlr; + struct rockchip_spi *rs; struct resource *mem; - struct device_node *np = pdev->dev.of_node; u32 rsd_nsecs, num_cs; bool target_mode; + int ret; target_mode = of_property_read_bool(np, "spi-slave"); if (target_mode) - ctlr = spi_alloc_target(&pdev->dev, - sizeof(struct rockchip_spi)); + ctlr = spi_alloc_target(&pdev->dev, sizeof(struct rockchip_spi)); else - ctlr = spi_alloc_host(&pdev->dev, - sizeof(struct rockchip_spi)); + ctlr = spi_alloc_host(&pdev->dev, sizeof(struct rockchip_spi)); if (!ctlr) return -ENOMEM; @@ -769,7 +767,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) /* Get basic io resource and map it */ rs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(rs->regs)) { - ret = PTR_ERR(rs->regs); + ret = PTR_ERR(rs->regs); goto err_put_ctlr; } @@ -794,7 +792,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_put_ctlr; ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL, - IRQF_ONESHOT, dev_name(&pdev->dev), ctlr); + IRQF_ONESHOT, dev_name(&pdev->dev), ctlr); if (ret) goto err_put_ctlr; @@ -804,16 +802,15 @@ static int rockchip_spi_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns", &rsd_nsecs)) { /* rx sample delay is expressed in parent clock cycles (max 3) */ - u32 rsd = DIV_ROUND_CLOSEST(rsd_nsecs * (rs->freq >> 8), - 1000000000 >> 8); + u32 rsd = DIV_ROUND_CLOSEST(rsd_nsecs * (rs->freq >> 8), 1000000000 >> 8); if (!rsd) { dev_warn(rs->dev, "%u Hz are too slow to express %u ns delay\n", - rs->freq, rsd_nsecs); + rs->freq, rsd_nsecs); } else if (rsd > CR0_RSD_MAX) { rsd = CR0_RSD_MAX; - dev_warn(rs->dev, "%u Hz are too fast to express %u ns delay, clamping at %u ns\n", - rs->freq, rsd_nsecs, - CR0_RSD_MAX * 1000000000U / rs->freq); + dev_warn(rs->dev, + "%u Hz are too fast to express %u ns delay, clamping at %u ns\n", + rs->freq, rsd_nsecs, CR0_RSD_MAX * 1000000000U / rs->freq); } rs->rsd = rsd; } -- 2.51.0 From cb91287b3b6d42e66f948fbc304f771792c2852f Mon Sep 17 00:00:00 2001 From: Dragan Simic Date: Sun, 29 Sep 2024 11:21:14 +0200 Subject: [PATCH 04/16] spi: rockchip-sfc: Perform trivial code cleanups Perform a couple of trivial code cleanups, to avoid unnecessary line wrapping by using the 100-column width a bit better, and to drop a stray empty line. No intended functional changes are introduced by these code cleanups. Reviewed-by: Heiko Stuebner Signed-off-by: Dragan Simic Link: https://patch.msgid.link/4dcd5d9cc4a20c9c6ad504d945475b767399b32f.1727601608.git.dsimic@manjaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip-sfc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index 0d7fadcd4ed3..505d5089bf03 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -591,8 +591,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev) return PTR_ERR(sfc->hclk); } - sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, - "rockchip,sfc-no-dma"); + sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma"); if (sfc->use_dma) { ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); @@ -602,8 +601,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev) } sfc->buffer = dmam_alloc_coherent(dev, SFC_MAX_IOSIZE_VER3, - &sfc->dma_buffer, - GFP_KERNEL); + &sfc->dma_buffer, GFP_KERNEL); if (!sfc->buffer) return -ENOMEM; } @@ -629,7 +627,6 @@ static int rockchip_sfc_probe(struct platform_device *pdev) 0, pdev->name, sfc); if (ret) { dev_err(dev, "Failed to request irq\n"); - goto err_irq; } -- 2.51.0 From 7d46b8d8d78338a2ad986eec0790ddb22fad23a8 Mon Sep 17 00:00:00 2001 From: Dragan Simic Date: Sun, 29 Sep 2024 11:21:15 +0200 Subject: [PATCH 05/16] spi: rockchip-sfc: Use dev_err_probe() in the probe path Use function dev_err_probe() in the probe path instead of dev_err() where appropriate, to make the code a bit more uniform and compact. Reviewed-by: Heiko Stuebner Signed-off-by: Dragan Simic Link: https://patch.msgid.link/398229ef316e64dc0c27944ea793dcddef1ead4e.1727601608.git.dsimic@manjaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip-sfc.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c index 505d5089bf03..7e0fb4944a34 100644 --- a/drivers/spi/spi-rockchip-sfc.c +++ b/drivers/spi/spi-rockchip-sfc.c @@ -580,16 +580,14 @@ static int rockchip_sfc_probe(struct platform_device *pdev) return PTR_ERR(sfc->regbase); sfc->clk = devm_clk_get(&pdev->dev, "clk_sfc"); - if (IS_ERR(sfc->clk)) { - dev_err(&pdev->dev, "Failed to get sfc interface clk\n"); - return PTR_ERR(sfc->clk); - } + if (IS_ERR(sfc->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sfc->clk), + "Failed to get sfc interface clk\n"); sfc->hclk = devm_clk_get(&pdev->dev, "hclk_sfc"); - if (IS_ERR(sfc->hclk)) { - dev_err(&pdev->dev, "Failed to get sfc ahb clk\n"); - return PTR_ERR(sfc->hclk); - } + if (IS_ERR(sfc->hclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(sfc->hclk), + "Failed to get sfc ahb clk\n"); sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma"); -- 2.51.0 From 36e69b160705b65bf136c2fb6a1194447eeb8478 Mon Sep 17 00:00:00 2001 From: Dragan Simic Date: Sun, 29 Sep 2024 11:21:16 +0200 Subject: [PATCH 06/16] driver core: Add device probe log helper dev_warn_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Some drivers can still provide their functionality to a certain extent even when some of their resource acquisitions eventually fail. In such cases, emitting errors isn't the desired action, but warnings should be emitted instead. To solve this, introduce dev_warn_probe() as a new device probe log helper, which behaves identically as the already existing dev_err_probe(), while it produces warnings instead of errors. The intended use is with the resources that are actually optional for a particular driver. While there, copyedit the kerneldoc for dev_err_probe() a bit, to simplify its wording a bit, and reuse it as the kerneldoc for dev_warn_probe(), with the necessary wording adjustments, of course. Signed-off-by: Dragan Simic Tested-by: Hélène Vulquin Acked-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2be0a28538bb2a3d1bcc91e2ca1f2d0dc09146d9.1727601608.git.dsimic@manjaro.org Signed-off-by: Mark Brown --- drivers/base/core.c | 129 +++++++++++++++++++++++++++++-------- include/linux/dev_printk.h | 1 + 2 files changed, 102 insertions(+), 28 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index a4c853411a6b..a84a7b952cfd 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4980,6 +4980,49 @@ define_dev_printk_level(_dev_info, KERN_INFO); #endif +static void __dev_probe_failed(const struct device *dev, int err, bool fatal, + const char *fmt, va_list vargsp) +{ + struct va_format vaf; + va_list vargs; + + /* + * On x86_64 and possibly on other architectures, va_list is actually a + * size-1 array containing a structure. As a result, function parameter + * vargsp decays from T[1] to T*, and &vargsp has type T** rather than + * T(*)[1], which is expected by its assignment to vaf.va below. + * + * One standard way to solve this mess is by creating a copy in a local + * variable of type va_list and then using a pointer to that local copy + * instead, which is the approach employed here. + */ + va_copy(vargs, vargsp); + + vaf.fmt = fmt; + vaf.va = &vargs; + + switch (err) { + case -EPROBE_DEFER: + device_set_deferred_probe_reason(dev, &vaf); + dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf); + break; + + case -ENOMEM: + /* Don't print anything on -ENOMEM, there's already enough output */ + break; + + default: + /* Log fatal final failures as errors, otherwise produce warnings */ + if (fatal) + dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf); + else + dev_warn(dev, "error %pe: %pV", ERR_PTR(err), &vaf); + break; + } + + va_end(vargs); +} + /** * dev_err_probe - probe error check and log helper * @dev: the pointer to the struct device @@ -4992,7 +5035,7 @@ define_dev_printk_level(_dev_info, KERN_INFO); * -EPROBE_DEFER and propagate error upwards. * In case of -EPROBE_DEFER it sets also defer probe reason, which can be * checked later by reading devices_deferred debugfs attribute. - * It replaces code sequence:: + * It replaces the following code sequence:: * * if (err != -EPROBE_DEFER) * dev_err(dev, ...); @@ -5004,47 +5047,77 @@ define_dev_printk_level(_dev_info, KERN_INFO); * * return dev_err_probe(dev, err, ...); * - * Using this helper in your probe function is totally fine even if @err is - * known to never be -EPROBE_DEFER. + * Using this helper in your probe function is totally fine even if @err + * is known to never be -EPROBE_DEFER. * The benefit compared to a normal dev_err() is the standardized format - * of the error code, it being emitted symbolically (i.e. you get "EAGAIN" - * instead of "-35") and the fact that the error code is returned which allows - * more compact error paths. + * of the error code, which is emitted symbolically (i.e. you get "EAGAIN" + * instead of "-35"), and having the error code returned allows more + * compact error paths. * * Returns @err. */ int dev_err_probe(const struct device *dev, int err, const char *fmt, ...) { - struct va_format vaf; - va_list args; + va_list vargs; - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; + va_start(vargs, fmt); - switch (err) { - case -EPROBE_DEFER: - device_set_deferred_probe_reason(dev, &vaf); - dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf); - break; + /* Use dev_err() for logging when err doesn't equal -EPROBE_DEFER */ + __dev_probe_failed(dev, err, true, fmt, vargs); - case -ENOMEM: - /* - * We don't print anything on -ENOMEM, there is already enough - * output. - */ - break; + va_end(vargs); - default: - dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf); - break; - } + return err; +} +EXPORT_SYMBOL_GPL(dev_err_probe); - va_end(args); +/** + * dev_warn_probe - probe error check and log helper + * @dev: the pointer to the struct device + * @err: error value to test + * @fmt: printf-style format string + * @...: arguments as specified in the format string + * + * This helper implements common pattern present in probe functions for error + * checking: print debug or warning message depending if the error value is + * -EPROBE_DEFER and propagate error upwards. + * In case of -EPROBE_DEFER it sets also defer probe reason, which can be + * checked later by reading devices_deferred debugfs attribute. + * It replaces the following code sequence:: + * + * if (err != -EPROBE_DEFER) + * dev_warn(dev, ...); + * else + * dev_dbg(dev, ...); + * return err; + * + * with:: + * + * return dev_warn_probe(dev, err, ...); + * + * Using this helper in your probe function is totally fine even if @err + * is known to never be -EPROBE_DEFER. + * The benefit compared to a normal dev_warn() is the standardized format + * of the error code, which is emitted symbolically (i.e. you get "EAGAIN" + * instead of "-35"), and having the error code returned allows more + * compact error paths. + * + * Returns @err. + */ +int dev_warn_probe(const struct device *dev, int err, const char *fmt, ...) +{ + va_list vargs; + + va_start(vargs, fmt); + + /* Use dev_warn() for logging when err doesn't equal -EPROBE_DEFER */ + __dev_probe_failed(dev, err, false, fmt, vargs); + + va_end(vargs); return err; } -EXPORT_SYMBOL_GPL(dev_err_probe); +EXPORT_SYMBOL_GPL(dev_warn_probe); static inline bool fwnode_is_primary(struct fwnode_handle *fwnode) { diff --git a/include/linux/dev_printk.h b/include/linux/dev_printk.h index ca32b5bb28eb..eb2094e43050 100644 --- a/include/linux/dev_printk.h +++ b/include/linux/dev_printk.h @@ -276,6 +276,7 @@ do { \ dev_driver_string(dev), dev_name(dev), ## arg) __printf(3, 4) int dev_err_probe(const struct device *dev, int err, const char *fmt, ...); +__printf(3, 4) int dev_warn_probe(const struct device *dev, int err, const char *fmt, ...); /* Simple helper for dev_err_probe() when ERR_PTR() is to be returned. */ #define dev_err_ptr_probe(dev, ___err, fmt, ...) \ -- 2.51.0 From e2fc05873905f2ee96b38a116ae86f45fe7d8e49 Mon Sep 17 00:00:00 2001 From: Dragan Simic Date: Sun, 29 Sep 2024 11:21:17 +0200 Subject: [PATCH 07/16] spi: rockchip: Use dev_{err,warn}_probe() in the probe path MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Use function dev_err_probe() in the probe path instead of dev_err() where appropriate, to make the code a bit more uniform and compact. Use the new function dev_warn_probe() to improve error handling for the TX and RX DMA channel requests, which are actually optional, and tweak the logged warnings a bit to additionally describe their optional nature. Previously, deferred requests for the TX and RX DMA channels produced no debug messages, and the final error messages didn't include the error codes, which are all highly useful when debugging permanently failed DMA channel requests, such as when the required drivers aren't enabled. Suggested-by: Hélene Vulquin Signed-off-by: Dragan Simic Tested-by: Hélène Vulquin Link: https://patch.msgid.link/5b6bd142dab3ab93d7039db3e2fdcfea6bee2217.1727601608.git.dsimic@manjaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 81046f7b7591..5b1857b25d31 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -773,15 +773,15 @@ static int rockchip_spi_probe(struct platform_device *pdev) rs->apb_pclk = devm_clk_get_enabled(&pdev->dev, "apb_pclk"); if (IS_ERR(rs->apb_pclk)) { - dev_err(&pdev->dev, "Failed to get apb_pclk\n"); - ret = PTR_ERR(rs->apb_pclk); + ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->apb_pclk), + "Failed to get apb_pclk\n"); goto err_put_ctlr; } rs->spiclk = devm_clk_get_enabled(&pdev->dev, "spiclk"); if (IS_ERR(rs->spiclk)) { - dev_err(&pdev->dev, "Failed to get spi_pclk\n"); - ret = PTR_ERR(rs->spiclk); + ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->spiclk), + "Failed to get spi_pclk\n"); goto err_put_ctlr; } @@ -817,8 +817,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) rs->fifo_len = get_fifo_len(rs); if (!rs->fifo_len) { - dev_err(&pdev->dev, "Failed to get fifo length\n"); - ret = -EINVAL; + ret = dev_err_probe(&pdev->dev, -EINVAL, "Failed to get fifo length\n"); goto err_put_ctlr; } @@ -858,22 +857,21 @@ static int rockchip_spi_probe(struct platform_device *pdev) ctlr->dma_tx = dma_request_chan(rs->dev, "tx"); if (IS_ERR(ctlr->dma_tx)) { - /* Check tx to see if we need defer probing driver */ - if (PTR_ERR(ctlr->dma_tx) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + /* Check tx to see if we need to defer driver probing */ + ret = dev_warn_probe(rs->dev, PTR_ERR(ctlr->dma_tx), + "Failed to request optional TX DMA channel\n"); + if (ret == -EPROBE_DEFER) goto err_disable_pm_runtime; - } - dev_warn(rs->dev, "Failed to request TX DMA channel\n"); ctlr->dma_tx = NULL; } ctlr->dma_rx = dma_request_chan(rs->dev, "rx"); if (IS_ERR(ctlr->dma_rx)) { - if (PTR_ERR(ctlr->dma_rx) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + /* Check rx to see if we need to defer driver probing */ + ret = dev_warn_probe(rs->dev, PTR_ERR(ctlr->dma_rx), + "Failed to request optional RX DMA channel\n"); + if (ret == -EPROBE_DEFER) goto err_free_dma_tx; - } - dev_warn(rs->dev, "Failed to request RX DMA channel\n"); ctlr->dma_rx = NULL; } -- 2.51.0 From b1258105f9ce5203f48a47fd2f2cec8c38c41841 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 9 Oct 2024 09:22:44 +0300 Subject: [PATCH 08/16] spi: intel: Add protected and locked attributes The manufacturing access to the PCH/SoC SPI device is traditionally performed via userspace driver accessing registers via /dev/mem but due to security concerns /dev/mem access is being much restricted, hence the reason for utilizing dedicated Intel PCH/SoC SPI controller driver, which is already implemented in the Linux kernel. Intel PCH/SoC SPI controller protects the flash storage via two mechanisms one is the via region protection registers and second via BIOS lock. The BIOS locks only the BIOS regions usually 0 and/or 6. The device always boots with BIOS lock set, but during manufacturing the BIOS lock has to be lifted in order to enable the write access. This can be done by passing "writeable=1" in the command line when the driver is loaded. This "locked" state is exposed through new sysfs attributes (intel_spi_locked, intel_spi_bios_locked). Second, also the region protection status is exposed via sysfs attribute (intel_spi_protected) as the manufacturing will need the both files in order to validate that the device is properly sealed. Includes code written by Tamar Mashiah. Signed-off-by: Alexander Usyskin Co-developed-by: Tomas Winkler Signed-off-by: Tomas Winkler Reviewed-by: Andy Shevchenko Signed-off-by: Mika Westerberg Link: https://patch.msgid.link/20241009062244.2436793-1-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown --- .../ABI/testing/sysfs-driver-spi-intel | 20 ++++++ drivers/spi/spi-intel-pci.c | 1 + drivers/spi/spi-intel-platform.c | 1 + drivers/spi/spi-intel.c | 64 +++++++++++++++++-- drivers/spi/spi-intel.h | 2 + 5 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-spi-intel diff --git a/Documentation/ABI/testing/sysfs-driver-spi-intel b/Documentation/ABI/testing/sysfs-driver-spi-intel new file mode 100644 index 000000000000..d7c9139ddbf3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-spi-intel @@ -0,0 +1,20 @@ +What: /sys/devices/.../intel_spi_protected +Date: Feb 2025 +KernelVersion: 6.13 +Contact: Alexander Usyskin +Description: This attribute allows the userspace to check if the + Intel SPI flash controller is write protected from the host. + +What: /sys/devices/.../intel_spi_locked +Date: Feb 2025 +KernelVersion: 6.13 +Contact: Alexander Usyskin +Description: This attribute allows the user space to check if the + Intel SPI flash controller locks supported opcodes. + +What: /sys/devices/.../intel_spi_bios_locked +Date: Feb 2025 +KernelVersion: 6.13 +Contact: Alexander Usyskin +Description: This attribute allows the user space to check if the + Intel SPI flash controller BIOS region is locked for writes. diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c index 4337ca51d7aa..c3b54928143d 100644 --- a/drivers/spi/spi-intel-pci.c +++ b/drivers/spi/spi-intel-pci.c @@ -94,6 +94,7 @@ static struct pci_driver intel_spi_pci_driver = { .name = "intel-spi", .id_table = intel_spi_pci_ids, .probe = intel_spi_pci_probe, + .dev_groups = intel_spi_groups, }; module_pci_driver(intel_spi_pci_driver); diff --git a/drivers/spi/spi-intel-platform.c b/drivers/spi/spi-intel-platform.c index 2ef09fa35661..0974cca83a5d 100644 --- a/drivers/spi/spi-intel-platform.c +++ b/drivers/spi/spi-intel-platform.c @@ -28,6 +28,7 @@ static struct platform_driver intel_spi_platform_driver = { .probe = intel_spi_platform_probe, .driver = { .name = "intel-spi", + .dev_groups = intel_spi_groups, }, }; diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 795b7e72baea..b0dcdb6fb8fa 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -148,6 +148,8 @@ * @pr_num: Maximum number of protected range registers * @chip0_size: Size of the first flash chip in bytes * @locked: Is SPI setting locked + * @protected: Whether the regions are write protected + * @bios_locked: Is BIOS region 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 @@ -166,6 +168,8 @@ struct intel_spi { size_t pr_num; size_t chip0_size; bool locked; + bool protected; + bool bios_locked; bool swseq_reg; bool swseq_erase; u8 atomic_preopcode; @@ -1109,10 +1113,13 @@ static int intel_spi_init(struct intel_spi *ispi) 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; + ispi->bios_locked = true; + /* Try to disable BIOS write protection if user asked to do so */ + if (writeable) { + if (intel_spi_set_writeable(ispi)) + ispi->bios_locked = false; + else + dev_warn(ispi->dev, "can't disable chip write protection\n"); } /* Disable #SMI generation from HW sequencer */ @@ -1247,8 +1254,10 @@ static void intel_spi_fill_partition(struct intel_spi *ispi, * 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)) + if (!writeable || intel_spi_is_protected(ispi, base, limit)) { part->mask_flags |= MTD_WRITEABLE; + ispi->protected = true; + } end = (limit << 12) + 4096; if (end > part->size) @@ -1411,6 +1420,50 @@ static int intel_spi_populate_chip(struct intel_spi *ispi) return 0; } +static ssize_t intel_spi_protected_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_spi *ispi = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", ispi->protected); +} +static DEVICE_ATTR_ADMIN_RO(intel_spi_protected); + +static ssize_t intel_spi_locked_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_spi *ispi = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", ispi->locked); +} +static DEVICE_ATTR_ADMIN_RO(intel_spi_locked); + +static ssize_t intel_spi_bios_locked_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_spi *ispi = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", ispi->bios_locked); +} +static DEVICE_ATTR_ADMIN_RO(intel_spi_bios_locked); + +static struct attribute *intel_spi_attrs[] = { + &dev_attr_intel_spi_protected.attr, + &dev_attr_intel_spi_locked.attr, + &dev_attr_intel_spi_bios_locked.attr, + NULL +}; + +static const struct attribute_group intel_spi_attr_group = { + .attrs = intel_spi_attrs, +}; + +const struct attribute_group *intel_spi_groups[] = { + &intel_spi_attr_group, + NULL +}; +EXPORT_SYMBOL_GPL(intel_spi_groups); + /** * intel_spi_probe() - Probe the Intel SPI flash controller * @dev: Pointer to the parent device @@ -1451,6 +1504,7 @@ int intel_spi_probe(struct device *dev, struct resource *mem, if (ret) return ret; + dev_set_drvdata(dev, ispi); return intel_spi_populate_chip(ispi); } EXPORT_SYMBOL_GPL(intel_spi_probe); diff --git a/drivers/spi/spi-intel.h b/drivers/spi/spi-intel.h index a4f0327a46ff..c5f35060dd63 100644 --- a/drivers/spi/spi-intel.h +++ b/drivers/spi/spi-intel.h @@ -13,6 +13,8 @@ struct resource; +extern const struct attribute_group *intel_spi_groups[]; + int intel_spi_probe(struct device *dev, struct resource *mem, const struct intel_spi_boardinfo *info); -- 2.51.0 From 941584e2f3ddde26e4d71941ebc0836ece181594 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Thu, 10 Oct 2024 15:33:03 +0200 Subject: [PATCH 09/16] spi: stm32: fix missing device mode capability in stm32mp25 The STM32MP25 SOC has capability to behave in device mode however missing .has_device_mode within its stm32mp25_spi_cfg structure leads to not being able to enable the device mode. Fixes: f6cd66231aa5 ("spi: stm32: add st,stm32mp25-spi compatible supporting STM32MP25 soc") Cc: stable@vger.kernel.org Signed-off-by: Alain Volmat Link: https://patch.msgid.link/20241010-spi-mp25-device-fix-v2-1-d13920de473d@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index f2dd8ab12df8..da3517d7102d 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -2044,6 +2044,7 @@ static const struct stm32_spi_cfg stm32mp25_spi_cfg = { .baud_rate_div_max = STM32H7_SPI_MBR_DIV_MAX, .has_fifo = true, .prevent_dma_burst = true, + .has_device_mode = true, }; static const struct of_device_id stm32_spi_of_match[] = { -- 2.51.0 From 4de1cdb3c299bb98d70198c1fa20c71f0893835c Mon Sep 17 00:00:00 2001 From: Karan Sanghavi Date: Thu, 17 Oct 2024 15:02:16 +0000 Subject: [PATCH 10/16] spi: dt-bindings: brcm,bcm2835-aux-spi: Convert to dtschema Convert bcm2835-aux-spi binding to Dt schema Signed-off-by: Karan Sanghavi Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/ZxEm-H-PjlQyXeOH@Emma Signed-off-by: Mark Brown --- .../bindings/spi/brcm,bcm2835-aux-spi.txt | 38 ------------- .../bindings/spi/brcm,bcm2835-aux-spi.yaml | 53 +++++++++++++++++++ 2 files changed, 53 insertions(+), 38 deletions(-) delete mode 100644 Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt create mode 100644 Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt b/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt deleted file mode 100644 index d7668f41b03b..000000000000 --- a/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt +++ /dev/null @@ -1,38 +0,0 @@ -Broadcom BCM2835 auxiliary SPI1/2 controller - -The BCM2835 contains two forms of SPI master controller, one known simply as -SPI0, and the other known as the "Universal SPI Master"; part of the -auxiliary block. This binding applies to the SPI1/2 controller. - -Required properties: -- compatible: Should be "brcm,bcm2835-aux-spi". -- reg: Should contain register location and length for the spi block -- interrupts: Should contain shared interrupt of the aux block -- clocks: The clock feeding the SPI controller - needs to - point to the auxiliary clock driver of the bcm2835, - as this clock will enable the output gate for the specific - clock. -- cs-gpios: the cs-gpios (native cs is NOT supported) - see also spi-bus.txt - -Example: - -spi1@7e215080 { - compatible = "brcm,bcm2835-aux-spi"; - reg = <0x7e215080 0x40>; - interrupts = <1 29>; - clocks = <&aux_clocks BCM2835_AUX_CLOCK_SPI1>; - #address-cells = <1>; - #size-cells = <0>; - cs-gpios = <&gpio 18>, <&gpio 17>, <&gpio 16>; -}; - -spi2@7e2150c0 { - compatible = "brcm,bcm2835-aux-spi"; - reg = <0x7e2150c0 0x40>; - interrupts = <1 29>; - clocks = <&aux_clocks BCM2835_AUX_CLOCK_SPI2>; - #address-cells = <1>; - #size-cells = <0>; - cs-gpios = <&gpio 43>, <&gpio 44>, <&gpio 45>; -}; diff --git a/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.yaml b/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.yaml new file mode 100644 index 000000000000..561319544ee3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/brcm,bcm2835-aux-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM2835 Auxiliary SPI1/2 Controller + +maintainers: + - Karan Sanghavi + +description: + The BCM2835 contains two forms of SPI master controller. One is known simply + as SPI0, and the other as the "Universal SPI Master," which is part of the + auxiliary block. This binding applies to the SPI1 and SPI2 auxiliary + controllers. + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + enum: + - brcm,bcm2835-aux-spi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + spi@7e215080 { + compatible = "brcm,bcm2835-aux-spi"; + reg = <0x7e215080 0x40>; + interrupts = <1 29>; + clocks = <&aux_clocks BCM2835_AUX_CLOCK_SPI1>; + #address-cells = <1>; + #size-cells = <0>; + }; -- 2.51.0 From 36dbe4521a381fd4d2561a90200ae4a2a3efb222 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 18 Oct 2024 14:24:37 +0200 Subject: [PATCH 11/16] spi: make class structs const The two instances of struct class are only used here in functions that take const pointers and so can too be made constant. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20241018122437.64275-1-brgl@bgdev.pl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7c5e76b15421..5528c46edd0e 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2926,7 +2926,7 @@ static void spi_controller_release(struct device *dev) kfree(ctlr); } -static struct class spi_master_class = { +static const struct class spi_master_class = { .name = "spi_master", .dev_release = spi_controller_release, .dev_groups = spi_master_groups, @@ -3016,7 +3016,7 @@ static const struct attribute_group *spi_slave_groups[] = { NULL, }; -static struct class spi_slave_class = { +static const struct class spi_slave_class = { .name = "spi_slave", .dev_release = spi_controller_release, .dev_groups = spi_slave_groups, -- 2.51.0 From eef26f1c6179eee5b622362b324a0a72dafb5c16 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 16 Oct 2024 11:54:32 +1300 Subject: [PATCH 12/16] dt-bindings: spi: Add realtek,rtl9301-snand Add a dtschema for the SPI-NAND controller on the RTL9300 SoCs. The controller supports * Serial/Dual/Quad data with * PIO and DMA data read/write operation * Configurable flash access timing Signed-off-by: Chris Packham Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20241015225434.3970360-2-chris.packham@alliedtelesis.co.nz Signed-off-by: Mark Brown --- .../bindings/spi/realtek,rtl9301-snand.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml diff --git a/Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml b/Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml new file mode 100644 index 000000000000..36d79a90552b --- /dev/null +++ b/Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/realtek,rtl9301-snand.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SPI-NAND Flash Controller for Realtek RTL9300 SoCs + +maintainers: + - Chris Packham + +description: + The Realtek RTL9300 SoCs have a built in SPI-NAND controller. It supports + typical SPI-NAND page cache operations in single, dual or quad IO mode. + +properties: + compatible: + oneOf: + - items: + - enum: + - realtek,rtl9302b-snand + - realtek,rtl9302c-snand + - realtek,rtl9303-snand + - const: realtek,rtl9301-snand + - const: realtek,rtl9301-snand + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +unevaluatedProperties: false + +examples: + - | + spi@1a400 { + compatible = "realtek,rtl9302c-snand", "realtek,rtl9301-snand"; + reg = <0x1a400 0x44>; + interrupt-parent = <&intc>; + interrupts = <19>; + clocks = <&lx_clk>; + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "spi-nand"; + reg = <0>; + }; + }; -- 2.51.0 From 42d20a6a61b8fccbb57d80df1ccde7dd82d5bbd6 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 16 Oct 2024 11:54:34 +1300 Subject: [PATCH 13/16] spi: spi-mem: Add Realtek SPI-NAND controller Add a driver for the SPI-NAND controller on the RTL9300 family of devices. The controller supports * Serial/Dual/Quad data with * PIO and DMA data read/write operation * Configurable flash access timing There is a separate ECC controller on the RTL9300 which isn't currently supported (instead we rely on the on-die ECC supported by most SPI-NAND chips). Signed-off-by: Chris Packham Link: https://patch.msgid.link/20241015225434.3970360-4-chris.packham@alliedtelesis.co.nz Signed-off-by: Mark Brown --- MAINTAINERS | 6 + drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-realtek-rtl-snand.c | 405 ++++++++++++++++++++++++++++ 4 files changed, 423 insertions(+) create mode 100644 drivers/spi/spi-realtek-rtl-snand.c diff --git a/MAINTAINERS b/MAINTAINERS index a097afd76ded..174cbe46cac3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19538,6 +19538,12 @@ S: Maintained F: Documentation/devicetree/bindings/net/dsa/realtek.yaml F: drivers/net/dsa/realtek/* +REALTEK SPI-NAND +M: Chris Packham +S: Maintained +F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml +F: drivers/spi/spi-realtek-rtl-snand.c + REALTEK WIRELESS DRIVER (rtlwifi family) M: Ping-Ke Shih L: linux-wireless@vger.kernel.org diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 823797217404..7133bb72d1c8 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -843,6 +843,17 @@ config SPI_PXA2XX config SPI_PXA2XX_PCI def_tristate SPI_PXA2XX && PCI && COMMON_CLK +config SPI_REALTEK_SNAND + tristate "Realtek SPI-NAND Flash Controller" + depends on MACH_REALTEK_RTL || COMPILE_TEST + select REGMAP + help + This enables support for the SPI-NAND Flash controller on + Realtek SoCs. + + This driver does not support generic SPI. The implementation + only supports the spi-mem interface. + config SPI_ROCKCHIP tristate "Rockchip SPI controller driver" depends on ARCH_ROCKCHIP || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index a9b1bc259b68..9a3338236645 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o +obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o diff --git a/drivers/spi/spi-realtek-rtl-snand.c b/drivers/spi/spi-realtek-rtl-snand.c new file mode 100644 index 000000000000..23c42c8469e4 --- /dev/null +++ b/drivers/spi/spi-realtek-rtl-snand.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SNAFCFR 0x00 +#define SNAFCFR_DMA_IE BIT(20) +#define SNAFCCR 0x04 +#define SNAFWCMR 0x08 +#define SNAFRCMR 0x0c +#define SNAFRDR 0x10 +#define SNAFWDR 0x14 +#define SNAFDTR 0x18 +#define SNAFDRSAR 0x1c +#define SNAFDIR 0x20 +#define SNAFDIR_DMA_IP BIT(0) +#define SNAFDLR 0x24 +#define SNAFSR 0x40 +#define SNAFSR_NFCOS BIT(3) +#define SNAFSR_NFDRS BIT(2) +#define SNAFSR_NFDWS BIT(1) + +#define CMR_LEN(len) ((len) - 1) +#define CMR_WID(width) (((width) >> 1) << 28) + +struct rtl_snand { + struct device *dev; + struct regmap *regmap; + struct completion comp; +}; + +static irqreturn_t rtl_snand_irq(int irq, void *data) +{ + struct rtl_snand *snand = data; + u32 val = 0; + + regmap_read(snand->regmap, SNAFSR, &val); + if (val & (SNAFSR_NFCOS | SNAFSR_NFDRS | SNAFSR_NFDWS)) + return IRQ_NONE; + + regmap_write(snand->regmap, SNAFDIR, SNAFDIR_DMA_IP); + complete(&snand->comp); + + return IRQ_HANDLED; +} + +static bool rtl_snand_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (!spi_mem_default_supports_op(mem, op)) + return false; + if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1) + return false; + return true; +} + +static void rtl_snand_set_cs(struct rtl_snand *snand, int cs, bool active) +{ + u32 val; + + if (active) + val = ~(1 << (4 * cs)); + else + val = ~0; + + regmap_write(snand->regmap, SNAFCCR, val); +} + +static int rtl_snand_wait_ready(struct rtl_snand *snand) +{ + u32 val; + + return regmap_read_poll_timeout(snand->regmap, SNAFSR, val, !(val & SNAFSR_NFCOS), + 0, 2 * USEC_PER_MSEC); +} + +static int rtl_snand_xfer_head(struct rtl_snand *snand, int cs, const struct spi_mem_op *op) +{ + int ret; + u32 val, len = 0; + + rtl_snand_set_cs(snand, cs, true); + + val = op->cmd.opcode << 24; + len = 1; + if (op->addr.nbytes && op->addr.buswidth == 1) { + val |= op->addr.val << ((3 - op->addr.nbytes) * 8); + len += op->addr.nbytes; + } + + ret = rtl_snand_wait_ready(snand); + if (ret) + return ret; + + ret = regmap_write(snand->regmap, SNAFWCMR, CMR_LEN(len)); + if (ret) + return ret; + + ret = regmap_write(snand->regmap, SNAFWDR, val); + if (ret) + return ret; + + ret = rtl_snand_wait_ready(snand); + if (ret) + return ret; + + if (op->addr.buswidth > 1) { + val = op->addr.val << ((3 - op->addr.nbytes) * 8); + len = op->addr.nbytes; + + ret = regmap_write(snand->regmap, SNAFWCMR, + CMR_WID(op->addr.buswidth) | CMR_LEN(len)); + if (ret) + return ret; + + ret = regmap_write(snand->regmap, SNAFWDR, val); + if (ret) + return ret; + + ret = rtl_snand_wait_ready(snand); + if (ret) + return ret; + } + + if (op->dummy.nbytes) { + val = 0; + + ret = regmap_write(snand->regmap, SNAFWCMR, + CMR_WID(op->dummy.buswidth) | CMR_LEN(op->dummy.nbytes)); + if (ret) + return ret; + + ret = regmap_write(snand->regmap, SNAFWDR, val); + if (ret) + return ret; + + ret = rtl_snand_wait_ready(snand); + if (ret) + return ret; + } + + return 0; +} + +static void rtl_snand_xfer_tail(struct rtl_snand *snand, int cs) +{ + rtl_snand_set_cs(snand, cs, false); +} + +static int rtl_snand_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op) +{ + unsigned int pos, nbytes; + int ret; + u32 val, len = 0; + + ret = rtl_snand_xfer_head(snand, cs, op); + if (ret) + goto out_deselect; + + if (op->data.dir == SPI_MEM_DATA_IN) { + pos = 0; + len = op->data.nbytes; + + while (pos < len) { + nbytes = len - pos; + if (nbytes > 4) + nbytes = 4; + + ret = rtl_snand_wait_ready(snand); + if (ret) + goto out_deselect; + + ret = regmap_write(snand->regmap, SNAFRCMR, + CMR_WID(op->data.buswidth) | CMR_LEN(nbytes)); + if (ret) + goto out_deselect; + + ret = rtl_snand_wait_ready(snand); + if (ret) + goto out_deselect; + + ret = regmap_read(snand->regmap, SNAFRDR, &val); + if (ret) + goto out_deselect; + + memcpy(op->data.buf.in + pos, &val, nbytes); + + pos += nbytes; + } + } else if (op->data.dir == SPI_MEM_DATA_OUT) { + pos = 0; + len = op->data.nbytes; + + while (pos < len) { + nbytes = len - pos; + if (nbytes > 4) + nbytes = 4; + + memcpy(&val, op->data.buf.out + pos, nbytes); + + pos += nbytes; + + ret = regmap_write(snand->regmap, SNAFWCMR, CMR_LEN(nbytes)); + if (ret) + goto out_deselect; + + ret = regmap_write(snand->regmap, SNAFWDR, val); + if (ret) + goto out_deselect; + + ret = rtl_snand_wait_ready(snand); + if (ret) + goto out_deselect; + } + } + +out_deselect: + rtl_snand_xfer_tail(snand, cs); + + if (ret) + dev_err(snand->dev, "transfer failed %d\n", ret); + + return ret; +} + +static int rtl_snand_dma_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op) +{ + int ret; + dma_addr_t buf_dma; + enum dma_data_direction dir; + u32 trig; + + ret = rtl_snand_xfer_head(snand, cs, op); + if (ret) + goto out_deselect; + + if (op->data.dir == SPI_MEM_DATA_IN) { + dir = DMA_FROM_DEVICE; + trig = 0; + } else if (op->data.dir == SPI_MEM_DATA_OUT) { + dir = DMA_TO_DEVICE; + trig = 1; + } else { + ret = -EOPNOTSUPP; + goto out_deselect; + } + + buf_dma = dma_map_single(snand->dev, op->data.buf.in, op->data.nbytes, dir); + ret = dma_mapping_error(snand->dev, buf_dma); + if (ret) + goto out_deselect; + + ret = regmap_write(snand->regmap, SNAFDIR, SNAFDIR_DMA_IP); + if (ret) + goto out_unmap; + + ret = regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, SNAFCFR_DMA_IE); + if (ret) + goto out_unmap; + + reinit_completion(&snand->comp); + + ret = regmap_write(snand->regmap, SNAFDRSAR, buf_dma); + if (ret) + goto out_disable_int; + + ret = regmap_write(snand->regmap, SNAFDLR, + CMR_WID(op->data.buswidth) | (op->data.nbytes & 0xffff)); + if (ret) + goto out_disable_int; + + ret = regmap_write(snand->regmap, SNAFDTR, trig); + if (ret) + goto out_disable_int; + + if (!wait_for_completion_timeout(&snand->comp, usecs_to_jiffies(20000))) + ret = -ETIMEDOUT; + + if (ret) + goto out_disable_int; + +out_disable_int: + regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, 0); +out_unmap: + dma_unmap_single(snand->dev, buf_dma, op->data.nbytes, dir); +out_deselect: + rtl_snand_xfer_tail(snand, cs); + + if (ret) + dev_err(snand->dev, "transfer failed %d\n", ret); + + return ret; +} + +static bool rtl_snand_dma_op(const struct spi_mem_op *op) +{ + switch (op->data.dir) { + case SPI_MEM_DATA_IN: + case SPI_MEM_DATA_OUT: + return op->data.nbytes > 32; + default: + return false; + } +} + +static int rtl_snand_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct rtl_snand *snand = spi_controller_get_devdata(mem->spi->controller); + int cs = spi_get_chipselect(mem->spi, 0); + + dev_dbg(snand->dev, "cs %d op cmd %02x %d:%d, dummy %d:%d, addr %08llx@%d:%d, data %d:%d\n", + cs, op->cmd.opcode, + op->cmd.buswidth, op->cmd.nbytes, op->dummy.buswidth, + op->dummy.nbytes, op->addr.val, op->addr.buswidth, + op->addr.nbytes, op->data.buswidth, op->data.nbytes); + + if (rtl_snand_dma_op(op)) + return rtl_snand_dma_xfer(snand, cs, op); + else + return rtl_snand_xfer(snand, cs, op); +} + +static const struct spi_controller_mem_ops rtl_snand_mem_ops = { + .supports_op = rtl_snand_supports_op, + .exec_op = rtl_snand_exec_op, +}; + +static const struct of_device_id rtl_snand_match[] = { + { .compatible = "realtek,rtl9301-snand" }, + { .compatible = "realtek,rtl9302b-snand" }, + { .compatible = "realtek,rtl9302c-snand" }, + { .compatible = "realtek,rtl9303-snand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rtl_snand_match); + +static int rtl_snand_probe(struct platform_device *pdev) +{ + struct rtl_snand *snand; + struct device *dev = &pdev->dev; + struct spi_controller *ctrl; + void __iomem *base; + const struct regmap_config rc = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .cache_type = REGCACHE_NONE, + }; + int irq, ret; + + ctrl = devm_spi_alloc_host(dev, sizeof(*snand)); + if (!ctrl) + return -ENOMEM; + + snand = spi_controller_get_devdata(ctrl); + snand->dev = dev; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + snand->regmap = devm_regmap_init_mmio(dev, base, &rc); + if (IS_ERR(snand->regmap)) + return PTR_ERR(snand->regmap); + + init_completion(&snand->comp); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = dma_set_mask(snand->dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "failed to set DMA mask\n"); + + ret = devm_request_irq(dev, irq, rtl_snand_irq, 0, "rtl-snand", snand); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq\n"); + + ctrl->num_chipselect = 2; + ctrl->mem_ops = &rtl_snand_mem_ops; + ctrl->bits_per_word_mask = SPI_BPW_MASK(8); + ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; + device_set_node(&ctrl->dev, dev_fwnode(dev)); + + return devm_spi_register_controller(dev, ctrl); +} + +static struct platform_driver rtl_snand_driver = { + .driver = { + .name = "realtek-rtl-snand", + .of_match_table = rtl_snand_match, + }, + .probe = rtl_snand_probe, +}; +module_platform_driver(rtl_snand_driver); + +MODULE_DESCRIPTION("Realtek SPI-NAND Flash Controller Driver"); +MODULE_LICENSE("GPL"); -- 2.51.0 From f45a4399c1b582c6ddc179cc940aed73907b9453 Mon Sep 17 00:00:00 2001 From: Ivaylo Ivanov Date: Sun, 20 Oct 2024 21:21:17 +0300 Subject: [PATCH 14/16] spi: dt-bindings: samsung: Add a compatible for samsung,exynos8895-spi According to the vendor kernel, the Exynos8895 SoC has an SPI configuration that matches with the Exynos850 one. SPI FIFO depth is 64 bytes for all SPI blocks. All blocks have DIV_4 as the default internal clock divider, and an internal loopback mode to run a loopback test. Reuse the samsung,exynos850-spi compatible. Signed-off-by: Ivaylo Ivanov Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20241020182121.377969-3-ivo.ivanov.ivanov1@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/samsung,spi.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/samsung,spi.yaml b/Documentation/devicetree/bindings/spi/samsung,spi.yaml index f681372da81f..3c206a64d60a 100644 --- a/Documentation/devicetree/bindings/spi/samsung,spi.yaml +++ b/Documentation/devicetree/bindings/spi/samsung,spi.yaml @@ -26,6 +26,10 @@ properties: - samsung,exynos850-spi - samsung,exynosautov9-spi - tesla,fsd-spi + - items: + - enum: + - samsung,exynos8895-spi + - const: samsung,exynos850-spi - const: samsung,exynos7-spi deprecated: true -- 2.51.0 From a992197bfcbb14e5a027796e06f86226c4444955 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 24 Oct 2024 16:04:27 +0200 Subject: [PATCH 15/16] spi: Replace deprecated PCI functions pcim_iomap_table() and pcim_request_regions() have been deprecated in commit e354bb84a4c1 ("PCI: Deprecate pcim_iomap_table(), pcim_iomap_regions_request_all()"). Replace these functions with pcim_iomap_region(). Signed-off-by: Philipp Stanner Link: https://patch.msgid.link/20241024140426.157444-2-pstanner@redhat.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-pci.c | 9 ++++----- drivers/spi/spi-loongson-pci.c | 5 ++--- drivers/spi/spi-pxa2xx-pci.c | 8 +++----- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 7c8279d13f31..6b8cc26e06f8 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -98,15 +98,14 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en dws->paddr = pci_resource_start(pdev, pci_bar); pci_set_master(pdev); - ret = pcim_iomap_regions(pdev, 1 << pci_bar, pci_name(pdev)); - if (ret) - return ret; - ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) return ret; - dws->regs = pcim_iomap_table(pdev)[pci_bar]; + dws->regs = pcim_iomap_region(pdev, pci_bar, pci_name(pdev)); + if (IS_ERR(dws->regs)) + return PTR_ERR(dws->regs); + dws->irq = pci_irq_vector(pdev, 0); /* diff --git a/drivers/spi/spi-loongson-pci.c b/drivers/spi/spi-loongson-pci.c index 134cda0c13a5..892cf1eba1cf 100644 --- a/drivers/spi/spi-loongson-pci.c +++ b/drivers/spi/spi-loongson-pci.c @@ -19,12 +19,11 @@ static int loongson_spi_pci_register(struct pci_dev *pdev, if (ret < 0) return dev_err_probe(dev, ret, "cannot enable pci device\n"); - ret = pcim_iomap_regions(pdev, BIT(pci_bar), pci_name(pdev)); + reg_base = pcim_iomap_region(pdev, pci_bar, pci_name(pdev)); + ret = PTR_ERR_OR_ZERO(reg_base); if (ret) return dev_err_probe(dev, ret, "failed to request and remap memory\n"); - reg_base = pcim_iomap_table(pdev)[pci_bar]; - ret = loongson_spi_init_controller(dev, reg_base); if (ret) return dev_err_probe(dev, ret, "failed to initialize controller\n"); diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index cc8dcf782399..e51c1b492283 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -273,10 +273,6 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, if (ret) return ret; - ret = pcim_iomap_regions(dev, 1 << 0, "PXA2xx SPI"); - if (ret) - return ret; - pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -284,7 +280,9 @@ static int pxa2xx_spi_pci_probe(struct pci_dev *dev, ssp = &pdata->ssp; ssp->dev = &dev->dev; ssp->phys_base = pci_resource_start(dev, 0); - ssp->mmio_base = pcim_iomap_table(dev)[0]; + ssp->mmio_base = pcim_iomap_region(dev, 0, "PXA2xx SPI"); + if (IS_ERR(ssp->mmio_base)) + return PTR_ERR(ssp->mmio_base); info = (struct pxa_spi_info *)ent->driver_data; ret = info->setup(dev, pdata); -- 2.51.0 From 8a9c132389bbd162336fcbe6725692455151bd7e Mon Sep 17 00:00:00 2001 From: Stanislav Jakubek Date: Wed, 30 Oct 2024 10:02:55 +0100 Subject: [PATCH 16/16] dt-bindings: spi: sprd,sc9860-spi: convert to YAML Convert the Spreadtrum SC9860 SPI controller bindings to DT schema. Adjust filename to match compatible. Signed-off-by: Stanislav Jakubek Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/ZyH2P3FlneLtGxXo@standask-GA-A55M-S2HP Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sprd.txt | 33 --------- .../bindings/spi/sprd,sc9860-spi.yaml | 72 +++++++++++++++++++ 2 files changed, 72 insertions(+), 33 deletions(-) delete mode 100644 Documentation/devicetree/bindings/spi/spi-sprd.txt create mode 100644 Documentation/devicetree/bindings/spi/sprd,sc9860-spi.yaml diff --git a/Documentation/devicetree/bindings/spi/spi-sprd.txt b/Documentation/devicetree/bindings/spi/spi-sprd.txt deleted file mode 100644 index 3c7eacce0ee3..000000000000 --- a/Documentation/devicetree/bindings/spi/spi-sprd.txt +++ /dev/null @@ -1,33 +0,0 @@ -Spreadtrum SPI Controller - -Required properties: -- compatible: Should be "sprd,sc9860-spi". -- reg: Offset and length of SPI controller register space. -- interrupts: Should contain SPI interrupt. -- clock-names: Should contain following entries: - "spi" for SPI clock, - "source" for SPI source (parent) clock, - "enable" for SPI module enable clock. -- clocks: List of clock input name strings sorted in the same order - as the clock-names property. -- #address-cells: The number of cells required to define a chip select - address on the SPI bus. Should be set to 1. -- #size-cells: Should be set to 0. - -Optional properties: -dma-names: Should contain names of the SPI used DMA channel. -dmas: Should contain DMA channels and DMA slave ids which the SPI used - sorted in the same order as the dma-names property. - -Example: -spi0: spi@70a00000{ - compatible = "sprd,sc9860-spi"; - reg = <0 0x70a00000 0 0x1000>; - interrupts = ; - clock-names = "spi", "source","enable"; - clocks = <&clk_spi0>, <&ext_26m>, <&clk_ap_apb_gates 5>; - dma-names = "rx_chn", "tx_chn"; - dmas = <&apdma 11 11>, <&apdma 12 12>; - #address-cells = <1>; - #size-cells = <0>; -}; diff --git a/Documentation/devicetree/bindings/spi/sprd,sc9860-spi.yaml b/Documentation/devicetree/bindings/spi/sprd,sc9860-spi.yaml new file mode 100644 index 000000000000..d55c01e9a038 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/sprd,sc9860-spi.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/sprd,sc9860-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spreadtrum SC9860 SPI Controller + +maintainers: + - Orson Zhai + - Baolin Wang + - Chunyan Zhang + +properties: + compatible: + const: sprd,sc9860-spi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: SPI clock + - description: SPI source (parent) clock + - description: SPI module enable clock + + clock-names: + items: + - const: spi + - const: source + - const: enable + + dmas: + maxItems: 2 + + dma-names: + items: + - const: rx_chn + - const: tx_chn + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +allOf: + - $ref: spi-controller.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + + spi@70a00000 { + compatible = "sprd,sc9860-spi"; + reg = <0x70a00000 0x1000>; + interrupts = ; + clocks = <&clk_spi0>, <&ext_26m>, <&clk_ap_apb_gates 5>; + clock-names = "spi", "source", "enable"; + dmas = <&apdma 11 11>, <&apdma 12 12>; + dma-names = "rx_chn", "tx_chn"; + #address-cells = <1>; + #size-cells = <0>; + }; +... -- 2.51.0