From 8f4b3d2f653937956fdc68b665c621dfa3cf2fa7 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong <jiapeng.chong@linux.alibaba.com> Date: Mon, 17 Oct 2022 17:21:41 +0800 Subject: [PATCH 01/66] spi: microchip-core: Remove the unused function mchp_corespi_enable() The function mchp_corespi_enable() is defined in the spi-microchip-core.c file, but not called elsewhere, so delete this unused function. drivers/spi/spi-microchip-core.c:122:20: warning: unused function 'mchp_corespi_enable'. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2418 Reported-by: Abaci Robot <abaci@linux.alibaba.com> Signed-off-by: Jiapeng Chong <jiapeng.chong@linux.alibaba.com> Reviewed-by: Conor Dooley <conor.dooley@microchip.com> Link: https://lore.kernel.org/r/20221017092141.9789-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-microchip-core.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/spi/spi-microchip-core.c b/drivers/spi/spi-microchip-core.c index d352844c798c9..aeaa1da88f39e 100644 --- a/drivers/spi/spi-microchip-core.c +++ b/drivers/spi/spi-microchip-core.c @@ -119,15 +119,6 @@ static inline void mchp_corespi_write(struct mchp_corespi *spi, unsigned int reg writel(val, spi->regs + reg); } -static inline void mchp_corespi_enable(struct mchp_corespi *spi) -{ - u32 control = mchp_corespi_read(spi, REG_CONTROL); - - control |= CONTROL_ENABLE; - - mchp_corespi_write(spi, REG_CONTROL, control); -} - static inline void mchp_corespi_disable(struct mchp_corespi *spi) { u32 control = mchp_corespi_read(spi, REG_CONTROL); From 1cc0cbea7167af524a7f7b2d0d2f19f7a324e807 Mon Sep 17 00:00:00 2001 From: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Date: Thu, 6 Oct 2022 10:35:13 +0530 Subject: [PATCH 02/66] spi: microchip: pci1xxxx: Add driver for SPI controller of PCI1XXXX PCIe switch Microchip pci1xxxx is a PCIe switch with a multi-function endpoint on one of its downstream ports. SPI is one of the functions in the multi-function endpoint. This function has 2 SPI masters, operates at a maximum frequency of 30 MHz and supports 7 client devices per master. This patch adds complete functionality to the SPI function except for suspend and resume. Signed-off-by: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Link: https://lore.kernel.org/r/20221006050514.115564-2-tharunkumar.pasumarthi@microchip.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-pci1xxxx.c | 397 +++++++++++++++++++++++++++++++++++++ 3 files changed, 407 insertions(+) create mode 100644 drivers/spi/spi-pci1xxxx.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d1bb62f7368b7..cb7e3a8ef3a5d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -710,6 +710,15 @@ config SPI_ORION This enables using the SPI master controller on the Orion and MVEBU chips. +config SPI_PCI1XXXX + tristate "PCI1XXXX SPI Bus support" + depends on PCI + help + Say "yes" to Enable the SPI Bus support for the PCI1xxxx card + This is a PCI to SPI Bus driver + This driver can be built as module. If so, the module will be + called as spi-pci1xxxx. + config SPI_PIC32 tristate "Microchip PIC32 series SPI" depends on MACH_PIC32 || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4b34e855c8412..60d0b2f611f1d 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -94,6 +94,7 @@ obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o obj-$(CONFIG_SPI_ORION) += spi-orion.o +obj-$(CONFIG_SPI_PCI1XXXX) += spi-pci1xxxx.o obj-$(CONFIG_SPI_PIC32) += spi-pic32.o obj-$(CONFIG_SPI_PIC32_SQI) += spi-pic32-sqi.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c new file mode 100644 index 0000000000000..958503a4d911e --- /dev/null +++ b/drivers/spi/spi-pci1xxxx.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +// PCI1xxxx SPI driver +// Copyright (C) 2022 Microchip Technology Inc. +// Authors: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> +// Kumaravel Thiagarajan <Kumaravel.Thiagarajan@microchip.com> + + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> + +#define DRV_NAME "spi-pci1xxxx" + +#define SYS_FREQ_DEFAULT (62500000) + +#define PCI1XXXX_SPI_MAX_CLOCK_HZ (30000000) +#define PCI1XXXX_SPI_CLK_20MHZ (20000000) +#define PCI1XXXX_SPI_CLK_15MHZ (15000000) +#define PCI1XXXX_SPI_CLK_12MHZ (12000000) +#define PCI1XXXX_SPI_CLK_10MHZ (10000000) +#define PCI1XXXX_SPI_MIN_CLOCK_HZ (2000000) + +#define PCI1XXXX_SPI_BUFFER_SIZE (320) + +#define SPI_MST_CTL_DEVSEL_MASK (GENMASK(27, 25)) +#define SPI_MST_CTL_CMD_LEN_MASK (GENMASK(16, 8)) +#define SPI_MST_CTL_SPEED_MASK (GENMASK(7, 5)) +#define SPI_MSI_VECTOR_SEL_MASK (GENMASK(4, 4)) + +#define SPI_MST_CTL_FORCE_CE (BIT(4)) +#define SPI_MST_CTL_MODE_SEL (BIT(2)) +#define SPI_MST_CTL_GO (BIT(0)) + +#define SPI_MST1_ADDR_BASE (0x800) + +/* x refers to SPI Host Controller HW instance id in the below macros - 0 or 1 */ + +#define SPI_MST_CMD_BUF_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x00) +#define SPI_MST_RSP_BUF_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x200) +#define SPI_MST_CTL_REG_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x400) +#define SPI_MST_EVENT_REG_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x420) +#define SPI_MST_EVENT_MASK_REG_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x424) +#define SPI_MST_PAD_CTL_REG_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x460) +#define SPIALERT_MST_DB_REG_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x464) +#define SPIALERT_MST_VAL_REG_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x468) +#define SPI_PCI_CTRL_REG_OFFSET(x) (((x) * SPI_MST1_ADDR_BASE) + 0x480) + +#define PCI1XXXX_IRQ_FLAGS (IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE) +#define SPI_MAX_DATA_LEN 320 + +#define PCI1XXXX_SPI_TIMEOUT (msecs_to_jiffies(100)) + +#define SPI_INTR BIT(8) +#define SPI_FORCE_CE BIT(4) + +#define SPI_CHIP_SEL_COUNT 7 +#define VENDOR_ID_MCHP 0x1055 + +struct pci1xxxx_spi_internal { + u8 hw_inst; + bool spi_xfer_in_progress; + int irq; + struct completion spi_xfer_done; + struct spi_master *spi_host; + struct pci1xxxx_spi *parent; + struct { + unsigned int dev_sel : 3; + unsigned int msi_vector_sel : 1; + } prev_val; +}; + +struct pci1xxxx_spi { + struct pci_dev *dev; + u8 total_hw_instances; + void __iomem *reg_base; + struct pci1xxxx_spi_internal *spi_int[]; +}; + +static const struct pci_device_id pci1xxxx_spi_pci_id_table[] = { + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa004, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa014, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa024, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa034, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0001), 0, 0, 0x02}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0002), 0, 0, 0x01}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, 0x0003), 0, 0, 0x11}, + { PCI_DEVICE_SUB(VENDOR_ID_MCHP, 0xa044, PCI_ANY_ID, PCI_ANY_ID), 0, 0, 0x01}, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, pci1xxxx_spi_pci_id_table); + +static void pci1xxxx_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi->controller); + struct pci1xxxx_spi *par = p->parent; + u32 regval; + + /* Set the DEV_SEL bits of the SPI_MST_CTL_REG */ + regval = readl(par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + if (enable) { + regval &= ~SPI_MST_CTL_DEVSEL_MASK; + regval |= (spi->chip_select << 25); + writel(regval, + par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + } else { + regval &= ~(spi->chip_select << 25); + writel(regval, + par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + + } +} + +static u8 pci1xxxx_get_clock_div(u32 hz) +{ + u8 val = 0; + + if (hz >= PCI1XXXX_SPI_MAX_CLOCK_HZ) + val = 2; + else if ((hz < PCI1XXXX_SPI_MAX_CLOCK_HZ) && (hz >= PCI1XXXX_SPI_CLK_20MHZ)) + val = 3; + else if ((hz < PCI1XXXX_SPI_CLK_20MHZ) && (hz >= PCI1XXXX_SPI_CLK_15MHZ)) + val = 4; + else if ((hz < PCI1XXXX_SPI_CLK_15MHZ) && (hz >= PCI1XXXX_SPI_CLK_12MHZ)) + val = 5; + else if ((hz < PCI1XXXX_SPI_CLK_12MHZ) && (hz >= PCI1XXXX_SPI_CLK_10MHZ)) + val = 6; + else if ((hz < PCI1XXXX_SPI_CLK_10MHZ) && (hz >= PCI1XXXX_SPI_MIN_CLOCK_HZ)) + val = 7; + else + val = 2; + + return val; +} + +static int pci1xxxx_spi_transfer_one(struct spi_controller *spi_ctlr, + struct spi_device *spi, struct spi_transfer *xfer) +{ + struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi_ctlr); + int mode, len, loop_iter, transfer_len; + struct pci1xxxx_spi *par = p->parent; + unsigned long bytes_transfered; + unsigned long bytes_recvd; + unsigned long loop_count; + u8 *rx_buf, result; + const u8 *tx_buf; + u32 regval; + u8 clkdiv; + + p->spi_xfer_in_progress = true; + mode = spi->mode; + clkdiv = pci1xxxx_get_clock_div(xfer->speed_hz); + tx_buf = xfer->tx_buf; + rx_buf = xfer->rx_buf; + transfer_len = xfer->len; + regval = readl(par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); + writel(regval, par->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); + + if (tx_buf) { + bytes_transfered = 0; + bytes_recvd = 0; + loop_count = transfer_len / SPI_MAX_DATA_LEN; + if (transfer_len % SPI_MAX_DATA_LEN != 0) + loop_count += 1; + + for (loop_iter = 0; loop_iter < loop_count; loop_iter++) { + len = SPI_MAX_DATA_LEN; + if ((transfer_len % SPI_MAX_DATA_LEN != 0) && + (loop_iter == loop_count - 1)) + len = transfer_len % SPI_MAX_DATA_LEN; + + reinit_completion(&p->spi_xfer_done); + memcpy_toio(par->reg_base + SPI_MST_CMD_BUF_OFFSET(p->hw_inst), + &tx_buf[bytes_transfered], len); + bytes_transfered += len; + regval = readl(par->reg_base + + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + regval &= ~(SPI_MST_CTL_MODE_SEL | SPI_MST_CTL_CMD_LEN_MASK | + SPI_MST_CTL_SPEED_MASK); + + if (mode == SPI_MODE_3) + regval |= SPI_MST_CTL_MODE_SEL; + else + regval &= ~SPI_MST_CTL_MODE_SEL; + + regval |= ((clkdiv << 5) | SPI_FORCE_CE | (len << 8)); + regval &= ~SPI_MST_CTL_CMD_LEN_MASK; + writel(regval, par->reg_base + + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + regval = readl(par->reg_base + + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + regval |= SPI_MST_CTL_GO; + writel(regval, par->reg_base + + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + + /* Wait for DMA_TERM interrupt */ + result = wait_for_completion_timeout(&p->spi_xfer_done, + PCI1XXXX_SPI_TIMEOUT); + if (!result) + return -ETIMEDOUT; + + if (rx_buf) { + memcpy_fromio(&rx_buf[bytes_recvd], par->reg_base + + SPI_MST_RSP_BUF_OFFSET(p->hw_inst), len); + bytes_recvd += len; + } + } + } + + regval = readl(par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + regval &= ~SPI_FORCE_CE; + writel(regval, par->reg_base + SPI_MST_CTL_REG_OFFSET(p->hw_inst)); + p->spi_xfer_in_progress = false; + + return 0; +} + +static irqreturn_t pci1xxxx_spi_isr(int irq, void *dev) +{ + struct pci1xxxx_spi_internal *p = dev; + irqreturn_t spi_int_fired = IRQ_NONE; + u32 regval; + + /* Clear the SPI GO_BIT Interrupt */ + regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); + if (regval & SPI_INTR) { + /* Clear xfer_done */ + complete(&p->spi_xfer_done); + spi_int_fired = IRQ_HANDLED; + } + + writel(regval, p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst)); + + return spi_int_fired; +} + +static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + u8 hw_inst_cnt, iter, start, only_sec_inst; + struct pci1xxxx_spi_internal *spi_sub_ptr; + struct device *dev = &pdev->dev; + struct pci1xxxx_spi *spi_bus; + struct spi_master *spi_host; + u32 regval; + int ret; + + hw_inst_cnt = ent->driver_data & 0x0f; + start = (ent->driver_data & 0xf0) >> 4; + if (start == 1) + only_sec_inst = 1; + else + only_sec_inst = 0; + + spi_bus = devm_kzalloc(&pdev->dev, + struct_size(spi_bus, spi_int, hw_inst_cnt), + GFP_KERNEL); + if (!spi_bus) + return -ENOMEM; + + spi_bus->dev = pdev; + spi_bus->total_hw_instances = hw_inst_cnt; + pci_set_master(pdev); + + for (iter = 0; iter < hw_inst_cnt; iter++) { + spi_bus->spi_int[iter] = devm_kzalloc(&pdev->dev, + sizeof(struct pci1xxxx_spi_internal), + GFP_KERNEL); + spi_sub_ptr = spi_bus->spi_int[iter]; + spi_sub_ptr->spi_host = devm_spi_alloc_master(dev, sizeof(struct spi_master)); + if (!spi_sub_ptr->spi_host) + return -ENOMEM; + + spi_sub_ptr->parent = spi_bus; + spi_sub_ptr->spi_xfer_in_progress = false; + + if (!iter) { + ret = pcim_enable_device(pdev); + if (ret) + return -ENOMEM; + + ret = pci_request_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; + } + + 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; + } + + init_completion(&spi_sub_ptr->spi_xfer_done); + /* Initialize Interrupts - SPI_INT */ + regval = readl(spi_bus->reg_base + + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); + regval &= ~SPI_INTR; + writel(regval, spi_bus->reg_base + + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); + spi_sub_ptr->irq = pci_irq_vector(pdev, 0); + + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS, + pci_name(pdev), spi_sub_ptr); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to request irq : %d", + spi_sub_ptr->irq); + ret = -ENODEV; + goto error; + } + + /* This register is only applicable for 1st instance */ + regval = readl(spi_bus->reg_base + SPI_PCI_CTRL_REG_OFFSET(0)); + if (!only_sec_inst) + regval |= (BIT(4)); + else + regval &= ~(BIT(4)); + + writel(regval, spi_bus->reg_base + SPI_PCI_CTRL_REG_OFFSET(0)); + } + + spi_sub_ptr->hw_inst = start++; + + if (iter == 1) { + init_completion(&spi_sub_ptr->spi_xfer_done); + /* Initialize Interrupts - SPI_INT */ + regval = readl(spi_bus->reg_base + + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); + regval &= ~SPI_INTR; + writel(regval, spi_bus->reg_base + + SPI_MST_EVENT_MASK_REG_OFFSET(spi_sub_ptr->hw_inst)); + spi_sub_ptr->irq = pci_irq_vector(pdev, iter); + ret = devm_request_irq(&pdev->dev, spi_sub_ptr->irq, + pci1xxxx_spi_isr, PCI1XXXX_IRQ_FLAGS, + pci_name(pdev), spi_sub_ptr); + if (ret < 0) { + dev_err(&pdev->dev, "Unable to request irq : %d", + spi_sub_ptr->irq); + ret = -ENODEV; + goto error; + } + } + + spi_host = spi_sub_ptr->spi_host; + spi_host->num_chipselect = SPI_CHIP_SEL_COUNT; + spi_host->mode_bits = SPI_MODE_0 | SPI_MODE_3 | SPI_RX_DUAL | + SPI_TX_DUAL | SPI_LOOP; + spi_host->transfer_one = pci1xxxx_spi_transfer_one; + spi_host->set_cs = pci1xxxx_spi_set_cs; + spi_host->bits_per_word_mask = SPI_BPW_MASK(8); + spi_host->max_speed_hz = PCI1XXXX_SPI_MAX_CLOCK_HZ; + spi_host->min_speed_hz = PCI1XXXX_SPI_MIN_CLOCK_HZ; + spi_host->flags = SPI_MASTER_MUST_TX; + spi_master_set_devdata(spi_host, spi_sub_ptr); + ret = devm_spi_register_master(dev, spi_host); + if (ret) + goto error; + } + pci_set_drvdata(pdev, spi_bus); + + return 0; + +error: + pci_release_regions(pdev); + return ret; +} + +static struct pci_driver pci1xxxx_spi_driver = { + .name = DRV_NAME, + .id_table = pci1xxxx_spi_pci_id_table, + .probe = pci1xxxx_spi_probe, +}; + +module_pci_driver(pci1xxxx_spi_driver); + +MODULE_DESCRIPTION("Microchip Technology Inc. pci1xxxx SPI bus driver"); +MODULE_AUTHOR("Tharun Kumar P<tharunkumar.pasumarthi@microchip.com>"); +MODULE_AUTHOR("Kumaravel Thiagarajan<kumaravel.thiagarajan@microchip.com>"); +MODULE_LICENSE("GPL v2"); From a008ae9f8336d79df589eb343a38080a4b98340d Mon Sep 17 00:00:00 2001 From: Yang Yingliang <yangyingliang@huawei.com> Date: Wed, 28 Sep 2022 22:58:51 +0800 Subject: [PATCH 03/66] spi: bcm63xx: Use devm_platform_get_and_ioremap_resource() Use the devm_platform_get_and_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang <yangyingliang@huawei.com> Link: https://lore.kernel.org/r/20220928145852.1882221-1-yangyingliang@huawei.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-bcm63xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 80fa0ef8909ca..3686d78c44a6d 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -547,8 +547,7 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); bs->pdev = pdev; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - bs->regs = devm_ioremap_resource(&pdev->dev, r); + bs->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r); if (IS_ERR(bs->regs)) { ret = PTR_ERR(bs->regs); goto out_err; From 4e12ef2b2e3f65c4fba895262363c499476848a1 Mon Sep 17 00:00:00 2001 From: Yang Yingliang <yangyingliang@huawei.com> Date: Wed, 28 Sep 2022 22:58:52 +0800 Subject: [PATCH 04/66] spi: cadence-quadspi: Use devm_platform_{get_and_}ioremap_resource() Use the devm_platform_{get_and}_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang <yangyingliang@huawei.com> Link: https://lore.kernel.org/r/20220928145852.1882221-2-yangyingliang@huawei.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-cadence-quadspi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 4472305479452..4219113cf36be 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1580,7 +1580,6 @@ static int cqspi_probe(struct platform_device *pdev) struct spi_master *master; struct resource *res_ahb; struct cqspi_st *cqspi; - struct resource *res; int ret; int irq; @@ -1616,8 +1615,7 @@ static int cqspi_probe(struct platform_device *pdev) } /* Obtain and remap controller address. */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - cqspi->iobase = devm_ioremap_resource(dev, res); + cqspi->iobase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cqspi->iobase)) { dev_err(dev, "Cannot remap controller address.\n"); ret = PTR_ERR(cqspi->iobase); @@ -1625,8 +1623,7 @@ static int cqspi_probe(struct platform_device *pdev) } /* Obtain and remap AHB address. */ - res_ahb = platform_get_resource(pdev, IORESOURCE_MEM, 1); - cqspi->ahb_base = devm_ioremap_resource(dev, res_ahb); + cqspi->ahb_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res_ahb); if (IS_ERR(cqspi->ahb_base)) { dev_err(dev, "Cannot remap AHB address.\n"); ret = PTR_ERR(cqspi->ahb_base); From e3b7fca31185813297bb995d7b21a6305bb62c84 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Mon, 17 Oct 2022 20:12:38 +0300 Subject: [PATCH 05/66] spi: pxa2xx: Simplify with devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. No functional changes. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20221017171243.57078-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pxa2xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 2bf21c2e7a52a..03ed6d4a14cd9 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1482,8 +1482,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) ssp = &pdata->ssp; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res); + ssp->mmio_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(ssp->mmio_base)) return ERR_CAST(ssp->mmio_base); From 666ea0ad932fbdc82644457f1a78f4584801f698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> Date: Mon, 17 Oct 2022 22:01:43 +0200 Subject: [PATCH 06/66] spi: bcm-qspi: Make bcm_qspi_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function bcm_qspi_remove() returns zero unconditionally. Make it return void. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Florian Fainelli <f.fainelli@gmail.com> Link: https://lore.kernel.org/r/20221017200143.1426528-1-u.kleine-koenig@pengutronix.de Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-bcm-qspi.c | 5 ++--- drivers/spi/spi-bcm-qspi.h | 2 +- drivers/spi/spi-brcmstb-qspi.c | 4 +++- drivers/spi/spi-iproc-qspi.c | 4 +++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index cad2d55dcd3d2..0eee574d3e1f2 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1682,7 +1682,7 @@ int bcm_qspi_probe(struct platform_device *pdev, /* probe function to be called by SoC specific platform driver probe */ EXPORT_SYMBOL_GPL(bcm_qspi_probe); -int bcm_qspi_remove(struct platform_device *pdev) +void bcm_qspi_remove(struct platform_device *pdev) { struct bcm_qspi *qspi = platform_get_drvdata(pdev); @@ -1690,9 +1690,8 @@ int bcm_qspi_remove(struct platform_device *pdev) bcm_qspi_hw_uninit(qspi); clk_disable_unprepare(qspi->clk); kfree(qspi->dev_ids); - - return 0; } + /* function to be called by SoC specific platform driver remove() */ EXPORT_SYMBOL_GPL(bcm_qspi_remove); diff --git a/drivers/spi/spi-bcm-qspi.h b/drivers/spi/spi-bcm-qspi.h index 01aec64601084..3d7c359c0239b 100644 --- a/drivers/spi/spi-bcm-qspi.h +++ b/drivers/spi/spi-bcm-qspi.h @@ -96,7 +96,7 @@ static inline u32 get_qspi_mask(int type) /* The common driver functions to be called by the SoC platform driver */ int bcm_qspi_probe(struct platform_device *pdev, struct bcm_qspi_soc_intc *soc_intc); -int bcm_qspi_remove(struct platform_device *pdev); +void bcm_qspi_remove(struct platform_device *pdev); /* pm_ops used by the SoC platform driver called on PM suspend/resume */ extern const struct dev_pm_ops bcm_qspi_pm_ops; diff --git a/drivers/spi/spi-brcmstb-qspi.c b/drivers/spi/spi-brcmstb-qspi.c index 75e9b76dab1e0..de362b35718f8 100644 --- a/drivers/spi/spi-brcmstb-qspi.c +++ b/drivers/spi/spi-brcmstb-qspi.c @@ -23,7 +23,9 @@ static int brcmstb_qspi_probe(struct platform_device *pdev) static int brcmstb_qspi_remove(struct platform_device *pdev) { - return bcm_qspi_remove(pdev); + bcm_qspi_remove(pdev); + + return 0; } static struct platform_driver brcmstb_qspi_driver = { diff --git a/drivers/spi/spi-iproc-qspi.c b/drivers/spi/spi-iproc-qspi.c index de297dacfd570..91cf8eb7213c3 100644 --- a/drivers/spi/spi-iproc-qspi.c +++ b/drivers/spi/spi-iproc-qspi.c @@ -129,7 +129,9 @@ static int bcm_iproc_probe(struct platform_device *pdev) static int bcm_iproc_remove(struct platform_device *pdev) { - return bcm_qspi_remove(pdev); + bcm_qspi_remove(pdev); + + return 0; } static const struct of_device_id bcm_iproc_of_match[] = { From 3be6acda8241352c57d47b4d7d9968cadcb954ea Mon Sep 17 00:00:00 2001 From: Giulio Benetti <giulio.benetti@benettiengineering.com> Date: Tue, 18 Oct 2022 23:57:54 +0200 Subject: [PATCH 07/66] spi: fsl-cpm: substitute empty_zero_page with helper ZERO_PAGE(0) Not all zero page implementations use empty_zero_page global pointer so let's substitute empty_zero_page occurence with helper ZERO_PAGE(0). Signed-off-by: Giulio Benetti <giulio.benetti@benettiengineering.com> Link: https://lore.kernel.org/r/20221018215755.33566-2-giulio.benetti@benettiengineering.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-fsl-cpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index ee905880769e6..17a44d4f50218 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -333,7 +333,7 @@ int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi) goto err_bds; } - mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE, + mspi->dma_dummy_tx = dma_map_single(dev, ZERO_PAGE(0), PAGE_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dev, mspi->dma_dummy_tx)) { dev_err(dev, "unable to map dummy tx buffer\n"); From b3b953084b1bd0e74785bc5017444dd56952fb39 Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Date: Tue, 11 Oct 2022 11:50:34 +0530 Subject: [PATCH 08/66] spi: spi-zynqmp-gqspi: Fix kernel-doc warnings Document zynqmp_qspi ctrl and op_lock member description. It also adds return documentation for 'zynqmp_qspi_setuprxdma' and zynqmp_qspi_read_op. Fixes below kernel-doc warnings- spi-zynqmp-gqspi.c:178: warning: Function parameter or member 'ctlr' not described in 'zynqmp_qspi' spi-zynqmp-gqspi.c:178: warning: Function parameter or member 'op_lock' not described in 'zynqmp_qspi' spi-zynqmp-gqspi.c:737: warning: No description found for return value of 'zynqmp_qspi_setuprxdma' spi-zynqmp-gqspi.c:822: warning: No description found for return value of 'zynqmp_qspi_read_op' Signed-off-by: Michal Simek <michal.simek@amd.com> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Link: https://lore.kernel.org/r/20221011062040.12116-2-amit.kumar-mahapatra@amd.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-zynqmp-gqspi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index c760aac070e54..973008a30a093 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -141,6 +141,7 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; /** * struct zynqmp_qspi - Defines qspi driver instance + * @ctlr: Pointer to the spi controller information * @regs: Virtual address of the QSPI controller registers * @refclk: Pointer to the peripheral clock * @pclk: Pointer to the APB clock @@ -157,6 +158,7 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; * @genfifoentry: Used for storing the genfifoentry instruction. * @mode: Defines the mode in which QSPI is operating * @data_completion: completion structure + * @op_lock: Operational lock */ struct zynqmp_qspi { struct spi_controller *ctlr; @@ -739,6 +741,8 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) /** * zynqmp_qspi_setuprxdma - This function sets up the RX DMA operation * @xqspi: xqspi is a pointer to the GQSPI instance. + * + * Return: 0 on success; error value otherwise. */ static int zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) { @@ -823,6 +827,8 @@ static void zynqmp_qspi_write_op(struct zynqmp_qspi *xqspi, u8 tx_nbits, * @rx_nbits: Receive buswidth. * @genfifoentry: genfifoentry is pointer to the variable in which * GENFIFO mask is returned to calling function + * + * Return: 0 on success; error value otherwise. */ static int zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits, u32 genfifoentry) From 22742b8bbdd9fee1ae30be49c7e7e3becba96fc1 Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Date: Tue, 11 Oct 2022 11:50:35 +0530 Subject: [PATCH 09/66] spi: spi-zynqmp-gqspi: Set CPOL and CPHA during hardware init During every transfer GQSPI driver writes the CPOL & CPHA values to the configuration register. But the CPOL & CPHA values do not change in between multiple transfers, so moved the CPOL & CPHA initialization to hardware init so that the values are written only once. Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Link: https://lore.kernel.org/r/20221011062040.12116-3-amit.kumar-mahapatra@amd.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-zynqmp-gqspi.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 973008a30a093..1b56dd29057f5 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -266,7 +266,9 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr, * - Enable manual slave select * - Enable manual start * - Deselect all the chip select lines - * - Set the little endian mode of TX FIFO and + * - Set the little endian mode of TX FIFO + * - Set clock phase + * - Set clock polarity and * - Enable the QSPI controller */ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) @@ -305,10 +307,17 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) config_reg |= GQSPI_CFG_WP_HOLD_MASK; /* Clear pre-scalar by default */ config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; - /* CPHA 0 */ - config_reg &= ~GQSPI_CFG_CLK_PHA_MASK; - /* CPOL 0 */ - config_reg &= ~GQSPI_CFG_CLK_POL_MASK; + /* Set CPHA */ + if (xqspi->ctlr->mode_bits & SPI_CPHA) + config_reg |= GQSPI_CFG_CLK_PHA_MASK; + else + config_reg &= ~GQSPI_CFG_CLK_PHA_MASK; + /* Set CPOL */ + if (xqspi->ctlr->mode_bits & SPI_CPOL) + config_reg |= GQSPI_CFG_CLK_POL_MASK; + else + config_reg &= ~GQSPI_CFG_CLK_POL_MASK; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); /* Clear the TX and RX FIFO */ @@ -470,14 +479,6 @@ static int zynqmp_qspi_config_op(struct zynqmp_qspi *xqspi, config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); - /* Set the QSPI clock phase and clock polarity */ - config_reg &= (~GQSPI_CFG_CLK_PHA_MASK) & (~GQSPI_CFG_CLK_POL_MASK); - - if (qspi->mode & SPI_CPHA) - config_reg |= GQSPI_CFG_CLK_PHA_MASK; - if (qspi->mode & SPI_CPOL) - config_reg |= GQSPI_CFG_CLK_POL_MASK; - config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT); zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); @@ -1170,6 +1171,9 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto clk_dis_all; } + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | + SPI_TX_DUAL | SPI_TX_QUAD; + /* QSPI controller initializations */ zynqmp_qspi_init_hw(xqspi); @@ -1207,8 +1211,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) ctlr->setup = zynqmp_qspi_setup_op; ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); - ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | - SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; ctlr->auto_runtime_pm = true; From 21764a49d32e041e9d118a7b38c14e3e02fae129 Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Date: Tue, 11 Oct 2022 11:50:36 +0530 Subject: [PATCH 10/66] spi: spi-zynqmp-gqspi: Avoid setting baud rate multiple times for same SPI frequency During every transfer the GQSPI driver configures the baud rate value. But when there is no change in the SPI clock frequency the driver should avoid rewriting the same baud rate value to the configuration register. Update GQSPI driver to rewrite the baud rate value if there is any change in SPI clock frequency. Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Link: https://lore.kernel.org/r/20221011062040.12116-4-amit.kumar-mahapatra@amd.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-zynqmp-gqspi.c | 49 ++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 1b56dd29057f5..0fecea3380273 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -159,6 +159,7 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; * @mode: Defines the mode in which QSPI is operating * @data_completion: completion structure * @op_lock: Operational lock + * @speed_hz: Current SPI bus clock speed in hz */ struct zynqmp_qspi { struct spi_controller *ctlr; @@ -179,6 +180,7 @@ struct zynqmp_qspi { enum mode_type mode; struct completion data_completion; struct mutex op_lock; + u32 speed_hz; }; /** @@ -273,7 +275,8 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr, */ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) { - u32 config_reg; + u32 config_reg, baud_rate_val = 0; + ulong clk_rate; /* Select the GQSPI mode */ zynqmp_gqspi_write(xqspi, GQSPI_SEL_OFST, GQSPI_SEL_MASK); @@ -318,6 +321,16 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) else config_reg &= ~GQSPI_CFG_CLK_POL_MASK; + /* Set the clock frequency */ + clk_rate = clk_get_rate(xqspi->refclk); + while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && + (clk_rate / + (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > xqspi->speed_hz) + baud_rate_val++; + + config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; + config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); /* Clear the TX and RX FIFO */ @@ -466,22 +479,29 @@ static int zynqmp_qspi_config_op(struct zynqmp_qspi *xqspi, struct spi_device *qspi) { ulong clk_rate; - u32 config_reg, baud_rate_val = 0; + u32 config_reg, req_speed_hz, baud_rate_val = 0; - /* Set the clock frequency */ - /* If req_hz == 0, default to lowest speed */ - clk_rate = clk_get_rate(xqspi->refclk); + req_speed_hz = qspi->max_speed_hz; - while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && - (clk_rate / - (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > qspi->max_speed_hz) - baud_rate_val++; + if (xqspi->speed_hz != req_speed_hz) { + xqspi->speed_hz = req_speed_hz; - config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + /* Set the clock frequency */ + /* If req_speed_hz == 0, default to lowest speed */ + clk_rate = clk_get_rate(xqspi->refclk); - config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; - config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT); - zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && + (clk_rate / + (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > + req_speed_hz) + baud_rate_val++; + + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + + config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; + config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + } return 0; } @@ -1173,6 +1193,8 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; + ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; + xqspi->speed_hz = ctlr->max_speed_hz; /* QSPI controller initializations */ zynqmp_qspi_init_hw(xqspi); @@ -1209,7 +1231,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) ctlr->bits_per_word_mask = SPI_BPW_MASK(8); ctlr->mem_ops = &zynqmp_qspi_mem_ops; ctlr->setup = zynqmp_qspi_setup_op; - ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); ctlr->dev.of_node = np; ctlr->auto_runtime_pm = true; From 1e400cb9cff2157f89ca95aba4589f95253425ba Mon Sep 17 00:00:00 2001 From: Rajan Vaja <rajan.vaja@xilinx.com> Date: Tue, 11 Oct 2022 11:50:37 +0530 Subject: [PATCH 11/66] firmware: xilinx: Add qspi firmware interface Add support for QSPI ioctl functions and enums. Signed-off-by: Rajan Vaja <rajan.vaja@xilinx.com> Signed-off-by: Michal Simek <michal.simek@amd.com> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Link: https://lore.kernel.org/r/20221011062040.12116-5-amit.kumar-mahapatra@amd.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/firmware/xilinx/zynqmp.c | 7 +++++++ include/linux/firmware/xlnx-zynqmp.h | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index ff5cabe70a2b2..6bc6b6c84241c 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -843,6 +843,13 @@ int zynqmp_pm_read_pggs(u32 index, u32 *value) } EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs); +int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value) +{ + return zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_SET_TAPDELAY_BYPASS, + index, value, NULL); +} +EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass); + /** * zynqmp_pm_set_boot_health_status() - PM API for setting healthy boot status * @value: Status value to be written diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 76d2b3ebad847..fac37680ffe7b 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -135,6 +135,7 @@ enum pm_ret_status { }; enum pm_ioctl_id { + IOCTL_SET_TAPDELAY_BYPASS = 4, IOCTL_SD_DLL_RESET = 6, IOCTL_SET_SD_TAPDELAY = 7, IOCTL_SET_PLL_FRAC_MODE = 8, @@ -389,6 +390,18 @@ enum zynqmp_pm_shutdown_subtype { ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM = 2, }; +enum tap_delay_signal_type { + PM_TAPDELAY_NAND_DQS_IN = 0, + PM_TAPDELAY_NAND_DQS_OUT = 1, + PM_TAPDELAY_QSPI = 2, + PM_TAPDELAY_MAX = 3, +}; + +enum tap_delay_bypass_ctrl { + PM_TAPDELAY_BYPASS_DISABLE = 0, + PM_TAPDELAY_BYPASS_ENABLE = 1, +}; + enum ospi_mux_select_type { PM_OSPI_MUX_SEL_DMA = 0, PM_OSPI_MUX_SEL_LINEAR = 1, @@ -484,6 +497,7 @@ int zynqmp_pm_write_ggs(u32 index, u32 value); int zynqmp_pm_read_ggs(u32 index, u32 *value); int zynqmp_pm_write_pggs(u32 index, u32 value); int zynqmp_pm_read_pggs(u32 index, u32 *value); +int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value); int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype); int zynqmp_pm_set_boot_health_status(u32 value); int zynqmp_pm_pinctrl_request(const u32 pin); @@ -696,6 +710,11 @@ static inline int zynqmp_pm_read_pggs(u32 index, u32 *value) return -ENODEV; } +static inline int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value) +{ + return -ENODEV; +} + static inline int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype) { return -ENODEV; From fae7b3c3ecd76a911a1f0e45d2258a420559cbf6 Mon Sep 17 00:00:00 2001 From: Naga Sureshkumar Relli <nagasure@xilinx.com> Date: Tue, 11 Oct 2022 11:50:38 +0530 Subject: [PATCH 12/66] spi: spi-zynqmp-gqspi: Add tap delay support for ZynqMP GQSPI Controller GQSPI controller uses the internal clock for loopback mode. The loopback mode is used with the high-speed Quad SPI timing mode, where the memory interface clock needs to be greater than 40 MHz. Based on the tap delay value programmed, the internal clock is delayed and used for capturing the data. Based upon the frequency of operation set the recommended tap delay values in GQSPI driver. Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com> Signed-off-by: Michal Simek <michal.simek@amd.com> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Link: https://lore.kernel.org/r/20221011062040.12116-6-amit.kumar-mahapatra@amd.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-zynqmp-gqspi.c | 50 +++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 0fecea3380273..c11736d96f334 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -48,6 +48,7 @@ #define GQSPI_QSPIDMA_DST_I_MASK_OFST 0x00000820 #define GQSPI_QSPIDMA_DST_ADDR_OFST 0x00000800 #define GQSPI_QSPIDMA_DST_ADDR_MSB_OFST 0x00000828 +#define GQSPI_DATA_DLY_ADJ_OFST 0x000001F8 /* GQSPI register bit masks */ #define GQSPI_SEL_MASK 0x00000001 @@ -136,6 +137,16 @@ #define GQSPI_MAX_NUM_CS 2 /* Maximum number of chip selects */ +#define GQSPI_USE_DATA_DLY 0x1 +#define GQSPI_USE_DATA_DLY_SHIFT 31 +#define GQSPI_DATA_DLY_ADJ_VALUE 0x2 +#define GQSPI_DATA_DLY_ADJ_SHIFT 28 + +#define GQSPI_FREQ_37_5MHZ 37500000 +#define GQSPI_FREQ_40MHZ 40000000 +#define GQSPI_FREQ_100MHZ 100000000 +#define GQSPI_FREQ_150MHZ 150000000 + #define SPI_AUTOSUSPEND_TIMEOUT 3000 enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; @@ -253,6 +264,37 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr, } } +/** + * zynqmp_qspi_set_tapdelay: To configure qspi tap delays + * @xqspi: Pointer to the zynqmp_qspi structure + * @baudrateval: Buadrate to configure + */ +static void zynqmp_qspi_set_tapdelay(struct zynqmp_qspi *xqspi, u32 baudrateval) +{ + u32 lpbkdlyadj = 0, datadlyadj = 0, clk_rate; + u32 reqhz = 0; + + clk_rate = clk_get_rate(xqspi->refclk); + reqhz = (clk_rate / (GQSPI_BAUD_DIV_SHIFT << baudrateval)); + + if (reqhz <= GQSPI_FREQ_40MHZ) { + zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI, + PM_TAPDELAY_BYPASS_ENABLE); + } else if (reqhz <= GQSPI_FREQ_100MHZ) { + zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI, + PM_TAPDELAY_BYPASS_ENABLE); + lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK); + datadlyadj |= ((GQSPI_USE_DATA_DLY << + GQSPI_USE_DATA_DLY_SHIFT) | + (GQSPI_DATA_DLY_ADJ_VALUE << + GQSPI_DATA_DLY_ADJ_SHIFT)); + } else if (reqhz <= GQSPI_FREQ_150MHZ) { + lpbkdlyadj |= GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK; + } + zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST, lpbkdlyadj); + zynqmp_gqspi_write(xqspi, GQSPI_DATA_DLY_ADJ_OFST, datadlyadj); +} + /** * zynqmp_qspi_init_hw - Initialize the hardware * @xqspi: Pointer to the zynqmp_qspi structure @@ -333,15 +375,14 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + /* Set the tapdelay for clock frequency */ + zynqmp_qspi_set_tapdelay(xqspi, baud_rate_val); + /* Clear the TX and RX FIFO */ zynqmp_gqspi_write(xqspi, GQSPI_FIFO_CTRL_OFST, GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK | GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK | GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK); - /* Set by default to allow for high frequencies */ - zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST, - zynqmp_gqspi_read(xqspi, GQSPI_LPBK_DLY_ADJ_OFST) | - GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK); /* Reset thresholds */ zynqmp_gqspi_write(xqspi, GQSPI_TX_THRESHOLD_OFST, GQSPI_TX_FIFO_THRESHOLD_RESET_VAL); @@ -501,6 +542,7 @@ static int zynqmp_qspi_config_op(struct zynqmp_qspi *xqspi, config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT); zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + zynqmp_qspi_set_tapdelay(xqspi, baud_rate_val); } return 0; } From 824590249b3cdf57d090d4c912f1497b8e61458f Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Date: Tue, 11 Oct 2022 11:50:39 +0530 Subject: [PATCH 13/66] spi: dt-bindings: zynqmp-qspi: Add support for Xilinx Versal QSPI Add new compatible to support QSPI controller on Xilinx Versal SoCs. Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20221011062040.12116-7-amit.kumar-mahapatra@amd.com Signed-off-by: Mark Brown <broonie@kernel.org> --- Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.yaml b/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.yaml index 6bf0edc57f4af..546c416cdb553 100644 --- a/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.yaml +++ b/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.yaml @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: xlnx,zynqmp-qspi-1.0 + enum: + - xlnx,versal-qspi-1.0 + - xlnx,zynqmp-qspi-1.0 reg: maxItems: 2 From 29f4d95b97bcabc0cd83c34495224b24490f0fe0 Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Date: Tue, 11 Oct 2022 11:50:40 +0530 Subject: [PATCH 14/66] spi: spi-zynqmp-gqspi: Add tap delay support for GQSPI controller on Versal platform Add tap delay support for GQSPI controller on Versal platform. Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com> Signed-off-by: Michal Simek <michal.simek@amd.com> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com> Link: https://lore.kernel.org/r/20221011062040.12116-8-amit.kumar-mahapatra@amd.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-zynqmp-gqspi.c | 86 ++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index c11736d96f334..95ff15665d448 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/of_irq.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> @@ -34,6 +35,7 @@ #define GQSPI_RXD_OFST 0x00000120 #define GQSPI_TX_THRESHOLD_OFST 0x00000128 #define GQSPI_RX_THRESHOLD_OFST 0x0000012C +#define IOU_TAPDLY_BYPASS_OFST 0x0000003C #define GQSPI_LPBK_DLY_ADJ_OFST 0x00000138 #define GQSPI_GEN_FIFO_OFST 0x00000140 #define GQSPI_SEL_OFST 0x00000144 @@ -141,6 +143,13 @@ #define GQSPI_USE_DATA_DLY_SHIFT 31 #define GQSPI_DATA_DLY_ADJ_VALUE 0x2 #define GQSPI_DATA_DLY_ADJ_SHIFT 28 +#define GQSPI_LPBK_DLY_ADJ_DLY_1 0x1 +#define GQSPI_LPBK_DLY_ADJ_DLY_1_SHIFT 0x3 +#define TAP_DLY_BYPASS_LQSPI_RX_VALUE 0x1 +#define TAP_DLY_BYPASS_LQSPI_RX_SHIFT 0x2 + +/* set to differentiate versal from zynqmp, 1=versal, 0=zynqmp */ +#define QSPI_QUIRK_HAS_TAPDELAY BIT(0) #define GQSPI_FREQ_37_5MHZ 37500000 #define GQSPI_FREQ_40MHZ 40000000 @@ -150,6 +159,14 @@ #define SPI_AUTOSUSPEND_TIMEOUT 3000 enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; +/** + * struct qspi_platform_data - zynqmp qspi platform data structure + * @quirks: Flags is used to identify the platform + */ +struct qspi_platform_data { + u32 quirks; +}; + /** * struct zynqmp_qspi - Defines qspi driver instance * @ctlr: Pointer to the spi controller information @@ -171,6 +188,7 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; * @data_completion: completion structure * @op_lock: Operational lock * @speed_hz: Current SPI bus clock speed in hz + * @has_tapdelay: Used for tapdelay register available in qspi */ struct zynqmp_qspi { struct spi_controller *ctlr; @@ -192,6 +210,7 @@ struct zynqmp_qspi { struct completion data_completion; struct mutex op_lock; u32 speed_hz; + bool has_tapdelay; }; /** @@ -271,25 +290,44 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr, */ static void zynqmp_qspi_set_tapdelay(struct zynqmp_qspi *xqspi, u32 baudrateval) { - u32 lpbkdlyadj = 0, datadlyadj = 0, clk_rate; + u32 tapdlybypass = 0, lpbkdlyadj = 0, datadlyadj = 0, clk_rate; u32 reqhz = 0; clk_rate = clk_get_rate(xqspi->refclk); reqhz = (clk_rate / (GQSPI_BAUD_DIV_SHIFT << baudrateval)); - if (reqhz <= GQSPI_FREQ_40MHZ) { - zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI, - PM_TAPDELAY_BYPASS_ENABLE); - } else if (reqhz <= GQSPI_FREQ_100MHZ) { - zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI, - PM_TAPDELAY_BYPASS_ENABLE); - lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK); - datadlyadj |= ((GQSPI_USE_DATA_DLY << - GQSPI_USE_DATA_DLY_SHIFT) | - (GQSPI_DATA_DLY_ADJ_VALUE << - GQSPI_DATA_DLY_ADJ_SHIFT)); - } else if (reqhz <= GQSPI_FREQ_150MHZ) { - lpbkdlyadj |= GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK; + if (!xqspi->has_tapdelay) { + if (reqhz <= GQSPI_FREQ_40MHZ) { + zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI, + PM_TAPDELAY_BYPASS_ENABLE); + } else if (reqhz <= GQSPI_FREQ_100MHZ) { + zynqmp_pm_set_tapdelay_bypass(PM_TAPDELAY_QSPI, + PM_TAPDELAY_BYPASS_ENABLE); + lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK); + datadlyadj |= ((GQSPI_USE_DATA_DLY << + GQSPI_USE_DATA_DLY_SHIFT) + | (GQSPI_DATA_DLY_ADJ_VALUE << + GQSPI_DATA_DLY_ADJ_SHIFT)); + } else if (reqhz <= GQSPI_FREQ_150MHZ) { + lpbkdlyadj |= GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK; + } + } else { + if (reqhz <= GQSPI_FREQ_37_5MHZ) { + tapdlybypass |= (TAP_DLY_BYPASS_LQSPI_RX_VALUE << + TAP_DLY_BYPASS_LQSPI_RX_SHIFT); + } else if (reqhz <= GQSPI_FREQ_100MHZ) { + tapdlybypass |= (TAP_DLY_BYPASS_LQSPI_RX_VALUE << + TAP_DLY_BYPASS_LQSPI_RX_SHIFT); + lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK); + datadlyadj |= (GQSPI_USE_DATA_DLY << + GQSPI_USE_DATA_DLY_SHIFT); + } else if (reqhz <= GQSPI_FREQ_150MHZ) { + lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK + | (GQSPI_LPBK_DLY_ADJ_DLY_1 << + GQSPI_LPBK_DLY_ADJ_DLY_1_SHIFT)); + } + zynqmp_gqspi_write(xqspi, + IOU_TAPDLY_BYPASS_OFST, tapdlybypass); } zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST, lpbkdlyadj); zynqmp_gqspi_write(xqspi, GQSPI_DATA_DLY_ADJ_OFST, datadlyadj); @@ -1156,6 +1194,16 @@ static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume) }; +static const struct qspi_platform_data versal_qspi_def = { + .quirks = QSPI_QUIRK_HAS_TAPDELAY, +}; + +static const struct of_device_id zynqmp_qspi_of_match[] = { + { .compatible = "xlnx,zynqmp-qspi-1.0"}, + { .compatible = "xlnx,versal-qspi-1.0", .data = &versal_qspi_def }, + { /* End of table */ } +}; + static const struct spi_controller_mem_ops zynqmp_qspi_mem_ops = { .exec_op = zynqmp_qspi_exec_op, }; @@ -1176,6 +1224,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; u32 num_cs; + const struct qspi_platform_data *p_data; ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); if (!ctlr) @@ -1186,6 +1235,10 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) xqspi->ctlr = ctlr; platform_set_drvdata(pdev, xqspi); + p_data = of_device_get_match_data(&pdev->dev); + if (p_data && (p_data->quirks & QSPI_QUIRK_HAS_TAPDELAY)) + xqspi->has_tapdelay = true; + xqspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xqspi->regs)) { ret = PTR_ERR(xqspi->regs); @@ -1324,11 +1377,6 @@ static int zynqmp_qspi_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id zynqmp_qspi_of_match[] = { - { .compatible = "xlnx,zynqmp-qspi-1.0", }, - { /* End of table */ } -}; - MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match); static struct platform_driver zynqmp_qspi_driver = { From a977c3a93fa9ed75fcd77b770c7e557d7b4f7bab Mon Sep 17 00:00:00 2001 From: Yang Yingliang <yangyingliang@huawei.com> Date: Wed, 19 Oct 2022 17:33:18 +0800 Subject: [PATCH 15/66] spi: img-spfi: Use devm_platform_get_and_ioremap_resource() Use the devm_platform_get_and_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang <yangyingliang@huawei.com> Link: https://lore.kernel.org/r/20221019093318.1183190-1-yangyingliang@huawei.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-img-spfi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index bfd12247f1734..257046f843ffd 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -540,8 +540,7 @@ static int img_spfi_probe(struct platform_device *pdev) spfi->master = master; spin_lock_init(&spfi->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spfi->regs = devm_ioremap_resource(spfi->dev, res); + spfi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(spfi->regs)) { ret = PTR_ERR(spfi->regs); goto put_spi; From aea672d054a21782ed8450c75febb6ba3c208ca4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Thu, 20 Oct 2022 22:54:21 +0300 Subject: [PATCH 16/66] spi: Introduce spi_get_device_match_data() helper The proposed spi_get_device_match_data() helper is for retrieving a driver data associated with the ID in an ID table. First, it tries to get driver data of the device enumerated by firmware interface (usually Device Tree or ACPI). If none is found it falls back to the SPI ID table matching. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20221020195421.10482-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi.c | 12 ++++++++++++ include/linux/spi/spi.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5f9aedd1f0b65..aaf07052fd019 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -360,6 +360,18 @@ const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev) } EXPORT_SYMBOL_GPL(spi_get_device_id); +const void *spi_get_device_match_data(const struct spi_device *sdev) +{ + const void *match; + + match = device_get_match_data(&sdev->dev); + if (match) + return match; + + return (const void *)spi_get_device_id(sdev)->driver_data; +} +EXPORT_SYMBOL_GPL(spi_get_device_match_data); + static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index fbf8c0d95968e..8fe3d0a9d2c9e 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -1514,6 +1514,9 @@ extern void spi_unregister_device(struct spi_device *spi); extern const struct spi_device_id * spi_get_device_id(const struct spi_device *sdev); +extern const void * +spi_get_device_match_data(const struct spi_device *sdev); + static inline bool spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer) { From 8fc8250a1586008cceaadd6f4df9db23643d4b3e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Thu, 20 Oct 2022 22:44:59 +0300 Subject: [PATCH 17/66] spi: pxa2xx: Consistently use dev variable in pxa2xx_spi_init_pdata() We have a temporary variable to keep a pointer to a struct device in the pxa2xx_spi_init_pdata(). Use it consistently there. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20221020194500.10225-5-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pxa2xx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 03ed6d4a14cd9..ddaf6664dae3c 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1468,7 +1468,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) if (pcidev) pcidev_id = pci_match_id(pxa2xx_spi_pci_compound_match, pcidev); - match = device_get_match_data(&pdev->dev); + match = device_get_match_data(dev); if (match) type = (enum pxa_ssp_type)match; else if (pcidev_id) @@ -1476,7 +1476,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) else return ERR_PTR(-EINVAL); - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); @@ -1496,7 +1496,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) } #endif - ssp->clk = devm_clk_get(&pdev->dev, NULL); + ssp->clk = devm_clk_get(dev, NULL); if (IS_ERR(ssp->clk)) return ERR_CAST(ssp->clk); @@ -1505,7 +1505,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) return ERR_PTR(ssp->irq); ssp->type = type; - ssp->dev = &pdev->dev; + ssp->dev = dev; status = acpi_dev_uid_to_integer(ACPI_COMPANION(dev), &uid); if (status) @@ -1513,7 +1513,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) else ssp->port_id = uid; - pdata->is_slave = device_property_read_bool(&pdev->dev, "spi-slave"); + pdata->is_slave = device_property_read_bool(dev, "spi-slave"); pdata->num_chipselect = 1; pdata->enable_dma = true; pdata->dma_burst_size = 1; From 6c3c438c085b2cd79b3291b67f8f7ece62371947 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Thu, 20 Oct 2022 22:45:00 +0300 Subject: [PATCH 18/66] spi: pxa2xx: Switch from PM ifdeffery to pm_ptr() Cleaning up the driver to use pm_ptr() macro instead of ifdeffery that makes it simpler and allows the compiler to remove those functions if built without CONFIG_PM and CONFIG_PM_SLEEP support. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Link: https://lore.kernel.org/r/20221020194500.10225-6-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pxa2xx.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index ddaf6664dae3c..c9f6a3fbe62f3 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1806,7 +1806,6 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int pxa2xx_spi_suspend(struct device *dev) { struct driver_data *drv_data = dev_get_drvdata(dev); @@ -1841,9 +1840,7 @@ static int pxa2xx_spi_resume(struct device *dev) /* Start the queue running */ return spi_controller_resume(drv_data->controller); } -#endif -#ifdef CONFIG_PM static int pxa2xx_spi_runtime_suspend(struct device *dev) { struct driver_data *drv_data = dev_get_drvdata(dev); @@ -1858,18 +1855,16 @@ static int pxa2xx_spi_runtime_resume(struct device *dev) return clk_prepare_enable(drv_data->ssp->clk); } -#endif static const struct dev_pm_ops pxa2xx_spi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pxa2xx_spi_suspend, pxa2xx_spi_resume) - SET_RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, - pxa2xx_spi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pxa2xx_spi_suspend, pxa2xx_spi_resume) + RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, pxa2xx_spi_runtime_resume, NULL) }; static struct platform_driver driver = { .driver = { .name = "pxa2xx-spi", - .pm = &pxa2xx_spi_pm_ops, + .pm = pm_ptr(&pxa2xx_spi_pm_ops), .acpi_match_table = ACPI_PTR(pxa2xx_spi_acpi_match), .of_match_table = of_match_ptr(pxa2xx_spi_of_match), }, From 0a7693a0da649e7ab7d07a5373fbda21231e67b2 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde <mkl@pengutronix.de> Date: Fri, 21 Oct 2022 15:10:51 +0200 Subject: [PATCH 19/66] spi: spi-imx: remove unused struct spi_imx_devtype_data::disable_dma callback In commit 7a908832ace7 ("spi: imx: add fallback feature") the last user of the struct spi_imx_devtype_data::disable_dma callback was removed. However the disable_dma member of struct spi_imx_devtype_data and the callback itself was not removed. Remove struct spi_imx_devtype_data::disable_dma and mx51_disable_dma() as they are unused. Cc: Robin Gong <yibin.gong@nxp.com> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> Link: https://lore.kernel.org/r/20221021131051.1777984-1-mkl@pengutronix.de Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-imx.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 30d82cc7300b2..a4bda03d3a8eb 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -77,7 +77,6 @@ struct spi_imx_devtype_data { void (*reset)(struct spi_imx_data *spi_imx); void (*setup_wml)(struct spi_imx_data *spi_imx); void (*disable)(struct spi_imx_data *spi_imx); - void (*disable_dma)(struct spi_imx_data *spi_imx); bool has_dmamode; bool has_slavemode; unsigned int fifo_size; @@ -497,11 +496,6 @@ static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MX51_ECSPI_CTRL); } -static void mx51_disable_dma(struct spi_imx_data *spi_imx) -{ - writel(0, spi_imx->base + MX51_ECSPI_DMA); -} - static void mx51_ecspi_disable(struct spi_imx_data *spi_imx) { u32 ctrl; @@ -1043,7 +1037,6 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { .rx_available = mx51_ecspi_rx_available, .reset = mx51_ecspi_reset, .setup_wml = mx51_setup_wml, - .disable_dma = mx51_disable_dma, .fifo_size = 64, .has_dmamode = true, .dynamic_burst = true, @@ -1058,7 +1051,6 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = { .prepare_transfer = mx51_ecspi_prepare_transfer, .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, - .disable_dma = mx51_disable_dma, .reset = mx51_ecspi_reset, .fifo_size = 64, .has_dmamode = true, From 031837826886e254fefff7d8b849dc63b6a7e2b9 Mon Sep 17 00:00:00 2001 From: Amjad Ouled-Ameur <aouledameur@baylibre.com> Date: Fri, 21 Oct 2022 15:31:25 +0200 Subject: [PATCH 20/66] spi: dt-bindings: amlogic, meson-gx-spicc: Add pinctrl names for SPI signal states SPI pins of the SPICC Controller in Meson-GX needs to be controlled by pin biais when idle. Therefore define three pinctrl names: - default: SPI pins are controlled by spi function. - idle-high: SCLK pin is pulled-up, but MOSI/MISO are still controlled by spi function. - idle-low: SCLK pin is pulled-down, but MOSI/MISO are still controlled by spi function. Reported-by: Da Xue <da@libre.computer> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Amjad Ouled-Ameur <aouledameur@baylibre.com> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20221004-up-aml-fix-spi-v4-1-0342d8e10c49@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org> --- .../bindings/spi/amlogic,meson-gx-spicc.yaml | 75 ++++++++++++------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/amlogic,meson-gx-spicc.yaml b/Documentation/devicetree/bindings/spi/amlogic,meson-gx-spicc.yaml index 0c10f7678178a..53eb6562b979f 100644 --- a/Documentation/devicetree/bindings/spi/amlogic,meson-gx-spicc.yaml +++ b/Documentation/devicetree/bindings/spi/amlogic,meson-gx-spicc.yaml @@ -10,9 +10,6 @@ title: Amlogic Meson SPI Communication Controller maintainers: - Neil Armstrong <neil.armstrong@linaro.org> -allOf: - - $ref: "spi-controller.yaml#" - description: | The Meson SPICC is a generic SPI controller for general purpose Full-Duplex communications with dedicated 16 words RX/TX PIO FIFOs. @@ -43,31 +40,53 @@ properties: minItems: 1 maxItems: 2 -if: - properties: - compatible: - contains: - enum: - - amlogic,meson-g12a-spicc - -then: - properties: - clocks: - minItems: 2 - - clock-names: - items: - - const: core - - const: pclk - -else: - properties: - clocks: - maxItems: 1 - - clock-names: - items: - - const: core +allOf: + - $ref: "spi-controller.yaml#" + - if: + properties: + compatible: + contains: + enum: + - amlogic,meson-g12a-spicc + + then: + properties: + clocks: + minItems: 2 + + clock-names: + items: + - const: core + - const: pclk + + else: + properties: + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + - if: + properties: + compatible: + contains: + enum: + - amlogic,meson-gx-spicc + + then: + properties: + pinctrl-0: true + pinctrl-1: true + pinctrl-2: true + + pinctrl-names: + minItems: 1 + items: + - const: default + - const: idle-high + - const: idle-low required: - compatible From f4567b28fdd4bede7cab0810200d567a1f03ec5e Mon Sep 17 00:00:00 2001 From: Amjad Ouled-Ameur <aouledameur@baylibre.com> Date: Fri, 21 Oct 2022 15:31:26 +0200 Subject: [PATCH 21/66] spi: meson-spicc: Use pinctrl to drive CLK line when idle Between SPI transactions, all SPI pins are in HiZ state. When using the SS signal from the SPICC controller it's not an issue because when the transaction resumes all pins come back to the right state at the same time as SS. The problem is when we use CS as a GPIO. In fact, between the GPIO CS state change and SPI pins state change from idle, you can have a missing or spurious clock transition. Set a bias on the clock depending on the clock polarity requested before CS goes active, by passing a special "idle-low" and "idle-high" pinctrl state and setting the right state at a start of a message Reported-by: Da Xue <da@libre.computer> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Amjad Ouled-Ameur <aouledameur@baylibre.com> Link: https://lore.kernel.org/r/20221004-up-aml-fix-spi-v4-2-0342d8e10c49@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-meson-spicc.c | 39 ++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index bad201510a992..ffea38e2339c8 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -21,6 +21,7 @@ #include <linux/types.h> #include <linux/interrupt.h> #include <linux/reset.h> +#include <linux/pinctrl/consumer.h> /* * The Meson SPICC controller could support DMA based transfers, but is not @@ -167,6 +168,9 @@ struct meson_spicc_device { unsigned long tx_remain; unsigned long rx_remain; unsigned long xfer_remain; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_idle_high; + struct pinctrl_state *pins_idle_low; }; #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div) @@ -175,8 +179,22 @@ static void meson_spicc_oen_enable(struct meson_spicc_device *spicc) { u32 conf; - if (!spicc->data->has_oen) + if (!spicc->data->has_oen) { + /* Try to get pinctrl states for idle high/low */ + spicc->pins_idle_high = pinctrl_lookup_state(spicc->pinctrl, + "idle-high"); + if (IS_ERR(spicc->pins_idle_high)) { + dev_warn(&spicc->pdev->dev, "can't get idle-high pinctrl\n"); + spicc->pins_idle_high = NULL; + } + spicc->pins_idle_low = pinctrl_lookup_state(spicc->pinctrl, + "idle-low"); + if (IS_ERR(spicc->pins_idle_low)) { + dev_warn(&spicc->pdev->dev, "can't get idle-low pinctrl\n"); + spicc->pins_idle_low = NULL; + } return; + } conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) | SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN; @@ -441,6 +459,16 @@ static int meson_spicc_prepare_message(struct spi_master *master, else conf &= ~SPICC_POL; + if (!spicc->data->has_oen) { + if (spi->mode & SPI_CPOL) { + if (spicc->pins_idle_high) + pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_high); + } else { + if (spicc->pins_idle_low) + pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_low); + } + } + if (spi->mode & SPI_CPHA) conf |= SPICC_PHA; else @@ -487,6 +515,9 @@ static int meson_spicc_unprepare_transfer(struct spi_master *master) /* Set default configuration, keeping datarate field */ writel_relaxed(conf, spicc->base + SPICC_CONREG); + if (!spicc->data->has_oen) + pinctrl_select_default_state(&spicc->pdev->dev); + return 0; } @@ -798,6 +829,12 @@ static int meson_spicc_probe(struct platform_device *pdev) goto out_core_clk; } + spicc->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(spicc->pinctrl)) { + ret = PTR_ERR(spicc->pinctrl); + goto out_clk; + } + device_reset_optional(&pdev->dev); master->num_chipselect = 4; From 6d0cebbdf29922eaba4648a7a06d0d4ffd00439f Mon Sep 17 00:00:00 2001 From: Yang Yingliang <yangyingliang@huawei.com> Date: Wed, 19 Oct 2022 17:26:35 +0800 Subject: [PATCH 22/66] spi: aspeed: Use devm_platform_{get_and_}ioremap_resource() Use the devm_platform_{get_and_}ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang <yangyingliang@huawei.com> Link: https://lore.kernel.org/r/20221019092635.1176622-1-yangyingliang@huawei.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-aspeed-smc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 33cefcf183927..710ff8cf121f1 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -734,13 +734,11 @@ static int aspeed_spi_probe(struct platform_device *pdev) aspi->data = data; aspi->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - aspi->regs = devm_ioremap_resource(dev, res); + aspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(aspi->regs)) return PTR_ERR(aspi->regs); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - aspi->ahb_base = devm_ioremap_resource(dev, res); + aspi->ahb_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); if (IS_ERR(aspi->ahb_base)) { dev_err(dev, "missing AHB mapping window\n"); return PTR_ERR(aspi->ahb_base); From 88a947215c29aa49307c8cd0638ba54e4cf07391 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Fri, 21 Oct 2022 22:00:15 +0300 Subject: [PATCH 23/66] spi: pxa2xx: Validate the correctness of the SSP type Currently we blindly apply the SSP type value from any source of the information. Increase robustness by validating the value before use. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20221021190018.63646-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pxa2xx.c | 6 ++++-- include/linux/pxa2xx_ssp.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index c9f6a3fbe62f3..93be7e8ef8843 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1460,7 +1460,7 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) struct resource *res; struct pci_dev *pcidev = dev_is_pci(parent) ? to_pci_dev(parent) : NULL; const struct pci_device_id *pcidev_id = NULL; - enum pxa_ssp_type type; + enum pxa_ssp_type type = SSP_UNDEFINED; const void *match; int status; u64 uid; @@ -1473,7 +1473,9 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) type = (enum pxa_ssp_type)match; else if (pcidev_id) type = (enum pxa_ssp_type)pcidev_id->driver_data; - else + + /* Validate the SSP type correctness */ + if (!(type > SSP_UNDEFINED && type < SSP_MAX)) return ERR_PTR(-EINVAL); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index a3fec2de512fc..cd1973e6ac4b4 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -229,6 +229,7 @@ enum pxa_ssp_type { LPSS_SPT_SSP, LPSS_BXT_SSP, LPSS_CNL_SSP, + SSP_MAX }; struct ssp_device { From 1a1864cd879a1497fd46f6ea2f31ddcde8385585 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Fri, 21 Oct 2022 22:00:16 +0300 Subject: [PATCH 24/66] spi: pxa2xx: Respect Intel SSP type given by a property Allow to set the Intel SSP type by reading the property. Only apply this to the known MFD enumerated LPSS devices. The check is done by the looking for the specifically named IO memory resource provided by upper layer. This won't be an issue in the future because we strictly prioritize the order in which we are looking for the SSP type in the code. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20221021190018.63646-3-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pxa2xx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 93be7e8ef8843..5527bcc5c7299 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1462,9 +1462,12 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) const struct pci_device_id *pcidev_id = NULL; enum pxa_ssp_type type = SSP_UNDEFINED; const void *match; + bool is_lpss_priv; int status; u64 uid; + is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv"); + if (pcidev) pcidev_id = pci_match_id(pxa2xx_spi_pci_compound_match, pcidev); @@ -1473,6 +1476,15 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) type = (enum pxa_ssp_type)match; else if (pcidev_id) type = (enum pxa_ssp_type)pcidev_id->driver_data; + else if (is_lpss_priv) { + u32 value; + + status = device_property_read_u32(dev, "intel,spi-pxa2xx-type", &value); + if (status) + return ERR_PTR(status); + + type = (enum pxa_ssp_type)value; + } /* Validate the SSP type correctness */ if (!(type > SSP_UNDEFINED && type < SSP_MAX)) From 07c337927e0618faf22ea98475c7162e6b7d2b35 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Fri, 21 Oct 2022 22:00:17 +0300 Subject: [PATCH 25/66] spi: pxa2xx: Remove no more needed PCI ID table Since the PCI enumerated devices provide a property with SSP type, there is no more necessity to bear the copy of the ID table here. Remove it for good. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Link: https://lore.kernel.org/r/20221021190018.63646-4-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pxa2xx.c | 117 +-------------------------------------- 1 file changed, 2 insertions(+), 115 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 5527bcc5c7299..38b8af8a9c12d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -20,7 +20,6 @@ #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/of.h> -#include <linux/pci.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> @@ -1335,121 +1334,17 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); #endif -/* - * PCI IDs of compound devices that integrate both host controller and private - * integrated DMA engine. Please note these are not used in module - * autoloading and probing in this module but matching the LPSS SSP type. - */ -static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { - /* SPT-LP */ - { PCI_VDEVICE(INTEL, 0x9d29), LPSS_SPT_SSP }, - { PCI_VDEVICE(INTEL, 0x9d2a), LPSS_SPT_SSP }, - /* SPT-H */ - { PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP }, - { PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP }, - /* KBL-H */ - { PCI_VDEVICE(INTEL, 0xa2a9), LPSS_SPT_SSP }, - { PCI_VDEVICE(INTEL, 0xa2aa), LPSS_SPT_SSP }, - /* CML-V */ - { PCI_VDEVICE(INTEL, 0xa3a9), LPSS_SPT_SSP }, - { PCI_VDEVICE(INTEL, 0xa3aa), LPSS_SPT_SSP }, - /* BXT A-Step */ - { PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x0ac6), LPSS_BXT_SSP }, - /* BXT B-Step */ - { PCI_VDEVICE(INTEL, 0x1ac2), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x1ac4), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x1ac6), LPSS_BXT_SSP }, - /* GLK */ - { PCI_VDEVICE(INTEL, 0x31c2), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x31c4), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x31c6), LPSS_BXT_SSP }, - /* ICL-LP */ - { PCI_VDEVICE(INTEL, 0x34aa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x34ab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x34fb), LPSS_CNL_SSP }, - /* EHL */ - { PCI_VDEVICE(INTEL, 0x4b2a), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x4b2b), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x4b37), LPSS_BXT_SSP }, - /* JSL */ - { PCI_VDEVICE(INTEL, 0x4daa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x4dab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x4dfb), LPSS_CNL_SSP }, - /* TGL-H */ - { PCI_VDEVICE(INTEL, 0x43aa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x43ab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x43fb), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x43fd), LPSS_CNL_SSP }, - /* ADL-P */ - { PCI_VDEVICE(INTEL, 0x51aa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x51ab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x51fb), LPSS_CNL_SSP }, - /* ADL-M */ - { PCI_VDEVICE(INTEL, 0x54aa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x54ab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x54fb), LPSS_CNL_SSP }, - /* APL */ - { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, - { PCI_VDEVICE(INTEL, 0x5ac6), LPSS_BXT_SSP }, - /* RPL-S */ - { PCI_VDEVICE(INTEL, 0x7a2a), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7a2b), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7a79), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7a7b), LPSS_CNL_SSP }, - /* ADL-S */ - { PCI_VDEVICE(INTEL, 0x7aaa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7aab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7af9), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7afb), LPSS_CNL_SSP }, - /* MTL-P */ - { PCI_VDEVICE(INTEL, 0x7e27), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7e30), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x7e46), LPSS_CNL_SSP }, - /* CNL-LP */ - { PCI_VDEVICE(INTEL, 0x9daa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x9dab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x9dfb), LPSS_CNL_SSP }, - /* CNL-H */ - { PCI_VDEVICE(INTEL, 0xa32a), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa32b), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa37b), LPSS_CNL_SSP }, - /* CML-LP */ - { PCI_VDEVICE(INTEL, 0x02aa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x02ab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x02fb), LPSS_CNL_SSP }, - /* CML-H */ - { PCI_VDEVICE(INTEL, 0x06aa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x06ab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0x06fb), LPSS_CNL_SSP }, - /* TGL-LP */ - { PCI_VDEVICE(INTEL, 0xa0aa), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa0ab), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa0de), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa0df), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa0fb), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa0fd), LPSS_CNL_SSP }, - { PCI_VDEVICE(INTEL, 0xa0fe), LPSS_CNL_SSP }, - { }, -}; - static const struct of_device_id pxa2xx_spi_of_match[] = { { .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP }, {}, }; MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match); -#ifdef CONFIG_PCI - static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param) { return param == chan->device->dev; } -#endif /* CONFIG_PCI */ - static struct pxa2xx_spi_controller * pxa2xx_spi_init_pdata(struct platform_device *pdev) { @@ -1458,8 +1353,6 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) struct device *parent = dev->parent; struct ssp_device *ssp; struct resource *res; - struct pci_dev *pcidev = dev_is_pci(parent) ? to_pci_dev(parent) : NULL; - const struct pci_device_id *pcidev_id = NULL; enum pxa_ssp_type type = SSP_UNDEFINED; const void *match; bool is_lpss_priv; @@ -1468,14 +1361,9 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) is_lpss_priv = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpss_priv"); - if (pcidev) - pcidev_id = pci_match_id(pxa2xx_spi_pci_compound_match, pcidev); - match = device_get_match_data(dev); if (match) type = (enum pxa_ssp_type)match; - else if (pcidev_id) - type = (enum pxa_ssp_type)pcidev_id->driver_data; else if (is_lpss_priv) { u32 value; @@ -1502,13 +1390,12 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) ssp->phys_base = res->start; -#ifdef CONFIG_PCI - if (pcidev_id) { + /* Platforms with iDMA 64-bit */ + if (is_lpss_priv) { pdata->tx_param = parent; pdata->rx_param = parent; pdata->dma_filter = pxa2xx_spi_idma_filter; } -#endif ssp->clk = devm_clk_get(dev, NULL); if (IS_ERR(ssp->clk)) From 0e1f0b1ca79fdcc03c3e6b4277a994ca894c9fcc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Date: Fri, 21 Oct 2022 22:00:18 +0300 Subject: [PATCH 26/66] spi: pxa2xx: Move OF and ACPI ID tables closer to their user There is no code that uses ID tables directly, except the struct device_driver at the end of the file. Hence, move tables closer to its user. It's always possible to access them via pointer to a platform device. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Link: https://lore.kernel.org/r/20221021190018.63646-5-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pxa2xx.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 38b8af8a9c12d..32cc82a89ec1e 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1321,25 +1321,6 @@ static void cleanup(struct spi_device *spi) kfree(chip); } -#ifdef CONFIG_ACPI -static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { - { "INT33C0", LPSS_LPT_SSP }, - { "INT33C1", LPSS_LPT_SSP }, - { "INT3430", LPSS_LPT_SSP }, - { "INT3431", LPSS_LPT_SSP }, - { "80860F0E", LPSS_BYT_SSP }, - { "8086228E", LPSS_BSW_SSP }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); -#endif - -static const struct of_device_id pxa2xx_spi_of_match[] = { - { .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP }, - {}, -}; -MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match); - static bool pxa2xx_spi_idma_filter(struct dma_chan *chan, void *param) { return param == chan->device->dev; @@ -1762,6 +1743,25 @@ static const struct dev_pm_ops pxa2xx_spi_pm_ops = { RUNTIME_PM_OPS(pxa2xx_spi_runtime_suspend, pxa2xx_spi_runtime_resume, NULL) }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { + { "80860F0E", LPSS_BYT_SSP }, + { "8086228E", LPSS_BSW_SSP }, + { "INT33C0", LPSS_LPT_SSP }, + { "INT33C1", LPSS_LPT_SSP }, + { "INT3430", LPSS_LPT_SSP }, + { "INT3431", LPSS_LPT_SSP }, + {} +}; +MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); +#endif + +static const struct of_device_id pxa2xx_spi_of_match[] = { + { .compatible = "marvell,mmp2-ssp", .data = (void *)MMP2_SSP }, + {} +}; +MODULE_DEVICE_TABLE(of, pxa2xx_spi_of_match); + static struct platform_driver driver = { .driver = { .name = "pxa2xx-spi", From 93cc2559d3fdcd28b1a7972ab519a6cd8ba20f9d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner <tglx@linutronix.de> Date: Wed, 26 Oct 2022 14:29:51 +0200 Subject: [PATCH 27/66] spi: Remove the obsolte u64_stats_fetch_*_irq() users. Now that the 32bit UP oddity is gone and 32bit uses always a sequence count, there is no need for the fetch_irq() variants anymore. Convert to the regular interface. Cc: Mark Brown <broonie@kernel.org> Cc: linux-spi@vger.kernel.org Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/20221026122951.331638-1-bigeasy@linutronix.de Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index aaf07052fd019..8df8b93df1c31 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -127,10 +127,10 @@ do { \ unsigned int start; \ pcpu_stats = per_cpu_ptr(in, i); \ do { \ - start = u64_stats_fetch_begin_irq( \ + start = u64_stats_fetch_begin( \ &pcpu_stats->syncp); \ inc = u64_stats_read(&pcpu_stats->field); \ - } while (u64_stats_fetch_retry_irq( \ + } while (u64_stats_fetch_retry( \ &pcpu_stats->syncp, start)); \ ret += inc; \ } \ From 1793d36672eb8d86fb319cd28e056a154945506f Mon Sep 17 00:00:00 2001 From: Yang Yingliang <yangyingliang@huawei.com> Date: Sat, 29 Oct 2022 15:15:29 +0800 Subject: [PATCH 28/66] spi: npcm-fiu: Use devm_platform_ioremap_resource_byname() Use the devm_platform_ioremap_resource_byname() helper instead of calling platform_get_resource_byname() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang <yangyingliang@huawei.com> Link: https://lore.kernel.org/r/20221029071529.3019626-1-yangyingliang@huawei.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-npcm-fiu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c index 49f6424e35af0..559d3a5b4062c 100644 --- a/drivers/spi/spi-npcm-fiu.c +++ b/drivers/spi/spi-npcm-fiu.c @@ -699,7 +699,6 @@ static int npcm_fiu_probe(struct platform_device *pdev) struct spi_controller *ctrl; struct npcm_fiu_spi *fiu; void __iomem *regbase; - struct resource *res; int id, ret; ctrl = devm_spi_alloc_master(dev, sizeof(*fiu)); @@ -725,8 +724,7 @@ static int npcm_fiu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fiu); fiu->dev = dev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); - regbase = devm_ioremap_resource(dev, res); + regbase = devm_platform_ioremap_resource_byname(pdev, "control"); if (IS_ERR(regbase)) return PTR_ERR(regbase); From 347ad8f295c66f3193d57cc5b69b6138f2e24231 Mon Sep 17 00:00:00 2001 From: Yang Yingliang <yangyingliang@huawei.com> Date: Sat, 29 Oct 2022 15:17:20 +0800 Subject: [PATCH 29/66] spi: mxic: Use devm_platform_ioremap_resource_byname() Use the devm_platform_ioremap_resource_byname() helper instead of calling platform_get_resource_byname() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang <yangyingliang@huawei.com> Link: https://lore.kernel.org/r/20221029071720.3041094-1-yangyingliang@huawei.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-mxic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c index 65be8e085ab83..a3dba17390eba 100644 --- a/drivers/spi/spi-mxic.c +++ b/drivers/spi/spi-mxic.c @@ -772,8 +772,7 @@ static int mxic_spi_probe(struct platform_device *pdev) if (IS_ERR(mxic->send_dly_clk)) return PTR_ERR(mxic->send_dly_clk); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - mxic->regs = devm_ioremap_resource(&pdev->dev, res); + mxic->regs = devm_platform_ioremap_resource_byname(pdev, "regs"); if (IS_ERR(mxic->regs)) return PTR_ERR(mxic->regs); From e8d6e1dd609696128e646a8c747b0f3fb1e02545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Mon, 31 Oct 2022 23:25:59 +0100 Subject: [PATCH 30/66] spi: nuvoton,npcm-fiu: Change spi-nor@0 name to flash@0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The node name for flash memories has been standardized to "flash@...". Fix the example in nuvoton,npcm-fiu.txt accordingly. Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Link: https://lore.kernel.org/r/20221031222559.199509-1-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt b/Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt index c63ce4cc0a80f..fb38e96d395fb 100644 --- a/Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt +++ b/Documentation/devicetree/bindings/spi/nuvoton,npcm-fiu.txt @@ -51,7 +51,7 @@ fiu3: spi@c00000000 { clocks = <&clk NPCM7XX_CLK_AHB>; pinctrl-names = "default"; pinctrl-0 = <&spi3_pins>; - spi-nor@0 { + flash@0 { ... }; }; From bf585ccee22faf469d82727cf375868105b362f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Tue, 1 Nov 2022 18:32:51 +0100 Subject: [PATCH 31/66] spi: Update reference to struct spi_controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct spi_master has been renamed to struct spi_controller. Update the reference in spi.rst to make it clickable again. Fixes: 8caab75fd2c2 ("spi: Generalize SPI "master" to "controller"") Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Link: https://lore.kernel.org/r/20221101173252.1069294-1-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- Documentation/driver-api/spi.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/spi.rst b/Documentation/driver-api/spi.rst index f64cb666498aa..f28887045049d 100644 --- a/Documentation/driver-api/spi.rst +++ b/Documentation/driver-api/spi.rst @@ -25,8 +25,8 @@ hardware, which may be as simple as a set of GPIO pins or as complex as a pair of FIFOs connected to dual DMA engines on the other side of the SPI shift register (maximizing throughput). Such drivers bridge between whatever bus they sit on (often the platform bus) and SPI, and expose -the SPI side of their device as a :c:type:`struct spi_master -<spi_master>`. SPI devices are children of that master, +the SPI side of their device as a :c:type:`struct spi_controller +<spi_controller>`. SPI devices are children of that master, represented as a :c:type:`struct spi_device <spi_device>` and manufactured from :c:type:`struct spi_board_info <spi_board_info>` descriptors which are usually provided by From b8d3b056a78dcc941fd1a117697ab2b956c2953f Mon Sep 17 00:00:00 2001 From: Yang Yingliang <yangyingliang@huawei.com> Date: Tue, 11 Oct 2022 17:22:04 +0800 Subject: [PATCH 32/66] spi: introduce new helpers with using modern naming For using modern names host/target to instead of all the legacy names, I think it takes 3 steps: - step1: introduce new helpers with modern naming. - step2: switch to use these new helpers in all drivers. - step3: remove all legacy helpers and update all legacy names. This patch is for step1, it introduces new helpers with host/target naming for drivers using. Signed-off-by: Yang Yingliang <yangyingliang@huawei.com> Link: https://lore.kernel.org/r/20221011092204.950288-1-yangyingliang@huawei.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi.c | 11 ++++++++++ include/linux/spi/spi.h | 47 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 8df8b93df1c31..4ddd250481f5a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2771,6 +2771,17 @@ int spi_slave_abort(struct spi_device *spi) } EXPORT_SYMBOL_GPL(spi_slave_abort); +int spi_target_abort(struct spi_device *spi) +{ + struct spi_controller *ctlr = spi->controller; + + if (spi_controller_is_target(ctlr) && ctlr->target_abort) + return ctlr->target_abort(ctlr); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(spi_target_abort); + static ssize_t slave_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 8fe3d0a9d2c9e..798c30229e07e 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -356,6 +356,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * @max_speed_hz: Highest supported transfer speed * @flags: other constraints relevant to this driver * @slave: indicates that this is an SPI slave controller + * @target: indicates that this is an SPI target controller * @devm_allocated: whether the allocation of this struct is devres-managed * @max_transfer_size: function that returns the max transfer size for * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. @@ -440,6 +441,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * @mem_caps: controller capabilities for the handling of memory operations. * @unprepare_message: undo any work done by prepare_message(). * @slave_abort: abort the ongoing transfer request on an SPI slave controller + * @target_abort: abort the ongoing transfer request on an SPI target controller * @cs_gpiods: Array of GPIO descs to use as chip select lines; one per CS * number. Any individual value may be NULL for CS lines that * are not GPIOs (driven by the SPI controller itself). @@ -535,8 +537,12 @@ struct spi_controller { /* Flag indicating if the allocation of this struct is devres-managed */ bool devm_allocated; - /* Flag indicating this is an SPI slave controller */ - bool slave; + union { + /* Flag indicating this is an SPI slave controller */ + bool slave; + /* Flag indicating this is an SPI target controller */ + bool target; + }; /* * on some hardware transfer / message size may be constrained @@ -650,6 +656,7 @@ struct spi_controller { int (*unprepare_message)(struct spi_controller *ctlr, struct spi_message *message); int (*slave_abort)(struct spi_controller *ctlr); + int (*target_abort)(struct spi_controller *ctlr); /* * These hooks are for drivers that use a generic implementation @@ -727,6 +734,11 @@ static inline bool spi_controller_is_slave(struct spi_controller *ctlr) return IS_ENABLED(CONFIG_SPI_SLAVE) && ctlr->slave; } +static inline bool spi_controller_is_target(struct spi_controller *ctlr) +{ + return IS_ENABLED(CONFIG_SPI_SLAVE) && ctlr->target; +} + /* PM calls that need to be issued by the driver */ extern int spi_controller_suspend(struct spi_controller *ctlr); extern int spi_controller_resume(struct spi_controller *ctlr); @@ -763,6 +775,21 @@ static inline struct spi_controller *spi_alloc_slave(struct device *host, return __spi_alloc_controller(host, size, true); } +static inline struct spi_controller *spi_alloc_host(struct device *dev, + unsigned int size) +{ + return __spi_alloc_controller(dev, size, false); +} + +static inline struct spi_controller *spi_alloc_target(struct device *dev, + unsigned int size) +{ + if (!IS_ENABLED(CONFIG_SPI_SLAVE)) + return NULL; + + return __spi_alloc_controller(dev, size, true); +} + struct spi_controller *__devm_spi_alloc_controller(struct device *dev, unsigned int size, bool slave); @@ -782,6 +809,21 @@ static inline struct spi_controller *devm_spi_alloc_slave(struct device *dev, return __devm_spi_alloc_controller(dev, size, true); } +static inline struct spi_controller *devm_spi_alloc_host(struct device *dev, + unsigned int size) +{ + return __devm_spi_alloc_controller(dev, size, false); +} + +static inline struct spi_controller *devm_spi_alloc_target(struct device *dev, + unsigned int size) +{ + if (!IS_ENABLED(CONFIG_SPI_SLAVE)) + return NULL; + + return __devm_spi_alloc_controller(dev, size, true); +} + extern int spi_register_controller(struct spi_controller *ctlr); extern int devm_spi_register_controller(struct device *dev, struct spi_controller *ctlr); @@ -1141,6 +1183,7 @@ static inline void spi_message_free(struct spi_message *m) extern int spi_setup(struct spi_device *spi); extern int spi_async(struct spi_device *spi, struct spi_message *message); extern int spi_slave_abort(struct spi_device *spi); +extern int spi_target_abort(struct spi_device *spi); static inline size_t spi_max_message_size(struct spi_device *spi) From 6c6c49f2c0993ab3ea4bb71a12c800afd0c59e30 Mon Sep 17 00:00:00 2001 From: Colin Ian King <colin.i.king@gmail.com> Date: Wed, 2 Nov 2022 15:29:04 +0000 Subject: [PATCH 33/66] spi: nxp-fspi: make const array ls1028a_soc_attr static Don't populate the const array ls1028a_soc_attr on the stack, instead make it static. Also makes the object code smaller. Signed-off-by: Colin Ian King <colin.i.king@gmail.com> Acked-by: Han Xu <han.xu@nxp.com> Link: https://lore.kernel.org/r/20221102152904.143423-1-colin.i.king@gmail.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-nxp-fspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index d6a65a989ef80..1c1991a26c153 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -924,7 +924,7 @@ static int nxp_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) static void erratum_err050568(struct nxp_fspi *f) { - const struct soc_device_attribute ls1028a_soc_attr[] = { + static const struct soc_device_attribute ls1028a_soc_attr[] = { { .family = "QorIQ LS1028A" }, { /* sentinel */ } }; From d82316d3cf1fcecd461b80ddb97d9ea6d7cba60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Thu, 3 Nov 2022 20:00:52 +0100 Subject: [PATCH 34/66] spi: hisi-sfc-v3xx: Fix a typo ("duall") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simple typo, simple fix. Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Link: https://lore.kernel.org/r/20221103190052.915755-1-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-hisi-sfc-v3xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index d3a23b1c2a4c5..f07d1045a30a2 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -165,7 +165,7 @@ static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem, } /* - * The controller only supports Standard SPI mode, Duall mode and + * The controller only supports Standard SPI mode, Dual mode and * Quad mode. Double sanitize the ops here to avoid OOB access. */ static bool hisi_sfc_v3xx_supports_op(struct spi_mem *mem, From 6c6871cdaef96361f6b79a3e45d451a6475df4d6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Fri, 4 Nov 2022 11:01:27 +0100 Subject: [PATCH 35/66] spi: Merge spi_controller.{slave,target}_abort() Mixing SPI slave/target handlers and SPI slave/target controllers using legacy and modern naming does not work well: there are now two different callbacks for aborting a slave/target operation, of which only one is populated, while spi_{slave,target}_abort() check and use only one, which may be the unpopulated one. Fix this by merging the slave/target abort callbacks into a single callback using a union, like is already done for the slave/target flags. Fixes: b8d3b056a78dcc94 ("spi: introduce new helpers with using modern naming") Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Link: https://lore.kernel.org/r/809c82d54b85dd87ef7ee69fc93016085be85cec.1667555967.git.geert+renesas@glider.be Signed-off-by: Mark Brown <broonie@kernel.org> --- include/linux/spi/spi.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 798c30229e07e..9a32495fbb1fd 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -655,8 +655,10 @@ struct spi_controller { struct spi_message *message); int (*unprepare_message)(struct spi_controller *ctlr, struct spi_message *message); - int (*slave_abort)(struct spi_controller *ctlr); - int (*target_abort)(struct spi_controller *ctlr); + union { + int (*slave_abort)(struct spi_controller *ctlr); + int (*target_abort)(struct spi_controller *ctlr); + }; /* * These hooks are for drivers that use a generic implementation From d52a826b40604387d3e24b54e12e404867902fbb Mon Sep 17 00:00:00 2001 From: bayi cheng <bayi.cheng@mediatek.com> Date: Mon, 14 Nov 2022 16:13:27 +0800 Subject: [PATCH 36/66] spi: spi-mtk-nor: Optimize timeout for dma read The timeout value of the current dma read is unreasonable. For example, If the spi flash clock is 26Mhz, It will takes about 1.3ms to read a 4KB data in spi mode. But the actual measurement exceeds 50s when a dma read timeout is encountered. In order to be more accurately, It is necessary to use usecs_to_jiffies, After modification, the measured timeout value is about 130ms. Signed-off-by: bayi cheng <bayi.cheng@mediatek.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Link: https://lore.kernel.org/r/20221114081327.25750-1-bayi.cheng@mediatek.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-mtk-nor.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index d167699a1a96b..58eca18b28b03 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -354,7 +354,7 @@ static int mtk_nor_dma_exec(struct mtk_nor *sp, u32 from, unsigned int length, dma_addr_t dma_addr) { int ret = 0; - ulong delay; + u32 delay, timeout; u32 reg; writel(from, sp->base + MTK_NOR_REG_DMA_FADR); @@ -376,15 +376,16 @@ static int mtk_nor_dma_exec(struct mtk_nor *sp, u32 from, unsigned int length, mtk_nor_rmw(sp, MTK_NOR_REG_DMA_CTL, MTK_NOR_DMA_START, 0); delay = CLK_TO_US(sp, (length + 5) * BITS_PER_BYTE); + timeout = (delay + 1) * 100; if (sp->has_irq) { if (!wait_for_completion_timeout(&sp->op_done, - (delay + 1) * 100)) + usecs_to_jiffies(max(timeout, 10000U)))) ret = -ETIMEDOUT; } else { ret = readl_poll_timeout(sp->base + MTK_NOR_REG_DMA_CTL, reg, !(reg & MTK_NOR_DMA_START), delay / 3, - (delay + 1) * 100); + timeout); } if (ret < 0) From 84b60f2bce1f0416617c4dbc015b5da906b8e2ad Mon Sep 17 00:00:00 2001 From: "Radu Pirea (NXP OSS)" <radu-nicolae.pirea@oss.nxp.com> Date: Fri, 11 Nov 2022 23:13:56 +0200 Subject: [PATCH 37/66] spi: fsl-dspi: add cs-gpios support Make the driver be able to bit-bang a GPIO for the Chip Select pin of select peripherals. The GPIO value is driven by the driver in that case, and none of the hardware Chip Select bits will be populated in the PUSHR register for the TX commands constructed for this peripheral. Signed-off-by: Radu Pirea (NXP OSS) <radu-nicolae.pirea@oss.nxp.com> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Link: https://lore.kernel.org/r/20221111211356.545026-1-vladimir.oltean@nxp.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-fsl-dspi.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index a33e547b7d395..e419642eb10e5 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -900,12 +900,31 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static void dspi_assert_cs(struct spi_device *spi, bool *cs) +{ + if (!spi->cs_gpiod || *cs) + return; + + gpiod_set_value_cansleep(spi->cs_gpiod, true); + *cs = true; +} + +static void dspi_deassert_cs(struct spi_device *spi, bool *cs) +{ + if (!spi->cs_gpiod || !*cs) + return; + + gpiod_set_value_cansleep(spi->cs_gpiod, false); + *cs = false; +} + static int dspi_transfer_one_message(struct spi_controller *ctlr, struct spi_message *message) { struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr); struct spi_device *spi = message->spi; struct spi_transfer *transfer; + bool cs = false; int status = 0; message->actual_length = 0; @@ -914,9 +933,14 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr, dspi->cur_transfer = transfer; dspi->cur_msg = message; dspi->cur_chip = spi_get_ctldata(spi); + + dspi_assert_cs(spi, &cs); + /* Prepare command word for CMD FIFO */ - dspi->tx_cmd = SPI_PUSHR_CMD_CTAS(0) | - SPI_PUSHR_CMD_PCS(spi->chip_select); + dspi->tx_cmd = SPI_PUSHR_CMD_CTAS(0); + if (!spi->cs_gpiod) + dspi->tx_cmd |= SPI_PUSHR_CMD_PCS(spi->chip_select); + if (list_is_last(&dspi->cur_transfer->transfer_list, &dspi->cur_msg->transfers)) { /* Leave PCS activated after last transfer when @@ -964,6 +988,9 @@ static int dspi_transfer_one_message(struct spi_controller *ctlr, break; spi_transfer_delay_exec(transfer); + + if (!(dspi->tx_cmd & SPI_PUSHR_CMD_CONT)) + dspi_deassert_cs(spi, &cs); } message->status = status; @@ -981,6 +1008,7 @@ static int dspi_setup(struct spi_device *spi) unsigned char pasc = 0, asc = 0; struct chip_data *chip; unsigned long clkrate; + bool cs = true; /* Only alloc on first setup */ chip = spi_get_ctldata(spi); @@ -1030,6 +1058,9 @@ static int dspi_setup(struct spi_device *spi) chip->ctar_val |= SPI_CTAR_LSBFE; } + gpiod_direction_output(spi->cs_gpiod, false); + dspi_deassert_cs(spi, &cs); + spi_set_ctldata(spi, chip); return 0; @@ -1248,6 +1279,7 @@ static int dspi_probe(struct platform_device *pdev) ctlr->cleanup = dspi_cleanup; ctlr->slave_abort = dspi_slave_abort; ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + ctlr->use_gpio_descriptors = true; pdata = dev_get_platdata(&pdev->dev); if (pdata) { From f6c911f3308c1cfb97ae1da6654080d7104e2df2 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus <tudor.ambarus@microchip.com> Date: Thu, 17 Nov 2022 12:52:42 +0200 Subject: [PATCH 38/66] spi: dt-bindings: Introduce spi-cs-setup-ns property SPI NOR flashes have specific cs-setup time requirements without which they can't work at frequencies close to their maximum supported frequency, as they miss the first bits of the instruction command. Unrecognized commands are ignored, thus the flash will be unresponsive. Introduce the spi-cs-setup-ns property to allow spi devices to specify their cs setup time. Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> Link: https://lore.kernel.org/r/20221117105249.115649-2-tudor.ambarus@microchip.com Signed-off-by: Mark Brown <broonie@kernel.org> --- .../devicetree/bindings/spi/spi-peripheral-props.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml index dca677f9e1b98..ead2cccf658fd 100644 --- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml @@ -44,6 +44,11 @@ properties: description: Maximum SPI clocking speed of the device in Hz. + spi-cs-setup-ns: + description: + Delay in nanosecods to be introduced by the controller after CS is + asserted. + spi-rx-bus-width: description: Bus width to the SPI bus used for read transfers. From 33a2fde5f77bd744b8bd0c694bc173cc968e55a5 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus <tudor.ambarus@microchip.com> Date: Thu, 17 Nov 2022 12:52:43 +0200 Subject: [PATCH 39/66] spi: Introduce spi-cs-setup-ns property SPI NOR flashes have specific cs-setup time requirements without which they can't work at frequencies close to their maximum supported frequency, as they miss the first bits of the instruction command. Unrecognized commands are ignored, thus the flash will be unresponsive. Introduce the spi-cs-setup-ns property to allow spi devices to specify their cs setup time. Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> Link: https://lore.kernel.org/r/20221117105249.115649-3-tudor.ambarus@microchip.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4ddd250481f5a..b93a6085d9a0f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2224,6 +2224,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, struct device_node *nc) { u32 value; + u16 cs_setup; int rc; /* Mode (clock phase/polarity/etc.) */ @@ -2309,6 +2310,11 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, if (!of_property_read_u32(nc, "spi-max-frequency", &value)) spi->max_speed_hz = value; + if (!of_property_read_u16(nc, "spi-cs-setup-ns", &cs_setup)) { + spi->cs_setup.value = cs_setup; + spi->cs_setup.unit = SPI_DELAY_UNIT_NSECS; + } + return 0; } From 684a47847ae639689e7b823251975348a8e5434f Mon Sep 17 00:00:00 2001 From: Tudor Ambarus <tudor.ambarus@microchip.com> Date: Thu, 17 Nov 2022 12:52:44 +0200 Subject: [PATCH 40/66] spi: Reintroduce spi_set_cs_timing() commit 4ccf359849ce ("spi: remove spi_set_cs_timing()"), removed the method as noboby used it. Nobody used it probably because some SPI controllers use some default large cs-setup time that covers the usual cs-setup time required by the spi devices. There are though SPI controllers that have a smaller granularity for the cs-setup time and their default value can't fulfill the spi device requirements. That's the case for the at91 QSPI IPs where the default cs-setup time is half of the QSPI clock period. This was observed when using an sst26vf064b SPI NOR flash which needs a spi-cs-setup-ns = <7>; in order to be operated close to its maximum 104 MHz frequency. Call spi_set_cs_timing() in spi_setup() just before calling spi_set_cs(), as the latter needs the CS timings already set. If spi->controller->set_cs_timing is not set, the method will return 0. There's no functional impact expected for the existing drivers. Even if the spi-mt65xx.c and spi-tegra114.c drivers set the set_cs_timing method, there's no user for them as of now. The only tested user of this support will be a SPI NOR flash that comunicates with the Atmel QSPI controller for which the support follows in the next patches. One will notice that this support is a bit different from the one that was removed in commit 4ccf359849ce ("spi: remove spi_set_cs_timing()"), because this patch adapts to the changes done after the removal: the move of the cs delays to the spi device, the retirement of the lelgacy GPIO handling. The mutex handling was removed from spi_set_cs_timing() because we now always call spi_set_cs_timing() in spi_setup(), which already handles the spi->controller->io_mutex, so use the mutex handling from spi_setup(). Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> Link: https://lore.kernel.org/r/20221117105249.115649-4-tudor.ambarus@microchip.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b93a6085d9a0f..3cc7bb4d03dec 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3621,6 +3621,37 @@ static int __spi_validate_bits_per_word(struct spi_controller *ctlr, return 0; } +/** + * spi_set_cs_timing - configure CS setup, hold, and inactive delays + * @spi: the device that requires specific CS timing configuration + * + * Return: zero on success, else a negative error code. + */ +static int spi_set_cs_timing(struct spi_device *spi) +{ + struct device *parent = spi->controller->dev.parent; + int status = 0; + + if (spi->controller->set_cs_timing && !spi->cs_gpiod) { + if (spi->controller->auto_runtime_pm) { + status = pm_runtime_get_sync(parent); + if (status < 0) { + pm_runtime_put_noidle(parent); + dev_err(&spi->controller->dev, "Failed to power device: %d\n", + status); + return status; + } + + status = spi->controller->set_cs_timing(spi); + pm_runtime_mark_last_busy(parent); + pm_runtime_put_autosuspend(parent); + } else { + status = spi->controller->set_cs_timing(spi); + } + } + return status; +} + /** * spi_setup - setup SPI mode and clock rate * @spi: the device whose settings are being modified @@ -3717,6 +3748,12 @@ int spi_setup(struct spi_device *spi) } } + status = spi_set_cs_timing(spi); + if (status) { + mutex_unlock(&spi->controller->io_mutex); + return status; + } + if (spi->controller->auto_runtime_pm && spi->controller->set_cs) { status = pm_runtime_resume_and_get(spi->controller->dev.parent); if (status < 0) { From f732646d0ccd22f42ed7de5e59c0abb7a848e034 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus <tudor.ambarus@microchip.com> Date: Thu, 17 Nov 2022 12:52:45 +0200 Subject: [PATCH 41/66] spi: atmel-quadspi: Add support for configuring CS timing The at91 QSPI IP uses a default value of half of the period of the QSPI clock period for the cs-setup time, which is not always enough, an example being the sst26vf064b SPI NOR flash which requires a minimum cs-setup time of 5 ns. It was observed that none of the at91 SoCs can fulfill the minimum CS setup time for the aforementioned flash, as they operate at high frequencies and half a period does not suffice for the required CS setup time. Add support for configuring the CS timing in the controller. Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com> Link: https://lore.kernel.org/r/20221117105249.115649-5-tudor.ambarus@microchip.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/atmel-quadspi.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 976a217e356d5..70637e46290a4 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -510,6 +510,39 @@ static int atmel_qspi_setup(struct spi_device *spi) return 0; } +static int atmel_qspi_set_cs_timing(struct spi_device *spi) +{ + struct spi_controller *ctrl = spi->master; + struct atmel_qspi *aq = spi_controller_get_devdata(ctrl); + unsigned long clk_rate; + u32 cs_setup; + int delay; + int ret; + + delay = spi_delay_to_ns(&spi->cs_setup, NULL); + if (delay <= 0) + return delay; + + clk_rate = clk_get_rate(aq->pclk); + if (!clk_rate) + return -EINVAL; + + cs_setup = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)), + 1000); + + ret = pm_runtime_resume_and_get(ctrl->dev.parent); + if (ret < 0) + return ret; + + aq->scr |= QSPI_SCR_DLYBS(cs_setup); + atmel_qspi_write(aq->scr, aq, QSPI_SCR); + + pm_runtime_mark_last_busy(ctrl->dev.parent); + pm_runtime_put_autosuspend(ctrl->dev.parent); + + return 0; +} + static void atmel_qspi_init(struct atmel_qspi *aq) { /* Reset the QSPI controller */ @@ -555,6 +588,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; ctrl->setup = atmel_qspi_setup; + ctrl->set_cs_timing = atmel_qspi_set_cs_timing; ctrl->bus_num = -1; ctrl->mem_ops = &atmel_qspi_mem_ops; ctrl->num_chipselect = 1; From c7f635bc16cefcd7c7ab6e508b2ae73d66af9ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> Date: Fri, 18 Nov 2022 23:44:58 +0100 Subject: [PATCH 42/66] spi: sc18is602: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .probe_new() doesn't get the i2c_device_id * parameter, so determine that explicitly in the probe function. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-for-MFD-by: Lee Jones <lee@kernel.org> Acked-for-Backlight-by: Lee Jones <lee@kernel.org> Link: https://lore.kernel.org/r/20221118224540.619276-565-uwe@kleine-koenig.org Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-sc18is602.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 5d27ee4822376..983b3621bc2ad 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -235,9 +235,9 @@ static int sc18is602_setup(struct spi_device *spi) return 0; } -static int sc18is602_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sc18is602_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device *dev = &client->dev; struct device_node *np = dev->of_node; struct sc18is602_platform_data *pdata = dev_get_platdata(dev); @@ -337,7 +337,7 @@ static struct i2c_driver sc18is602_driver = { .name = "sc18is602", .of_match_table = of_match_ptr(sc18is602_of_match), }, - .probe = sc18is602_probe, + .probe_new = sc18is602_probe, .id_table = sc18is602_id, }; From 1a165a067ffdba66af3a696f49dfab24a0e0449e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de> Date: Fri, 18 Nov 2022 23:44:59 +0100 Subject: [PATCH 43/66] spi: xcomm: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-for-MFD-by: Lee Jones <lee@kernel.org> Acked-for-Backlight-by: Lee Jones <lee@kernel.org> Link: https://lore.kernel.org/r/20221118224540.619276-566-uwe@kleine-koenig.org Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-xcomm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 1d9b3f03d986e..8628241ec99e4 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -202,8 +202,7 @@ static int spi_xcomm_transfer_one(struct spi_master *master, return status; } -static int spi_xcomm_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int spi_xcomm_probe(struct i2c_client *i2c) { struct spi_xcomm *spi_xcomm; struct spi_master *master; @@ -242,7 +241,7 @@ static struct i2c_driver spi_xcomm_driver = { .name = "spi-xcomm", }, .id_table = spi_xcomm_ids, - .probe = spi_xcomm_probe, + .probe_new = spi_xcomm_probe, }; module_i2c_driver(spi_xcomm_driver); From 1b74dd64c8612619e399e5a31da79a3636914495 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Date: Thu, 24 Nov 2022 09:33:51 +0900 Subject: [PATCH 44/66] spi: Add Socionext F_OSPI SPI flash controller driver Introduce Socionext F_OSPI controller driver. This controller is used to communicate with slave devices such as SPI Flash memories. It supports 4 slave devices and up to 8-bit wide bus, but supports master mode only. This driver uses spi-mem framework for SPI flash memory access, and can only operate indirect access mode and single data rate mode. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Link: https://lore.kernel.org/r/20221124003351.7792-3-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-sn-f-ospi.c | 703 ++++++++++++++++++++++++++++++++++++ 3 files changed, 713 insertions(+) create mode 100644 drivers/spi/spi-sn-f-ospi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index cb7e3a8ef3a5d..c6fb8cd6d2aeb 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -906,6 +906,15 @@ config SPI_SLAVE_MT27XX say Y or M here.If you are not sure, say N. SPI slave drivers for Mediatek MT27XX series ARM SoCs. +config SPI_SN_F_OSPI + tristate "Socionext F_OSPI SPI flash controller" + depends on OF && HAS_IOMEM + depends on SPI_MEM + help + This enables support for the Socionext F_OSPI controller + for connecting an SPI Flash memory over up to 8-bit wide bus. + It supports indirect access mode only. + config SPI_SPRD tristate "Spreadtrum SPI controller" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 60d0b2f611f1d..971bd62e44816 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o obj-$(CONFIG_SPI_SLAVE_MT27XX) += spi-slave-mt27xx.o +obj-$(CONFIG_SPI_SN_F_OSPI) += spi-sn-f-ospi.o obj-$(CONFIG_SPI_SPRD) += spi-sprd.o obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o obj-$(CONFIG_SPI_STM32) += spi-stm32.o diff --git a/drivers/spi/spi-sn-f-ospi.c b/drivers/spi/spi-sn-f-ospi.c new file mode 100644 index 0000000000000..348c6e1edd38a --- /dev/null +++ b/drivers/spi/spi-sn-f-ospi.c @@ -0,0 +1,703 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Socionext SPI flash controller F_OSPI driver + * Copyright (C) 2021 Socionext Inc. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> + +/* Registers */ +#define OSPI_PROT_CTL_INDIR 0x00 +#define OSPI_PROT_MODE_DATA_MASK GENMASK(31, 30) +#define OSPI_PROT_MODE_ALT_MASK GENMASK(29, 28) +#define OSPI_PROT_MODE_ADDR_MASK GENMASK(27, 26) +#define OSPI_PROT_MODE_CODE_MASK GENMASK(25, 24) +#define OSPI_PROT_MODE_SINGLE 0 +#define OSPI_PROT_MODE_DUAL 1 +#define OSPI_PROT_MODE_QUAD 2 +#define OSPI_PROT_MODE_OCTAL 3 +#define OSPI_PROT_DATA_RATE_DATA BIT(23) +#define OSPI_PROT_DATA_RATE_ALT BIT(22) +#define OSPI_PROT_DATA_RATE_ADDR BIT(21) +#define OSPI_PROT_DATA_RATE_CODE BIT(20) +#define OSPI_PROT_SDR 0 +#define OSPI_PROT_DDR 1 +#define OSPI_PROT_BIT_POS_DATA BIT(19) +#define OSPI_PROT_BIT_POS_ALT BIT(18) +#define OSPI_PROT_BIT_POS_ADDR BIT(17) +#define OSPI_PROT_BIT_POS_CODE BIT(16) +#define OSPI_PROT_SAMP_EDGE BIT(12) +#define OSPI_PROT_DATA_UNIT_MASK GENMASK(11, 10) +#define OSPI_PROT_DATA_UNIT_1B 0 +#define OSPI_PROT_DATA_UNIT_2B 1 +#define OSPI_PROT_DATA_UNIT_4B 3 +#define OSPI_PROT_TRANS_DIR_WRITE BIT(9) +#define OSPI_PROT_DATA_EN BIT(8) +#define OSPI_PROT_ALT_SIZE_MASK GENMASK(7, 5) +#define OSPI_PROT_ADDR_SIZE_MASK GENMASK(4, 2) +#define OSPI_PROT_CODE_SIZE_MASK GENMASK(1, 0) + +#define OSPI_CLK_CTL 0x10 +#define OSPI_CLK_CTL_BOOT_INT_CLK_EN BIT(16) +#define OSPI_CLK_CTL_PHA BIT(12) +#define OSPI_CLK_CTL_PHA_180 0 +#define OSPI_CLK_CTL_PHA_90 1 +#define OSPI_CLK_CTL_DIV GENMASK(9, 8) +#define OSPI_CLK_CTL_DIV_1 0 +#define OSPI_CLK_CTL_DIV_2 1 +#define OSPI_CLK_CTL_DIV_4 2 +#define OSPI_CLK_CTL_DIV_8 3 +#define OSPI_CLK_CTL_INT_CLK_EN BIT(0) + +#define OSPI_CS_CTL1 0x14 +#define OSPI_CS_CTL2 0x18 +#define OSPI_SSEL 0x20 +#define OSPI_CMD_IDX_INDIR 0x40 +#define OSPI_ADDR 0x50 +#define OSPI_ALT_INDIR 0x60 +#define OSPI_DMY_INDIR 0x70 +#define OSPI_DAT 0x80 +#define OSPI_DAT_SWP_INDIR 0x90 + +#define OSPI_DAT_SIZE_INDIR 0xA0 +#define OSPI_DAT_SIZE_EN BIT(15) +#define OSPI_DAT_SIZE_MASK GENMASK(10, 0) +#define OSPI_DAT_SIZE_MAX (OSPI_DAT_SIZE_MASK + 1) + +#define OSPI_TRANS_CTL 0xC0 +#define OSPI_TRANS_CTL_STOP_REQ BIT(1) /* RW1AC */ +#define OSPI_TRANS_CTL_START_REQ BIT(0) /* RW1AC */ + +#define OSPI_ACC_MODE 0xC4 +#define OSPI_ACC_MODE_BOOT_DISABLE BIT(0) + +#define OSPI_SWRST 0xD0 +#define OSPI_SWRST_INDIR_WRITE_FIFO BIT(9) /* RW1AC */ +#define OSPI_SWRST_INDIR_READ_FIFO BIT(8) /* RW1AC */ + +#define OSPI_STAT 0xE0 +#define OSPI_STAT_IS_AXI_WRITING BIT(10) +#define OSPI_STAT_IS_AXI_READING BIT(9) +#define OSPI_STAT_IS_SPI_INT_CLK_STOP BIT(4) +#define OSPI_STAT_IS_SPI_IDLE BIT(3) + +#define OSPI_IRQ 0xF0 +#define OSPI_IRQ_CS_DEASSERT BIT(8) +#define OSPI_IRQ_WRITE_BUF_READY BIT(2) +#define OSPI_IRQ_READ_BUF_READY BIT(1) +#define OSPI_IRQ_CS_TRANS_COMP BIT(0) +#define OSPI_IRQ_ALL \ + (OSPI_IRQ_CS_DEASSERT | OSPI_IRQ_WRITE_BUF_READY \ + | OSPI_IRQ_READ_BUF_READY | OSPI_IRQ_CS_TRANS_COMP) + +#define OSPI_IRQ_STAT_EN 0xF4 +#define OSPI_IRQ_SIG_EN 0xF8 + +/* Parameters */ +#define OSPI_NUM_CS 4 +#define OSPI_DUMMY_CYCLE_MAX 255 +#define OSPI_WAIT_MAX_MSEC 100 + +struct f_ospi { + void __iomem *base; + struct device *dev; + struct clk *clk; + struct mutex mlock; +}; + +static u32 f_ospi_get_dummy_cycle(const struct spi_mem_op *op) +{ + return (op->dummy.nbytes * 8) / op->dummy.buswidth; +} + +static void f_ospi_clear_irq(struct f_ospi *ospi) +{ + writel(OSPI_IRQ_CS_DEASSERT | OSPI_IRQ_CS_TRANS_COMP, + ospi->base + OSPI_IRQ); +} + +static void f_ospi_enable_irq_status(struct f_ospi *ospi, u32 irq_bits) +{ + u32 val; + + val = readl(ospi->base + OSPI_IRQ_STAT_EN); + val |= irq_bits; + writel(val, ospi->base + OSPI_IRQ_STAT_EN); +} + +static void f_ospi_disable_irq_status(struct f_ospi *ospi, u32 irq_bits) +{ + u32 val; + + val = readl(ospi->base + OSPI_IRQ_STAT_EN); + val &= ~irq_bits; + writel(val, ospi->base + OSPI_IRQ_STAT_EN); +} + +static void f_ospi_disable_irq_output(struct f_ospi *ospi, u32 irq_bits) +{ + u32 val; + + val = readl(ospi->base + OSPI_IRQ_SIG_EN); + val &= ~irq_bits; + writel(val, ospi->base + OSPI_IRQ_SIG_EN); +} + +static int f_ospi_prepare_config(struct f_ospi *ospi) +{ + u32 val, stat0, stat1; + + /* G4: Disable internal clock */ + val = readl(ospi->base + OSPI_CLK_CTL); + val &= ~(OSPI_CLK_CTL_BOOT_INT_CLK_EN | OSPI_CLK_CTL_INT_CLK_EN); + writel(val, ospi->base + OSPI_CLK_CTL); + + /* G5: Wait for stop */ + stat0 = OSPI_STAT_IS_AXI_WRITING | OSPI_STAT_IS_AXI_READING; + stat1 = OSPI_STAT_IS_SPI_IDLE | OSPI_STAT_IS_SPI_INT_CLK_STOP; + + return readl_poll_timeout(ospi->base + OSPI_STAT, + val, (val & (stat0 | stat1)) == stat1, + 0, OSPI_WAIT_MAX_MSEC); +} + +static int f_ospi_unprepare_config(struct f_ospi *ospi) +{ + u32 val; + + /* G11: Enable internal clock */ + val = readl(ospi->base + OSPI_CLK_CTL); + val |= OSPI_CLK_CTL_BOOT_INT_CLK_EN | OSPI_CLK_CTL_INT_CLK_EN; + writel(val, ospi->base + OSPI_CLK_CTL); + + /* G12: Wait for clock to start */ + return readl_poll_timeout(ospi->base + OSPI_STAT, + val, !(val & OSPI_STAT_IS_SPI_INT_CLK_STOP), + 0, OSPI_WAIT_MAX_MSEC); +} + +static void f_ospi_config_clk(struct f_ospi *ospi, u32 device_hz) +{ + long rate_hz = clk_get_rate(ospi->clk); + u32 div = DIV_ROUND_UP(rate_hz, device_hz); + u32 div_reg; + u32 val; + + if (rate_hz < device_hz) { + dev_warn(ospi->dev, "Device frequency too large: %d\n", + device_hz); + div_reg = OSPI_CLK_CTL_DIV_1; + } else { + if (div == 1) { + div_reg = OSPI_CLK_CTL_DIV_1; + } else if (div == 2) { + div_reg = OSPI_CLK_CTL_DIV_2; + } else if (div <= 4) { + div_reg = OSPI_CLK_CTL_DIV_4; + } else if (div <= 8) { + div_reg = OSPI_CLK_CTL_DIV_8; + } else { + dev_warn(ospi->dev, "Device frequency too small: %d\n", + device_hz); + div_reg = OSPI_CLK_CTL_DIV_8; + } + } + + /* + * G7: Set clock mode + * clock phase is fixed at 180 degrees and configure edge direction + * instead. + */ + val = readl(ospi->base + OSPI_CLK_CTL); + + val &= ~(OSPI_CLK_CTL_PHA | OSPI_CLK_CTL_DIV); + val |= FIELD_PREP(OSPI_CLK_CTL_PHA, OSPI_CLK_CTL_PHA_180) + | FIELD_PREP(OSPI_CLK_CTL_DIV, div_reg); + + writel(val, ospi->base + OSPI_CLK_CTL); +} + +static void f_ospi_config_dll(struct f_ospi *ospi) +{ + /* G8: Configure DLL, nothing */ +} + +static u8 f_ospi_get_mode(struct f_ospi *ospi, int width, int data_size) +{ + u8 mode = OSPI_PROT_MODE_SINGLE; + + switch (width) { + case 1: + mode = OSPI_PROT_MODE_SINGLE; + break; + case 2: + mode = OSPI_PROT_MODE_DUAL; + break; + case 4: + mode = OSPI_PROT_MODE_QUAD; + break; + case 8: + mode = OSPI_PROT_MODE_OCTAL; + break; + default: + if (data_size) + dev_err(ospi->dev, "Invalid buswidth: %d\n", width); + break; + } + + return mode; +} + +static void f_ospi_config_indir_protocol(struct f_ospi *ospi, + struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct spi_device *spi = mem->spi; + u8 mode; + u32 prot = 0, val; + int unit; + + /* Set one chip select */ + writel(BIT(spi->chip_select), ospi->base + OSPI_SSEL); + + mode = f_ospi_get_mode(ospi, op->cmd.buswidth, 1); + prot |= FIELD_PREP(OSPI_PROT_MODE_CODE_MASK, mode); + + mode = f_ospi_get_mode(ospi, op->addr.buswidth, op->addr.nbytes); + prot |= FIELD_PREP(OSPI_PROT_MODE_ADDR_MASK, mode); + + mode = f_ospi_get_mode(ospi, op->data.buswidth, op->data.nbytes); + prot |= FIELD_PREP(OSPI_PROT_MODE_DATA_MASK, mode); + + prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_DATA, OSPI_PROT_SDR); + prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_ALT, OSPI_PROT_SDR); + prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_ADDR, OSPI_PROT_SDR); + prot |= FIELD_PREP(OSPI_PROT_DATA_RATE_CODE, OSPI_PROT_SDR); + + if (spi->mode & SPI_LSB_FIRST) + prot |= OSPI_PROT_BIT_POS_DATA | OSPI_PROT_BIT_POS_ALT + | OSPI_PROT_BIT_POS_ADDR | OSPI_PROT_BIT_POS_CODE; + + if (spi->mode & SPI_CPHA) + prot |= OSPI_PROT_SAMP_EDGE; + + /* Examine nbytes % 4 */ + switch (op->data.nbytes & 0x3) { + case 0: + unit = OSPI_PROT_DATA_UNIT_4B; + val = 0; + break; + case 2: + unit = OSPI_PROT_DATA_UNIT_2B; + val = OSPI_DAT_SIZE_EN | (op->data.nbytes - 1); + break; + default: + unit = OSPI_PROT_DATA_UNIT_1B; + val = OSPI_DAT_SIZE_EN | (op->data.nbytes - 1); + break; + } + prot |= FIELD_PREP(OSPI_PROT_DATA_UNIT_MASK, unit); + + switch (op->data.dir) { + case SPI_MEM_DATA_IN: + prot |= OSPI_PROT_DATA_EN; + break; + + case SPI_MEM_DATA_OUT: + prot |= OSPI_PROT_TRANS_DIR_WRITE | OSPI_PROT_DATA_EN; + break; + + case SPI_MEM_NO_DATA: + prot |= OSPI_PROT_TRANS_DIR_WRITE; + break; + + default: + dev_warn(ospi->dev, "Unsupported direction"); + break; + } + + prot |= FIELD_PREP(OSPI_PROT_ADDR_SIZE_MASK, op->addr.nbytes); + prot |= FIELD_PREP(OSPI_PROT_CODE_SIZE_MASK, 1); /* 1byte */ + + writel(prot, ospi->base + OSPI_PROT_CTL_INDIR); + writel(val, ospi->base + OSPI_DAT_SIZE_INDIR); +} + +static int f_ospi_indir_prepare_op(struct f_ospi *ospi, struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct spi_device *spi = mem->spi; + u32 irq_stat_en; + int ret; + + ret = f_ospi_prepare_config(ospi); + if (ret) + return ret; + + f_ospi_config_clk(ospi, spi->max_speed_hz); + + f_ospi_config_indir_protocol(ospi, mem, op); + + writel(f_ospi_get_dummy_cycle(op), ospi->base + OSPI_DMY_INDIR); + writel(op->addr.val, ospi->base + OSPI_ADDR); + writel(op->cmd.opcode, ospi->base + OSPI_CMD_IDX_INDIR); + + f_ospi_clear_irq(ospi); + + switch (op->data.dir) { + case SPI_MEM_DATA_IN: + irq_stat_en = OSPI_IRQ_READ_BUF_READY | OSPI_IRQ_CS_TRANS_COMP; + break; + + case SPI_MEM_DATA_OUT: + irq_stat_en = OSPI_IRQ_WRITE_BUF_READY | OSPI_IRQ_CS_TRANS_COMP; + break; + + case SPI_MEM_NO_DATA: + irq_stat_en = OSPI_IRQ_CS_TRANS_COMP; + break; + + default: + dev_warn(ospi->dev, "Unsupported direction"); + irq_stat_en = 0; + } + + f_ospi_disable_irq_status(ospi, ~irq_stat_en); + f_ospi_enable_irq_status(ospi, irq_stat_en); + + return f_ospi_unprepare_config(ospi); +} + +static void f_ospi_indir_start_xfer(struct f_ospi *ospi) +{ + /* Write only 1, auto cleared */ + writel(OSPI_TRANS_CTL_START_REQ, ospi->base + OSPI_TRANS_CTL); +} + +static void f_ospi_indir_stop_xfer(struct f_ospi *ospi) +{ + /* Write only 1, auto cleared */ + writel(OSPI_TRANS_CTL_STOP_REQ, ospi->base + OSPI_TRANS_CTL); +} + +static int f_ospi_indir_wait_xfer_complete(struct f_ospi *ospi) +{ + u32 val; + + return readl_poll_timeout(ospi->base + OSPI_IRQ, val, + val & OSPI_IRQ_CS_TRANS_COMP, + 0, OSPI_WAIT_MAX_MSEC); +} + +static int f_ospi_indir_read(struct f_ospi *ospi, struct spi_mem *mem, + const struct spi_mem_op *op) +{ + u8 *buf = op->data.buf.in; + u32 val; + int i, ret; + + mutex_lock(&ospi->mlock); + + /* E1-2: Prepare transfer operation */ + ret = f_ospi_indir_prepare_op(ospi, mem, op); + if (ret) + goto out; + + f_ospi_indir_start_xfer(ospi); + + /* E3-4: Wait for ready and read data */ + for (i = 0; i < op->data.nbytes; i++) { + ret = readl_poll_timeout(ospi->base + OSPI_IRQ, val, + val & OSPI_IRQ_READ_BUF_READY, + 0, OSPI_WAIT_MAX_MSEC); + if (ret) + goto out; + + buf[i] = readl(ospi->base + OSPI_DAT) & 0xFF; + } + + /* E5-6: Stop transfer if data size is nothing */ + if (!(readl(ospi->base + OSPI_DAT_SIZE_INDIR) & OSPI_DAT_SIZE_EN)) + f_ospi_indir_stop_xfer(ospi); + + /* E7-8: Wait for completion and clear */ + ret = f_ospi_indir_wait_xfer_complete(ospi); + if (ret) + goto out; + + writel(OSPI_IRQ_CS_TRANS_COMP, ospi->base + OSPI_IRQ); + + /* E9: Do nothing if data size is valid */ + if (readl(ospi->base + OSPI_DAT_SIZE_INDIR) & OSPI_DAT_SIZE_EN) + goto out; + + /* E10-11: Reset and check read fifo */ + writel(OSPI_SWRST_INDIR_READ_FIFO, ospi->base + OSPI_SWRST); + + ret = readl_poll_timeout(ospi->base + OSPI_SWRST, val, + !(val & OSPI_SWRST_INDIR_READ_FIFO), + 0, OSPI_WAIT_MAX_MSEC); +out: + mutex_unlock(&ospi->mlock); + + return ret; +} + +static int f_ospi_indir_write(struct f_ospi *ospi, struct spi_mem *mem, + const struct spi_mem_op *op) +{ + u8 *buf = (u8 *)op->data.buf.out; + u32 val; + int i, ret; + + mutex_lock(&ospi->mlock); + + /* F1-3: Prepare transfer operation */ + ret = f_ospi_indir_prepare_op(ospi, mem, op); + if (ret) + goto out; + + f_ospi_indir_start_xfer(ospi); + + if (!(readl(ospi->base + OSPI_PROT_CTL_INDIR) & OSPI_PROT_DATA_EN)) + goto nodata; + + /* F4-5: Wait for buffer ready and write data */ + for (i = 0; i < op->data.nbytes; i++) { + ret = readl_poll_timeout(ospi->base + OSPI_IRQ, val, + val & OSPI_IRQ_WRITE_BUF_READY, + 0, OSPI_WAIT_MAX_MSEC); + if (ret) + goto out; + + writel(buf[i], ospi->base + OSPI_DAT); + } + + /* F6-7: Stop transfer if data size is nothing */ + if (!(readl(ospi->base + OSPI_DAT_SIZE_INDIR) & OSPI_DAT_SIZE_EN)) + f_ospi_indir_stop_xfer(ospi); + +nodata: + /* F8-9: Wait for completion and clear */ + ret = f_ospi_indir_wait_xfer_complete(ospi); + if (ret) + goto out; + + writel(OSPI_IRQ_CS_TRANS_COMP, ospi->base + OSPI_IRQ); +out: + mutex_unlock(&ospi->mlock); + + return ret; +} + +static int f_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct f_ospi *ospi = spi_controller_get_devdata(mem->spi->master); + int err = 0; + + switch (op->data.dir) { + case SPI_MEM_DATA_IN: + err = f_ospi_indir_read(ospi, mem, op); + break; + + case SPI_MEM_DATA_OUT: + fallthrough; + case SPI_MEM_NO_DATA: + err = f_ospi_indir_write(ospi, mem, op); + break; + + default: + dev_warn(ospi->dev, "Unsupported direction"); + err = -EOPNOTSUPP; + } + + return err; +} + +static bool f_ospi_supports_op_width(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + u8 width_available[] = { 0, 1, 2, 4, 8 }; + u8 width_op[] = { op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth }; + bool is_match_found; + int i, j; + + for (i = 0; i < ARRAY_SIZE(width_op); i++) { + is_match_found = false; + + for (j = 0; j < ARRAY_SIZE(width_available); j++) { + if (width_op[i] == width_available[j]) { + is_match_found = true; + break; + } + } + + if (!is_match_found) + return false; + } + + return true; +} + +static bool f_ospi_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (f_ospi_get_dummy_cycle(op) > OSPI_DUMMY_CYCLE_MAX) + return false; + + if (op->addr.nbytes > 4) + return false; + + if (!f_ospi_supports_op_width(mem, op)) + return false; + + return true; +} + +static int f_ospi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + op->data.nbytes = min((int)op->data.nbytes, (int)(OSPI_DAT_SIZE_MAX)); + + return 0; +} + +static const struct spi_controller_mem_ops f_ospi_mem_ops = { + .adjust_op_size = f_ospi_adjust_op_size, + .supports_op = f_ospi_supports_op, + .exec_op = f_ospi_exec_op, +}; + +static int f_ospi_init(struct f_ospi *ospi) +{ + int ret; + + ret = f_ospi_prepare_config(ospi); + if (ret) + return ret; + + /* Disable boot signal */ + writel(OSPI_ACC_MODE_BOOT_DISABLE, ospi->base + OSPI_ACC_MODE); + + f_ospi_config_dll(ospi); + + /* Disable IRQ */ + f_ospi_clear_irq(ospi); + f_ospi_disable_irq_status(ospi, OSPI_IRQ_ALL); + f_ospi_disable_irq_output(ospi, OSPI_IRQ_ALL); + + return f_ospi_unprepare_config(ospi); +} + +static int f_ospi_probe(struct platform_device *pdev) +{ + struct spi_controller *ctlr; + struct device *dev = &pdev->dev; + struct f_ospi *ospi; + u32 num_cs = OSPI_NUM_CS; + int ret; + + ctlr = spi_alloc_master(dev, sizeof(*ospi)); + if (!ctlr) + return -ENOMEM; + + ctlr->mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL + | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL + | SPI_MODE_0 | SPI_MODE_1 | SPI_LSB_FIRST; + ctlr->mem_ops = &f_ospi_mem_ops; + ctlr->bus_num = -1; + of_property_read_u32(dev->of_node, "num-cs", &num_cs); + if (num_cs > OSPI_NUM_CS) { + dev_err(dev, "num-cs too large: %d\n", num_cs); + return -ENOMEM; + } + ctlr->num_chipselect = num_cs; + ctlr->dev.of_node = dev->of_node; + + ospi = spi_controller_get_devdata(ctlr); + ospi->dev = dev; + + platform_set_drvdata(pdev, ospi); + + ospi->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ospi->base)) { + ret = PTR_ERR(ospi->base); + goto err_put_ctlr; + } + + ospi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ospi->clk)) { + ret = PTR_ERR(ospi->clk); + goto err_put_ctlr; + } + + ret = clk_prepare_enable(ospi->clk); + if (ret) { + dev_err(dev, "Failed to enable the clock\n"); + goto err_disable_clk; + } + + mutex_init(&ospi->mlock); + + ret = f_ospi_init(ospi); + if (ret) + goto err_destroy_mutex; + + ret = devm_spi_register_controller(dev, ctlr); + if (ret) + goto err_destroy_mutex; + + return 0; + +err_destroy_mutex: + mutex_destroy(&ospi->mlock); + +err_disable_clk: + clk_disable_unprepare(ospi->clk); + +err_put_ctlr: + spi_controller_put(ctlr); + + return ret; +} + +static int f_ospi_remove(struct platform_device *pdev) +{ + struct f_ospi *ospi = platform_get_drvdata(pdev); + + clk_disable_unprepare(ospi->clk); + + mutex_destroy(&ospi->mlock); + + return 0; +} + +static const struct of_device_id f_ospi_dt_ids[] = { + { .compatible = "socionext,f-ospi" }, + {} +}; +MODULE_DEVICE_TABLE(of, f_ospi_dt_ids); + +static struct platform_driver f_ospi_driver = { + .driver = { + .name = "socionext,f-ospi", + .of_match_table = f_ospi_dt_ids, + }, + .probe = f_ospi_probe, + .remove = f_ospi_remove, +}; +module_platform_driver(f_ospi_driver); + +MODULE_DESCRIPTION("Socionext F_OSPI controller driver"); +MODULE_AUTHOR("Socionext Inc."); +MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); +MODULE_LICENSE("GPL"); From bcd58c8ca0f89fe6a890f909916bc97561341a06 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Date: Thu, 24 Nov 2022 09:33:50 +0900 Subject: [PATCH 45/66] spi: Add Socionext F_OSPI controller bindings Add devicetree binding documentation for Socionext F_OSPI SPI flash controller. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20221124003351.7792-2-hayashi.kunihiko@socionext.com Signed-off-by: Mark Brown <broonie@kernel.org> --- .../bindings/spi/socionext,f-ospi.yaml | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/socionext,f-ospi.yaml diff --git a/Documentation/devicetree/bindings/spi/socionext,f-ospi.yaml b/Documentation/devicetree/bindings/spi/socionext,f-ospi.yaml new file mode 100644 index 0000000000000..9878d14465524 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/socionext,f-ospi.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/socionext,f-ospi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Socionext F_OSPI controller + +description: | + The Socionext F_OSPI is a controller used to interface with flash + memories using the SPI communication interface. + +maintainers: + - Kunihiko Hayashi <hayashi.kunihiko@socionext.com> + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: socionext,f-ospi + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + num-cs: + minimum: 1 + maximum: 4 + +required: + - compatible + - reg + - clocks + - "#address-cells" + - "#size-cells" + +unevaluatedProperties: false + +examples: + - | + ospi0: spi@80000000 { + compatible = "socionext,f-ospi"; + reg = <0x80000000 0x1000>; + clocks = <&clks 0>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "spansion,s25fl128s", "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <50000000>; + }; + }; From 1f8811a2613ef9e233d56885a19dd4c6e81a5d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Thu, 24 Nov 2022 20:13:59 +0100 Subject: [PATCH 46/66] spi: wpcm-fiu: Add driver for Nuvoton WPCM450 Flash Interface Unit (FIU) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Flash Interface Unit (FIU) is the SPI flash controller in the Nuvoton WPCM450 BMC SoC. It supports four chip selects, and direct (memory-mapped) access to 16 MiB per chip. Larger flash chips can be accessed by software-defined SPI transfers. The FIU in newer NPCM7xx SoCs is not compatible with the WPCM450 FIU. Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Link: https://lore.kernel.org/r/20221124191400.287918-3-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/spi-wpcm-fiu.c | 444 +++++++++++++++++++++++++++++++++++++ 3 files changed, 456 insertions(+) create mode 100644 drivers/spi/spi-wpcm-fiu.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c6fb8cd6d2aeb..b5412b1af8113 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -635,6 +635,17 @@ config SPI_MTK_SNFI is implemented as a SPI-MEM controller with pipelined ECC capcability. +config SPI_WPCM_FIU + tristate "Nuvoton WPCM450 Flash Interface Unit" + depends on ARCH_NPCM || COMPILE_TEST + select REGMAP + help + This enables support got the Flash Interface Unit SPI controller + present in the Nuvoton WPCM450 SoC. + + This driver does not support generic SPI. The implementation only + supports the spi-mem interface. + config SPI_NPCM_FIU tristate "Nuvoton NPCM FLASH Interface Unit" depends on ARCH_NPCM || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 971bd62e44816..be9ba40ef8d03 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_SPI_MTK_NOR) += spi-mtk-nor.o obj-$(CONFIG_SPI_MTK_SNFI) += spi-mtk-snfi.o obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o +obj-$(CONFIG_SPI_WPCM_FIU) += spi-wpcm-fiu.o obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o obj-$(CONFIG_SPI_NXP_FLEXSPI) += spi-nxp-fspi.o diff --git a/drivers/spi/spi-wpcm-fiu.c b/drivers/spi/spi-wpcm-fiu.c new file mode 100644 index 0000000000000..e525fe074f883 --- /dev/null +++ b/drivers/spi/spi-wpcm-fiu.c @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2022 Jonathan Neuschäfer + +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spi/spi-mem.h> + +#define FIU_CFG 0x00 +#define FIU_BURST_BFG 0x01 +#define FIU_RESP_CFG 0x02 +#define FIU_CFBB_PROT 0x03 +#define FIU_FWIN1_LOW 0x04 +#define FIU_FWIN1_HIGH 0x06 +#define FIU_FWIN2_LOW 0x08 +#define FIU_FWIN2_HIGH 0x0a +#define FIU_FWIN3_LOW 0x0c +#define FIU_FWIN3_HIGH 0x0e +#define FIU_PROT_LOCK 0x10 +#define FIU_PROT_CLEAR 0x11 +#define FIU_SPI_FL_CFG 0x14 +#define FIU_UMA_CODE 0x16 +#define FIU_UMA_AB0 0x17 +#define FIU_UMA_AB1 0x18 +#define FIU_UMA_AB2 0x19 +#define FIU_UMA_DB0 0x1a +#define FIU_UMA_DB1 0x1b +#define FIU_UMA_DB2 0x1c +#define FIU_UMA_DB3 0x1d +#define FIU_UMA_CTS 0x1e +#define FIU_UMA_ECTS 0x1f + +#define FIU_BURST_CFG_R16 3 + +#define FIU_UMA_CTS_D_SIZE(x) (x) +#define FIU_UMA_CTS_A_SIZE BIT(3) +#define FIU_UMA_CTS_WR BIT(4) +#define FIU_UMA_CTS_CS(x) ((x) << 5) +#define FIU_UMA_CTS_EXEC_DONE BIT(7) + +#define SHM_FLASH_SIZE 0x02 +#define SHM_FLASH_SIZE_STALL_HOST BIT(6) + +/* + * I observed a typical wait time of 16 iterations for a UMA transfer to + * finish, so this should be a safe limit. + */ +#define UMA_WAIT_ITERATIONS 100 + +struct wpcm_fiu_spi { + struct device *dev; + struct clk *clk; + void __iomem *regs; + struct regmap *shm_regmap; +}; + +static void wpcm_fiu_set_opcode(struct wpcm_fiu_spi *fiu, u8 opcode) +{ + writeb(opcode, fiu->regs + FIU_UMA_CODE); +} + +static void wpcm_fiu_set_addr(struct wpcm_fiu_spi *fiu, u32 addr) +{ + writeb((addr >> 0) & 0xff, fiu->regs + FIU_UMA_AB0); + writeb((addr >> 8) & 0xff, fiu->regs + FIU_UMA_AB1); + writeb((addr >> 16) & 0xff, fiu->regs + FIU_UMA_AB2); +} + +static void wpcm_fiu_set_data(struct wpcm_fiu_spi *fiu, const u8 *data, unsigned int nbytes) +{ + int i; + + for (i = 0; i < nbytes; i++) + writeb(data[i], fiu->regs + FIU_UMA_DB0 + i); +} + +static void wpcm_fiu_get_data(struct wpcm_fiu_spi *fiu, u8 *data, unsigned int nbytes) +{ + int i; + + for (i = 0; i < nbytes; i++) + data[i] = readb(fiu->regs + FIU_UMA_DB0 + i); +} + +/* + * Perform a UMA (User Mode Access) operation, i.e. a software-controlled SPI transfer. + */ +static int wpcm_fiu_do_uma(struct wpcm_fiu_spi *fiu, unsigned int cs, + bool use_addr, bool write, int data_bytes) +{ + int i = 0; + u8 cts = FIU_UMA_CTS_EXEC_DONE | FIU_UMA_CTS_CS(cs); + + if (use_addr) + cts |= FIU_UMA_CTS_A_SIZE; + if (write) + cts |= FIU_UMA_CTS_WR; + cts |= FIU_UMA_CTS_D_SIZE(data_bytes); + + writeb(cts, fiu->regs + FIU_UMA_CTS); + + for (i = 0; i < UMA_WAIT_ITERATIONS; i++) + if (!(readb(fiu->regs + FIU_UMA_CTS) & FIU_UMA_CTS_EXEC_DONE)) + return 0; + + dev_info(fiu->dev, "UMA transfer has not finished in %d iterations\n", UMA_WAIT_ITERATIONS); + return -EIO; +} + +static void wpcm_fiu_ects_assert(struct wpcm_fiu_spi *fiu, unsigned int cs) +{ + u8 ects = readb(fiu->regs + FIU_UMA_ECTS); + + ects &= ~BIT(cs); + writeb(ects, fiu->regs + FIU_UMA_ECTS); +} + +static void wpcm_fiu_ects_deassert(struct wpcm_fiu_spi *fiu, unsigned int cs) +{ + u8 ects = readb(fiu->regs + FIU_UMA_ECTS); + + ects |= BIT(cs); + writeb(ects, fiu->regs + FIU_UMA_ECTS); +} + +struct wpcm_fiu_op_shape { + bool (*match)(const struct spi_mem_op *op); + int (*exec)(struct spi_mem *mem, const struct spi_mem_op *op); +}; + +static bool wpcm_fiu_normal_match(const struct spi_mem_op *op) +{ + // Opcode 0x0b (FAST READ) is treated differently in hardware + if (op->cmd.opcode == 0x0b) + return false; + + return (op->addr.nbytes == 0 || op->addr.nbytes == 3) && + op->dummy.nbytes == 0 && op->data.nbytes <= 4; +} + +static int wpcm_fiu_normal_exec(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); + int ret; + + wpcm_fiu_set_opcode(fiu, op->cmd.opcode); + wpcm_fiu_set_addr(fiu, op->addr.val); + if (op->data.dir == SPI_MEM_DATA_OUT) + wpcm_fiu_set_data(fiu, op->data.buf.out, op->data.nbytes); + + ret = wpcm_fiu_do_uma(fiu, mem->spi->chip_select, op->addr.nbytes == 3, + op->data.dir == SPI_MEM_DATA_OUT, op->data.nbytes); + + if (op->data.dir == SPI_MEM_DATA_IN) + wpcm_fiu_get_data(fiu, op->data.buf.in, op->data.nbytes); + + return ret; +} + +static bool wpcm_fiu_fast_read_match(const struct spi_mem_op *op) +{ + return op->cmd.opcode == 0x0b && op->addr.nbytes == 3 && + op->dummy.nbytes == 1 && + op->data.nbytes >= 1 && op->data.nbytes <= 4 && + op->data.dir == SPI_MEM_DATA_IN; +} + +static int wpcm_fiu_fast_read_exec(struct spi_mem *mem, const struct spi_mem_op *op) +{ + return -EINVAL; +} + +/* + * 4-byte addressing. + * + * Flash view: [ C A A A A D D D D] + * bytes: 13 aa bb cc dd -> 5a a5 f0 0f + * FIU's view: [ C A A A][ C D D D D] + * FIU mode: [ read/write][ read ] + */ +static bool wpcm_fiu_4ba_match(const struct spi_mem_op *op) +{ + return op->addr.nbytes == 4 && op->dummy.nbytes == 0 && op->data.nbytes <= 4; +} + +static int wpcm_fiu_4ba_exec(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); + int cs = mem->spi->chip_select; + + wpcm_fiu_ects_assert(fiu, cs); + + wpcm_fiu_set_opcode(fiu, op->cmd.opcode); + wpcm_fiu_set_addr(fiu, op->addr.val >> 8); + wpcm_fiu_do_uma(fiu, cs, true, false, 0); + + wpcm_fiu_set_opcode(fiu, op->addr.val & 0xff); + wpcm_fiu_set_addr(fiu, 0); + if (op->data.dir == SPI_MEM_DATA_OUT) + wpcm_fiu_set_data(fiu, op->data.buf.out, op->data.nbytes); + wpcm_fiu_do_uma(fiu, cs, false, op->data.dir == SPI_MEM_DATA_OUT, op->data.nbytes); + + wpcm_fiu_ects_deassert(fiu, cs); + + if (op->data.dir == SPI_MEM_DATA_IN) + wpcm_fiu_get_data(fiu, op->data.buf.in, op->data.nbytes); + + return 0; +} + +/* + * RDID (Read Identification) needs special handling because Linux expects to + * be able to read 6 ID bytes and FIU can only read up to 4 at once. + * + * We're lucky in this case, because executing the RDID instruction twice will + * result in the same result. + * + * What we do is as follows (C: write command/opcode byte, D: read data byte, + * A: write address byte): + * + * 1. C D D D + * 2. C A A A D D D + */ +static bool wpcm_fiu_rdid_match(const struct spi_mem_op *op) +{ + return op->cmd.opcode == 0x9f && op->addr.nbytes == 0 && + op->dummy.nbytes == 0 && op->data.nbytes == 6 && + op->data.dir == SPI_MEM_DATA_IN; +} + +static int wpcm_fiu_rdid_exec(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); + int cs = mem->spi->chip_select; + + /* First transfer */ + wpcm_fiu_set_opcode(fiu, op->cmd.opcode); + wpcm_fiu_set_addr(fiu, 0); + wpcm_fiu_do_uma(fiu, cs, false, false, 3); + wpcm_fiu_get_data(fiu, op->data.buf.in, 3); + + /* Second transfer */ + wpcm_fiu_set_opcode(fiu, op->cmd.opcode); + wpcm_fiu_set_addr(fiu, 0); + wpcm_fiu_do_uma(fiu, cs, true, false, 3); + wpcm_fiu_get_data(fiu, op->data.buf.in + 3, 3); + + return 0; +} + +/* + * With some dummy bytes. + * + * C A A A X* X D D D D + * [C A A A D*][C D D D D] + */ +static bool wpcm_fiu_dummy_match(const struct spi_mem_op *op) +{ + // Opcode 0x0b (FAST READ) is treated differently in hardware + if (op->cmd.opcode == 0x0b) + return false; + + return (op->addr.nbytes == 0 || op->addr.nbytes == 3) && + op->dummy.nbytes >= 1 && op->dummy.nbytes <= 5 && + op->data.nbytes <= 4; +} + +static int wpcm_fiu_dummy_exec(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); + int cs = mem->spi->chip_select; + + wpcm_fiu_ects_assert(fiu, cs); + + /* First transfer */ + wpcm_fiu_set_opcode(fiu, op->cmd.opcode); + wpcm_fiu_set_addr(fiu, op->addr.val); + wpcm_fiu_do_uma(fiu, cs, op->addr.nbytes != 0, true, op->dummy.nbytes - 1); + + /* Second transfer */ + wpcm_fiu_set_opcode(fiu, 0); + wpcm_fiu_set_addr(fiu, 0); + wpcm_fiu_do_uma(fiu, cs, false, false, op->data.nbytes); + wpcm_fiu_get_data(fiu, op->data.buf.in, op->data.nbytes); + + wpcm_fiu_ects_deassert(fiu, cs); + + return 0; +} + +static const struct wpcm_fiu_op_shape wpcm_fiu_op_shapes[] = { + { .match = wpcm_fiu_normal_match, .exec = wpcm_fiu_normal_exec }, + { .match = wpcm_fiu_fast_read_match, .exec = wpcm_fiu_fast_read_exec }, + { .match = wpcm_fiu_4ba_match, .exec = wpcm_fiu_4ba_exec }, + { .match = wpcm_fiu_rdid_match, .exec = wpcm_fiu_rdid_exec }, + { .match = wpcm_fiu_dummy_match, .exec = wpcm_fiu_dummy_exec }, +}; + +static const struct wpcm_fiu_op_shape *wpcm_fiu_find_op_shape(const struct spi_mem_op *op) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(wpcm_fiu_op_shapes); i++) { + const struct wpcm_fiu_op_shape *shape = &wpcm_fiu_op_shapes[i]; + + if (shape->match(op)) + return shape; + } + + return NULL; +} + +static bool wpcm_fiu_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + if (!spi_mem_default_supports_op(mem, op)) + return false; + + if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) + return false; + + if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 || + op->dummy.buswidth > 1 || op->data.buswidth > 1) + return false; + + return wpcm_fiu_find_op_shape(op) != NULL; +} + +/* + * In order to ensure the integrity of SPI transfers performed via UMA, + * temporarily disable (stall) memory accesses coming from the host CPU. + */ +static void wpcm_fiu_stall_host(struct wpcm_fiu_spi *fiu, bool stall) +{ + if (fiu->shm_regmap) { + int res = regmap_update_bits(fiu->shm_regmap, SHM_FLASH_SIZE, + SHM_FLASH_SIZE_STALL_HOST, + stall ? SHM_FLASH_SIZE_STALL_HOST : 0); + if (res) + dev_warn(fiu->dev, "Failed to (un)stall host memory accesses: %d\n", res); + } +} + +static int wpcm_fiu_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(mem->spi->controller); + const struct wpcm_fiu_op_shape *shape = wpcm_fiu_find_op_shape(op); + + wpcm_fiu_stall_host(fiu, true); + + if (shape) + return shape->exec(mem, op); + + wpcm_fiu_stall_host(fiu, false); + + return -ENOTSUPP; +} + +static int wpcm_fiu_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + if (op->data.nbytes > 4) + op->data.nbytes = 4; + + return 0; +} + +static const struct spi_controller_mem_ops wpcm_fiu_mem_ops = { + .adjust_op_size = wpcm_fiu_adjust_op_size, + .supports_op = wpcm_fiu_supports_op, + .exec_op = wpcm_fiu_exec_op, +}; + +static void wpcm_fiu_hw_init(struct wpcm_fiu_spi *fiu) +{ + /* Deassert all manually asserted chip selects */ + writeb(0x0f, fiu->regs + FIU_UMA_ECTS); +} + +static int wpcm_fiu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_controller *ctrl; + struct wpcm_fiu_spi *fiu; + struct resource *res; + + ctrl = devm_spi_alloc_master(dev, sizeof(*fiu)); + if (!ctrl) + return -ENOMEM; + + fiu = spi_controller_get_devdata(ctrl); + fiu->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); + fiu->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(fiu->regs)) { + dev_err(dev, "Failed to map registers\n"); + return PTR_ERR(fiu->regs); + } + + fiu->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(fiu->clk)) + return PTR_ERR(fiu->clk); + + fiu->shm_regmap = syscon_regmap_lookup_by_phandle_optional(dev->of_node, "nuvoton,shm"); + + wpcm_fiu_hw_init(fiu); + + ctrl->bus_num = -1; + ctrl->mem_ops = &wpcm_fiu_mem_ops; + ctrl->num_chipselect = 4; + ctrl->dev.of_node = dev->of_node; + + /* + * The FIU doesn't include a clock divider, the clock is entirely + * determined by the AHB3 bus clock. + */ + ctrl->min_speed_hz = clk_get_rate(fiu->clk); + ctrl->max_speed_hz = clk_get_rate(fiu->clk); + + return devm_spi_register_controller(dev, ctrl); +} + +static const struct of_device_id wpcm_fiu_dt_ids[] = { + { .compatible = "nuvoton,wpcm450-fiu", }, + { } +}; +MODULE_DEVICE_TABLE(of, wpcm_fiu_dt_ids); + +static struct platform_driver wpcm_fiu_driver = { + .driver = { + .name = "wpcm450-fiu", + .bus = &platform_bus_type, + .of_match_table = wpcm_fiu_dt_ids, + }, + .probe = wpcm_fiu_probe, +}; +module_platform_driver(wpcm_fiu_driver); + +MODULE_DESCRIPTION("Nuvoton WPCM450 FIU SPI controller driver"); +MODULE_AUTHOR("Jonathan Neuschäfer <j.neuschaefer@gmx.net>"); +MODULE_LICENSE("GPL"); From 9838c182471ee5532824bae7f2669d3539719f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Thu, 24 Nov 2022 20:14:00 +0100 Subject: [PATCH 47/66] spi: wpcm-fiu: Add direct map support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Besides software controlled SPI transfers (UMA, "user mode access"), FIU also supports a 16 MiB mapping window per attached flash chip. This patch implements direct mapped read access, to speed up flash reads. Without direct mapping: # time dd if=/dev/mtd0ro of=dump bs=1M 16+0 records in 16+0 records out real 1m 47.74s user 0m 0.00s sys 1m 47.75s With direct mapping: # time dd if=/dev/mtd0ro of=dump bs=1M 16+0 records in 16+0 records out real 0m 30.81s user 0m 0.00s sys 0m 30.81s Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Link: https://lore.kernel.org/r/20221124191400.287918-4-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-wpcm-fiu.c | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/spi/spi-wpcm-fiu.c b/drivers/spi/spi-wpcm-fiu.c index e525fe074f883..ab33710d50ac8 100644 --- a/drivers/spi/spi-wpcm-fiu.c +++ b/drivers/spi/spi-wpcm-fiu.c @@ -51,10 +51,16 @@ */ #define UMA_WAIT_ITERATIONS 100 +/* The memory-mapped view of flash is 16 MiB long */ +#define MAX_MEMORY_SIZE_PER_CS (16 << 20) +#define MAX_MEMORY_SIZE_TOTAL (4 * MAX_MEMORY_SIZE_PER_CS) + struct wpcm_fiu_spi { struct device *dev; struct clk *clk; void __iomem *regs; + void __iomem *memory; + size_t memory_size; struct regmap *shm_regmap; }; @@ -367,14 +373,64 @@ static int wpcm_fiu_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) return 0; } +static int wpcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ + struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(desc->mem->spi->controller); + int cs = desc->mem->spi->chip_select; + + if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + return -ENOTSUPP; + + /* + * Unfortunately, FIU only supports a 16 MiB direct mapping window (per + * attached flash chip), but the SPI MEM core doesn't support partial + * direct mappings. This means that we can't support direct mapping on + * flashes that are bigger than 16 MiB. + */ + if (desc->info.offset + desc->info.length > MAX_MEMORY_SIZE_PER_CS) + return -ENOTSUPP; + + /* Don't read past the memory window */ + if (cs * MAX_MEMORY_SIZE_PER_CS + desc->info.offset + desc->info.length > fiu->memory_size) + return -ENOTSUPP; + + return 0; +} + +static ssize_t wpcm_fiu_direct_read(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, void *buf) +{ + struct wpcm_fiu_spi *fiu = spi_controller_get_devdata(desc->mem->spi->controller); + int cs = desc->mem->spi->chip_select; + + if (offs >= MAX_MEMORY_SIZE_PER_CS) + return -ENOTSUPP; + + offs += cs * MAX_MEMORY_SIZE_PER_CS; + + if (!fiu->memory || offs >= fiu->memory_size) + return -ENOTSUPP; + + len = min_t(size_t, len, fiu->memory_size - offs); + memcpy_fromio(buf, fiu->memory + offs, len); + + return len; +} + static const struct spi_controller_mem_ops wpcm_fiu_mem_ops = { .adjust_op_size = wpcm_fiu_adjust_op_size, .supports_op = wpcm_fiu_supports_op, .exec_op = wpcm_fiu_exec_op, + .dirmap_create = wpcm_fiu_dirmap_create, + .dirmap_read = wpcm_fiu_direct_read, }; static void wpcm_fiu_hw_init(struct wpcm_fiu_spi *fiu) { + /* Configure memory-mapped flash access */ + writeb(FIU_BURST_CFG_R16, fiu->regs + FIU_BURST_BFG); + writeb(MAX_MEMORY_SIZE_TOTAL / (512 << 10), fiu->regs + FIU_CFG); + writeb(MAX_MEMORY_SIZE_PER_CS / (512 << 10) | BIT(6), fiu->regs + FIU_SPI_FL_CFG); + /* Deassert all manually asserted chip selects */ writeb(0x0f, fiu->regs + FIU_UMA_ECTS); } @@ -404,6 +460,14 @@ static int wpcm_fiu_probe(struct platform_device *pdev) if (IS_ERR(fiu->clk)) return PTR_ERR(fiu->clk); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory"); + fiu->memory = devm_ioremap_resource(dev, res); + fiu->memory_size = min_t(size_t, resource_size(res), MAX_MEMORY_SIZE_TOTAL); + if (IS_ERR(fiu->memory)) { + dev_err(dev, "Failed to map flash memory window\n"); + return PTR_ERR(fiu->memory); + } + fiu->shm_regmap = syscon_regmap_lookup_by_phandle_optional(dev->of_node, "nuvoton,shm"); wpcm_fiu_hw_init(fiu); From dd71cd4dd6c9bede8ee8277d650fcb9c1b12702c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Thu, 24 Nov 2022 20:13:58 +0100 Subject: [PATCH 48/66] spi: Add Nuvoton WPCM450 Flash Interface Unit (FIU) bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Flash Interface Unit (FIU) is the SPI flash controller in the Nuvoton WPCM450 BMC SoC. It supports four chip selects, and direct (memory-mapped) access to 16 MiB per chip. Larger flash chips can be accessed by software-defined SPI transfers. The FIU in newer NPCM7xx SoCs is not compatible with the WPCM450 FIU. Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20221124191400.287918-2-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- .../bindings/spi/nuvoton,wpcm450-fiu.yaml | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml diff --git a/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml new file mode 100644 index 0000000000000..ef94803e75d90 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/nuvoton,wpcm450-fiu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton WPCM450 Flash Interface Unit (FIU) + +maintainers: + - Jonathan Neuschäfer <j.neuschaefer@gmx.net> + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + const: nuvoton,wpcm450-fiu + + reg: + items: + - description: FIU registers + - description: Memory-mapped flash contents + + reg-names: + items: + - const: control + - const: memory + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + nuvoton,shm: + $ref: /schemas/types.yaml#/definitions/phandle + description: a phandle to the SHM block (see ../arm/nuvoton,shm.yaml) + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/nuvoton,wpcm450-clk.h> + spi@c8000000 { + compatible = "nuvoton,wpcm450-fiu"; + reg = <0xc8000000 0x1000>, <0xc0000000 0x4000000>; + #address-cells = <1>; + #size-cells = <0>; + reg-names = "control", "memory"; + clocks = <&clk WPCM450_CLK_FIU>; + nuvoton,shm = <&shm>; + + flash@0 { + compatible = "jedec,spi-nor"; + }; + }; + + shm: syscon@c8001000 { + compatible = "nuvoton,wpcm450-shm", "syscon"; + reg = <0xc8001000 0x1000>; + }; From 9c512e476b0bf8b4f22982eac82db7ff7cc08f73 Mon Sep 17 00:00:00 2001 From: Jean Delvare <jdelvare@suse.de> Date: Fri, 25 Nov 2022 08:31:14 +0100 Subject: [PATCH 49/66] spi: cadence: Drop obsolete dependency on COMPILE_TEST Since commit 0166dc11be91 ("of: make CONFIG_OF user selectable"), it is possible to test-build any driver which depends on OF on any architecture by explicitly selecting OF. Therefore depending on COMPILE_TEST as an alternative is no longer needed. Signed-off-by: Jean Delvare <jdelvare@suse.de> Link: https://lore.kernel.org/r/20221125083114.67e7f83c@endymion.delvare Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/Kconfig | 2 +- drivers/spi/spi-cadence-xspi.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c6fb8cd6d2aeb..160d8d00c389d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -241,7 +241,7 @@ config SPI_CADENCE_QUADSPI config SPI_CADENCE_XSPI tristate "Cadence XSPI controller" - depends on (OF || COMPILE_TEST) && HAS_IOMEM + depends on OF && HAS_IOMEM depends on SPI_MEM help Enable support for the Cadence XSPI Flash controller. diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 9e187f9c6c95e..520b4cc69cdc9 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -607,7 +607,6 @@ static int cdns_xspi_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF static const struct of_device_id cdns_xspi_of_match[] = { { .compatible = "cdns,xspi-nor", @@ -615,9 +614,6 @@ static const struct of_device_id cdns_xspi_of_match[] = { { /* end of table */} }; MODULE_DEVICE_TABLE(of, cdns_xspi_of_match); -#else -#define cdns_xspi_of_match NULL -#endif /* CONFIG_OF */ static struct platform_driver cdns_xspi_platform_driver = { .probe = cdns_xspi_probe, From f73f6bd200c399d52d7147f66b956a01c93d7606 Mon Sep 17 00:00:00 2001 From: Mika Westerberg <mika.westerberg@linux.intel.com> Date: Tue, 25 Oct 2022 09:46:20 +0300 Subject: [PATCH 50/66] spi: intel: Use ->replacement_op in intel_spi_hw_cycle() This way we do not need the SPI-NOR opcode -> Intel controller opcode mapping in the function anymore. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Link: https://lore.kernel.org/r/20221025064623.22808-2-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-intel.c | 52 ++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 605acb1bf4b08..2857d4086851a 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -352,34 +352,25 @@ static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype) return 0; } -static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len) +static int intel_spi_hw_cycle(struct intel_spi *ispi, + const struct intel_spi_mem_op *iop, size_t len) { u32 val, status; int ret; + if (!iop->replacement_op) + return -EINVAL; + val = readl(ispi->base + HSFSTS_CTL); val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK); - switch (opcode) { - case SPINOR_OP_RDID: - val |= HSFSTS_CTL_FCYCLE_RDID; - break; - case SPINOR_OP_WRSR: - val |= HSFSTS_CTL_FCYCLE_WRSR; - break; - case SPINOR_OP_RDSR: - val |= HSFSTS_CTL_FCYCLE_RDSR; - break; - default: - return -EINVAL; - } - if (len > INTEL_SPI_FIFO_SZ) return -EINVAL; val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT; val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; val |= HSFSTS_CTL_FGO; + val |= iop->replacement_op; writel(val, ispi->base + HSFSTS_CTL); ret = intel_spi_wait_hw_busy(ispi); @@ -483,7 +474,7 @@ static int intel_spi_read_reg(struct intel_spi *ispi, const struct spi_mem *mem, ret = intel_spi_sw_cycle(ispi, opcode, nbytes, OPTYPE_READ_NO_ADDR); else - ret = intel_spi_hw_cycle(ispi, opcode, nbytes); + ret = intel_spi_hw_cycle(ispi, iop, nbytes); if (ret) return ret; @@ -548,7 +539,7 @@ static int intel_spi_write_reg(struct intel_spi *ispi, const struct spi_mem *mem if (ispi->swseq_reg) return intel_spi_sw_cycle(ispi, opcode, nbytes, OPTYPE_WRITE_NO_ADDR); - return intel_spi_hw_cycle(ispi, opcode, nbytes); + return intel_spi_hw_cycle(ispi, iop, nbytes); } static int intel_spi_read(struct intel_spi *ispi, const struct spi_mem *mem, @@ -912,18 +903,21 @@ static const struct spi_controller_mem_ops intel_spi_mem_ops = { */ #define INTEL_SPI_GENERIC_OPS \ /* Status register operations */ \ - INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), \ - SPI_MEM_OP_NO_ADDR, \ - INTEL_SPI_OP_DATA_IN(1), \ - intel_spi_read_reg), \ - INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), \ - SPI_MEM_OP_NO_ADDR, \ - INTEL_SPI_OP_DATA_IN(1), \ - intel_spi_read_reg), \ - INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), \ - SPI_MEM_OP_NO_ADDR, \ - INTEL_SPI_OP_DATA_OUT(1), \ - intel_spi_write_reg), \ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), \ + SPI_MEM_OP_NO_ADDR, \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read_reg, \ + HSFSTS_CTL_FCYCLE_RDID), \ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), \ + SPI_MEM_OP_NO_ADDR, \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read_reg, \ + HSFSTS_CTL_FCYCLE_RDSR), \ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), \ + SPI_MEM_OP_NO_ADDR, \ + INTEL_SPI_OP_DATA_OUT(1), \ + intel_spi_write_reg, \ + HSFSTS_CTL_FCYCLE_WRSR), \ /* Normal read */ \ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ INTEL_SPI_OP_ADDR(3), \ From 8a9a784fb337cfd07f305faf5358335d4c12a788 Mon Sep 17 00:00:00 2001 From: Mika Westerberg <mika.westerberg@linux.intel.com> Date: Tue, 25 Oct 2022 09:46:21 +0300 Subject: [PATCH 51/66] spi: intel: Implement adjust_op_size() This allows us to get rid of the checks in the intel_spi_[sh]w_cycle() and makes it possible for the SPI-NOR core to split the transaction into smaller chunks as needed. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Link: https://lore.kernel.org/r/20221025064623.22808-3-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-intel.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 2857d4086851a..431a6f97daf20 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -363,10 +363,6 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, val = readl(ispi->base + HSFSTS_CTL); val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK); - - if (len > INTEL_SPI_FIFO_SZ) - return -EINVAL; - val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT; val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE; val |= HSFSTS_CTL_FGO; @@ -397,9 +393,6 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len, if (ret < 0) return ret; - if (len > INTEL_SPI_FIFO_SZ) - return -EINVAL; - /* * Always clear it after each SW sequencer operation regardless * of whether it is successful or not. @@ -704,6 +697,12 @@ static int intel_spi_erase(struct intel_spi *ispi, const struct spi_mem *mem, return 0; } +static int intel_spi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + op->data.nbytes = clamp_val(op->data.nbytes, 0, INTEL_SPI_FIFO_SZ); + return 0; +} + static bool intel_spi_cmp_mem_op(const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { @@ -844,6 +843,7 @@ static ssize_t intel_spi_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs } static const struct spi_controller_mem_ops intel_spi_mem_ops = { + .adjust_op_size = intel_spi_adjust_op_size, .supports_op = intel_spi_supports_mem_op, .exec_op = intel_spi_exec_mem_op, .get_name = intel_spi_get_name, From 43f173e7e508ede3d6f5411b9ffbb33d6d284211 Mon Sep 17 00:00:00 2001 From: Mika Westerberg <mika.westerberg@linux.intel.com> Date: Tue, 25 Oct 2022 09:46:22 +0300 Subject: [PATCH 52/66] spi: intel: Take possible chip address into account in intel_spi_read/write_reg() The SPI-NOR operation can have non-zero chip address as well so take this into account in intel_spi_read/write_reg(). Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Link: https://lore.kernel.org/r/20221025064623.22808-4-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-intel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 431a6f97daf20..4d8fda991e7b7 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -457,11 +457,12 @@ static int intel_spi_read_reg(struct intel_spi *ispi, const struct spi_mem *mem, const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { + u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val; size_t nbytes = op->data.nbytes; u8 opcode = op->cmd.opcode; int ret; - writel(intel_spi_chip_addr(ispi, mem), ispi->base + FADDR); + writel(addr, ispi->base + FADDR); if (ispi->swseq_reg) ret = intel_spi_sw_cycle(ispi, opcode, nbytes, @@ -479,6 +480,7 @@ static int intel_spi_write_reg(struct intel_spi *ispi, const struct spi_mem *mem const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { + u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val; size_t nbytes = op->data.nbytes; u8 opcode = op->cmd.opcode; int ret; @@ -522,7 +524,7 @@ static int intel_spi_write_reg(struct intel_spi *ispi, const struct spi_mem *mem if (opcode == SPINOR_OP_WRDI) return 0; - writel(intel_spi_chip_addr(ispi, mem), ispi->base + FADDR); + writel(addr, ispi->base + FADDR); /* Write the value beforehand */ ret = intel_spi_write_block(ispi, op->data.buf.out, nbytes); From ec4a04aa6962fff3cfa63d70536537844f7446d2 Mon Sep 17 00:00:00 2001 From: Mika Westerberg <mika.westerberg@linux.intel.com> Date: Tue, 25 Oct 2022 09:46:23 +0300 Subject: [PATCH 53/66] spi: intel: Add support for SFDP opcode The Intel SPI-NOR controller supports SFDP (Serial Flash Discoverable Parameter) opcode so add it to the list of supported opcodes. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Link: https://lore.kernel.org/r/20221025064623.22808-5-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-intel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 4d8fda991e7b7..71c36ad778fc8 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -33,6 +33,7 @@ #define HSFSTS_CTL_FCYCLE_WRITE (0x02 << HSFSTS_CTL_FCYCLE_SHIFT) #define HSFSTS_CTL_FCYCLE_ERASE (0x03 << HSFSTS_CTL_FCYCLE_SHIFT) #define HSFSTS_CTL_FCYCLE_ERASE_64K (0x04 << HSFSTS_CTL_FCYCLE_SHIFT) +#define HSFSTS_CTL_FCYCLE_RDSFDP (0x05 << HSFSTS_CTL_FCYCLE_SHIFT) #define HSFSTS_CTL_FCYCLE_RDID (0x06 << HSFSTS_CTL_FCYCLE_SHIFT) #define HSFSTS_CTL_FCYCLE_WRSR (0x07 << HSFSTS_CTL_FCYCLE_SHIFT) #define HSFSTS_CTL_FCYCLE_RDSR (0x08 << HSFSTS_CTL_FCYCLE_SHIFT) @@ -920,6 +921,11 @@ static const struct spi_controller_mem_ops intel_spi_mem_ops = { INTEL_SPI_OP_DATA_OUT(1), \ intel_spi_write_reg, \ HSFSTS_CTL_FCYCLE_WRSR), \ + INTEL_SPI_MEM_OP_REPL(SPI_MEM_OP_CMD(SPINOR_OP_RDSFDP, 1), \ + INTEL_SPI_OP_ADDR(3), \ + INTEL_SPI_OP_DATA_IN(1), \ + intel_spi_read_reg, \ + HSFSTS_CTL_FCYCLE_RDSFDP), \ /* Normal read */ \ INTEL_SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 1), \ INTEL_SPI_OP_ADDR(3), \ From 63d9a4d88499569210c445a862209515207c2732 Mon Sep 17 00:00:00 2001 From: bayi cheng <bayi.cheng@mediatek.com> Date: Tue, 15 Nov 2022 20:46:55 +0800 Subject: [PATCH 54/66] spi: spi-mtk-nor: Unify write buffer on/off The logical structures of mtk_nor_write_buffer_enable and mtk_nor_write_buffer_disable are very similar, So it is necessary to combine them into one. Signed-off-by: bayi cheng <bayi.cheng@mediatek.com> Link: https://lore.kernel.org/r/20221115124655.10124-1-bayi.cheng@mediatek.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-mtk-nor.c | 40 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 58eca18b28b03..7a62b65bf72e4 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -444,36 +444,28 @@ static int mtk_nor_read_pio(struct mtk_nor *sp, const struct spi_mem_op *op) return ret; } -static int mtk_nor_write_buffer_enable(struct mtk_nor *sp) +static int mtk_nor_setup_write_buffer(struct mtk_nor *sp, bool on) { int ret; u32 val; - if (sp->wbuf_en) + if (!(sp->wbuf_en ^ on)) return 0; val = readl(sp->base + MTK_NOR_REG_CFG2); - writel(val | MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); - ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, - val & MTK_NOR_WR_BUF_EN, 0, 10000); - if (!ret) - sp->wbuf_en = true; - return ret; -} - -static int mtk_nor_write_buffer_disable(struct mtk_nor *sp) -{ - int ret; - u32 val; + if (on) { + writel(val | MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); + ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, + val & MTK_NOR_WR_BUF_EN, 0, 10000); + } else { + writel(val & ~MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); + ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, + !(val & MTK_NOR_WR_BUF_EN), 0, 10000); + } - if (!sp->wbuf_en) - return 0; - val = readl(sp->base + MTK_NOR_REG_CFG2); - writel(val & ~MTK_NOR_WR_BUF_EN, sp->base + MTK_NOR_REG_CFG2); - ret = readl_poll_timeout(sp->base + MTK_NOR_REG_CFG2, val, - !(val & MTK_NOR_WR_BUF_EN), 0, 10000); if (!ret) - sp->wbuf_en = false; + sp->wbuf_en = on; + return ret; } @@ -483,7 +475,7 @@ static int mtk_nor_pp_buffered(struct mtk_nor *sp, const struct spi_mem_op *op) u32 val; int ret, i; - ret = mtk_nor_write_buffer_enable(sp); + ret = mtk_nor_setup_write_buffer(sp, true); if (ret < 0) return ret; @@ -502,7 +494,7 @@ static int mtk_nor_pp_unbuffered(struct mtk_nor *sp, const u8 *buf = op->data.buf.out; int ret; - ret = mtk_nor_write_buffer_disable(sp); + ret = mtk_nor_setup_write_buffer(sp, false); if (ret < 0) return ret; writeb(buf[0], sp->base + MTK_NOR_REG_WDATA); @@ -609,7 +601,7 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) } if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op)) { - ret = mtk_nor_write_buffer_disable(sp); + ret = mtk_nor_setup_write_buffer(sp, false); if (ret < 0) return ret; mtk_nor_setup_bus(sp, op); From c6f7874687f7027d7c4b2f53ff6e4d22850f915d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda <ribalda@chromium.org> Date: Mon, 28 Nov 2022 12:00:01 +0100 Subject: [PATCH 55/66] spi: mediatek: Enable irq when pdata is ready If the device does not come straight from reset, we might receive an IRQ before we are ready to handle it. Fixes: [ 0.832328] Unable to handle kernel read from unreadable memory at virtual address 0000000000000010 [ 1.040343] Call trace: [ 1.040347] mtk_spi_can_dma+0xc/0x40 ... [ 1.262265] start_kernel+0x338/0x42c Signed-off-by: Ricardo Ribalda <ribalda@chromium.org> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Link: https://lore.kernel.org/r/20221128-spi-mt65xx-v1-0-509266830665@chromium.org Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-mt65xx.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 11aeae7fe7fc9..f011b5ff68218 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -1189,11 +1189,6 @@ static int mtk_spi_probe(struct platform_device *pdev) else dma_set_max_seg_size(dev, SZ_256K); - ret = devm_request_irq(dev, irq, mtk_spi_interrupt, - IRQF_TRIGGER_NONE, dev_name(dev), master); - if (ret) - return dev_err_probe(dev, ret, "failed to register irq\n"); - mdata->parent_clk = devm_clk_get(dev, "parent-clk"); if (IS_ERR(mdata->parent_clk)) return dev_err_probe(dev, PTR_ERR(mdata->parent_clk), @@ -1263,6 +1258,13 @@ static int mtk_spi_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "failed to register master\n"); } + ret = devm_request_irq(dev, irq, mtk_spi_interrupt, + IRQF_TRIGGER_NONE, dev_name(dev), master); + if (ret) { + pm_runtime_disable(dev); + return dev_err_probe(dev, ret, "failed to register irq\n"); + } + return 0; } From 1e2872f5912fbc87a00d00d49af98e428f4ff8b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Tue, 29 Nov 2022 11:22:24 +0100 Subject: [PATCH 56/66] spi: dt-bindings: nuvoton,wpcm450-fiu: Fix error in example (bogus include) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nuvoton,wpcm450-fiu binding's example includes nuvoton,wpcm450-clk.h, which has not been merged yet, thus causing a dt_binding_check error on -next. Fix this error by simply hardcoding the clock index in the example, before the breakage spreads any further. Fixes: dd71cd4dd6c9b ("spi: Add Nuvoton WPCM450 Flash Interface Unit (FIU) bindings") Reported-by: Rob Herring <robh@kernel.org> Reported-by: Conor Dooley <conor.dooley@microchip.com> Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20221129102225.3598044-2-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml index ef94803e75d90..e4162845fcc1d 100644 --- a/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml +++ b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml @@ -45,14 +45,13 @@ unevaluatedProperties: false examples: - | - #include <dt-bindings/clock/nuvoton,wpcm450-clk.h> spi@c8000000 { compatible = "nuvoton,wpcm450-fiu"; reg = <0xc8000000 0x1000>, <0xc0000000 0x4000000>; #address-cells = <1>; #size-cells = <0>; reg-names = "control", "memory"; - clocks = <&clk WPCM450_CLK_FIU>; + clocks = <&clk 0>; nuvoton,shm = <&shm>; flash@0 { From c771b4eabd6a52afff0b6f01c361a9d04fa8cd9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= <j.neuschaefer@gmx.net> Date: Tue, 29 Nov 2022 11:22:25 +0100 Subject: [PATCH 57/66] spi: dt-bindings: nuvoton,wpcm450-fiu: Fix warning in example (missing reg property) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing "reg = <0>;" property to the flash@0 node in the example. Fixes: dd71cd4dd6c9b ("spi: Add Nuvoton WPCM450 Flash Interface Unit (FIU) bindings") Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20221129102225.3598044-3-j.neuschaefer@gmx.net Signed-off-by: Mark Brown <broonie@kernel.org> --- Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml index e4162845fcc1d..4e0d391e1d697 100644 --- a/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml +++ b/Documentation/devicetree/bindings/spi/nuvoton,wpcm450-fiu.yaml @@ -56,6 +56,7 @@ examples: flash@0 { compatible = "jedec,spi-nor"; + reg = <0>; }; }; From 7ba63521a1e9d8ca6fb55ead19e6e2b850b8fd80 Mon Sep 17 00:00:00 2001 From: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Date: Thu, 6 Oct 2022 10:35:14 +0530 Subject: [PATCH 58/66] spi: microchip: pci1xxxx: Add suspend and resume support for PCI1XXXX SPI driver Implement suspend, resume callbacks, store config at suspend and restore config at time of resume Signed-off-by: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Link: https://lore.kernel.org/r/20221006050514.115564-3-tharunkumar.pasumarthi@microchip.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-pci1xxxx.c | 78 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/spi/spi-pci1xxxx.c b/drivers/spi/spi-pci1xxxx.c index 958503a4d911e..a31c3b612a430 100644 --- a/drivers/spi/spi-pci1xxxx.c +++ b/drivers/spi/spi-pci1xxxx.c @@ -57,6 +57,9 @@ #define SPI_CHIP_SEL_COUNT 7 #define VENDOR_ID_MCHP 0x1055 +#define SPI_SUSPEND_CONFIG 0x101 +#define SPI_RESUME_CONFIG 0x303 + struct pci1xxxx_spi_internal { u8 hw_inst; bool spi_xfer_in_progress; @@ -383,10 +386,85 @@ static int pci1xxxx_spi_probe(struct pci_dev *pdev, const struct pci_device_id * return ret; } +static void store_restore_config(struct pci1xxxx_spi *spi_ptr, + struct pci1xxxx_spi_internal *spi_sub_ptr, + u8 inst, bool store) +{ + u32 regval; + + if (store) { + regval = readl(spi_ptr->reg_base + + SPI_MST_CTL_REG_OFFSET(spi_sub_ptr->hw_inst)); + regval &= SPI_MST_CTL_DEVSEL_MASK; + spi_sub_ptr->prev_val.dev_sel = (regval >> 25) & 7; + regval = readl(spi_ptr->reg_base + + SPI_PCI_CTRL_REG_OFFSET(spi_sub_ptr->hw_inst)); + regval &= SPI_MSI_VECTOR_SEL_MASK; + spi_sub_ptr->prev_val.msi_vector_sel = (regval >> 4) & 1; + } else { + regval = readl(spi_ptr->reg_base + SPI_MST_CTL_REG_OFFSET(inst)); + regval &= ~SPI_MST_CTL_DEVSEL_MASK; + regval |= (spi_sub_ptr->prev_val.dev_sel << 25); + writel(regval, + spi_ptr->reg_base + SPI_MST_CTL_REG_OFFSET(inst)); + writel((spi_sub_ptr->prev_val.msi_vector_sel << 4), + spi_ptr->reg_base + SPI_PCI_CTRL_REG_OFFSET(inst)); + } +} + +static int pci1xxxx_spi_resume(struct device *dev) +{ + struct pci1xxxx_spi *spi_ptr = dev_get_drvdata(dev); + struct pci1xxxx_spi_internal *spi_sub_ptr; + u32 regval = SPI_RESUME_CONFIG; + u8 iter; + + for (iter = 0; iter < spi_ptr->total_hw_instances; iter++) { + spi_sub_ptr = spi_ptr->spi_int[iter]; + spi_master_resume(spi_sub_ptr->spi_host); + writel(regval, spi_ptr->reg_base + + SPI_MST_EVENT_MASK_REG_OFFSET(iter)); + + /* Restore config at resume */ + store_restore_config(spi_ptr, spi_sub_ptr, iter, 0); + } + + return 0; +} + +static int pci1xxxx_spi_suspend(struct device *dev) +{ + struct pci1xxxx_spi *spi_ptr = dev_get_drvdata(dev); + struct pci1xxxx_spi_internal *spi_sub_ptr; + u32 reg1 = SPI_SUSPEND_CONFIG; + u8 iter; + + for (iter = 0; iter < spi_ptr->total_hw_instances; iter++) { + spi_sub_ptr = spi_ptr->spi_int[iter]; + + while (spi_sub_ptr->spi_xfer_in_progress) + msleep(20); + + /* Store existing config before suspend */ + store_restore_config(spi_ptr, spi_sub_ptr, iter, 1); + spi_master_suspend(spi_sub_ptr->spi_host); + writel(reg1, spi_ptr->reg_base + + SPI_MST_EVENT_MASK_REG_OFFSET(iter)); + } + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(spi_pm_ops, pci1xxxx_spi_suspend, + pci1xxxx_spi_resume); + static struct pci_driver pci1xxxx_spi_driver = { .name = DRV_NAME, .id_table = pci1xxxx_spi_pci_id_table, .probe = pci1xxxx_spi_probe, + .driver = { + .pm = pm_sleep_ptr(&spi_pm_ops), + }, }; module_pci_driver(pci1xxxx_spi_driver); From f8fc65e50ad71c139a12a96e64eeba5005e491d5 Mon Sep 17 00:00:00 2001 From: Nathan Barrett-Morrison <nathan.morrison@timesys.com> Date: Mon, 28 Nov 2022 11:41:47 -0500 Subject: [PATCH 59/66] spi: cadence-quadspi: Add minimum operable clock rate warning to baudrate divisor calculation This Cadence QSPI IP has a 4-bit clock divisor field for baud rate division. For example: 0b0000 = /2 0b0001 = /4 0b0010 = /6 ... 0b1111 = /32 The maximum divisor is 32 (when div = CQSPI_REG_CONFIG_BAUD_MASK). If we assume a reference clock of 500MHz and we set our spi-max-frequency to something low, such as 10 MHz. The calculated bit field for the divisor ends up being: DIV_ROUND_UP(500000000/(2*10000000))-1 = 25 25 is 0b11001... which truncates to a divisor field of 0b1001 (or /20). This is higher than our anticipated max-frequency of 10MHz (500MHz/20 = 25 MHz). Instead, let's make sure we're always using the maximum divisor (/32) in this case and give the user a warning about the rate adjustment. Signed-off-by: Nathan Barrett-Morrison <nathan.morrison@timesys.com> Link: https://lore.kernel.org/r/20221128164147.158441-1-nathan.morrison@timesys.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-cadence-quadspi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 4219113cf36be..676313e1bdad1 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1119,6 +1119,14 @@ static void cqspi_config_baudrate_div(struct cqspi_st *cqspi) /* Recalculate the baudrate divisor based on QSPI specification. */ div = DIV_ROUND_UP(ref_clk_hz, 2 * cqspi->sclk) - 1; + /* Maximum baud divisor */ + if (div > CQSPI_REG_CONFIG_BAUD_MASK) { + div = CQSPI_REG_CONFIG_BAUD_MASK; + dev_warn(&cqspi->pdev->dev, + "Unable to adjust clock <= %d hz. Reduced to %d hz\n", + cqspi->sclk, ref_clk_hz/((div+1)*2)); + } + reg = readl(reg_base + CQSPI_REG_CONFIG); reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB); reg |= (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB; From 7dbfa445ff7393d1c4c066c1727c9e0af1251958 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin <alexander.sverdlin@siemens.com> Date: Wed, 30 Nov 2022 17:29:27 +0100 Subject: [PATCH 60/66] spi: spidev: mask SPI_CS_HIGH in SPI_IOC_RD_MODE Commit f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs") has changed the user-space interface so that bogus SPI_CS_HIGH started to appear in the mask returned by SPI_IOC_RD_MODE even for active-low CS pins. Commit 138c9c32f090 ("spi: spidev: Fix CS polarity if GPIO descriptors are used") fixed only SPI_IOC_WR_MODE part of the problem. Let's fix SPI_IOC_RD_MODE symmetrically. Test case: #include <sys/ioctl.h> #include <fcntl.h> #include <linux/spi/spidev.h> int main(int argc, char **argv) { char modew = SPI_CPHA; char moder; int f = open("/dev/spidev0.0", O_RDWR); if (f < 0) return 1; ioctl(f, SPI_IOC_WR_MODE, &modew); ioctl(f, SPI_IOC_RD_MODE, &moder); return moder == modew ? 0 : 2; } Fixes: f3186dd87669 ("spi: Optionally use GPIO descriptors for CS GPIOs") Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com> Link: https://lore.kernel.org/r/20221130162927.539512-1-alexander.sverdlin@siemens.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spidev.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index b2775d82d2d7b..6313e7d0cdf87 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -377,12 +377,23 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { /* read requests */ case SPI_IOC_RD_MODE: - retval = put_user(spi->mode & SPI_MODE_MASK, - (__u8 __user *)arg); - break; case SPI_IOC_RD_MODE32: - retval = put_user(spi->mode & SPI_MODE_MASK, - (__u32 __user *)arg); + tmp = spi->mode; + + { + struct spi_controller *ctlr = spi->controller; + + if (ctlr->use_gpio_descriptors && ctlr->cs_gpiods && + ctlr->cs_gpiods[spi->chip_select]) + tmp &= ~SPI_CS_HIGH; + } + + if (cmd == SPI_IOC_RD_MODE) + retval = put_user(tmp & SPI_MODE_MASK, + (__u8 __user *)arg); + else + retval = put_user(tmp & SPI_MODE_MASK, + (__u32 __user *)arg); break; case SPI_IOC_RD_LSB_FIRST: retval = put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0, From 7073888c86601389e17f3ee8ab15ab7aef148839 Mon Sep 17 00:00:00 2001 From: Xiangsheng Hou <xiangsheng.hou@mediatek.com> Date: Mon, 5 Dec 2022 14:57:48 +0800 Subject: [PATCH 61/66] spi: mtk-snfi: Add snfi support for MT7986 IC Add snfi support for MT7986 IC. Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Link: https://lore.kernel.org/r/20221205065756.26875-2-xiangsheng.hou@mediatek.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-mtk-snfi.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c index d66bf9762557c..fa8412ba20e2e 100644 --- a/drivers/spi/spi-mtk-snfi.c +++ b/drivers/spi/spi-mtk-snfi.c @@ -126,7 +126,8 @@ #define STR_DATA BIT(0) #define NFI_STA 0x060 -#define NFI_NAND_FSM GENMASK(28, 24) +#define NFI_NAND_FSM_7622 GENMASK(28, 24) +#define NFI_NAND_FSM_7986 GENMASK(29, 23) #define NFI_FSM GENMASK(19, 16) #define READ_EMPTY BIT(12) @@ -158,6 +159,7 @@ #define MAS_WR GENMASK(5, 3) #define MAS_RDDLY GENMASK(2, 0) #define NFI_MASTERSTA_MASK_7622 (MAS_ADDR | MAS_RD | MAS_WR | MAS_RDDLY) +#define NFI_MASTERSTA_MASK_7986 3 // SNFI registers #define SNF_MAC_CTL 0x500 @@ -220,6 +222,11 @@ static const u8 mt7622_spare_sizes[] = { 16, 26, 27, 28 }; +static const u8 mt7986_spare_sizes[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, 67, + 74 +}; + struct mtk_snand_caps { u16 sector_size; u16 max_sectors; @@ -230,6 +237,7 @@ struct mtk_snand_caps { bool bbm_swap; bool empty_page_check; u32 mastersta_mask; + u32 nandfsm_mask; const u8 *spare_sizes; u32 num_spare_size; @@ -244,6 +252,7 @@ static const struct mtk_snand_caps mt7622_snand_caps = { .bbm_swap = false, .empty_page_check = false, .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .nandfsm_mask = NFI_NAND_FSM_7622, .spare_sizes = mt7622_spare_sizes, .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) }; @@ -257,10 +266,25 @@ static const struct mtk_snand_caps mt7629_snand_caps = { .bbm_swap = true, .empty_page_check = false, .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .nandfsm_mask = NFI_NAND_FSM_7622, .spare_sizes = mt7622_spare_sizes, .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) }; +static const struct mtk_snand_caps mt7986_snand_caps = { + .sector_size = 1024, + .max_sectors = 8, + .fdm_size = 8, + .fdm_ecc_size = 1, + .fifo_size = 64, + .bbm_swap = true, + .empty_page_check = true, + .mastersta_mask = NFI_MASTERSTA_MASK_7986, + .nandfsm_mask = NFI_NAND_FSM_7986, + .spare_sizes = mt7986_spare_sizes, + .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes) +}; + struct mtk_snand_conf { size_t page_size; size_t oob_size; @@ -360,7 +384,7 @@ static int mtk_nfi_reset(struct mtk_snand *snf) } ret = readl_poll_timeout(snf->nfi_base + NFI_STA, val, - !(val & (NFI_FSM | NFI_NAND_FSM)), 0, + !(val & (NFI_FSM | snf->caps->nandfsm_mask)), 0, SNFI_POLL_INTERVAL); if (ret) { dev_err(snf->dev, "Failed to reset NFI\n"); @@ -1295,6 +1319,7 @@ static irqreturn_t mtk_snand_irq(int irq, void *id) static const struct of_device_id mtk_snand_ids[] = { { .compatible = "mediatek,mt7622-snand", .data = &mt7622_snand_caps }, { .compatible = "mediatek,mt7629-snand", .data = &mt7629_snand_caps }, + { .compatible = "mediatek,mt7986-snand", .data = &mt7986_snand_caps }, {}, }; From 5f947746f0089529c85654704643f158b420ff92 Mon Sep 17 00:00:00 2001 From: Han Xu <han.xu@nxp.com> Date: Tue, 6 Dec 2022 16:54:09 -0600 Subject: [PATCH 62/66] spi: spi-fsl-lpspi: support multiple cs for lpspi support to get chip select number from DT file. Signed-off-by: Han Xu <han.xu@nxp.com> Link: https://lore.kernel.org/r/20221206225410.604482-1-han.xu@nxp.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-fsl-lpspi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index e8c1c8a4c6c82..34488de555871 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -98,6 +98,7 @@ struct fsl_lpspi_data { struct clk *clk_ipg; struct clk *clk_per; bool is_slave; + u32 num_cs; bool is_only_cs1; bool is_first_byte; @@ -840,6 +841,9 @@ static int fsl_lpspi_probe(struct platform_device *pdev) fsl_lpspi->is_slave = is_slave; fsl_lpspi->is_only_cs1 = of_property_read_bool((&pdev->dev)->of_node, "fsl,spi-only-use-cs1-sel"); + if (of_property_read_u32((&pdev->dev)->of_node, "num-cs", + &fsl_lpspi->num_cs)) + fsl_lpspi->num_cs = 1; controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32); controller->transfer_one = fsl_lpspi_transfer_one; @@ -849,6 +853,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; controller->dev.of_node = pdev->dev.of_node; controller->bus_num = pdev->id; + controller->num_chipselect = fsl_lpspi->num_cs; controller->slave_abort = fsl_lpspi_slave_abort; if (!fsl_lpspi->is_slave) controller->use_gpio_descriptors = true; From bc9ab1b7a6c687370b5d4edf34064bf04af8d369 Mon Sep 17 00:00:00 2001 From: Han Xu <han.xu@nxp.com> Date: Tue, 6 Dec 2022 16:54:10 -0600 Subject: [PATCH 63/66] spi: spi-fsl-lpspi: add num-cs binding for lpspi Add num-cs property to support multiple cs for lpspi. This property is optional. Signed-off-by: Han Xu <han.xu@nxp.com> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Link: https://lore.kernel.org/r/20221206225410.604482-2-han.xu@nxp.com Signed-off-by: Mark Brown <broonie@kernel.org> --- Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml index 8b44284d30c6c..94caa2b7e2417 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml +++ b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml @@ -56,6 +56,13 @@ properties: this property to re-config the chipselect value in the LPSPI driver. type: boolean + num-cs: + description: + number of chip selects. + minimum: 1 + maximum: 2 + default: 1 + required: - compatible - reg @@ -80,4 +87,5 @@ examples: clock-names = "per", "ipg"; spi-slave; fsl,spi-only-use-cs1-sel; + num-cs = <2>; }; From 8330e9e8269bb76dd502e84efb5f351016512cf8 Mon Sep 17 00:00:00 2001 From: bayi cheng <bayi.cheng@mediatek.com> Date: Wed, 7 Dec 2022 13:54:35 +0800 Subject: [PATCH 64/66] spi: spi-mtk-nor: Add recovery mechanism for dma read timeout The state machine of MTK spi nor controller may be disturbed by some glitch signals from the relevant BUS during dma read, Although the possibility of causing the dma read to fail is next to nothing, However, if error-handling is not implemented, which makes the feature somewhat risky. Add an error-handling mechanism here, reset the state machine and re-read the data when an error occurs. Signed-off-by: bayi cheng <bayi.cheng@mediatek.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Link: https://lore.kernel.org/r/20221207055435.30557-1-bayi.cheng@mediatek.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-mtk-nor.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 7a62b65bf72e4..aad92a58c4b89 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -80,6 +80,9 @@ #define MTK_NOR_REG_DMA_FADR 0x71c #define MTK_NOR_REG_DMA_DADR 0x720 #define MTK_NOR_REG_DMA_END_DADR 0x724 +#define MTK_NOR_REG_CG_DIS 0x728 +#define MTK_NOR_SFC_SW_RST BIT(2) + #define MTK_NOR_REG_DMA_DADR_HB 0x738 #define MTK_NOR_REG_DMA_END_DADR_HB 0x73c @@ -147,6 +150,15 @@ static inline int mtk_nor_cmd_exec(struct mtk_nor *sp, u32 cmd, ulong clk) return ret; } +static void mtk_nor_reset(struct mtk_nor *sp) +{ + mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, 0, MTK_NOR_SFC_SW_RST); + mb(); /* flush previous writes */ + mtk_nor_rmw(sp, MTK_NOR_REG_CG_DIS, MTK_NOR_SFC_SW_RST, 0); + mb(); /* flush previous writes */ + writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP); +} + static void mtk_nor_set_addr(struct mtk_nor *sp, const struct spi_mem_op *op) { u32 addr = op->addr.val; @@ -609,7 +621,15 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) mtk_nor_set_addr(sp, op); return mtk_nor_read_pio(sp, op); } else { - return mtk_nor_read_dma(sp, op); + ret = mtk_nor_read_dma(sp, op); + if (unlikely(ret)) { + /* Handle rare bus glitch */ + mtk_nor_reset(sp); + mtk_nor_setup_bus(sp, op); + return mtk_nor_read_dma(sp, op); + } + + return ret; } } From 3a6f994f848a69deb2bf3cd9d130dd0c09730e55 Mon Sep 17 00:00:00 2001 From: Kris Bahnsen <kris@embeddedTS.com> Date: Wed, 7 Dec 2022 15:08:53 -0800 Subject: [PATCH 65/66] spi: spi-gpio: Don't set MOSI as an input if not 3WIRE mode The addition of 3WIRE support would affect MOSI direction even when still in standard (4 wire) mode. This can lead to MOSI being at an invalid logic level when a device driver sets an SPI message with a NULL tx_buf. spi.h states that if tx_buf is NULL then "zeros will be shifted out ... " If MOSI is tristated then the data shifted out is subject to pull resistors, keepers, or in the absence of those, noise. This issue came to light when using spi-gpio connected to an ADS7843 touchscreen controller. MOSI pulled high when clocking MISO data in caused the SPI device to interpret this as a command which would put the device in an unexpected and non-functional state. Fixes: 4b859db2c606 ("spi: spi-gpio: add SPI_3WIRE support") Fixes: 5132b3d28371 ("spi: gpio: Support 3WIRE high-impedance turn-around") Signed-off-by: Kris Bahnsen <kris@embeddedTS.com> Link: https://lore.kernel.org/r/20221207230853.6174-1-kris@embeddedTS.com Signed-off-by: Mark Brown <broonie@kernel.org> --- drivers/spi/spi-gpio.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 4b12c4964a664..9c8c7948044ed 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -268,9 +268,19 @@ static int spi_gpio_set_direction(struct spi_device *spi, bool output) if (output) return gpiod_direction_output(spi_gpio->mosi, 1); - ret = gpiod_direction_input(spi_gpio->mosi); - if (ret) - return ret; + /* + * Only change MOSI to an input if using 3WIRE mode. + * Otherwise, MOSI could be left floating if there is + * no pull resistor connected to the I/O pin, or could + * be left logic high if there is a pull-up. Transmitting + * logic high when only clocking MISO data in can put some + * SPI devices in to a bad state. + */ + if (spi->mode & SPI_3WIRE) { + ret = gpiod_direction_input(spi_gpio->mosi); + if (ret) + return ret; + } /* * Send a turnaround high impedance cycle when switching * from output to input. Theoretically there should be From 3cf241c3d56ff19f5192cb42a025bc6582b6e8fa Mon Sep 17 00:00:00 2001 From: Rob Herring <robh@kernel.org> Date: Fri, 9 Dec 2022 11:16:43 -0600 Subject: [PATCH 66/66] spi: dt-bindings: Convert Synquacer SPI to DT schema Convert the Socionext Synquacer SPI binding to DT format. Signed-off-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20221209171644.3351787-1-robh@kernel.org Signed-off-by: Mark Brown <broonie@kernel.org> --- .../bindings/spi/socionext,synquacer-spi.yaml | 73 +++++++++++++++++++ .../devicetree/bindings/spi/spi-synquacer.txt | 27 ------- MAINTAINERS | 2 +- 3 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 Documentation/devicetree/bindings/spi/socionext,synquacer-spi.yaml delete mode 100644 Documentation/devicetree/bindings/spi/spi-synquacer.txt diff --git a/Documentation/devicetree/bindings/spi/socionext,synquacer-spi.yaml b/Documentation/devicetree/bindings/spi/socionext,synquacer-spi.yaml new file mode 100644 index 0000000000000..45cbe744c7ff0 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/socionext,synquacer-spi.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/socionext,synquacer-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Socionext SynQuacer HS-SPI Controller + +maintainers: + - Masahisa Kojima <masahisa.kojima@linaro.org> + - Jassi Brar <jaswinder.singh@linaro.org> + +allOf: + - $ref: spi-controller.yaml# + +properties: + compatible: + const: socionext,synquacer-spi + + reg: + maxItems: 1 + + clocks: + minItems: 1 + items: + - description: core clock + - description: rate clock + + clock-names: + minItems: 1 + items: + - const: iHCLK + - const: iPCLK + + interrupts: + items: + - description: Receive Interrupt + - description: Transmit Interrupt + - description: Fault Interrupt + + socionext,use-rtm: + type: boolean + description: Enable using "retimed clock" for RX + + socionext,set-aces: + type: boolean + description: Enable same active clock edges field to be set + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + spi@ff110000 { + compatible = "socionext,synquacer-spi"; + reg = <0xff110000 0x1000>; + interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_hsspi>; + clock-names = "iHCLK"; + socionext,use-rtm; + socionext,set-aces; + }; +... diff --git a/Documentation/devicetree/bindings/spi/spi-synquacer.txt b/Documentation/devicetree/bindings/spi/spi-synquacer.txt deleted file mode 100644 index 291dfa692d0a8..0000000000000 --- a/Documentation/devicetree/bindings/spi/spi-synquacer.txt +++ /dev/null @@ -1,27 +0,0 @@ -* Socionext Synquacer HS-SPI bindings - -Required Properties: -- compatible: should be "socionext,synquacer-spi" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: should contain the "spi_rx", "spi_tx" and "spi_fault" interrupts. -- clocks: core clock iHCLK. Optional rate clock iPCLK (default is iHCLK) -- clock-names: Shall be "iHCLK" and "iPCLK" respectively - -Optional Properties: -- socionext,use-rtm: boolean, if required to use "retimed clock" for RX -- socionext,set-aces: boolean, if same active clock edges field to be set. - -Example: - - spi0: spi@ff110000 { - compatible = "socionext,synquacer-spi"; - reg = <0xff110000 0x1000>; - interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&clk_hsspi>; - clock-names = "iHCLK"; - socionext,use-rtm; - socionext,set-aces; - }; diff --git a/MAINTAINERS b/MAINTAINERS index 046ff06ff97fa..715636748f79b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19041,7 +19041,7 @@ M: Masahisa Kojima <masahisa.kojima@linaro.org> M: Jassi Brar <jaswinder.singh@linaro.org> L: linux-spi@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/spi/spi-synquacer.txt +F: Documentation/devicetree/bindings/spi/socionext,synquacer-spi.yaml F: drivers/spi/spi-synquacer.c SOCIONEXT SYNQUACER I2C DRIVER