From d58b4eb7c03cabb10d4eebc89d7596e06376e54d Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Mon, 24 Mar 2025 10:40:13 +0100 Subject: [PATCH 01/90] spi: dt-bindings: st,stm32mp25-ospi: Make "resets" a required property On STM32MP2x SoC's family, OSPI is child of Octo Memory Manager which must have asccess to OSPI's reset to ensure its initialization. Make "resets" a required property. Fixes: bed97e35786a ("dt-bindings: spi: Add STM32 OSPI controller") Signed-off-by: Patrice Chotard Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250324-upstream_ospi_required_resets-v2-1-85a48afcedec@foss.st.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml index 5f276f27dc4c..272bc308726b 100644 --- a/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml +++ b/Documentation/devicetree/bindings/spi/st,stm32mp25-ospi.yaml @@ -68,6 +68,7 @@ required: - compatible - reg - clocks + - resets - interrupts - st,syscfg-dlyb From 69e3433fa5e24edc94e94b4f34e3dbb754bdedbf Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Mon, 24 Mar 2025 10:40:14 +0100 Subject: [PATCH 02/90] spi: spi-stm32-ospi: Make "resets" a required property On some STM32MP2 SoCs, an Octo Memory Manager is embedded and need to retrieve OSPI's reset to perform its own initialization. Make "resets" property mandatory. Fixes: 79b8a705e26c ("spi: stm32: Add OSPI driver") Signed-off-by: Patrice Chotard Link: https://patch.msgid.link/20250324-upstream_ospi_required_resets-v2-2-85a48afcedec@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-ospi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32-ospi.c b/drivers/spi/spi-stm32-ospi.c index 668022098b1e..d002b9c16714 100644 --- a/drivers/spi/spi-stm32-ospi.c +++ b/drivers/spi/spi-stm32-ospi.c @@ -804,7 +804,7 @@ static int stm32_ospi_get_resources(struct platform_device *pdev) return ret; } - ospi->rstc = devm_reset_control_array_get_optional_exclusive(dev); + ospi->rstc = devm_reset_control_array_get_exclusive(dev); if (IS_ERR(ospi->rstc)) return dev_err_probe(dev, PTR_ERR(ospi->rstc), "Can't get reset\n"); From dd8a9807fa03666bff52cb28472fb227eaac36c9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 31 Mar 2025 13:35:59 +0300 Subject: [PATCH 03/90] spi: Group CS related fields in struct spi_device The CS related fields are sparse in the struct spi_device. Group them. While at it, fix the comment style of cs_index_mask. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250331103609.4160281-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 0ba5e49bace4..e10f0ebb8250 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -136,13 +136,6 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * @max_speed_hz: Maximum clock rate to be used with this chip * (on this board); may be changed by the device's driver. * The spi_transfer.speed_hz can override this for each transfer. - * @chip_select: Array of physical chipselect, spi->chipselect[i] gives - * the corresponding physical CS for logical CS i. - * @mode: The spi mode defines how data is clocked out and in. - * This may be changed by the device's driver. - * The "active low" default for chipselect mode can be overridden - * (by specifying SPI_CS_HIGH) as can the "MSB first" default for - * each word in a transfer (by specifying SPI_LSB_FIRST). * @bits_per_word: Data transfers involve one or more words; word sizes * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). @@ -150,6 +143,11 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * default (0) indicating protocol words are eight bit bytes. * The spi_transfer.bits_per_word can override this for each transfer. * @rt: Make the pump thread real time priority. + * @mode: The spi mode defines how data is clocked out and in. + * This may be changed by the device's driver. + * The "active low" default for chipselect mode can be overridden + * (by specifying SPI_CS_HIGH) as can the "MSB first" default for + * each word in a transfer (by specifying SPI_LSB_FIRST). * @irq: Negative, or the number passed to request_irq() to receive * interrupts from this device. * @controller_state: Controller's runtime state @@ -162,8 +160,7 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * the device will bind to the named driver and only the named driver. * Do not set directly, because core frees it; use driver_set_override() to * set or clear it. - * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines - * (optional, NULL when not using a GPIO line) + * @pcpu_statistics: statistics for the spi_device * @word_delay: delay to be inserted between consecutive * words of a transfer * @cs_setup: delay to be introduced by the controller after CS is asserted @@ -171,8 +168,11 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * @cs_inactive: delay to be introduced by the controller after CS is * deasserted. If @cs_change_delay is used from @spi_transfer, then the * two delays will be added up. - * @pcpu_statistics: statistics for the spi_device + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives + * the corresponding physical CS for logical CS i. * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array + * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines + * (optional, NULL when not using a GPIO line) * * A @spi_device is used to interchange data between an SPI target device * (usually a discrete chip) and CPU memory. @@ -187,7 +187,6 @@ struct spi_device { struct device dev; struct spi_controller *controller; u32 max_speed_hz; - u8 chip_select[SPI_CS_CNT_MAX]; u8 bits_per_word; bool rt; #define SPI_NO_TX BIT(31) /* No transmit wire */ @@ -218,23 +217,29 @@ struct spi_device { void *controller_data; char modalias[SPI_NAME_SIZE]; const char *driver_override; - struct gpio_desc *cs_gpiod[SPI_CS_CNT_MAX]; /* Chip select gpio desc */ + + /* The statistics */ + struct spi_statistics __percpu *pcpu_statistics; + struct spi_delay word_delay; /* Inter-word delay */ + /* CS delays */ struct spi_delay cs_setup; struct spi_delay cs_hold; struct spi_delay cs_inactive; - /* The statistics */ - struct spi_statistics __percpu *pcpu_statistics; + u8 chip_select[SPI_CS_CNT_MAX]; - /* Bit mask of the chipselect(s) that the driver need to use from - * the chipselect array.When the controller is capable to handle + /* + * Bit mask of the chipselect(s) that the driver need to use from + * the chipselect array. When the controller is capable to handle * multiple chip selects & memories are connected in parallel * then more than one bit need to be set in cs_index_mask. */ u32 cs_index_mask : SPI_CS_CNT_MAX; + struct gpio_desc *cs_gpiod[SPI_CS_CNT_MAX]; /* Chip select gpio desc */ + /* * Likely need more hooks for more protocol options affecting how * the controller talks to each chip, like: From 1f1d979fbf741c3608a344373f88444dc8749967 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 31 Mar 2025 12:39:15 +0300 Subject: [PATCH 04/90] spi: Simplify conditionals in spi_set_cs() First of all, the (foo && bar) || (!foo && !bar) when foo and bar are booleans is equivalent to (foo == bar). Second, reuse variable that holds already the calculation of the SPI CS mode to be active-high. No functional changes intended. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250331093915.4041600-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 90e27729ef6b..00b81d81c09a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1076,10 +1076,8 @@ 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 && ((enable && spi->controller->last_cs_index_mask == spi->cs_index_mask && - spi_is_last_cs(spi)) || - (!enable && spi->controller->last_cs_index_mask == spi->cs_index_mask && - !spi_is_last_cs(spi))) && + if (!force && (enable == spi_is_last_cs(spi)) && + (spi->controller->last_cs_index_mask == spi->cs_index_mask) && (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH))) return; @@ -1088,9 +1086,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) spi->controller->last_cs_index_mask = spi->cs_index_mask; for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi, 0) : SPI_INVALID_CS; - spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; - if (spi->mode & SPI_CS_HIGH) + spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; + if (spi->controller->last_cs_mode_high) enable = !enable; /* From f48d80503504257682e493dc17408f2f0b47bcfa Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 20 Mar 2025 19:11:59 +0100 Subject: [PATCH 05/90] spi: spi-qpic-snand: use kmalloc() for OOB buffer allocation The qcom_spi_ecc_init_ctx_pipelined() function allocates zeroed memory for the OOB buffer, then it fills the buffer with '0xff' bytes right after the allocation. In this case zeroing the memory during allocation is superfluous, so use kmalloc() instead of kzalloc() to avoid that. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250320-qpic-snand-kmalloc-v1-1-94e267550675@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 17eb67e19132..a2af731049d4 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -261,7 +261,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); if (!ecc_cfg) return -ENOMEM; - snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize, + snandc->qspi->oob_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!snandc->qspi->oob_buf) { kfree(ecc_cfg); From b644c2776652671256edcd7a8e71161e212b59ac Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Wed, 2 Apr 2025 17:45:14 +0530 Subject: [PATCH 06/90] spi: spi_amd: Add PCI-based driver for AMD HID2 SPI controller Register a new driver(spi_amd_pci) for the HID2 SPI controller using the PCI ID of the LPC bridge device. Add a new common probe function in spi_amd driver to encapsulate the code required for registering the controller driver. This function will be utilized by both the existing ACPI driver and the newly introduced PCI-based driver for the HID2 SPI controller. The MMIO register base address of the HID2 SPI controller can be obtained from the PCI LPC bridge registers. By implementing these changes, the DMA buffer will be correctly associated with the LPC bridge device, preventing IO_PAGE_FAULT caused by IOMMU when the LPC bridge attempts DMA operations on addresses owned by the HID2 SPI controller. Co-developed-by: Krishnamoorthi M Signed-off-by: Krishnamoorthi M Co-developed-by: Akshata MukundShetty Signed-off-by: Akshata MukundShetty Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20250402121514.2941334-1-Raju.Rangoju@amd.com Signed-off-by: Mark Brown --- MAINTAINERS | 2 + drivers/spi/Makefile | 2 +- drivers/spi/spi-amd-pci.c | 70 +++++++++++++++++++++++++++++++++++ drivers/spi/spi-amd.c | 78 +++++++++++++++------------------------ drivers/spi/spi-amd.h | 44 ++++++++++++++++++++++ 5 files changed, 147 insertions(+), 49 deletions(-) create mode 100644 drivers/spi/spi-amd-pci.c create mode 100644 drivers/spi/spi-amd.h diff --git a/MAINTAINERS b/MAINTAINERS index 96b827049501..cda27131bb1b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1216,7 +1216,9 @@ AMD SPI DRIVER M: Raju Rangoju L: linux-spi@vger.kernel.org S: Supported +F: drivers/spi/spi-amd-pci.c F: drivers/spi/spi-amd.c +F: drivers/spi/spi-amd.h AMD XDNA DRIVER M: Min Ma diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c3a1a47b3bf4..4ea89f6fc531 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -162,7 +162,7 @@ obj-$(CONFIG_SPI_XLP) += spi-xlp.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o -obj-$(CONFIG_SPI_AMD) += spi-amd.o +obj-$(CONFIG_SPI_AMD) += spi-amd.o spi-amd-pci.o # SPI slave protocol handlers obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o diff --git a/drivers/spi/spi-amd-pci.c b/drivers/spi/spi-amd-pci.c new file mode 100644 index 000000000000..e1ecab755fe9 --- /dev/null +++ b/drivers/spi/spi-amd-pci.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD SPI controller driver + * + * Copyright (c) 2025, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Authors: Krishnamoorthi M + * Akshata MukundShetty + */ + +#include +#include +#include + +#include "spi-amd.h" + +#define AMD_PCI_DEVICE_ID_LPC_BRIDGE 0x1682 +#define AMD_PCI_LPC_SPI_BASE_ADDR_REG 0xA0 +#define AMD_SPI_BASE_ADDR_MASK ~0xFF +#define AMD_HID2_PCI_BAR_OFFSET 0x00002000 +#define AMD_HID2_MEM_SIZE 0x200 + +static struct pci_device_id pci_spi_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_PCI_DEVICE_ID_LPC_BRIDGE) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, pci_spi_ids); + +static int amd_spi_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct spi_controller *host; + struct amd_spi *amd_spi; + u32 io_base_addr; + + /* Allocate storage for host and driver private data */ + host = devm_spi_alloc_host(dev, sizeof(struct amd_spi)); + if (!host) + return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n"); + + amd_spi = spi_controller_get_devdata(host); + + pci_read_config_dword(pdev, AMD_PCI_LPC_SPI_BASE_ADDR_REG, &io_base_addr); + io_base_addr = (io_base_addr & AMD_SPI_BASE_ADDR_MASK) + AMD_HID2_PCI_BAR_OFFSET; + amd_spi->io_remap_addr = devm_ioremap(dev, io_base_addr, AMD_HID2_MEM_SIZE); + + if (IS_ERR(amd_spi->io_remap_addr)) + return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr), + "ioremap of SPI registers failed\n"); + + dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); + + amd_spi->version = AMD_HID2_SPI; + host->bus_num = 2; + + return amd_spi_probe_common(dev, host); +} + +static struct pci_driver amd_spi_pci_driver = { + .name = "amd_spi_pci", + .id_table = pci_spi_ids, + .probe = amd_spi_pci_probe, +}; + +module_pci_driver(amd_spi_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD HID2 SPI Controller Driver"); diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 17fc0b17e756..90b1f7daa072 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -17,6 +17,8 @@ #include #include +#include "spi-amd.h" + #define AMD_SPI_CTRL0_REG 0x00 #define AMD_SPI_EXEC_CMD BIT(16) #define AMD_SPI_FIFO_CLEAR BIT(20) @@ -81,18 +83,6 @@ #define AMD_SPI_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */ #define AMD_SPI_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */ -/** - * enum amd_spi_versions - SPI controller versions - * @AMD_SPI_V1: AMDI0061 hardware version - * @AMD_SPI_V2: AMDI0062 hardware version - * @AMD_HID2_SPI: AMDI0063 hardware version - */ -enum amd_spi_versions { - AMD_SPI_V1 = 1, - AMD_SPI_V2, - AMD_HID2_SPI, -}; - enum amd_spi_speed { F_66_66MHz, F_33_33MHz, @@ -118,22 +108,6 @@ struct amd_spi_freq { u32 spd7_val; }; -/** - * struct amd_spi - SPI driver instance - * @io_remap_addr: Start address of the SPI controller registers - * @phy_dma_buf: Physical address of DMA buffer - * @dma_virt_addr: Virtual address of DMA buffer - * @version: SPI controller hardware version - * @speed_hz: Device frequency - */ -struct amd_spi { - void __iomem *io_remap_addr; - dma_addr_t phy_dma_buf; - void *dma_virt_addr; - enum amd_spi_versions version; - unsigned int speed_hz; -}; - static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx) { return readb((u8 __iomem *)amd_spi->io_remap_addr + idx); @@ -749,30 +723,12 @@ static int amd_spi_setup_hiddma(struct amd_spi *amd_spi, struct device *dev) return 0; } -static int amd_spi_probe(struct platform_device *pdev) +int amd_spi_probe_common(struct device *dev, struct spi_controller *host) { - struct device *dev = &pdev->dev; - struct spi_controller *host; - struct amd_spi *amd_spi; + struct amd_spi *amd_spi = spi_controller_get_devdata(host); int err; - /* Allocate storage for host and driver private data */ - host = devm_spi_alloc_host(dev, sizeof(struct amd_spi)); - if (!host) - return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n"); - - amd_spi = spi_controller_get_devdata(host); - amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(amd_spi->io_remap_addr)) - return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr), - "ioremap of SPI registers failed\n"); - - dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); - - amd_spi->version = (uintptr_t) device_get_match_data(dev); - /* Initialize the spi_controller fields */ - host->bus_num = (amd_spi->version == AMD_HID2_SPI) ? 2 : 0; host->num_chipselect = 4; host->mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD; host->flags = SPI_CONTROLLER_HALF_DUPLEX; @@ -795,6 +751,32 @@ static int amd_spi_probe(struct platform_device *pdev) return err; } +EXPORT_SYMBOL_GPL(amd_spi_probe_common); + +static int amd_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_controller *host; + struct amd_spi *amd_spi; + + /* Allocate storage for host and driver private data */ + host = devm_spi_alloc_host(dev, sizeof(struct amd_spi)); + if (!host) + return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n"); + + amd_spi = spi_controller_get_devdata(host); + amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(amd_spi->io_remap_addr)) + return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr), + "ioremap of SPI registers failed\n"); + + dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); + + amd_spi->version = (uintptr_t)device_get_match_data(dev); + host->bus_num = 0; + + return amd_spi_probe_common(dev, host); +} #ifdef CONFIG_ACPI static const struct acpi_device_id spi_acpi_match[] = { diff --git a/drivers/spi/spi-amd.h b/drivers/spi/spi-amd.h new file mode 100644 index 000000000000..5f39ce7b5587 --- /dev/null +++ b/drivers/spi/spi-amd.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD SPI controller driver common stuff + * + * Copyright (c) 2025, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Krishnamoorthi M + */ + +#ifndef SPI_AMD_H +#define SPI_AMD_H + +/** + * enum amd_spi_versions - SPI controller versions + * @AMD_SPI_V1: AMDI0061 hardware version + * @AMD_SPI_V2: AMDI0062 hardware version + * @AMD_HID2_SPI: AMDI0063 hardware version + */ +enum amd_spi_versions { + AMD_SPI_V1 = 1, + AMD_SPI_V2, + AMD_HID2_SPI, +}; + +/** + * struct amd_spi - SPI driver instance + * @io_remap_addr: Start address of the SPI controller registers + * @phy_dma_buf: Physical address of DMA buffer + * @dma_virt_addr: Virtual address of DMA buffer + * @version: SPI controller hardware version + * @speed_hz: Device frequency + */ +struct amd_spi { + void __iomem *io_remap_addr; + dma_addr_t phy_dma_buf; + void *dma_virt_addr; + enum amd_spi_versions version; + unsigned int speed_hz; +}; + +int amd_spi_probe_common(struct device *dev, struct spi_controller *host); + +#endif /* SPI_AMD_H */ From 5e21900ef64244fadeddc9015e8b8307d116764a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 8 Apr 2025 09:39:56 +0200 Subject: [PATCH 07/90] spi: xcomm: use new GPIO line value setter callbacks struct gpio_chip now has callbacks for setting line values that return an integer, allowing to indicate failures. Convert the driver to using them. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20250408-gpiochip-set-rv-spi-v1-1-597660766654@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-xcomm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 3bd0149d8f4e..1a40c4866ce1 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -44,8 +44,8 @@ struct spi_xcomm { u8 buf[63]; }; -static void spi_xcomm_gpio_set_value(struct gpio_chip *chip, - unsigned int offset, int val) +static int spi_xcomm_gpio_set_value(struct gpio_chip *chip, + unsigned int offset, int val) { struct spi_xcomm *spi_xcomm = gpiochip_get_data(chip); unsigned char buf[2]; @@ -53,7 +53,7 @@ static void spi_xcomm_gpio_set_value(struct gpio_chip *chip, buf[0] = SPI_XCOMM_CMD_GPIO_SET; buf[1] = !!val; - i2c_master_send(spi_xcomm->i2c, buf, 2); + return i2c_master_send(spi_xcomm->i2c, buf, 2); } static int spi_xcomm_gpio_get_direction(struct gpio_chip *chip, @@ -70,7 +70,7 @@ static int spi_xcomm_gpio_add(struct spi_xcomm *spi_xcomm) return 0; spi_xcomm->gc.get_direction = spi_xcomm_gpio_get_direction; - spi_xcomm->gc.set = spi_xcomm_gpio_set_value; + spi_xcomm->gc.set_rv = spi_xcomm_gpio_set_value; spi_xcomm->gc.can_sleep = 1; spi_xcomm->gc.base = -1; spi_xcomm->gc.ngpio = 1; From e686365c0411275474527c2055ac133f2eb47526 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Apr 2025 14:00:35 +0300 Subject: [PATCH 08/90] spi: spi_amd: Fix an IS_ERR() vs NULL check in probe The devm_ioremap() function returns NULL on error, it doesn't return error pointers. Update the error checking to match. Fixes: b644c2776652 ("spi: spi_amd: Add PCI-based driver for AMD HID2 SPI controller") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/0e0f5ede-8993-4fac-967c-51e46b095416@stanley.mountain Signed-off-by: Mark Brown --- drivers/spi/spi-amd-pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-amd-pci.c b/drivers/spi/spi-amd-pci.c index e1ecab755fe9..e5faab414c17 100644 --- a/drivers/spi/spi-amd-pci.c +++ b/drivers/spi/spi-amd-pci.c @@ -46,8 +46,8 @@ static int amd_spi_pci_probe(struct pci_dev *pdev, io_base_addr = (io_base_addr & AMD_SPI_BASE_ADDR_MASK) + AMD_HID2_PCI_BAR_OFFSET; amd_spi->io_remap_addr = devm_ioremap(dev, io_base_addr, AMD_HID2_MEM_SIZE); - if (IS_ERR(amd_spi->io_remap_addr)) - return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr), + if (!amd_spi->io_remap_addr) + return dev_err_probe(dev, -ENOMEM, "ioremap of SPI registers failed\n"); dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); From 3f7b48efb79d91883d98dd7e33dc2a0abfa9f923 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 10 Apr 2025 14:56:11 +0800 Subject: [PATCH 09/90] spi: fsl-qspi: Simplify probe error handling using managed API - Switch to devm_spi_alloc_host() to avoid manual spi_controller_put() calls in error paths. - Factor out the hardware disable logic into a dedicated fsl_qspi_disable() helper and register it as a managed cleanup action, removing the need to explicitly disable hardware after fsl_qspi_default_setup() failures. - Move fsl_qspi_cleanup() earlier in the probe sequence to eliminate the need for manual cleanup in the irq failure path. With these changes we can completely eliminate the messy goto labels in probe function. Signed-off-by: Kevin Hao Link: https://patch.msgid.link/20250410-spi-v1-3-56e867cc19cf@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-qspi.c | 73 ++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index b5ecffcaf795..f879533535e8 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -844,13 +844,18 @@ static const struct spi_controller_mem_caps fsl_qspi_mem_caps = { .per_op_freq = true, }; -static void fsl_qspi_cleanup(void *data) +static void fsl_qspi_disable(void *data) { struct fsl_qspi *q = data; /* disable the hardware */ qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER); +} + +static void fsl_qspi_cleanup(void *data) +{ + struct fsl_qspi *q = data; fsl_qspi_clk_disable_unprep(q); @@ -866,7 +871,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) struct fsl_qspi *q; int ret; - ctlr = spi_alloc_host(&pdev->dev, sizeof(*q)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*q)); if (!ctlr) return -ENOMEM; @@ -876,68 +881,60 @@ static int fsl_qspi_probe(struct platform_device *pdev) q = spi_controller_get_devdata(ctlr); q->dev = dev; q->devtype_data = of_device_get_match_data(dev); - if (!q->devtype_data) { - ret = -ENODEV; - goto err_put_ctrl; - } + if (!q->devtype_data) + return -ENODEV; platform_set_drvdata(pdev, q); /* find the resources */ q->iobase = devm_platform_ioremap_resource_byname(pdev, "QuadSPI"); - if (IS_ERR(q->iobase)) { - ret = PTR_ERR(q->iobase); - goto err_put_ctrl; - } + if (IS_ERR(q->iobase)) + return PTR_ERR(q->iobase); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI-memory"); - if (!res) { - ret = -EINVAL; - goto err_put_ctrl; - } + if (!res) + return -EINVAL; q->memmap_phy = res->start; /* Since there are 4 cs, map size required is 4 times ahb_buf_size */ q->ahb_addr = devm_ioremap(dev, q->memmap_phy, (q->devtype_data->ahb_buf_size * 4)); - if (!q->ahb_addr) { - ret = -ENOMEM; - goto err_put_ctrl; - } + if (!q->ahb_addr) + return -ENOMEM; /* find the clocks */ q->clk_en = devm_clk_get(dev, "qspi_en"); - if (IS_ERR(q->clk_en)) { - ret = PTR_ERR(q->clk_en); - goto err_put_ctrl; - } + if (IS_ERR(q->clk_en)) + return PTR_ERR(q->clk_en); q->clk = devm_clk_get(dev, "qspi"); - if (IS_ERR(q->clk)) { - ret = PTR_ERR(q->clk); - goto err_put_ctrl; - } + if (IS_ERR(q->clk)) + return PTR_ERR(q->clk); + + mutex_init(&q->lock); ret = fsl_qspi_clk_prep_enable(q); if (ret) { dev_err(dev, "can not enable the clock\n"); - goto err_put_ctrl; + return ret; } + ret = devm_add_action_or_reset(dev, fsl_qspi_cleanup, q); + if (ret) + return ret; + /* find the irq */ ret = platform_get_irq(pdev, 0); if (ret < 0) - goto err_disable_clk; + return ret; ret = devm_request_irq(dev, ret, fsl_qspi_irq_handler, 0, pdev->name, q); if (ret) { dev_err(dev, "failed to request irq: %d\n", ret); - goto err_disable_clk; + return ret; } - mutex_init(&q->lock); - ctlr->bus_num = -1; ctlr->num_chipselect = 4; ctlr->mem_ops = &fsl_qspi_mem_ops; @@ -947,23 +944,15 @@ static int fsl_qspi_probe(struct platform_device *pdev) ctlr->dev.of_node = np; - ret = devm_add_action_or_reset(dev, fsl_qspi_cleanup, q); + ret = devm_add_action_or_reset(dev, fsl_qspi_disable, q); if (ret) - goto err_put_ctrl; + return ret; ret = devm_spi_register_controller(dev, ctlr); if (ret) - goto err_put_ctrl; + return ret; return 0; - -err_disable_clk: - fsl_qspi_clk_disable_unprep(q); - -err_put_ctrl: - spi_controller_put(ctlr); - - return ret; } static int fsl_qspi_suspend(struct device *dev) From 7a978d8fcf57b283cb8c88dd4c9431502bd36ea8 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 10 Apr 2025 16:07:49 +0200 Subject: [PATCH 10/90] spi: amd: add CONFIG_PCI dependency Without CONFIG_PCI, the module_pci_driver() macro is not defined: drivers/spi/spi-amd-pci.c:67:1: error: type specifier missing, defaults to 'int'; ISO C99 and later do not support implicit int [-Wimplicit-int] 67 | module_pci_driver(amd_spi_pci_driver); Fixes: b644c2776652 ("spi: spi_amd: Add PCI-based driver for AMD HID2 SPI controller") Signed-off-by: Arnd Bergmann Link: https://patch.msgid.link/20250410140809.812863-1-arnd@kernel.org Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ed38f6d41f47..b03a7005f1bd 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1266,6 +1266,7 @@ config SPI_ZYNQMP_GQSPI config SPI_AMD tristate "AMD SPI controller" + depends on PCI depends on SPI_MASTER || COMPILE_TEST help Enables SPI controller driver for AMD SoC. From 88113e09ada52be28968aacf4af7b3d667832f00 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Savaliya Date: Fri, 4 Apr 2025 19:24:27 +0530 Subject: [PATCH 11/90] spi: Add support for Double Transfer Rate (DTR) mode Introduce support for protocol drivers to specify whether a transfer should use single or dual transfer mode. Currently, the SPI controller cannot determine this information from the user, leading to potential limitations in transfer capabilities. Add a new field `dtr_mode` in the `spi_transfer` structure. The `dtr_mode` field allows protocol drivers to indicate if Double Transfer Rate (DTR) mode is supported for a given transfer. When `dtr_mode` is set to true, the SPI controller will use DTR mode; otherwise, it will default to single transfer mode. Introduce another field `dtr_caps` to indicate if the QSPI controller is capable of supporting DTR mode (SDR and DDR). By default, both `dtr_caps` and `dtr_mode` will be false. These flags manage the QSPI controller's DTR mode capabilities within the SPI framework. The QSPI controller driver uses these flags to configure single or double transfer rates using the controller register. The existing spi-mem driver helps configure the DTR mode but is limited to memory devices. There is no support available to set DTR mode for non-memory devices, e.g., touch or any generic SPI sensor. This change is backward compatible and doesn't break existing SPI or QSPI drivers. Changes include: - Addition of `dtr_mode` and `dtr_caps` fields in the `spi_transfer` structure. - Documentation updates to reflect the new `dtr_mode` and `dtr_caps` fields. Signed-off-by: Mukesh Kumar Savaliya Link: https://patch.msgid.link/20250404135427.313825-1-quic_msavaliy@quicinc.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 7 +++++++ include/linux/spi/spi.h | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 00b81d81c09a..b0e7702951fe 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -4094,6 +4094,13 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) if (__spi_validate_bits_per_word(ctlr, xfer->bits_per_word)) return -EINVAL; + /* DDR mode is supported only if controller has dtr_caps=true. + * default considered as SDR mode for SPI and QSPI controller. + * Note: This is applicable only to QSPI controller. + */ + if (xfer->dtr_mode && !ctlr->dtr_caps) + return -EINVAL; + /* * SPI transfer length should be multiple of SPI word size * where SPI word size should be power-of-two multiple. diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e10f0ebb8250..834a09bd8ccc 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -508,6 +508,8 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * found. * @put_offload: release the offload instance acquired by @get_offload. * @mem_caps: controller capabilities for the handling of memory operations. + * @dtr_caps: true if controller has dtr(single/dual transfer rate) capability. + * QSPI based controller should fill this based on controller's capability. * @unprepare_message: undo any work done by prepare_message(). * @target_abort: abort the ongoing transfer request on an SPI target controller * @cs_gpiods: Array of GPIO descriptors to use as chip select lines; one per CS @@ -751,6 +753,9 @@ struct spi_controller { const struct spi_controller_mem_ops *mem_ops; const struct spi_controller_mem_caps *mem_caps; + /* SPI or QSPI controller can set to true if supports SDR/DDR transfer rate */ + bool dtr_caps; + struct spi_offload *(*get_offload)(struct spi_device *spi, const struct spi_offload_config *config); void (*put_offload)(struct spi_offload *offload); @@ -1003,6 +1008,7 @@ struct spi_res { * processed the word, i.e. the "pre" timestamp should be taken before * transmitting the "pre" word, and the "post" timestamp after receiving * transmit confirmation from the controller for the "post" word. + * @dtr_mode: true if supports double transfer rate. * @timestamped: true if the transfer has been timestamped * @error: Error status logged by SPI controller driver. * @@ -1054,6 +1060,9 @@ struct spi_res { * two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x) * SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer. * + * User may also set dtr_mode to true to use dual transfer mode if desired. if + * not, default considered as single transfer mode. + * * The code that submits an spi_message (and its spi_transfers) * to the lower layers is responsible for managing its memory. * Zero-initialize every field you don't set up explicitly, to @@ -1088,6 +1097,7 @@ struct spi_transfer { unsigned tx_nbits:4; unsigned rx_nbits:4; unsigned timestamped:1; + bool dtr_mode; #define SPI_NBITS_SINGLE 0x01 /* 1-bit transfer */ #define SPI_NBITS_DUAL 0x02 /* 2-bit transfer */ #define SPI_NBITS_QUAD 0x04 /* 4-bit transfer */ From 279b418f477fd6c1c21b1cf212837622c774f15f Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Fri, 11 Apr 2025 15:06:41 +0800 Subject: [PATCH 12/90] spi: fsl-qspi: Optimize fsl_qspi struct Reorgize the members of the fsl_qspi struct to: - Reduce a hole in the struct. - Group members required by each op (e.g., iobase, ahb_addr, devtype_data and lock) into the same cacheline. Before: struct fsl_qspi { [...] /* size: 176, cachelines: 3, members: 11 */ /* sum members: 168, holes: 1, sum holes: 4 */ /* padding: 4 */ /* member types with holes: 1, total: 1 */ /* last cacheline: 48 bytes */ }; after: struct fsl_qspi { void * iobase; /* 0 8 */ void * ahb_addr; /* 8 8 */ const struct fsl_qspi_devtype_data * devtype_data; /* 16 8 */ struct mutex lock; /* 24 32 */ struct completion c; /* 56 32 */ /* XXX last struct has 1 hole */ /* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */ struct clk * clk; /* 88 8 */ struct clk * clk_en; /* 96 8 */ struct pm_qos_request pm_qos_req; /* 104 48 */ /* --- cacheline 2 boundary (128 bytes) was 24 bytes ago --- */ struct device * dev; /* 152 8 */ int selected; /* 160 4 */ u32 memmap_phy; /* 164 4 */ /* size: 168, cachelines: 3, members: 11 */ /* member types with holes: 1, total: 1 */ /* last cacheline: 40 bytes */ }; Reviewed-by: Han Xu Signed-off-by: Kevin Hao Link: https://patch.msgid.link/20250411-spi-v1-1-8d6dfb1a9262@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-qspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index f879533535e8..c887abb028d7 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -264,14 +264,14 @@ static const struct fsl_qspi_devtype_data ls2080a_data = { struct fsl_qspi { void __iomem *iobase; void __iomem *ahb_addr; - u32 memmap_phy; - struct clk *clk, *clk_en; - struct device *dev; - struct completion c; const struct fsl_qspi_devtype_data *devtype_data; struct mutex lock; + struct completion c; + struct clk *clk, *clk_en; struct pm_qos_request pm_qos_req; + struct device *dev; int selected; + u32 memmap_phy; }; static inline int needs_swap_endian(struct fsl_qspi *q) From bf657e234ac12923b579b13d8b9f1b5ca0519697 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 1 Apr 2025 15:35:20 +0100 Subject: [PATCH 13/90] memory: renesas-rpc-if: Fix RPCIF_DRENR_CDB macro error Fix the below macro error found while moving the reg definitions to renesas-rpc-if-regs.h in the later patch. ERROR: Macros with complex values should be enclosed in parentheses Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250401143537.224047-3-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 15b4706aafee..c29bec8327e8 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -56,7 +56,7 @@ #define RPCIF_DROPR 0x0018 /* R/W */ #define RPCIF_DRENR 0x001C /* R/W */ -#define RPCIF_DRENR_CDB(o) (u32)((((o) & 0x3) << 30)) +#define RPCIF_DRENR_CDB(o) (((u32)((o) & 0x3)) << 30) #define RPCIF_DRENR_OCDB(o) (((o) & 0x3) << 28) #define RPCIF_DRENR_ADB(o) (((o) & 0x3) << 24) #define RPCIF_DRENR_OPDB(o) (((o) & 0x3) << 20) From 74c35c84f2ba942e7a7744658a8257d0b3188ac2 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 1 Apr 2025 15:35:23 +0100 Subject: [PATCH 14/90] memory: renesas-rpc-if: Move rpcif_info definitions near to the user Move rpcif_info definitions near to the user. Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250401143537.224047-6-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index c29bec8327e8..4a59bf27eace 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -191,26 +191,6 @@ struct rpcif_priv { u32 ddr; /* DRDRENR or SMDRENR */ }; -static const struct rpcif_info rpcif_info_r8a7796 = { - .type = RPCIF_RCAR_GEN3, - .strtim = 6, -}; - -static const struct rpcif_info rpcif_info_gen3 = { - .type = RPCIF_RCAR_GEN3, - .strtim = 7, -}; - -static const struct rpcif_info rpcif_info_rz_g2l = { - .type = RPCIF_RZ_G2L, - .strtim = 7, -}; - -static const struct rpcif_info rpcif_info_gen4 = { - .type = RPCIF_RCAR_GEN4, - .strtim = 15, -}; - /* * Custom accessor functions to ensure SM[RW]DR[01] are always accessed with * proper width. Requires rpcif_priv.xfer_size to be correctly set before! @@ -784,6 +764,26 @@ static void rpcif_remove(struct platform_device *pdev) platform_device_unregister(rpc->vdev); } +static const struct rpcif_info rpcif_info_r8a7796 = { + .type = RPCIF_RCAR_GEN3, + .strtim = 6, +}; + +static const struct rpcif_info rpcif_info_gen3 = { + .type = RPCIF_RCAR_GEN3, + .strtim = 7, +}; + +static const struct rpcif_info rpcif_info_rz_g2l = { + .type = RPCIF_RZ_G2L, + .strtim = 7, +}; + +static const struct rpcif_info rpcif_info_gen4 = { + .type = RPCIF_RCAR_GEN4, + .strtim = 15, +}; + static const struct of_device_id rpcif_of_match[] = { { .compatible = "renesas,r8a7796-rpc-if", .data = &rpcif_info_r8a7796 }, { .compatible = "renesas,rcar-gen3-rpc-if", .data = &rpcif_info_gen3 }, From 23812bbd7d5fe27b6b2e0fe5a8ba4c6f37f26671 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 17 Apr 2025 10:39:02 +0200 Subject: [PATCH 15/90] spi: spi-cavium-thunderx: Use non-hybrid PCI devres API cavium-thunderx enables its PCI device with pcim_enable_device(). This, implicitly, switches the function pci_request_regions() into managed mode, where it becomes a devres function. The PCI subsystem wants to remove this hybrid nature from its interfaces. To do so, users of the aforementioned combination of functions must be ported to non-hybrid functions. Moreover, since both functions are already managed in this driver, the calls to pci_release_regions() are unnecessary. Remove the calls to pci_release_regions(). Replace the call to sometimes-managed pci_request_regions() with one to the always-managed pcim_request_all_regions(). Signed-off-by: Philipp Stanner Link: https://patch.msgid.link/20250417083902.23483-2-phasta@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cavium-thunderx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c index 337aef12abcc..367ae7120bb3 100644 --- a/drivers/spi/spi-cavium-thunderx.c +++ b/drivers/spi/spi-cavium-thunderx.c @@ -34,7 +34,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev, if (ret) goto error; - ret = pci_request_regions(pdev, DRV_NAME); + ret = pcim_request_all_regions(pdev, DRV_NAME); if (ret) goto error; @@ -78,7 +78,6 @@ static int thunderx_spi_probe(struct pci_dev *pdev, return 0; error: - pci_release_regions(pdev); spi_controller_put(host); return ret; } @@ -92,7 +91,6 @@ static void thunderx_spi_remove(struct pci_dev *pdev) if (!p) return; - pci_release_regions(pdev); /* Put everything in a known state. */ writeq(0, p->register_base + OCTEON_SPI_CFG(p)); } From d981e7b3f25fbabca9cdd02aa2a8f16d6f235fc2 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Thu, 17 Apr 2025 10:39:04 +0200 Subject: [PATCH 16/90] spi: pci1xxxx: Use non-hybrid PCI devres API pci1xxxx enables its PCI device with pcim_enable_device(). This, implicitly, switches the function pci_request_regions() into managed mode, where it becomes a devres function. The PCI subsystem wants to remove this hybrid nature from its interfaces. To do so, users of the aforementioned combination of functions must be ported to non-hybrid functions. Moreover, since both functions are already managed in this driver, the call to pci_release_regions() is unnecessary. Remove the call to pci_release_regions(). Replace the call to sometimes-managed pci_request_regions() with one to the always-managed pcim_request_all_regions(). Signed-off-by: Philipp Stanner Link: https://patch.msgid.link/20250417083902.23483-4-phasta@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-pci1xxxx.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index fc98979eba48..330078b1d50f 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -741,21 +741,19 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * if (ret) return -ENOMEM; - ret = pci_request_regions(pdev, DRV_NAME); + ret = pcim_request_all_regions(pdev, DRV_NAME); if (ret) return -ENOMEM; spi_bus->reg_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); - if (!spi_bus->reg_base) { - ret = -EINVAL; - goto error; - } + if (!spi_bus->reg_base) + return -EINVAL; ret = pci_alloc_irq_vectors(pdev, hw_inst_cnt, hw_inst_cnt, PCI_IRQ_ALL_TYPES); if (ret < 0) { dev_err(&pdev->dev, "Error allocating MSI vectors\n"); - goto error; + return ret; } init_completion(&spi_sub_ptr->spi_xfer_done); @@ -773,13 +771,12 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * if (ret < 0) { dev_err(&pdev->dev, "Unable to request irq : %d", spi_sub_ptr->irq); - ret = -ENODEV; - goto error; + return -ENODEV; } ret = pci1xxxx_spi_dma_init(spi_bus, spi_sub_ptr->irq); if (ret && ret != -EOPNOTSUPP) - goto error; + return ret; /* This register is only applicable for 1st instance */ regval = readl(spi_bus->reg_base + SPI_PCI_CTRL_REG_OFFSET(0)); @@ -808,8 +805,7 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * if (ret < 0) { dev_err(&pdev->dev, "Unable to request irq : %d", spi_sub_ptr->irq); - ret = -ENODEV; - goto error; + return -ENODEV; } } @@ -828,15 +824,11 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * spi_controller_set_devdata(spi_host, spi_sub_ptr); ret = devm_spi_register_controller(dev, spi_host); if (ret) - goto error; + return ret; } pci_set_drvdata(pdev, spi_bus); return 0; - -error: - pci_release_regions(pdev); - return ret; } static void store_restore_config(struct pci1xxxx_spi *spi_ptr, From 163ddf1fea590229c30a8dc4c29ff4febfb895c3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 17 Apr 2025 18:24:46 +0300 Subject: [PATCH 17/90] spi: Add spi_bpw_to_bytes() helper and use it This helper converts the given bits per word to bytes. The result will always be power-of-two, e.g., =============== ================= Input (in bits) Output (in bytes) =============== ================= 5 1 9 2 21 4 37 8 =============== ================= It will return 0 for the 0 input. There are a couple of cases in SPI that are using the same approach and at least one more (in IIO) would benefit of it. Add a helper for everyone. Signed-off-by: Andy Shevchenko Reviewed-by: David Lechner Link: https://patch.msgid.link/20250417152529.490582-2-andriy.shevchenko@linux.intel.com Acked-by: Mukesh Kumar Savaliya Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- include/linux/spi/spi.h | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 90e27729ef6b..cdec46a5ab72 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3802,7 +3802,7 @@ int spi_split_transfers_maxwords(struct spi_controller *ctlr, size_t maxsize; int ret; - maxsize = maxwords * roundup_pow_of_two(BITS_TO_BYTES(xfer->bits_per_word)); + maxsize = maxwords * spi_bpw_to_bytes(xfer->bits_per_word); if (xfer->len > maxsize) { ret = __spi_split_transfer_maxsize(ctlr, msg, &xfer, maxsize); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 0ba5e49bace4..5b872fe3b1a2 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -1325,6 +1325,32 @@ static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw) return false; } +/** + * spi_bpw_to_bytes - Covert bits per word to bytes + * @bpw: Bits per word + * + * This function converts the given @bpw to bytes. The result is always + * power-of-two, e.g., + * + * =============== ================= + * Input (in bits) Output (in bytes) + * =============== ================= + * 5 1 + * 9 2 + * 21 4 + * 37 8 + * =============== ================= + * + * It will return 0 for the 0 input. + * + * Returns: + * Bytes for the given @bpw. + */ +static inline u32 spi_bpw_to_bytes(u32 bpw) +{ + return roundup_pow_of_two(BITS_TO_BYTES(bpw)); +} + /** * spi_controller_xfer_timeout - Compute a suitable timeout value * @ctlr: SPI device From e30b7a75666b3f444abfabed6a144642fa9994d8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 17 Apr 2025 18:24:47 +0300 Subject: [PATCH 18/90] spi: dw: Use spi_bpw_to_bytes() helper Use existing helper to get amount of bytes (as power-of-two value) from bits per word. Signed-off-by: Andy Shevchenko Reviewed-by: David Lechner Link: https://patch.msgid.link/20250417152529.490582-3-andriy.shevchenko@linux.intel.com Acked-by: Mukesh Kumar Savaliya Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 941ecc6f59f8..b3b883cb9541 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -423,7 +423,7 @@ static int dw_spi_transfer_one(struct spi_controller *host, int ret; dws->dma_mapped = 0; - dws->n_bytes = roundup_pow_of_two(BITS_TO_BYTES(transfer->bits_per_word)); + dws->n_bytes = spi_bpw_to_bytes(transfer->bits_per_word); dws->tx = (void *)transfer->tx_buf; dws->tx_len = transfer->len / dws->n_bytes; dws->rx = transfer->rx_buf; From 4cc9cf2f437ccf6915100c2f38f63cfb1abad6f9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 17 Apr 2025 13:16:31 +0200 Subject: [PATCH 19/90] spi: dt-bindings: Fix description mentioning a removed property 'spi-cpha' was removed from this file. So, replace it in the description with an existing example. Reformat the paragraph to adhere to max line length. Fixes: 233363aba72a ("spi/panel: dt-bindings: drop CPHA and CPOL from common properties") Signed-off-by: Wolfram Sang Link: https://patch.msgid.link/20250417111630.53084-2-wsa+renesas@sang-engineering.com Signed-off-by: Mark Brown --- .../bindings/spi/spi-peripheral-props.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml index 0bb443b8decd..8fc17e16efb2 100644 --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml @@ -8,12 +8,13 @@ title: Peripheral-specific properties for a SPI bus. description: Many SPI controllers need to add properties to peripheral devices. They could - be common properties like spi-max-frequency, spi-cpha, etc. or they could be - controller specific like delay in clock or data lines, etc. These properties - need to be defined in the peripheral node because they are per-peripheral and - there can be multiple peripherals attached to a controller. All those - properties are listed here. The controller specific properties should go in - their own separate schema that should be referenced from here. + be common properties like spi-max-frequency, spi-cs-high, etc. or they could + be controller specific like delay in clock or data lines, etc. These + properties need to be defined in the peripheral node because they are + per-peripheral and there can be multiple peripherals attached to a + controller. All those properties are listed here. The controller specific + properties should go in their own separate schema that should be referenced + from here. maintainers: - Mark Brown From fcab1637bb115b3b7bbc600a6d019b785653f2d5 Mon Sep 17 00:00:00 2001 From: Andres Urian Florez Date: Fri, 18 Apr 2025 17:47:49 -0500 Subject: [PATCH 20/90] spi: offload: check for match callback when a trigger is being registered Make match a required callback when a new trigger is being registered, this allows that other functions like spi_offload_trigger_get() could safely invoke the callback when it is required In v2: - improve readability of the condition Signed-off-by: Andres Urian Florez Reviewed-by: David Lechner Link: https://patch.msgid.link/20250418224750.46219-1-andres.emb.sys@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-offload.c b/drivers/spi/spi-offload.c index 6bad042fe437..21a0f3a3a176 100644 --- a/drivers/spi/spi-offload.c +++ b/drivers/spi/spi-offload.c @@ -434,7 +434,7 @@ int devm_spi_offload_trigger_register(struct device *dev, { struct spi_offload_trigger *trigger; - if (!info->fwnode || !info->ops) + if (!info->fwnode || !info->ops || !info->ops->match) return -EINVAL; trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); From 296e8d289bdd7eb0d832683ebd3e847fbb4c1b12 Mon Sep 17 00:00:00 2001 From: Andres Urian Florez Date: Fri, 18 Apr 2025 12:14:24 -0500 Subject: [PATCH 21/90] spi: offload: remove unnecessary check on trigger->ops Considering that trigger->ops = NULL happens only when the trigger is being removed from the list, and at that point the operation is protected with the spi_offload_triggers_lock, it is possible to remove the !trigger->ops check because it will never be true in spi_offload_trigger_get() Signed-off-by: Andres Urian Florez Reviewed-by: David Lechner Link: https://patch.msgid.link/20250418171426.9868-1-andres.emb.sys@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-offload.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/spi/spi-offload.c b/drivers/spi/spi-offload.c index 21a0f3a3a176..e674097bf3be 100644 --- a/drivers/spi/spi-offload.c +++ b/drivers/spi/spi-offload.c @@ -183,9 +183,6 @@ static struct spi_offload_trigger guard(mutex)(&trigger->lock); - if (!trigger->ops) - return ERR_PTR(-ENODEV); - if (trigger->ops->request) { ret = trigger->ops->request(trigger, type, args->args, args->nargs); if (ret) From b50a1e1f3c4630f729629a787d891d7b4348007f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 9 Apr 2025 18:25:01 +0200 Subject: [PATCH 22/90] spi: intel: Improve resource mapping Let's use the pci/platform-specialized functions for mapping a resource, and pass the mapped address to intel_spi_probe. Benefits are: - No separate call needed for getting the resource, and no access to struct pci_dev internals (pdev->resource[]). - More user-friendly output in /proc/iomem. In my case: before 80704000-80704fff : 0000:00:1f.5 80704000-80704fff : 0000:00:1f.5 0000:00:1f.5 after 80704000-80704fff : 0000:00:1f.5 80704000-80704fff : spi_intel_pci Signed-off-by: Heiner Kallweit Link: https://patch.msgid.link/2585fa05-60c4-48c4-a838-e87014665ae2@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-intel-pci.c | 8 +++++++- drivers/spi/spi-intel-platform.c | 9 ++++++--- drivers/spi/spi-intel.c | 9 +++------ drivers/spi/spi-intel.h | 4 +--- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c index 4d9ffec900bb..4b63cb98df9c 100644 --- a/drivers/spi/spi-intel-pci.c +++ b/drivers/spi/spi-intel-pci.c @@ -44,6 +44,7 @@ static int intel_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct intel_spi_boardinfo *info; + void __iomem *base; int ret; ret = pcim_enable_device(pdev); @@ -56,7 +57,12 @@ static int intel_spi_pci_probe(struct pci_dev *pdev, return -ENOMEM; info->data = pdev; - return intel_spi_probe(&pdev->dev, &pdev->resource[0], info); + + base = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); + if (IS_ERR(base)) + return PTR_ERR(base); + + return intel_spi_probe(&pdev->dev, base, info); } static const struct pci_device_id intel_spi_pci_ids[] = { diff --git a/drivers/spi/spi-intel-platform.c b/drivers/spi/spi-intel-platform.c index 0974cca83a5d..6cbed0b2e063 100644 --- a/drivers/spi/spi-intel-platform.c +++ b/drivers/spi/spi-intel-platform.c @@ -14,14 +14,17 @@ static int intel_spi_platform_probe(struct platform_device *pdev) { struct intel_spi_boardinfo *info; - struct resource *mem; + void __iomem *base; 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); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + return intel_spi_probe(&pdev->dev, base, info); } static struct platform_driver intel_spi_platform_driver = { diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index b0dcdb6fb8fa..5d5a546c62ea 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -1467,13 +1467,13 @@ EXPORT_SYMBOL_GPL(intel_spi_groups); /** * intel_spi_probe() - Probe the Intel SPI flash controller * @dev: Pointer to the parent device - * @mem: MMIO resource + * @base: iomapped MMIO resource * @info: Platform specific 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, +int intel_spi_probe(struct device *dev, void __iomem *base, const struct intel_spi_boardinfo *info) { struct spi_controller *host; @@ -1488,10 +1488,7 @@ int intel_spi_probe(struct device *dev, struct resource *mem, ispi = spi_controller_get_devdata(host); - ispi->base = devm_ioremap_resource(dev, mem); - if (IS_ERR(ispi->base)) - return PTR_ERR(ispi->base); - + ispi->base = base; ispi->dev = dev; ispi->host = host; ispi->info = info; diff --git a/drivers/spi/spi-intel.h b/drivers/spi/spi-intel.h index c5f35060dd63..53b292c6ea0c 100644 --- a/drivers/spi/spi-intel.h +++ b/drivers/spi/spi-intel.h @@ -11,11 +11,9 @@ #include -struct resource; - extern const struct attribute_group *intel_spi_groups[]; -int intel_spi_probe(struct device *dev, struct resource *mem, +int intel_spi_probe(struct device *dev, void __iomem *base, const struct intel_spi_boardinfo *info); #endif /* SPI_INTEL_H */ From dcb06c638a1174008a985849fa30fc0da7d08904 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Wed, 16 Apr 2025 11:06:01 +0000 Subject: [PATCH 23/90] spi: tegra210-quad: Fix X1_X2_X4 encoding and support x4 transfers This patch corrects the QSPI_COMMAND_X1_X2_X4 and QSPI_ADDRESS_X1_X2_X4 macros to properly encode the bus width for x1, x2, and x4 transfers. Although these macros were previously incorrect, they were not being used in the driver, so no functionality was affected. The patch updates tegra_qspi_cmd_config() and tegra_qspi_addr_config() function calls to use the actual bus width from the transfer, instead of hardcoding it to 0 (which implied x1 mode). This change enables proper support for x1, x2, and x4 data transfers by correctly configuring the interface width for commands and addresses. These modifications improve the QSPI driver's flexibility and prepare it for future use cases that may require different bus widths for commands and addresses. Fixes: 1b8342cc4a38 ("spi: tegra210-quad: combined sequence mode") Signed-off-by: Vishwaroop A Link: https://patch.msgid.link/20250416110606.2737315-2-va@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 08e49a876894..4983b1f705b8 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -134,7 +134,7 @@ #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(x) ((((x) >> 1) & 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) @@ -147,7 +147,7 @@ #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(x) ((((x) >> 1) & 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) @@ -1036,10 +1036,6 @@ 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 @@ -1079,13 +1075,13 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, switch (transfer_phase) { case CMD_TRANSFER: /* X1 SDR mode */ - cmd_config = tegra_qspi_cmd_config(false, 0, + cmd_config = tegra_qspi_cmd_config(false, xfer->tx_nbits, xfer->len); cmd_value = *((const u8 *)(xfer->tx_buf)); break; case ADDR_TRANSFER: /* X1 SDR mode */ - addr_config = tegra_qspi_addr_config(false, 0, + addr_config = tegra_qspi_addr_config(false, xfer->tx_nbits, xfer->len); address_value = *((const u32 *)(xfer->tx_buf)); break; From 400d9f1a27cc2fceabdb1ed93eaf0b89b6d32ba5 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Wed, 16 Apr 2025 11:06:02 +0000 Subject: [PATCH 24/90] spi: tegra210-quad: remove redundant error handling code Remove unnecessary error handling code that terminated transfers and executed delay on errors. This code was redundant as error handling is already done at a higher level in the SPI core. Fixes: 1b8342cc4a38 ("spi: tegra210-quad: combined sequence mode") Signed-off-by: Vishwaroop A Link: https://patch.msgid.link/20250416110606.2737315-3-va@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 4983b1f705b8..a9359b005ee8 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -1175,10 +1175,6 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, exit: msg->status = ret; - if (ret < 0) { - tegra_qspi_transfer_end(spi); - spi_transfer_delay_exec(xfer); - } return ret; } From d8966b65413390d1b5b706886987caac05fbe024 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Wed, 16 Apr 2025 11:06:03 +0000 Subject: [PATCH 25/90] spi: tegra210-quad: modify chip select (CS) deactivation Modify the chip select (CS) deactivation and inter-transfer delay execution only during the DATA_TRANSFER phase when the cs_change flag is not set. This ensures proper CS handling and timing between transfers while eliminating redundant operations. Fixes: 1b8342cc4a38 ("spi: tegra210-quad: combined sequence mode") Signed-off-by: Vishwaroop A Link: https://patch.msgid.link/20250416110606.2737315-4-va@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index a9359b005ee8..159fbbfd4a38 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -1159,16 +1159,16 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, ret = -EIO; goto exit; } - if (!xfer->cs_change) { - tegra_qspi_transfer_end(spi); - spi_transfer_delay_exec(xfer); - } break; default: ret = -EINVAL; goto exit; } msg->actual_length += xfer->len; + if (!xfer->cs_change && transfer_phase == DATA_TRANSFER) { + tegra_qspi_transfer_end(spi); + spi_transfer_delay_exec(xfer); + } transfer_phase++; } ret = 0; From c283fcdc4e2b89678c171691fd26f576139fc256 Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Wed, 16 Apr 2025 11:06:05 +0000 Subject: [PATCH 26/90] spi: tegra210-quad: Update dummy sequence configuration Adding support for the dummy sequence configuration. The dummy sequence introduces a delay between the command and the data phases of a transfer. This delay, measured in clock cycles, allows the slave device to prepare for data transmission, ensuring data integrity and proper synchronization. Signed-off-by: Vishwaroop A Link: https://patch.msgid.link/20250416110606.2737315-6-va@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 159fbbfd4a38..04f41e92c1e2 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -22,6 +22,7 @@ #include #include #include +#include #define QSPI_COMMAND1 0x000 #define QSPI_BIT_LENGTH(x) (((x) & 0x1f) << 0) @@ -156,10 +157,14 @@ #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 +#define DEFAULT_QSPI_DMA_BUF_LEN SZ_64K + +enum tegra_qspi_transfer_type { + CMD_TRANSFER = 0, + ADDR_TRANSFER = 1, + DUMMY_TRANSFER = 2, + DATA_TRANSFER = 3 +}; struct tegra_qspi_soc_data { bool has_dma; @@ -1085,6 +1090,13 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, xfer->len); address_value = *((const u32 *)(xfer->tx_buf)); break; + case DUMMY_TRANSFER: + if (xfer->dummy_data) { + tqspi->dummy_cycles = xfer->len * 8 / xfer->tx_nbits; + break; + } + transfer_phase++; + fallthrough; case DATA_TRANSFER: /* Program Command, Address value in register */ tegra_qspi_writel(tqspi, cmd_value, QSPI_CMB_SEQ_CMD); @@ -1292,7 +1304,9 @@ static bool tegra_qspi_validate_cmb_seq(struct tegra_qspi *tqspi, list_for_each_entry(xfer, &msg->transfers, transfer_list) { transfer_count++; } - if (!tqspi->soc_data->cmb_xfer_capable || transfer_count != 3) + if (!tqspi->soc_data->cmb_xfer_capable) + return false; + if (transfer_count > 4 || transfer_count < 3) return false; xfer = list_first_entry(&msg->transfers, typeof(*xfer), transfer_list); @@ -1302,6 +1316,13 @@ static bool tegra_qspi_validate_cmb_seq(struct tegra_qspi *tqspi, if (xfer->len > 4 || xfer->len < 3) return false; xfer = list_next_entry(xfer, transfer_list); + if (transfer_count == 4) { + if (xfer->dummy_data != 1) + return false; + if ((xfer->len * 8 / xfer->tx_nbits) > QSPI_DUMMY_CYCLES_MAX) + return false; + xfer = list_next_entry(xfer, transfer_list); + } if (!tqspi->soc_data->has_dma && xfer->len > (QSPI_FIFO_DEPTH << 2)) return false; From 18197e98353d931fc7bb2bb9ec671d3aa407831d Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Mon, 14 Apr 2025 14:30:16 +0800 Subject: [PATCH 27/90] spi: meson-spicc: add DMA support Add DMA support for spicc driver. DMA works if the transfer meets the following conditions: 1. 64 bits per word; 2. The transfer length must be multiples of the dma_burst_len, and the dma_burst_len should be one of 8,7...2, otherwise, it will be split into several SPI bursts. Signed-off-by: Sunny Luo Signed-off-by: Xianwei Zhao Link: https://patch.msgid.link/20250414-spi-dma-v2-1-84bbd92fa469@amlogic.com Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spicc.c | 241 +++++++++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index df74ad5060f8..6b9137307533 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -21,18 +21,26 @@ #include #include #include +#include /* - * The Meson SPICC controller could support DMA based transfers, but is not - * implemented by the vendor code, and while having the registers documentation - * it has never worked on the GXL Hardware. - * The PIO mode is the only mode implemented, and due to badly designed HW : - * - all transfers are cutted in 16 words burst because the FIFO hangs on - * TX underflow, and there is no TX "Half-Empty" interrupt, so we go by - * FIFO max size chunk only - * - CS management is dumb, and goes UP between every burst, so is really a - * "Data Valid" signal than a Chip Select, GPIO link should be used instead - * to have a CS go down over the full transfer + * There are two modes for data transmission: PIO and DMA. + * When bits_per_word is 8, 16, 24, or 32, data is transferred using PIO mode. + * When bits_per_word is 64, DMA mode is used by default. + * + * DMA achieves a transfer with one or more SPI bursts, each SPI burst is made + * up of one or more DMA bursts. The DMA burst implementation mechanism is, + * For TX, when the number of words in TXFIFO is less than the preset + * reading threshold, SPICC starts a reading DMA burst, which reads the preset + * number of words from TX buffer, then writes them into TXFIFO. + * For RX, when the number of words in RXFIFO is greater than the preset + * writing threshold, SPICC starts a writing request burst, which reads the + * preset number of words from RXFIFO, then write them into RX buffer. + * DMA works if the transfer meets the following conditions, + * - 64 bits per word + * - The transfer length in word must be multiples of the dma_burst_len, and + * the dma_burst_len should be one of 8,7...2, otherwise, it will be split + * into several SPI bursts by this driver */ #define SPICC_MAX_BURST 128 @@ -128,6 +136,23 @@ #define SPICC_DWADDR 0x24 /* Write Address of DMA */ +#define SPICC_LD_CNTL0 0x28 +#define VSYNC_IRQ_SRC_SELECT BIT(0) +#define DMA_EN_SET_BY_VSYNC BIT(2) +#define XCH_EN_SET_BY_VSYNC BIT(3) +#define DMA_READ_COUNTER_EN BIT(4) +#define DMA_WRITE_COUNTER_EN BIT(5) +#define DMA_RADDR_LOAD_BY_VSYNC BIT(6) +#define DMA_WADDR_LOAD_BY_VSYNC BIT(7) +#define DMA_ADDR_LOAD_FROM_LD_ADDR BIT(8) + +#define SPICC_LD_CNTL1 0x2c +#define DMA_READ_COUNTER GENMASK(15, 0) +#define DMA_WRITE_COUNTER GENMASK(31, 16) +#define DMA_BURST_LEN_DEFAULT 8 +#define DMA_BURST_COUNT_MAX 0xffff +#define SPI_BURST_LEN_MAX (DMA_BURST_LEN_DEFAULT * DMA_BURST_COUNT_MAX) + #define SPICC_ENH_CTL0 0x38 /* Enhanced Feature */ #define SPICC_ENH_CLK_CS_DELAY_MASK GENMASK(15, 0) #define SPICC_ENH_DATARATE_MASK GENMASK(23, 16) @@ -171,6 +196,9 @@ struct meson_spicc_device { struct pinctrl *pinctrl; struct pinctrl_state *pins_idle_high; struct pinctrl_state *pins_idle_low; + dma_addr_t tx_dma; + dma_addr_t rx_dma; + bool using_dma; }; #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div) @@ -202,6 +230,148 @@ static void meson_spicc_oen_enable(struct meson_spicc_device *spicc) writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0); } +static int meson_spicc_dma_map(struct meson_spicc_device *spicc, + struct spi_transfer *t) +{ + struct device *dev = spicc->host->dev.parent; + + if (!(t->tx_buf && t->rx_buf)) + return -EINVAL; + + t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE); + if (dma_mapping_error(dev, t->tx_dma)) + return -ENOMEM; + + t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, t->rx_dma)) + return -ENOMEM; + + spicc->tx_dma = t->tx_dma; + spicc->rx_dma = t->rx_dma; + + return 0; +} + +static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc, + struct spi_transfer *t) +{ + struct device *dev = spicc->host->dev.parent; + + if (t->tx_dma) + dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE); + if (t->rx_dma) + dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE); +} + +/* + * According to the remain words length, calculate a suitable spi burst length + * and a dma burst length for current spi burst + */ +static u32 meson_spicc_calc_dma_len(struct meson_spicc_device *spicc, + u32 len, u32 *dma_burst_len) +{ + u32 i; + + if (len <= spicc->data->fifo_size) { + *dma_burst_len = len; + return len; + } + + *dma_burst_len = DMA_BURST_LEN_DEFAULT; + + if (len == (SPI_BURST_LEN_MAX + 1)) + return SPI_BURST_LEN_MAX - DMA_BURST_LEN_DEFAULT; + + if (len >= SPI_BURST_LEN_MAX) + return SPI_BURST_LEN_MAX; + + for (i = DMA_BURST_LEN_DEFAULT; i > 1; i--) + if ((len % i) == 0) { + *dma_burst_len = i; + return len; + } + + i = len % DMA_BURST_LEN_DEFAULT; + len -= i; + + if (i == 1) + len -= DMA_BURST_LEN_DEFAULT; + + return len; +} + +static void meson_spicc_setup_dma(struct meson_spicc_device *spicc) +{ + unsigned int len; + unsigned int dma_burst_len, dma_burst_count; + unsigned int count_en = 0; + unsigned int txfifo_thres = 0; + unsigned int read_req = 0; + unsigned int rxfifo_thres = 31; + unsigned int write_req = 0; + unsigned int ld_ctr1 = 0; + + writel_relaxed(spicc->tx_dma, spicc->base + SPICC_DRADDR); + writel_relaxed(spicc->rx_dma, spicc->base + SPICC_DWADDR); + + /* Set the max burst length to support a transmission with length of + * no more than 1024 bytes(128 words), which must use the CS management + * because of some strict timing requirements + */ + writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, SPICC_BURSTLENGTH_MASK, + spicc->base + SPICC_CONREG); + + len = meson_spicc_calc_dma_len(spicc, spicc->xfer_remain, + &dma_burst_len); + spicc->xfer_remain -= len; + dma_burst_count = DIV_ROUND_UP(len, dma_burst_len); + dma_burst_len--; + + if (spicc->tx_dma) { + spicc->tx_dma += len; + count_en |= DMA_READ_COUNTER_EN; + txfifo_thres = spicc->data->fifo_size - dma_burst_len; + read_req = dma_burst_len; + ld_ctr1 |= FIELD_PREP(DMA_READ_COUNTER, dma_burst_count); + } + + if (spicc->rx_dma) { + spicc->rx_dma += len; + count_en |= DMA_WRITE_COUNTER_EN; + rxfifo_thres = dma_burst_len; + write_req = dma_burst_len; + ld_ctr1 |= FIELD_PREP(DMA_WRITE_COUNTER, dma_burst_count); + } + + writel_relaxed(count_en, spicc->base + SPICC_LD_CNTL0); + writel_relaxed(ld_ctr1, spicc->base + SPICC_LD_CNTL1); + writel_relaxed(SPICC_DMA_ENABLE + | SPICC_DMA_URGENT + | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK, txfifo_thres) + | FIELD_PREP(SPICC_READ_BURST_MASK, read_req) + | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, rxfifo_thres) + | FIELD_PREP(SPICC_WRITE_BURST_MASK, write_req), + spicc->base + SPICC_DMAREG); +} + +static irqreturn_t meson_spicc_dma_irq(struct meson_spicc_device *spicc) +{ + if (readl_relaxed(spicc->base + SPICC_DMAREG) & SPICC_DMA_ENABLE) + return IRQ_HANDLED; + + if (spicc->xfer_remain) { + meson_spicc_setup_dma(spicc); + } else { + writel_bits_relaxed(SPICC_SMC, 0, spicc->base + SPICC_CONREG); + writel_relaxed(0, spicc->base + SPICC_INTREG); + writel_relaxed(0, spicc->base + SPICC_DMAREG); + meson_spicc_dma_unmap(spicc, spicc->xfer); + complete(&spicc->done); + } + + return IRQ_HANDLED; +} + static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc) { return !!FIELD_GET(SPICC_TF, @@ -293,6 +463,9 @@ static irqreturn_t meson_spicc_irq(int irq, void *data) writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG); + if (spicc->using_dma) + return meson_spicc_dma_irq(spicc); + /* Empty RX FIFO */ meson_spicc_rx(spicc); @@ -426,9 +599,6 @@ static int meson_spicc_transfer_one(struct spi_controller *host, meson_spicc_reset_fifo(spicc); - /* Setup burst */ - meson_spicc_setup_burst(spicc); - /* Setup wait for completion */ reinit_completion(&spicc->done); @@ -442,11 +612,36 @@ static int meson_spicc_transfer_one(struct spi_controller *host, /* Increase it twice and add 200 ms tolerance */ timeout += timeout + 200; - /* Start burst */ - writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); + if (xfer->bits_per_word == 64) { + int ret; - /* Enable interrupts */ - writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG); + /* dma_burst_len 1 can't trigger a dma burst */ + if (xfer->len < 16) + return -EINVAL; + + ret = meson_spicc_dma_map(spicc, xfer); + if (ret) { + meson_spicc_dma_unmap(spicc, xfer); + dev_err(host->dev.parent, "dma map failed\n"); + return ret; + } + + spicc->using_dma = true; + spicc->xfer_remain = DIV_ROUND_UP(xfer->len, spicc->bytes_per_word); + meson_spicc_setup_dma(spicc); + writel_relaxed(SPICC_TE_EN, spicc->base + SPICC_INTREG); + writel_bits_relaxed(SPICC_SMC, SPICC_SMC, spicc->base + SPICC_CONREG); + } else { + spicc->using_dma = false; + /* Setup burst */ + meson_spicc_setup_burst(spicc); + + /* Start burst */ + writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); + + /* Enable interrupts */ + writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG); + } if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout))) return -ETIMEDOUT; @@ -545,6 +740,14 @@ static int meson_spicc_setup(struct spi_device *spi) if (!spi->controller_state) spi->controller_state = spi_controller_get_devdata(spi->controller); + /* DMA works at 64 bits, the rest works on PIO */ + if (spi->bits_per_word != 8 && + spi->bits_per_word != 16 && + spi->bits_per_word != 24 && + spi->bits_per_word != 32 && + spi->bits_per_word != 64) + return -EINVAL; + return 0; } @@ -853,10 +1056,6 @@ static int meson_spicc_probe(struct platform_device *pdev) host->num_chipselect = 4; host->dev.of_node = pdev->dev.of_node; host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP; - host->bits_per_word_mask = SPI_BPW_MASK(32) | - SPI_BPW_MASK(24) | - SPI_BPW_MASK(16) | - SPI_BPW_MASK(8); host->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX); host->min_speed_hz = spicc->data->min_speed_hz; host->max_speed_hz = spicc->data->max_speed_hz; From 64c05a1d66193b3a40ad1f29c3d8ba5483e4e0dc Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 24 Apr 2025 20:10:59 +0200 Subject: [PATCH 28/90] spi: spi-qpic-snand: remove unused 'wlen' member of 'struct qpic_spi_nand' The 'wlen' member of the qpic_spi_nand structure is never used in the code so remove that. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250424-qpic-snand-remove-wlen-v1-1-2a7467ce2e3a@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index a2af731049d4..d0e4f49cd89b 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -116,7 +116,6 @@ struct qpic_spi_nand { struct nand_ecc_engine ecc_eng; u8 *data_buf; u8 *oob_buf; - u32 wlen; __le32 addr1; __le32 addr2; __le32 cmd; From 73db799bf5efc5a04654bb3ff6c9bf63a0dfa473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= Date: Thu, 27 Mar 2025 20:59:26 +0100 Subject: [PATCH 29/90] PM: runtime: Add new devm functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `devm_pm_runtime_set_active_enabled()` and `devm_pm_runtime_get_noresume()` for simplifying common cases in drivers. Signed-off-by: Bence Csókás Link: https://patch.msgid.link/20250327195928.680771-3-csokas.bence@prolan.hu Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 44 ++++++++++++++++++++++++++++++++++++ include/linux/pm_runtime.h | 4 ++++ 2 files changed, 48 insertions(+) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 0e127b0329c0..205a4f8828b0 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1568,6 +1568,32 @@ out: } EXPORT_SYMBOL_GPL(pm_runtime_enable); +static void pm_runtime_set_suspended_action(void *data) +{ + pm_runtime_set_suspended(data); +} + +/** + * devm_pm_runtime_set_active_enabled - set_active version of devm_pm_runtime_enable. + * + * @dev: Device to handle. + */ +int devm_pm_runtime_set_active_enabled(struct device *dev) +{ + int err; + + err = pm_runtime_set_active(dev); + if (err) + return err; + + err = devm_add_action_or_reset(dev, pm_runtime_set_suspended_action, dev); + if (err) + return err; + + return devm_pm_runtime_enable(dev); +} +EXPORT_SYMBOL_GPL(devm_pm_runtime_set_active_enabled); + static void pm_runtime_disable_action(void *data) { pm_runtime_dont_use_autosuspend(data); @@ -1590,6 +1616,24 @@ int devm_pm_runtime_enable(struct device *dev) } EXPORT_SYMBOL_GPL(devm_pm_runtime_enable); +static void pm_runtime_put_noidle_action(void *data) +{ + pm_runtime_put_noidle(data); +} + +/** + * devm_pm_runtime_get_noresume - devres-enabled version of pm_runtime_get_noresume. + * + * @dev: Device to handle. + */ +int devm_pm_runtime_get_noresume(struct device *dev) +{ + pm_runtime_get_noresume(dev); + + return devm_add_action_or_reset(dev, pm_runtime_put_noidle_action, dev); +} +EXPORT_SYMBOL_GPL(devm_pm_runtime_get_noresume); + /** * pm_runtime_forbid - Block runtime PM of a device. * @dev: Device to handle. diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 7fb5a459847e..756b842dcd30 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -96,7 +96,9 @@ extern void pm_runtime_new_link(struct device *dev); extern void pm_runtime_drop_link(struct device_link *link); extern void pm_runtime_release_supplier(struct device_link *link); +int devm_pm_runtime_set_active_enabled(struct device *dev); extern int devm_pm_runtime_enable(struct device *dev); +int devm_pm_runtime_get_noresume(struct device *dev); /** * pm_suspend_ignore_children - Set runtime PM behavior regarding children. @@ -294,7 +296,9 @@ static inline bool pm_runtime_blocked(struct device *dev) { return true; } static inline void pm_runtime_allow(struct device *dev) {} static inline void pm_runtime_forbid(struct device *dev) {} +static inline int devm_pm_runtime_set_active_enabled(struct device *dev) { return 0; } static inline int devm_pm_runtime_enable(struct device *dev) { return 0; } +static inline int devm_pm_runtime_get_noresume(struct device *dev) { return 0; } static inline void pm_suspend_ignore_children(struct device *dev, bool enable) {} static inline void pm_runtime_get_noresume(struct device *dev) {} From 1d0ee0c9df31c9fd1e4f8d7e2464e36fbf6e3f75 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 28 Apr 2025 15:58:56 -0500 Subject: [PATCH 30/90] spi: axi-spi-engine: wait for completion in setup Add a polling wait for SPI instruction execution to complete in the spi_engine_setup() function. In practice, these instructions complete in a few 10s of nanoseconds, so we never ran into any race conditions, but it is good practice to wait for the completion of the SPI engine instructions before returning from the setup function. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250428-adi-main-v1-1-4b8a1b88a212@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index da9840957778..d040deffa9bb 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -739,12 +740,16 @@ static int spi_engine_setup(struct spi_device *device) { struct spi_controller *host = device->controller; struct spi_engine *spi_engine = spi_controller_get_devdata(host); + unsigned int reg; if (device->mode & SPI_CS_HIGH) spi_engine->cs_inv |= BIT(spi_get_chipselect(device, 0)); else spi_engine->cs_inv &= ~BIT(spi_get_chipselect(device, 0)); + writel_relaxed(SPI_ENGINE_CMD_SYNC(0), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + writel_relaxed(SPI_ENGINE_CMD_CS_INV(spi_engine->cs_inv), spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); @@ -755,7 +760,11 @@ static int spi_engine_setup(struct spi_device *device) writel_relaxed(SPI_ENGINE_CMD_ASSERT(0, 0xff), spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); - return 0; + writel_relaxed(SPI_ENGINE_CMD_SYNC(1), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + + return readl_relaxed_poll_timeout(spi_engine->base + SPI_ENGINE_REG_SYNC_ID, + reg, reg == 1, 1, 1000); } static int spi_engine_transfer_one_message(struct spi_controller *host, From 8fc13b822c74a46587c0d8aae4ea0820b6bdb933 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 28 Apr 2025 15:58:57 -0500 Subject: [PATCH 31/90] spi: axi-spi-engine: don't repeat mode config for offload Add an optimization to avoid repeating the config instruction in each SPI message when using SPI offloading. Instead, the instruction is run once when the SPI offload trigger is enabled. This is done to allow higher sample rates for ADCs using this SPI controller. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250428-adi-main-v1-2-4b8a1b88a212@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 33 +++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index d040deffa9bb..05ef2589f8dc 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -141,6 +141,7 @@ struct spi_engine_offload { struct spi_engine *spi_engine; unsigned long flags; unsigned int offload_num; + unsigned int spi_mode_config; }; struct spi_engine { @@ -284,6 +285,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, { struct spi_device *spi = msg->spi; struct spi_controller *host = spi->controller; + struct spi_engine_offload *priv; struct spi_transfer *xfer; int clk_div, new_clk_div, inst_ns; bool keep_cs = false; @@ -297,9 +299,18 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, clk_div = 1; - spi_engine_program_add_cmd(p, dry, - SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, - spi_engine_get_config(spi))); + /* + * As an optimization, SPI offload sets once this when the offload is + * enabled instead of repeating the instruction in each message. + */ + if (msg->offload) { + priv = msg->offload->priv; + priv->spi_mode_config = spi_engine_get_config(spi); + } else { + spi_engine_program_add_cmd(p, dry, + SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, + spi_engine_get_config(spi))); + } xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list); spi_engine_gen_cs(p, dry, spi, !xfer->cs_off); @@ -842,6 +853,22 @@ static int spi_engine_trigger_enable(struct spi_offload *offload) struct spi_engine_offload *priv = offload->priv; struct spi_engine *spi_engine = priv->spi_engine; unsigned int reg; + int ret; + + writel_relaxed(SPI_ENGINE_CMD_SYNC(0), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, + priv->spi_mode_config), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + + writel_relaxed(SPI_ENGINE_CMD_SYNC(1), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + + ret = readl_relaxed_poll_timeout(spi_engine->base + SPI_ENGINE_REG_SYNC_ID, + reg, reg == 1, 1, 1000); + if (ret) + return ret; reg = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_OFFLOAD_CTRL(priv->offload_num)); From 087591c9e4fde86fe2971c34a2745c208103248e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 28 Apr 2025 15:58:58 -0500 Subject: [PATCH 32/90] spi: axi-spi-engine: optimize bits_per_word for offload Add an optimization to avoid repeating bits_per_word instructions in each message when using SPI offload. This only applies when all data xfers in a message have the same bits_per_word. In this case, we can execute the instruction that sets bits_per_word when the offload trigger is enabled. This is useful e.g. for obtaining higher sample rates on ADCs since each message takes less time to execute. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250428-adi-main-v1-3-4b8a1b88a212@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 05ef2589f8dc..b54d2e1437c9 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -142,6 +142,7 @@ struct spi_engine_offload { unsigned long flags; unsigned int offload_num; unsigned int spi_mode_config; + u8 bits_per_word; }; struct spi_engine { @@ -267,6 +268,8 @@ static int spi_engine_precompile_message(struct spi_message *msg) { unsigned int clk_div, max_hz = msg->spi->controller->max_speed_hz; struct spi_transfer *xfer; + u8 min_bits_per_word = U8_MAX; + u8 max_bits_per_word = 0; list_for_each_entry(xfer, &msg->transfers, transfer_list) { /* If we have an offload transfer, we can't rx to buffer */ @@ -275,6 +278,24 @@ static int spi_engine_precompile_message(struct spi_message *msg) clk_div = DIV_ROUND_UP(max_hz, xfer->speed_hz); xfer->effective_speed_hz = max_hz / min(clk_div, 256U); + + if (xfer->len) { + min_bits_per_word = min(min_bits_per_word, xfer->bits_per_word); + max_bits_per_word = max(max_bits_per_word, xfer->bits_per_word); + } + } + + /* + * If all xfers in the message use the same bits_per_word, we can + * provide some optimization when using SPI offload. + */ + if (msg->offload) { + struct spi_engine_offload *priv = msg->offload->priv; + + if (min_bits_per_word == max_bits_per_word) + priv->bits_per_word = min_bits_per_word; + else + priv->bits_per_word = 0; } return 0; @@ -306,6 +327,12 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, if (msg->offload) { priv = msg->offload->priv; priv->spi_mode_config = spi_engine_get_config(spi); + + /* + * If all xfers use the same bits_per_word, it can be optimized + * in the same way. + */ + bits_per_word = priv->bits_per_word; } else { spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, @@ -862,6 +889,11 @@ static int spi_engine_trigger_enable(struct spi_offload *offload) priv->spi_mode_config), spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + if (priv->bits_per_word) + writel_relaxed(SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_XFER_BITS, + priv->bits_per_word), + spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); + writel_relaxed(SPI_ENGINE_CMD_SYNC(1), spi_engine->base + SPI_ENGINE_REG_CMD_FIFO); From e6702c44c2adb28b62f81de498e9b1e4562ce660 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 28 Apr 2025 15:58:59 -0500 Subject: [PATCH 33/90] spi: axi-spi-engine: omit SYNC from offload instructions Add optimization to omit SYNC instructions from offload messages. Starting with IP core v1.5.0, the SYNC instruction is no longer required for proper operation when using the offload feature. Omitting the SYNC instruction saves a few clock cycles needed to executed which can e.g. allow achieving higher sample rates on ADCs. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250428-adi-main-v1-4-4b8a1b88a212@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index b54d2e1437c9..8cc19934b48b 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -162,6 +162,7 @@ struct spi_engine { unsigned int offload_sdo_mem_size; struct spi_offload *offload; u32 offload_caps; + bool offload_requires_sync; }; static void spi_engine_program_add_cmd(struct spi_engine_program *p, @@ -702,6 +703,8 @@ static void spi_engine_offload_unprepare(struct spi_offload *offload) static int spi_engine_optimize_message(struct spi_message *msg) { + struct spi_controller *host = msg->spi->controller; + struct spi_engine *spi_engine = spi_controller_get_devdata(host); struct spi_engine_program p_dry, *p; int ret; @@ -718,8 +721,13 @@ static int spi_engine_optimize_message(struct spi_message *msg) spi_engine_compile_message(msg, false, p); - spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC( - msg->offload ? 0 : AXI_SPI_ENGINE_CUR_MSG_SYNC_ID)); + /* + * Non-offload needs SYNC for completion interrupt. Older versions of + * the IP core also need SYNC for offload to work properly. + */ + if (!msg->offload || spi_engine->offload_requires_sync) + spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC( + msg->offload ? 0 : AXI_SPI_ENGINE_CUR_MSG_SYNC_ID)); msg->opt_state = p; @@ -1055,6 +1063,9 @@ static int spi_engine_probe(struct platform_device *pdev) spi_engine->offload_sdo_mem_size = SPI_ENGINE_OFFLOAD_SDO_FIFO_SIZE; } + /* IP v1.5 dropped the requirement for SYNC in offload messages. */ + spi_engine->offload_requires_sync = ADI_AXI_PCORE_VER_MINOR(version) < 5; + writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET); writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); From b2d25905366b4e6791f60e6bc76a636d1b88e6f8 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Apr 2025 09:59:49 +0100 Subject: [PATCH 34/90] dt-bindings: memory: Document RZ/G3E support Document support for the Expanded Serial Peripheral Interface (xSPI) Controller in the Renesas RZ/G3E (R9A09G047) SoC. Reviewed-by: Rob Herring (Arm) Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250424090000.136804-2-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- .../renesas,rzg3e-xspi.yaml | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml new file mode 100644 index 000000000000..2bfe63ec62dc --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rzg3e-xspi.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/renesas,rzg3e-xspi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas Expanded Serial Peripheral Interface (xSPI) + +maintainers: + - Biju Das + +description: | + Renesas xSPI allows a SPI flash connected to the SoC to be accessed via + the memory-mapping or the manual command mode. + + The flash chip itself should be represented by a subnode of the XSPI node. + The flash interface is selected based on the "compatible" property of this + subnode: + - "jedec,spi-nor"; + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + const: renesas,r9a09g047-xspi # RZ/G3E + + reg: + items: + - description: xSPI registers + - description: direct mapping area + + reg-names: + items: + - const: regs + - const: dirmap + + interrupts: + items: + - description: Interrupt pulse signal by factors excluding errors + - description: Interrupt pulse signal by error factors + + interrupt-names: + items: + - const: pulse + - const: err_pulse + + clocks: + items: + - description: AHB clock + - description: AXI clock + - description: SPI clock + - description: Double speed SPI clock + + clock-names: + items: + - const: ahb + - const: axi + - const: spi + - const: spix2 + + power-domains: + maxItems: 1 + + resets: + items: + - description: Hardware reset + - description: AXI reset + + reset-names: + items: + - const: hresetn + - const: aresetn + + renesas,xspi-cs-addr-sys: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Phandle to the system controller (sys) that allows to configure + xSPI CS0 and CS1 addresses. + +patternProperties: + "flash@[0-9a-f]+$": + type: object + additionalProperties: true + + properties: + compatible: + contains: + const: jedec,spi-nor + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - clocks + - clock-names + - power-domains + - resets + - reset-names + - '#address-cells' + - '#size-cells' + +unevaluatedProperties: false + +examples: + - | + #include + #include + + spi@11030000 { + compatible = "renesas,r9a09g047-xspi"; + reg = <0x11030000 0x10000>, <0x20000000 0x10000000>; + reg-names = "regs", "dirmap"; + interrupts = , + ; + interrupt-names = "pulse", "err_pulse"; + clocks = <&cpg CPG_MOD 0x9f>, <&cpg CPG_MOD 0xa0>, + <&cpg CPG_CORE 9>, <&cpg CPG_MOD 0xa1>; + clock-names = "ahb", "axi", "spi", "spix2"; + power-domains = <&cpg>; + resets = <&cpg 0xa3>, <&cpg 0xa4>; + reset-names = "hresetn", "aresetn"; + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <40000000>; + spi-tx-bus-width = <1>; + spi-rx-bus-width = <1>; + }; + }; From 228e72bf943687d04bf06a98722ab6cf9d4b8ba6 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Apr 2025 09:59:50 +0100 Subject: [PATCH 35/90] memory: renesas-rpc-if: Move rpc-if reg definitions Move rpc-if reg definitions to a header file for the preparation of adding support for RZ/G3E XSPI that has different register definitions. Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250424090000.136804-3-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if-regs.h | 147 +++++++++++++++++++++++++++ drivers/memory/renesas-rpc-if.c | 134 +----------------------- 2 files changed, 148 insertions(+), 133 deletions(-) create mode 100644 drivers/memory/renesas-rpc-if-regs.h diff --git a/drivers/memory/renesas-rpc-if-regs.h b/drivers/memory/renesas-rpc-if-regs.h new file mode 100644 index 000000000000..e6b33f7c40a8 --- /dev/null +++ b/drivers/memory/renesas-rpc-if-regs.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * R-Car RPC Interface Registers Definitions + * + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#ifndef __RENESAS_RPC_IF_REGS_H__ +#define __RENESAS_RPC_IF_REGS_H__ + +#include + +#define RPCIF_CMNCR 0x0000 /* R/W */ +#define RPCIF_CMNCR_MD BIT(31) +#define RPCIF_CMNCR_MOIIO3(val) (((val) & 0x3) << 22) +#define RPCIF_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) +#define RPCIF_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) +#define RPCIF_CMNCR_MOIIO0(val) (((val) & 0x3) << 16) +#define RPCIF_CMNCR_MOIIO(val) (RPCIF_CMNCR_MOIIO0(val) | RPCIF_CMNCR_MOIIO1(val) | \ + RPCIF_CMNCR_MOIIO2(val) | RPCIF_CMNCR_MOIIO3(val)) +#define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* documented for RZ/G2L */ +#define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* documented for RZ/G2L */ +#define RPCIF_CMNCR_IO0FV(val) (((val) & 0x3) << 8) +#define RPCIF_CMNCR_IOFV(val) (RPCIF_CMNCR_IO0FV(val) | RPCIF_CMNCR_IO2FV(val) | \ + RPCIF_CMNCR_IO3FV(val)) +#define RPCIF_CMNCR_BSZ(val) (((val) & 0x3) << 0) + +#define RPCIF_SSLDR 0x0004 /* R/W */ +#define RPCIF_SSLDR_SPNDL(d) (((d) & 0x7) << 16) +#define RPCIF_SSLDR_SLNDL(d) (((d) & 0x7) << 8) +#define RPCIF_SSLDR_SCKDL(d) (((d) & 0x7) << 0) + +#define RPCIF_DRCR 0x000C /* R/W */ +#define RPCIF_DRCR_SSLN BIT(24) +#define RPCIF_DRCR_RBURST(v) ((((v) - 1) & 0x1F) << 16) +#define RPCIF_DRCR_RCF BIT(9) +#define RPCIF_DRCR_RBE BIT(8) +#define RPCIF_DRCR_SSLE BIT(0) + +#define RPCIF_DRCMR 0x0010 /* R/W */ +#define RPCIF_DRCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPCIF_DRCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPCIF_DREAR 0x0014 /* R/W */ +#define RPCIF_DREAR_EAV(c) (((c) & 0xF) << 16) +#define RPCIF_DREAR_EAC(c) (((c) & 0x7) << 0) + +#define RPCIF_DROPR 0x0018 /* R/W */ + +#define RPCIF_DRENR 0x001C /* R/W */ +#define RPCIF_DRENR_CDB(o) (((u32)((o) & 0x3)) << 30) +#define RPCIF_DRENR_OCDB(o) (((o) & 0x3) << 28) +#define RPCIF_DRENR_ADB(o) (((o) & 0x3) << 24) +#define RPCIF_DRENR_OPDB(o) (((o) & 0x3) << 20) +#define RPCIF_DRENR_DRDB(o) (((o) & 0x3) << 16) +#define RPCIF_DRENR_DME BIT(15) +#define RPCIF_DRENR_CDE BIT(14) +#define RPCIF_DRENR_OCDE BIT(12) +#define RPCIF_DRENR_ADE(v) (((v) & 0xF) << 8) +#define RPCIF_DRENR_OPDE(v) (((v) & 0xF) << 4) + +#define RPCIF_SMCR 0x0020 /* R/W */ +#define RPCIF_SMCR_SSLKP BIT(8) +#define RPCIF_SMCR_SPIRE BIT(2) +#define RPCIF_SMCR_SPIWE BIT(1) +#define RPCIF_SMCR_SPIE BIT(0) + +#define RPCIF_SMCMR 0x0024 /* R/W */ +#define RPCIF_SMCMR_CMD(c) (((c) & 0xFF) << 16) +#define RPCIF_SMCMR_OCMD(c) (((c) & 0xFF) << 0) + +#define RPCIF_SMADR 0x0028 /* R/W */ + +#define RPCIF_SMOPR 0x002C /* R/W */ +#define RPCIF_SMOPR_OPD3(o) (((o) & 0xFF) << 24) +#define RPCIF_SMOPR_OPD2(o) (((o) & 0xFF) << 16) +#define RPCIF_SMOPR_OPD1(o) (((o) & 0xFF) << 8) +#define RPCIF_SMOPR_OPD0(o) (((o) & 0xFF) << 0) + +#define RPCIF_SMENR 0x0030 /* R/W */ +#define RPCIF_SMENR_CDB(o) (((o) & 0x3) << 30) +#define RPCIF_SMENR_OCDB(o) (((o) & 0x3) << 28) +#define RPCIF_SMENR_ADB(o) (((o) & 0x3) << 24) +#define RPCIF_SMENR_OPDB(o) (((o) & 0x3) << 20) +#define RPCIF_SMENR_SPIDB(o) (((o) & 0x3) << 16) +#define RPCIF_SMENR_DME BIT(15) +#define RPCIF_SMENR_CDE BIT(14) +#define RPCIF_SMENR_OCDE BIT(12) +#define RPCIF_SMENR_ADE(v) (((v) & 0xF) << 8) +#define RPCIF_SMENR_OPDE(v) (((v) & 0xF) << 4) +#define RPCIF_SMENR_SPIDE(v) (((v) & 0xF) << 0) + +#define RPCIF_SMRDR0 0x0038 /* R */ +#define RPCIF_SMRDR1 0x003C /* R */ +#define RPCIF_SMWDR0 0x0040 /* W */ +#define RPCIF_SMWDR1 0x0044 /* W */ + +#define RPCIF_CMNSR 0x0048 /* R */ +#define RPCIF_CMNSR_SSLF BIT(1) +#define RPCIF_CMNSR_TEND BIT(0) + +#define RPCIF_DRDMCR 0x0058 /* R/W */ +#define RPCIF_DMDMCR_DMCYC(v) ((((v) - 1) & 0x1F) << 0) + +#define RPCIF_DRDRENR 0x005C /* R/W */ +#define RPCIF_DRDRENR_HYPE(v) (((v) & 0x7) << 12) +#define RPCIF_DRDRENR_ADDRE BIT(8) +#define RPCIF_DRDRENR_OPDRE BIT(4) +#define RPCIF_DRDRENR_DRDRE BIT(0) + +#define RPCIF_SMDMCR 0x0060 /* R/W */ +#define RPCIF_SMDMCR_DMCYC(v) ((((v) - 1) & 0x1F) << 0) + +#define RPCIF_SMDRENR 0x0064 /* R/W */ +#define RPCIF_SMDRENR_HYPE(v) (((v) & 0x7) << 12) +#define RPCIF_SMDRENR_ADDRE BIT(8) +#define RPCIF_SMDRENR_OPDRE BIT(4) +#define RPCIF_SMDRENR_SPIDRE BIT(0) + +#define RPCIF_PHYADD 0x0070 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */ +#define RPCIF_PHYWR 0x0074 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */ + +#define RPCIF_PHYCNT 0x007C /* R/W */ +#define RPCIF_PHYCNT_CAL BIT(31) +#define RPCIF_PHYCNT_OCTA(v) (((v) & 0x3) << 22) +#define RPCIF_PHYCNT_EXDS BIT(21) +#define RPCIF_PHYCNT_OCT BIT(20) +#define RPCIF_PHYCNT_DDRCAL BIT(19) +#define RPCIF_PHYCNT_HS BIT(18) +#define RPCIF_PHYCNT_CKSEL(v) (((v) & 0x3) << 16) /* valid only for RZ/G2L */ +#define RPCIF_PHYCNT_STRTIM(v) (((v) & 0x7) << 15 | ((v) & 0x8) << 24) /* valid for R-Car and RZ/G2{E,H,M,N} */ + +#define RPCIF_PHYCNT_WBUF2 BIT(4) +#define RPCIF_PHYCNT_WBUF BIT(2) +#define RPCIF_PHYCNT_PHYMEM(v) (((v) & 0x3) << 0) +#define RPCIF_PHYCNT_PHYMEM_MASK GENMASK(1, 0) + +#define RPCIF_PHYOFFSET1 0x0080 /* R/W */ +#define RPCIF_PHYOFFSET1_DDRTMG(v) (((v) & 0x3) << 28) + +#define RPCIF_PHYOFFSET2 0x0084 /* R/W */ +#define RPCIF_PHYOFFSET2_OCTTMG(v) (((v) & 0x7) << 8) + +#define RPCIF_PHYINT 0x0088 /* R/W */ +#define RPCIF_PHYINT_WPVAL BIT(1) + +#endif /* __RENESAS_RPC_IF_REGS_H__ */ diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 4a59bf27eace..64618a2d09f7 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -18,139 +18,7 @@ #include -#define RPCIF_CMNCR 0x0000 /* R/W */ -#define RPCIF_CMNCR_MD BIT(31) -#define RPCIF_CMNCR_MOIIO3(val) (((val) & 0x3) << 22) -#define RPCIF_CMNCR_MOIIO2(val) (((val) & 0x3) << 20) -#define RPCIF_CMNCR_MOIIO1(val) (((val) & 0x3) << 18) -#define RPCIF_CMNCR_MOIIO0(val) (((val) & 0x3) << 16) -#define RPCIF_CMNCR_MOIIO(val) (RPCIF_CMNCR_MOIIO0(val) | RPCIF_CMNCR_MOIIO1(val) | \ - RPCIF_CMNCR_MOIIO2(val) | RPCIF_CMNCR_MOIIO3(val)) -#define RPCIF_CMNCR_IO3FV(val) (((val) & 0x3) << 14) /* documented for RZ/G2L */ -#define RPCIF_CMNCR_IO2FV(val) (((val) & 0x3) << 12) /* documented for RZ/G2L */ -#define RPCIF_CMNCR_IO0FV(val) (((val) & 0x3) << 8) -#define RPCIF_CMNCR_IOFV(val) (RPCIF_CMNCR_IO0FV(val) | RPCIF_CMNCR_IO2FV(val) | \ - RPCIF_CMNCR_IO3FV(val)) -#define RPCIF_CMNCR_BSZ(val) (((val) & 0x3) << 0) - -#define RPCIF_SSLDR 0x0004 /* R/W */ -#define RPCIF_SSLDR_SPNDL(d) (((d) & 0x7) << 16) -#define RPCIF_SSLDR_SLNDL(d) (((d) & 0x7) << 8) -#define RPCIF_SSLDR_SCKDL(d) (((d) & 0x7) << 0) - -#define RPCIF_DRCR 0x000C /* R/W */ -#define RPCIF_DRCR_SSLN BIT(24) -#define RPCIF_DRCR_RBURST(v) ((((v) - 1) & 0x1F) << 16) -#define RPCIF_DRCR_RCF BIT(9) -#define RPCIF_DRCR_RBE BIT(8) -#define RPCIF_DRCR_SSLE BIT(0) - -#define RPCIF_DRCMR 0x0010 /* R/W */ -#define RPCIF_DRCMR_CMD(c) (((c) & 0xFF) << 16) -#define RPCIF_DRCMR_OCMD(c) (((c) & 0xFF) << 0) - -#define RPCIF_DREAR 0x0014 /* R/W */ -#define RPCIF_DREAR_EAV(c) (((c) & 0xF) << 16) -#define RPCIF_DREAR_EAC(c) (((c) & 0x7) << 0) - -#define RPCIF_DROPR 0x0018 /* R/W */ - -#define RPCIF_DRENR 0x001C /* R/W */ -#define RPCIF_DRENR_CDB(o) (((u32)((o) & 0x3)) << 30) -#define RPCIF_DRENR_OCDB(o) (((o) & 0x3) << 28) -#define RPCIF_DRENR_ADB(o) (((o) & 0x3) << 24) -#define RPCIF_DRENR_OPDB(o) (((o) & 0x3) << 20) -#define RPCIF_DRENR_DRDB(o) (((o) & 0x3) << 16) -#define RPCIF_DRENR_DME BIT(15) -#define RPCIF_DRENR_CDE BIT(14) -#define RPCIF_DRENR_OCDE BIT(12) -#define RPCIF_DRENR_ADE(v) (((v) & 0xF) << 8) -#define RPCIF_DRENR_OPDE(v) (((v) & 0xF) << 4) - -#define RPCIF_SMCR 0x0020 /* R/W */ -#define RPCIF_SMCR_SSLKP BIT(8) -#define RPCIF_SMCR_SPIRE BIT(2) -#define RPCIF_SMCR_SPIWE BIT(1) -#define RPCIF_SMCR_SPIE BIT(0) - -#define RPCIF_SMCMR 0x0024 /* R/W */ -#define RPCIF_SMCMR_CMD(c) (((c) & 0xFF) << 16) -#define RPCIF_SMCMR_OCMD(c) (((c) & 0xFF) << 0) - -#define RPCIF_SMADR 0x0028 /* R/W */ - -#define RPCIF_SMOPR 0x002C /* R/W */ -#define RPCIF_SMOPR_OPD3(o) (((o) & 0xFF) << 24) -#define RPCIF_SMOPR_OPD2(o) (((o) & 0xFF) << 16) -#define RPCIF_SMOPR_OPD1(o) (((o) & 0xFF) << 8) -#define RPCIF_SMOPR_OPD0(o) (((o) & 0xFF) << 0) - -#define RPCIF_SMENR 0x0030 /* R/W */ -#define RPCIF_SMENR_CDB(o) (((o) & 0x3) << 30) -#define RPCIF_SMENR_OCDB(o) (((o) & 0x3) << 28) -#define RPCIF_SMENR_ADB(o) (((o) & 0x3) << 24) -#define RPCIF_SMENR_OPDB(o) (((o) & 0x3) << 20) -#define RPCIF_SMENR_SPIDB(o) (((o) & 0x3) << 16) -#define RPCIF_SMENR_DME BIT(15) -#define RPCIF_SMENR_CDE BIT(14) -#define RPCIF_SMENR_OCDE BIT(12) -#define RPCIF_SMENR_ADE(v) (((v) & 0xF) << 8) -#define RPCIF_SMENR_OPDE(v) (((v) & 0xF) << 4) -#define RPCIF_SMENR_SPIDE(v) (((v) & 0xF) << 0) - -#define RPCIF_SMRDR0 0x0038 /* R */ -#define RPCIF_SMRDR1 0x003C /* R */ -#define RPCIF_SMWDR0 0x0040 /* W */ -#define RPCIF_SMWDR1 0x0044 /* W */ - -#define RPCIF_CMNSR 0x0048 /* R */ -#define RPCIF_CMNSR_SSLF BIT(1) -#define RPCIF_CMNSR_TEND BIT(0) - -#define RPCIF_DRDMCR 0x0058 /* R/W */ -#define RPCIF_DMDMCR_DMCYC(v) ((((v) - 1) & 0x1F) << 0) - -#define RPCIF_DRDRENR 0x005C /* R/W */ -#define RPCIF_DRDRENR_HYPE(v) (((v) & 0x7) << 12) -#define RPCIF_DRDRENR_ADDRE BIT(8) -#define RPCIF_DRDRENR_OPDRE BIT(4) -#define RPCIF_DRDRENR_DRDRE BIT(0) - -#define RPCIF_SMDMCR 0x0060 /* R/W */ -#define RPCIF_SMDMCR_DMCYC(v) ((((v) - 1) & 0x1F) << 0) - -#define RPCIF_SMDRENR 0x0064 /* R/W */ -#define RPCIF_SMDRENR_HYPE(v) (((v) & 0x7) << 12) -#define RPCIF_SMDRENR_ADDRE BIT(8) -#define RPCIF_SMDRENR_OPDRE BIT(4) -#define RPCIF_SMDRENR_SPIDRE BIT(0) - -#define RPCIF_PHYADD 0x0070 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */ -#define RPCIF_PHYWR 0x0074 /* R/W available on R-Car E3/D3/V3M and RZ/G2{E,L} */ - -#define RPCIF_PHYCNT 0x007C /* R/W */ -#define RPCIF_PHYCNT_CAL BIT(31) -#define RPCIF_PHYCNT_OCTA(v) (((v) & 0x3) << 22) -#define RPCIF_PHYCNT_EXDS BIT(21) -#define RPCIF_PHYCNT_OCT BIT(20) -#define RPCIF_PHYCNT_DDRCAL BIT(19) -#define RPCIF_PHYCNT_HS BIT(18) -#define RPCIF_PHYCNT_CKSEL(v) (((v) & 0x3) << 16) /* valid only for RZ/G2L */ -#define RPCIF_PHYCNT_STRTIM(v) (((v) & 0x7) << 15 | ((v) & 0x8) << 24) /* valid for R-Car and RZ/G2{E,H,M,N} */ - -#define RPCIF_PHYCNT_WBUF2 BIT(4) -#define RPCIF_PHYCNT_WBUF BIT(2) -#define RPCIF_PHYCNT_PHYMEM(v) (((v) & 0x3) << 0) -#define RPCIF_PHYCNT_PHYMEM_MASK GENMASK(1, 0) - -#define RPCIF_PHYOFFSET1 0x0080 /* R/W */ -#define RPCIF_PHYOFFSET1_DDRTMG(v) (((v) & 0x3) << 28) - -#define RPCIF_PHYOFFSET2 0x0084 /* R/W */ -#define RPCIF_PHYOFFSET2_OCTTMG(v) (((v) & 0x7) << 8) - -#define RPCIF_PHYINT 0x0088 /* R/W */ -#define RPCIF_PHYINT_WPVAL BIT(1) +#include "renesas-rpc-if-regs.h" static const struct regmap_range rpcif_volatile_ranges[] = { regmap_reg_range(RPCIF_SMRDR0, RPCIF_SMRDR1), From c66fce831d0669dc809f90bcefd80ae979e35431 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Apr 2025 09:59:51 +0100 Subject: [PATCH 36/90] memory: renesas-rpc-if: Use devm_reset_control_array_get_exclusive() Replace devm_*_get_exclusive()->devm_*_array_get_exclusive() to support existing SoCs along with RZ/G3E as RZ/G3E has 2 resets. Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250424090000.136804-4-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 64618a2d09f7..9f3ac5cb9ff4 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -603,7 +603,7 @@ static int rpcif_probe(struct platform_device *pdev) rpc->size = resource_size(res); rpc->info = of_device_get_match_data(dev); - rpc->rstc = devm_reset_control_get_exclusive(dev, NULL); + rpc->rstc = devm_reset_control_array_get_exclusive(dev); if (IS_ERR(rpc->rstc)) return PTR_ERR(rpc->rstc); From 198158a8f64cf150325f68044274e21ab0aa69c8 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Apr 2025 09:59:52 +0100 Subject: [PATCH 37/90] memory: renesas-rpc-if: Add regmap to struct rpcif_info The RZ/G3E XSPI has different regmap compared to RPC-IF. Add regmap to struct rpcif_info in order to support RZ/G3E XSPI. Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250424090000.136804-5-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 9f3ac5cb9ff4..56b2e944beca 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -32,6 +32,7 @@ static const struct regmap_access_table rpcif_volatile_table = { }; struct rpcif_info { + const struct regmap_config *regmap_config; enum rpcif_type type; u8 strtim; }; @@ -588,8 +589,8 @@ static int rpcif_probe(struct platform_device *pdev) rpc->base = devm_platform_ioremap_resource_byname(pdev, "regs"); if (IS_ERR(rpc->base)) return PTR_ERR(rpc->base); - - rpc->regmap = devm_regmap_init(dev, NULL, rpc, &rpcif_regmap_config); + rpc->info = of_device_get_match_data(dev); + rpc->regmap = devm_regmap_init(dev, NULL, rpc, rpc->info->regmap_config); if (IS_ERR(rpc->regmap)) { dev_err(dev, "failed to init regmap for rpcif, error %ld\n", PTR_ERR(rpc->regmap)); @@ -602,7 +603,6 @@ static int rpcif_probe(struct platform_device *pdev) return PTR_ERR(rpc->dirmap); rpc->size = resource_size(res); - rpc->info = of_device_get_match_data(dev); rpc->rstc = devm_reset_control_array_get_exclusive(dev); if (IS_ERR(rpc->rstc)) return PTR_ERR(rpc->rstc); @@ -633,21 +633,25 @@ static void rpcif_remove(struct platform_device *pdev) } static const struct rpcif_info rpcif_info_r8a7796 = { + .regmap_config = &rpcif_regmap_config, .type = RPCIF_RCAR_GEN3, .strtim = 6, }; static const struct rpcif_info rpcif_info_gen3 = { + .regmap_config = &rpcif_regmap_config, .type = RPCIF_RCAR_GEN3, .strtim = 7, }; static const struct rpcif_info rpcif_info_rz_g2l = { + .regmap_config = &rpcif_regmap_config, .type = RPCIF_RZ_G2L, .strtim = 7, }; static const struct rpcif_info rpcif_info_gen4 = { + .regmap_config = &rpcif_regmap_config, .type = RPCIF_RCAR_GEN4, .strtim = 15, }; From e1c200a4c7f0e8b842a9a5b7f38fa7036c0df417 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Apr 2025 09:59:53 +0100 Subject: [PATCH 38/90] memory: renesas-rpc-if: Add wrapper functions Even though XSPI and RPCIF has different register layout, reuse the code by adding wrapper functions to support both XSPI and RPC-IF. While at it, replace error check for pm_runtime_resume_and_get(). Reviewed-by: Geert Uytterhoeven Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250424090000.136804-6-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 96 ++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 56b2e944beca..eea38f448e03 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -174,16 +174,11 @@ static void rpcif_rzg2l_timing_adjust_sdr(struct rpcif_priv *rpc) regmap_write(rpc->regmap, RPCIF_PHYADD, 0x80000032); } -int rpcif_hw_init(struct device *dev, bool hyperflash) +static int rpcif_hw_init_impl(struct rpcif_priv *rpc, bool hyperflash) { - struct rpcif_priv *rpc = dev_get_drvdata(dev); u32 dummy; int ret; - ret = pm_runtime_resume_and_get(dev); - if (ret) - return ret; - if (rpc->info->type == RPCIF_RZ_G2L) { ret = reset_control_reset(rpc->rstc); if (ret) @@ -231,12 +226,26 @@ int rpcif_hw_init(struct device *dev, bool hyperflash) regmap_write(rpc->regmap, RPCIF_SSLDR, RPCIF_SSLDR_SPNDL(7) | RPCIF_SSLDR_SLNDL(7) | RPCIF_SSLDR_SCKDL(7)); - pm_runtime_put(dev); - rpc->bus_size = hyperflash ? 2 : 1; return 0; } + +int rpcif_hw_init(struct device *dev, bool hyperflash) +{ + struct rpcif_priv *rpc = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = rpcif_hw_init_impl(rpc, hyperflash); + + pm_runtime_put(dev); + + return ret; +} EXPORT_SYMBOL(rpcif_hw_init); static int wait_msg_xfer_end(struct rpcif_priv *rpc) @@ -261,11 +270,9 @@ static u8 rpcif_bit_size(u8 buswidth) return buswidth > 4 ? 2 : ilog2(buswidth); } -void rpcif_prepare(struct device *dev, const struct rpcif_op *op, u64 *offs, - size_t *len) +static void rpcif_prepare_impl(struct rpcif_priv *rpc, const struct rpcif_op *op, + u64 *offs, size_t *len) { - struct rpcif_priv *rpc = dev_get_drvdata(dev); - rpc->smcr = 0; rpc->smadr = 0; rpc->enable = 0; @@ -346,18 +353,21 @@ void rpcif_prepare(struct device *dev, const struct rpcif_op *op, u64 *offs, rpc->enable |= RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth)); } } -EXPORT_SYMBOL(rpcif_prepare); -int rpcif_manual_xfer(struct device *dev) +void rpcif_prepare(struct device *dev, const struct rpcif_op *op, u64 *offs, + size_t *len) { struct rpcif_priv *rpc = dev_get_drvdata(dev); + + rpcif_prepare_impl(rpc, op, offs, len); +} +EXPORT_SYMBOL(rpcif_prepare); + +static int rpcif_manual_xfer_impl(struct rpcif_priv *rpc) +{ u32 smenr, smcr, pos = 0, max = rpc->bus_size == 2 ? 8 : 4; int ret = 0; - ret = pm_runtime_resume_and_get(dev); - if (ret < 0) - return ret; - regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, RPCIF_PHYCNT_CAL, RPCIF_PHYCNT_CAL); regmap_update_bits(rpc->regmap, RPCIF_CMNCR, @@ -465,15 +475,29 @@ int rpcif_manual_xfer(struct device *dev) goto err_out; } -exit: - pm_runtime_put(dev); return ret; err_out: if (reset_control_reset(rpc->rstc)) - dev_err(dev, "Failed to reset HW\n"); - rpcif_hw_init(dev, rpc->bus_size == 2); - goto exit; + dev_err(rpc->dev, "Failed to reset HW\n"); + rpcif_hw_init_impl(rpc, rpc->bus_size == 2); + return ret; +} + +int rpcif_manual_xfer(struct device *dev) +{ + struct rpcif_priv *rpc = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + ret = rpcif_manual_xfer_impl(rpc); + + pm_runtime_put(dev); + + return ret; } EXPORT_SYMBOL(rpcif_manual_xfer); @@ -519,20 +543,15 @@ static void memcpy_fromio_readw(void *to, } } -ssize_t rpcif_dirmap_read(struct device *dev, u64 offs, size_t len, void *buf) +static size_t rpcif_dirmap_read_impl(struct rpcif_priv *rpc, u64 offs, + size_t len, void *buf) { - struct rpcif_priv *rpc = dev_get_drvdata(dev); loff_t from = offs & (rpc->size - 1); size_t size = rpc->size - from; - int ret; if (len > size) len = size; - ret = pm_runtime_resume_and_get(dev); - if (ret < 0) - return ret; - regmap_update_bits(rpc->regmap, RPCIF_CMNCR, RPCIF_CMNCR_MD, 0); regmap_write(rpc->regmap, RPCIF_DRCR, 0); regmap_write(rpc->regmap, RPCIF_DRCMR, rpc->command); @@ -549,9 +568,24 @@ ssize_t rpcif_dirmap_read(struct device *dev, u64 offs, size_t len, void *buf) else memcpy_fromio(buf, rpc->dirmap + from, len); + return len; +} + +ssize_t rpcif_dirmap_read(struct device *dev, u64 offs, size_t len, void *buf) +{ + struct rpcif_priv *rpc = dev_get_drvdata(dev); + size_t read; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + read = rpcif_dirmap_read_impl(rpc, offs, len, buf); + pm_runtime_put(dev); - return len; + return read; } EXPORT_SYMBOL(rpcif_dirmap_read); From 687cac9559d8e9277830bdfb68b57d7403695b1d Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Apr 2025 09:59:54 +0100 Subject: [PATCH 39/90] memory: renesas-rpc-if: Add RZ/G3E xSPI support Add support for RZ/G3E xSPI. Compared to RPC-IF, it can support writes on memory-mapped area. Introduce struct rpcif_impl for holding the function pointers and data to handle the differences between xspi and rpc-if interface. Signed-off-by: Biju Das Link: https://lore.kernel.org/r/20250424090000.136804-7-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 440 +++++++++++++++++++++++++- drivers/memory/renesas-xspi-if-regs.h | 105 ++++++ include/memory/renesas-rpc-if.h | 4 + 3 files changed, 542 insertions(+), 7 deletions(-) create mode 100644 drivers/memory/renesas-xspi-if-regs.h diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index eea38f448e03..4b2e903f2b0d 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -19,6 +19,7 @@ #include #include "renesas-rpc-if-regs.h" +#include "renesas-xspi-if-regs.h" static const struct regmap_range rpcif_volatile_ranges[] = { regmap_reg_range(RPCIF_SMRDR0, RPCIF_SMRDR1), @@ -31,8 +32,31 @@ static const struct regmap_access_table rpcif_volatile_table = { .n_yes_ranges = ARRAY_SIZE(rpcif_volatile_ranges), }; +static const struct regmap_range xspi_volatile_ranges[] = { + regmap_reg_range(XSPI_CDD0BUF0, XSPI_CDD0BUF0), +}; + +static const struct regmap_access_table xspi_volatile_table = { + .yes_ranges = xspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(xspi_volatile_ranges), +}; + +struct rpcif_priv; + +struct rpcif_impl { + int (*hw_init)(struct rpcif_priv *rpc, bool hyperflash); + void (*prepare)(struct rpcif_priv *rpc, const struct rpcif_op *op, + u64 *offs, size_t *len); + int (*manual_xfer)(struct rpcif_priv *rpc); + size_t (*dirmap_read)(struct rpcif_priv *rpc, u64 offs, size_t len, + void *buf); + u32 status_reg; + u32 status_mask; +}; + struct rpcif_info { const struct regmap_config *regmap_config; + const struct rpcif_impl *impl; enum rpcif_type type; u8 strtim; }; @@ -49,6 +73,8 @@ struct rpcif_priv { enum rpcif_data_dir dir; u8 bus_size; u8 xfer_size; + u8 addr_nbytes; /* Specified for xSPI */ + u32 proto; /* Specified for xSPI */ void *buffer; u32 xferlen; u32 smcr; @@ -149,6 +175,33 @@ static const struct regmap_config rpcif_regmap_config = { .volatile_table = &rpcif_volatile_table, }; +static int xspi_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct rpcif_priv *xspi = context; + + *val = readl(xspi->base + reg); + return 0; +} + +static int xspi_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct rpcif_priv *xspi = context; + + writel(val, xspi->base + reg); + return 0; +} + +static const struct regmap_config xspi_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_read = xspi_reg_read, + .reg_write = xspi_reg_write, + .fast_io = true, + .max_register = XSPI_INTE, + .volatile_table = &xspi_volatile_table, +}; + int rpcif_sw_init(struct rpcif *rpcif, struct device *dev) { struct rpcif_priv *rpc = dev_get_drvdata(dev); @@ -156,6 +209,7 @@ int rpcif_sw_init(struct rpcif *rpcif, struct device *dev) rpcif->dev = dev; rpcif->dirmap = rpc->dirmap; rpcif->size = rpc->size; + rpcif->xspi = rpc->info->type == XSPI_RZ_G3E; return 0; } EXPORT_SYMBOL(rpcif_sw_init); @@ -231,6 +285,33 @@ static int rpcif_hw_init_impl(struct rpcif_priv *rpc, bool hyperflash) return 0; } +static int xspi_hw_init_impl(struct rpcif_priv *xspi, bool hyperflash) +{ + int ret; + + ret = reset_control_reset(xspi->rstc); + if (ret) + return ret; + + regmap_write(xspi->regmap, XSPI_WRAPCFG, 0x0); + + regmap_update_bits(xspi->regmap, XSPI_LIOCFGCS0, + XSPI_LIOCFG_PRTMD(0x3ff) | XSPI_LIOCFG_CSMIN(0xf) | + XSPI_LIOCFG_CSASTEX | XSPI_LIOCFG_CSNEGEX, + XSPI_LIOCFG_PRTMD(0) | XSPI_LIOCFG_CSMIN(0) | + XSPI_LIOCFG_CSASTEX | XSPI_LIOCFG_CSNEGEX); + + regmap_update_bits(xspi->regmap, XSPI_CCCTL0CS0, XSPI_CCCTL0_CAEN, 0); + + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, + XSPI_CDCTL0_TRREQ | XSPI_CDCTL0_CSSEL, 0); + + regmap_update_bits(xspi->regmap, XSPI_INTE, XSPI_INTE_CMDCMPE, + XSPI_INTE_CMDCMPE); + + return 0; +} + int rpcif_hw_init(struct device *dev, bool hyperflash) { struct rpcif_priv *rpc = dev_get_drvdata(dev); @@ -240,7 +321,7 @@ int rpcif_hw_init(struct device *dev, bool hyperflash) if (ret) return ret; - ret = rpcif_hw_init_impl(rpc, hyperflash); + ret = rpc->info->impl->hw_init(rpc, hyperflash); pm_runtime_put(dev); @@ -252,9 +333,9 @@ static int wait_msg_xfer_end(struct rpcif_priv *rpc) { u32 sts; - return regmap_read_poll_timeout(rpc->regmap, RPCIF_CMNSR, sts, - sts & RPCIF_CMNSR_TEND, 0, - USEC_PER_SEC); + return regmap_read_poll_timeout(rpc->regmap, rpc->info->impl->status_reg, + sts, sts & rpc->info->impl->status_mask, + 0, USEC_PER_SEC); } static u8 rpcif_bits_set(struct rpcif_priv *rpc, u32 nbytes) @@ -354,12 +435,67 @@ static void rpcif_prepare_impl(struct rpcif_priv *rpc, const struct rpcif_op *op } } +static void xspi_prepare_impl(struct rpcif_priv *xspi, const struct rpcif_op *op, + u64 *offs, size_t *len) +{ + xspi->smadr = 0; + xspi->addr_nbytes = 0; + xspi->command = 0; + xspi->option = 0; + xspi->dummy = 0; + xspi->xferlen = 0; + xspi->proto = 0; + + if (op->cmd.buswidth) + xspi->command = op->cmd.opcode; + + if (op->ocmd.buswidth) + xspi->command = (xspi->command << 8) | op->ocmd.opcode; + + if (op->addr.buswidth) { + xspi->addr_nbytes = op->addr.nbytes; + if (offs && len) + xspi->smadr = *offs; + else + xspi->smadr = op->addr.val; + } + + if (op->dummy.buswidth) + xspi->dummy = op->dummy.ncycles; + + xspi->dir = op->data.dir; + if (op->data.buswidth) { + u32 nbytes; + + xspi->buffer = op->data.buf.in; + + if (offs && len) + nbytes = *len; + else + nbytes = op->data.nbytes; + xspi->xferlen = nbytes; + } + + if (op->cmd.buswidth == 1) { + if (op->addr.buswidth == 2 || op->data.buswidth == 2) + xspi->proto = PROTO_1S_2S_2S; + else if (op->addr.buswidth == 4 || op->data.buswidth == 4) + xspi->proto = PROTO_1S_4S_4S; + } else if (op->cmd.buswidth == 2 && + (op->addr.buswidth == 2 || op->data.buswidth == 2)) { + xspi->proto = PROTO_2S_2S_2S; + } else if (op->cmd.buswidth == 4 && + (op->addr.buswidth == 4 || op->data.buswidth == 4)) { + xspi->proto = PROTO_4S_4S_4S; + } +} + void rpcif_prepare(struct device *dev, const struct rpcif_op *op, u64 *offs, size_t *len) { struct rpcif_priv *rpc = dev_get_drvdata(dev); - rpcif_prepare_impl(rpc, op, offs, len); + rpc->info->impl->prepare(rpc, op, offs, len); } EXPORT_SYMBOL(rpcif_prepare); @@ -484,6 +620,146 @@ err_out: return ret; } +static int xspi_manual_xfer_impl(struct rpcif_priv *xspi) +{ + u32 pos = 0, max = 8; + int ret = 0; + + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, XSPI_CDCTL0_TRNUM(0x3), + XSPI_CDCTL0_TRNUM(0)); + + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, XSPI_CDCTL0_TRREQ, 0); + + regmap_write(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_CMDSIZE(0x1) | XSPI_CDTBUF_CMD_FIELD(xspi->command)); + + regmap_write(xspi->regmap, XSPI_CDABUF0, 0); + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, XSPI_CDTBUF_ADDSIZE(0x7), + XSPI_CDTBUF_ADDSIZE(xspi->addr_nbytes)); + + regmap_write(xspi->regmap, XSPI_CDABUF0, xspi->smadr); + + regmap_update_bits(xspi->regmap, XSPI_LIOCFGCS0, XSPI_LIOCFG_PRTMD(0x3ff), + XSPI_LIOCFG_PRTMD(xspi->proto)); + + switch (xspi->dir) { + case RPCIF_DATA_OUT: + while (pos < xspi->xferlen) { + u32 bytes_left = xspi->xferlen - pos; + u32 nbytes, data[2], *p = data; + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_TRTYPE, XSPI_CDTBUF_TRTYPE); + + nbytes = bytes_left >= max ? max : bytes_left; + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_DATASIZE(0xf), + XSPI_CDTBUF_DATASIZE(nbytes)); + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_ADDSIZE(0x7), + XSPI_CDTBUF_ADDSIZE(xspi->addr_nbytes)); + + memcpy(data, xspi->buffer + pos, nbytes); + + if (nbytes > 4) { + regmap_write(xspi->regmap, XSPI_CDD0BUF0, *p++); + regmap_write(xspi->regmap, XSPI_CDD1BUF0, *p); + } else { + regmap_write(xspi->regmap, XSPI_CDD0BUF0, *p); + } + + regmap_write(xspi->regmap, XSPI_CDABUF0, xspi->smadr + pos); + + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, + XSPI_CDCTL0_TRREQ, XSPI_CDCTL0_TRREQ); + + ret = wait_msg_xfer_end(xspi); + if (ret) + goto err_out; + + regmap_update_bits(xspi->regmap, XSPI_INTC, + XSPI_INTC_CMDCMPC, XSPI_INTC_CMDCMPC); + + pos += nbytes; + } + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, XSPI_CDCTL0_TRREQ, 0); + break; + case RPCIF_DATA_IN: + while (pos < xspi->xferlen) { + u32 bytes_left = xspi->xferlen - pos; + u32 nbytes, data[2], *p = data; + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_TRTYPE, + ~(u32)XSPI_CDTBUF_TRTYPE); + + /* nbytes can be up to 8 bytes */ + nbytes = bytes_left >= max ? max : bytes_left; + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_DATASIZE(0xf), + XSPI_CDTBUF_DATASIZE(nbytes)); + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_ADDSIZE(0x7), + XSPI_CDTBUF_ADDSIZE(xspi->addr_nbytes)); + + if (xspi->addr_nbytes) + regmap_write(xspi->regmap, XSPI_CDABUF0, + xspi->smadr + pos); + + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_LATE(0x1f), + XSPI_CDTBUF_LATE(xspi->dummy)); + + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, + XSPI_CDCTL0_TRREQ, XSPI_CDCTL0_TRREQ); + + ret = wait_msg_xfer_end(xspi); + if (ret) + goto err_out; + + if (nbytes > 4) { + regmap_read(xspi->regmap, XSPI_CDD0BUF0, p++); + regmap_read(xspi->regmap, XSPI_CDD1BUF0, p); + } else { + regmap_read(xspi->regmap, XSPI_CDD0BUF0, p); + } + + memcpy(xspi->buffer + pos, data, nbytes); + + regmap_update_bits(xspi->regmap, XSPI_INTC, + XSPI_INTC_CMDCMPC, XSPI_INTC_CMDCMPC); + + pos += nbytes; + } + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, + XSPI_CDCTL0_TRREQ, 0); + break; + default: + regmap_update_bits(xspi->regmap, XSPI_CDTBUF0, + XSPI_CDTBUF_TRTYPE, XSPI_CDTBUF_TRTYPE); + regmap_update_bits(xspi->regmap, XSPI_CDCTL0, + XSPI_CDCTL0_TRREQ, XSPI_CDCTL0_TRREQ); + + ret = wait_msg_xfer_end(xspi); + if (ret) + goto err_out; + + regmap_update_bits(xspi->regmap, XSPI_INTC, + XSPI_INTC_CMDCMPC, XSPI_INTC_CMDCMPC); + } + + return ret; + +err_out: + xspi_hw_init_impl(xspi, false); + return ret; +} + int rpcif_manual_xfer(struct device *dev) { struct rpcif_priv *rpc = dev_get_drvdata(dev); @@ -493,7 +769,7 @@ int rpcif_manual_xfer(struct device *dev) if (ret) return ret; - ret = rpcif_manual_xfer_impl(rpc); + ret = rpc->info->impl->manual_xfer(rpc); pm_runtime_put(dev); @@ -571,6 +847,42 @@ static size_t rpcif_dirmap_read_impl(struct rpcif_priv *rpc, u64 offs, return len; } +static size_t xspi_dirmap_read_impl(struct rpcif_priv *xspi, u64 offs, + size_t len, void *buf) +{ + loff_t from = offs & (xspi->size - 1); + size_t size = xspi->size - from; + u8 addsize = xspi->addr_nbytes - 1; + + if (len > size) + len = size; + + regmap_update_bits(xspi->regmap, XSPI_CMCFG0CS0, + XSPI_CMCFG0_FFMT(0x3) | XSPI_CMCFG0_ADDSIZE(0x3), + XSPI_CMCFG0_FFMT(0) | XSPI_CMCFG0_ADDSIZE(addsize)); + + regmap_update_bits(xspi->regmap, XSPI_CMCFG1CS0, + XSPI_CMCFG1_RDCMD(0xffff) | XSPI_CMCFG1_RDLATE(0x1f), + XSPI_CMCFG1_RDCMD_UPPER_BYTE(xspi->command) | + XSPI_CMCFG1_RDLATE(xspi->dummy)); + + regmap_update_bits(xspi->regmap, XSPI_BMCTL0, XSPI_BMCTL0_CS0ACC(0xff), + XSPI_BMCTL0_CS0ACC(0x01)); + + regmap_update_bits(xspi->regmap, XSPI_BMCFG, + XSPI_BMCFG_WRMD | XSPI_BMCFG_MWRCOMB | + XSPI_BMCFG_MWRSIZE(0xff) | XSPI_BMCFG_PREEN, + 0 | XSPI_BMCFG_MWRCOMB | XSPI_BMCFG_MWRSIZE(0x0f) | + XSPI_BMCFG_PREEN); + + regmap_update_bits(xspi->regmap, XSPI_LIOCFGCS0, XSPI_LIOCFG_PRTMD(0x3ff), + XSPI_LIOCFG_PRTMD(xspi->proto)); + + memcpy_fromio(buf, xspi->dirmap + from, len); + + return len; +} + ssize_t rpcif_dirmap_read(struct device *dev, u64 offs, size_t len, void *buf) { struct rpcif_priv *rpc = dev_get_drvdata(dev); @@ -581,7 +893,7 @@ ssize_t rpcif_dirmap_read(struct device *dev, u64 offs, size_t len, void *buf) if (ret) return ret; - read = rpcif_dirmap_read_impl(rpc, offs, len, buf); + read = rpc->info->impl->dirmap_read(rpc, offs, len, buf); pm_runtime_put(dev); @@ -589,6 +901,72 @@ ssize_t rpcif_dirmap_read(struct device *dev, u64 offs, size_t len, void *buf) } EXPORT_SYMBOL(rpcif_dirmap_read); +/** + * xspi_dirmap_write - Write data to xspi memory. + * @dev: xspi device + * @offs: offset + * @len: Number of bytes to be written. + * @buf: Buffer holding write data. + * + * This function writes data into xspi memory. + * + * Returns number of bytes written on success, else negative errno. + */ +ssize_t xspi_dirmap_write(struct device *dev, u64 offs, size_t len, const void *buf) +{ + struct rpcif_priv *xspi = dev_get_drvdata(dev); + loff_t from = offs & (xspi->size - 1); + u8 addsize = xspi->addr_nbytes - 1; + size_t size = xspi->size - from; + ssize_t writebytes; + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + + if (len > size) + len = size; + + if (len > MWRSIZE_MAX) + writebytes = MWRSIZE_MAX; + else + writebytes = len; + + regmap_update_bits(xspi->regmap, XSPI_CMCFG0CS0, + XSPI_CMCFG0_FFMT(0x3) | XSPI_CMCFG0_ADDSIZE(0x3), + XSPI_CMCFG0_FFMT(0) | XSPI_CMCFG0_ADDSIZE(addsize)); + + regmap_update_bits(xspi->regmap, XSPI_CMCFG2CS0, + XSPI_CMCFG2_WRCMD_UPPER(0xff) | XSPI_CMCFG2_WRLATE(0x1f), + XSPI_CMCFG2_WRCMD_UPPER(xspi->command) | + XSPI_CMCFG2_WRLATE(xspi->dummy)); + + regmap_update_bits(xspi->regmap, XSPI_BMCTL0, + XSPI_BMCTL0_CS0ACC(0xff), XSPI_BMCTL0_CS0ACC(0x03)); + + regmap_update_bits(xspi->regmap, XSPI_BMCFG, + XSPI_BMCFG_WRMD | XSPI_BMCFG_MWRCOMB | + XSPI_BMCFG_MWRSIZE(0xff) | XSPI_BMCFG_PREEN, + 0 | XSPI_BMCFG_MWRCOMB | XSPI_BMCFG_MWRSIZE(0x0f) | + XSPI_BMCFG_PREEN); + + regmap_update_bits(xspi->regmap, XSPI_LIOCFGCS0, XSPI_LIOCFG_PRTMD(0x3ff), + XSPI_LIOCFG_PRTMD(xspi->proto)); + + memcpy_toio(xspi->dirmap + from, buf, writebytes); + + /* Request to push the pending data */ + if (writebytes < MWRSIZE_MAX) + regmap_update_bits(xspi->regmap, XSPI_BMCTL1, + XSPI_BMCTL1_MWRPUSH, XSPI_BMCTL1_MWRPUSH); + + pm_runtime_put(dev); + + return writebytes; +} +EXPORT_SYMBOL_GPL(xspi_dirmap_write); + static int rpcif_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -641,6 +1019,25 @@ static int rpcif_probe(struct platform_device *pdev) if (IS_ERR(rpc->rstc)) return PTR_ERR(rpc->rstc); + /* + * The enabling/disabling of spi/spix2 clocks at runtime leading to + * flash write failure. So, enable these clocks during probe() and + * disable it in remove(). + */ + if (rpc->info->type == XSPI_RZ_G3E) { + struct clk *spi_clk; + + spi_clk = devm_clk_get_enabled(dev, "spix2"); + if (IS_ERR(spi_clk)) + return dev_err_probe(dev, PTR_ERR(spi_clk), + "cannot get enabled spix2 clk\n"); + + spi_clk = devm_clk_get_enabled(dev, "spi"); + if (IS_ERR(spi_clk)) + return dev_err_probe(dev, PTR_ERR(spi_clk), + "cannot get enabled spi clk\n"); + } + vdev = platform_device_alloc(name, pdev->id); if (!vdev) return -ENOMEM; @@ -666,32 +1063,61 @@ static void rpcif_remove(struct platform_device *pdev) platform_device_unregister(rpc->vdev); } +struct rpcif_impl rpcif_impl = { + .hw_init = rpcif_hw_init_impl, + .prepare = rpcif_prepare_impl, + .manual_xfer = rpcif_manual_xfer_impl, + .dirmap_read = rpcif_dirmap_read_impl, + .status_reg = RPCIF_CMNSR, + .status_mask = RPCIF_CMNSR_TEND, +}; + +struct rpcif_impl xspi_impl = { + .hw_init = xspi_hw_init_impl, + .prepare = xspi_prepare_impl, + .manual_xfer = xspi_manual_xfer_impl, + .dirmap_read = xspi_dirmap_read_impl, + .status_reg = XSPI_INTS, + .status_mask = XSPI_INTS_CMDCMP, +}; + static const struct rpcif_info rpcif_info_r8a7796 = { .regmap_config = &rpcif_regmap_config, + .impl = &rpcif_impl, .type = RPCIF_RCAR_GEN3, .strtim = 6, }; static const struct rpcif_info rpcif_info_gen3 = { .regmap_config = &rpcif_regmap_config, + .impl = &rpcif_impl, .type = RPCIF_RCAR_GEN3, .strtim = 7, }; static const struct rpcif_info rpcif_info_rz_g2l = { .regmap_config = &rpcif_regmap_config, + .impl = &rpcif_impl, .type = RPCIF_RZ_G2L, .strtim = 7, }; static const struct rpcif_info rpcif_info_gen4 = { .regmap_config = &rpcif_regmap_config, + .impl = &rpcif_impl, .type = RPCIF_RCAR_GEN4, .strtim = 15, }; +static const struct rpcif_info xspi_info_r9a09g047 = { + .regmap_config = &xspi_regmap_config, + .impl = &xspi_impl, + .type = XSPI_RZ_G3E, +}; + static const struct of_device_id rpcif_of_match[] = { { .compatible = "renesas,r8a7796-rpc-if", .data = &rpcif_info_r8a7796 }, + { .compatible = "renesas,r9a09g047-xspi", .data = &xspi_info_r9a09g047 }, { .compatible = "renesas,rcar-gen3-rpc-if", .data = &rpcif_info_gen3 }, { .compatible = "renesas,rcar-gen4-rpc-if", .data = &rpcif_info_gen4 }, { .compatible = "renesas,rzg2l-rpc-if", .data = &rpcif_info_rz_g2l }, diff --git a/drivers/memory/renesas-xspi-if-regs.h b/drivers/memory/renesas-xspi-if-regs.h new file mode 100644 index 000000000000..53f801d591f2 --- /dev/null +++ b/drivers/memory/renesas-xspi-if-regs.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RZ xSPI Interface Registers Definitions + * + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#ifndef __RENESAS_XSPI_IF_REGS_H__ +#define __RENESAS_XSPI_IF_REGS_H__ + +#include + +/* xSPI Wrapper Configuration Register */ +#define XSPI_WRAPCFG 0x0000 + +/* xSPI Bridge Configuration Register */ +#define XSPI_BMCFG 0x0008 +#define XSPI_BMCFG_WRMD BIT(0) +#define XSPI_BMCFG_MWRCOMB BIT(7) +#define XSPI_BMCFG_MWRSIZE(val) (((val) & 0xff) << 8) +#define XSPI_BMCFG_PREEN BIT(16) + +/* xSPI Command Map Configuration Register 0 CS0 */ +#define XSPI_CMCFG0CS0 0x0010 +#define XSPI_CMCFG0_FFMT(val) (((val) & 0x03) << 0) +#define XSPI_CMCFG0_ADDSIZE(val) (((val) & 0x03) << 2) + +/* xSPI Command Map Configuration Register 1 CS0 */ +#define XSPI_CMCFG1CS0 0x0014 +#define XSPI_CMCFG1_RDCMD(val) (((val) & 0xffff) << 0) +#define XSPI_CMCFG1_RDCMD_UPPER_BYTE(val) (((val) & 0xff) << 8) +#define XSPI_CMCFG1_RDLATE(val) (((val) & 0x1f) << 16) + +/* xSPI Command Map Configuration Register 2 CS0 */ +#define XSPI_CMCFG2CS0 0x0018 +#define XSPI_CMCFG2_WRCMD(val) (((val) & 0xffff) << 0) +#define XSPI_CMCFG2_WRCMD_UPPER(val) (((val) & 0xff) << 8) +#define XSPI_CMCFG2_WRLATE(val) (((val) & 0x1f) << 16) + +/* xSPI Link I/O Configuration Register CS0 */ +#define XSPI_LIOCFGCS0 0x0050 +#define XSPI_LIOCFG_PRTMD(val) (((val) & 0x3ff) << 0) +#define XSPI_LIOCFG_CSMIN(val) (((val) & 0x0f) << 16) +#define XSPI_LIOCFG_CSASTEX BIT(20) +#define XSPI_LIOCFG_CSNEGEX BIT(21) + +/* xSPI Bridge Map Control Register 0 */ +#define XSPI_BMCTL0 0x0060 +#define XSPI_BMCTL0_CS0ACC(val) (((val) & 0x03) << 0) + +/* xSPI Bridge Map Control Register 1 */ +#define XSPI_BMCTL1 0x0064 +#define XSPI_BMCTL1_MWRPUSH BIT(8) + +/* xSPI Command Manual Control Register 0 */ +#define XSPI_CDCTL0 0x0070 +#define XSPI_CDCTL0_TRREQ BIT(0) +#define XSPI_CDCTL0_CSSEL BIT(3) +#define XSPI_CDCTL0_TRNUM(val) (((val) & 0x03) << 4) + +/* xSPI Command Manual Type Buf */ +#define XSPI_CDTBUF0 0x0080 +#define XSPI_CDTBUF_CMDSIZE(val) (((val) & 0x03) << 0) +#define XSPI_CDTBUF_ADDSIZE(val) (((val) & 0x07) << 2) +#define XSPI_CDTBUF_DATASIZE(val) (((val) & 0x0f) << 5) +#define XSPI_CDTBUF_LATE(val) (((val) & 0x1f) << 9) +#define XSPI_CDTBUF_TRTYPE BIT(15) +#define XSPI_CDTBUF_CMD(val) (((val) & 0xffff) << 16) +#define XSPI_CDTBUF_CMD_FIELD(val) (((val) & 0xff) << 24) + +/* xSPI Command Manual Address Buff */ +#define XSPI_CDABUF0 0x0084 + +/* xSPI Command Manual Data 0 Buf */ +#define XSPI_CDD0BUF0 0x0088 + +/* xSPI Command Manual Data 1 Buf */ +#define XSPI_CDD1BUF0 0x008c + +/* xSPI Command Calibration Control Register 0 CS0 */ +#define XSPI_CCCTL0CS0 0x0130 +#define XSPI_CCCTL0_CAEN BIT(0) + +/* xSPI Interrupt Status Register */ +#define XSPI_INTS 0x0190 +#define XSPI_INTS_CMDCMP BIT(0) + +/* xSPI Interrupt Clear Register */ +#define XSPI_INTC 0x0194 +#define XSPI_INTC_CMDCMPC BIT(0) + +/* xSPI Interrupt Enable Register */ +#define XSPI_INTE 0x0198 +#define XSPI_INTE_CMDCMPE BIT(0) + +/* Maximum data size of MWRSIZE*/ +#define MWRSIZE_MAX 64 + +/* xSPI Protocol mode */ +#define PROTO_1S_2S_2S 0x48 +#define PROTO_2S_2S_2S 0x49 +#define PROTO_1S_4S_4S 0x090 +#define PROTO_4S_4S_4S 0x092 + +#endif /* __RENESAS_XSPI_IF_REGS_H__ */ diff --git a/include/memory/renesas-rpc-if.h b/include/memory/renesas-rpc-if.h index b8fa30fd6b50..53663c4e5ae3 100644 --- a/include/memory/renesas-rpc-if.h +++ b/include/memory/renesas-rpc-if.h @@ -61,12 +61,14 @@ enum rpcif_type { RPCIF_RCAR_GEN3, RPCIF_RCAR_GEN4, RPCIF_RZ_G2L, + XSPI_RZ_G3E, }; struct rpcif { struct device *dev; void __iomem *dirmap; size_t size; + bool xspi; }; int rpcif_sw_init(struct rpcif *rpc, struct device *dev); @@ -75,5 +77,7 @@ void rpcif_prepare(struct device *dev, const struct rpcif_op *op, u64 *offs, size_t *len); int rpcif_manual_xfer(struct device *dev); ssize_t rpcif_dirmap_read(struct device *dev, u64 offs, size_t len, void *buf); +ssize_t xspi_dirmap_write(struct device *dev, u64 offs, size_t len, + const void *buf); #endif // __RENESAS_RPC_IF_H From 2dbe74c63cb73829be0aab0d0e7e68b87071b5fa Mon Sep 17 00:00:00 2001 From: George Moussalem Date: Thu, 1 May 2025 13:20:51 +0400 Subject: [PATCH 40/90] spi: dt-bindings: spi-qpic-snand: Add IPQ5018 compatible IPQ5018 contains the QPIC-SPI-NAND flash controller which is the same as the one found in IPQ9574. So let's document the IPQ5018 compatible and use IPQ9574 as the fallback. Signed-off-by: George Moussalem Link: https://patch.msgid.link/20250501-ipq5018-spi-qpic-snand-v1-1-31e01fbb606f@outlook.com Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/qcom,spi-qpic-snand.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml index aa3f93319203..cb1f15224b45 100644 --- a/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml +++ b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml @@ -21,8 +21,12 @@ allOf: properties: compatible: - enum: - - qcom,ipq9574-snand + oneOf: + - items: + - enum: + - qcom,ipq5018-snand + - const: qcom,ipq9574-snand + - const: qcom,ipq9574-snand reg: maxItems: 1 From 233d740e3a819829ccd6d21319015a94349d64eb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 2 May 2025 13:12:39 +0200 Subject: [PATCH 41/90] spi: loopback-test: Simplify strange loopback value check Apply De Morgan's Theorem and drop superfluous parentheses to simplify the check for strange loopback values. While at it, add the missing zero in the related comment. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/da612090f543c8c7cc99fb9dc6ef4abc9560abe4.1746184293.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 31a878d9458d..369cdb707fad 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -635,8 +635,8 @@ static int spi_test_check_loopback_result(struct spi_device *spi, } else { /* first byte received */ txb = ((u8 *)xfer->rx_buf)[0]; - /* first byte may be 0 or xff */ - if (!((txb == 0) || (txb == 0xff))) { + /* first byte may be 0 or 0xff */ + if (txb != 0 && txb != 0xff) { dev_err(&spi->dev, "loopback strangeness - we expect 0x00 or 0xff, but not 0x%02x\n", txb); From 075812e45e9ef1b882aa66b9d122d8b8739aae59 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 1 May 2025 17:01:59 +0200 Subject: [PATCH 42/90] spi: atmel-quadspi: Fix printed error code during DMA setup On dma_request_chan() failure driver NULL-ifies the 'rx_chan' and immediately uses it as PTR_ERR() so dev_err_probe() prints incorrect error code. Rework the code so proper error code will be printed and NULL-ifying of 'rx_chan' will happen in common error handling block (failure of DMA setup is not fatal for the driver and further code depends on 'rx_chan' being non-NULL for DMA operations). Reported by Smatch: drivers/spi/atmel-quadspi.c:1287 atmel_qspi_dma_init() warn: passing zero to 'PTR_ERR' Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250501-n-smatch-fixes-v2-1-d2ad9c1f2e67@linaro.org Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 244ac0106862..fc3675176388 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -1287,9 +1287,9 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl) aq->rx_chan = dma_request_chan(&aq->pdev->dev, "rx"); if (IS_ERR(aq->rx_chan)) { - aq->rx_chan = NULL; - return dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->rx_chan), - "RX DMA channel is not available\n"); + ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->rx_chan), + "RX DMA channel is not available\n"); + goto null_rx_chan; } aq->tx_chan = dma_request_chan(&aq->pdev->dev, "tx"); @@ -1310,8 +1310,9 @@ static int atmel_qspi_dma_init(struct spi_controller *ctrl) release_rx_chan: dma_release_channel(aq->rx_chan); - aq->rx_chan = NULL; aq->tx_chan = NULL; +null_rx_chan: + aq->rx_chan = NULL; return ret; } From f4ea37a485e97234af9550b63c52394d029037ee Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 1 May 2025 17:02:00 +0200 Subject: [PATCH 43/90] spi: cs42l43: Make handling missing spk-id GPIOs explicit gpiod_get_array_optional() for spk-id GPIOs can return NULL, if they are missing, so do not pass the value to PTR_ERR but instead explicitly treat NULL as acceptable condition. The old code was correct, but misleading because PTR_ERR usually is used on errors. Reported by Smatch: drivers/spi/spi-cs42l43.c:241 cs42l43_get_speaker_id_gpios() warn: passing zero to 'PTR_ERR' Signed-off-by: Krzysztof Kozlowski Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20250501-n-smatch-fixes-v2-2-d2ad9c1f2e67@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-cs42l43.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index ceefc253c549..b28a840b3b04 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -237,7 +237,9 @@ static int cs42l43_get_speaker_id_gpios(struct cs42l43_spi *priv, int *result) int i, ret; descs = gpiod_get_array_optional(priv->dev, "spk-id", GPIOD_IN); - if (IS_ERR_OR_NULL(descs)) + if (!descs) + return 0; + else if (IS_ERR(descs)) return PTR_ERR(descs); spkid = 0; From 222a87f6b94f6f177e896d6fcdc7881480344e34 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 1 May 2025 17:02:01 +0200 Subject: [PATCH 44/90] spi: cadence-quadspi: Assume device could match via platform Driver has only of_device_id table, however it also has MODULE_ALIAS() for platform name, thus assume there is a configuration where this can be loaded outside of OF system. In such case of_device_get_match_data() will return NULL, which is already checked in one place of probe() function but not in the other, leading to Smatch warning: drivers/spi/spi-cadence-quadspi.c:1942 cqspi_probe() error: we previously assumed 'ddata' could be null (see line 1885) Driver should be consistent, so assume device can be matched via platform bus and of_device_get_match_data() can indeed return NULL. This is also possible with malformed DTS on OF-platform: no unit address and device node name matching driver name. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250501-n-smatch-fixes-v2-3-d2ad9c1f2e67@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index c90462783b3f..fe0f122f07b0 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1949,7 +1949,7 @@ static int cqspi_probe(struct platform_device *pdev) host->num_chipselect = cqspi->num_chipselect; - if (ddata->quirks & CQSPI_SUPPORT_DEVICE_RESET) + if (ddata && (ddata->quirks & CQSPI_SUPPORT_DEVICE_RESET)) cqspi_device_reset(cqspi); if (cqspi->use_direct_mode) { From 48900813abd2730a35c6e3afd1609bafac5271cc Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 28 Apr 2025 18:06:43 +0800 Subject: [PATCH 45/90] spi: spi-nxp-fspi: remove the goto in probe Remove all the goto in probe to simplify the driver. Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20250428-flexspipatch-v3-1-61d5e8f591bc@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 87 ++++++++++++-------------------------- 1 file changed, 27 insertions(+), 60 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index bad6b30bab0e..00da184be88a 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -1161,10 +1161,10 @@ static int nxp_fspi_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct resource *res; struct nxp_fspi *f; - int ret; + int ret, irq; u32 reg; - ctlr = spi_alloc_host(&pdev->dev, sizeof(*f)); + ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*f)); if (!ctlr) return -ENOMEM; @@ -1174,10 +1174,8 @@ static int nxp_fspi_probe(struct platform_device *pdev) f = spi_controller_get_devdata(ctlr); f->dev = dev; f->devtype_data = (struct nxp_fspi_devtype_data *)device_get_match_data(dev); - if (!f->devtype_data) { - ret = -ENODEV; - goto err_put_ctrl; - } + if (!f->devtype_data) + return -ENODEV; platform_set_drvdata(pdev, f); @@ -1186,11 +1184,8 @@ static int nxp_fspi_probe(struct platform_device *pdev) f->iobase = devm_platform_ioremap_resource(pdev, 0); else f->iobase = devm_platform_ioremap_resource_byname(pdev, "fspi_base"); - - if (IS_ERR(f->iobase)) { - ret = PTR_ERR(f->iobase); - goto err_put_ctrl; - } + if (IS_ERR(f->iobase)) + return PTR_ERR(f->iobase); /* find the resources - controller memory mapped space */ if (is_acpi_node(dev_fwnode(f->dev))) @@ -1198,11 +1193,8 @@ static int nxp_fspi_probe(struct platform_device *pdev) else res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fspi_mmap"); - - if (!res) { - ret = -ENODEV; - goto err_put_ctrl; - } + if (!res) + return -ENODEV; /* assign memory mapped starting address and mapped size. */ f->memmap_phy = res->start; @@ -1211,69 +1203,46 @@ static int nxp_fspi_probe(struct platform_device *pdev) /* find the clocks */ if (dev_of_node(&pdev->dev)) { f->clk_en = devm_clk_get(dev, "fspi_en"); - if (IS_ERR(f->clk_en)) { - ret = PTR_ERR(f->clk_en); - goto err_put_ctrl; - } + if (IS_ERR(f->clk_en)) + return PTR_ERR(f->clk_en); f->clk = devm_clk_get(dev, "fspi"); - if (IS_ERR(f->clk)) { - ret = PTR_ERR(f->clk); - goto err_put_ctrl; - } - - ret = nxp_fspi_clk_prep_enable(f); - if (ret) { - dev_err(dev, "can not enable the clock\n"); - goto err_put_ctrl; - } + if (IS_ERR(f->clk)) + return PTR_ERR(f->clk); } + /* find the irq */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return dev_err_probe(dev, irq, "Failed to get irq source"); + + ret = nxp_fspi_clk_prep_enable(f); + if (ret) + return dev_err_probe(dev, ret, "Can't enable the clock\n"); + /* Clear potential interrupts */ reg = fspi_readl(f, f->iobase + FSPI_INTR); if (reg) fspi_writel(f, reg, f->iobase + FSPI_INTR); - /* find the irq */ - ret = platform_get_irq(pdev, 0); - if (ret < 0) - goto err_disable_clk; + nxp_fspi_default_setup(f); - ret = devm_request_irq(dev, ret, + ret = devm_request_irq(dev, irq, nxp_fspi_irq_handler, 0, pdev->name, f); if (ret) { - dev_err(dev, "failed to request irq: %d\n", ret); - goto err_disable_clk; + nxp_fspi_clk_disable_unprep(f); + return dev_err_probe(dev, ret, "Failed to request irq\n"); } - mutex_init(&f->lock); + devm_mutex_init(dev, &f->lock); ctlr->bus_num = -1; ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT; ctlr->mem_ops = &nxp_fspi_mem_ops; ctlr->mem_caps = &nxp_fspi_mem_caps; - - nxp_fspi_default_setup(f); - ctlr->dev.of_node = np; - ret = devm_spi_register_controller(&pdev->dev, ctlr); - if (ret) - goto err_destroy_mutex; - - return 0; - -err_destroy_mutex: - mutex_destroy(&f->lock); - -err_disable_clk: - nxp_fspi_clk_disable_unprep(f); - -err_put_ctrl: - spi_controller_put(ctlr); - - dev_err(dev, "NXP FSPI probe failed\n"); - return ret; + return devm_spi_register_controller(&pdev->dev, ctlr); } static void nxp_fspi_remove(struct platform_device *pdev) @@ -1285,8 +1254,6 @@ static void nxp_fspi_remove(struct platform_device *pdev) nxp_fspi_clk_disable_unprep(f); - mutex_destroy(&f->lock); - if (f->ahb_addr) iounmap(f->ahb_addr); } From 97be4b919a609fc8c4bd1118502b5d26cc2f77c4 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 28 Apr 2025 18:06:44 +0800 Subject: [PATCH 46/90] spi: spi-nxp-fspi: enable runtime pm for fspi Enable the runtime PM in fspi driver. Also for system PM, On some board like i.MX8ULP-EVK board, after system suspend, IOMUX module will lost power, so all the pinctrl setting will lost when system resume back, need driver to save/restore the pinctrl setting. Signed-off-by: Han Xu Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20250428-flexspipatch-v3-2-61d5e8f591bc@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 99 ++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 00da184be88a..f7c3330a1900 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include #include @@ -57,6 +59,9 @@ #include #include +/* runtime pm timeout */ +#define FSPI_RPM_TIMEOUT 50 /* 50ms */ + /* Registers used by the driver */ #define FSPI_MCR0 0x00 #define FSPI_MCR0_AHB_TIMEOUT(x) ((x) << 24) @@ -394,6 +399,8 @@ struct nxp_fspi { struct mutex lock; struct pm_qos_request pm_qos_req; int selected; +#define FSPI_NEED_INIT (1 << 0) + int flags; }; static inline int needs_ip_only(struct nxp_fspi *f) @@ -927,6 +934,13 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) mutex_lock(&f->lock); + err = pm_runtime_get_sync(f->dev); + if (err < 0) { + mutex_unlock(&f->lock); + dev_err(f->dev, "Failed to enable clock %d\n", __LINE__); + return err; + } + /* Wait for controller being ready. */ err = fspi_readl_poll_tout(f, f->iobase + FSPI_STS0, FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true); @@ -955,8 +969,10 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) /* Invalidate the data in the AHB buffer. */ nxp_fspi_invalid(f); - mutex_unlock(&f->lock); + pm_runtime_mark_last_busy(f->dev); + pm_runtime_put_autosuspend(f->dev); + mutex_unlock(&f->lock); return err; } @@ -1216,9 +1232,14 @@ static int nxp_fspi_probe(struct platform_device *pdev) if (irq < 0) return dev_err_probe(dev, irq, "Failed to get irq source"); - ret = nxp_fspi_clk_prep_enable(f); - if (ret) - return dev_err_probe(dev, ret, "Can't enable the clock\n"); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, FSPI_RPM_TIMEOUT); + pm_runtime_use_autosuspend(dev); + + /* enable clock */ + ret = pm_runtime_get_sync(f->dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable clock"); /* Clear potential interrupts */ reg = fspi_readl(f, f->iobase + FSPI_INTR); @@ -1227,12 +1248,14 @@ static int nxp_fspi_probe(struct platform_device *pdev) nxp_fspi_default_setup(f); + ret = pm_runtime_put_sync(dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to disable clock"); + ret = devm_request_irq(dev, irq, nxp_fspi_irq_handler, 0, pdev->name, f); - if (ret) { - nxp_fspi_clk_disable_unprep(f); + if (ret) return dev_err_probe(dev, ret, "Failed to request irq\n"); - } devm_mutex_init(dev, &f->lock); @@ -1249,29 +1272,70 @@ static void nxp_fspi_remove(struct platform_device *pdev) { struct nxp_fspi *f = platform_get_drvdata(pdev); + /* enable clock first since there is reigster access */ + pm_runtime_get_sync(f->dev); + /* disable the hardware */ fspi_writel(f, FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + pm_runtime_disable(f->dev); + pm_runtime_put_noidle(f->dev); nxp_fspi_clk_disable_unprep(f); if (f->ahb_addr) iounmap(f->ahb_addr); } -static int nxp_fspi_suspend(struct device *dev) -{ - return 0; -} - -static int nxp_fspi_resume(struct device *dev) +static int nxp_fspi_runtime_suspend(struct device *dev) { struct nxp_fspi *f = dev_get_drvdata(dev); - nxp_fspi_default_setup(f); + nxp_fspi_clk_disable_unprep(f); return 0; } +static int nxp_fspi_runtime_resume(struct device *dev) +{ + struct nxp_fspi *f = dev_get_drvdata(dev); + int ret; + + ret = nxp_fspi_clk_prep_enable(f); + if (ret) + return ret; + + if (f->flags & FSPI_NEED_INIT) { + nxp_fspi_default_setup(f); + ret = pinctrl_pm_select_default_state(dev); + if (ret) + dev_err(dev, "select flexspi default pinctrl failed!\n"); + f->flags &= ~FSPI_NEED_INIT; + } + + return ret; +} + +static int nxp_fspi_suspend(struct device *dev) +{ + struct nxp_fspi *f = dev_get_drvdata(dev); + int ret; + + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) { + dev_err(dev, "select flexspi sleep pinctrl failed!\n"); + return ret; + } + + f->flags |= FSPI_NEED_INIT; + + return pm_runtime_force_suspend(dev); +} + +static const struct dev_pm_ops nxp_fspi_pm_ops = { + RUNTIME_PM_OPS(nxp_fspi_runtime_suspend, nxp_fspi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(nxp_fspi_suspend, pm_runtime_force_resume) +}; + static const struct of_device_id nxp_fspi_dt_ids[] = { { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, }, { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, @@ -1291,17 +1355,12 @@ static const struct acpi_device_id nxp_fspi_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, nxp_fspi_acpi_ids); #endif -static const struct dev_pm_ops nxp_fspi_pm_ops = { - .suspend = nxp_fspi_suspend, - .resume = nxp_fspi_resume, -}; - static struct platform_driver nxp_fspi_driver = { .driver = { .name = "nxp-fspi", .of_match_table = nxp_fspi_dt_ids, .acpi_match_table = ACPI_PTR(nxp_fspi_acpi_ids), - .pm = &nxp_fspi_pm_ops, + .pm = pm_ptr(&nxp_fspi_pm_ops), }, .probe = nxp_fspi_probe, .remove = nxp_fspi_remove, From 93be516f819dcb1fdac035566381d8899d4b389c Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 28 Apr 2025 18:06:45 +0800 Subject: [PATCH 47/90] spi: spi-nxp-fspi: use guard(mutex) to simplify the code Use guard(mutex) to simplify the code logic. Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20250428-flexspipatch-v3-3-61d5e8f591bc@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index f7c3330a1900..ca0b51a254c1 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -932,11 +932,10 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->controller); int err = 0; - mutex_lock(&f->lock); + guard(mutex)(&f->lock); err = pm_runtime_get_sync(f->dev); if (err < 0) { - mutex_unlock(&f->lock); dev_err(f->dev, "Failed to enable clock %d\n", __LINE__); return err; } @@ -972,7 +971,6 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) pm_runtime_mark_last_busy(f->dev); pm_runtime_put_autosuspend(f->dev); - mutex_unlock(&f->lock); return err; } From e0558eb74c6e082b60e03981eb7cbf0fc3780ef4 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Mon, 28 Apr 2025 18:06:46 +0800 Subject: [PATCH 48/90] spi: spi-nxp-fspi: remove the unchecked return value for nxp_fspi_clk_disable_unprep For nxp_fspi_clk_disable_unprep(), no caller check the return value, so remove the unchecked return value. Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20250428-flexspipatch-v3-4-61d5e8f591bc@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index ca0b51a254c1..0ea04e77a968 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -634,15 +634,15 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f) return 0; } -static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f) +static void nxp_fspi_clk_disable_unprep(struct nxp_fspi *f) { if (is_acpi_node(dev_fwnode(f->dev))) - return 0; + return; clk_disable_unprepare(f->clk); clk_disable_unprepare(f->clk_en); - return 0; + return; } static void nxp_fspi_dll_calibration(struct nxp_fspi *f) From 9f7cd1bcb6363368abc954ff4e727b579813c697 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 28 Apr 2025 18:06:47 +0800 Subject: [PATCH 49/90] spi: nxp-fspi: use devm instead of remove for driver detach fspi driver use devm APIs to manage clk/irq/resources and register the spi controller, but the legacy remove function will be called first during device detach and trigger kernel panic. Drop the remove function and use devm_add_action_or_reset() for driver cleanup to ensure the release sequence. Signed-off-by: Han Xu Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20250428-flexspipatch-v3-5-61d5e8f591bc@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 41 ++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 0ea04e77a968..e63c77e41823 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -1168,6 +1168,24 @@ static const struct spi_controller_mem_caps nxp_fspi_mem_caps = { .per_op_freq = true, }; +static void nxp_fspi_cleanup(void *data) +{ + struct nxp_fspi *f = data; + + /* enable clock first since there is register access */ + pm_runtime_get_sync(f->dev); + + /* disable the hardware */ + fspi_writel(f, FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + + pm_runtime_disable(f->dev); + pm_runtime_put_noidle(f->dev); + nxp_fspi_clk_disable_unprep(f); + + if (f->ahb_addr) + iounmap(f->ahb_addr); +} + static int nxp_fspi_probe(struct platform_device *pdev) { struct spi_controller *ctlr; @@ -1263,27 +1281,13 @@ static int nxp_fspi_probe(struct platform_device *pdev) ctlr->mem_caps = &nxp_fspi_mem_caps; ctlr->dev.of_node = np; + ret = devm_add_action_or_reset(dev, nxp_fspi_cleanup, f); + if (ret) + return dev_err_probe(dev, ret, "Failed to register nxp_fspi_cleanup\n"); + return devm_spi_register_controller(&pdev->dev, ctlr); } -static void nxp_fspi_remove(struct platform_device *pdev) -{ - struct nxp_fspi *f = platform_get_drvdata(pdev); - - /* enable clock first since there is reigster access */ - pm_runtime_get_sync(f->dev); - - /* disable the hardware */ - fspi_writel(f, FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); - - pm_runtime_disable(f->dev); - pm_runtime_put_noidle(f->dev); - nxp_fspi_clk_disable_unprep(f); - - if (f->ahb_addr) - iounmap(f->ahb_addr); -} - static int nxp_fspi_runtime_suspend(struct device *dev) { struct nxp_fspi *f = dev_get_drvdata(dev); @@ -1361,7 +1365,6 @@ static struct platform_driver nxp_fspi_driver = { .pm = pm_ptr(&nxp_fspi_pm_ops), }, .probe = nxp_fspi_probe, - .remove = nxp_fspi_remove, }; module_platform_driver(nxp_fspi_driver); From 798dc3f19c9e3855c18c3afb610bc5d27195ef44 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 7 May 2025 17:21:44 +0100 Subject: [PATCH 50/90] memory: renesas-rpc-if: Add missing static keyword Fix the below sparse warnings: symbol 'rpcif_impl' was not declared. Should it be static? symbol 'xspi_impl' was not declared. Should it be static? Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202505072013.1EqwjtaR-lkp@intel.com/ Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20250507162146.140494-1-biju.das.jz@bp.renesas.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/renesas-rpc-if.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 4b2e903f2b0d..4a417b693080 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -1063,7 +1063,7 @@ static void rpcif_remove(struct platform_device *pdev) platform_device_unregister(rpc->vdev); } -struct rpcif_impl rpcif_impl = { +static const struct rpcif_impl rpcif_impl = { .hw_init = rpcif_hw_init_impl, .prepare = rpcif_prepare_impl, .manual_xfer = rpcif_manual_xfer_impl, @@ -1072,7 +1072,7 @@ struct rpcif_impl rpcif_impl = { .status_mask = RPCIF_CMNSR_TEND, }; -struct rpcif_impl xspi_impl = { +static const struct rpcif_impl xspi_impl = { .hw_init = xspi_hw_init_impl, .prepare = xspi_prepare_impl, .manual_xfer = xspi_manual_xfer_impl, From b0b8d3aeadb5c49bf78305a1bc844e5a9378257c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 24 Apr 2025 09:59:55 +0100 Subject: [PATCH 51/90] spi: rpc-if: Add write support for memory-mapped area Add write support for memory-mapped area as xSPI interface require it. Signed-off-by: Biju Das Link: https://patch.msgid.link/20250424090000.136804-8-biju.das.jz@bp.renesas.com Signed-off-by: Mark Brown --- drivers/spi/spi-rpc-if.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index e0c66a24a3cb..627cffea5d5c 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -75,6 +75,19 @@ static bool rpcif_spi_mem_supports_op(struct spi_mem *mem, return true; } +static ssize_t xspi_spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, const void *buf) +{ + struct rpcif *rpc = spi_controller_get_devdata(desc->mem->spi->controller); + + if (offs + desc->info.offset + len > U32_MAX) + return -EINVAL; + + rpcif_spi_mem_prepare(desc->mem->spi, &desc->info.op_tmpl, &offs, &len); + + return xspi_dirmap_write(rpc->dev, offs, len, buf); +} + static ssize_t rpcif_spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, void *buf) { @@ -103,7 +116,7 @@ static int rpcif_spi_mem_dirmap_create(struct spi_mem_dirmap_desc *desc) if (!rpc->dirmap) return -EOPNOTSUPP; - if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + if (!rpc->xspi && desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) return -EOPNOTSUPP; return 0; @@ -125,6 +138,7 @@ static const struct spi_controller_mem_ops rpcif_spi_mem_ops = { .exec_op = rpcif_spi_mem_exec_op, .dirmap_create = rpcif_spi_mem_dirmap_create, .dirmap_read = rpcif_spi_mem_dirmap_read, + .dirmap_write = xspi_spi_mem_dirmap_write, }; static int rpcif_spi_probe(struct platform_device *pdev) From 846656f278e803cb60161f0cba4ee90a058440cc Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 May 2025 13:21:31 +0200 Subject: [PATCH 52/90] spi: dt-bindings: fsl,dspi: Fix example indentation DTS example in the bindings should be indented with 2- or 4-spaces, so correct a mixture of different styles to keep consistent 4-spaces. Signed-off-by: Krzysztof Kozlowski Acked-by: Conor Dooley Link: https://patch.msgid.link/20250509112130.123462-3-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/fsl,dspi.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml index 7ca8fceda717..bf9cce53c48d 100644 --- a/Documentation/devicetree/bindings/spi/fsl,dspi.yaml +++ b/Documentation/devicetree/bindings/spi/fsl,dspi.yaml @@ -105,12 +105,12 @@ examples: big-endian; flash@0 { - compatible = "jedec,spi-nor"; - reg = <0>; - spi-max-frequency = <16000000>; - spi-cpol; - spi-cpha; - spi-cs-setup-delay-ns = <100>; - spi-cs-hold-delay-ns = <50>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <16000000>; + spi-cpol; + spi-cpha; + spi-cs-setup-delay-ns = <100>; + spi-cs-hold-delay-ns = <50>; }; }; From a4ca02454821cbc411e0bf16e527d392f188c218 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 May 2025 13:21:32 +0200 Subject: [PATCH 53/90] spi: dt-bindings: nuvoton,wpcm450-fiu: Drop unrelated nodes from DTS example Binding example should not contain other nodes, including other providers like syscon, because this is redundant and only adds unnecessary bloat. Signed-off-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20250509112130.123462-4-krzysztof.kozlowski@linaro.org Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml index 4e0d391e1d69..c97bf48b56b4 100644 --- a/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml +++ b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml @@ -59,8 +59,3 @@ examples: reg = <0>; }; }; - - shm: syscon@c8001000 { - compatible = "nuvoton,wpcm450-shm", "syscon"; - reg = <0xc8001000 0x1000>; - }; From 8856eafcc05ecf54d6dd2b6c67804fefd276472c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bence=20Cs=C3=B3k=C3=A1s?= Date: Thu, 27 Mar 2025 20:59:27 +0100 Subject: [PATCH 54/90] spi: atmel-quadspi: Fix unbalanced pm_runtime by using devm_ API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix unbalanced PM in error path of `atmel_qspi_probe()` by using `devm_pm_runtime_*()` functions. Reported-by: Alexander Dahl Closes: https://lore.kernel.org/linux-spi/20250110-paycheck-irregular-bcddab1276c7@thorsis.com/ Fixes: 5af42209a4d2 ("spi: atmel-quadspi: Add support for sama7g5 QSPI") Signed-off-by: Bence Csókás Link: https://patch.msgid.link/20250327195928.680771-4-csokas.bence@prolan.hu Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 244ac0106862..e7b61dc4ce67 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -1436,22 +1436,17 @@ static int atmel_qspi_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, 500); pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - pm_runtime_get_noresume(&pdev->dev); + devm_pm_runtime_set_active_enabled(&pdev->dev); + devm_pm_runtime_get_noresume(&pdev->dev); err = atmel_qspi_init(aq); if (err) goto dma_release; err = spi_register_controller(ctrl); - if (err) { - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); + if (err) goto dma_release; - } + pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); @@ -1530,10 +1525,6 @@ static void atmel_qspi_remove(struct platform_device *pdev) */ dev_warn(&pdev->dev, "Failed to resume device on remove\n"); } - - pm_runtime_disable(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); } static int __maybe_unused atmel_qspi_suspend(struct device *dev) From 65cb56d49f6edea409600a3c61effc70ee5d43d8 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 1 May 2025 18:19:16 +0200 Subject: [PATCH 55/90] spi: spi-qpic-snand: validate user/chip specific ECC properties The driver only supports 512 bytes ECC step size and 4 bit ECC strength at the moment, however it does not reject unsupported step/strength configurations. Due to this, whenever the driver is used with a flash chip which needs stronger ECC protection, the following warning is shown in the kernel log: [ 0.574648] spi-nand spi0.0: GigaDevice SPI NAND was found. [ 0.635748] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128 [ 0.649079] nand: WARNING: (null): the ECC used on your system is too weak compared to the one required by the NAND chip Although the message indicates that something is wrong, but it often gets unnoticed, which can cause serious problems. For example when the user writes something into the flash chip despite the warning, the written data may won't be readable by the bootloader or by the boot ROM. In the worst case, when the attached SPI NAND chip is the boot device, the board may not be able to boot anymore. Also, it is not even possible to create a backup of the flash, because reading its content results in bogus data. For example, dumping the first page of the flash gives this: # hexdump -C -n 2048 /dev/mtd0 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 00000040 0f 0f 0f 0f 0f 0f 0f 0d 0f 0f 0f 0f 0f 0f 0f 0f |................| 00000050 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 000001c0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| 000001d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 00000200 0f 0f 0f 0f f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.....[..........| 00000210 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 000002f0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 1f 0f 0f |................| 00000300 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 000003c0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f ff 0f 0f 0f |................| 000003d0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 00000400 0f 0f 0f 0f 0f 0f 0f 0f e9 74 c9 06 f5 5b ff ff |.........t...[..| 00000410 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 000005d0 0f 0f 0f 0f ff 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| 000005e0 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 00000600 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f c6 be 0f c3 |................| 00000610 e9 74 c9 06 f5 5b ff ff 0f 0f 0f 0f 0f 0f 0f 0f |.t...[..........| 00000620 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 00000770 0f 0f 0f 0f 8f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| 00000780 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 00000800 # Doing the same by using the downstream kernel results in different output: # hexdump -C -n 2048 /dev/mtd0 00000000 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f |................| * 00000800 # This patch adds some sanity checks to the code to prevent using the driver with unsupported ECC step/strength configurations. After the change, probing of the driver fails in such cases: [ 0.655038] spi-nand spi0.0: GigaDevice SPI NAND was found. [ 0.659159] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128 [ 0.669138] qcom_snand 79b0000.spi: only 4 bits ECC strength is supported [ 0.677476] nand: No suitable ECC configuration [ 0.689909] spi-nand spi0.0: probe with driver spi-nand failed with error -95 This helps to avoid the aforementioned hassles until support for 8 bit ECC strength gets implemented. Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250501-qpic-snand-validate-ecc-v1-1-532776581a66@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 42 +++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 18a8e572434e..7207bbb57802 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -249,9 +249,11 @@ static const struct mtd_ooblayout_ops qcom_spi_ooblayout = { static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) { struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand); + struct nand_ecc_props *reqs = &nand->ecc.requirements; + struct nand_ecc_props *user = &nand->ecc.user_conf; struct nand_ecc_props *conf = &nand->ecc.ctx.conf; struct mtd_info *mtd = nanddev_to_mtd(nand); - int cwperpage, bad_block_byte; + int cwperpage, bad_block_byte, ret; struct qpic_ecc *ecc_cfg; cwperpage = mtd->writesize / NANDC_STEP_SIZE; @@ -260,11 +262,39 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); if (!ecc_cfg) return -ENOMEM; + + if (user->step_size && user->strength) { + ecc_cfg->step_size = user->step_size; + ecc_cfg->strength = user->strength; + } else if (reqs->step_size && reqs->strength) { + ecc_cfg->step_size = reqs->step_size; + ecc_cfg->strength = reqs->strength; + } else { + /* use defaults */ + ecc_cfg->step_size = NANDC_STEP_SIZE; + ecc_cfg->strength = 4; + } + + if (ecc_cfg->step_size != NANDC_STEP_SIZE) { + dev_err(snandc->dev, + "only %u bytes ECC step size is supported\n", + NANDC_STEP_SIZE); + ret = -EOPNOTSUPP; + goto err_free_ecc_cfg; + } + + if (ecc_cfg->strength != 4) { + dev_err(snandc->dev, + "only 4 bits ECC strength is supported\n"); + ret = -EOPNOTSUPP; + goto err_free_ecc_cfg; + } + snandc->qspi->oob_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!snandc->qspi->oob_buf) { - kfree(ecc_cfg); - return -ENOMEM; + ret = -ENOMEM; + goto err_free_ecc_cfg; } memset(snandc->qspi->oob_buf, 0xff, mtd->writesize + mtd->oobsize); @@ -279,8 +309,6 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size; ecc_cfg->steps = 4; - ecc_cfg->strength = 4; - ecc_cfg->step_size = 512; ecc_cfg->cw_data = 516; ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes; bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1; @@ -338,6 +366,10 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) ecc_cfg->strength, ecc_cfg->step_size); return 0; + +err_free_ecc_cfg: + kfree(ecc_cfg); + return ret; } static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand) From 017f1b0bae08e8b456cf35cbdaae93ec19b50f0a Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Tue, 13 May 2025 20:00:43 +0000 Subject: [PATCH 56/90] spi: tegra210-quad: Add support for internal DMA Add support for internal DMA in Tegra234 devices. Tegra234 has an internal DMA controller, while Tegra241 continues to use an external DMA controller (GPCDMA). This patch adds support for both internal and external DMA controllers. Signed-off-by: Vishwaroop A Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20250513200043.608292-2-va@nvidia.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra210-quad.c | 225 +++++++++++++++++++------------- 1 file changed, 131 insertions(+), 94 deletions(-) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index a93e19911ef1..3581757a269b 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -111,6 +111,9 @@ #define QSPI_DMA_BLK 0x024 #define QSPI_DMA_BLK_SET(x) (((x) & 0xffff) << 0) +#define QSPI_DMA_MEM_ADDRESS 0x028 +#define QSPI_DMA_HI_ADDRESS 0x02c + #define QSPI_TX_FIFO 0x108 #define QSPI_RX_FIFO 0x188 @@ -167,9 +170,9 @@ enum tegra_qspi_transfer_type { }; struct tegra_qspi_soc_data { - bool has_dma; bool cmb_xfer_capable; bool supports_tpm; + bool has_ext_dma; unsigned int cs_count; }; @@ -605,13 +608,16 @@ static void tegra_qspi_dma_unmap_xfer(struct tegra_qspi *tqspi, struct spi_trans len = DIV_ROUND_UP(tqspi->curr_dma_words * tqspi->bytes_per_word, 4) * 4; - dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); - dma_unmap_single(tqspi->dev, t->rx_dma, len, DMA_FROM_DEVICE); + if (t->tx_buf) + dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); + if (t->rx_buf) + dma_unmap_single(tqspi->dev, t->rx_dma, len, DMA_FROM_DEVICE); } static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct spi_transfer *t) { struct dma_slave_config dma_sconfig = { 0 }; + dma_addr_t rx_dma_phys, tx_dma_phys; unsigned int len; u8 dma_burst; int ret = 0; @@ -634,60 +640,86 @@ static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct len = tqspi->curr_dma_words * 4; /* set attention level based on length of transfer */ - val = 0; - if (len & 0xf) { - val |= QSPI_TX_TRIG_1 | QSPI_RX_TRIG_1; - dma_burst = 1; - } else if (((len) >> 4) & 0x1) { - val |= QSPI_TX_TRIG_4 | QSPI_RX_TRIG_4; - dma_burst = 4; - } else { - val |= QSPI_TX_TRIG_8 | QSPI_RX_TRIG_8; - dma_burst = 8; + if (tqspi->soc_data->has_ext_dma) { + val = 0; + if (len & 0xf) { + val |= QSPI_TX_TRIG_1 | QSPI_RX_TRIG_1; + dma_burst = 1; + } else if (((len) >> 4) & 0x1) { + val |= QSPI_TX_TRIG_4 | QSPI_RX_TRIG_4; + dma_burst = 4; + } else { + val |= QSPI_TX_TRIG_8 | QSPI_RX_TRIG_8; + dma_burst = 8; + } + + tegra_qspi_writel(tqspi, val, QSPI_DMA_CTL); } - tegra_qspi_writel(tqspi, val, QSPI_DMA_CTL); tqspi->dma_control_reg = val; dma_sconfig.device_fc = true; - if (tqspi->cur_direction & DATA_DIR_TX) { - dma_sconfig.dst_addr = tqspi->phys + QSPI_TX_FIFO; - dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.dst_maxburst = dma_burst; - ret = dmaengine_slave_config(tqspi->tx_dma_chan, &dma_sconfig); - if (ret < 0) { - dev_err(tqspi->dev, "failed DMA slave config: %d\n", ret); - return ret; - } - tegra_qspi_copy_client_txbuf_to_qspi_txbuf(tqspi, t); - ret = tegra_qspi_start_tx_dma(tqspi, t, len); - if (ret < 0) { - dev_err(tqspi->dev, "failed to starting TX DMA: %d\n", ret); - return ret; + if (tqspi->cur_direction & DATA_DIR_TX) { + if (tqspi->tx_dma_chan) { + dma_sconfig.dst_addr = tqspi->phys + QSPI_TX_FIFO; + dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.dst_maxburst = dma_burst; + ret = dmaengine_slave_config(tqspi->tx_dma_chan, &dma_sconfig); + if (ret < 0) { + dev_err(tqspi->dev, "failed DMA slave config: %d\n", ret); + return ret; + } + + tegra_qspi_copy_client_txbuf_to_qspi_txbuf(tqspi, t); + ret = tegra_qspi_start_tx_dma(tqspi, t, len); + if (ret < 0) { + dev_err(tqspi->dev, "failed to starting TX DMA: %d\n", ret); + return ret; + } + } else { + if (tqspi->is_packed) + tx_dma_phys = t->tx_dma; + else + tx_dma_phys = tqspi->tx_dma_phys; + tegra_qspi_copy_client_txbuf_to_qspi_txbuf(tqspi, t); + tegra_qspi_writel(tqspi, lower_32_bits(tx_dma_phys), + QSPI_DMA_MEM_ADDRESS); + tegra_qspi_writel(tqspi, (upper_32_bits(tx_dma_phys) & 0xff), + QSPI_DMA_HI_ADDRESS); } } if (tqspi->cur_direction & DATA_DIR_RX) { - dma_sconfig.src_addr = tqspi->phys + QSPI_RX_FIFO; - dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dma_sconfig.src_maxburst = dma_burst; - ret = dmaengine_slave_config(tqspi->rx_dma_chan, &dma_sconfig); - if (ret < 0) { - dev_err(tqspi->dev, "failed DMA slave config: %d\n", ret); - return ret; - } + if (tqspi->rx_dma_chan) { + dma_sconfig.src_addr = tqspi->phys + QSPI_RX_FIFO; + dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_sconfig.src_maxburst = dma_burst; + ret = dmaengine_slave_config(tqspi->rx_dma_chan, &dma_sconfig); + if (ret < 0) { + dev_err(tqspi->dev, "failed DMA slave config: %d\n", ret); + return ret; + } - dma_sync_single_for_device(tqspi->dev, tqspi->rx_dma_phys, - tqspi->dma_buf_size, - DMA_FROM_DEVICE); + dma_sync_single_for_device(tqspi->dev, tqspi->rx_dma_phys, + tqspi->dma_buf_size, DMA_FROM_DEVICE); + ret = tegra_qspi_start_rx_dma(tqspi, t, len); + if (ret < 0) { + dev_err(tqspi->dev, "failed to start RX DMA: %d\n", ret); + if (tqspi->cur_direction & DATA_DIR_TX) + dmaengine_terminate_all(tqspi->tx_dma_chan); + return ret; + } + } else { + if (tqspi->is_packed) + rx_dma_phys = t->rx_dma; + else + rx_dma_phys = tqspi->rx_dma_phys; - ret = tegra_qspi_start_rx_dma(tqspi, t, len); - if (ret < 0) { - dev_err(tqspi->dev, "failed to start RX DMA: %d\n", ret); - if (tqspi->cur_direction & DATA_DIR_TX) - dmaengine_terminate_all(tqspi->tx_dma_chan); - return ret; + tegra_qspi_writel(tqspi, lower_32_bits(rx_dma_phys), + QSPI_DMA_MEM_ADDRESS); + tegra_qspi_writel(tqspi, (upper_32_bits(rx_dma_phys) & 0xff), + QSPI_DMA_HI_ADDRESS); } } @@ -726,9 +758,6 @@ static int tegra_qspi_start_cpu_based_transfer(struct tegra_qspi *qspi, struct s static void tegra_qspi_deinit_dma(struct tegra_qspi *tqspi) { - if (!tqspi->soc_data->has_dma) - return; - if (tqspi->tx_dma_buf) { dma_free_coherent(tqspi->dev, tqspi->dma_buf_size, tqspi->tx_dma_buf, tqspi->tx_dma_phys); @@ -759,17 +788,30 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) u32 *dma_buf; int err; - if (!tqspi->soc_data->has_dma) - return 0; + if (tqspi->soc_data->has_ext_dma) { + dma_chan = dma_request_chan(tqspi->dev, "rx"); + if (IS_ERR(dma_chan)) { + err = PTR_ERR(dma_chan); + goto err_out; + } - dma_chan = dma_request_chan(tqspi->dev, "rx"); - if (IS_ERR(dma_chan)) { - err = PTR_ERR(dma_chan); - goto err_out; + tqspi->rx_dma_chan = dma_chan; + + dma_chan = dma_request_chan(tqspi->dev, "tx"); + if (IS_ERR(dma_chan)) { + err = PTR_ERR(dma_chan); + goto err_out; + } + + tqspi->tx_dma_chan = dma_chan; + } else { + if (!device_iommu_mapped(tqspi->dev)) { + dev_warn(tqspi->dev, + "IOMMU not enabled in device-tree, falling back to PIO mode\n"); + return 0; + } } - tqspi->rx_dma_chan = dma_chan; - dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { err = -ENOMEM; @@ -779,14 +821,6 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) tqspi->rx_dma_buf = dma_buf; tqspi->rx_dma_phys = dma_phys; - dma_chan = dma_request_chan(tqspi->dev, "tx"); - if (IS_ERR(dma_chan)) { - err = PTR_ERR(dma_chan); - goto err_out; - } - - tqspi->tx_dma_chan = dma_chan; - dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { err = -ENOMEM; @@ -1128,15 +1162,14 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, if (WARN_ON_ONCE(ret == 0)) { dev_err_ratelimited(tqspi->dev, "QSPI Transfer failed with timeout\n"); - 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); + if (tqspi->is_curr_dma_xfer) { + if ((tqspi->cur_direction & DATA_DIR_TX) && + tqspi->tx_dma_chan) + dmaengine_terminate_all(tqspi->tx_dma_chan); + if ((tqspi->cur_direction & DATA_DIR_RX) && + tqspi->rx_dma_chan) + dmaengine_terminate_all(tqspi->rx_dma_chan); + } /* Abort transfer by resetting pio/dma bit */ if (!tqspi->is_curr_dma_xfer) { @@ -1251,10 +1284,12 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi, QSPI_DMA_TIMEOUT); if (WARN_ON(ret == 0)) { dev_err(tqspi->dev, "transfer timeout\n"); - 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); + if (tqspi->is_curr_dma_xfer) { + if ((tqspi->cur_direction & DATA_DIR_TX) && tqspi->tx_dma_chan) + dmaengine_terminate_all(tqspi->tx_dma_chan); + if ((tqspi->cur_direction & DATA_DIR_RX) && tqspi->rx_dma_chan) + dmaengine_terminate_all(tqspi->rx_dma_chan); + } tegra_qspi_handle_error(tqspi); ret = -EIO; goto complete_xfer; @@ -1323,7 +1358,7 @@ static bool tegra_qspi_validate_cmb_seq(struct tegra_qspi *tqspi, return false; xfer = list_next_entry(xfer, transfer_list); } - if (!tqspi->soc_data->has_dma && xfer->len > (QSPI_FIFO_DEPTH << 2)) + if (!tqspi->soc_data->has_ext_dma && xfer->len > (QSPI_FIFO_DEPTH << 2)) return false; return true; @@ -1384,41 +1419,43 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi) unsigned int total_fifo_words; unsigned long flags; long wait_status; - int err = 0; + int num_errors = 0; if (tqspi->cur_direction & DATA_DIR_TX) { if (tqspi->tx_status) { - dmaengine_terminate_all(tqspi->tx_dma_chan); - err += 1; - } else { + if (tqspi->tx_dma_chan) + dmaengine_terminate_all(tqspi->tx_dma_chan); + num_errors++; + } else if (tqspi->tx_dma_chan) { wait_status = wait_for_completion_interruptible_timeout( &tqspi->tx_dma_complete, QSPI_DMA_TIMEOUT); if (wait_status <= 0) { dmaengine_terminate_all(tqspi->tx_dma_chan); dev_err(tqspi->dev, "failed TX DMA transfer\n"); - err += 1; + num_errors++; } } } if (tqspi->cur_direction & DATA_DIR_RX) { if (tqspi->rx_status) { - dmaengine_terminate_all(tqspi->rx_dma_chan); - err += 2; - } else { + if (tqspi->rx_dma_chan) + dmaengine_terminate_all(tqspi->rx_dma_chan); + num_errors++; + } else if (tqspi->rx_dma_chan) { wait_status = wait_for_completion_interruptible_timeout( &tqspi->rx_dma_complete, QSPI_DMA_TIMEOUT); if (wait_status <= 0) { dmaengine_terminate_all(tqspi->rx_dma_chan); dev_err(tqspi->dev, "failed RX DMA transfer\n"); - err += 2; + num_errors++; } } } spin_lock_irqsave(&tqspi->lock, flags); - if (err) { + if (num_errors) { tegra_qspi_dma_unmap_xfer(tqspi, t); tegra_qspi_handle_error(tqspi); complete(&tqspi->xfer_completion); @@ -1444,9 +1481,9 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi) /* continue transfer in current message */ total_fifo_words = tegra_qspi_calculate_curr_xfer_param(tqspi, t); if (total_fifo_words > QSPI_FIFO_DEPTH) - err = tegra_qspi_start_dma_based_transfer(tqspi, t); + num_errors = tegra_qspi_start_dma_based_transfer(tqspi, t); else - err = tegra_qspi_start_cpu_based_transfer(tqspi, t); + num_errors = tegra_qspi_start_cpu_based_transfer(tqspi, t); exit: spin_unlock_irqrestore(&tqspi->lock, flags); @@ -1474,28 +1511,28 @@ 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, + .has_ext_dma = true, .cmb_xfer_capable = false, .supports_tpm = false, .cs_count = 1, }; static struct tegra_qspi_soc_data tegra186_qspi_soc_data = { - .has_dma = true, + .has_ext_dma = true, .cmb_xfer_capable = true, .supports_tpm = false, .cs_count = 1, }; static struct tegra_qspi_soc_data tegra234_qspi_soc_data = { - .has_dma = false, + .has_ext_dma = false, .cmb_xfer_capable = true, .supports_tpm = true, .cs_count = 1, }; static struct tegra_qspi_soc_data tegra241_qspi_soc_data = { - .has_dma = false, + .has_ext_dma = true, .cmb_xfer_capable = true, .supports_tpm = true, .cs_count = 4, From 4614fd6342ab69feebb067d5db84a9bfb9aada9f Mon Sep 17 00:00:00 2001 From: Vishwaroop A Date: Tue, 13 May 2025 20:00:42 +0000 Subject: [PATCH 57/90] spi: dt-bindings: tegra: Document IOMMU property for Tegra234 QSPI Add the 'iommus' property to the Tegra QSPI device tree binding. The property is needed for Tegra234 when using the internal DMA controller, and is not supported on other Tegra chips, as DMA is handled by an external controller. Signed-off-by: Vishwaroop A Acked-by: Conor Dooley Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20250513200043.608292-1-va@nvidia.com Signed-off-by: Mark Brown --- .../bindings/spi/nvidia,tegra210-quad.yaml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml b/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml index 48e97e240265..8b3640280559 100644 --- a/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml +++ b/Documentation/devicetree/bindings/spi/nvidia,tegra210-quad.yaml @@ -10,9 +10,6 @@ maintainers: - Thierry Reding - Jonathan Hunter -allOf: - - $ref: spi-controller.yaml# - properties: compatible: enum: @@ -47,6 +44,9 @@ properties: - const: rx - const: tx + iommus: + maxItems: 1 + patternProperties: "@[0-9a-f]+$": type: object @@ -69,6 +69,18 @@ required: unevaluatedProperties: false +allOf: + - $ref: spi-controller.yaml# + - if: + properties: + compatible: + not: + contains: + const: nvidia,tegra234-qspi + then: + properties: + iommus: false + examples: - | #include From 2abf107dcd797c60c86e9f17319cd1658862f6b2 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 15 May 2025 20:58:05 +0200 Subject: [PATCH 58/90] spi: spi-qpic-snand: use CW_PER_PAGE_MASK bitmask Change the code to use the already defined CW_PER_PAGE_MASK bitmask along with the FIELD_PREP() macro instead of using magic values. This makes the code more readable. It also syncs the affected codes with their counterparts in the 'qcom_nandc' driver, so it makes it easier to spot the differences between the two implementations. No functional changes intended. Signed-off-by: Gabor Juhos Reviewed-by: Md Sadre Alam Link: https://patch.msgid.link/20250515-qpic-snand-use-bitmasks-v1-1-11729aeae73b@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 7207bbb57802..bc45b834fadc 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -483,7 +483,8 @@ static int qcom_spi_block_erase(struct qcom_nand_controller *snandc) snandc->regs->cmd = snandc->qspi->cmd; snandc->regs->addr0 = snandc->qspi->addr1; snandc->regs->addr1 = snandc->qspi->addr2; - snandc->regs->cfg0 = cpu_to_le32(ecc_cfg->cfg0_raw & ~(7 << CW_PER_PAGE)); + snandc->regs->cfg0 = cpu_to_le32((ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, 0)); snandc->regs->cfg1 = cpu_to_le32(ecc_cfg->cfg1_raw); snandc->regs->exec = cpu_to_le32(1); @@ -544,8 +545,8 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, snandc->regs->addr0 = (snandc->qspi->addr1 | cpu_to_le32(col)); snandc->regs->addr1 = snandc->qspi->addr2; - cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | - 0 << CW_PER_PAGE; + cfg0 = (ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, 0); cfg1 = ecc_cfg->cfg1_raw; ecc_bch_cfg = ECC_CFG_ECC_DISABLE; @@ -687,8 +688,8 @@ static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_bu qcom_clear_bam_transaction(snandc); raw_cw = num_cw - 1; - cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | - 0 << CW_PER_PAGE; + cfg0 = (ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, 0); cfg1 = ecc_cfg->cfg1_raw; ecc_bch_cfg = ECC_CFG_ECC_DISABLE; @@ -808,8 +809,8 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, snandc->buf_start = 0; qcom_clear_read_regs(snandc); - cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; + cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); cfg1 = ecc_cfg->cfg1; ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; @@ -904,8 +905,8 @@ static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc, qcom_clear_read_regs(snandc); qcom_clear_bam_transaction(snandc); - cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; + cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); cfg1 = ecc_cfg->cfg1; ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; @@ -1015,8 +1016,8 @@ static int qcom_spi_program_raw(struct qcom_nand_controller *snandc, int num_cw = snandc->qspi->num_cw; u32 cfg0, cfg1, ecc_bch_cfg; - cfg0 = (ecc_cfg->cfg0_raw & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; + cfg0 = (ecc_cfg->cfg0_raw & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); cfg1 = ecc_cfg->cfg1_raw; ecc_bch_cfg = ECC_CFG_ECC_DISABLE; @@ -1098,8 +1099,8 @@ static int qcom_spi_program_ecc(struct qcom_nand_controller *snandc, int num_cw = snandc->qspi->num_cw; u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; - cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; + cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); cfg1 = ecc_cfg->cfg1; ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; @@ -1175,8 +1176,8 @@ static int qcom_spi_program_oob(struct qcom_nand_controller *snandc, int num_cw = snandc->qspi->num_cw; u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg; - cfg0 = (ecc_cfg->cfg0 & ~(7U << CW_PER_PAGE)) | - (num_cw - 1) << CW_PER_PAGE; + cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) | + FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1); cfg1 = ecc_cfg->cfg1; ecc_bch_cfg = ecc_cfg->ecc_bch_cfg; ecc_buf_cfg = ecc_cfg->ecc_buf_cfg; From 76ebfa371f0ff8ff8a42a9415f61432d6c48fa55 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 15 May 2025 20:58:06 +0200 Subject: [PATCH 59/90] spi: spi-qpic-snand: extend FIELD_PREP() macro usage Large part of the code uses the FIELD_PREP() macro already to construct values to be written to hardware registers. Change the code to use also the macro for more registers of which the corresponding bitmasks are defined already. This makes the code more readable. It also syncs the affected codes with their counterparts in the 'qcom_nandc' driver, so it makes it easier to spot the differences between the two implementations. No functional changes intended. Signed-off-by: Gabor Juhos Reviewed-by: Md Sadre Alam Link: https://patch.msgid.link/20250515-qpic-snand-use-bitmasks-v1-2-11729aeae73b@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index bc45b834fadc..ad01bb456a88 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -130,9 +130,9 @@ static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc, int is_last_read_loc) { __le32 locreg_val; - u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | - ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) - << READ_LOCATION_LAST)); + u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | + FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | + FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); locreg_val = cpu_to_le32(val); @@ -151,9 +151,9 @@ static void qcom_spi_set_read_loc_last(struct qcom_nand_controller *snandc, int is_last_read_loc) { __le32 locreg_val; - u32 val = (((cw_offset) << READ_LOCATION_OFFSET) | - ((read_size) << READ_LOCATION_SIZE) | ((is_last_read_loc) - << READ_LOCATION_LAST)); + u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) | + FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) | + FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc); locreg_val = cpu_to_le32(val); @@ -352,7 +352,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) FIELD_PREP(ECC_MODE_MASK, 0) | FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw); - ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS; + ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203); ecc_cfg->clrflashstatus = FS_READY_BSY_N; ecc_cfg->clrreadstatus = 0xc0; From 0e1c7d0591ac4054c67529e200a9edfaf5344f4a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:04 +0200 Subject: [PATCH 60/90] spi: sh-msiof: Drop comma after OF match table sentinel It does not make sense to have a comma after a sentinel, as any new elements must be added before the sentinel. Add a comment to clarify the purpose of the empty element. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/2ab5745407339ba54b63c3e6410082c7c566bf95.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index d9e3d83fc7e8..325bd5890b78 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1101,7 +1101,7 @@ static const struct of_device_id sh_msiof_match[] __maybe_unused = { { .compatible = "renesas,rcar-gen3-msiof", .data = &rcar_gen3_data }, { .compatible = "renesas,rcar-gen4-msiof", .data = &rcar_gen3_data }, { .compatible = "renesas,sh-msiof", .data = &sh_data }, /* Deprecated */ - {}, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sh_msiof_match); From ee44d3fc9d8b882cbc98acf2d3c773aba971aa67 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:05 +0200 Subject: [PATCH 61/90] spi: sh-msiof: Remove unneeded compatible values The Clock-Synchronized Serial Interfaces with FIFO (MSIOF) driver matches against both SoC-specific and family-specific compatible values to maintain backwards-compatibility with old DTBs predating the introduction of the family-specific compatible values. For RZ/G1, the SoC-specific compatible match entry can be removed from the driver: their DT always had the family-specific compatible values, and thus there was never a need to add the SoC-specific compatible values to the driver. For R-Car Gen2 and M3-W, the SoC-specific compatible match entries can be removed, too, as there are a few points in time where DT backwards-compatibility was broken for other reasons: - Legacy DT clock support is no longer supported since commit 58256143cff7c2e0 ("clk: renesas: Remove R-Car Gen2 legacy DT clock support") in v5.5, and the addition of "renesas,rcar-gen2-msiof" to DTS in v4.11 predates the completion of the clock conversion in v4.15, - Legacy DT LVDS support is no longer supported since commit 841281fe52a769fe ("drm: rcar-du: Drop LVDS device tree backward compatibility") in v5.18, and the addition of "renesas,rcar-gen3-msiof" in commit 8b51f97138ca22b6 ("arm64: dts: r8a7796: Use R-Car Gen 3 fallback binding for msiof nodes") in v4.11 predates the LVDS conversion in commit 58e8ed2ee9abe718 ("arm64: dts: renesas: Convert to new LVDS DT bindings") in v4.20. For R-Car H3, the SoC-specific compatible match entry cannot be removed, as its purpose is to handle an SoC-specific quirk. Suggested-by: Kuninori Morimoto Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/d33393ac7536bc3f0f624b079f70d80dd19843db.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 325bd5890b78..15e42af35f7e 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1088,16 +1088,8 @@ static const struct sh_msiof_chipdata rcar_r8a7795_data = { static const struct of_device_id sh_msiof_match[] __maybe_unused = { { .compatible = "renesas,sh-mobile-msiof", .data = &sh_data }, - { .compatible = "renesas,msiof-r8a7743", .data = &rcar_gen2_data }, - { .compatible = "renesas,msiof-r8a7745", .data = &rcar_gen2_data }, - { .compatible = "renesas,msiof-r8a7790", .data = &rcar_gen2_data }, - { .compatible = "renesas,msiof-r8a7791", .data = &rcar_gen2_data }, - { .compatible = "renesas,msiof-r8a7792", .data = &rcar_gen2_data }, - { .compatible = "renesas,msiof-r8a7793", .data = &rcar_gen2_data }, - { .compatible = "renesas,msiof-r8a7794", .data = &rcar_gen2_data }, { .compatible = "renesas,rcar-gen2-msiof", .data = &rcar_gen2_data }, { .compatible = "renesas,msiof-r8a7795", .data = &rcar_r8a7795_data }, - { .compatible = "renesas,msiof-r8a7796", .data = &rcar_gen3_data }, { .compatible = "renesas,rcar-gen3-msiof", .data = &rcar_gen3_data }, { .compatible = "renesas,rcar-gen4-msiof", .data = &rcar_gen3_data }, { .compatible = "renesas,sh-msiof", .data = &sh_data }, /* Deprecated */ From 0941d5166629cb766000530945e54b4e49680c68 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:06 +0200 Subject: [PATCH 62/90] spi: sh-msiof: Fix maximum DMA transfer size The maximum amount of data to transfer in a single DMA request is calculated from the FIFO sizes (which is technically not 100% correct, but a simplification, as it is limited by the maximum word count values in the Transmit and Control Data Registers). However, in case there is both data to transmit and to receive, the transmit limit is overwritten by the receive limit. Fix this by using the minimum applicable FIFO size instead. Move the calculation outside the loop, so it is not repeated for each individual DMA transfer. As currently tx_fifo_size is always equal to rx_fifo_size, this bug had no real impact. Fixes: fe78d0b7691c0274 ("spi: sh-msiof: Fix FIFO size to 64 word from 256 word") Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/d9961767a97758b2614f2ee8afe1bd56dc900a60.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 15e42af35f7e..cf93c2ca821f 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -919,6 +919,7 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, void *rx_buf = t->rx_buf; unsigned int len = t->len; unsigned int bits = t->bits_per_word; + unsigned int max_wdlen = 256; unsigned int bytes_per_word; unsigned int words; int n; @@ -932,17 +933,17 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, if (!spi_controller_is_target(p->ctlr)) sh_msiof_spi_set_clk_regs(p, t); + if (tx_buf) + max_wdlen = min(max_wdlen, p->tx_fifo_size); + if (rx_buf) + max_wdlen = min(max_wdlen, p->rx_fifo_size); + while (ctlr->dma_tx && len > 15) { /* * DMA supports 32-bit words only, hence pack 8-bit and 16-bit * words, with byte resp. word swapping. */ - unsigned int l = 0; - - if (tx_buf) - l = min(round_down(len, 4), p->tx_fifo_size * 4); - if (rx_buf) - l = min(round_down(len, 4), p->rx_fifo_size * 4); + unsigned int l = min(round_down(len, 4), max_wdlen * 4); if (bits <= 8) { copy32 = copy_bswap32; From 74df74ee83acfea4e9a6b47386c7907379c9295f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:07 +0200 Subject: [PATCH 63/90] spi: sh-msiof: Complete using dev in sh_msiof_spi_probe() Commit c4887bd4b35b225f ("spi: sh-msiof: use dev in sh_msiof_spi_probe()") forgot to convert one instance. Signed-off-by: Geert Uytterhoeven Acked-by: Kuninori Morimoto Link: https://patch.msgid.link/88d271b2d16c6ad7f174858894573f91cec1bc90.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index cf93c2ca821f..367622985fea 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1332,7 +1332,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) goto err1; } - ret = devm_request_irq(dev, i, sh_msiof_spi_irq, 0, dev_name(&pdev->dev), p); + ret = devm_request_irq(dev, i, sh_msiof_spi_irq, 0, dev_name(dev), p); if (ret) { dev_err(dev, "unable to request irq\n"); goto err1; From 3dd5ed19a2e8283f9ead5f2fd09b267a337ab86d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:08 +0200 Subject: [PATCH 64/90] spi: sh-msiof: Use bool for boolean flags Use bools instead of integers for boolean flags, which allows us to remove the "!!" idiom from several expressions. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/35cd51bdfb3c810911a5be757e0ce5bb29dcc755.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 367622985fea..11ef5c0a498d 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -348,11 +348,11 @@ static u32 sh_msiof_spi_get_dtdl_and_syncdl(struct sh_msiof_spi_priv *p) } static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, u32 ss, - u32 cpol, u32 cpha, - u32 tx_hi_z, u32 lsb_first, u32 cs_high) + bool cpol, bool cpha, bool tx_hi_z, + bool lsb_first, bool cs_high) { + bool edge; u32 tmp; - int edge; /* * CPOL CPHA TSCKIZ RSCKIZ TEDG REDG @@ -587,7 +587,8 @@ static int sh_msiof_prepare_message(struct spi_controller *ctlr, { struct sh_msiof_spi_priv *p = spi_controller_get_devdata(ctlr); const struct spi_device *spi = msg->spi; - u32 ss, cs_high; + bool cs_high; + u32 ss; /* Configure pins before asserting CS */ if (spi_get_csgpiod(spi, 0)) { @@ -595,12 +596,11 @@ static int sh_msiof_prepare_message(struct spi_controller *ctlr, cs_high = p->native_cs_high; } else { ss = spi_get_chipselect(spi, 0); - cs_high = !!(spi->mode & SPI_CS_HIGH); + cs_high = spi->mode & SPI_CS_HIGH; } - sh_msiof_spi_set_pin_regs(p, ss, !!(spi->mode & SPI_CPOL), - !!(spi->mode & SPI_CPHA), - !!(spi->mode & SPI_3WIRE), - !!(spi->mode & SPI_LSB_FIRST), cs_high); + sh_msiof_spi_set_pin_regs(p, ss, spi->mode & SPI_CPOL, + spi->mode & SPI_CPHA, spi->mode & SPI_3WIRE, + spi->mode & SPI_LSB_FIRST, cs_high); return 0; } From b4eec5cdf112e3a4e286fb79fe507b09e2fca66f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:09 +0200 Subject: [PATCH 65/90] spi: sh-msiof: Make words/bits unsigned in sh_msiof_spi_txrx_once() Make the words and bits parameters of sh_msiof_spi_txrx_once() unsigned, as that matches what is passed by the caller. This allows us to replace min_t() by the safer min(). Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/30eff1052642a4bcb0f1bc4bed7aae25d355a7dc.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 11ef5c0a498d..fdb13dbc1752 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -677,16 +677,16 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int), const void *tx_buf, void *rx_buf, - int words, int bits) + unsigned int words, unsigned int bits) { int fifo_shift; int ret; /* limit maximum word transfer to rx/tx fifo size */ if (tx_buf) - words = min_t(int, words, p->tx_fifo_size); + words = min(words, p->tx_fifo_size); if (rx_buf) - words = min_t(int, words, p->rx_fifo_size); + words = min(words, p->rx_fifo_size); /* the fifo contents need shifting */ fifo_shift = 32 - bits; From 74cb19c943ddb46f3d06323c3d0469f14282c8ca Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:10 +0200 Subject: [PATCH 66/90] spi: sh-msiof: Make words/fs unsigned in FIFO helpers Make the words and fs parameters of the various FIFO filler and emptier functions unsigned, as they can never be negative. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/a7b13ecb1811148227ec8c883079085ed1ea6eac.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 84 +++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index fdb13dbc1752..7c15de08f4d8 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -412,140 +412,154 @@ static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p) } static void sh_msiof_spi_write_fifo_8(struct sh_msiof_spi_priv *p, - const void *tx_buf, int words, int fs) + const void *tx_buf, unsigned int words, + unsigned int fs) { const u8 *buf_8 = tx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) sh_msiof_write(p, SITFDR, buf_8[k] << fs); } static void sh_msiof_spi_write_fifo_16(struct sh_msiof_spi_priv *p, - const void *tx_buf, int words, int fs) + const void *tx_buf, unsigned int words, + unsigned int fs) { const u16 *buf_16 = tx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) sh_msiof_write(p, SITFDR, buf_16[k] << fs); } static void sh_msiof_spi_write_fifo_16u(struct sh_msiof_spi_priv *p, - const void *tx_buf, int words, int fs) + const void *tx_buf, unsigned int words, + unsigned int fs) { const u16 *buf_16 = tx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) sh_msiof_write(p, SITFDR, get_unaligned(&buf_16[k]) << fs); } static void sh_msiof_spi_write_fifo_32(struct sh_msiof_spi_priv *p, - const void *tx_buf, int words, int fs) + const void *tx_buf, unsigned int words, + unsigned int fs) { const u32 *buf_32 = tx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) sh_msiof_write(p, SITFDR, buf_32[k] << fs); } static void sh_msiof_spi_write_fifo_32u(struct sh_msiof_spi_priv *p, - const void *tx_buf, int words, int fs) + const void *tx_buf, unsigned int words, + unsigned int fs) { const u32 *buf_32 = tx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) sh_msiof_write(p, SITFDR, get_unaligned(&buf_32[k]) << fs); } static void sh_msiof_spi_write_fifo_s32(struct sh_msiof_spi_priv *p, - const void *tx_buf, int words, int fs) + const void *tx_buf, unsigned int words, + unsigned int fs) { const u32 *buf_32 = tx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) sh_msiof_write(p, SITFDR, swab32(buf_32[k] << fs)); } static void sh_msiof_spi_write_fifo_s32u(struct sh_msiof_spi_priv *p, - const void *tx_buf, int words, int fs) + const void *tx_buf, + unsigned int words, unsigned int fs) { const u32 *buf_32 = tx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) sh_msiof_write(p, SITFDR, swab32(get_unaligned(&buf_32[k]) << fs)); } static void sh_msiof_spi_read_fifo_8(struct sh_msiof_spi_priv *p, - void *rx_buf, int words, int fs) + void *rx_buf, unsigned int words, + unsigned int fs) { u8 *buf_8 = rx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) buf_8[k] = sh_msiof_read(p, SIRFDR) >> fs; } static void sh_msiof_spi_read_fifo_16(struct sh_msiof_spi_priv *p, - void *rx_buf, int words, int fs) + void *rx_buf, unsigned int words, + unsigned int fs) { u16 *buf_16 = rx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) buf_16[k] = sh_msiof_read(p, SIRFDR) >> fs; } static void sh_msiof_spi_read_fifo_16u(struct sh_msiof_spi_priv *p, - void *rx_buf, int words, int fs) + void *rx_buf, unsigned int words, + unsigned int fs) { u16 *buf_16 = rx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) put_unaligned(sh_msiof_read(p, SIRFDR) >> fs, &buf_16[k]); } static void sh_msiof_spi_read_fifo_32(struct sh_msiof_spi_priv *p, - void *rx_buf, int words, int fs) + void *rx_buf, unsigned int words, + unsigned int fs) { u32 *buf_32 = rx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) buf_32[k] = sh_msiof_read(p, SIRFDR) >> fs; } static void sh_msiof_spi_read_fifo_32u(struct sh_msiof_spi_priv *p, - void *rx_buf, int words, int fs) + void *rx_buf, unsigned int words, + unsigned int fs) { u32 *buf_32 = rx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) put_unaligned(sh_msiof_read(p, SIRFDR) >> fs, &buf_32[k]); } static void sh_msiof_spi_read_fifo_s32(struct sh_msiof_spi_priv *p, - void *rx_buf, int words, int fs) + void *rx_buf, unsigned int words, + unsigned int fs) { u32 *buf_32 = rx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) buf_32[k] = swab32(sh_msiof_read(p, SIRFDR) >> fs); } static void sh_msiof_spi_read_fifo_s32u(struct sh_msiof_spi_priv *p, - void *rx_buf, int words, int fs) + void *rx_buf, unsigned int words, + unsigned int fs) { u32 *buf_32 = rx_buf; - int k; + unsigned int k; for (k = 0; k < words; k++) put_unaligned(swab32(sh_msiof_read(p, SIRFDR) >> fs), &buf_32[k]); @@ -673,13 +687,15 @@ static int sh_msiof_wait_for_completion(struct sh_msiof_spi_priv *p, static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, void (*tx_fifo)(struct sh_msiof_spi_priv *, - const void *, int, int), + const void *, unsigned int, + unsigned int), void (*rx_fifo)(struct sh_msiof_spi_priv *, - void *, int, int), + void *, unsigned int, + unsigned int), const void *tx_buf, void *rx_buf, unsigned int words, unsigned int bits) { - int fifo_shift; + unsigned int fifo_shift; int ret; /* limit maximum word transfer to rx/tx fifo size */ @@ -913,8 +929,10 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, { struct sh_msiof_spi_priv *p = spi_controller_get_devdata(ctlr); void (*copy32)(u32 *, const u32 *, unsigned int); - void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, int, int); - void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, int, int); + void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, unsigned int, + unsigned int); + void (*rx_fifo)(struct sh_msiof_spi_priv *, void *, unsigned int, + unsigned int); const void *tx_buf = t->tx_buf; void *rx_buf = t->rx_buf; unsigned int len = t->len; From 6bae252a9452ca8496670230a7821c34450763a2 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:11 +0200 Subject: [PATCH 67/90] spi: sh-msiof: SITMDR1/SIRMDR1 bitfield conversion Convert MSIOF Transmit and Receive Mode Register 1 field accesses to use the FIELD_PREP() bitfield access macro. This gets rid of explicit shifts. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/9685c54e752b8ef4256c9b281e9d8292e71d222e.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 45 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 7c15de08f4d8..fb83b049690e 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -7,6 +7,7 @@ * Copyright (C) 2014-2017 Glider bvba */ +#include #include #include #include @@ -84,20 +85,19 @@ struct sh_msiof_spi_priv { /* SITMDR1 and SIRMDR1 */ #define SIMDR1_TRMD BIT(31) /* Transfer Mode (1 = Master mode) */ -#define SIMDR1_SYNCMD_MASK GENMASK(29, 28) /* SYNC Mode */ -#define SIMDR1_SYNCMD_SPI (2 << 28) /* Level mode/SPI */ -#define SIMDR1_SYNCMD_LR (3 << 28) /* L/R mode */ -#define SIMDR1_SYNCAC_SHIFT 25 /* Sync Polarity (1 = Active-low) */ -#define SIMDR1_BITLSB_SHIFT 24 /* MSB/LSB First (1 = LSB first) */ -#define SIMDR1_DTDL_SHIFT 20 /* Data Pin Bit Delay for MSIOF_SYNC */ -#define SIMDR1_SYNCDL_SHIFT 16 /* Frame Sync Signal Timing Delay */ -#define SIMDR1_FLD_MASK GENMASK(3, 2) /* Frame Sync Signal Interval (0-3) */ -#define SIMDR1_FLD_SHIFT 2 +#define SIMDR1_SYNCMD GENMASK(29, 28) /* SYNC Mode */ +#define SIMDR1_SYNCMD_SPI 2U /* Level mode/SPI */ +#define SIMDR1_SYNCMD_LR 3U /* L/R mode */ +#define SIMDR1_SYNCAC BIT(25) /* Sync Polarity (1 = Active-low) */ +#define SIMDR1_BITLSB BIT(24) /* MSB/LSB First (1 = LSB first) */ +#define SIMDR1_DTDL GENMASK(22, 20) /* Data Pin Bit Delay for MSIOF_SYNC */ +#define SIMDR1_SYNCDL GENMASK(18, 16) /* Frame Sync Signal Timing Delay */ +#define SIMDR1_FLD GENMASK(3, 2) /* Frame Sync Signal Interval (0-3) */ #define SIMDR1_XXSTP BIT(0) /* Transmission/Reception Stop on FIFO */ /* SITMDR1 */ #define SITMDR1_PCON BIT(30) /* Transfer Signal Connection */ -#define SITMDR1_SYNCCH_MASK GENMASK(27, 26) /* Sync Signal Channel Select */ -#define SITMDR1_SYNCCH_SHIFT 26 /* 0=MSIOF_SYNC, 1=MSIOF_SS1, 2=MSIOF_SS2 */ +#define SITMDR1_SYNCCH GENMASK(27, 26) /* Sync Signal Channel Select */ + /* 0=MSIOF_SYNC, 1=MSIOF_SS1, 2=MSIOF_SS2 */ /* SITMDR2 and SIRMDR2 */ #define SIMDR2_BITLEN1(i) (((i) - 1) << 24) /* Data Size (8-32 bits) */ @@ -341,8 +341,9 @@ static u32 sh_msiof_spi_get_dtdl_and_syncdl(struct sh_msiof_spi_priv *p) return 0; } - val = sh_msiof_get_delay_bit(p->info->dtdl) << SIMDR1_DTDL_SHIFT; - val |= sh_msiof_get_delay_bit(p->info->syncdl) << SIMDR1_SYNCDL_SHIFT; + val = FIELD_PREP(SIMDR1_DTDL, sh_msiof_get_delay_bit(p->info->dtdl)) | + FIELD_PREP(SIMDR1_SYNCDL, + sh_msiof_get_delay_bit(p->info->syncdl)); return val; } @@ -361,16 +362,18 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, u32 ss, * 1 0 11 11 0 0 * 1 1 11 11 1 1 */ - tmp = SIMDR1_SYNCMD_SPI | 1 << SIMDR1_FLD_SHIFT | SIMDR1_XXSTP; - tmp |= !cs_high << SIMDR1_SYNCAC_SHIFT; - tmp |= lsb_first << SIMDR1_BITLSB_SHIFT; + tmp = FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_SPI) | + FIELD_PREP(SIMDR1_FLD, 1) | SIMDR1_XXSTP | + FIELD_PREP(SIMDR1_SYNCAC, !cs_high) | + FIELD_PREP(SIMDR1_BITLSB, lsb_first); tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p); if (spi_controller_is_target(p->ctlr)) { sh_msiof_write(p, SITMDR1, tmp | SITMDR1_PCON); } else { sh_msiof_write(p, SITMDR1, tmp | SIMDR1_TRMD | SITMDR1_PCON | - (ss < MAX_SS ? ss : 0) << SITMDR1_SYNCCH_SHIFT); + FIELD_PREP(SITMDR1_SYNCCH, + ss < MAX_SS ? ss : 0)); } if (p->ctlr->flags & SPI_CONTROLLER_MUST_TX) { /* These bits are reserved if RX needs TX */ @@ -579,12 +582,12 @@ static int sh_msiof_spi_setup(struct spi_device *spi) return 0; /* Configure native chip select mode/polarity early */ - clr = SIMDR1_SYNCMD_MASK; - set = SIMDR1_SYNCMD_SPI; + clr = SIMDR1_SYNCMD; + set = FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_SPI); if (spi->mode & SPI_CS_HIGH) - clr |= BIT(SIMDR1_SYNCAC_SHIFT); + clr |= SIMDR1_SYNCAC; else - set |= BIT(SIMDR1_SYNCAC_SHIFT); + set |= SIMDR1_SYNCAC; pm_runtime_get_sync(&p->pdev->dev); tmp = sh_msiof_read(p, SITMDR1) & ~clr; sh_msiof_write(p, SITMDR1, tmp | set | SIMDR1_TRMD | SITMDR1_PCON); From 386cc5207ba2b68a4672cb68810c44412531e74e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:12 +0200 Subject: [PATCH 68/90] spi: sh-msiof: SITMDR2 and SIRMDR2 bitfield conversion Convert MSIOF Transmit and Receive Mode Register 2 field accesses to use the FIELD_PREP() bitfield access macro. This gets rid of explicit shifts and custom field preparation macros. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/135b92d010a71e2c224feab3a5792724b4e60ff1.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index fb83b049690e..51a9e8936475 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -100,8 +100,8 @@ struct sh_msiof_spi_priv { /* 0=MSIOF_SYNC, 1=MSIOF_SS1, 2=MSIOF_SS2 */ /* SITMDR2 and SIRMDR2 */ -#define SIMDR2_BITLEN1(i) (((i) - 1) << 24) /* Data Size (8-32 bits) */ -#define SIMDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */ +#define SIMDR2_BITLEN1 GENMASK(28, 24) /* Data Size (8-32 bits) */ +#define SIMDR2_WDLEN1 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ #define SIMDR2_GRPMASK1 BIT(0) /* Group Output Mask 1 (SH, A1) */ /* SITSCR and SIRSCR */ @@ -397,7 +397,8 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, const void *tx_buf, void *rx_buf, u32 bits, u32 words) { - u32 dr2 = SIMDR2_BITLEN1(bits) | SIMDR2_WDLEN1(words); + u32 dr2 = FIELD_PREP(SIMDR2_BITLEN1, bits - 1) | + FIELD_PREP(SIMDR2_WDLEN1, words - 1); if (tx_buf || (p->ctlr->flags & SPI_CONTROLLER_MUST_TX)) sh_msiof_write(p, SITMDR2, dr2); @@ -931,6 +932,7 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, struct spi_transfer *t) { struct sh_msiof_spi_priv *p = spi_controller_get_devdata(ctlr); + unsigned int max_wdlen = FIELD_MAX(SIMDR2_WDLEN1) + 1; void (*copy32)(u32 *, const u32 *, unsigned int); void (*tx_fifo)(struct sh_msiof_spi_priv *, const void *, unsigned int, unsigned int); @@ -940,7 +942,6 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, void *rx_buf = t->rx_buf; unsigned int len = t->len; unsigned int bits = t->bits_per_word; - unsigned int max_wdlen = 256; unsigned int bytes_per_word; unsigned int words; int n; From c2cc4b72fc14c84fdb1bc49ec98af252ed64fbf3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:13 +0200 Subject: [PATCH 69/90] spi: sh-msiof: SITSCR/SIRSCR bitfield conversion Convert MSIOF Transmit and Receive Clock Select Register field accesses to use the FIELD_PREP() bitfield access macro. This gets rid of explicit shifts and custom field preparation macros. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/f2462c99b6ea2e45b995ab4509c2f039043da032.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 51a9e8936475..9bddf85dd9c7 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -105,15 +105,14 @@ struct sh_msiof_spi_priv { #define SIMDR2_GRPMASK1 BIT(0) /* Group Output Mask 1 (SH, A1) */ /* SITSCR and SIRSCR */ -#define SISCR_BRPS_MASK GENMASK(12, 8) /* Prescaler Setting (1-32) */ -#define SISCR_BRPS(i) (((i) - 1) << 8) -#define SISCR_BRDV_MASK GENMASK(2, 0) /* Baud Rate Generator's Division Ratio */ -#define SISCR_BRDV_DIV_2 0 -#define SISCR_BRDV_DIV_4 1 -#define SISCR_BRDV_DIV_8 2 -#define SISCR_BRDV_DIV_16 3 -#define SISCR_BRDV_DIV_32 4 -#define SISCR_BRDV_DIV_1 7 +#define SISCR_BRPS GENMASK(12, 8) /* Prescaler Setting (1-32) */ +#define SISCR_BRDV GENMASK(2, 0) /* Baud Rate Generator's Division Ratio */ +#define SISCR_BRDV_DIV_2 0U +#define SISCR_BRDV_DIV_4 1U +#define SISCR_BRDV_DIV_8 2U +#define SISCR_BRDV_DIV_16 3U +#define SISCR_BRDV_DIV_32 4U +#define SISCR_BRDV_DIV_1 7U /* SICTR */ #define SICTR_TSCKIZ_MASK GENMASK(31, 30) /* Transmit Clock I/O Polarity Select */ @@ -299,7 +298,8 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, t->effective_speed_hz = parent_rate / (brps << div_pow); - scr = sh_msiof_spi_div_array[div_pow] | SISCR_BRPS(brps); + scr = FIELD_PREP(SISCR_BRDV, sh_msiof_spi_div_array[div_pow]) | + FIELD_PREP(SISCR_BRPS, brps - 1); sh_msiof_write(p, SITSCR, scr); if (!(p->ctlr->flags & SPI_CONTROLLER_MUST_TX)) sh_msiof_write(p, SIRSCR, scr); From bd8d6658e7084ff9e78da9623633e79c9d31ad68 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:14 +0200 Subject: [PATCH 70/90] spi: sh-msiof: SICTR bitfield conversion Convert MSIOF Control Register field accesses to use the FIELD_PREP() bitfield access macro. This gets rid of explicit shifts. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/4511c678c8fce5969eb50ffa7372d53396ff80ff.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 9bddf85dd9c7..4f582ecc524f 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -115,18 +115,18 @@ struct sh_msiof_spi_priv { #define SISCR_BRDV_DIV_1 7U /* SICTR */ -#define SICTR_TSCKIZ_MASK GENMASK(31, 30) /* Transmit Clock I/O Polarity Select */ +#define SICTR_TSCKIZ GENMASK(31, 30) /* Transmit Clock I/O Polarity Select */ #define SICTR_TSCKIZ_SCK BIT(31) /* Disable SCK when TX disabled */ -#define SICTR_TSCKIZ_POL_SHIFT 30 /* Transmit Clock Polarity */ -#define SICTR_RSCKIZ_MASK GENMASK(29, 28) /* Receive Clock Polarity Select */ +#define SICTR_TSCKIZ_POL BIT(30) /* Transmit Clock Polarity */ +#define SICTR_RSCKIZ GENMASK(29, 28) /* Receive Clock Polarity Select */ #define SICTR_RSCKIZ_SCK BIT(29) /* Must match CTR_TSCKIZ_SCK */ -#define SICTR_RSCKIZ_POL_SHIFT 28 /* Receive Clock Polarity */ -#define SICTR_TEDG_SHIFT 27 /* Transmit Timing (1 = falling edge) */ -#define SICTR_REDG_SHIFT 26 /* Receive Timing (1 = falling edge) */ -#define SICTR_TXDIZ_MASK GENMASK(23, 22) /* Pin Output When TX is Disabled */ -#define SICTR_TXDIZ_LOW (0 << 22) /* 0 */ -#define SICTR_TXDIZ_HIGH (1 << 22) /* 1 */ -#define SICTR_TXDIZ_HIZ (2 << 22) /* High-impedance */ +#define SICTR_RSCKIZ_POL BIT(28) /* Receive Clock Polarity */ +#define SICTR_TEDG BIT(27) /* Transmit Timing (1 = falling edge) */ +#define SICTR_REDG BIT(26) /* Receive Timing (1 = falling edge) */ +#define SICTR_TXDIZ GENMASK(23, 22) /* Pin Output When TX is Disabled */ +#define SICTR_TXDIZ_LOW 0U /* 0 */ +#define SICTR_TXDIZ_HIGH 1U /* 1 */ +#define SICTR_TXDIZ_HIZ 2U /* High-impedance */ #define SICTR_TSCKE BIT(15) /* Transmit Serial Clock Output Enable */ #define SICTR_TFSE BIT(14) /* Transmit Frame Sync Signal Output Enable */ #define SICTR_TXE BIT(9) /* Transmit Enable */ @@ -382,14 +382,15 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, u32 ss, sh_msiof_write(p, SIRMDR1, tmp); tmp = 0; - tmp |= SICTR_TSCKIZ_SCK | cpol << SICTR_TSCKIZ_POL_SHIFT; - tmp |= SICTR_RSCKIZ_SCK | cpol << SICTR_RSCKIZ_POL_SHIFT; + tmp |= SICTR_TSCKIZ_SCK | FIELD_PREP(SICTR_TSCKIZ_POL, cpol); + tmp |= SICTR_RSCKIZ_SCK | FIELD_PREP(SICTR_RSCKIZ_POL, cpol); edge = cpol ^ !cpha; - tmp |= edge << SICTR_TEDG_SHIFT; - tmp |= edge << SICTR_REDG_SHIFT; - tmp |= tx_hi_z ? SICTR_TXDIZ_HIZ : SICTR_TXDIZ_LOW; + tmp |= FIELD_PREP(SICTR_TEDG, edge); + tmp |= FIELD_PREP(SICTR_REDG, edge); + tmp |= FIELD_PREP(SICTR_TXDIZ, + tx_hi_z ? SICTR_TXDIZ_HIZ : SICTR_TXDIZ_LOW); sh_msiof_write(p, SICTR, tmp); } From 8f3903b382f73eb5c36d342dea838d991e4bbe08 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:15 +0200 Subject: [PATCH 71/90] spi: sh-msiof: SIFCTR bitfield conversion Convert MSIOF FIFO Control Register field accesses to use the FIELD_PREP() bitfield access macro. This gets rid of explicit shifts and custom field preparation macros. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/0bf4c366381a8999c9755285272897300852bc18.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 48 ++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 4f582ecc524f..812e64ea5f79 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -135,30 +135,26 @@ struct sh_msiof_spi_priv { #define SICTR_RXRST BIT(0) /* Receive Reset */ /* SIFCTR */ -#define SIFCTR_TFWM_MASK GENMASK(31, 29) /* Transmit FIFO Watermark */ -#define SIFCTR_TFWM_64 (0UL << 29) /* Transfer Request when 64 empty stages */ -#define SIFCTR_TFWM_32 (1UL << 29) /* Transfer Request when 32 empty stages */ -#define SIFCTR_TFWM_24 (2UL << 29) /* Transfer Request when 24 empty stages */ -#define SIFCTR_TFWM_16 (3UL << 29) /* Transfer Request when 16 empty stages */ -#define SIFCTR_TFWM_12 (4UL << 29) /* Transfer Request when 12 empty stages */ -#define SIFCTR_TFWM_8 (5UL << 29) /* Transfer Request when 8 empty stages */ -#define SIFCTR_TFWM_4 (6UL << 29) /* Transfer Request when 4 empty stages */ -#define SIFCTR_TFWM_1 (7UL << 29) /* Transfer Request when 1 empty stage */ -#define SIFCTR_TFUA_MASK GENMASK(26, 20) /* Transmit FIFO Usable Area */ -#define SIFCTR_TFUA_SHIFT 20 -#define SIFCTR_TFUA(i) ((i) << SIFCTR_TFUA_SHIFT) -#define SIFCTR_RFWM_MASK GENMASK(15, 13) /* Receive FIFO Watermark */ -#define SIFCTR_RFWM_1 (0 << 13) /* Transfer Request when 1 valid stages */ -#define SIFCTR_RFWM_4 (1 << 13) /* Transfer Request when 4 valid stages */ -#define SIFCTR_RFWM_8 (2 << 13) /* Transfer Request when 8 valid stages */ -#define SIFCTR_RFWM_16 (3 << 13) /* Transfer Request when 16 valid stages */ -#define SIFCTR_RFWM_32 (4 << 13) /* Transfer Request when 32 valid stages */ -#define SIFCTR_RFWM_64 (5 << 13) /* Transfer Request when 64 valid stages */ -#define SIFCTR_RFWM_128 (6 << 13) /* Transfer Request when 128 valid stages */ -#define SIFCTR_RFWM_256 (7 << 13) /* Transfer Request when 256 valid stages */ -#define SIFCTR_RFUA_MASK GENMASK(12, 4) /* Receive FIFO Usable Area (0x40 = full) */ -#define SIFCTR_RFUA_SHIFT 4 -#define SIFCTR_RFUA(i) ((i) << SIFCTR_RFUA_SHIFT) +#define SIFCTR_TFWM GENMASK(31, 29) /* Transmit FIFO Watermark */ +#define SIFCTR_TFWM_64 0U /* Transfer Request when 64 empty stages */ +#define SIFCTR_TFWM_32 1U /* Transfer Request when 32 empty stages */ +#define SIFCTR_TFWM_24 2U /* Transfer Request when 24 empty stages */ +#define SIFCTR_TFWM_16 3U /* Transfer Request when 16 empty stages */ +#define SIFCTR_TFWM_12 4U /* Transfer Request when 12 empty stages */ +#define SIFCTR_TFWM_8 5U /* Transfer Request when 8 empty stages */ +#define SIFCTR_TFWM_4 6U /* Transfer Request when 4 empty stages */ +#define SIFCTR_TFWM_1 7U /* Transfer Request when 1 empty stage */ +#define SIFCTR_TFUA GENMASK(26, 20) /* Transmit FIFO Usable Area */ +#define SIFCTR_RFWM GENMASK(15, 13) /* Receive FIFO Watermark */ +#define SIFCTR_RFWM_1 0U /* Transfer Request when 1 valid stages */ +#define SIFCTR_RFWM_4 1U /* Transfer Request when 4 valid stages */ +#define SIFCTR_RFWM_8 2U /* Transfer Request when 8 valid stages */ +#define SIFCTR_RFWM_16 3U /* Transfer Request when 16 valid stages */ +#define SIFCTR_RFWM_32 4U /* Transfer Request when 32 valid stages */ +#define SIFCTR_RFWM_64 5U /* Transfer Request when 64 valid stages */ +#define SIFCTR_RFWM_128 6U /* Transfer Request when 128 valid stages */ +#define SIFCTR_RFWM_256 7U /* Transfer Request when 256 valid stages */ +#define SIFCTR_RFUA GENMASK(12, 4) /* Receive FIFO Usable Area (0x40 = full) */ /* SISTR */ #define SISTR_TFEMP BIT(29) /* Transmit FIFO Empty */ @@ -811,7 +807,9 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, } /* 1 stage FIFO watermarks for DMA */ - sh_msiof_write(p, SIFCTR, SIFCTR_TFWM_1 | SIFCTR_RFWM_1); + sh_msiof_write(p, SIFCTR, + FIELD_PREP(SIFCTR_TFWM, SIFCTR_TFWM_1) | + FIELD_PREP(SIFCTR_RFWM, SIFCTR_RFWM_1)); /* setup msiof transfer mode registers (32-bit words) */ sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4); From acedbff0f0de1c116a0f8d943c4cf005f61f6143 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:16 +0200 Subject: [PATCH 72/90] spi: sh-msiof: Correct SIMDR2_GRPMASK The Group Output Mask is not a single bit, but a bit field, containing one bit for each of the four possible groups. Correct the definition. Note that this change has no direct impact, as the driver only uses the first group. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/ad268d67807cb7e544eddaf7a056793482a965d4.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 812e64ea5f79..57f27040b0fc 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -102,7 +102,7 @@ struct sh_msiof_spi_priv { /* SITMDR2 and SIRMDR2 */ #define SIMDR2_BITLEN1 GENMASK(28, 24) /* Data Size (8-32 bits) */ #define SIMDR2_WDLEN1 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ -#define SIMDR2_GRPMASK1 BIT(0) /* Group Output Mask 1 (SH, A1) */ +#define SIMDR2_GRPMASK GENMASK(3, 0) /* Group Output Mask 1-4 (SH, A1) */ /* SITSCR and SIRSCR */ #define SISCR_BRPS GENMASK(12, 8) /* Prescaler Setting (1-32) */ @@ -400,7 +400,7 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, if (tx_buf || (p->ctlr->flags & SPI_CONTROLLER_MUST_TX)) sh_msiof_write(p, SITMDR2, dr2); else - sh_msiof_write(p, SITMDR2, dr2 | SIMDR2_GRPMASK1); + sh_msiof_write(p, SITMDR2, dr2 | SIMDR2_GRPMASK); if (rx_buf) sh_msiof_write(p, SIRMDR2, dr2); From 955f7ce6680564963765e5fc1c3e71027a39a806 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:17 +0200 Subject: [PATCH 73/90] spi: sh-msiof: Add core support for dual-group transfers All MSIOF variants support transferring data of multiple (2 or 4) groups. Add definitions for the register bits related to multiple groups, and enhance sh_msiof_spi_set_mode_regs() to accept a second group size. For now the second group is unused. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/be75e20cfcd2a6c0d73ab09e0126f902911adc69.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 57f27040b0fc..ea9277ab87f6 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -100,10 +100,15 @@ struct sh_msiof_spi_priv { /* 0=MSIOF_SYNC, 1=MSIOF_SS1, 2=MSIOF_SS2 */ /* SITMDR2 and SIRMDR2 */ +#define SIMDR2_GRP GENMASK(31, 30) /* Group Count */ #define SIMDR2_BITLEN1 GENMASK(28, 24) /* Data Size (8-32 bits) */ #define SIMDR2_WDLEN1 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ #define SIMDR2_GRPMASK GENMASK(3, 0) /* Group Output Mask 1-4 (SH, A1) */ +/* SITMDR3 and SIRMDR3 */ +#define SIMDR3_BITLEN2 GENMASK(28, 24) /* Data Size (8-32 bits) */ +#define SIMDR3_WDLEN2 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ + /* SITSCR and SIRSCR */ #define SISCR_BRPS GENMASK(12, 8) /* Prescaler Setting (1-32) */ #define SISCR_BRDV GENMASK(2, 0) /* Baud Rate Generator's Division Ratio */ @@ -392,10 +397,11 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, u32 ss, static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, const void *tx_buf, void *rx_buf, - u32 bits, u32 words) + u32 bits, u32 words1, u32 words2) { - u32 dr2 = FIELD_PREP(SIMDR2_BITLEN1, bits - 1) | - FIELD_PREP(SIMDR2_WDLEN1, words - 1); + u32 dr2 = FIELD_PREP(SIMDR2_GRP, words2 ? 1 : 0) | + FIELD_PREP(SIMDR2_BITLEN1, bits - 1) | + FIELD_PREP(SIMDR2_WDLEN1, words1 - 1); if (tx_buf || (p->ctlr->flags & SPI_CONTROLLER_MUST_TX)) sh_msiof_write(p, SITMDR2, dr2); @@ -404,6 +410,15 @@ static void sh_msiof_spi_set_mode_regs(struct sh_msiof_spi_priv *p, if (rx_buf) sh_msiof_write(p, SIRMDR2, dr2); + + if (words2) { + u32 dr3 = FIELD_PREP(SIMDR3_BITLEN2, bits - 1) | + FIELD_PREP(SIMDR3_WDLEN2, words2 - 1); + + sh_msiof_write(p, SITMDR3, dr3); + if (rx_buf) + sh_msiof_write(p, SIRMDR3, dr3); + } } static void sh_msiof_reset_str(struct sh_msiof_spi_priv *p) @@ -712,7 +727,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, sh_msiof_write(p, SIFCTR, 0); /* setup msiof transfer mode registers */ - sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words); + sh_msiof_spi_set_mode_regs(p, tx_buf, rx_buf, bits, words, 0); sh_msiof_write(p, SIIER, SIIER_TEOFE | SIIER_REOFE); /* write tx fifo */ @@ -812,7 +827,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, FIELD_PREP(SIFCTR_RFWM, SIFCTR_RFWM_1)); /* setup msiof transfer mode registers (32-bit words) */ - sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4); + sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4, 0); sh_msiof_write(p, SIIER, ier_bits); From 1ab2c8c45f5c5df6707cc848686e674f4ec5c3d9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:18 +0200 Subject: [PATCH 74/90] spi: sh-msiof: Correct RX FIFO size for R-Car Gen2 According to Renesas Technical Updates TN-RCS-S068A/E, the MSIOF receive FIFOs on R-Car Gen2 SoCs have room for 128 words of 32 bits. Note that this change has no actual impact on the behavior of the driver, as SPI_CONTROLLER_MUST_TX is set, and transfer size is currenty limited to the minimum of the transmit and receive FIFO sizes. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/fd11933f932df81d84f417a21e2179bd4fdcfdc1.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index ea9277ab87f6..8d18a26128d6 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1099,7 +1099,7 @@ static const struct sh_msiof_chipdata rcar_gen2_data = { .bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) | SPI_BPW_MASK(24) | SPI_BPW_MASK(32), .tx_fifo_size = 64, - .rx_fifo_size = 64, + .rx_fifo_size = 128, .ctlr_flags = SPI_CONTROLLER_MUST_TX, .min_div_pow = 0, }; From f669c2827dd691b10fcf4e4c5546869a51d0a46e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:19 +0200 Subject: [PATCH 75/90] spi: sh-msiof: Correct RX FIFO size for R-Car Gen3 According to the R-Car Gen3 Hardware Manual Errata for Rev 0.55 of September 28, 2017, the MSIOF receive FIFOs on R-Car Gen3 SoCs have room for 256 words of 32 bits. Note that this change has no actual impact on the behavior of the driver, as SPI_CONTROLLER_MUST_TX is set, and transfer size is currenty limited to the minimum of the transmit and receive FIFO sizes. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/6f74508ea4681aa0b7c6bf6810eab026725e75a3.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 8d18a26128d6..8afb7c1f64cd 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1108,7 +1108,7 @@ static const struct sh_msiof_chipdata rcar_gen3_data = { .bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) | SPI_BPW_MASK(24) | SPI_BPW_MASK(32), .tx_fifo_size = 64, - .rx_fifo_size = 64, + .rx_fifo_size = 256, .ctlr_flags = SPI_CONTROLLER_MUST_TX, .min_div_pow = 1, }; @@ -1117,7 +1117,7 @@ static const struct sh_msiof_chipdata rcar_r8a7795_data = { .bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) | SPI_BPW_MASK(24) | SPI_BPW_MASK(32), .tx_fifo_size = 64, - .rx_fifo_size = 64, + .rx_fifo_size = 256, .ctlr_flags = SPI_CONTROLLER_MUST_TX, .min_div_pow = 1, .flags = SH_MSIOF_FLAG_FIXED_DTDL_200, From 5b91dc7e3e371e7263b95c0d31151c645d6652a6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:20 +0200 Subject: [PATCH 76/90] spi: sh-msiof: Increase TX FIFO size for R-Car V4H/V4M The MSIOF transmit FIFOs on R-Car V4H and V4M have 256 stages. Add a new family-specific match entry to handle this. Add quirk match entries for older R-Car Gen4 Socs (R-Car V3U and S4-8) that have transmit FIFOs with only 64 stages, just like on R-Car Gen3. Update the (unused) definition of SIFCTR_TFUA for consistency. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/69cb5fc48f034d37484fa127b9864a1971a83417.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 8afb7c1f64cd..26e71fc8890f 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -149,7 +149,7 @@ struct sh_msiof_spi_priv { #define SIFCTR_TFWM_8 5U /* Transfer Request when 8 empty stages */ #define SIFCTR_TFWM_4 6U /* Transfer Request when 4 empty stages */ #define SIFCTR_TFWM_1 7U /* Transfer Request when 1 empty stage */ -#define SIFCTR_TFUA GENMASK(26, 20) /* Transmit FIFO Usable Area */ +#define SIFCTR_TFUA GENMASK(28, 20) /* Transmit FIFO Usable Area */ #define SIFCTR_RFWM GENMASK(15, 13) /* Receive FIFO Watermark */ #define SIFCTR_RFWM_1 0U /* Transfer Request when 1 valid stages */ #define SIFCTR_RFWM_4 1U /* Transfer Request when 4 valid stages */ @@ -1113,6 +1113,15 @@ static const struct sh_msiof_chipdata rcar_gen3_data = { .min_div_pow = 1, }; +static const struct sh_msiof_chipdata rcar_gen4_data = { + .bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(24) | SPI_BPW_MASK(32), + .tx_fifo_size = 256, + .rx_fifo_size = 256, + .ctlr_flags = SPI_CONTROLLER_MUST_TX, + .min_div_pow = 1, +}; + static const struct sh_msiof_chipdata rcar_r8a7795_data = { .bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) | SPI_BPW_MASK(24) | SPI_BPW_MASK(32), @@ -1128,7 +1137,9 @@ static const struct of_device_id sh_msiof_match[] __maybe_unused = { { .compatible = "renesas,rcar-gen2-msiof", .data = &rcar_gen2_data }, { .compatible = "renesas,msiof-r8a7795", .data = &rcar_r8a7795_data }, { .compatible = "renesas,rcar-gen3-msiof", .data = &rcar_gen3_data }, - { .compatible = "renesas,rcar-gen4-msiof", .data = &rcar_gen3_data }, + { .compatible = "renesas,msiof-r8a779a0", .data = &rcar_gen3_data }, + { .compatible = "renesas,msiof-r8a779f0", .data = &rcar_gen3_data }, + { .compatible = "renesas,rcar-gen4-msiof", .data = &rcar_gen4_data }, { .compatible = "renesas,sh-msiof", .data = &sh_data }, /* Deprecated */ { /* sentinel */ } }; From 39d0856f4102b6b61fdc650f90a1d9231e2bab17 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:21 +0200 Subject: [PATCH 77/90] spi: sh-msiof: Simplify BRG's Division Ratio As FIELD_PREP() masks the value to be stored in the field, the Baud Rate Generator's Division Ratio handling can be simplified from a look-up table to a single subtraction. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/e736221942b0381fb53dc64109a1389f7ec5f44a.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 26e71fc8890f..2b8c143b2121 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -112,12 +112,6 @@ struct sh_msiof_spi_priv { /* SITSCR and SIRSCR */ #define SISCR_BRPS GENMASK(12, 8) /* Prescaler Setting (1-32) */ #define SISCR_BRDV GENMASK(2, 0) /* Baud Rate Generator's Division Ratio */ -#define SISCR_BRDV_DIV_2 0U -#define SISCR_BRDV_DIV_4 1U -#define SISCR_BRDV_DIV_8 2U -#define SISCR_BRDV_DIV_16 3U -#define SISCR_BRDV_DIV_32 4U -#define SISCR_BRDV_DIV_1 7U /* SICTR */ #define SICTR_TSCKIZ GENMASK(31, 30) /* Transmit Clock I/O Polarity Select */ @@ -256,11 +250,6 @@ static void sh_msiof_spi_reset_regs(struct sh_msiof_spi_priv *p) 100); } -static const u32 sh_msiof_spi_div_array[] = { - SISCR_BRDV_DIV_1, SISCR_BRDV_DIV_2, SISCR_BRDV_DIV_4, - SISCR_BRDV_DIV_8, SISCR_BRDV_DIV_16, SISCR_BRDV_DIV_32, -}; - static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, struct spi_transfer *t) { @@ -299,7 +288,8 @@ static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, t->effective_speed_hz = parent_rate / (brps << div_pow); - scr = FIELD_PREP(SISCR_BRDV, sh_msiof_spi_div_array[div_pow]) | + /* div_pow == 0 maps to SISCR_BRDV_DIV_1 == all ones */ + scr = FIELD_PREP(SISCR_BRDV, div_pow - 1) | FIELD_PREP(SISCR_BRPS, brps - 1); sh_msiof_write(p, SITSCR, scr); if (!(p->ctlr->flags & SPI_CONTROLLER_MUST_TX)) From acb47aa9b1e2f47121cf2233fcaf998151ab5410 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:22 +0200 Subject: [PATCH 78/90] spi: sh-msiof: Double maximum DMA transfer size using two groups The maximum DMA transfer size is limited by the maximum values that can be written to the word count fields (WDLENx) in the Transmit and Control Data Registers (SITDR2/SIRDR2). As all MSIOF variants support transferring data of multiple (two or four) groups, the maximum size can be doubled by using two groups instead of one, thus reducing setup overhead for very large SPI transfers. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/bad522c76b8d225c195433977b22f95015cf2612.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 2b8c143b2121..4d9a44118e1c 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -767,10 +767,12 @@ static void sh_msiof_dma_complete(void *arg) } static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, - void *rx, unsigned int len) + void *rx, unsigned int len, + unsigned int max_wdlen) { u32 ier_bits = 0; struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL; + unsigned int words1, words2; dma_cookie_t cookie; int ret; @@ -817,7 +819,9 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, FIELD_PREP(SIFCTR_RFWM, SIFCTR_RFWM_1)); /* setup msiof transfer mode registers (32-bit words) */ - sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4, 0); + words1 = min(len / 4, max_wdlen); + words2 = len / 4 - words1; + sh_msiof_spi_set_mode_regs(p, tx, rx, 32, words1, words2); sh_msiof_write(p, SIIER, ier_bits); @@ -969,7 +973,7 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, * DMA supports 32-bit words only, hence pack 8-bit and 16-bit * words, with byte resp. word swapping. */ - unsigned int l = min(round_down(len, 4), max_wdlen * 4); + unsigned int l = min(round_down(len, 4), 2 * max_wdlen * 4); if (bits <= 8) { copy32 = copy_bswap32; @@ -982,7 +986,7 @@ static int sh_msiof_transfer_one(struct spi_controller *ctlr, if (tx_buf) copy32(p->tx_dma_page, tx_buf, l / 4); - ret = sh_msiof_dma_once(p, tx_buf, rx_buf, l); + ret = sh_msiof_dma_once(p, tx_buf, rx_buf, l, max_wdlen); if (ret == -EAGAIN) { dev_warn_once(&p->pdev->dev, "DMA not available, falling back to PIO\n"); From 8f836868d848a9b84c38ae1f8e7366e01927c024 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:23 +0200 Subject: [PATCH 79/90] spi: sh-msiof: Document frame start sync pulse mode Unused, but nice to have it documented. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/3d3acaab4a4125106a0655d28c09c050341c5eeb.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 4d9a44118e1c..5d9d551dbd22 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -86,6 +86,7 @@ struct sh_msiof_spi_priv { /* SITMDR1 and SIRMDR1 */ #define SIMDR1_TRMD BIT(31) /* Transfer Mode (1 = Master mode) */ #define SIMDR1_SYNCMD GENMASK(29, 28) /* SYNC Mode */ +#define SIMDR1_SYNCMD_PULSE 0U /* Frame start sync pulse */ #define SIMDR1_SYNCMD_SPI 2U /* Level mode/SPI */ #define SIMDR1_SYNCMD_LR 3U /* L/R mode */ #define SIMDR1_SYNCAC BIT(25) /* Sync Polarity (1 = Active-low) */ From ec23a899d96f9ee3389abe6c516d09cae2fde5e3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 May 2025 15:32:24 +0200 Subject: [PATCH 80/90] spi: sh-msiof: Move register definitions to Move the MSIOF register and register bit definitions from the MSIOF SPI driver to the existing header file , so they can be shared with the MSIOF I2S driver. Signed-off-by: Geert Uytterhoeven Link: https://patch.msgid.link/066d1086973eb309006258484e9fe8138807e565.1747401908.git.geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 124 ---------------------------------- include/linux/spi/sh_msiof.h | 125 +++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 124 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 5d9d551dbd22..94a867967e02 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -7,7 +7,6 @@ * Copyright (C) 2014-2017 Glider bvba */ -#include #include #include #include @@ -64,129 +63,6 @@ struct sh_msiof_spi_priv { #define MAX_SS 3 /* Maximum number of native chip selects */ -#define SITMDR1 0x00 /* Transmit Mode Register 1 */ -#define SITMDR2 0x04 /* Transmit Mode Register 2 */ -#define SITMDR3 0x08 /* Transmit Mode Register 3 */ -#define SIRMDR1 0x10 /* Receive Mode Register 1 */ -#define SIRMDR2 0x14 /* Receive Mode Register 2 */ -#define SIRMDR3 0x18 /* Receive Mode Register 3 */ -#define SITSCR 0x20 /* Transmit Clock Select Register */ -#define SIRSCR 0x22 /* Receive Clock Select Register (SH, A1, APE6) */ -#define SICTR 0x28 /* Control Register */ -#define SIFCTR 0x30 /* FIFO Control Register */ -#define SISTR 0x40 /* Status Register */ -#define SIIER 0x44 /* Interrupt Enable Register */ -#define SITDR1 0x48 /* Transmit Control Data Register 1 (SH, A1) */ -#define SITDR2 0x4c /* Transmit Control Data Register 2 (SH, A1) */ -#define SITFDR 0x50 /* Transmit FIFO Data Register */ -#define SIRDR1 0x58 /* Receive Control Data Register 1 (SH, A1) */ -#define SIRDR2 0x5c /* Receive Control Data Register 2 (SH, A1) */ -#define SIRFDR 0x60 /* Receive FIFO Data Register */ - -/* SITMDR1 and SIRMDR1 */ -#define SIMDR1_TRMD BIT(31) /* Transfer Mode (1 = Master mode) */ -#define SIMDR1_SYNCMD GENMASK(29, 28) /* SYNC Mode */ -#define SIMDR1_SYNCMD_PULSE 0U /* Frame start sync pulse */ -#define SIMDR1_SYNCMD_SPI 2U /* Level mode/SPI */ -#define SIMDR1_SYNCMD_LR 3U /* L/R mode */ -#define SIMDR1_SYNCAC BIT(25) /* Sync Polarity (1 = Active-low) */ -#define SIMDR1_BITLSB BIT(24) /* MSB/LSB First (1 = LSB first) */ -#define SIMDR1_DTDL GENMASK(22, 20) /* Data Pin Bit Delay for MSIOF_SYNC */ -#define SIMDR1_SYNCDL GENMASK(18, 16) /* Frame Sync Signal Timing Delay */ -#define SIMDR1_FLD GENMASK(3, 2) /* Frame Sync Signal Interval (0-3) */ -#define SIMDR1_XXSTP BIT(0) /* Transmission/Reception Stop on FIFO */ -/* SITMDR1 */ -#define SITMDR1_PCON BIT(30) /* Transfer Signal Connection */ -#define SITMDR1_SYNCCH GENMASK(27, 26) /* Sync Signal Channel Select */ - /* 0=MSIOF_SYNC, 1=MSIOF_SS1, 2=MSIOF_SS2 */ - -/* SITMDR2 and SIRMDR2 */ -#define SIMDR2_GRP GENMASK(31, 30) /* Group Count */ -#define SIMDR2_BITLEN1 GENMASK(28, 24) /* Data Size (8-32 bits) */ -#define SIMDR2_WDLEN1 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ -#define SIMDR2_GRPMASK GENMASK(3, 0) /* Group Output Mask 1-4 (SH, A1) */ - -/* SITMDR3 and SIRMDR3 */ -#define SIMDR3_BITLEN2 GENMASK(28, 24) /* Data Size (8-32 bits) */ -#define SIMDR3_WDLEN2 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ - -/* SITSCR and SIRSCR */ -#define SISCR_BRPS GENMASK(12, 8) /* Prescaler Setting (1-32) */ -#define SISCR_BRDV GENMASK(2, 0) /* Baud Rate Generator's Division Ratio */ - -/* SICTR */ -#define SICTR_TSCKIZ GENMASK(31, 30) /* Transmit Clock I/O Polarity Select */ -#define SICTR_TSCKIZ_SCK BIT(31) /* Disable SCK when TX disabled */ -#define SICTR_TSCKIZ_POL BIT(30) /* Transmit Clock Polarity */ -#define SICTR_RSCKIZ GENMASK(29, 28) /* Receive Clock Polarity Select */ -#define SICTR_RSCKIZ_SCK BIT(29) /* Must match CTR_TSCKIZ_SCK */ -#define SICTR_RSCKIZ_POL BIT(28) /* Receive Clock Polarity */ -#define SICTR_TEDG BIT(27) /* Transmit Timing (1 = falling edge) */ -#define SICTR_REDG BIT(26) /* Receive Timing (1 = falling edge) */ -#define SICTR_TXDIZ GENMASK(23, 22) /* Pin Output When TX is Disabled */ -#define SICTR_TXDIZ_LOW 0U /* 0 */ -#define SICTR_TXDIZ_HIGH 1U /* 1 */ -#define SICTR_TXDIZ_HIZ 2U /* High-impedance */ -#define SICTR_TSCKE BIT(15) /* Transmit Serial Clock Output Enable */ -#define SICTR_TFSE BIT(14) /* Transmit Frame Sync Signal Output Enable */ -#define SICTR_TXE BIT(9) /* Transmit Enable */ -#define SICTR_RXE BIT(8) /* Receive Enable */ -#define SICTR_TXRST BIT(1) /* Transmit Reset */ -#define SICTR_RXRST BIT(0) /* Receive Reset */ - -/* SIFCTR */ -#define SIFCTR_TFWM GENMASK(31, 29) /* Transmit FIFO Watermark */ -#define SIFCTR_TFWM_64 0U /* Transfer Request when 64 empty stages */ -#define SIFCTR_TFWM_32 1U /* Transfer Request when 32 empty stages */ -#define SIFCTR_TFWM_24 2U /* Transfer Request when 24 empty stages */ -#define SIFCTR_TFWM_16 3U /* Transfer Request when 16 empty stages */ -#define SIFCTR_TFWM_12 4U /* Transfer Request when 12 empty stages */ -#define SIFCTR_TFWM_8 5U /* Transfer Request when 8 empty stages */ -#define SIFCTR_TFWM_4 6U /* Transfer Request when 4 empty stages */ -#define SIFCTR_TFWM_1 7U /* Transfer Request when 1 empty stage */ -#define SIFCTR_TFUA GENMASK(28, 20) /* Transmit FIFO Usable Area */ -#define SIFCTR_RFWM GENMASK(15, 13) /* Receive FIFO Watermark */ -#define SIFCTR_RFWM_1 0U /* Transfer Request when 1 valid stages */ -#define SIFCTR_RFWM_4 1U /* Transfer Request when 4 valid stages */ -#define SIFCTR_RFWM_8 2U /* Transfer Request when 8 valid stages */ -#define SIFCTR_RFWM_16 3U /* Transfer Request when 16 valid stages */ -#define SIFCTR_RFWM_32 4U /* Transfer Request when 32 valid stages */ -#define SIFCTR_RFWM_64 5U /* Transfer Request when 64 valid stages */ -#define SIFCTR_RFWM_128 6U /* Transfer Request when 128 valid stages */ -#define SIFCTR_RFWM_256 7U /* Transfer Request when 256 valid stages */ -#define SIFCTR_RFUA GENMASK(12, 4) /* Receive FIFO Usable Area (0x40 = full) */ - -/* SISTR */ -#define SISTR_TFEMP BIT(29) /* Transmit FIFO Empty */ -#define SISTR_TDREQ BIT(28) /* Transmit Data Transfer Request */ -#define SISTR_TEOF BIT(23) /* Frame Transmission End */ -#define SISTR_TFSERR BIT(21) /* Transmit Frame Synchronization Error */ -#define SISTR_TFOVF BIT(20) /* Transmit FIFO Overflow */ -#define SISTR_TFUDF BIT(19) /* Transmit FIFO Underflow */ -#define SISTR_RFFUL BIT(13) /* Receive FIFO Full */ -#define SISTR_RDREQ BIT(12) /* Receive Data Transfer Request */ -#define SISTR_REOF BIT(7) /* Frame Reception End */ -#define SISTR_RFSERR BIT(5) /* Receive Frame Synchronization Error */ -#define SISTR_RFUDF BIT(4) /* Receive FIFO Underflow */ -#define SISTR_RFOVF BIT(3) /* Receive FIFO Overflow */ - -/* SIIER */ -#define SIIER_TDMAE BIT(31) /* Transmit Data DMA Transfer Req. Enable */ -#define SIIER_TFEMPE BIT(29) /* Transmit FIFO Empty Enable */ -#define SIIER_TDREQE BIT(28) /* Transmit Data Transfer Request Enable */ -#define SIIER_TEOFE BIT(23) /* Frame Transmission End Enable */ -#define SIIER_TFSERRE BIT(21) /* Transmit Frame Sync Error Enable */ -#define SIIER_TFOVFE BIT(20) /* Transmit FIFO Overflow Enable */ -#define SIIER_TFUDFE BIT(19) /* Transmit FIFO Underflow Enable */ -#define SIIER_RDMAE BIT(15) /* Receive Data DMA Transfer Req. Enable */ -#define SIIER_RFFULE BIT(13) /* Receive FIFO Full Enable */ -#define SIIER_RDREQE BIT(12) /* Receive Data Transfer Request Enable */ -#define SIIER_REOFE BIT(7) /* Frame Reception End Enable */ -#define SIIER_RFSERRE BIT(5) /* Receive Frame Sync Error Enable */ -#define SIIER_RFUDFE BIT(4) /* Receive FIFO Underflow Enable */ -#define SIIER_RFOVFE BIT(3) /* Receive FIFO Overflow Enable */ - - static u32 sh_msiof_read(struct sh_msiof_spi_priv *p, int reg_offs) { switch (reg_offs) { diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h index f950d280461b..9fbef3fd4056 100644 --- a/include/linux/spi/sh_msiof.h +++ b/include/linux/spi/sh_msiof.h @@ -2,6 +2,131 @@ #ifndef __SPI_SH_MSIOF_H__ #define __SPI_SH_MSIOF_H__ +#include +#include + +#define SITMDR1 0x00 /* Transmit Mode Register 1 */ +#define SITMDR2 0x04 /* Transmit Mode Register 2 */ +#define SITMDR3 0x08 /* Transmit Mode Register 3 */ +#define SIRMDR1 0x10 /* Receive Mode Register 1 */ +#define SIRMDR2 0x14 /* Receive Mode Register 2 */ +#define SIRMDR3 0x18 /* Receive Mode Register 3 */ +#define SITSCR 0x20 /* Transmit Clock Select Register */ +#define SIRSCR 0x22 /* Receive Clock Select Register (SH, A1, APE6) */ +#define SICTR 0x28 /* Control Register */ +#define SIFCTR 0x30 /* FIFO Control Register */ +#define SISTR 0x40 /* Status Register */ +#define SIIER 0x44 /* Interrupt Enable Register */ +#define SITDR1 0x48 /* Transmit Control Data Register 1 (SH, A1) */ +#define SITDR2 0x4c /* Transmit Control Data Register 2 (SH, A1) */ +#define SITFDR 0x50 /* Transmit FIFO Data Register */ +#define SIRDR1 0x58 /* Receive Control Data Register 1 (SH, A1) */ +#define SIRDR2 0x5c /* Receive Control Data Register 2 (SH, A1) */ +#define SIRFDR 0x60 /* Receive FIFO Data Register */ + +/* SITMDR1 and SIRMDR1 */ +#define SIMDR1_TRMD BIT(31) /* Transfer Mode (1 = Master mode) */ +#define SIMDR1_SYNCMD GENMASK(29, 28) /* SYNC Mode */ +#define SIMDR1_SYNCMD_PULSE 0U /* Frame start sync pulse */ +#define SIMDR1_SYNCMD_SPI 2U /* Level mode/SPI */ +#define SIMDR1_SYNCMD_LR 3U /* L/R mode */ +#define SIMDR1_SYNCAC BIT(25) /* Sync Polarity (1 = Active-low) */ +#define SIMDR1_BITLSB BIT(24) /* MSB/LSB First (1 = LSB first) */ +#define SIMDR1_DTDL GENMASK(22, 20) /* Data Pin Bit Delay for MSIOF_SYNC */ +#define SIMDR1_SYNCDL GENMASK(18, 16) /* Frame Sync Signal Timing Delay */ +#define SIMDR1_FLD GENMASK(3, 2) /* Frame Sync Signal Interval (0-3) */ +#define SIMDR1_XXSTP BIT(0) /* Transmission/Reception Stop on FIFO */ +/* SITMDR1 */ +#define SITMDR1_PCON BIT(30) /* Transfer Signal Connection */ +#define SITMDR1_SYNCCH GENMASK(27, 26) /* Sync Signal Channel Select */ + /* 0=MSIOF_SYNC, 1=MSIOF_SS1, 2=MSIOF_SS2 */ + +/* SITMDR2 and SIRMDR2 */ +#define SIMDR2_GRP GENMASK(31, 30) /* Group Count */ +#define SIMDR2_BITLEN1 GENMASK(28, 24) /* Data Size (8-32 bits) */ +#define SIMDR2_WDLEN1 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ +#define SIMDR2_GRPMASK GENMASK(3, 0) /* Group Output Mask 1-4 (SH, A1) */ + +/* SITMDR3 and SIRMDR3 */ +#define SIMDR3_BITLEN2 GENMASK(28, 24) /* Data Size (8-32 bits) */ +#define SIMDR3_WDLEN2 GENMASK(23, 16) /* Word Count (1-64/256 (SH, A1))) */ + +/* SITSCR and SIRSCR */ +#define SISCR_BRPS GENMASK(12, 8) /* Prescaler Setting (1-32) */ +#define SISCR_BRDV GENMASK(2, 0) /* Baud Rate Generator's Division Ratio */ + +/* SICTR */ +#define SICTR_TSCKIZ GENMASK(31, 30) /* Transmit Clock I/O Polarity Select */ +#define SICTR_TSCKIZ_SCK BIT(31) /* Disable SCK when TX disabled */ +#define SICTR_TSCKIZ_POL BIT(30) /* Transmit Clock Polarity */ +#define SICTR_RSCKIZ GENMASK(29, 28) /* Receive Clock Polarity Select */ +#define SICTR_RSCKIZ_SCK BIT(29) /* Must match CTR_TSCKIZ_SCK */ +#define SICTR_RSCKIZ_POL BIT(28) /* Receive Clock Polarity */ +#define SICTR_TEDG BIT(27) /* Transmit Timing (1 = falling edge) */ +#define SICTR_REDG BIT(26) /* Receive Timing (1 = falling edge) */ +#define SICTR_TXDIZ GENMASK(23, 22) /* Pin Output When TX is Disabled */ +#define SICTR_TXDIZ_LOW 0U /* 0 */ +#define SICTR_TXDIZ_HIGH 1U /* 1 */ +#define SICTR_TXDIZ_HIZ 2U /* High-impedance */ +#define SICTR_TSCKE BIT(15) /* Transmit Serial Clock Output Enable */ +#define SICTR_TFSE BIT(14) /* Transmit Frame Sync Signal Output Enable */ +#define SICTR_TXE BIT(9) /* Transmit Enable */ +#define SICTR_RXE BIT(8) /* Receive Enable */ +#define SICTR_TXRST BIT(1) /* Transmit Reset */ +#define SICTR_RXRST BIT(0) /* Receive Reset */ + +/* SIFCTR */ +#define SIFCTR_TFWM GENMASK(31, 29) /* Transmit FIFO Watermark */ +#define SIFCTR_TFWM_64 0U /* Transfer Request when 64 empty stages */ +#define SIFCTR_TFWM_32 1U /* Transfer Request when 32 empty stages */ +#define SIFCTR_TFWM_24 2U /* Transfer Request when 24 empty stages */ +#define SIFCTR_TFWM_16 3U /* Transfer Request when 16 empty stages */ +#define SIFCTR_TFWM_12 4U /* Transfer Request when 12 empty stages */ +#define SIFCTR_TFWM_8 5U /* Transfer Request when 8 empty stages */ +#define SIFCTR_TFWM_4 6U /* Transfer Request when 4 empty stages */ +#define SIFCTR_TFWM_1 7U /* Transfer Request when 1 empty stage */ +#define SIFCTR_TFUA GENMASK(28, 20) /* Transmit FIFO Usable Area */ +#define SIFCTR_RFWM GENMASK(15, 13) /* Receive FIFO Watermark */ +#define SIFCTR_RFWM_1 0U /* Transfer Request when 1 valid stages */ +#define SIFCTR_RFWM_4 1U /* Transfer Request when 4 valid stages */ +#define SIFCTR_RFWM_8 2U /* Transfer Request when 8 valid stages */ +#define SIFCTR_RFWM_16 3U /* Transfer Request when 16 valid stages */ +#define SIFCTR_RFWM_32 4U /* Transfer Request when 32 valid stages */ +#define SIFCTR_RFWM_64 5U /* Transfer Request when 64 valid stages */ +#define SIFCTR_RFWM_128 6U /* Transfer Request when 128 valid stages */ +#define SIFCTR_RFWM_256 7U /* Transfer Request when 256 valid stages */ +#define SIFCTR_RFUA GENMASK(12, 4) /* Receive FIFO Usable Area (0x40 = full) */ + +/* SISTR */ +#define SISTR_TFEMP BIT(29) /* Transmit FIFO Empty */ +#define SISTR_TDREQ BIT(28) /* Transmit Data Transfer Request */ +#define SISTR_TEOF BIT(23) /* Frame Transmission End */ +#define SISTR_TFSERR BIT(21) /* Transmit Frame Synchronization Error */ +#define SISTR_TFOVF BIT(20) /* Transmit FIFO Overflow */ +#define SISTR_TFUDF BIT(19) /* Transmit FIFO Underflow */ +#define SISTR_RFFUL BIT(13) /* Receive FIFO Full */ +#define SISTR_RDREQ BIT(12) /* Receive Data Transfer Request */ +#define SISTR_REOF BIT(7) /* Frame Reception End */ +#define SISTR_RFSERR BIT(5) /* Receive Frame Synchronization Error */ +#define SISTR_RFUDF BIT(4) /* Receive FIFO Underflow */ +#define SISTR_RFOVF BIT(3) /* Receive FIFO Overflow */ + +/* SIIER */ +#define SIIER_TDMAE BIT(31) /* Transmit Data DMA Transfer Req. Enable */ +#define SIIER_TFEMPE BIT(29) /* Transmit FIFO Empty Enable */ +#define SIIER_TDREQE BIT(28) /* Transmit Data Transfer Request Enable */ +#define SIIER_TEOFE BIT(23) /* Frame Transmission End Enable */ +#define SIIER_TFSERRE BIT(21) /* Transmit Frame Sync Error Enable */ +#define SIIER_TFOVFE BIT(20) /* Transmit FIFO Overflow Enable */ +#define SIIER_TFUDFE BIT(19) /* Transmit FIFO Underflow Enable */ +#define SIIER_RDMAE BIT(15) /* Receive Data DMA Transfer Req. Enable */ +#define SIIER_RFFULE BIT(13) /* Receive FIFO Full Enable */ +#define SIIER_RDREQE BIT(12) /* Receive Data Transfer Request Enable */ +#define SIIER_REOFE BIT(7) /* Frame Reception End Enable */ +#define SIIER_RFSERRE BIT(5) /* Receive Frame Sync Error Enable */ +#define SIIER_RFUDFE BIT(4) /* Receive FIFO Underflow Enable */ +#define SIIER_RFOVFE BIT(3) /* Receive FIFO Overflow Enable */ + enum { MSIOF_SPI_HOST, MSIOF_SPI_TARGET, From 8cd079e69dc51e707b0a7ce105b01f6dbb66ddc1 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Fri, 16 May 2025 15:36:56 +0530 Subject: [PATCH 81/90] spi: spi_amd: Remove read{q,b} usage on DMA buffer Add changes to replace the usage of read{q,b} MMIO accessors with direct memory copy logic for reading data from DMA buffer. Co-developed-by: Krishnamoorthi M Signed-off-by: Krishnamoorthi M Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20250516100658.585654-2-Raju.Rangoju@amd.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 90b1f7daa072..52d893d41b12 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -590,15 +590,21 @@ static void amd_spi_mem_data_in(struct amd_spi *amd_spi, * Use index mode otherwise. */ if (amd_spi->version == AMD_HID2_SPI && amd_is_spi_read_cmd(op->cmd.opcode)) { + u64 *dma_buf64 = (u64 *)amd_spi->dma_virt_addr; + u8 *dma_buf; + amd_spi_hiddma_read(amd_spi, op); - for (i = 0; left_data >= 8; i++, left_data -= 8) - *buf_64++ = readq((u8 __iomem *)amd_spi->dma_virt_addr + (i * 8)); + /* Copy data from DMA buffer */ + while (left_data >= 8) { + *buf_64++ = *dma_buf64++; + left_data -= 8; + } buf = (u8 *)buf_64; - for (i = 0; i < left_data; i++) - buf[i] = readb((u8 __iomem *)amd_spi->dma_virt_addr + - (nbytes - left_data + i)); + dma_buf = (u8 *)dma_buf64; + while (left_data--) + *buf++ = *dma_buf++; /* Reset HID RX memory logic */ data = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_CNTRL); From a5733666c775eb852409261d7a6363883d97ff93 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Fri, 16 May 2025 15:36:57 +0530 Subject: [PATCH 82/90] spi: spi_amd: Add HIDDMA basic write support SPI index mode has hardware limitation of transferring only 64 bytes per transaction due to fixed number of FIFO registers. This constraint leads to performance issues when reading/writing data to/from NAND/NOR flash devices, as the controller must issue multiple requests to read/write 64-byte chunks, even if the slave can transfer up to 2 or 4 KB in a single transaction. The AMD HID2 SPI controller supports DMA mode, allowing for reading/writing up to 4 KB of data in a single transaction. The existing spi_amd driver already supports HID2 DMA read operations. This patch introduces changes to implement HID2 DMA single mode basic write support for the HID2 SPI controller. Co-developed-by: Krishnamoorthi M Signed-off-by: Krishnamoorthi M Co-developed-by: Akshata MukundShetty Signed-off-by: Akshata MukundShetty Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20250516100658.585654-3-Raju.Rangoju@amd.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 135 +++++++++++++++++++++++++++++++++++------- 1 file changed, 112 insertions(+), 23 deletions(-) diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 52d893d41b12..02e7fe095a0b 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -54,10 +54,13 @@ #define AMD_SPI_SPD7_MASK GENMASK(13, AMD_SPI_SPD7_SHIFT) #define AMD_SPI_HID2_INPUT_RING_BUF0 0X100 +#define AMD_SPI_HID2_OUTPUT_BUF0 0x140 #define AMD_SPI_HID2_CNTRL 0x150 #define AMD_SPI_HID2_INT_STATUS 0x154 #define AMD_SPI_HID2_CMD_START 0x156 #define AMD_SPI_HID2_INT_MASK 0x158 +#define AMD_SPI_HID2_WRITE_CNTRL0 0x160 +#define AMD_SPI_HID2_WRITE_CNTRL1 0x164 #define AMD_SPI_HID2_READ_CNTRL0 0x170 #define AMD_SPI_HID2_READ_CNTRL1 0x174 #define AMD_SPI_HID2_READ_CNTRL2 0x180 @@ -83,6 +86,10 @@ #define AMD_SPI_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */ #define AMD_SPI_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */ +/* SPINAND write command opcodes */ +#define AMD_SPI_OP_PP 0x02 /* Page program */ +#define AMD_SPI_OP_PP_RANDOM 0x84 /* Page program */ + enum amd_spi_speed { F_66_66MHz, F_33_33MHz, @@ -419,6 +426,17 @@ static inline bool amd_is_spi_read_cmd(const u16 op) } } +static inline bool amd_is_spi_write_cmd(const u16 op) +{ + switch (op) { + case AMD_SPI_OP_PP: + case AMD_SPI_OP_PP_RANDOM: + return true; + default: + return false; + } +} + static bool amd_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { @@ -429,7 +447,7 @@ static bool amd_spi_supports_op(struct spi_mem *mem, return false; /* AMD SPI controllers support quad mode only for read operations */ - if (amd_is_spi_read_cmd(op->cmd.opcode)) { + if (amd_is_spi_read_cmd(op->cmd.opcode) || amd_is_spi_write_cmd(op->cmd.opcode)) { if (op->data.buswidth > 4) return false; @@ -438,7 +456,8 @@ static bool amd_spi_supports_op(struct spi_mem *mem, * doesn't support 4-byte address commands. */ if (amd_spi->version == AMD_HID2_SPI) { - if (amd_is_spi_read_cmd_4b(op->cmd.opcode) || + if ((amd_is_spi_read_cmd_4b(op->cmd.opcode) || + amd_is_spi_write_cmd(op->cmd.opcode)) && op->data.nbytes > AMD_SPI_HID2_DMA_SIZE) return false; } else if (op->data.nbytes > AMD_SPI_MAX_DATA) { @@ -464,7 +483,8 @@ static int amd_spi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) * controller index mode supports maximum of 64 bytes in a single * transaction. */ - if (amd_spi->version == AMD_HID2_SPI && amd_is_spi_read_cmd(op->cmd.opcode)) + if (amd_spi->version == AMD_HID2_SPI && (amd_is_spi_read_cmd(op->cmd.opcode) || + amd_is_spi_write_cmd(op->cmd.opcode))) op->data.nbytes = clamp_val(op->data.nbytes, 0, AMD_SPI_HID2_DMA_SIZE); else op->data.nbytes = clamp_val(op->data.nbytes, 0, AMD_SPI_MAX_DATA); @@ -488,32 +508,96 @@ static void amd_spi_set_addr(struct amd_spi *amd_spi, } } +static void amd_spi_hiddma_write(struct amd_spi *amd_spi, const struct spi_mem_op *op) +{ + u16 hid_cmd_start, val; + u32 hid_regval; + + /* + * Program the HID2 output Buffer0. 4k aligned buf_memory_addr[31:12], + * buf_size[2:0]. + */ + hid_regval = amd_spi->phy_dma_buf | BIT(0); + amd_spi_writereg32(amd_spi, AMD_SPI_HID2_OUTPUT_BUF0, hid_regval); + + /* Program max write length in hid2_write_control1 register */ + hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_WRITE_CNTRL1); + hid_regval = (hid_regval & ~GENMASK(15, 0)) | ((op->data.nbytes) + 3); + amd_spi_writereg32(amd_spi, AMD_SPI_HID2_WRITE_CNTRL1, hid_regval); + + /* Set cmd start bit in hid2_cmd_start register to trigger HID basic write operation */ + hid_cmd_start = amd_spi_readreg16(amd_spi, AMD_SPI_HID2_CMD_START); + amd_spi_writereg16(amd_spi, AMD_SPI_HID2_CMD_START, (hid_cmd_start | BIT(2))); + + /* Check interrupt status of HIDDMA basic write operation in hid2_int_status register */ + readw_poll_timeout(amd_spi->io_remap_addr + AMD_SPI_HID2_INT_STATUS, val, + (val & BIT(2)), AMD_SPI_IO_SLEEP_US, AMD_SPI_IO_TIMEOUT_US); + + /* Clear the interrupts by writing to hid2_int_status register */ + val = amd_spi_readreg16(amd_spi, AMD_SPI_HID2_INT_STATUS); + amd_spi_writereg16(amd_spi, AMD_SPI_HID2_INT_STATUS, val); +} + static void amd_spi_mem_data_out(struct amd_spi *amd_spi, const struct spi_mem_op *op) { int base_addr = AMD_SPI_FIFO_BASE + op->addr.nbytes; u64 *buf_64 = (u64 *)op->data.buf.out; + u64 addr_val = op->addr.val; u32 nbytes = op->data.nbytes; u32 left_data = nbytes; u8 *buf; int i; - amd_spi_set_opcode(amd_spi, op->cmd.opcode); - amd_spi_set_addr(amd_spi, op); + /* + * Condition for using HID write mode. Only for writing complete page data, use HID write. + * Use index mode otherwise. + */ + if (amd_spi->version == AMD_HID2_SPI && amd_is_spi_write_cmd(op->cmd.opcode)) { + u64 *dma_buf64 = (u64 *)(amd_spi->dma_virt_addr + op->addr.nbytes + op->cmd.nbytes); + u8 *dma_buf = (u8 *)amd_spi->dma_virt_addr; - for (i = 0; left_data >= 8; i++, left_data -= 8) - amd_spi_writereg64(amd_spi, base_addr + op->dummy.nbytes + (i * 8), *buf_64++); + /* Copy opcode and address to DMA buffer */ + *dma_buf = op->cmd.opcode; - buf = (u8 *)buf_64; - for (i = 0; i < left_data; i++) { - amd_spi_writereg8(amd_spi, base_addr + op->dummy.nbytes + nbytes + i - left_data, - buf[i]); + dma_buf = (u8 *)dma_buf64; + for (i = 0; i < op->addr.nbytes; i++) { + *--dma_buf = addr_val & GENMASK(7, 0); + addr_val >>= 8; + } + + /* Copy data to DMA buffer */ + while (left_data >= 8) { + *dma_buf64++ = *buf_64++; + left_data -= 8; + } + + buf = (u8 *)buf_64; + dma_buf = (u8 *)dma_buf64; + while (left_data--) + *dma_buf++ = *buf++; + + amd_spi_hiddma_write(amd_spi, op); + } else { + amd_spi_set_opcode(amd_spi, op->cmd.opcode); + amd_spi_set_addr(amd_spi, op); + + for (i = 0; left_data >= 8; i++, left_data -= 8) + amd_spi_writereg64(amd_spi, base_addr + op->dummy.nbytes + (i * 8), + *buf_64++); + + buf = (u8 *)buf_64; + for (i = 0; i < left_data; i++) { + amd_spi_writereg8(amd_spi, + base_addr + op->dummy.nbytes + nbytes + i - left_data, + buf[i]); + } + + amd_spi_set_tx_count(amd_spi, op->addr.nbytes + op->data.nbytes); + amd_spi_set_rx_count(amd_spi, 0); + amd_spi_clear_fifo_ptr(amd_spi); + amd_spi_execute_opcode(amd_spi); } - - amd_spi_set_tx_count(amd_spi, op->addr.nbytes + op->data.nbytes); - amd_spi_set_rx_count(amd_spi, 0); - amd_spi_clear_fifo_ptr(amd_spi); - amd_spi_execute_opcode(amd_spi); } static void amd_spi_hiddma_read(struct amd_spi *amd_spi, const struct spi_mem_op *op) @@ -708,23 +792,28 @@ static int amd_spi_setup_hiddma(struct amd_spi *amd_spi, struct device *dev) { u32 hid_regval; - /* Allocate DMA buffer to use for HID basic read operation */ - amd_spi->dma_virt_addr = dma_alloc_coherent(dev, AMD_SPI_HID2_DMA_SIZE, - &amd_spi->phy_dma_buf, GFP_KERNEL); + /* Allocate DMA buffer to use for HID basic read and write operations. For write + * operations, the DMA buffer should include the opcode, address bytes and dummy + * bytes(if any) in addition to the data bytes. Additionally, the hardware requires + * that the buffer address be 4K aligned. So, allocate DMA buffer of size + * 2 * AMD_SPI_HID2_DMA_SIZE. + */ + amd_spi->dma_virt_addr = dmam_alloc_coherent(dev, AMD_SPI_HID2_DMA_SIZE * 2, + &amd_spi->phy_dma_buf, GFP_KERNEL); if (!amd_spi->dma_virt_addr) return -ENOMEM; /* * Enable interrupts and set mask bits in hid2_int_mask register to generate interrupt - * properly for HIDDMA basic read operations. + * properly for HIDDMA basic read and write operations. */ hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_INT_MASK); - hid_regval = (hid_regval & GENMASK(31, 8)) | BIT(19); + hid_regval = (hid_regval & GENMASK(31, 8)) | BIT(18) | BIT(19); amd_spi_writereg32(amd_spi, AMD_SPI_HID2_INT_MASK, hid_regval); - /* Configure buffer unit(4k) in hid2_control register */ + /* Configure buffer unit(4k) and write threshold in hid2_control register */ hid_regval = amd_spi_readreg32(amd_spi, AMD_SPI_HID2_CNTRL); - amd_spi_writereg32(amd_spi, AMD_SPI_HID2_CNTRL, hid_regval & ~BIT(3)); + amd_spi_writereg32(amd_spi, AMD_SPI_HID2_CNTRL, (hid_regval | GENMASK(13, 12)) & ~BIT(3)); return 0; } From dbb79974193a2932e828ebbd216efb428c81dc63 Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Fri, 16 May 2025 15:36:58 +0530 Subject: [PATCH 83/90] spi: spi_amd: Update Kconfig dependencies Add X86 and SPI_MEM as dependencies for the spi_amd driver to ensure it is built only on relevant platforms and with the required SPI memory framework. Co-developed-by: Akshata MukundShetty Signed-off-by: Akshata MukundShetty Signed-off-by: Raju Rangoju Link: https://patch.msgid.link/20250516100658.585654-4-Raju.Rangoju@amd.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b03a7005f1bd..c51da3fc3604 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1267,7 +1267,8 @@ config SPI_ZYNQMP_GQSPI config SPI_AMD tristate "AMD SPI controller" depends on PCI - depends on SPI_MASTER || COMPILE_TEST + depends on SPI_MASTER || X86 || COMPILE_TEST + depends on SPI_MEM help Enables SPI controller driver for AMD SoC. From 70e5f38e734572ca5a56cff48cf01a0f31917099 Mon Sep 17 00:00:00 2001 From: Chukun Pan Date: Tue, 20 May 2025 18:01:01 +0800 Subject: [PATCH 84/90] spi: dt-bindings: Add rk3528-spi compatible This adds a compatible string for the SPI controller on RK3528. Signed-off-by: Chukun Pan Acked-by: Conor Dooley Link: https://patch.msgid.link/20250520100102.1226725-2-amadeus@jmu.edu.cn Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-rockchip.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.yaml b/Documentation/devicetree/bindings/spi/spi-rockchip.yaml index 104f5ffdd04e..748faf7f7081 100644 --- a/Documentation/devicetree/bindings/spi/spi-rockchip.yaml +++ b/Documentation/devicetree/bindings/spi/spi-rockchip.yaml @@ -34,6 +34,7 @@ properties: - rockchip,rk3328-spi - rockchip,rk3368-spi - rockchip,rk3399-spi + - rockchip,rk3528-spi - rockchip,rk3562-spi - rockchip,rk3568-spi - rockchip,rk3576-spi From 4026c6b51cb9ffd1eea2206191552f8aa3cb55ea Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 14 May 2025 12:16:38 +0200 Subject: [PATCH 85/90] spi: spi-qpic-snand: reuse qcom_spi_check_raw_flash_errors() The qcom_spi_check_raw_flash_errors() function can be used to verify the flash status after raw operations. Move the function slightly up in the code and change the qcom_spi_read_last_cw() function to call it instead of using an open coded implementation of the same check. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250514-qpic-snand-error-check-v1-1-c0ebd3aae72a@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 40 +++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 7207bbb57802..a90829e18d3f 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -524,6 +524,22 @@ static void qcom_spi_config_single_cw_page_read(struct qcom_nand_controller *sna qcom_read_reg_dma(snandc, NAND_FLASH_STATUS, 1, 0); } +static int qcom_spi_check_raw_flash_errors(struct qcom_nand_controller *snandc, int cw_cnt) +{ + int i; + + qcom_nandc_dev_to_mem(snandc, true); + + for (i = 0; i < cw_cnt; i++) { + u32 flash = le32_to_cpu(snandc->reg_read_buf[i]); + + if (flash & (FS_OP_ERR | FS_MPU_ERR)) + return -EIO; + } + + return 0; +} + static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) { @@ -569,11 +585,9 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, return ret; } - qcom_nandc_dev_to_mem(snandc, true); - u32 flash = le32_to_cpu(snandc->reg_read_buf[0]); - - if (flash & (FS_OP_ERR | FS_MPU_ERR)) - return -EIO; + ret = qcom_spi_check_raw_flash_errors(snandc, 1); + if (ret) + return ret; bbpos = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1); @@ -654,22 +668,6 @@ static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_bu return 0; } -static int qcom_spi_check_raw_flash_errors(struct qcom_nand_controller *snandc, int cw_cnt) -{ - int i; - - qcom_nandc_dev_to_mem(snandc, true); - - for (i = 0; i < cw_cnt; i++) { - u32 flash = le32_to_cpu(snandc->reg_read_buf[i]); - - if (flash & (FS_OP_ERR | FS_MPU_ERR)) - return -EIO; - } - - return 0; -} - static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_buf, u8 *oob_buf, int cw) { From 477d16c0919e82a7f5f673a6e3ae39a17c773037 Mon Sep 17 00:00:00 2001 From: Faraz Ata Date: Wed, 21 May 2025 14:13:24 +0530 Subject: [PATCH 86/90] dt-bindings: spi: samsung: add exynosautov920-spi compatible Add "samsung,exynosautov920-spi" dedicated compatible for SPI found in ExynosAutov920 SoC. Signed-off-by: Faraz Ata Link: https://patch.msgid.link/20250521084324.2759530-1-faraz.ata@samsung.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/samsung,spi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/samsung,spi.yaml b/Documentation/devicetree/bindings/spi/samsung,spi.yaml index 3c206a64d60a..fe298d47b1a9 100644 --- a/Documentation/devicetree/bindings/spi/samsung,spi.yaml +++ b/Documentation/devicetree/bindings/spi/samsung,spi.yaml @@ -29,6 +29,7 @@ properties: - items: - enum: - samsung,exynos8895-spi + - samsung,exynosautov920-spi - const: samsung,exynos850-spi - const: samsung,exynos7-spi deprecated: true From 0f529570ecaf99244dc86b8af13618f0d07b0e44 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 20 May 2025 21:08:56 +0200 Subject: [PATCH 87/90] spi: spi-qpic-snand: remove superfluous parameters of qcom_spi_check_error() The qcom_spi_check_error() function determines the errors of a previous page read operation solely by using the cached register values in the register read buffer. The data pointed by the 'data_buf' and the 'oob_buf' parameters are not used for that at all. Remove the superfluous parameters of the function along with the related local variables to simplify the code. Also, remove the variables from the caller functions which became unused due to the change. Note: Althought the similar parse_read_errors() function in the 'qcom_nand' driver has the same parameters, but that function passes down the pointers to check_for_erased_page() at the end of the function. It is not clear, that a similar call is missing here, or the superfluous parameters are simply leftovers of the development process. Nevertheless, if additional code is needed, the parameters can be added back later. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250520-qpic-snand-superfluous-params-v1-1-86dd4963e90f@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index a90829e18d3f..17bcd12f6cb9 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -601,7 +601,7 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc, return ret; } -static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_buf, u8 *oob_buf) +static int qcom_spi_check_error(struct qcom_nand_controller *snandc) { struct snandc_read_status *buf; struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; @@ -618,15 +618,6 @@ static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_bu for (i = 0; i < num_cw; i++, buf++) { u32 flash, buffer, erased_cw; - int data_len, oob_len; - - if (i == (num_cw - 1)) { - data_len = NANDC_STEP_SIZE - ((num_cw - 1) << 2); - oob_len = num_cw << 2; - } else { - data_len = ecc_cfg->cw_data; - oob_len = 0; - } flash = le32_to_cpu(buf->snandc_flash); buffer = le32_to_cpu(buf->snandc_buffer); @@ -650,11 +641,6 @@ static int qcom_spi_check_error(struct qcom_nand_controller *snandc, u8 *data_bu snandc->qspi->ecc_stats.corrected += stat; max_bitflips = max(max_bitflips, stat); } - - if (data_buf) - data_buf += data_len; - if (oob_buf) - oob_buf += oob_len + ecc_cfg->bytes; } if (flash_op_err) @@ -792,15 +778,12 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) { struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; - u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; + u8 *data_buf = NULL, *oob_buf = NULL; int ret, i; u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; data_buf = op->data.buf.in; - data_buf_start = data_buf; - oob_buf = snandc->qspi->oob_buf; - oob_buf_start = oob_buf; snandc->buf_count = 0; snandc->buf_start = 0; @@ -881,21 +864,18 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc, return ret; } - return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); + return qcom_spi_check_error(snandc); } static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc, const struct spi_mem_op *op) { struct qpic_ecc *ecc_cfg = snandc->qspi->ecc; - u8 *data_buf = NULL, *data_buf_start, *oob_buf = NULL, *oob_buf_start; + u8 *oob_buf = NULL; int ret, i; u32 cfg0, cfg1, ecc_bch_cfg, num_cw = snandc->qspi->num_cw; oob_buf = op->data.buf.in; - oob_buf_start = oob_buf; - - data_buf_start = data_buf; snandc->buf_count = 0; snandc->buf_start = 0; @@ -963,7 +943,7 @@ static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc, return ret; } - return qcom_spi_check_error(snandc, data_buf_start, oob_buf_start); + return qcom_spi_check_error(snandc); } static int qcom_spi_read_page(struct qcom_nand_controller *snandc, From 2712a7d362904d0c4283ae91fac8cea6ecd6f9c2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 May 2025 12:57:26 +0200 Subject: [PATCH 88/90] spi: gpio: fix const issue in spi_to_spi_gpio() While the struct spi_device * passed into spi_to_spi_gpio() is a const one, the struct spi_bitbang * that is retrieved from the controller field in the spi_device is NOT a const pointer, as it is coming from the spi_controller_get_devdata() call, and then passed to container_of() which would strip off the const attribute for no good reason (i.e. if a const pointer is passed to container_of() it still is const coming out). Fix this all up by properly declaring the struct spi_bitbang * as not const. Cc: Mark Brown Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2025052225-scallion-ritzy-dbbd@gregkh Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 405deb6677c1..ea5f1b10b79e 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -46,7 +46,7 @@ struct spi_gpio { static inline struct spi_gpio *__pure spi_to_spi_gpio(const struct spi_device *spi) { - const struct spi_bitbang *bang; + struct spi_bitbang *bang; struct spi_gpio *spi_gpio; bang = spi_controller_get_devdata(spi->controller); From e7f3d11567c2c79c4342791ba91c500b434ce147 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 May 2025 12:59:14 +0200 Subject: [PATCH 89/90] spi: loopback-test: fix up const pointer issue in rx_ranges_cmp() When a list head is a const pointer, the list entry for that head also must remain a const pointer, otherwise we are just "throwing it away" for no good reason. Fix this up by properly marking these structures as const. Cc: Mark Brown Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2025052213-semifinal-sublevel-d631@gregkh Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 369cdb707fad..f02d56fd917d 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -494,8 +494,8 @@ struct rx_ranges { static int rx_ranges_cmp(void *priv, const struct list_head *a, const struct list_head *b) { - struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list); - struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list); + const struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list); + const struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list); if (rx_a->start > rx_b->start) return 1; From 72b17676d3683040a0add8988ec051a2a5adafd7 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 15 May 2025 22:13:29 +0200 Subject: [PATCH 90/90] spi: spi-qpic-snand: return early on error from qcom_spi_io_op() When submitting of the descriptors fails, it is quite likely that the register read buffer contains no valid data. Even if the data is valid the function returns with an error code anyway. Change the code to return early if qcom_submit_descs() fails to avoid superfluously copying possibly invalid data. Also change the return statement at the end of the function to use zero value to indicate success obviusly. Signed-off-by: Gabor Juhos Link: https://patch.msgid.link/20250515-qpic-snand-early-error-v1-1-681c87611213@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 17bcd12f6cb9..2c9373226dd8 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -1383,8 +1383,10 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_ } ret = qcom_submit_descs(snandc); - if (ret) + if (ret) { dev_err(snandc->dev, "failure in submitting descriptor for:%d\n", opcode); + return ret; + } if (copy) { qcom_nandc_dev_to_mem(snandc, true); @@ -1398,7 +1400,7 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_ memcpy(op->data.buf.in, &val, snandc->buf_count); } - return ret; + return 0; } static bool qcom_spi_is_page_op(const struct spi_mem_op *op)