diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-01 19:09:04 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-11-01 19:09:04 -0700 |
commit | 2019295c9ea3137364682046bb6afc0eb364e591 (patch) | |
tree | 0f905528cd55cce6e1cd0db952f70f0531b06ff6 /drivers/spi | |
parent | 1260d242d94ae423c585050bbaabe9064741f419 (diff) | |
parent | 28b5eaf9712bbed90c2b5a5608d70a16b7950856 (diff) | |
download | linux-2019295c9ea3137364682046bb6afc0eb364e591.tar.bz2 |
Merge tag 'spi-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"This is quite a quiet release for SPI, there's been a bit of cleanup
to the core from Uwe but nothing functionality wise.
We have added several new drivers, Cadence XSPI, Ingenic JZ47xx,
Qualcomm SC7280 and SC7180 and Xilinx Versal OSPI"
* tag 'spi-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (41 commits)
spi: Convert NXP flexspi to json schema
spi: spi-geni-qcom: Add support for GPI dma
spi: fsi: Fix contention in the FSI2SPI engine
spi: spi-rpc-if: Check return value of rpcif_sw_init()
spi: tegra210-quad: Put device into suspend on driver removal
spi: tegra20-slink: Put device into suspend on driver removal
spi: bcm-qspi: Fix missing clk_disable_unprepare() on error in bcm_qspi_probe()
spi: at91-usart: replacing legacy gpio interface for gpiod
spi: replace snprintf in show functions with sysfs_emit
spi: cadence: Add of_node_put() before return
spi: orion: Add of_node_put() before goto
spi: cadence-quadspi: fix dma_unmap_single() call
spi: tegra20: fix build with CONFIG_PM_SLEEP=n
spi: bcm-qspi: add support for 3-wire mode for half duplex transfer
spi: bcm-qspi: Add mspi spcr3 32/64-bits xfer mode
spi: Make several public functions private to spi.c
spi: Reorder functions to simplify the next commit
spi: Remove unused function spi_busnum_to_master()
spi: Move comment about chipselect check to the right place
spi: fsi: Print status on error
...
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 26 | ||||
-rw-r--r-- | drivers/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/spi/atmel-quadspi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-amd.c | 113 | ||||
-rw-r--r-- | drivers/spi/spi-at91-usart.c | 27 | ||||
-rw-r--r-- | drivers/spi/spi-bcm-qspi.c | 193 | ||||
-rw-r--r-- | drivers/spi/spi-cadence-quadspi.c | 214 | ||||
-rw-r--r-- | drivers/spi/spi-cadence-xspi.c | 642 | ||||
-rw-r--r-- | drivers/spi/spi-fsi.c | 121 | ||||
-rw-r--r-- | drivers/spi/spi-geni-qcom.c | 254 | ||||
-rw-r--r-- | drivers/spi/spi-ingenic.c | 482 | ||||
-rw-r--r-- | drivers/spi/spi-mtk-nor.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-orion.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-rpc-if.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi-rspi.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-sh-msiof.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-stm32-qspi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-tegra20-slink.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi-tegra210-quad.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi-tle62x0.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi.c | 237 |
21 files changed, 2005 insertions, 329 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 83e352b0c8f9..596705d24400 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -228,6 +228,18 @@ config SPI_CADENCE_QUADSPI device with a Cadence QSPI controller and want to access the Flash as an MTD device. +config SPI_CADENCE_XSPI + tristate "Cadence XSPI controller" + depends on (OF || COMPILE_TEST) && HAS_IOMEM + depends on SPI_MEM + help + Enable support for the Cadence XSPI Flash controller. + + Cadence XSPI is a specialized controller for connecting an SPI + Flash over upto 8bit wide bus. Enable this option if you have a + device with a Cadence XSPI controller and want to access the + Flash as an MTD device. + config SPI_CLPS711X tristate "CLPS711X host SPI controller" depends on ARCH_CLPS711X || COMPILE_TEST @@ -406,6 +418,15 @@ config SPI_IMX help This enables support for the Freescale i.MX SPI controllers. +config SPI_INGENIC + tristate "Ingenic JZ47xx SoCs SPI controller" + depends on MACH_INGENIC || COMPILE_TEST + help + This enables support for the Ingenic JZ47xx SoCs SPI controller. + + To compile this driver as a module, choose M here: the module + will be called spi-ingenic. + config SPI_JCORE tristate "J-Core SPI Master" depends on OF && (SUPERH || COMPILE_TEST) @@ -738,10 +759,11 @@ config SPI_S3C24XX_FIQ TX and RX data paths. config SPI_S3C64XX - tristate "Samsung S3C64XX series type SPI" + tristate "Samsung S3C64XX/Exynos SoC series type SPI" depends on (PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST) help - SPI driver for Samsung S3C64XX and newer SoCs. + SPI driver for Samsung S3C64XX, S5Pv210 and Exynos SoCs. + Choose Y/M here only if you build for such Samsung SoC. config SPI_SC18IS602 tristate "NXP SC18IS602/602B/603 I2C to SPI bridge" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 699db95c8441..dd7393a6046f 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CADENCE_QUADSPI) += spi-cadence-quadspi.o +obj-$(CONFIG_SPI_CADENCE_XSPI) += spi-cadence-xspi.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o @@ -59,6 +60,7 @@ obj-$(CONFIG_SPI_HISI_KUNPENG) += spi-hisi-kunpeng.o obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o obj-$(CONFIG_SPI_IMX) += spi-imx.o +obj-$(CONFIG_SPI_INGENIC) += spi-ingenic.o obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o obj-$(CONFIG_SPI_JCORE) += spi-jcore.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 95d4fa32c299..92d9610df1fd 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -310,7 +310,7 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, return mode; ifr |= atmel_qspi_modes[mode].config; - if (op->dummy.buswidth && op->dummy.nbytes) + if (op->dummy.nbytes) dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth; /* diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 3cf76096a76d..4b3ac7aceaf6 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -38,126 +38,102 @@ struct amd_spi { void __iomem *io_remap_addr; unsigned long io_base_addr; u32 rom_addr; - u8 chip_select; }; -static inline u8 amd_spi_readreg8(struct spi_master *master, int idx) +static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx); } -static inline void amd_spi_writereg8(struct spi_master *master, int idx, - u8 val) +static inline void amd_spi_writereg8(struct amd_spi *amd_spi, int idx, u8 val) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); } -static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx, - u8 set, u8 clear) +static void amd_spi_setclear_reg8(struct amd_spi *amd_spi, int idx, u8 set, u8 clear) { - u8 tmp = amd_spi_readreg8(master, idx); + u8 tmp = amd_spi_readreg8(amd_spi, idx); tmp = (tmp & ~clear) | set; - amd_spi_writereg8(master, idx, tmp); + amd_spi_writereg8(amd_spi, idx, tmp); } -static inline u32 amd_spi_readreg32(struct spi_master *master, int idx) +static inline u32 amd_spi_readreg32(struct amd_spi *amd_spi, int idx) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx); } -static inline void amd_spi_writereg32(struct spi_master *master, int idx, - u32 val) +static inline void amd_spi_writereg32(struct amd_spi *amd_spi, int idx, u32 val) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); } -static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx, - u32 set, u32 clear) +static inline void amd_spi_setclear_reg32(struct amd_spi *amd_spi, int idx, u32 set, u32 clear) { - u32 tmp = amd_spi_readreg32(master, idx); + u32 tmp = amd_spi_readreg32(amd_spi, idx); tmp = (tmp & ~clear) | set; - amd_spi_writereg32(master, idx, tmp); + amd_spi_writereg32(amd_spi, idx, tmp); } -static void amd_spi_select_chip(struct spi_master *master) +static void amd_spi_select_chip(struct amd_spi *amd_spi, u8 cs) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - u8 chip_select = amd_spi->chip_select; - - amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select, - AMD_SPI_ALT_CS_MASK); + amd_spi_setclear_reg8(amd_spi, AMD_SPI_ALT_CS_REG, cs, AMD_SPI_ALT_CS_MASK); } -static void amd_spi_clear_fifo_ptr(struct spi_master *master) +static void amd_spi_clear_fifo_ptr(struct amd_spi *amd_spi) { - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, - AMD_SPI_FIFO_CLEAR); + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, AMD_SPI_FIFO_CLEAR); } -static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode) +static void amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode) { - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode, - AMD_SPI_OPCODE_MASK); + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode, AMD_SPI_OPCODE_MASK); } -static inline void amd_spi_set_rx_count(struct spi_master *master, - u8 rx_count) +static inline void amd_spi_set_rx_count(struct amd_spi *amd_spi, u8 rx_count) { - amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); + amd_spi_setclear_reg8(amd_spi, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); } -static inline void amd_spi_set_tx_count(struct spi_master *master, - u8 tx_count) +static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count) { - amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); + amd_spi_setclear_reg8(amd_spi, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); } -static inline int amd_spi_busy_wait(struct amd_spi *amd_spi) +static int amd_spi_busy_wait(struct amd_spi *amd_spi) { - bool spi_busy; int timeout = 100000; /* poll for SPI bus to become idle */ - spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + - AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; - while (spi_busy) { + while (amd_spi_readreg32(amd_spi, AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) { usleep_range(10, 20); if (timeout-- < 0) return -ETIMEDOUT; - - spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + - AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; } return 0; } -static void amd_spi_execute_opcode(struct spi_master *master) +static int amd_spi_execute_opcode(struct amd_spi *amd_spi) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); + int ret; + + ret = amd_spi_busy_wait(amd_spi); + if (ret) + return ret; /* Set ExecuteOpCode bit in the CTRL0 register */ - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, - AMD_SPI_EXEC_CMD); + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, AMD_SPI_EXEC_CMD); - amd_spi_busy_wait(amd_spi); + return 0; } static int amd_spi_master_setup(struct spi_device *spi) { - struct spi_master *master = spi->master; + struct amd_spi *amd_spi = spi_master_get_devdata(spi->master); - amd_spi_clear_fifo_ptr(master); + amd_spi_clear_fifo_ptr(amd_spi); return 0; } @@ -185,19 +161,18 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, tx_len = xfer->len - 1; cmd_opcode = *(u8 *)xfer->tx_buf; buf++; - amd_spi_set_opcode(master, cmd_opcode); + amd_spi_set_opcode(amd_spi, cmd_opcode); /* Write data into the FIFO. */ for (i = 0; i < tx_len; i++) { - iowrite8(buf[i], - ((u8 __iomem *)amd_spi->io_remap_addr + + iowrite8(buf[i], ((u8 __iomem *)amd_spi->io_remap_addr + AMD_SPI_FIFO_BASE + i)); } - amd_spi_set_tx_count(master, tx_len); - amd_spi_clear_fifo_ptr(master); + amd_spi_set_tx_count(amd_spi, tx_len); + amd_spi_clear_fifo_ptr(amd_spi); /* Execute command */ - amd_spi_execute_opcode(master); + amd_spi_execute_opcode(amd_spi); } if (m_cmd & AMD_SPI_XFER_RX) { /* @@ -206,15 +181,14 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, */ rx_len = xfer->len; buf = (u8 *)xfer->rx_buf; - amd_spi_set_rx_count(master, rx_len); - amd_spi_clear_fifo_ptr(master); + amd_spi_set_rx_count(amd_spi, rx_len); + amd_spi_clear_fifo_ptr(amd_spi); /* Execute command */ - amd_spi_execute_opcode(master); + amd_spi_execute_opcode(amd_spi); + amd_spi_busy_wait(amd_spi); /* Read data from FIFO to receive buffer */ for (i = 0; i < rx_len; i++) - buf[i] = amd_spi_readreg8(master, - AMD_SPI_FIFO_BASE + - tx_len + i); + buf[i] = amd_spi_readreg8(amd_spi, AMD_SPI_FIFO_BASE + tx_len + i); } } @@ -233,8 +207,7 @@ static int amd_spi_master_transfer(struct spi_master *master, struct amd_spi *amd_spi = spi_master_get_devdata(master); struct spi_device *spi = msg->spi; - amd_spi->chip_select = spi->chip_select; - amd_spi_select_chip(master); + amd_spi_select_chip(amd_spi, spi->chip_select); /* * Extract spi_transfers from the spi message and diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index 8c8352625d23..9cd738682aab 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -14,7 +14,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_platform.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -482,29 +482,12 @@ static void at91_usart_spi_init(struct at91_usart_spi *aus) static int at91_usart_gpio_setup(struct platform_device *pdev) { - struct device_node *np = pdev->dev.parent->of_node; - int i; - int ret; - int nb; - - if (!np) - return -EINVAL; - - nb = of_gpio_named_count(np, "cs-gpios"); - for (i = 0; i < nb; i++) { - int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); + struct gpio_descs *cs_gpios; - if (cs_gpio < 0) - return cs_gpio; + cs_gpios = devm_gpiod_get_array_optional(&pdev->dev, "cs", GPIOD_OUT_LOW); - if (gpio_is_valid(cs_gpio)) { - ret = devm_gpio_request_one(&pdev->dev, cs_gpio, - GPIOF_DIR_OUT, - dev_name(&pdev->dev)); - if (ret) - return ret; - } - } + if (IS_ERR(cs_gpios)) + return PTR_ERR(cs_gpios); return 0; } diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 3043677ba222..f3de3305d0f5 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -83,6 +83,9 @@ /* MSPI register offsets */ #define MSPI_SPCR0_LSB 0x000 #define MSPI_SPCR0_MSB 0x004 +#define MSPI_SPCR0_MSB_CPHA BIT(0) +#define MSPI_SPCR0_MSB_CPOL BIT(1) +#define MSPI_SPCR0_MSB_BITS_SHIFT 0x2 #define MSPI_SPCR1_LSB 0x008 #define MSPI_SPCR1_MSB 0x00c #define MSPI_NEWQP 0x010 @@ -100,8 +103,10 @@ #define MSPI_MASTER_BIT BIT(7) #define MSPI_NUM_CDRAM 16 +#define MSPI_CDRAM_OUTP BIT(8) #define MSPI_CDRAM_CONT_BIT BIT(7) #define MSPI_CDRAM_BITSE_BIT BIT(6) +#define MSPI_CDRAM_DT_BIT BIT(5) #define MSPI_CDRAM_PCS 0xf #define MSPI_SPCR2_SPE BIT(6) @@ -114,6 +119,14 @@ ~(BIT(10) | BIT(11))) #define MSPI_SPCR3_SYSCLKSEL_108 (MSPI_SPCR3_SYSCLKSEL_MASK & \ BIT(11)) +#define MSPI_SPCR3_TXRXDAM_MASK GENMASK(4, 2) +#define MSPI_SPCR3_DAM_8BYTE 0 +#define MSPI_SPCR3_DAM_16BYTE (BIT(2) | BIT(4)) +#define MSPI_SPCR3_DAM_32BYTE (BIT(3) | BIT(5)) +#define MSPI_SPCR3_HALFDUPLEX BIT(6) +#define MSPI_SPCR3_HDOUTTYPE BIT(7) +#define MSPI_SPCR3_DATA_REG_SZ BIT(8) +#define MSPI_SPCR3_CPHARX BIT(9) #define MSPI_MSPI_STATUS_SPIF BIT(0) @@ -153,6 +166,14 @@ #define TRANS_STATUS_BREAK_DESELECT (TRANS_STATUS_BREAK_EOM | \ TRANS_STATUS_BREAK_CS_CHANGE) +/* + * Used for writing and reading data in the right order + * to TXRAM and RXRAM when used as 32-bit registers respectively + */ +#define swap4bytes(__val) \ + ((((__val) >> 24) & 0x000000FF) | (((__val) >> 8) & 0x0000FF00) | \ + (((__val) << 8) & 0x00FF0000) | (((__val) << 24) & 0xFF000000)) + struct bcm_qspi_parms { u32 speed_hz; u8 mode; @@ -261,7 +282,7 @@ static inline bool bcm_qspi_has_sysclk_108(struct bcm_qspi *qspi) static inline int bcm_qspi_spbr_min(struct bcm_qspi *qspi) { if (bcm_qspi_has_fastbr(qspi)) - return 1; + return (bcm_qspi_has_sysclk_108(qspi) ? 4 : 1); else return 8; } @@ -395,7 +416,8 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, if (addrlen == BSPI_ADDRLEN_4BYTES) bpp = BSPI_BPP_ADDR_SELECT_MASK; - bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth; + if (op->dummy.nbytes) + bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth; switch (width) { case SPI_NBITS_SINGLE: @@ -570,23 +592,23 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, { u32 spcr, spbr = 0; - if (xp->speed_hz) - spbr = qspi->base_clk / (2 * xp->speed_hz); - - spcr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX); - bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr); - if (!qspi->mspi_maj_rev) /* legacy controller */ spcr = MSPI_MASTER_BIT; else spcr = 0; - /* for 16 bit the data should be zero */ - if (xp->bits_per_word != 16) - spcr |= xp->bits_per_word << 2; - spcr |= xp->mode & 3; + /* + * Bits per transfer. BITS determines the number of data bits + * transferred if the command control bit (BITSE of a + * CDRAM Register) is equal to 1. + * If CDRAM BITSE is equal to 0, 8 data bits are transferred + * regardless + */ + if (xp->bits_per_word != 16 && xp->bits_per_word != 64) + spcr |= xp->bits_per_word << MSPI_SPCR0_MSB_BITS_SHIFT; + spcr |= xp->mode & (MSPI_SPCR0_MSB_CPHA | MSPI_SPCR0_MSB_CPOL); bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr); if (bcm_qspi_has_fastbr(qspi)) { @@ -595,17 +617,44 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, /* enable fastbr */ spcr |= MSPI_SPCR3_FASTBR; + if (xp->mode & SPI_3WIRE) + spcr |= MSPI_SPCR3_HALFDUPLEX | MSPI_SPCR3_HDOUTTYPE; + if (bcm_qspi_has_sysclk_108(qspi)) { /* SYSCLK_108 */ spcr |= MSPI_SPCR3_SYSCLKSEL_108; qspi->base_clk = MSPI_BASE_FREQ * 4; - /* Change spbr as we changed sysclk */ - bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, 4); } + if (xp->bits_per_word > 16) { + /* data_reg_size 1 (64bit) */ + spcr |= MSPI_SPCR3_DATA_REG_SZ; + /* TxRx RAM data access mode 2 for 32B and set fastdt */ + spcr |= MSPI_SPCR3_DAM_32BYTE | MSPI_SPCR3_FASTDT; + /* + * Set length of delay after transfer + * DTL from 0(256) to 1 + */ + bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_LSB, 1); + } else { + /* data_reg_size[8] = 0 */ + spcr &= ~(MSPI_SPCR3_DATA_REG_SZ); + + /* + * TxRx RAM access mode 8B + * and disable fastdt + */ + spcr &= ~(MSPI_SPCR3_DAM_32BYTE); + } bcm_qspi_write(qspi, MSPI, MSPI_SPCR3, spcr); } + if (xp->speed_hz) + spbr = qspi->base_clk / (2 * xp->speed_hz); + + spbr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX); + bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spbr); + qspi->last_parms = *xp; } @@ -626,7 +675,7 @@ static int bcm_qspi_setup(struct spi_device *spi) { struct bcm_qspi_parms *xp; - if (spi->bits_per_word > 16) + if (spi->bits_per_word > 64) return -EINVAL; xp = spi_get_ctldata(spi); @@ -665,8 +714,12 @@ static int update_qspi_trans_byte_count(struct bcm_qspi *qspi, /* count the last transferred bytes */ if (qt->trans->bits_per_word <= 8) qt->byte++; - else + else if (qt->trans->bits_per_word <= 16) qt->byte += 2; + else if (qt->trans->bits_per_word <= 32) + qt->byte += 4; + else if (qt->trans->bits_per_word <= 64) + qt->byte += 8; if (qt->byte >= qt->trans->len) { /* we're at the end of the spi_transfer */ @@ -709,6 +762,33 @@ static inline u16 read_rxram_slot_u16(struct bcm_qspi *qspi, int slot) ((bcm_qspi_read(qspi, MSPI, msb_offset) & 0xff) << 8); } +static inline u32 read_rxram_slot_u32(struct bcm_qspi *qspi, int slot) +{ + u32 reg_offset = MSPI_RXRAM; + u32 offset = reg_offset + (slot << 3); + u32 val; + + val = bcm_qspi_read(qspi, MSPI, offset); + val = swap4bytes(val); + + return val; +} + +static inline u64 read_rxram_slot_u64(struct bcm_qspi *qspi, int slot) +{ + u32 reg_offset = MSPI_RXRAM; + u32 lsb_offset = reg_offset + (slot << 3) + 0x4; + u32 msb_offset = reg_offset + (slot << 3); + u32 msb, lsb; + + msb = bcm_qspi_read(qspi, MSPI, msb_offset); + msb = swap4bytes(msb); + lsb = bcm_qspi_read(qspi, MSPI, lsb_offset); + lsb = swap4bytes(lsb); + + return ((u64)msb << 32 | lsb); +} + static void read_from_hw(struct bcm_qspi *qspi, int slots) { struct qspi_trans tp; @@ -732,7 +812,7 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots) buf[tp.byte] = read_rxram_slot_u8(qspi, slot); dev_dbg(&qspi->pdev->dev, "RD %02x\n", buf ? buf[tp.byte] : 0x0); - } else { + } else if (tp.trans->bits_per_word <= 16) { u16 *buf = tp.trans->rx_buf; if (buf) @@ -740,6 +820,25 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots) slot); dev_dbg(&qspi->pdev->dev, "RD %04x\n", buf ? buf[tp.byte / 2] : 0x0); + } else if (tp.trans->bits_per_word <= 32) { + u32 *buf = tp.trans->rx_buf; + + if (buf) + buf[tp.byte / 4] = read_rxram_slot_u32(qspi, + slot); + dev_dbg(&qspi->pdev->dev, "RD %08x\n", + buf ? buf[tp.byte / 4] : 0x0); + + } else if (tp.trans->bits_per_word <= 64) { + u64 *buf = tp.trans->rx_buf; + + if (buf) + buf[tp.byte / 8] = read_rxram_slot_u64(qspi, + slot); + dev_dbg(&qspi->pdev->dev, "RD %llx\n", + buf ? buf[tp.byte / 8] : 0x0); + + } update_qspi_trans_byte_count(qspi, &tp, @@ -769,6 +868,28 @@ static inline void write_txram_slot_u16(struct bcm_qspi *qspi, int slot, bcm_qspi_write(qspi, MSPI, lsb_offset, (val & 0xff)); } +static inline void write_txram_slot_u32(struct bcm_qspi *qspi, int slot, + u32 val) +{ + u32 reg_offset = MSPI_TXRAM; + u32 msb_offset = reg_offset + (slot << 3); + + bcm_qspi_write(qspi, MSPI, msb_offset, swap4bytes(val)); +} + +static inline void write_txram_slot_u64(struct bcm_qspi *qspi, int slot, + u64 val) +{ + u32 reg_offset = MSPI_TXRAM; + u32 msb_offset = reg_offset + (slot << 3); + u32 lsb_offset = reg_offset + (slot << 3) + 0x4; + u32 msb = upper_32_bits(val); + u32 lsb = lower_32_bits(val); + + bcm_qspi_write(qspi, MSPI, msb_offset, swap4bytes(msb)); + bcm_qspi_write(qspi, MSPI, lsb_offset, swap4bytes(lsb)); +} + static inline u32 read_cdram_slot(struct bcm_qspi *qspi, int slot) { return bcm_qspi_read(qspi, MSPI, MSPI_CDRAM + (slot << 2)); @@ -792,20 +913,43 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) /* Run until end of transfer or reached the max data */ while (!tstatus && slot < MSPI_NUM_CDRAM) { + mspi_cdram = MSPI_CDRAM_CONT_BIT; if (tp.trans->bits_per_word <= 8) { const u8 *buf = tp.trans->tx_buf; u8 val = buf ? buf[tp.byte] : 0x00; write_txram_slot_u8(qspi, slot, val); dev_dbg(&qspi->pdev->dev, "WR %02x\n", val); - } else { + } else if (tp.trans->bits_per_word <= 16) { const u16 *buf = tp.trans->tx_buf; u16 val = buf ? buf[tp.byte / 2] : 0x0000; write_txram_slot_u16(qspi, slot, val); dev_dbg(&qspi->pdev->dev, "WR %04x\n", val); + } else if (tp.trans->bits_per_word <= 32) { + const u32 *buf = tp.trans->tx_buf; + u32 val = buf ? buf[tp.byte/4] : 0x0; + + write_txram_slot_u32(qspi, slot, val); + dev_dbg(&qspi->pdev->dev, "WR %08x\n", val); + } else if (tp.trans->bits_per_word <= 64) { + const u64 *buf = tp.trans->tx_buf; + u64 val = (buf ? buf[tp.byte/8] : 0x0); + + /* use the length of delay from SPCR1_LSB */ + if (bcm_qspi_has_fastbr(qspi)) + mspi_cdram |= MSPI_CDRAM_DT_BIT; + + write_txram_slot_u64(qspi, slot, val); + dev_dbg(&qspi->pdev->dev, "WR %llx\n", val); } - mspi_cdram = MSPI_CDRAM_CONT_BIT; + + mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 : + MSPI_CDRAM_BITSE_BIT); + + /* set 3wrire halfduplex mode data from master to slave */ + if ((spi->mode & SPI_3WIRE) && tp.trans->tx_buf) + mspi_cdram |= MSPI_CDRAM_OUTP; if (has_bspi(qspi)) mspi_cdram &= ~1; @@ -813,9 +957,6 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) mspi_cdram |= (~(1 << spi->chip_select) & MSPI_CDRAM_PCS); - mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 : - MSPI_CDRAM_BITSE_BIT); - write_cdram_slot(qspi, slot, mspi_cdram); tstatus = update_qspi_trans_byte_count(qspi, &tp, @@ -1350,7 +1491,8 @@ int bcm_qspi_probe(struct platform_device *pdev, qspi->master = master; master->bus_num = -1; - master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD | + SPI_3WIRE; master->setup = bcm_qspi_setup; master->transfer_one = bcm_qspi_transfer_one; master->mem_ops = &bcm_qspi_mem_ops; @@ -1460,7 +1602,7 @@ int bcm_qspi_probe(struct platform_device *pdev, &qspi->dev_ids[val]); if (ret < 0) { dev_err(&pdev->dev, "IRQ %s not found\n", name); - goto qspi_probe_err; + goto qspi_unprepare_err; } qspi->dev_ids[val].dev = qspi; @@ -1475,7 +1617,7 @@ int bcm_qspi_probe(struct platform_device *pdev, if (!num_ints) { dev_err(&pdev->dev, "no IRQs registered, cannot init driver\n"); ret = -EINVAL; - goto qspi_probe_err; + goto qspi_unprepare_err; } bcm_qspi_hw_init(qspi); @@ -1499,6 +1641,7 @@ int bcm_qspi_probe(struct platform_device *pdev, qspi_reg_err: bcm_qspi_hw_uninit(qspi); +qspi_unprepare_err: clk_disable_unprepare(qspi->clk); qspi_probe_err: kfree(qspi->dev_ids); diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 101cc71bffa7..8b3d268ac63c 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -13,6 +13,7 @@ #include <linux/dmaengine.h> #include <linux/err.h> #include <linux/errno.h> +#include <linux/firmware/xlnx-zynqmp.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/iopoll.h> @@ -35,6 +36,7 @@ /* Quirks */ #define CQSPI_NEEDS_WR_DELAY BIT(0) #define CQSPI_DISABLE_DAC_MODE BIT(1) +#define CQSPI_SUPPORT_EXTERNAL_DMA BIT(2) /* Capabilities */ #define CQSPI_SUPPORTS_OCTAL BIT(0) @@ -82,11 +84,16 @@ struct cqspi_st { u32 wr_delay; bool use_direct_mode; struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT]; + bool use_dma_read; + u32 pd_dev_id; }; struct cqspi_driver_platdata { u32 hwcaps_mask; u8 quirks; + int (*indirect_read_dma)(struct cqspi_flash_pdata *f_pdata, + u_char *rxbuf, loff_t from_addr, size_t n_rx); + u32 (*get_dma_status)(struct cqspi_st *cqspi); }; /* Operation timeout value */ @@ -217,6 +224,8 @@ struct cqspi_driver_platdata { #define CQSPI_REG_INDIRECTWRSTARTADDR 0x78 #define CQSPI_REG_INDIRECTWRBYTES 0x7C +#define CQSPI_REG_INDTRIG_ADDRRANGE 0x80 + #define CQSPI_REG_CMDADDRESS 0x94 #define CQSPI_REG_CMDREADDATALOWER 0xA0 #define CQSPI_REG_CMDREADDATAUPPER 0xA4 @@ -231,6 +240,23 @@ struct cqspi_driver_platdata { #define CQSPI_REG_OP_EXT_WRITE_LSB 16 #define CQSPI_REG_OP_EXT_STIG_LSB 0 +#define CQSPI_REG_VERSAL_DMA_SRC_ADDR 0x1000 + +#define CQSPI_REG_VERSAL_DMA_DST_ADDR 0x1800 +#define CQSPI_REG_VERSAL_DMA_DST_SIZE 0x1804 + +#define CQSPI_REG_VERSAL_DMA_DST_CTRL 0x180C + +#define CQSPI_REG_VERSAL_DMA_DST_I_STS 0x1814 +#define CQSPI_REG_VERSAL_DMA_DST_I_EN 0x1818 +#define CQSPI_REG_VERSAL_DMA_DST_I_DIS 0x181C +#define CQSPI_REG_VERSAL_DMA_DST_DONE_MASK BIT(1) + +#define CQSPI_REG_VERSAL_DMA_DST_ADDR_MSB 0x1828 + +#define CQSPI_REG_VERSAL_DMA_DST_CTRL_VAL 0xF43FFA00 +#define CQSPI_REG_VERSAL_ADDRRANGE_WIDTH_VAL 0x6 + /* Interrupt status bits */ #define CQSPI_REG_IRQ_MODE_ERR BIT(0) #define CQSPI_REG_IRQ_UNDERFLOW BIT(1) @@ -250,6 +276,9 @@ struct cqspi_driver_platdata { CQSPI_REG_IRQ_UNDERFLOW) #define CQSPI_IRQ_STATUS_MASK 0x1FFFF +#define CQSPI_DMA_UNALIGN 0x3 + +#define CQSPI_REG_VERSAL_DMA_VAL 0x602 static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr) { @@ -275,10 +304,26 @@ static u32 cqspi_get_rd_sram_level(struct cqspi_st *cqspi) return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK; } +static u32 cqspi_get_versal_dma_status(struct cqspi_st *cqspi) +{ + u32 dma_status; + + dma_status = readl(cqspi->iobase + + CQSPI_REG_VERSAL_DMA_DST_I_STS); + writel(dma_status, cqspi->iobase + + CQSPI_REG_VERSAL_DMA_DST_I_STS); + + return dma_status & CQSPI_REG_VERSAL_DMA_DST_DONE_MASK; +} + static irqreturn_t cqspi_irq_handler(int this_irq, void *dev) { struct cqspi_st *cqspi = dev; unsigned int irq_status; + struct device *device = &cqspi->pdev->dev; + const struct cqspi_driver_platdata *ddata; + + ddata = of_device_get_match_data(device); /* Read interrupt status */ irq_status = readl(cqspi->iobase + CQSPI_REG_IRQSTATUS); @@ -286,6 +331,13 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev) /* Clear interrupt */ writel(irq_status, cqspi->iobase + CQSPI_REG_IRQSTATUS); + if (cqspi->use_dma_read && ddata && ddata->get_dma_status) { + if (ddata->get_dma_status(cqspi)) { + complete(&cqspi->transfer_complete); + return IRQ_HANDLED; + } + } + irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR; if (irq_status) @@ -781,6 +833,131 @@ failrd: return ret; } +static int cqspi_versal_indirect_read_dma(struct cqspi_flash_pdata *f_pdata, + u_char *rxbuf, loff_t from_addr, + size_t n_rx) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + struct device *dev = &cqspi->pdev->dev; + void __iomem *reg_base = cqspi->iobase; + u32 reg, bytes_to_dma; + loff_t addr = from_addr; + void *buf = rxbuf; + dma_addr_t dma_addr; + u8 bytes_rem; + int ret = 0; + + bytes_rem = n_rx % 4; + bytes_to_dma = (n_rx - bytes_rem); + + if (!bytes_to_dma) + goto nondmard; + + ret = zynqmp_pm_ospi_mux_select(cqspi->pd_dev_id, PM_OSPI_MUX_SEL_DMA); + if (ret) + return ret; + + reg = readl(cqspi->iobase + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_DMA_MASK; + writel(reg, cqspi->iobase + CQSPI_REG_CONFIG); + + dma_addr = dma_map_single(dev, rxbuf, bytes_to_dma, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, dma_addr)) { + dev_err(dev, "dma mapping failed\n"); + return -ENOMEM; + } + + writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR); + writel(bytes_to_dma, reg_base + CQSPI_REG_INDIRECTRDBYTES); + writel(CQSPI_REG_VERSAL_ADDRRANGE_WIDTH_VAL, + reg_base + CQSPI_REG_INDTRIG_ADDRRANGE); + + /* Clear all interrupts. */ + writel(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS); + + /* Enable DMA done interrupt */ + writel(CQSPI_REG_VERSAL_DMA_DST_DONE_MASK, + reg_base + CQSPI_REG_VERSAL_DMA_DST_I_EN); + + /* Default DMA periph configuration */ + writel(CQSPI_REG_VERSAL_DMA_VAL, reg_base + CQSPI_REG_DMA); + + /* Configure DMA Dst address */ + writel(lower_32_bits(dma_addr), + reg_base + CQSPI_REG_VERSAL_DMA_DST_ADDR); + writel(upper_32_bits(dma_addr), + reg_base + CQSPI_REG_VERSAL_DMA_DST_ADDR_MSB); + + /* Configure DMA Src address */ + writel(cqspi->trigger_address, reg_base + + CQSPI_REG_VERSAL_DMA_SRC_ADDR); + + /* Set DMA destination size */ + writel(bytes_to_dma, reg_base + CQSPI_REG_VERSAL_DMA_DST_SIZE); + + /* Set DMA destination control */ + writel(CQSPI_REG_VERSAL_DMA_DST_CTRL_VAL, + reg_base + CQSPI_REG_VERSAL_DMA_DST_CTRL); + + writel(CQSPI_REG_INDIRECTRD_START_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + + reinit_completion(&cqspi->transfer_complete); + + if (!wait_for_completion_timeout(&cqspi->transfer_complete, + msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS))) { + ret = -ETIMEDOUT; + goto failrd; + } + + /* Disable DMA interrupt */ + writel(0x0, cqspi->iobase + CQSPI_REG_VERSAL_DMA_DST_I_DIS); + + /* Clear indirect completion status */ + writel(CQSPI_REG_INDIRECTRD_DONE_MASK, + cqspi->iobase + CQSPI_REG_INDIRECTRD); + dma_unmap_single(dev, dma_addr, bytes_to_dma, DMA_FROM_DEVICE); + + reg = readl(cqspi->iobase + CQSPI_REG_CONFIG); + reg &= ~CQSPI_REG_CONFIG_DMA_MASK; + writel(reg, cqspi->iobase + CQSPI_REG_CONFIG); + + ret = zynqmp_pm_ospi_mux_select(cqspi->pd_dev_id, + PM_OSPI_MUX_SEL_LINEAR); + if (ret) + return ret; + +nondmard: + if (bytes_rem) { + addr += bytes_to_dma; + buf += bytes_to_dma; + ret = cqspi_indirect_read_execute(f_pdata, buf, addr, + bytes_rem); + if (ret) + return ret; + } + + return 0; + +failrd: + /* Disable DMA interrupt */ + writel(0x0, reg_base + CQSPI_REG_VERSAL_DMA_DST_I_DIS); + + /* Cancel the indirect read */ + writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK, + reg_base + CQSPI_REG_INDIRECTRD); + + dma_unmap_single(dev, dma_addr, bytes_to_dma, DMA_FROM_DEVICE); + + reg = readl(cqspi->iobase + CQSPI_REG_CONFIG); + reg &= ~CQSPI_REG_CONFIG_DMA_MASK; + writel(reg, cqspi->iobase + CQSPI_REG_CONFIG); + + zynqmp_pm_ospi_mux_select(cqspi->pd_dev_id, PM_OSPI_MUX_SEL_LINEAR); + + return ret; +} + static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { @@ -1180,11 +1357,15 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { struct cqspi_st *cqspi = f_pdata->cqspi; + struct device *dev = &cqspi->pdev->dev; + const struct cqspi_driver_platdata *ddata; loff_t from = op->addr.val; size_t len = op->data.nbytes; u_char *buf = op->data.buf.in; + u64 dma_align = (u64)(uintptr_t)buf; int ret; + ddata = of_device_get_match_data(dev); ret = cqspi_set_protocol(f_pdata, op); if (ret) return ret; @@ -1196,6 +1377,10 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata, if (cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size)) return cqspi_direct_read_execute(f_pdata, buf, from, len); + if (cqspi->use_dma_read && ddata && ddata->indirect_read_dma && + virt_addr_valid(buf) && ((dma_align & CQSPI_DMA_UNALIGN) == 0)) + return ddata->indirect_read_dma(f_pdata, buf, from, len); + return cqspi_indirect_read_execute(f_pdata, buf, from, len); } @@ -1299,6 +1484,7 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi) { struct device *dev = &cqspi->pdev->dev; struct device_node *np = dev->of_node; + u32 id[2]; cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs"); @@ -1323,6 +1509,10 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi) cqspi->rclk_en = of_property_read_bool(np, "cdns,rclk-en"); + if (!of_property_read_u32_array(np, "power-domains", id, + ARRAY_SIZE(id))) + cqspi->pd_dev_id = id[1]; + return 0; } @@ -1359,6 +1549,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi) writel(reg, cqspi->iobase + CQSPI_REG_CONFIG); } + /* Enable DMA interface */ + if (cqspi->use_dma_read) { + reg = readl(cqspi->iobase + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_DMA_MASK; + writel(reg, cqspi->iobase + CQSPI_REG_CONFIG); + } + cqspi_controller_enable(cqspi, 1); } @@ -1548,6 +1745,12 @@ static int cqspi_probe(struct platform_device *pdev) master->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL; if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE)) cqspi->use_direct_mode = true; + if (ddata->quirks & CQSPI_SUPPORT_EXTERNAL_DMA) + cqspi->use_dma_read = true; + + if (of_device_is_compatible(pdev->dev.of_node, + "xlnx,versal-ospi-1.0")) + dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); } ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0, @@ -1656,6 +1859,13 @@ static const struct cqspi_driver_platdata intel_lgm_qspi = { .quirks = CQSPI_DISABLE_DAC_MODE, }; +static const struct cqspi_driver_platdata versal_ospi = { + .hwcaps_mask = CQSPI_SUPPORTS_OCTAL, + .quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA, + .indirect_read_dma = cqspi_versal_indirect_read_dma, + .get_dma_status = cqspi_get_versal_dma_status, +}; + static const struct of_device_id cqspi_dt_ids[] = { { .compatible = "cdns,qspi-nor", @@ -1673,6 +1883,10 @@ static const struct of_device_id cqspi_dt_ids[] = { .compatible = "intel,lgm-qspi", .data = &intel_lgm_qspi, }, + { + .compatible = "xlnx,versal-ospi-1.0", + .data = (void *)&versal_ospi, + }, { /* end of table */ } }; diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c new file mode 100644 index 000000000000..4bc1b93fc276 --- /dev/null +++ b/drivers/spi/spi-cadence-xspi.c @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Cadence XSPI flash controller driver +// Copyright (C) 2020-21 Cadence + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> +#include <linux/bitfield.h> +#include <linux/limits.h> +#include <linux/log2.h> + +#define CDNS_XSPI_MAGIC_NUM_VALUE 0x6522 +#define CDNS_XSPI_MAX_BANKS 8 +#define CDNS_XSPI_NAME "cadence-xspi" + +/* + * Note: below are additional auxiliary registers to + * configure XSPI controller pin-strap settings + */ + +/* PHY DQ timing register */ +#define CDNS_XSPI_CCP_PHY_DQ_TIMING 0x0000 + +/* PHY DQS timing register */ +#define CDNS_XSPI_CCP_PHY_DQS_TIMING 0x0004 + +/* PHY gate loopback control register */ +#define CDNS_XSPI_CCP_PHY_GATE_LPBCK_CTRL 0x0008 + +/* PHY DLL slave control register */ +#define CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL 0x0010 + +/* DLL PHY control register */ +#define CDNS_XSPI_DLL_PHY_CTRL 0x1034 + +/* Command registers */ +#define CDNS_XSPI_CMD_REG_0 0x0000 +#define CDNS_XSPI_CMD_REG_1 0x0004 +#define CDNS_XSPI_CMD_REG_2 0x0008 +#define CDNS_XSPI_CMD_REG_3 0x000C +#define CDNS_XSPI_CMD_REG_4 0x0010 +#define CDNS_XSPI_CMD_REG_5 0x0014 + +/* Command status registers */ +#define CDNS_XSPI_CMD_STATUS_REG 0x0044 + +/* Controller status register */ +#define CDNS_XSPI_CTRL_STATUS_REG 0x0100 +#define CDNS_XSPI_INIT_COMPLETED BIT(16) +#define CDNS_XSPI_INIT_LEGACY BIT(9) +#define CDNS_XSPI_INIT_FAIL BIT(8) +#define CDNS_XSPI_CTRL_BUSY BIT(7) + +/* Controller interrupt status register */ +#define CDNS_XSPI_INTR_STATUS_REG 0x0110 +#define CDNS_XSPI_STIG_DONE BIT(23) +#define CDNS_XSPI_SDMA_ERROR BIT(22) +#define CDNS_XSPI_SDMA_TRIGGER BIT(21) +#define CDNS_XSPI_CMD_IGNRD_EN BIT(20) +#define CDNS_XSPI_DDMA_TERR_EN BIT(18) +#define CDNS_XSPI_CDMA_TREE_EN BIT(17) +#define CDNS_XSPI_CTRL_IDLE_EN BIT(16) + +#define CDNS_XSPI_TRD_COMP_INTR_STATUS 0x0120 +#define CDNS_XSPI_TRD_ERR_INTR_STATUS 0x0130 +#define CDNS_XSPI_TRD_ERR_INTR_EN 0x0134 + +/* Controller interrupt enable register */ +#define CDNS_XSPI_INTR_ENABLE_REG 0x0114 +#define CDNS_XSPI_INTR_EN BIT(31) +#define CDNS_XSPI_STIG_DONE_EN BIT(23) +#define CDNS_XSPI_SDMA_ERROR_EN BIT(22) +#define CDNS_XSPI_SDMA_TRIGGER_EN BIT(21) + +#define CDNS_XSPI_INTR_MASK (CDNS_XSPI_INTR_EN | \ + CDNS_XSPI_STIG_DONE_EN | \ + CDNS_XSPI_SDMA_ERROR_EN | \ + CDNS_XSPI_SDMA_TRIGGER_EN) + +/* Controller config register */ +#define CDNS_XSPI_CTRL_CONFIG_REG 0x0230 +#define CDNS_XSPI_CTRL_WORK_MODE GENMASK(6, 5) + +#define CDNS_XSPI_WORK_MODE_DIRECT 0 +#define CDNS_XSPI_WORK_MODE_STIG 1 +#define CDNS_XSPI_WORK_MODE_ACMD 3 + +/* SDMA trigger transaction registers */ +#define CDNS_XSPI_SDMA_SIZE_REG 0x0240 +#define CDNS_XSPI_SDMA_TRD_INFO_REG 0x0244 +#define CDNS_XSPI_SDMA_DIR BIT(8) + +/* Controller features register */ +#define CDNS_XSPI_CTRL_FEATURES_REG 0x0F04 +#define CDNS_XSPI_NUM_BANKS GENMASK(25, 24) +#define CDNS_XSPI_DMA_DATA_WIDTH BIT(21) +#define CDNS_XSPI_NUM_THREADS GENMASK(3, 0) + +/* Controller version register */ +#define CDNS_XSPI_CTRL_VERSION_REG 0x0F00 +#define CDNS_XSPI_MAGIC_NUM GENMASK(31, 16) +#define CDNS_XSPI_CTRL_REV GENMASK(7, 0) + +/* STIG Profile 1.0 instruction fields (split into registers) */ +#define CDNS_XSPI_CMD_INSTR_TYPE GENMASK(6, 0) +#define CDNS_XSPI_CMD_P1_R1_ADDR0 GENMASK(31, 24) +#define CDNS_XSPI_CMD_P1_R2_ADDR1 GENMASK(7, 0) +#define CDNS_XSPI_CMD_P1_R2_ADDR2 GENMASK(15, 8) +#define CDNS_XSPI_CMD_P1_R2_ADDR3 GENMASK(23, 16) +#define CDNS_XSPI_CMD_P1_R2_ADDR4 GENMASK(31, 24) +#define CDNS_XSPI_CMD_P1_R3_ADDR5 GENMASK(7, 0) +#define CDNS_XSPI_CMD_P1_R3_CMD GENMASK(23, 16) +#define CDNS_XSPI_CMD_P1_R3_NUM_ADDR_BYTES GENMASK(30, 28) +#define CDNS_XSPI_CMD_P1_R4_ADDR_IOS GENMASK(1, 0) +#define CDNS_XSPI_CMD_P1_R4_CMD_IOS GENMASK(9, 8) +#define CDNS_XSPI_CMD_P1_R4_BANK GENMASK(14, 12) + +/* STIG data sequence instruction fields (split into registers) */ +#define CDNS_XSPI_CMD_DSEQ_R2_DCNT_L GENMASK(31, 16) +#define CDNS_XSPI_CMD_DSEQ_R3_DCNT_H GENMASK(15, 0) +#define CDNS_XSPI_CMD_DSEQ_R3_NUM_OF_DUMMY GENMASK(25, 20) +#define CDNS_XSPI_CMD_DSEQ_R4_BANK GENMASK(14, 12) +#define CDNS_XSPI_CMD_DSEQ_R4_DATA_IOS GENMASK(9, 8) +#define CDNS_XSPI_CMD_DSEQ_R4_DIR BIT(4) + +/* STIG command status fields */ +#define CDNS_XSPI_CMD_STATUS_COMPLETED BIT(15) +#define CDNS_XSPI_CMD_STATUS_FAILED BIT(14) +#define CDNS_XSPI_CMD_STATUS_DQS_ERROR BIT(3) +#define CDNS_XSPI_CMD_STATUS_CRC_ERROR BIT(2) +#define CDNS_XSPI_CMD_STATUS_BUS_ERROR BIT(1) +#define CDNS_XSPI_CMD_STATUS_INV_SEQ_ERROR BIT(0) + +#define CDNS_XSPI_STIG_DONE_FLAG BIT(0) +#define CDNS_XSPI_TRD_STATUS 0x0104 + +/* Helper macros for filling command registers */ +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_1(op, data_phase) ( \ + FIELD_PREP(CDNS_XSPI_CMD_INSTR_TYPE, (data_phase) ? \ + CDNS_XSPI_STIG_INSTR_TYPE_1 : CDNS_XSPI_STIG_INSTR_TYPE_0) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R1_ADDR0, (op)->addr.val & 0xff)) + +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_2(op) ( \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR1, ((op)->addr.val >> 8) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR2, ((op)->addr.val >> 16) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR3, ((op)->addr.val >> 24) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR4, ((op)->addr.val >> 32) & 0xFF)) + +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_3(op) ( \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R3_ADDR5, ((op)->addr.val >> 40) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R3_CMD, (op)->cmd.opcode) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R3_NUM_ADDR_BYTES, (op)->addr.nbytes)) + +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_4(op, chipsel) ( \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R4_ADDR_IOS, ilog2((op)->addr.buswidth)) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R4_CMD_IOS, ilog2((op)->cmd.buswidth)) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R4_BANK, chipsel)) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_1(op) \ + FIELD_PREP(CDNS_XSPI_CMD_INSTR_TYPE, CDNS_XSPI_STIG_INSTR_TYPE_DATA_SEQ) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_2(op) \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R2_DCNT_L, (op)->data.nbytes & 0xFFFF) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_3(op) ( \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_DCNT_H, \ + ((op)->data.nbytes >> 16) & 0xffff) | \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_NUM_OF_DUMMY, (op)->dummy.nbytes * 8)) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_4(op, chipsel) ( \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_BANK, chipsel) | \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_DATA_IOS, \ + ilog2((op)->data.buswidth)) | \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_DIR, \ + ((op)->data.dir == SPI_MEM_DATA_IN) ? \ + CDNS_XSPI_STIG_CMD_DIR_READ : CDNS_XSPI_STIG_CMD_DIR_WRITE)) + +enum cdns_xspi_stig_instr_type { + CDNS_XSPI_STIG_INSTR_TYPE_0, + CDNS_XSPI_STIG_INSTR_TYPE_1, + CDNS_XSPI_STIG_INSTR_TYPE_DATA_SEQ = 127, +}; + +enum cdns_xspi_sdma_dir { + CDNS_XSPI_SDMA_DIR_READ, + CDNS_XSPI_SDMA_DIR_WRITE, +}; + +enum cdns_xspi_stig_cmd_dir { + CDNS_XSPI_STIG_CMD_DIR_READ, + CDNS_XSPI_STIG_CMD_DIR_WRITE, +}; + +struct cdns_xspi_dev { + struct platform_device *pdev; + struct device *dev; + + void __iomem *iobase; + void __iomem *auxbase; + void __iomem *sdmabase; + + int irq; + int cur_cs; + unsigned int sdmasize; + + struct completion cmd_complete; + struct completion auto_cmd_complete; + struct completion sdma_complete; + bool sdma_error; + + void *in_buffer; + const void *out_buffer; + + u8 hw_num_banks; +}; + +static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi) +{ + u32 ctrl_stat; + + return readl_relaxed_poll_timeout(cdns_xspi->iobase + + CDNS_XSPI_CTRL_STATUS_REG, + ctrl_stat, + ((ctrl_stat & + CDNS_XSPI_CTRL_BUSY) == 0), + 100, 1000); +} + +static void cdns_xspi_trigger_command(struct cdns_xspi_dev *cdns_xspi, + u32 cmd_regs[6]) +{ + writel(cmd_regs[5], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_5); + writel(cmd_regs[4], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_4); + writel(cmd_regs[3], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_3); + writel(cmd_regs[2], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_2); + writel(cmd_regs[1], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_1); + writel(cmd_regs[0], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_0); +} + +static int cdns_xspi_check_command_status(struct cdns_xspi_dev *cdns_xspi) +{ + int ret = 0; + u32 cmd_status = readl(cdns_xspi->iobase + CDNS_XSPI_CMD_STATUS_REG); + + if (cmd_status & CDNS_XSPI_CMD_STATUS_COMPLETED) { + if ((cmd_status & CDNS_XSPI_CMD_STATUS_FAILED) != 0) { + if (cmd_status & CDNS_XSPI_CMD_STATUS_DQS_ERROR) { + dev_err(cdns_xspi->dev, + "Incorrect DQS pulses detected\n"); + ret = -EPROTO; + } + if (cmd_status & CDNS_XSPI_CMD_STATUS_CRC_ERROR) { + dev_err(cdns_xspi->dev, + "CRC error received\n"); + ret = -EPROTO; + } + if (cmd_status & CDNS_XSPI_CMD_STATUS_BUS_ERROR) { + dev_err(cdns_xspi->dev, + "Error resp on system DMA interface\n"); + ret = -EPROTO; + } + if (cmd_status & CDNS_XSPI_CMD_STATUS_INV_SEQ_ERROR) { + dev_err(cdns_xspi->dev, + "Invalid command sequence detected\n"); + ret = -EPROTO; + } + } + } else { + dev_err(cdns_xspi->dev, "Fatal err - command not completed\n"); + ret = -EPROTO; + } + + return ret; +} + +static void cdns_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi, + bool enabled) +{ + u32 intr_enable; + + intr_enable = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG); + if (enabled) + intr_enable |= CDNS_XSPI_INTR_MASK; + else + intr_enable &= ~CDNS_XSPI_INTR_MASK; + writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG); +} + +static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi) +{ + u32 ctrl_ver; + u32 ctrl_features; + u16 hw_magic_num; + + ctrl_ver = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_VERSION_REG); + hw_magic_num = FIELD_GET(CDNS_XSPI_MAGIC_NUM, ctrl_ver); + if (hw_magic_num != CDNS_XSPI_MAGIC_NUM_VALUE) { + dev_err(cdns_xspi->dev, + "Incorrect XSPI magic number: %x, expected: %x\n", + hw_magic_num, CDNS_XSPI_MAGIC_NUM_VALUE); + return -EIO; + } + + ctrl_features = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_FEATURES_REG); + cdns_xspi->hw_num_banks = FIELD_GET(CDNS_XSPI_NUM_BANKS, ctrl_features); + cdns_xspi_set_interrupts(cdns_xspi, false); + + return 0; +} + +static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) +{ + u32 sdma_size, sdma_trd_info; + u8 sdma_dir; + + sdma_size = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_SIZE_REG); + sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG); + sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info); + + switch (sdma_dir) { + case CDNS_XSPI_SDMA_DIR_READ: + ioread8_rep(cdns_xspi->sdmabase, + cdns_xspi->in_buffer, sdma_size); + break; + + case CDNS_XSPI_SDMA_DIR_WRITE: + iowrite8_rep(cdns_xspi->sdmabase, + cdns_xspi->out_buffer, sdma_size); + break; + } +} + +static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi, + const struct spi_mem_op *op, + bool data_phase) +{ + u32 cmd_regs[6]; + u32 cmd_status; + int ret; + + ret = cdns_xspi_wait_for_controller_idle(cdns_xspi); + if (ret < 0) + return -EIO; + + writel(FIELD_PREP(CDNS_XSPI_CTRL_WORK_MODE, CDNS_XSPI_WORK_MODE_STIG), + cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG); + + cdns_xspi_set_interrupts(cdns_xspi, true); + cdns_xspi->sdma_error = false; + + memset(cmd_regs, 0, sizeof(cmd_regs)); + cmd_regs[1] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_1(op, data_phase); + cmd_regs[2] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_2(op); + cmd_regs[3] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_3(op); + cmd_regs[4] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_4(op, + cdns_xspi->cur_cs); + + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + + if (data_phase) { + cmd_regs[0] = CDNS_XSPI_STIG_DONE_FLAG; + cmd_regs[1] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_1(op); + cmd_regs[2] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_2(op); + cmd_regs[3] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_3(op); + cmd_regs[4] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_4(op, + cdns_xspi->cur_cs); + + cdns_xspi->in_buffer = op->data.buf.in; + cdns_xspi->out_buffer = op->data.buf.out; + + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + + wait_for_completion(&cdns_xspi->sdma_complete); + if (cdns_xspi->sdma_error) { + cdns_xspi_set_interrupts(cdns_xspi, false); + return -EIO; + } + cdns_xspi_sdma_handle(cdns_xspi); + } + + wait_for_completion(&cdns_xspi->cmd_complete); + cdns_xspi_set_interrupts(cdns_xspi, false); + + cmd_status = cdns_xspi_check_command_status(cdns_xspi); + if (cmd_status) + return -EPROTO; + + return 0; +} + +static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi, + struct spi_mem *mem, + const struct spi_mem_op *op) +{ + enum spi_mem_data_dir dir = op->data.dir; + + if (cdns_xspi->cur_cs != mem->spi->chip_select) + cdns_xspi->cur_cs = mem->spi->chip_select; + + return cdns_xspi_send_stig_command(cdns_xspi, op, + (dir != SPI_MEM_NO_DATA)); +} + +static int cdns_xspi_mem_op_execute(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct cdns_xspi_dev *cdns_xspi = + spi_master_get_devdata(mem->spi->master); + int ret = 0; + + ret = cdns_xspi_mem_op(cdns_xspi, mem, op); + + return ret; +} + +static int cdns_xspi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + struct cdns_xspi_dev *cdns_xspi = + spi_master_get_devdata(mem->spi->master); + + op->data.nbytes = clamp_val(op->data.nbytes, 0, cdns_xspi->sdmasize); + + return 0; +} + +static const struct spi_controller_mem_ops cadence_xspi_mem_ops = { + .exec_op = cdns_xspi_mem_op_execute, + .adjust_op_size = cdns_xspi_adjust_mem_op_size, +}; + +static irqreturn_t cdns_xspi_irq_handler(int this_irq, void *dev) +{ + struct cdns_xspi_dev *cdns_xspi = dev; + u32 irq_status; + irqreturn_t result = IRQ_NONE; + + irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG); + writel(irq_status, cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG); + + if (irq_status & + (CDNS_XSPI_SDMA_ERROR | CDNS_XSPI_SDMA_TRIGGER | + CDNS_XSPI_STIG_DONE)) { + if (irq_status & CDNS_XSPI_SDMA_ERROR) { + dev_err(cdns_xspi->dev, + "Slave DMA transaction error\n"); + cdns_xspi->sdma_error = true; + complete(&cdns_xspi->sdma_complete); + } + + if (irq_status & CDNS_XSPI_SDMA_TRIGGER) + complete(&cdns_xspi->sdma_complete); + + if (irq_status & CDNS_XSPI_STIG_DONE) + complete(&cdns_xspi->cmd_complete); + + result = IRQ_HANDLED; + } + + irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_TRD_COMP_INTR_STATUS); + if (irq_status) { + writel(irq_status, + cdns_xspi->iobase + CDNS_XSPI_TRD_COMP_INTR_STATUS); + + complete(&cdns_xspi->auto_cmd_complete); + + result = IRQ_HANDLED; + } + + return result; +} + +static int cdns_xspi_of_get_plat_data(struct platform_device *pdev) +{ + struct device_node *node_prop = pdev->dev.of_node; + struct device_node *node_child; + unsigned int cs; + + for_each_child_of_node(node_prop, node_child) { + if (!of_device_is_available(node_child)) + continue; + + if (of_property_read_u32(node_child, "reg", &cs)) { + dev_err(&pdev->dev, "Couldn't get memory chip select\n"); + of_node_put(node_child); + return -ENXIO; + } else if (cs >= CDNS_XSPI_MAX_BANKS) { + dev_err(&pdev->dev, "reg (cs) parameter value too large\n"); + of_node_put(node_child); + return -ENXIO; + } + } + + return 0; +} + +static void cdns_xspi_print_phy_config(struct cdns_xspi_dev *cdns_xspi) +{ + struct device *dev = cdns_xspi->dev; + + dev_info(dev, "PHY configuration\n"); + dev_info(dev, " * xspi_dll_phy_ctrl: %08x\n", + readl(cdns_xspi->iobase + CDNS_XSPI_DLL_PHY_CTRL)); + dev_info(dev, " * phy_dq_timing: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DQ_TIMING)); + dev_info(dev, " * phy_dqs_timing: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DQS_TIMING)); + dev_info(dev, " * phy_gate_loopback_ctrl: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_GATE_LPBCK_CTRL)); + dev_info(dev, " * phy_dll_slave_ctrl: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL)); +} + +static int cdns_xspi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_master *master = NULL; + struct cdns_xspi_dev *cdns_xspi = NULL; + struct resource *res; + int ret; + + master = devm_spi_alloc_master(dev, sizeof(*cdns_xspi)); + if (!master) + return -ENOMEM; + + master->mode_bits = SPI_3WIRE | SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL | SPI_RX_OCTAL | + SPI_MODE_0 | SPI_MODE_3; + + master->mem_ops = &cadence_xspi_mem_ops; + master->dev.of_node = pdev->dev.of_node; + master->bus_num = -1; + + platform_set_drvdata(pdev, master); + + cdns_xspi = spi_master_get_devdata(master); + cdns_xspi->pdev = pdev; + cdns_xspi->dev = &pdev->dev; + cdns_xspi->cur_cs = 0; + + init_completion(&cdns_xspi->cmd_complete); + init_completion(&cdns_xspi->auto_cmd_complete); + init_completion(&cdns_xspi->sdma_complete); + + ret = cdns_xspi_of_get_plat_data(pdev); + if (ret) + return -ENODEV; + + cdns_xspi->iobase = devm_platform_ioremap_resource_byname(pdev, "io"); + if (IS_ERR(cdns_xspi->iobase)) { + dev_err(dev, "Failed to remap controller base address\n"); + return PTR_ERR(cdns_xspi->iobase); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sdma"); + cdns_xspi->sdmabase = devm_ioremap_resource(dev, res); + if (IS_ERR(cdns_xspi->sdmabase)) { + dev_err(dev, "Failed to remap SDMA address\n"); + return PTR_ERR(cdns_xspi->sdmabase); + } + cdns_xspi->sdmasize = resource_size(res); + + cdns_xspi->auxbase = devm_platform_ioremap_resource_byname(pdev, "aux"); + if (IS_ERR(cdns_xspi->auxbase)) { + dev_err(dev, "Failed to remap AUX address\n"); + return PTR_ERR(cdns_xspi->auxbase); + } + + cdns_xspi->irq = platform_get_irq(pdev, 0); + if (cdns_xspi->irq < 0) { + dev_err(dev, "Failed to get IRQ\n"); + return -ENXIO; + } + + ret = devm_request_irq(dev, cdns_xspi->irq, cdns_xspi_irq_handler, + IRQF_SHARED, pdev->name, cdns_xspi); + if (ret) { + dev_err(dev, "Failed to request IRQ: %d\n", cdns_xspi->irq); + return ret; + } + + cdns_xspi_print_phy_config(cdns_xspi); + + ret = cdns_xspi_controller_init(cdns_xspi); + if (ret) { + dev_err(dev, "Failed to initialize controller\n"); + return ret; + } + + master->num_chipselect = 1 << cdns_xspi->hw_num_banks; + + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(dev, "Failed to register SPI master\n"); + return ret; + } + + dev_info(dev, "Successfully registered SPI master\n"); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id cdns_xspi_of_match[] = { + { + .compatible = "cdns,xspi-nor", + }, + { /* 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, + .remove = NULL, + .driver = { + .name = CDNS_XSPI_NAME, + .of_match_table = cdns_xspi_of_match, + }, +}; + +module_platform_driver(cdns_xspi_platform_driver); + +MODULE_DESCRIPTION("Cadence XSPI Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" CDNS_XSPI_NAME); +MODULE_AUTHOR("Konrad Kociolek <konrad@cadence.com>"); +MODULE_AUTHOR("Jayshri Pawar <jpawar@cadence.com>"); +MODULE_AUTHOR("Parshuram Thombare <pthombar@cadence.com>"); diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index 829770b8ec74..b6c7467f0b59 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -67,9 +67,14 @@ SPI_FSI_STATUS_RDR_OVERRUN) #define SPI_FSI_PORT_CTRL 0x9 +struct fsi2spi { + struct fsi_device *fsi; /* FSI2SPI CFAM engine device */ + struct mutex lock; /* lock access to the device */ +}; + struct fsi_spi { struct device *dev; /* SPI controller device */ - struct fsi_device *fsi; /* FSI2SPI CFAM engine device */ + struct fsi2spi *bridge; /* FSI2SPI device */ u32 base; }; @@ -104,7 +109,7 @@ static int fsi_spi_check_status(struct fsi_spi *ctx) u32 sts; __be32 sts_be; - rc = fsi_device_read(ctx->fsi, FSI2SPI_STATUS, &sts_be, + rc = fsi_device_read(ctx->bridge->fsi, FSI2SPI_STATUS, &sts_be, sizeof(sts_be)); if (rc) return rc; @@ -120,73 +125,91 @@ static int fsi_spi_check_status(struct fsi_spi *ctx) static int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value) { - int rc; + int rc = 0; __be32 cmd_be; __be32 data_be; u32 cmd = offset + ctx->base; + struct fsi2spi *bridge = ctx->bridge; *value = 0ULL; if (cmd & FSI2SPI_CMD_WRITE) return -EINVAL; - cmd_be = cpu_to_be32(cmd); - rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be)); + rc = mutex_lock_interruptible(&bridge->lock); if (rc) return rc; + cmd_be = cpu_to_be32(cmd); + rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be, + sizeof(cmd_be)); + if (rc) + goto unlock; + rc = fsi_spi_check_status(ctx); if (rc) - return rc; + goto unlock; - rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA0, &data_be, + rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA0, &data_be, sizeof(data_be)); if (rc) - return rc; + goto unlock; *value |= (u64)be32_to_cpu(data_be) << 32; - rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA1, &data_be, + rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA1, &data_be, sizeof(data_be)); if (rc) - return rc; + goto unlock; *value |= (u64)be32_to_cpu(data_be); dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value); - return 0; +unlock: + mutex_unlock(&bridge->lock); + return rc; } static int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value) { - int rc; + int rc = 0; __be32 cmd_be; __be32 data_be; u32 cmd = offset + ctx->base; + struct fsi2spi *bridge = ctx->bridge; if (cmd & FSI2SPI_CMD_WRITE) return -EINVAL; + rc = mutex_lock_interruptible(&bridge->lock); + if (rc) + return rc; + dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value); data_be = cpu_to_be32(upper_32_bits(value)); - rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA0, &data_be, + rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA0, &data_be, sizeof(data_be)); if (rc) - return rc; + goto unlock; data_be = cpu_to_be32(lower_32_bits(value)); - rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA1, &data_be, + rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA1, &data_be, sizeof(data_be)); if (rc) - return rc; + goto unlock; cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE); - rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be)); + rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be, + sizeof(cmd_be)); if (rc) - return rc; + goto unlock; - return fsi_spi_check_status(ctx); + rc = fsi_spi_check_status(ctx); + +unlock: + mutex_unlock(&bridge->lock); + return rc; } static int fsi_spi_data_in(u64 in, u8 *rx, int len) @@ -234,6 +257,26 @@ static int fsi_spi_reset(struct fsi_spi *ctx) return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL); } +static int fsi_spi_status(struct fsi_spi *ctx, u64 *status, const char *dir) +{ + int rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, status); + + if (rc) + return rc; + + if (*status & SPI_FSI_STATUS_ANY_ERROR) { + dev_err(ctx->dev, "%s error: %016llx\n", dir, *status); + + rc = fsi_spi_reset(ctx); + if (rc) + return rc; + + return -EREMOTEIO; + } + + return 0; +} + static void fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val) { /* @@ -273,18 +316,9 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, return rc; do { - rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, - &status); + rc = fsi_spi_status(ctx, &status, "TX"); if (rc) return rc; - - if (status & SPI_FSI_STATUS_ANY_ERROR) { - rc = fsi_spi_reset(ctx); - if (rc) - return rc; - - return -EREMOTEIO; - } } while (status & SPI_FSI_STATUS_TDR_FULL); sent += nb; @@ -296,18 +330,9 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, while (transfer->len > recv) { do { - rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, - &status); + rc = fsi_spi_status(ctx, &status, "RX"); if (rc) return rc; - - if (status & SPI_FSI_STATUS_ANY_ERROR) { - rc = fsi_spi_reset(ctx); - if (rc) - return rc; - - return -EREMOTEIO; - } } while (!(status & SPI_FSI_STATUS_RDR_FULL)); rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in); @@ -348,8 +373,12 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx) if (status & (SPI_FSI_STATUS_ANY_ERROR | SPI_FSI_STATUS_TDR_FULL | SPI_FSI_STATUS_RDR_FULL)) { - if (reset) + if (reset) { + dev_err(ctx->dev, + "Initialization error: %08llx\n", + status); return -EIO; + } rc = fsi_spi_reset(ctx); if (rc) @@ -388,7 +417,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, struct spi_transfer *transfer; struct fsi_spi *ctx = spi_controller_get_devdata(ctlr); - rc = fsi_spi_check_mux(ctx->fsi, ctx->dev); + rc = fsi_spi_check_mux(ctx->bridge->fsi, ctx->dev); if (rc) goto error; @@ -478,12 +507,20 @@ static int fsi_spi_probe(struct device *dev) int rc; struct device_node *np; int num_controllers_registered = 0; + struct fsi2spi *bridge; struct fsi_device *fsi = to_fsi_dev(dev); rc = fsi_spi_check_mux(fsi, dev); if (rc) return -ENODEV; + bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return -ENOMEM; + + bridge->fsi = fsi; + mutex_init(&bridge->lock); + for_each_available_child_of_node(dev->of_node, np) { u32 base; struct fsi_spi *ctx; @@ -506,7 +543,7 @@ static int fsi_spi_probe(struct device *dev) ctx = spi_controller_get_devdata(ctlr); ctx->dev = &ctlr->dev; - ctx->fsi = fsi; + ctx->bridge = bridge; ctx->base = base + SPI_FSI_BASE; rc = devm_spi_register_controller(dev, ctlr); diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 2f51421e2a71..27a446faf143 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -2,6 +2,9 @@ // Copyright (c) 2017-2018, The Linux foundation. All rights reserved. #include <linux/clk.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/dma/qcom-gpi-dma.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/log2.h> @@ -63,6 +66,15 @@ #define TIMESTAMP_AFTER BIT(3) #define POST_CMD_DELAY BIT(4) +#define GSI_LOOPBACK_EN BIT(0) +#define GSI_CS_TOGGLE BIT(3) +#define GSI_CPHA BIT(4) +#define GSI_CPOL BIT(5) + +#define MAX_TX_SG 3 +#define NUM_SPI_XFER 8 +#define SPI_XFER_TIMEOUT_MS 250 + struct spi_geni_master { struct geni_se se; struct device *dev; @@ -84,6 +96,9 @@ struct spi_geni_master { int irq; bool cs_flag; bool abort_failed; + struct dma_chan *tx; + struct dma_chan *rx; + int cur_xfer_mode; }; static int get_spi_clk_cfg(unsigned int speed_hz, @@ -330,34 +345,197 @@ static int setup_fifo_params(struct spi_device *spi_slv, return geni_spi_set_clock_and_bw(mas, spi_slv->max_speed_hz); } +static void +spi_gsi_callback_result(void *cb, const struct dmaengine_result *result) +{ + struct spi_master *spi = cb; + + if (result->result != DMA_TRANS_NOERROR) { + dev_err(&spi->dev, "DMA txn failed: %d\n", result->result); + return; + } + + if (!result->residue) { + dev_dbg(&spi->dev, "DMA txn completed\n"); + spi_finalize_current_transfer(spi); + } else { + dev_err(&spi->dev, "DMA xfer has pending: %d\n", result->residue); + } +} + +static int setup_gsi_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas, + struct spi_device *spi_slv, struct spi_master *spi) +{ + unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + struct dma_slave_config config = {}; + struct gpi_spi_config peripheral = {}; + struct dma_async_tx_descriptor *tx_desc, *rx_desc; + int ret; + + config.peripheral_config = &peripheral; + config.peripheral_size = sizeof(peripheral); + peripheral.set_config = true; + + if (xfer->bits_per_word != mas->cur_bits_per_word || + xfer->speed_hz != mas->cur_speed_hz) { + mas->cur_bits_per_word = xfer->bits_per_word; + mas->cur_speed_hz = xfer->speed_hz; + } + + if (xfer->tx_buf && xfer->rx_buf) { + peripheral.cmd = SPI_DUPLEX; + } else if (xfer->tx_buf) { + peripheral.cmd = SPI_TX; + peripheral.rx_len = 0; + } else if (xfer->rx_buf) { + peripheral.cmd = SPI_RX; + if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) { + peripheral.rx_len = ((xfer->len << 3) / mas->cur_bits_per_word); + } else { + int bytes_per_word = (mas->cur_bits_per_word / BITS_PER_BYTE) + 1; + + peripheral.rx_len = (xfer->len / bytes_per_word); + } + } + + peripheral.loopback_en = !!(spi_slv->mode & SPI_LOOP); + peripheral.clock_pol_high = !!(spi_slv->mode & SPI_CPOL); + peripheral.data_pol_high = !!(spi_slv->mode & SPI_CPHA); + peripheral.cs = spi_slv->chip_select; + peripheral.pack_en = true; + peripheral.word_len = xfer->bits_per_word - MIN_WORD_LEN; + + ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, + &peripheral.clk_src, &peripheral.clk_div); + if (ret) { + dev_err(mas->dev, "Err in get_spi_clk_cfg() :%d\n", ret); + return ret; + } + + if (!xfer->cs_change) { + if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers)) + peripheral.fragmentation = FRAGMENTATION; + } + + if (peripheral.cmd & SPI_RX) { + dmaengine_slave_config(mas->rx, &config); + rx_desc = dmaengine_prep_slave_sg(mas->rx, xfer->rx_sg.sgl, xfer->rx_sg.nents, + DMA_DEV_TO_MEM, flags); + if (!rx_desc) { + dev_err(mas->dev, "Err setting up rx desc\n"); + return -EIO; + } + } + + /* + * Prepare the TX always, even for RX or tx_buf being null, we would + * need TX to be prepared per GSI spec + */ + dmaengine_slave_config(mas->tx, &config); + tx_desc = dmaengine_prep_slave_sg(mas->tx, xfer->tx_sg.sgl, xfer->tx_sg.nents, + DMA_MEM_TO_DEV, flags); + if (!tx_desc) { + dev_err(mas->dev, "Err setting up tx desc\n"); + return -EIO; + } + + tx_desc->callback_result = spi_gsi_callback_result; + tx_desc->callback_param = spi; + + if (peripheral.cmd & SPI_RX) + dmaengine_submit(rx_desc); + dmaengine_submit(tx_desc); + + if (peripheral.cmd & SPI_RX) + dma_async_issue_pending(mas->rx); + + dma_async_issue_pending(mas->tx); + return 1; +} + +static bool geni_can_dma(struct spi_controller *ctlr, + struct spi_device *slv, struct spi_transfer *xfer) +{ + struct spi_geni_master *mas = spi_master_get_devdata(slv->master); + + /* check if dma is supported */ + return mas->cur_xfer_mode != GENI_SE_FIFO; +} + static int spi_geni_prepare_message(struct spi_master *spi, struct spi_message *spi_msg) { - int ret; struct spi_geni_master *mas = spi_master_get_devdata(spi); + int ret; - if (spi_geni_is_abort_still_pending(mas)) - return -EBUSY; + switch (mas->cur_xfer_mode) { + case GENI_SE_FIFO: + if (spi_geni_is_abort_still_pending(mas)) + return -EBUSY; + ret = setup_fifo_params(spi_msg->spi, spi); + if (ret) + dev_err(mas->dev, "Couldn't select mode %d\n", ret); + return ret; - ret = setup_fifo_params(spi_msg->spi, spi); - if (ret) - dev_err(mas->dev, "Couldn't select mode %d\n", ret); + case GENI_GPI_DMA: + /* nothing to do for GPI DMA */ + return 0; + } + + dev_err(mas->dev, "Mode not supported %d", mas->cur_xfer_mode); + return -EINVAL; +} + +static int spi_geni_grab_gpi_chan(struct spi_geni_master *mas) +{ + int ret; + + mas->tx = dma_request_chan(mas->dev, "tx"); + ret = dev_err_probe(mas->dev, IS_ERR(mas->tx), "Failed to get tx DMA ch\n"); + if (ret < 0) + goto err_tx; + + mas->rx = dma_request_chan(mas->dev, "rx"); + ret = dev_err_probe(mas->dev, IS_ERR(mas->rx), "Failed to get rx DMA ch\n"); + if (ret < 0) + goto err_rx; + + return 0; + +err_rx: + dma_release_channel(mas->tx); + mas->tx = NULL; +err_tx: + mas->rx = NULL; return ret; } +static void spi_geni_release_dma_chan(struct spi_geni_master *mas) +{ + if (mas->rx) { + dma_release_channel(mas->rx); + mas->rx = NULL; + } + + if (mas->tx) { + dma_release_channel(mas->tx); + mas->tx = NULL; + } +} + static int spi_geni_init(struct spi_geni_master *mas) { struct geni_se *se = &mas->se; unsigned int proto, major, minor, ver; - u32 spi_tx_cfg; + u32 spi_tx_cfg, fifo_disable; + int ret = -ENXIO; pm_runtime_get_sync(mas->dev); proto = geni_se_read_proto(se); if (proto != GENI_SE_SPI) { dev_err(mas->dev, "Invalid proto %d\n", proto); - pm_runtime_put(mas->dev); - return -ENXIO; + goto out_pm; } mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se); @@ -380,15 +558,38 @@ static int spi_geni_init(struct spi_geni_master *mas) else mas->oversampling = 1; - geni_se_select_mode(se, GENI_SE_FIFO); + fifo_disable = readl(se->base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE; + switch (fifo_disable) { + case 1: + ret = spi_geni_grab_gpi_chan(mas); + if (!ret) { /* success case */ + mas->cur_xfer_mode = GENI_GPI_DMA; + geni_se_select_mode(se, GENI_GPI_DMA); + dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n"); + break; + } + /* + * in case of failure to get dma channel, we can still do the + * FIFO mode, so fallthrough + */ + dev_warn(mas->dev, "FIFO mode disabled, but couldn't get DMA, fall back to FIFO mode\n"); + fallthrough; + + case 0: + mas->cur_xfer_mode = GENI_SE_FIFO; + geni_se_select_mode(se, GENI_SE_FIFO); + ret = 0; + break; + } /* We always control CS manually */ spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG); spi_tx_cfg &= ~CS_TOGGLE; writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG); +out_pm: pm_runtime_put(mas->dev); - return 0; + return ret; } static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas) @@ -569,8 +770,11 @@ static int spi_geni_transfer_one(struct spi_master *spi, if (!xfer->len) return 0; - setup_fifo_xfer(xfer, mas, slv->mode, spi); - return 1; + if (mas->cur_xfer_mode == GENI_SE_FIFO) { + setup_fifo_xfer(xfer, mas, slv->mode, spi); + return 1; + } + return setup_gsi_xfer(xfer, mas, slv, spi); } static irqreturn_t geni_spi_isr(int irq, void *data) @@ -665,6 +869,13 @@ static int spi_geni_probe(struct platform_device *pdev) if (irq < 0) return irq; + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "could not set DMA mask\n"); + } + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -704,9 +915,10 @@ static int spi_geni_probe(struct platform_device *pdev) spi->max_speed_hz = 50000000; spi->prepare_message = spi_geni_prepare_message; spi->transfer_one = spi_geni_transfer_one; + spi->can_dma = geni_can_dma; + spi->dma_map_dev = dev->parent; spi->auto_runtime_pm = true; spi->handle_err = handle_fifo_timeout; - spi->set_cs = spi_geni_set_cs; spi->use_gpio_descriptors = true; init_completion(&mas->cs_done); @@ -732,9 +944,17 @@ static int spi_geni_probe(struct platform_device *pdev) if (ret) goto spi_geni_probe_runtime_disable; + /* + * check the mode supported and set_cs for fifo mode only + * for dma (gsi) mode, the gsi will set cs based on params passed in + * TRE + */ + if (mas->cur_xfer_mode == GENI_SE_FIFO) + spi->set_cs = spi_geni_set_cs; + ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi); if (ret) - goto spi_geni_probe_runtime_disable; + goto spi_geni_release_dma; ret = spi_register_master(spi); if (ret) @@ -743,6 +963,8 @@ static int spi_geni_probe(struct platform_device *pdev) return 0; spi_geni_probe_free_irq: free_irq(mas->irq, spi); +spi_geni_release_dma: + spi_geni_release_dma_chan(mas); spi_geni_probe_runtime_disable: pm_runtime_disable(dev); return ret; @@ -756,6 +978,8 @@ static int spi_geni_remove(struct platform_device *pdev) /* Unregister _before_ disabling pm_runtime() so we stop transfers */ spi_unregister_master(spi); + spi_geni_release_dma_chan(mas); + free_irq(mas->irq, spi); pm_runtime_disable(&pdev->dev); return 0; diff --git a/drivers/spi/spi-ingenic.c b/drivers/spi/spi-ingenic.c new file mode 100644 index 000000000000..03077a7e11c8 --- /dev/null +++ b/drivers/spi/spi-ingenic.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SPI bus driver for the Ingenic JZ47xx SoCs + * Copyright (c) 2017-2021 Artur Rojek <contact@artur-rojek.eu> + * Copyright (c) 2017-2021 Paul Cercueil <paul@crapouillou.net> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#define REG_SSIDR 0x0 +#define REG_SSICR0 0x4 +#define REG_SSICR1 0x8 +#define REG_SSISR 0xc +#define REG_SSIGR 0x18 + +#define REG_SSICR0_TENDIAN_LSB BIT(19) +#define REG_SSICR0_RENDIAN_LSB BIT(17) +#define REG_SSICR0_SSIE BIT(15) +#define REG_SSICR0_LOOP BIT(10) +#define REG_SSICR0_EACLRUN BIT(7) +#define REG_SSICR0_FSEL BIT(6) +#define REG_SSICR0_TFLUSH BIT(2) +#define REG_SSICR0_RFLUSH BIT(1) + +#define REG_SSICR1_FRMHL_MASK (BIT(31) | BIT(30)) +#define REG_SSICR1_FRMHL BIT(30) +#define REG_SSICR1_LFST BIT(25) +#define REG_SSICR1_UNFIN BIT(23) +#define REG_SSICR1_PHA BIT(1) +#define REG_SSICR1_POL BIT(0) + +#define REG_SSISR_END BIT(7) +#define REG_SSISR_BUSY BIT(6) +#define REG_SSISR_TFF BIT(5) +#define REG_SSISR_RFE BIT(4) +#define REG_SSISR_RFHF BIT(2) +#define REG_SSISR_UNDR BIT(1) +#define REG_SSISR_OVER BIT(0) + +#define SPI_INGENIC_FIFO_SIZE 128u + +struct jz_soc_info { + u32 bits_per_word_mask; + struct reg_field flen_field; + bool has_trendian; +}; + +struct ingenic_spi { + const struct jz_soc_info *soc_info; + struct clk *clk; + struct resource *mem_res; + + struct regmap *map; + struct regmap_field *flen_field; +}; + +static int spi_ingenic_wait(struct ingenic_spi *priv, + unsigned long mask, + bool condition) +{ + unsigned int val; + + return regmap_read_poll_timeout(priv->map, REG_SSISR, val, + !!(val & mask) == condition, + 100, 10000); +} + +static void spi_ingenic_set_cs(struct spi_device *spi, bool disable) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(spi->controller); + + if (disable) { + regmap_clear_bits(priv->map, REG_SSICR1, REG_SSICR1_UNFIN); + regmap_clear_bits(priv->map, REG_SSISR, + REG_SSISR_UNDR | REG_SSISR_OVER); + + spi_ingenic_wait(priv, REG_SSISR_END, true); + } else { + regmap_set_bits(priv->map, REG_SSICR1, REG_SSICR1_UNFIN); + } + + regmap_set_bits(priv->map, REG_SSICR0, + REG_SSICR0_RFLUSH | REG_SSICR0_TFLUSH); +} + +static void spi_ingenic_prepare_transfer(struct ingenic_spi *priv, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + unsigned long clk_hz = clk_get_rate(priv->clk); + u32 cdiv, speed_hz = xfer->speed_hz ?: spi->max_speed_hz, + bits_per_word = xfer->bits_per_word ?: spi->bits_per_word; + + cdiv = clk_hz / (speed_hz * 2); + cdiv = clamp(cdiv, 1u, 0x100u) - 1; + + regmap_write(priv->map, REG_SSIGR, cdiv); + + regmap_field_write(priv->flen_field, bits_per_word - 2); +} + +static void spi_ingenic_finalize_transfer(void *controller) +{ + spi_finalize_current_transfer(controller); +} + +static struct dma_async_tx_descriptor * +spi_ingenic_prepare_dma(struct spi_controller *ctlr, struct dma_chan *chan, + struct sg_table *sg, enum dma_transfer_direction dir, + unsigned int bits) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + struct dma_slave_config cfg = { + .direction = dir, + .src_addr = priv->mem_res->start + REG_SSIDR, + .dst_addr = priv->mem_res->start + REG_SSIDR, + }; + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + + if (bits > 16) { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = cfg.dst_maxburst = 4; + } else if (bits > 8) { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.src_maxburst = cfg.dst_maxburst = 2; + } else { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + cfg.src_maxburst = cfg.dst_maxburst = 1; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) + return ERR_PTR(ret); + + desc = dmaengine_prep_slave_sg(chan, sg->sgl, sg->nents, dir, + DMA_PREP_INTERRUPT); + if (!desc) + return ERR_PTR(-ENOMEM); + + if (dir == DMA_DEV_TO_MEM) { + desc->callback = spi_ingenic_finalize_transfer; + desc->callback_param = ctlr; + } + + cookie = dmaengine_submit(desc); + + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_desc_free(desc); + return ERR_PTR(ret); + } + + return desc; +} + +static int spi_ingenic_dma_tx(struct spi_controller *ctlr, + struct spi_transfer *xfer, unsigned int bits) +{ + struct dma_async_tx_descriptor *rx_desc, *tx_desc; + + rx_desc = spi_ingenic_prepare_dma(ctlr, ctlr->dma_rx, + &xfer->rx_sg, DMA_DEV_TO_MEM, bits); + if (IS_ERR(rx_desc)) + return PTR_ERR(rx_desc); + + tx_desc = spi_ingenic_prepare_dma(ctlr, ctlr->dma_tx, + &xfer->tx_sg, DMA_MEM_TO_DEV, bits); + if (IS_ERR(tx_desc)) { + dmaengine_terminate_async(ctlr->dma_rx); + dmaengine_desc_free(rx_desc); + return PTR_ERR(tx_desc); + } + + dma_async_issue_pending(ctlr->dma_rx); + dma_async_issue_pending(ctlr->dma_tx); + + return 1; +} + +#define SPI_INGENIC_TX(x) \ +static int spi_ingenic_tx##x(struct ingenic_spi *priv, \ + struct spi_transfer *xfer) \ +{ \ + unsigned int count = xfer->len / (x / 8); \ + unsigned int prefill = min(count, SPI_INGENIC_FIFO_SIZE); \ + const u##x *tx_buf = xfer->tx_buf; \ + u##x *rx_buf = xfer->rx_buf; \ + unsigned int i, val; \ + int err; \ + \ + /* Fill up the TX fifo */ \ + for (i = 0; i < prefill; i++) { \ + val = tx_buf ? tx_buf[i] : 0; \ + \ + regmap_write(priv->map, REG_SSIDR, val); \ + } \ + \ + for (i = 0; i < count; i++) { \ + err = spi_ingenic_wait(priv, REG_SSISR_RFE, false); \ + if (err) \ + return err; \ + \ + regmap_read(priv->map, REG_SSIDR, &val); \ + if (rx_buf) \ + rx_buf[i] = val; \ + \ + if (i < count - prefill) { \ + val = tx_buf ? tx_buf[i + prefill] : 0; \ + \ + regmap_write(priv->map, REG_SSIDR, val); \ + } \ + } \ + \ + return 0; \ +} +SPI_INGENIC_TX(8) +SPI_INGENIC_TX(16) +SPI_INGENIC_TX(32) +#undef SPI_INGENIC_TX + +static int spi_ingenic_transfer_one(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + unsigned int bits = xfer->bits_per_word ?: spi->bits_per_word; + bool can_dma = ctlr->can_dma && ctlr->can_dma(ctlr, spi, xfer); + + spi_ingenic_prepare_transfer(priv, spi, xfer); + + if (ctlr->cur_msg_mapped && can_dma) + return spi_ingenic_dma_tx(ctlr, xfer, bits); + + if (bits > 16) + return spi_ingenic_tx32(priv, xfer); + + if (bits > 8) + return spi_ingenic_tx16(priv, xfer); + + return spi_ingenic_tx8(priv, xfer); +} + +static int spi_ingenic_prepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + struct spi_device *spi = message->spi; + unsigned int cs = REG_SSICR1_FRMHL << spi->chip_select; + unsigned int ssicr0_mask = REG_SSICR0_LOOP | REG_SSICR0_FSEL; + unsigned int ssicr1_mask = REG_SSICR1_PHA | REG_SSICR1_POL | cs; + unsigned int ssicr0 = 0, ssicr1 = 0; + + if (priv->soc_info->has_trendian) { + ssicr0_mask |= REG_SSICR0_RENDIAN_LSB | REG_SSICR0_TENDIAN_LSB; + + if (spi->mode & SPI_LSB_FIRST) + ssicr0 |= REG_SSICR0_RENDIAN_LSB | REG_SSICR0_TENDIAN_LSB; + } else { + ssicr1_mask |= REG_SSICR1_LFST; + + if (spi->mode & SPI_LSB_FIRST) + ssicr1 |= REG_SSICR1_LFST; + } + + if (spi->mode & SPI_LOOP) + ssicr0 |= REG_SSICR0_LOOP; + if (spi->chip_select) + ssicr0 |= REG_SSICR0_FSEL; + + if (spi->mode & SPI_CPHA) + ssicr1 |= REG_SSICR1_PHA; + if (spi->mode & SPI_CPOL) + ssicr1 |= REG_SSICR1_POL; + if (spi->mode & SPI_CS_HIGH) + ssicr1 |= cs; + + regmap_update_bits(priv->map, REG_SSICR0, ssicr0_mask, ssicr0); + regmap_update_bits(priv->map, REG_SSICR1, ssicr1_mask, ssicr1); + + return 0; +} + +static int spi_ingenic_prepare_hardware(struct spi_controller *ctlr) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + regmap_write(priv->map, REG_SSICR0, REG_SSICR0_EACLRUN); + regmap_write(priv->map, REG_SSICR1, 0); + regmap_write(priv->map, REG_SSISR, 0); + regmap_set_bits(priv->map, REG_SSICR0, REG_SSICR0_SSIE); + + return 0; +} + +static int spi_ingenic_unprepare_hardware(struct spi_controller *ctlr) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + + regmap_clear_bits(priv->map, REG_SSICR0, REG_SSICR0_SSIE); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static bool spi_ingenic_can_dma(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct dma_slave_caps caps; + int ret; + + ret = dma_get_slave_caps(ctlr->dma_tx, &caps); + if (ret) { + dev_err(&spi->dev, "Unable to get slave caps: %d\n", ret); + return false; + } + + return !caps.max_sg_burst || + xfer->len <= caps.max_sg_burst * SPI_INGENIC_FIFO_SIZE; +} + +static int spi_ingenic_request_dma(struct spi_controller *ctlr, + struct device *dev) +{ + ctlr->dma_tx = dma_request_slave_channel(dev, "tx"); + if (!ctlr->dma_tx) + return -ENODEV; + + ctlr->dma_rx = dma_request_slave_channel(dev, "rx"); + + if (!ctlr->dma_rx) + return -ENODEV; + + ctlr->can_dma = spi_ingenic_can_dma; + + return 0; +} + +static void spi_ingenic_release_dma(void *data) +{ + struct spi_controller *ctlr = data; + + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); +} + +static const struct regmap_config spi_ingenic_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_SSIGR, +}; + +static int spi_ingenic_probe(struct platform_device *pdev) +{ + const struct jz_soc_info *pdata; + struct device *dev = &pdev->dev; + struct spi_controller *ctlr; + struct ingenic_spi *priv; + void __iomem *base; + int ret; + + pdata = of_device_get_match_data(dev); + if (!pdata) { + dev_err(dev, "Missing platform data.\n"); + return -EINVAL; + } + + ctlr = devm_spi_alloc_master(dev, sizeof(*priv)); + if (!ctlr) { + dev_err(dev, "Unable to allocate SPI controller.\n"); + return -ENOMEM; + } + + priv = spi_controller_get_devdata(ctlr); + priv->soc_info = pdata; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Unable to get clock.\n"); + } + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->mem_res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->map = devm_regmap_init_mmio(dev, base, &spi_ingenic_regmap_config); + if (IS_ERR(priv->map)) + return PTR_ERR(priv->map); + + priv->flen_field = devm_regmap_field_alloc(dev, priv->map, + pdata->flen_field); + if (IS_ERR(priv->flen_field)) + return PTR_ERR(priv->flen_field); + + platform_set_drvdata(pdev, ctlr); + + ctlr->prepare_transfer_hardware = spi_ingenic_prepare_hardware; + ctlr->unprepare_transfer_hardware = spi_ingenic_unprepare_hardware; + ctlr->prepare_message = spi_ingenic_prepare_message; + ctlr->set_cs = spi_ingenic_set_cs; + ctlr->transfer_one = spi_ingenic_transfer_one; + ctlr->mode_bits = SPI_MODE_3 | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH; + ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + ctlr->max_dma_len = SPI_INGENIC_FIFO_SIZE; + ctlr->bits_per_word_mask = pdata->bits_per_word_mask; + ctlr->min_speed_hz = 7200; + ctlr->max_speed_hz = 54000000; + ctlr->num_chipselect = 2; + ctlr->dev.of_node = pdev->dev.of_node; + + if (spi_ingenic_request_dma(ctlr, dev)) + dev_warn(dev, "DMA not available.\n"); + + ret = devm_add_action_or_reset(dev, spi_ingenic_release_dma, ctlr); + if (ret) { + dev_err(dev, "Unable to add action.\n"); + return ret; + } + + ret = devm_spi_register_controller(dev, ctlr); + if (ret) + dev_err(dev, "Unable to register SPI controller.\n"); + + return ret; +} + +static const struct jz_soc_info jz4750_soc_info = { + .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 17), + .flen_field = REG_FIELD(REG_SSICR1, 4, 7), + .has_trendian = false, +}; + +static const struct jz_soc_info jz4780_soc_info = { + .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 32), + .flen_field = REG_FIELD(REG_SSICR1, 3, 7), + .has_trendian = true, +}; + +static const struct of_device_id spi_ingenic_of_match[] = { + { .compatible = "ingenic,jz4750-spi", .data = &jz4750_soc_info }, + { .compatible = "ingenic,jz4780-spi", .data = &jz4780_soc_info }, + {} +}; +MODULE_DEVICE_TABLE(of, spi_ingenic_of_match); + +static struct platform_driver spi_ingenic_driver = { + .driver = { + .name = "spi-ingenic", + .of_match_table = spi_ingenic_of_match, + }, + .probe = spi_ingenic_probe, +}; + +module_platform_driver(spi_ingenic_driver); +MODULE_DESCRIPTION("SPI bus driver for the Ingenic JZ47xx SoCs"); +MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 41e7b341d261..5c93730615f8 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -160,7 +160,7 @@ static bool mtk_nor_match_read(const struct spi_mem_op *op) { int dummy = 0; - if (op->dummy.buswidth) + if (op->dummy.nbytes) dummy = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth; if ((op->data.buswidth == 2) || (op->data.buswidth == 4)) { diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index e8de3cbbfb2a..565cd4c48d7b 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -769,6 +769,7 @@ static int orion_spi_probe(struct platform_device *pdev) dir_acc->vaddr = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); if (!dir_acc->vaddr) { status = -ENOMEM; + of_node_put(np); goto out_rel_axi_clk; } dir_acc->size = PAGE_SIZE; diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index c53138ce0030..83796a4ead34 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -139,7 +139,9 @@ static int rpcif_spi_probe(struct platform_device *pdev) return -ENOMEM; rpc = spi_controller_get_devdata(ctlr); - rpcif_sw_init(rpc, parent); + error = rpcif_sw_init(rpc, parent); + if (error) + return error; platform_set_drvdata(pdev, ctlr); diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index d16ed88802d3..41761f0d892a 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1427,4 +1427,3 @@ module_platform_driver(rspi_driver); MODULE_DESCRIPTION("Renesas RSPI bus driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Yoshihiro Shimoda"); -MODULE_ALIAS("platform:rspi"); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index f88d9acd20d9..d0012b30410c 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1426,4 +1426,3 @@ module_platform_driver(sh_msiof_spi_drv); MODULE_DESCRIPTION("SuperH MSIOF SPI Controller Interface Driver"); MODULE_AUTHOR("Magnus Damm"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:spi_sh_msiof"); diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 27f35aa2d746..514337c86d2c 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -397,7 +397,7 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op) ccr |= FIELD_PREP(CCR_ADSIZE_MASK, op->addr.nbytes - 1); } - if (op->dummy.buswidth && op->dummy.nbytes) + if (op->dummy.nbytes) ccr |= FIELD_PREP(CCR_DCYC_MASK, op->dummy.nbytes * 8 / op->dummy.buswidth); diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 3226c4e1c7c0..e8204e155484 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1124,7 +1124,7 @@ exit_free_irq: exit_pm_put: pm_runtime_put(&pdev->dev); exit_pm_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); tegra_slink_deinit_dma_param(tspi, false); exit_rx_dma_free: @@ -1143,7 +1143,7 @@ static int tegra_slink_remove(struct platform_device *pdev) free_irq(tspi->irq, tspi); - pm_runtime_disable(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); if (tspi->tx_dma_chan) tegra_slink_deinit_dma_param(tspi, false); diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 2354ca1e3858..c0f9a75b44b5 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -1318,7 +1318,7 @@ static int tegra_qspi_probe(struct platform_device *pdev) exit_free_irq: free_irq(qspi_irq, tqspi); exit_pm_disable: - pm_runtime_disable(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); tegra_qspi_deinit_dma(tqspi); return ret; } @@ -1330,7 +1330,7 @@ static int tegra_qspi_remove(struct platform_device *pdev) spi_unregister_master(master); free_irq(tqspi->irq, tqspi); - pm_runtime_disable(&pdev->dev); + pm_runtime_force_suspend(&pdev->dev); tegra_qspi_deinit_dma(tqspi); return 0; diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index 60dc69a39ace..f8ad0709d015 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -141,7 +141,7 @@ static ssize_t tle62x0_gpio_show(struct device *dev, value = (st->gpio_state >> gpio_num) & 1; mutex_unlock(&st->lock); - return snprintf(buf, PAGE_SIZE, "%d", value); + return sysfs_emit(buf, "%d", value); } static ssize_t tle62x0_gpio_store(struct device *dev, diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2a2f41b6df68..b23e675953e1 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -285,9 +285,9 @@ static const struct attribute_group *spi_master_groups[] = { NULL, }; -void spi_statistics_add_transfer_stats(struct spi_statistics *stats, - struct spi_transfer *xfer, - struct spi_controller *ctlr) +static void spi_statistics_add_transfer_stats(struct spi_statistics *stats, + struct spi_transfer *xfer, + struct spi_controller *ctlr) { unsigned long flags; int l2len = min(fls(xfer->len), SPI_STATISTICS_HISTO_SIZE) - 1; @@ -310,7 +310,6 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats, spin_unlock_irqrestore(&stats->lock, flags); } -EXPORT_SYMBOL_GPL(spi_statistics_add_transfer_stats); /* modalias support makes "modprobe $MODALIAS" new-style hotplug work, * and the sysfs version makes coldplug work too. @@ -536,7 +535,7 @@ static DEFINE_MUTEX(board_lock); * * Return: a pointer to the new device, or NULL. */ -struct spi_device *spi_alloc_device(struct spi_controller *ctlr) +static struct spi_device *spi_alloc_device(struct spi_controller *ctlr) { struct spi_device *spi; @@ -561,7 +560,6 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr) device_initialize(&spi->dev); return spi; } -EXPORT_SYMBOL_GPL(spi_alloc_device); static void spi_dev_set_name(struct spi_device *spi) { @@ -599,6 +597,11 @@ static int __spi_add_device(struct spi_device *spi) struct device *dev = ctlr->dev.parent; int status; + /* + * We need to make sure there's no other device with this + * chipselect **BEFORE** we call setup(), else we'll trash + * its configuration. + */ status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check); if (status) { dev_err(dev, "chipselect %d already in use\n", @@ -651,7 +654,7 @@ static int __spi_add_device(struct spi_device *spi) * * Return: 0 on success; negative errno on failure */ -int spi_add_device(struct spi_device *spi) +static int spi_add_device(struct spi_device *spi) { struct spi_controller *ctlr = spi->controller; struct device *dev = ctlr->dev.parent; @@ -667,16 +670,11 @@ int spi_add_device(struct spi_device *spi) /* Set the bus ID string */ spi_dev_set_name(spi); - /* We need to make sure there's no other device with this - * chipselect **BEFORE** we call setup(), else we'll trash - * its configuration. Lock against concurrent add() calls. - */ mutex_lock(&ctlr->add_lock); status = __spi_add_device(spi); mutex_unlock(&ctlr->add_lock); return status; } -EXPORT_SYMBOL_GPL(spi_add_device); static int spi_add_device_locked(struct spi_device *spi) { @@ -851,6 +849,87 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n) /*-------------------------------------------------------------------------*/ +/* Core methods for SPI resource management */ + +/** + * spi_res_alloc - allocate a spi resource that is life-cycle managed + * during the processing of a spi_message while using + * spi_transfer_one + * @spi: the spi device for which we allocate memory + * @release: the release code to execute for this resource + * @size: size to alloc and return + * @gfp: GFP allocation flags + * + * Return: the pointer to the allocated data + * + * This may get enhanced in the future to allocate from a memory pool + * of the @spi_device or @spi_controller to avoid repeated allocations. + */ +static void *spi_res_alloc(struct spi_device *spi, spi_res_release_t release, + size_t size, gfp_t gfp) +{ + struct spi_res *sres; + + sres = kzalloc(sizeof(*sres) + size, gfp); + if (!sres) + return NULL; + + INIT_LIST_HEAD(&sres->entry); + sres->release = release; + + return sres->data; +} + +/** + * spi_res_free - free an spi resource + * @res: pointer to the custom data of a resource + * + */ +static void spi_res_free(void *res) +{ + struct spi_res *sres = container_of(res, struct spi_res, data); + + if (!res) + return; + + WARN_ON(!list_empty(&sres->entry)); + kfree(sres); +} + +/** + * spi_res_add - add a spi_res to the spi_message + * @message: the spi message + * @res: the spi_resource + */ +static void spi_res_add(struct spi_message *message, void *res) +{ + struct spi_res *sres = container_of(res, struct spi_res, data); + + WARN_ON(!list_empty(&sres->entry)); + list_add_tail(&sres->entry, &message->resources); +} + +/** + * spi_res_release - release all spi resources for this message + * @ctlr: the @spi_controller + * @message: the @spi_message + */ +static void spi_res_release(struct spi_controller *ctlr, struct spi_message *message) +{ + struct spi_res *res, *tmp; + + list_for_each_entry_safe_reverse(res, tmp, &message->resources, entry) { + if (res->release) + res->release(ctlr, message, res->data); + + list_del(&res->entry); + + kfree(res); + } +} + +/*-------------------------------------------------------------------------*/ + static void spi_set_cs(struct spi_device *spi, bool enable, bool force) { bool activate = enable; @@ -3068,127 +3147,6 @@ int spi_controller_resume(struct spi_controller *ctlr) } EXPORT_SYMBOL_GPL(spi_controller_resume); -static int __spi_controller_match(struct device *dev, const void *data) -{ - struct spi_controller *ctlr; - const u16 *bus_num = data; - - ctlr = container_of(dev, struct spi_controller, dev); - return ctlr->bus_num == *bus_num; -} - -/** - * spi_busnum_to_master - look up master associated with bus_num - * @bus_num: the master's bus number - * Context: can sleep - * - * This call may be used with devices that are registered after - * arch init time. It returns a refcounted pointer to the relevant - * spi_controller (which the caller must release), or NULL if there is - * no such master registered. - * - * Return: the SPI master structure on success, else NULL. - */ -struct spi_controller *spi_busnum_to_master(u16 bus_num) -{ - struct device *dev; - struct spi_controller *ctlr = NULL; - - dev = class_find_device(&spi_master_class, NULL, &bus_num, - __spi_controller_match); - if (dev) - ctlr = container_of(dev, struct spi_controller, dev); - /* reference got in class_find_device */ - return ctlr; -} -EXPORT_SYMBOL_GPL(spi_busnum_to_master); - -/*-------------------------------------------------------------------------*/ - -/* Core methods for SPI resource management */ - -/** - * spi_res_alloc - allocate a spi resource that is life-cycle managed - * during the processing of a spi_message while using - * spi_transfer_one - * @spi: the spi device for which we allocate memory - * @release: the release code to execute for this resource - * @size: size to alloc and return - * @gfp: GFP allocation flags - * - * Return: the pointer to the allocated data - * - * This may get enhanced in the future to allocate from a memory pool - * of the @spi_device or @spi_controller to avoid repeated allocations. - */ -void *spi_res_alloc(struct spi_device *spi, - spi_res_release_t release, - size_t size, gfp_t gfp) -{ - struct spi_res *sres; - - sres = kzalloc(sizeof(*sres) + size, gfp); - if (!sres) - return NULL; - - INIT_LIST_HEAD(&sres->entry); - sres->release = release; - - return sres->data; -} -EXPORT_SYMBOL_GPL(spi_res_alloc); - -/** - * spi_res_free - free an spi resource - * @res: pointer to the custom data of a resource - * - */ -void spi_res_free(void *res) -{ - struct spi_res *sres = container_of(res, struct spi_res, data); - - if (!res) - return; - - WARN_ON(!list_empty(&sres->entry)); - kfree(sres); -} -EXPORT_SYMBOL_GPL(spi_res_free); - -/** - * spi_res_add - add a spi_res to the spi_message - * @message: the spi message - * @res: the spi_resource - */ -void spi_res_add(struct spi_message *message, void *res) -{ - struct spi_res *sres = container_of(res, struct spi_res, data); - - WARN_ON(!list_empty(&sres->entry)); - list_add_tail(&sres->entry, &message->resources); -} -EXPORT_SYMBOL_GPL(spi_res_add); - -/** - * spi_res_release - release all spi resources for this message - * @ctlr: the @spi_controller - * @message: the @spi_message - */ -void spi_res_release(struct spi_controller *ctlr, struct spi_message *message) -{ - struct spi_res *res, *tmp; - - list_for_each_entry_safe_reverse(res, tmp, &message->resources, entry) { - if (res->release) - res->release(ctlr, message, res->data); - - list_del(&res->entry); - - kfree(res); - } -} -EXPORT_SYMBOL_GPL(spi_res_release); - /*-------------------------------------------------------------------------*/ /* Core methods for spi_message alterations */ @@ -3227,7 +3185,7 @@ static void __spi_replace_transfers_release(struct spi_controller *ctlr, * Returns: pointer to @spi_replaced_transfers, * PTR_ERR(...) in case of errors. */ -struct spi_replaced_transfers *spi_replace_transfers( +static struct spi_replaced_transfers *spi_replace_transfers( struct spi_message *msg, struct spi_transfer *xfer_first, size_t remove, @@ -3319,7 +3277,6 @@ struct spi_replaced_transfers *spi_replace_transfers( return rxfer; } -EXPORT_SYMBOL_GPL(spi_replace_transfers); static int __spi_split_transfer_maxsize(struct spi_controller *ctlr, struct spi_message *msg, @@ -3869,7 +3826,7 @@ EXPORT_SYMBOL_GPL(spi_async); * * Return: zero on success, else a negative error code. */ -int spi_async_locked(struct spi_device *spi, struct spi_message *message) +static int spi_async_locked(struct spi_device *spi, struct spi_message *message) { struct spi_controller *ctlr = spi->controller; int ret; @@ -3888,7 +3845,6 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message) return ret; } -EXPORT_SYMBOL_GPL(spi_async_locked); /*-------------------------------------------------------------------------*/ @@ -4146,18 +4102,15 @@ EXPORT_SYMBOL_GPL(spi_write_then_read); /*-------------------------------------------------------------------------*/ -#if IS_ENABLED(CONFIG_OF) +#if IS_ENABLED(CONFIG_OF_DYNAMIC) /* must call put_device() when done with returned spi_device device */ -struct spi_device *of_find_spi_device_by_node(struct device_node *node) +static struct spi_device *of_find_spi_device_by_node(struct device_node *node) { struct device *dev = bus_find_device_by_of_node(&spi_bus_type, node); return dev ? to_spi_device(dev) : NULL; } -EXPORT_SYMBOL_GPL(of_find_spi_device_by_node); -#endif /* IS_ENABLED(CONFIG_OF) */ -#if IS_ENABLED(CONFIG_OF_DYNAMIC) /* the spi controllers are not using spi_bus, so we find it with another way */ static struct spi_controller *of_find_spi_controller_by_node(struct device_node *node) { |