From 6db8595a3e1996654a130760d363912cdd28706d Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Fri, 22 Jul 2022 14:41:36 +0300 Subject: spi: npcm-pspi: Add NPCM845 peripheral SPI support Add Nuvoton BMC NPCM845 NPCM Peripheral SPI (PSPI) support. Signed-off-by: Tomer Maimon Link: https://lore.kernel.org/r/20220722114136.251415-3-tmaimon77@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-pspi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index 1668a347e003..7f2e4d1b0d43 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -443,6 +443,7 @@ static int npcm_pspi_remove(struct platform_device *pdev) static const struct of_device_id npcm_pspi_match[] = { { .compatible = "nuvoton,npcm750-pspi", .data = NULL }, + { .compatible = "nuvoton,npcm845-pspi", .data = NULL }, {} }; MODULE_DEVICE_TABLE(of, npcm_pspi_match); -- cgit v1.2.3 From 8596124c4c1bc7561454cee0463c16eca70b5d25 Mon Sep 17 00:00:00 2001 From: Naga Sureshkumar Relli Date: Mon, 8 Aug 2022 12:16:02 +0530 Subject: spi: microchip-core-qspi: Add support for microchip fpga qspi controllers Add a driver for Microchip FPGA QSPI controllers. This driver also supports "hard" QSPI controllers on Polarfire SoC. Signed-off-by: Naga Sureshkumar Relli Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20220808064603.1174906-4-nagasuresh.relli@microchip.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 9 + drivers/spi/Makefile | 1 + drivers/spi/spi-microchip-core-qspi.c | 600 ++++++++++++++++++++++++++++++++++ 3 files changed, 610 insertions(+) create mode 100644 drivers/spi/spi-microchip-core-qspi.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e32f6a2058ae..d1bb62f7368b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -591,6 +591,15 @@ config SPI_MICROCHIP_CORE PolarFire SoC. If built as a module, it will be called spi-microchip-core. +config SPI_MICROCHIP_CORE_QSPI + tristate "Microchip FPGA QSPI controllers" + depends on SPI_MASTER + help + This enables the QSPI driver for Microchip FPGA QSPI controllers. + Say Y or M here if you want to use the QSPI controllers on + PolarFire SoC. + If built as a module, it will be called spi-microchip-core-qspi. + config SPI_MT65XX tristate "MediaTek SPI controller" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 15d2f3835e45..4b34e855c841 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o obj-$(CONFIG_SPI_MICROCHIP_CORE) += spi-microchip-core.o +obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c new file mode 100644 index 000000000000..19a6a46829f6 --- /dev/null +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: (GPL-2.0) +/* + * Microchip coreQSPI QSPI controller driver + * + * Copyright (C) 2018-2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Naga Sureshkumar Relli + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * QSPI Control register mask defines + */ +#define CONTROL_ENABLE BIT(0) +#define CONTROL_MASTER BIT(1) +#define CONTROL_XIP BIT(2) +#define CONTROL_XIPADDR BIT(3) +#define CONTROL_CLKIDLE BIT(10) +#define CONTROL_SAMPLE_MASK GENMASK(12, 11) +#define CONTROL_MODE0 BIT(13) +#define CONTROL_MODE12_MASK GENMASK(15, 14) +#define CONTROL_MODE12_EX_RO BIT(14) +#define CONTROL_MODE12_EX_RW BIT(15) +#define CONTROL_MODE12_FULL GENMASK(15, 14) +#define CONTROL_FLAGSX4 BIT(16) +#define CONTROL_CLKRATE_MASK GENMASK(27, 24) +#define CONTROL_CLKRATE_SHIFT 24 + +/* + * QSPI Frames register mask defines + */ +#define FRAMES_TOTALBYTES_MASK GENMASK(15, 0) +#define FRAMES_CMDBYTES_MASK GENMASK(24, 16) +#define FRAMES_CMDBYTES_SHIFT 16 +#define FRAMES_SHIFT 25 +#define FRAMES_IDLE_MASK GENMASK(29, 26) +#define FRAMES_IDLE_SHIFT 26 +#define FRAMES_FLAGBYTE BIT(30) +#define FRAMES_FLAGWORD BIT(31) + +/* + * QSPI Interrupt Enable register mask defines + */ +#define IEN_TXDONE BIT(0) +#define IEN_RXDONE BIT(1) +#define IEN_RXAVAILABLE BIT(2) +#define IEN_TXAVAILABLE BIT(3) +#define IEN_RXFIFOEMPTY BIT(4) +#define IEN_TXFIFOFULL BIT(5) + +/* + * QSPI Status register mask defines + */ +#define STATUS_TXDONE BIT(0) +#define STATUS_RXDONE BIT(1) +#define STATUS_RXAVAILABLE BIT(2) +#define STATUS_TXAVAILABLE BIT(3) +#define STATUS_RXFIFOEMPTY BIT(4) +#define STATUS_TXFIFOFULL BIT(5) +#define STATUS_READY BIT(7) +#define STATUS_FLAGSX4 BIT(8) +#define STATUS_MASK GENMASK(8, 0) + +#define BYTESUPPER_MASK GENMASK(31, 16) +#define BYTESLOWER_MASK GENMASK(15, 0) + +#define MAX_DIVIDER 16 +#define MIN_DIVIDER 0 +#define MAX_DATA_CMD_LEN 256 + +/* QSPI ready time out value */ +#define TIMEOUT_MS 500 + +/* + * QSPI Register offsets. + */ +#define REG_CONTROL (0x00) +#define REG_FRAMES (0x04) +#define REG_IEN (0x0c) +#define REG_STATUS (0x10) +#define REG_DIRECT_ACCESS (0x14) +#define REG_UPPER_ACCESS (0x18) +#define REG_RX_DATA (0x40) +#define REG_TX_DATA (0x44) +#define REG_X4_RX_DATA (0x48) +#define REG_X4_TX_DATA (0x4c) +#define REG_FRAMESUP (0x50) + +/** + * struct mchp_coreqspi - Defines qspi driver instance + * @regs: Virtual address of the QSPI controller registers + * @clk: QSPI Operating clock + * @data_completion: completion structure + * @op_lock: lock access to the device + * @txbuf: TX buffer + * @rxbuf: RX buffer + * @irq: IRQ number + * @tx_len: Number of bytes left to transfer + * @rx_len: Number of bytes left to receive + */ +struct mchp_coreqspi { + void __iomem *regs; + struct clk *clk; + struct completion data_completion; + struct mutex op_lock; /* lock access to the device */ + u8 *txbuf; + u8 *rxbuf; + int irq; + int tx_len; + int rx_len; +}; + +static int mchp_coreqspi_set_mode(struct mchp_coreqspi *qspi, const struct spi_mem_op *op) +{ + u32 control = readl_relaxed(qspi->regs + REG_CONTROL); + + /* + * The operating mode can be configured based on the command that needs to be send. + * bits[15:14]: Sets whether multiple bit SPI operates in normal, extended or full modes. + * 00: Normal (single DQ0 TX and single DQ1 RX lines) + * 01: Extended RO (command and address bytes on DQ0 only) + * 10: Extended RW (command byte on DQ0 only) + * 11: Full. (command and address are on all DQ lines) + * bit[13]: Sets whether multiple bit SPI uses 2 or 4 bits of data + * 0: 2-bits (BSPI) + * 1: 4-bits (QSPI) + */ + if (op->data.buswidth == 4 || op->data.buswidth == 2) { + control &= ~CONTROL_MODE12_MASK; + if (op->cmd.buswidth == 1 && (op->addr.buswidth == 1 || op->addr.buswidth == 0)) + control |= CONTROL_MODE12_EX_RO; + else if (op->cmd.buswidth == 1) + control |= CONTROL_MODE12_EX_RW; + else + control |= CONTROL_MODE12_FULL; + + control |= CONTROL_MODE0; + } else { + control &= ~(CONTROL_MODE12_MASK | + CONTROL_MODE0); + } + + writel_relaxed(control, qspi->regs + REG_CONTROL); + + return 0; +} + +static inline void mchp_coreqspi_read_op(struct mchp_coreqspi *qspi) +{ + u32 control, data; + + if (!qspi->rx_len) + return; + + control = readl_relaxed(qspi->regs + REG_CONTROL); + + /* + * Read 4-bytes from the SPI FIFO in single transaction and then read + * the reamaining data byte wise. + */ + control |= CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->rx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); + *(u32 *)qspi->rxbuf = data; + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } + + control &= ~CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->rx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_RX_DATA); + *qspi->rxbuf++ = (data & 0xFF); + } +} + +static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word) +{ + u32 control, data; + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control |= CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + data = *(u32 *)qspi->txbuf; + qspi->txbuf += 4; + qspi->tx_len -= 4; + writel_relaxed(data, qspi->regs + REG_X4_TX_DATA); + } + + control &= ~CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + data = *qspi->txbuf++; + writel_relaxed(data, qspi->regs + REG_TX_DATA); + } +} + +static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi) +{ + u32 mask = IEN_TXDONE | + IEN_RXDONE | + IEN_RXAVAILABLE; + + writel_relaxed(mask, qspi->regs + REG_IEN); +} + +static void mchp_coreqspi_disable_ints(struct mchp_coreqspi *qspi) +{ + writel_relaxed(0, qspi->regs + REG_IEN); +} + +static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id) +{ + struct mchp_coreqspi *qspi = (struct mchp_coreqspi *)dev_id; + irqreturn_t ret = IRQ_NONE; + int intfield = readl_relaxed(qspi->regs + REG_STATUS) & STATUS_MASK; + + if (intfield == 0) + return ret; + + if (intfield & IEN_TXDONE) { + writel_relaxed(IEN_TXDONE, qspi->regs + REG_STATUS); + ret = IRQ_HANDLED; + } + + if (intfield & IEN_RXAVAILABLE) { + writel_relaxed(IEN_RXAVAILABLE, qspi->regs + REG_STATUS); + mchp_coreqspi_read_op(qspi); + ret = IRQ_HANDLED; + } + + if (intfield & IEN_RXDONE) { + writel_relaxed(IEN_RXDONE, qspi->regs + REG_STATUS); + complete(&qspi->data_completion); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi) +{ + unsigned long clk_hz; + u32 control, baud_rate_val = 0; + + clk_hz = clk_get_rate(qspi->clk); + if (!clk_hz) + return -EINVAL; + + baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * spi->max_speed_hz); + if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) { + dev_err(&spi->dev, + "could not configure the clock for spi clock %d Hz & system clock %ld Hz\n", + spi->max_speed_hz, clk_hz); + return -EINVAL; + } + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control |= baud_rate_val << CONTROL_CLKRATE_SHIFT; + writel_relaxed(control, qspi->regs + REG_CONTROL); + control = readl_relaxed(qspi->regs + REG_CONTROL); + + if ((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) + control |= CONTROL_CLKIDLE; + else + control &= ~CONTROL_CLKIDLE; + + writel_relaxed(control, qspi->regs + REG_CONTROL); + + return 0; +} + +static int mchp_coreqspi_setup_op(struct spi_device *spi_dev) +{ + struct spi_controller *ctlr = spi_dev->master; + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + u32 control = readl_relaxed(qspi->regs + REG_CONTROL); + + control |= (CONTROL_MASTER | CONTROL_ENABLE); + control &= ~CONTROL_CLKIDLE; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + return 0; +} + +static inline void mchp_coreqspi_config_op(struct mchp_coreqspi *qspi, const struct spi_mem_op *op) +{ + u32 idle_cycles = 0; + int total_bytes, cmd_bytes, frames, ctrl; + + cmd_bytes = op->cmd.nbytes + op->addr.nbytes; + total_bytes = cmd_bytes + op->data.nbytes; + + /* + * As per the coreQSPI IP spec,the number of command and data bytes are + * controlled by the frames register for each SPI sequence. This supports + * the SPI flash memory read and writes sequences as below. so configure + * the cmd and total bytes accordingly. + * --------------------------------------------------------------------- + * TOTAL BYTES | CMD BYTES | What happens | + * ______________________________________________________________________ + * | | | + * 1 | 1 | The SPI core will transmit a single byte | + * | | and receive data is discarded | + * | | | + * 1 | 0 | The SPI core will transmit a single byte | + * | | and return a single byte | + * | | | + * 10 | 4 | The SPI core will transmit 4 command | + * | | bytes discarding the receive data and | + * | | transmits 6 dummy bytes returning the 6 | + * | | received bytes and return a single byte | + * | | | + * 10 | 10 | The SPI core will transmit 10 command | + * | | | + * 10 | 0 | The SPI core will transmit 10 command | + * | | bytes and returning 10 received bytes | + * ______________________________________________________________________ + */ + if (!(op->data.dir == SPI_MEM_DATA_IN)) + cmd_bytes = total_bytes; + + frames = total_bytes & BYTESUPPER_MASK; + writel_relaxed(frames, qspi->regs + REG_FRAMESUP); + frames = total_bytes & BYTESLOWER_MASK; + frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT; + + if (op->dummy.buswidth) + idle_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth; + + frames |= idle_cycles << FRAMES_IDLE_SHIFT; + ctrl = readl_relaxed(qspi->regs + REG_CONTROL); + + if (ctrl & CONTROL_MODE12_MASK) + frames |= (1 << FRAMES_SHIFT); + + frames |= FRAMES_FLAGWORD; + writel_relaxed(frames, qspi->regs + REG_FRAMES); +} + +static int mchp_qspi_wait_for_ready(struct spi_mem *mem) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata + (mem->spi->master); + u32 status; + int ret; + + ret = readl_poll_timeout(qspi->regs + REG_STATUS, status, + (status & STATUS_READY), 0, + TIMEOUT_MS); + if (ret) { + dev_err(&mem->spi->dev, + "Timeout waiting on QSPI ready.\n"); + return -ETIMEDOUT; + } + + return ret; +} + +static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata + (mem->spi->master); + u32 address = op->addr.val; + u8 opcode = op->cmd.opcode; + u8 opaddr[5]; + int err, i; + + mutex_lock(&qspi->op_lock); + err = mchp_qspi_wait_for_ready(mem); + if (err) + goto error; + + err = mchp_coreqspi_setup_clock(qspi, mem->spi); + if (err) + goto error; + + err = mchp_coreqspi_set_mode(qspi, op); + if (err) + goto error; + + reinit_completion(&qspi->data_completion); + mchp_coreqspi_config_op(qspi, op); + if (op->cmd.opcode) { + qspi->txbuf = &opcode; + qspi->rxbuf = NULL; + qspi->tx_len = op->cmd.nbytes; + qspi->rx_len = 0; + mchp_coreqspi_write_op(qspi, false); + } + + qspi->txbuf = &opaddr[0]; + if (op->addr.nbytes) { + for (i = 0; i < op->addr.nbytes; i++) + qspi->txbuf[i] = address >> (8 * (op->addr.nbytes - i - 1)); + + qspi->rxbuf = NULL; + qspi->tx_len = op->addr.nbytes; + qspi->rx_len = 0; + mchp_coreqspi_write_op(qspi, false); + } + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_OUT) { + qspi->txbuf = (u8 *)op->data.buf.out; + qspi->rxbuf = NULL; + qspi->rx_len = 0; + qspi->tx_len = op->data.nbytes; + mchp_coreqspi_write_op(qspi, true); + } else { + qspi->txbuf = NULL; + qspi->rxbuf = (u8 *)op->data.buf.in; + qspi->rx_len = op->data.nbytes; + qspi->tx_len = 0; + } + } + + mchp_coreqspi_enable_ints(qspi); + + if (!wait_for_completion_timeout(&qspi->data_completion, msecs_to_jiffies(1000))) + err = -ETIMEDOUT; + +error: + mutex_unlock(&qspi->op_lock); + mchp_coreqspi_disable_ints(qspi); + + return err; +} + +static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + if (!spi_mem_default_supports_op(mem, op)) + return false; + + if ((op->data.buswidth == 4 || op->data.buswidth == 2) && + (op->cmd.buswidth == 1 && (op->addr.buswidth == 1 || op->addr.buswidth == 0))) { + /* + * If the command and address are on DQ0 only, then this + * controller doesn't support sending data on dual and + * quad lines. but it supports reading data on dual and + * quad lines with same configuration as command and + * address on DQ0. + * i.e. The control register[15:13] :EX_RO(read only) is + * meant only for the command and address are on DQ0 but + * not to write data, it is just to read. + * Ex: 0x34h is Quad Load Program Data which is not + * supported. Then the spi-mem layer will iterate over + * each command and it will chose the supported one. + */ + if (op->data.dir == SPI_MEM_DATA_OUT) + return false; + } + + return true; +} + +static int mchp_coreqspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + if (op->data.dir == SPI_MEM_DATA_OUT || op->data.dir == SPI_MEM_DATA_IN) { + if (op->data.nbytes > MAX_DATA_CMD_LEN) + op->data.nbytes = MAX_DATA_CMD_LEN; + } + + return 0; +} + +static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = { + .adjust_op_size = mchp_coreqspi_adjust_op_size, + .supports_op = mchp_coreqspi_supports_op, + .exec_op = mchp_coreqspi_exec_op, +}; + +static int mchp_coreqspi_probe(struct platform_device *pdev) +{ + struct spi_controller *ctlr; + struct mchp_coreqspi *qspi; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret; + + ctlr = devm_spi_alloc_master(&pdev->dev, sizeof(*qspi)); + if (!ctlr) + return dev_err_probe(&pdev->dev, -ENOMEM, + "unable to allocate master for QSPI controller\n"); + + qspi = spi_controller_get_devdata(ctlr); + platform_set_drvdata(pdev, qspi); + + qspi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(qspi->regs)) + return dev_err_probe(&pdev->dev, PTR_ERR(qspi->regs), + "failed to map registers\n"); + + qspi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(qspi->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(qspi->clk), + "could not get clock\n"); + + ret = clk_prepare_enable(qspi->clk); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to enable clock\n"); + + init_completion(&qspi->data_completion); + mutex_init(&qspi->op_lock); + + qspi->irq = platform_get_irq(pdev, 0); + if (qspi->irq < 0) { + ret = qspi->irq; + goto out; + } + + ret = devm_request_irq(&pdev->dev, qspi->irq, mchp_coreqspi_isr, + IRQF_SHARED, pdev->name, qspi); + if (ret) { + dev_err(&pdev->dev, "request_irq failed %d\n", ret); + goto out; + } + + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->mem_ops = &mchp_coreqspi_mem_ops; + ctlr->setup = mchp_coreqspi_setup_op; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | + SPI_TX_DUAL | SPI_TX_QUAD; + ctlr->dev.of_node = np; + + ret = devm_spi_register_controller(&pdev->dev, ctlr); + if (ret) { + dev_err_probe(&pdev->dev, ret, + "spi_register_controller failed\n"); + goto out; + } + + return 0; + +out: + clk_disable_unprepare(qspi->clk); + + return ret; +} + +static int mchp_coreqspi_remove(struct platform_device *pdev) +{ + struct mchp_coreqspi *qspi = platform_get_drvdata(pdev); + u32 control = readl_relaxed(qspi->regs + REG_CONTROL); + + mchp_coreqspi_disable_ints(qspi); + control &= ~CONTROL_ENABLE; + writel_relaxed(control, qspi->regs + REG_CONTROL); + clk_disable_unprepare(qspi->clk); + + return 0; +} + +static const struct of_device_id mchp_coreqspi_of_match[] = { + { .compatible = "microchip,coreqspi-rtl-v2" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mchp_coreqspi_of_match); + +static struct platform_driver mchp_coreqspi_driver = { + .probe = mchp_coreqspi_probe, + .driver = { + .name = "microchip,coreqspi", + .of_match_table = mchp_coreqspi_of_match, + }, + .remove = mchp_coreqspi_remove, +}; +module_platform_driver(mchp_coreqspi_driver); + +MODULE_AUTHOR("Naga Sureshkumar Relli >) >> drivers/spi/spi-s3c64xx.c:387:34: sparse: sparse: Using plain integer as NULL pointer drivers/spi/spi-s3c64xx.c:388:34: sparse: sparse: Using plain integer as NULL pointer Reported-by: kernel test robot Fixes: 82295bc0d192 ("spi: s3c64xx: move dma_release_channel to unprepare") Fixes: f52b03c70744 ("spi: s3c64xx: requests spi-dma channel only during data transfer") Signed-off-by: Chanho Park Reviewed-by: Andi Shyti Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220808004851.25122-1-chanho61.park@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 7f346866614a..651c35dd9124 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -389,8 +389,8 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) if (sdd->rx_dma.ch && sdd->tx_dma.ch) { dma_release_channel(sdd->rx_dma.ch); dma_release_channel(sdd->tx_dma.ch); - sdd->rx_dma.ch = 0; - sdd->tx_dma.ch = 0; + sdd->rx_dma.ch = NULL; + sdd->tx_dma.ch = NULL; } return 0; -- cgit v1.2.3 From 7964e817d2311b37b86d6fbf588cd9fbe747b108 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 5 Aug 2022 23:33:17 +0200 Subject: spi: microchip-core: Simplify some error message dev_err_probe() already prints the error code in a human readable way, so there is no need to duplicate it as a numerical value at the end of the message. Moreover, in the case of devm_clk_get() it would only display '0' because 'ret' is know to be 0 at this point. Fixes: cdeaf3a99a02 ("spi: microchip-core: switch to use dev_err_probe()") Signed-off-by: Christophe JAILLET Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/fb894ecec68e03fb7fc9353027c8b1a2610833d7.1659735153.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-microchip-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-microchip-core.c b/drivers/spi/spi-microchip-core.c index ce4385330b19..d352844c798c 100644 --- a/drivers/spi/spi-microchip-core.c +++ b/drivers/spi/spi-microchip-core.c @@ -548,12 +548,12 @@ static int mchp_corespi_probe(struct platform_device *pdev) IRQF_SHARED, dev_name(&pdev->dev), master); if (ret) return dev_err_probe(&pdev->dev, ret, - "could not request irq: %d\n", ret); + "could not request irq\n"); spi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(spi->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk), - "could not get clk: %d\n", ret); + "could not get clk\n"); ret = clk_prepare_enable(spi->clk); if (ret) -- cgit v1.2.3 From 0df874c6712d9aa8f43c50ec887a21f7b86fc917 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 5 Aug 2022 23:39:19 +0200 Subject: spi: lpspi: Simplify some error message dev_err_probe() already prints the error code in a human readable way, so there is no need to duplicate it as a numerical value at the end of the message. Fixes: 12f62a857c83 ("spi: lpspi: Silence error message upon deferred probe") Signed-off-by: Christophe JAILLET Acked-By: Alexander Stein Link: https://lore.kernel.org/r/253543c462b765eca40ba54c66f4e3fdf4acdeb7.1659735546.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 19b1f3d881b0..cbbe8bbef90a 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -912,7 +912,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = devm_spi_register_controller(&pdev->dev, controller); if (ret < 0) { - dev_err_probe(&pdev->dev, ret, "spi_register_controller error: %i\n", ret); + dev_err_probe(&pdev->dev, ret, "spi_register_controller error\n"); goto free_dma; } -- cgit v1.2.3 From 9d08f700ab78fd96cbe5922c261051743cb9c86e Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Thu, 18 Aug 2022 04:00:59 +0300 Subject: spi: amd: Setup all xfers before opcode execution The AMD SPI controller hardware seems to expect the FIFO buffer to be fully setup with the details of all transfers in the SPI message before it is able to start processing the data in a reliable way. Furthermore, it imposes a strict ordering restriction, in the sense that all TX transfers must be handled prior any RX transfer. Hence, let's ensure amd_spi_execute_opcode() is called only once, after all TX transfers have been setup, and process any remaining RX transfers afterwards, in a second iteration. Additionally, get rid of the unnecessary AMD_SPI_XFER_TX/RX defines and improve error handling. Signed-off-by: Cristian Ciocaltea Link: https://lore.kernel.org/r/20220818010059.403776-1-cristian.ciocaltea@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 93 ++++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 46 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 08df4f8d0531..b3b3f5167c4c 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -36,10 +36,6 @@ #define AMD_SPI_FIFO_SIZE 70 #define AMD_SPI_MEM_SIZE 200 -/* M_CMD OP codes for SPI */ -#define AMD_SPI_XFER_TX 1 -#define AMD_SPI_XFER_RX 2 - /** * enum amd_spi_versions - SPI controller versions * @AMD_SPI_V1: AMDI0061 hardware version @@ -194,60 +190,67 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, struct spi_message *message) { struct spi_transfer *xfer = NULL; - u8 cmd_opcode; + u8 cmd_opcode = 0, fifo_pos = AMD_SPI_FIFO_BASE; u8 *buf = NULL; - u32 m_cmd = 0; u32 i = 0; u32 tx_len = 0, rx_len = 0; list_for_each_entry(xfer, &message->transfers, transfer_list) { - if (xfer->rx_buf) - m_cmd = AMD_SPI_XFER_RX; - if (xfer->tx_buf) - m_cmd = AMD_SPI_XFER_TX; - - if (m_cmd & AMD_SPI_XFER_TX) { + if (xfer->tx_buf) { buf = (u8 *)xfer->tx_buf; - tx_len = xfer->len - 1; - cmd_opcode = *(u8 *)xfer->tx_buf; - buf++; - amd_spi_set_opcode(amd_spi, cmd_opcode); + if (!tx_len) { + cmd_opcode = *(u8 *)xfer->tx_buf; + buf++; + xfer->len--; + } + tx_len += xfer->len; /* Write data into the FIFO. */ - for (i = 0; i < tx_len; i++) { - iowrite8(buf[i], ((u8 __iomem *)amd_spi->io_remap_addr + - AMD_SPI_FIFO_BASE + i)); - } + for (i = 0; i < xfer->len; i++) + amd_spi_writereg8(amd_spi, fifo_pos + i, buf[i]); - amd_spi_set_tx_count(amd_spi, tx_len); - amd_spi_clear_fifo_ptr(amd_spi); - /* Execute command */ - amd_spi_execute_opcode(amd_spi); - } - if (m_cmd & AMD_SPI_XFER_RX) { - /* - * Store no. of bytes to be received from - * FIFO - */ - rx_len = xfer->len; - buf = (u8 *)xfer->rx_buf; - amd_spi_set_rx_count(amd_spi, rx_len); - amd_spi_clear_fifo_ptr(amd_spi); - /* Execute command */ - 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(amd_spi, AMD_SPI_FIFO_BASE + tx_len + i); + fifo_pos += xfer->len; } + + /* Store no. of bytes to be received from FIFO */ + if (xfer->rx_buf) + rx_len += xfer->len; + } + + if (!buf) { + message->status = -EINVAL; + goto fin_msg; + } + + amd_spi_set_opcode(amd_spi, cmd_opcode); + amd_spi_set_tx_count(amd_spi, tx_len); + amd_spi_set_rx_count(amd_spi, rx_len); + + /* Execute command */ + message->status = amd_spi_execute_opcode(amd_spi); + if (message->status) + goto fin_msg; + + if (rx_len) { + message->status = amd_spi_busy_wait(amd_spi); + if (message->status) + goto fin_msg; + + list_for_each_entry(xfer, &message->transfers, transfer_list) + if (xfer->rx_buf) { + buf = (u8 *)xfer->rx_buf; + /* Read data from FIFO to receive buffer */ + for (i = 0; i < xfer->len; i++) + buf[i] = amd_spi_readreg8(amd_spi, fifo_pos + i); + fifo_pos += xfer->len; + } } /* Update statistics */ message->actual_length = tx_len + rx_len + 1; - /* complete the transaction */ - message->status = 0; +fin_msg: switch (amd_spi->version) { case AMD_SPI_V1: break; @@ -260,7 +263,7 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, spi_finalize_current_message(master); - return 0; + return message->status; } static int amd_spi_master_transfer(struct spi_master *master, @@ -275,9 +278,7 @@ static int amd_spi_master_transfer(struct spi_master *master, * Extract spi_transfers from the spi message and * program the controller. */ - amd_spi_fifo_xfer(amd_spi, master, msg); - - return 0; + return amd_spi_fifo_xfer(amd_spi, master, msg); } static size_t amd_spi_max_transfer_size(struct spi_device *spi) -- cgit v1.2.3 From 51e99de583697cc073ef8888690675b07fe8ef3c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 18 Aug 2022 23:01:07 +0200 Subject: spi: move from strlcpy with unused retval to strscpy Follow the advice of the below link and prefer 'strscpy' in this subsystem. Conversion is 1:1 because the return value is not used. Generated by a coccinelle script. Link: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/ Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20220818210107.7373-1-wsa+renesas@sang-engineering.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 83da8862b8f2..97487e1f27b5 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -753,7 +753,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr, proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; proxy->irq = chip->irq; - strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); + strscpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias)); proxy->dev.platform_data = (void *) chip->platform_data; proxy->controller_data = chip->controller_data; proxy->controller_state = NULL; @@ -2330,7 +2330,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi, goto err_out; } - strlcpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias)); + strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias)); /* Use provided chip-select for ancillary device */ ancillary->chip_select = chip_select; @@ -2726,7 +2726,7 @@ static ssize_t slave_store(struct device *dev, struct device_attribute *attr, if (!spi) return -ENOMEM; - strlcpy(spi->modalias, name, sizeof(spi->modalias)); + strscpy(spi->modalias, name, sizeof(spi->modalias)); rc = spi_add_device(spi); if (rc) { -- cgit v1.2.3 From 3f03c618bebb024bf8770a74480a9416c847ce53 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 16 Aug 2022 16:08:18 +0300 Subject: spi: intel: Add support for second flash chip Intel SPI flash controller has been supporting two chip selects long time already even if the most common configuration is to have a single flash chip for the BIOS and related data. This adds support for the second chip select if we find out that there are two flash components (this information is available in the mandatory flash descriptor on the first chip). The second chip is exposed as is without any partition information. Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20220816130818.89600-1-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-intel.c | 163 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 147 insertions(+), 16 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 66063687ae27..52d43324bb87 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -116,6 +116,22 @@ #define ERASE_64K_OPCODE_SHIFT 16 #define ERASE_64K_OPCODE_MASK (0xff << ERASE_OPCODE_SHIFT) +/* Flash descriptor fields */ +#define FLVALSIG_MAGIC 0x0ff0a55a +#define FLMAP0_NC_MASK GENMASK(9, 8) +#define FLMAP0_NC_SHIFT 8 +#define FLMAP0_FCBA_MASK GENMASK(7, 0) + +#define FLCOMP_C0DEN_MASK GENMASK(3, 0) +#define FLCOMP_C0DEN_512K 0x00 +#define FLCOMP_C0DEN_1M 0x01 +#define FLCOMP_C0DEN_2M 0x02 +#define FLCOMP_C0DEN_4M 0x03 +#define FLCOMP_C0DEN_8M 0x04 +#define FLCOMP_C0DEN_16M 0x05 +#define FLCOMP_C0DEN_32M 0x06 +#define FLCOMP_C0DEN_64M 0x07 + #define INTEL_SPI_TIMEOUT 5000 /* ms */ #define INTEL_SPI_FIFO_SZ 64 @@ -129,6 +145,7 @@ * @master: Pointer to the SPI controller structure * @nregions: Maximum number of regions * @pr_num: Maximum number of protected range registers + * @chip0_size: Size of the first flash chip in bytes * @locked: Is SPI setting locked * @swseq_reg: Use SW sequencer in register reads/writes * @swseq_erase: Use SW sequencer in erase operation @@ -146,6 +163,7 @@ struct intel_spi { struct spi_controller *master; size_t nregions; size_t pr_num; + size_t chip0_size; bool locked; bool swseq_reg; bool swseq_erase; @@ -158,6 +176,7 @@ struct intel_spi_mem_op { struct spi_mem_op mem_op; u32 replacement_op; int (*exec_op)(struct intel_spi *ispi, + const struct spi_mem *mem, const struct intel_spi_mem_op *iop, const struct spi_mem_op *op); }; @@ -441,7 +460,16 @@ static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len, return 0; } -static int intel_spi_read_reg(struct intel_spi *ispi, +static u32 intel_spi_chip_addr(const struct intel_spi *ispi, + const struct spi_mem *mem) +{ + /* Pick up the correct start address */ + if (!mem) + return 0; + return mem->spi->chip_select == 1 ? ispi->chip0_size : 0; +} + +static int intel_spi_read_reg(struct intel_spi *ispi, const struct spi_mem *mem, const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { @@ -449,8 +477,7 @@ static int intel_spi_read_reg(struct intel_spi *ispi, u8 opcode = op->cmd.opcode; int ret; - /* Address of the first chip */ - writel(0, ispi->base + FADDR); + writel(intel_spi_chip_addr(ispi, mem), ispi->base + FADDR); if (ispi->swseq_reg) ret = intel_spi_sw_cycle(ispi, opcode, nbytes, @@ -464,7 +491,7 @@ static int intel_spi_read_reg(struct intel_spi *ispi, return intel_spi_read_block(ispi, op->data.buf.in, nbytes); } -static int intel_spi_write_reg(struct intel_spi *ispi, +static int intel_spi_write_reg(struct intel_spi *ispi, const struct spi_mem *mem, const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { @@ -511,7 +538,7 @@ static int intel_spi_write_reg(struct intel_spi *ispi, if (opcode == SPINOR_OP_WRDI) return 0; - writel(0, ispi->base + FADDR); + writel(intel_spi_chip_addr(ispi, mem), ispi->base + FADDR); /* Write the value beforehand */ ret = intel_spi_write_block(ispi, op->data.buf.out, nbytes); @@ -524,13 +551,13 @@ static int intel_spi_write_reg(struct intel_spi *ispi, return intel_spi_hw_cycle(ispi, opcode, nbytes); } -static int intel_spi_read(struct intel_spi *ispi, +static int intel_spi_read(struct intel_spi *ispi, const struct spi_mem *mem, const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { - void *read_buf = op->data.buf.in; + u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val; size_t block_size, nbytes = op->data.nbytes; - u32 addr = op->addr.val; + void *read_buf = op->data.buf.in; u32 val, status; int ret; @@ -585,13 +612,13 @@ static int intel_spi_read(struct intel_spi *ispi, return 0; } -static int intel_spi_write(struct intel_spi *ispi, +static int intel_spi_write(struct intel_spi *ispi, const struct spi_mem *mem, const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { + u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val; size_t block_size, nbytes = op->data.nbytes; const void *write_buf = op->data.buf.out; - u32 addr = op->addr.val; u32 val, status; int ret; @@ -648,12 +675,12 @@ static int intel_spi_write(struct intel_spi *ispi, return 0; } -static int intel_spi_erase(struct intel_spi *ispi, +static int intel_spi_erase(struct intel_spi *ispi, const struct spi_mem *mem, const struct intel_spi_mem_op *iop, const struct spi_mem_op *op) { + u32 addr = intel_spi_chip_addr(ispi, mem) + op->addr.val; u8 opcode = op->cmd.opcode; - u32 addr = op->addr.val; u32 val, status; int ret; @@ -765,7 +792,7 @@ static int intel_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *o if (!iop) return -EOPNOTSUPP; - return iop->exec_op(ispi, iop, op); + return iop->exec_op(ispi, mem, iop, op); } static const char *intel_spi_get_name(struct spi_mem *mem) @@ -805,7 +832,7 @@ static ssize_t intel_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, op.data.nbytes = len; op.data.buf.in = buf; - ret = iop->exec_op(ispi, iop, &op); + ret = iop->exec_op(ispi, desc->mem, iop, &op); return ret ? ret : len; } @@ -821,7 +848,7 @@ static ssize_t intel_spi_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs op.data.nbytes = len; op.data.buf.out = buf; - ret = iop->exec_op(ispi, iop, &op); + ret = iop->exec_op(ispi, desc->mem, iop, &op); return ret ? ret : len; } @@ -1226,10 +1253,98 @@ static void intel_spi_fill_partition(struct intel_spi *ispi, } } +static int intel_spi_read_desc(struct intel_spi *ispi) +{ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ, 0), + SPI_MEM_OP_ADDR(3, 0, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(0, NULL, 0)); + u32 buf[2], nc, fcba, flcomp; + ssize_t ret; + + op.addr.val = 0x10; + op.data.buf.in = buf; + op.data.nbytes = sizeof(buf); + + ret = intel_spi_read(ispi, NULL, NULL, &op); + if (ret) { + dev_warn(ispi->dev, "failed to read descriptor\n"); + return ret; + } + + dev_dbg(ispi->dev, "FLVALSIG=0x%08x\n", buf[0]); + dev_dbg(ispi->dev, "FLMAP0=0x%08x\n", buf[1]); + + if (buf[0] != FLVALSIG_MAGIC) { + dev_warn(ispi->dev, "descriptor signature not valid\n"); + return -ENODEV; + } + + fcba = (buf[1] & FLMAP0_FCBA_MASK) << 4; + dev_dbg(ispi->dev, "FCBA=%#x\n", fcba); + + op.addr.val = fcba; + op.data.buf.in = &flcomp; + op.data.nbytes = sizeof(flcomp); + + ret = intel_spi_read(ispi, NULL, NULL, &op); + if (ret) { + dev_warn(ispi->dev, "failed to read FLCOMP\n"); + return -ENODEV; + } + + dev_dbg(ispi->dev, "FLCOMP=0x%08x\n", flcomp); + + switch (flcomp & FLCOMP_C0DEN_MASK) { + case FLCOMP_C0DEN_512K: + ispi->chip0_size = SZ_512K; + break; + case FLCOMP_C0DEN_1M: + ispi->chip0_size = SZ_1M; + break; + case FLCOMP_C0DEN_2M: + ispi->chip0_size = SZ_2M; + break; + case FLCOMP_C0DEN_4M: + ispi->chip0_size = SZ_4M; + break; + case FLCOMP_C0DEN_8M: + ispi->chip0_size = SZ_8M; + break; + case FLCOMP_C0DEN_16M: + ispi->chip0_size = SZ_16M; + break; + case FLCOMP_C0DEN_32M: + ispi->chip0_size = SZ_32M; + break; + case FLCOMP_C0DEN_64M: + ispi->chip0_size = SZ_64M; + break; + default: + return -EINVAL; + } + + dev_dbg(ispi->dev, "chip0 size %zd KB\n", ispi->chip0_size / SZ_1K); + + nc = (buf[1] & FLMAP0_NC_MASK) >> FLMAP0_NC_SHIFT; + if (!nc) + ispi->master->num_chipselect = 1; + else if (nc == 1) + ispi->master->num_chipselect = 2; + else + return -EINVAL; + + dev_dbg(ispi->dev, "%u flash components found\n", + ispi->master->num_chipselect); + return 0; +} + static int intel_spi_populate_chip(struct intel_spi *ispi) { struct flash_platform_data *pdata; struct spi_board_info chip; + int ret; pdata = devm_kzalloc(ispi->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1247,7 +1362,23 @@ static int intel_spi_populate_chip(struct intel_spi *ispi) snprintf(chip.modalias, 8, "spi-nor"); chip.platform_data = pdata; - return spi_new_device(ispi->master, &chip) ? 0 : -ENODEV; + if (!spi_new_device(ispi->master, &chip)) + return -ENODEV; + + /* Add the second chip if present */ + if (ispi->master->num_chipselect < 2) + return 0; + + ret = intel_spi_read_desc(ispi); + if (ret) + return ret; + + chip.platform_data = NULL; + chip.chip_select = 1; + + if (!spi_new_device(ispi->master, &chip)) + return -ENODEV; + return 0; } /** -- cgit v1.2.3 From 1d895be13af0d962bef67ba0ceaefbdc6954fe67 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 16 Aug 2022 15:55:37 +0300 Subject: spi: intel: 64k erase is supported from Canon Lake and beyond The hardware sequencer in Intel Canon Lake and beyond supports also 64k erase command. The SPI-NOR core uses SFDP (Serial Flash Discovery Parameter) to figure out what the chip actually supports and only issues 64k erase if it is supported. Signed-off-by: Mika Westerberg Link: https://lore.kernel.org/r/20220816125537.89389-1-mika.westerberg@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c index 52d43324bb87..55f4ee2db002 100644 --- a/drivers/spi/spi-intel.c +++ b/drivers/spi/spi-intel.c @@ -1100,6 +1100,7 @@ static int intel_spi_init(struct intel_spi *ispi) ispi->pregs = ispi->base + CNL_PR; ispi->nregions = CNL_FREG_NUM; ispi->pr_num = CNL_PR_NUM; + erase_64k = true; break; default: -- cgit v1.2.3 From a557fca630cc6d4a07eb9bac0039e88a6b1c4847 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Tue, 23 Aug 2022 09:58:50 +0200 Subject: spi: stm32_qspi: Add transfer_one_message() spi callback Add transfer_one_message() spi callback in order to use the QSPI interface as a communication channel using up to 8 qspi lines (QSPI configured in dual flash mode). To enable this mode, both spi-rx-bus-width and spi-tx-bus-width must be set to 8 and cs-qpios must be populated. Signed-off-by: Patrice Chotard Link: https://lore.kernel.org/r/20220823075850.575043-3-patrice.chotard@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 118 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 109 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index f3fe92300639..92459daca95f 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -355,10 +356,10 @@ static int stm32_qspi_get_mode(u8 buswidth) return buswidth; } -static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op) +static int stm32_qspi_send(struct spi_device *spi, const struct spi_mem_op *op) { - struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master); - struct stm32_qspi_flash *flash = &qspi->flash[mem->spi->chip_select]; + struct stm32_qspi *qspi = spi_controller_get_devdata(spi->master); + struct stm32_qspi_flash *flash = &qspi->flash[spi->chip_select]; u32 ccr, cr; int timeout, err = 0, err_poll_status = 0; @@ -465,7 +466,7 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op * qspi->fmode = CCR_FMODE_APM; qspi->status_timeout = timeout_ms; - ret = stm32_qspi_send(mem, op); + ret = stm32_qspi_send(mem->spi, op); mutex_unlock(&qspi->lock); pm_runtime_mark_last_busy(qspi->dev); @@ -489,7 +490,7 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) else qspi->fmode = CCR_FMODE_INDW; - ret = stm32_qspi_send(mem, op); + ret = stm32_qspi_send(mem->spi, op); mutex_unlock(&qspi->lock); pm_runtime_mark_last_busy(qspi->dev); @@ -545,7 +546,7 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, else qspi->fmode = CCR_FMODE_INDR; - ret = stm32_qspi_send(desc->mem, &op); + ret = stm32_qspi_send(desc->mem->spi, &op); mutex_unlock(&qspi->lock); pm_runtime_mark_last_busy(qspi->dev); @@ -554,12 +555,87 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, return ret ?: len; } +static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl, + struct spi_message *msg) +{ + struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl); + struct spi_transfer *transfer; + struct spi_device *spi = msg->spi; + struct spi_mem_op op; + int ret; + + if (!spi->cs_gpiod) + return -EOPNOTSUPP; + + mutex_lock(&qspi->lock); + + gpiod_set_value_cansleep(spi->cs_gpiod, true); + + list_for_each_entry(transfer, &msg->transfers, transfer_list) { + u8 dummy_bytes = 0; + + memset(&op, 0, sizeof(op)); + + dev_dbg(qspi->dev, "tx_buf:%p tx_nbits:%d rx_buf:%p rx_nbits:%d len:%d dummy_data:%d\n", + transfer->tx_buf, transfer->tx_nbits, + transfer->rx_buf, transfer->rx_nbits, + transfer->len, transfer->dummy_data); + + /* + * QSPI hardware supports dummy bytes transfer. + * If current transfer is dummy byte, merge it with the next + * transfer in order to take into account QSPI block constraint + */ + if (transfer->dummy_data) { + op.dummy.buswidth = transfer->tx_nbits; + op.dummy.nbytes = transfer->len; + dummy_bytes = transfer->len; + + /* if happens, means that message is not correctly built */ + if (list_is_last(&transfer->transfer_list, &msg->transfers)) + goto end_of_transfer; + + transfer = list_next_entry(transfer, transfer_list); + } + + op.data.nbytes = transfer->len; + + if (transfer->rx_buf) { + qspi->fmode = CCR_FMODE_INDR; + op.data.buswidth = transfer->rx_nbits; + op.data.dir = SPI_MEM_DATA_IN; + op.data.buf.in = transfer->rx_buf; + } else { + qspi->fmode = CCR_FMODE_INDW; + op.data.buswidth = transfer->tx_nbits; + op.data.dir = SPI_MEM_DATA_OUT; + op.data.buf.out = transfer->tx_buf; + } + + ret = stm32_qspi_send(spi, &op); + if (ret) + goto end_of_transfer; + + msg->actual_length += transfer->len + dummy_bytes; + } + +end_of_transfer: + gpiod_set_value_cansleep(spi->cs_gpiod, false); + + mutex_unlock(&qspi->lock); + + msg->status = ret; + spi_finalize_current_message(ctrl); + + return ret; +} + static int stm32_qspi_setup(struct spi_device *spi) { struct spi_controller *ctrl = spi->master; struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl); struct stm32_qspi_flash *flash; - u32 presc; + u32 presc, mode; int ret; if (ctrl->busy) @@ -568,6 +644,16 @@ static int stm32_qspi_setup(struct spi_device *spi) if (!spi->max_speed_hz) return -EINVAL; + mode = spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL); + if ((mode == SPI_TX_OCTAL || mode == SPI_RX_OCTAL) || + ((mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) && + of_gpio_named_count(qspi->dev->of_node, "cs-gpios") == -ENOENT)) { + dev_err(qspi->dev, "spi-rx-bus-width\\/spi-tx-bus-width\\/cs-gpios\n"); + dev_err(qspi->dev, "configuration not supported\n"); + + return -EINVAL; + } + ret = pm_runtime_resume_and_get(qspi->dev); if (ret < 0) return ret; @@ -580,6 +666,17 @@ static int stm32_qspi_setup(struct spi_device *spi) mutex_lock(&qspi->lock); qspi->cr_reg = CR_APMS | 3 << CR_FTHRES_SHIFT | CR_SSHIFT | CR_EN; + + /* + * Dual flash mode is only enable in case SPI_TX_OCTAL and SPI_TX_OCTAL + * are both set in spi->mode and "cs-gpios" properties is found in DT + */ + if (((spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL)) == (SPI_TX_OCTAL | SPI_RX_OCTAL)) && + of_gpio_named_count(qspi->dev->of_node, "cs-gpios")) { + qspi->cr_reg |= CR_DFM; + dev_dbg(qspi->dev, "Dual flash mode enable"); + } + writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR); /* set dcr fsize to max address */ @@ -741,11 +838,14 @@ static int stm32_qspi_probe(struct platform_device *pdev) mutex_init(&qspi->lock); - ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD - | SPI_TX_DUAL | SPI_TX_QUAD; + ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL + | SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_OCTAL; ctrl->setup = stm32_qspi_setup; ctrl->bus_num = -1; ctrl->mem_ops = &stm32_qspi_mem_ops; + ctrl->use_gpio_descriptors = true; + ctrl->transfer_one_message = stm32_qspi_transfer_one_message; + ctrl->auto_runtime_pm = true; ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP; ctrl->dev.of_node = dev->of_node; -- cgit v1.2.3 From 52c135d495ca019abbacf063f5ef43bd5b189070 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Mon, 22 Aug 2022 21:18:53 +0300 Subject: spi: dw: Quite logging on deferred controller registration It's pretty possible to have the spi_register_controller() method returning -EPROBE_DEFER status in case, for instance, if the GPIOs used for the CS implementation aren't ready to be requested due to the corresponding platform devices still pending to be probed. Let's make sure the DW SSI driver won't print error message in that case by calling the dev_err_probe() function if the SPI-registration procedure exited with a non-zero status. Signed-off-by: Serge Semin Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220822181853.23063-1-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index f87d97ccd2d6..99edddf9958b 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -955,7 +955,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ret = spi_register_controller(master); if (ret) { - dev_err(&master->dev, "problem registering spi master\n"); + dev_err_probe(dev, ret, "problem registering spi master\n"); goto err_dma_exit; } -- cgit v1.2.3 From d294e99cdc823f368530b8169e33a599fa2a1afe Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Thu, 25 Aug 2022 07:28:28 +0000 Subject: spi: pxa2xx: Remove the unneeded result variable Return the value clk_prepare_enable() directly instead of storing it in another redundant variable. Reported-by: Zeal Robot Signed-off-by: ye xingchen Link: https://lore.kernel.org/r/20220825072828.229294-1-ye.xingchen@zte.com.cn Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 838d12e65144..986ffc4bf1ed 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1873,10 +1873,8 @@ static int pxa2xx_spi_runtime_suspend(struct device *dev) static int pxa2xx_spi_runtime_resume(struct device *dev) { struct driver_data *drv_data = dev_get_drvdata(dev); - int status; - status = clk_prepare_enable(drv_data->ssp->clk); - return status; + return clk_prepare_enable(drv_data->ssp->clk); } #endif -- cgit v1.2.3 From 3fe26121dc3a9bf64e18fe0075cd9a92c9cd1b1a Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Thu, 25 Aug 2022 20:01:32 +0530 Subject: spi: amd: Configure device speed Number of clock frequencies are supported by AMD controller which are mentioned in the amd_spi_freq structure table. Create mechanism to configure device clock frequency such that it is strictly less than the requested frequency. Give priority to the device transfer speed and in case it is not set then use the max clock speed supported by the device. Signed-off-by: Lucas Tanure Co-developed-by: Shreeya Patel Signed-off-by: Shreeya Patel Link: https://lore.kernel.org/r/20220825143132.253224-1-shreeya.patel@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index b3b3f5167c4c..264633c5ce0b 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -36,6 +36,18 @@ #define AMD_SPI_FIFO_SIZE 70 #define AMD_SPI_MEM_SIZE 200 +#define AMD_SPI_ENA_REG 0x20 +#define AMD_SPI_ALT_SPD_SHIFT 20 +#define AMD_SPI_ALT_SPD_MASK GENMASK(23, AMD_SPI_ALT_SPD_SHIFT) +#define AMD_SPI_SPI100_SHIFT 0 +#define AMD_SPI_SPI100_MASK GENMASK(AMD_SPI_SPI100_SHIFT, AMD_SPI_SPI100_SHIFT) +#define AMD_SPI_SPEED_REG 0x6C +#define AMD_SPI_SPD7_SHIFT 8 +#define AMD_SPI_SPD7_MASK GENMASK(13, AMD_SPI_SPD7_SHIFT) + +#define AMD_SPI_MAX_HZ 100000000 +#define AMD_SPI_MIN_HZ 800000 + /** * enum amd_spi_versions - SPI controller versions * @AMD_SPI_V1: AMDI0061 hardware version @@ -46,14 +58,41 @@ enum amd_spi_versions { AMD_SPI_V2, }; +enum amd_spi_speed { + F_66_66MHz, + F_33_33MHz, + F_22_22MHz, + F_16_66MHz, + F_100MHz, + F_800KHz, + SPI_SPD7, + F_50MHz = 0x4, + F_4MHz = 0x32, + F_3_17MHz = 0x3F +}; + +/** + * struct amd_spi_freq - Matches device speed with values to write in regs + * @speed_hz: Device frequency + * @enable_val: Value to be written to "enable register" + * @spd7_val: Some frequencies requires to have a value written at SPISPEED register + */ +struct amd_spi_freq { + u32 speed_hz; + u32 enable_val; + u32 spd7_val; +}; + /** * struct amd_spi - SPI driver instance * @io_remap_addr: Start address of the SPI controller registers * @version: SPI controller hardware version + * @speed_hz: Device frequency */ struct amd_spi { void __iomem *io_remap_addr; enum amd_spi_versions version; + unsigned int speed_hz; }; static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx) @@ -185,11 +224,62 @@ static int amd_spi_master_setup(struct spi_device *spi) return 0; } +static const struct amd_spi_freq amd_spi_freq[] = { + { AMD_SPI_MAX_HZ, F_100MHz, 0}, + { 66660000, F_66_66MHz, 0}, + { 50000000, SPI_SPD7, F_50MHz}, + { 33330000, F_33_33MHz, 0}, + { 22220000, F_22_22MHz, 0}, + { 16660000, F_16_66MHz, 0}, + { 4000000, SPI_SPD7, F_4MHz}, + { 3170000, SPI_SPD7, F_3_17MHz}, + { AMD_SPI_MIN_HZ, F_800KHz, 0}, +}; + +static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz) +{ + unsigned int i, spd7_val, alt_spd; + + if (speed_hz == amd_spi->speed_hz) + return 0; + + if (speed_hz < AMD_SPI_MIN_HZ) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++) + if (speed_hz >= amd_spi_freq[i].speed_hz) + break; + + if (speed_hz == amd_spi_freq[i].speed_hz) + return 0; + + amd_spi->speed_hz = amd_spi_freq[i].speed_hz; + + alt_spd = (amd_spi_freq[i].enable_val << AMD_SPI_ALT_SPD_SHIFT) + & AMD_SPI_ALT_SPD_MASK; + amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, alt_spd, + AMD_SPI_ALT_SPD_MASK); + + if (amd_spi->speed_hz == AMD_SPI_MAX_HZ) + amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, 1, + AMD_SPI_SPI100_MASK); + + if (amd_spi_freq[i].spd7_val) { + spd7_val = (amd_spi_freq[i].spd7_val << AMD_SPI_SPD7_SHIFT) + & AMD_SPI_SPD7_MASK; + amd_spi_setclear_reg32(amd_spi, AMD_SPI_SPEED_REG, spd7_val, + AMD_SPI_SPD7_MASK); + } + + return 0; +} + static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, struct spi_master *master, struct spi_message *message) { struct spi_transfer *xfer = NULL; + struct spi_device *spi = message->spi; u8 cmd_opcode = 0, fifo_pos = AMD_SPI_FIFO_BASE; u8 *buf = NULL; u32 i = 0; @@ -197,6 +287,11 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (xfer->speed_hz) + amd_set_spi_freq(amd_spi, xfer->speed_hz); + else + amd_set_spi_freq(amd_spi, spi->max_speed_hz); + if (xfer->tx_buf) { buf = (u8 *)xfer->tx_buf; if (!tx_len) { @@ -313,6 +408,8 @@ static int amd_spi_probe(struct platform_device *pdev) master->num_chipselect = 4; master->mode_bits = 0; master->flags = SPI_MASTER_HALF_DUPLEX; + master->max_speed_hz = AMD_SPI_MAX_HZ; + master->min_speed_hz = AMD_SPI_MIN_HZ; master->setup = amd_spi_master_setup; master->transfer_one_message = amd_spi_master_transfer; master->max_transfer_size = amd_spi_max_transfer_size; -- cgit v1.2.3 From 56ec456293239b3c5dfb0e4dcf22972b1b8c571d Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 26 Aug 2022 11:20:31 +0200 Subject: spi: stm32-qspi: Fix stm32_qspi_transfer_one_message() error path The patch a557fca630cc: "spi: stm32_qspi: Add transfer_one_message() spi callback" from Aug 23, 2022, leads to the following Smatch static checker warning: drivers/spi/spi-stm32-qspi.c:627 stm32_qspi_transfer_one_message() error: uninitialized symbol 'ret'.Fix the following Smatch static checker warning: Fixes: a557fca630cc ("spi: stm32_qspi: Add transfer_one_message() spi callback") Reported-by: Dan Carpenter Signed-off-by: Patrice Chotard Link: https://lore.kernel.org/r/20220826092031.1393430-1-patrice.chotard@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 92459daca95f..679fd1c34f7e 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -562,7 +562,7 @@ static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl, struct spi_transfer *transfer; struct spi_device *spi = msg->spi; struct spi_mem_op op; - int ret; + int ret = 0; if (!spi->cs_gpiod) return -EOPNOTSUPP; @@ -592,8 +592,10 @@ static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl, dummy_bytes = transfer->len; /* if happens, means that message is not correctly built */ - if (list_is_last(&transfer->transfer_list, &msg->transfers)) + if (list_is_last(&transfer->transfer_list, &msg->transfers)) { + ret = -EINVAL; goto end_of_transfer; + } transfer = list_next_entry(transfer, transfer_list); } -- cgit v1.2.3 From ea9d001550abaf2f4c75cf4fcd936ea19f932b84 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 24 Aug 2022 11:43:25 +0200 Subject: spi: sh-msiof: add generic Gen4 binding No further changes in this generation discovered yet. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20220824094327.33685-3-wsa+renesas@sang-engineering.com Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index d0012b30410c..9bca3d076f05 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1085,6 +1085,7 @@ static const struct of_device_id sh_msiof_match[] = { { .compatible = "renesas,rcar-gen2-msiof", .data = &rcar_gen2_data }, { .compatible = "renesas,msiof-r8a7796", .data = &rcar_gen3_data }, { .compatible = "renesas,rcar-gen3-msiof", .data = &rcar_gen3_data }, + { .compatible = "renesas,rcar-gen4-msiof", .data = &rcar_gen3_data }, { .compatible = "renesas,sh-msiof", .data = &sh_data }, /* Deprecated */ {}, }; -- cgit v1.2.3 From 47c32b2b7fcfa97f7224df222f439fc0ccf94ffe Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Mon, 29 Aug 2022 14:32:50 +0200 Subject: spi: stm32-qspi: Fix pm_runtime management in stm32_qspi_transfer_one_message() ctrl->auto_runtime_pm was wrongly set to true when adding transfer_one_message() callback. As explained in commit 6e6ccb3d4cdc ("spi: stm32-qspi: Add pm_runtime support") the expected behavior is to prevent runtime suspends between each transfer. Add needed pm_runtime API calls in stm32_qspi_transfer_one_message(). Fixes: a557fca630cc ("spi: stm32_qspi: Add transfer_one_message() spi callback") Signed-off-by: Patrice Chotard Link: https://lore.kernel.org/r/20220829123250.2170562-1-patrice.chotard@foss.st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 679fd1c34f7e..227f450aa5f0 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -567,6 +567,10 @@ static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl, if (!spi->cs_gpiod) return -EOPNOTSUPP; + ret = pm_runtime_resume_and_get(qspi->dev); + if (ret < 0) + return ret; + mutex_lock(&qspi->lock); gpiod_set_value_cansleep(spi->cs_gpiod, true); @@ -629,6 +633,9 @@ end_of_transfer: msg->status = ret; spi_finalize_current_message(ctrl); + pm_runtime_mark_last_busy(qspi->dev); + pm_runtime_put_autosuspend(qspi->dev); + return ret; } @@ -847,7 +854,6 @@ static int stm32_qspi_probe(struct platform_device *pdev) ctrl->mem_ops = &stm32_qspi_mem_ops; ctrl->use_gpio_descriptors = true; ctrl->transfer_one_message = stm32_qspi_transfer_one_message; - ctrl->auto_runtime_pm = true; ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP; ctrl->dev.of_node = dev->of_node; -- cgit v1.2.3 From 2b2bf6b7faa9010fae10dc7de76627a3fdb525b3 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 27 Aug 2022 13:42:07 +0200 Subject: spi: mt7621: Fix an error message in mt7621_spi_probe() 'status' is known to be 0 at this point. The expected error code is PTR_ERR(clk). Switch to dev_err_probe() in order to display the expected error code (in a human readable way). This also filters -EPROBE_DEFER cases, should it happen. Fixes: 1ab7f2a43558 ("staging: mt7621-spi: add mt7621 support") Signed-off-by: Christophe JAILLET Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/r/928f3fb507d53ba0774df27cea0bbba4b055993b.1661599671.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-mt7621.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index b4b9b7309b5e..351b0ef52bbc 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -340,11 +340,9 @@ static int mt7621_spi_probe(struct platform_device *pdev) return PTR_ERR(base); clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n", - status); - return PTR_ERR(clk); - } + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), + "unable to get SYS clock\n"); status = clk_prepare_enable(clk); if (status) -- cgit v1.2.3 From 3d6af96814d753f34cf897466e7ddf041d0cdf3b Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 27 Aug 2022 13:42:19 +0200 Subject: spi: mt7621: Use the devm_clk_get_enabled() helper to simplify error handling The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. This helper is well suited for cases where the clock would be kept prepared or enabled for the whole lifetime of the driver. This simplifies the error handling a lot. The order between spi_unregister_controller() (in the remove function) and the clk_disable_unprepare() (now handle by a managed resource) is kept the same. (see commit 46b5c4fb87ce ("spi: mt7621: Don't leak SPI master in probe error path") to see why it matters) Signed-off-by: Christophe JAILLET Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/r/05a7fd22719008c8a905d6328aa9548ce40f2a7a.1661599671.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-mt7621.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index 351b0ef52bbc..2580b28042be 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -327,7 +327,6 @@ static int mt7621_spi_probe(struct platform_device *pdev) struct spi_controller *master; struct mt7621_spi *rs; void __iomem *base; - int status = 0; struct clk *clk; int ret; @@ -339,19 +338,14 @@ static int mt7621_spi_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - clk = devm_clk_get(&pdev->dev, NULL); + clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) return dev_err_probe(&pdev->dev, PTR_ERR(clk), "unable to get SYS clock\n"); - status = clk_prepare_enable(clk); - if (status) - return status; - master = devm_spi_alloc_master(&pdev->dev, sizeof(*rs)); if (!master) { dev_info(&pdev->dev, "master allocation failed\n"); - clk_disable_unprepare(clk); return -ENOMEM; } @@ -376,13 +370,10 @@ static int mt7621_spi_probe(struct platform_device *pdev) ret = device_reset(&pdev->dev); if (ret) { dev_err(&pdev->dev, "SPI reset failed!\n"); - clk_disable_unprepare(clk); return ret; } ret = spi_register_controller(master); - if (ret) - clk_disable_unprepare(clk); return ret; } @@ -390,13 +381,10 @@ static int mt7621_spi_probe(struct platform_device *pdev) static int mt7621_spi_remove(struct platform_device *pdev) { struct spi_controller *master; - struct mt7621_spi *rs; master = dev_get_drvdata(&pdev->dev); - rs = spi_controller_get_devdata(master); spi_unregister_controller(master); - clk_disable_unprepare(rs->clk); return 0; } -- cgit v1.2.3 From 30b31b29a866d32fc381b406d4c4f5db2636e5ec Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 27 Aug 2022 13:42:29 +0200 Subject: spi: mt7621: Use devm_spi_register_controller() Now that clk_disable_unprepare(clk) is handled with a managed resource, we can use devm_spi_register_controller() and axe the .remove function. The order between spi_unregister_controller() and clk_disable_unprepare() is still the same. (see commit 46b5c4fb87ce ("spi: mt7621: Don't leak SPI master in probe error path") to see why it matters) Signed-off-by: Christophe JAILLET Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/r/b7698e198acc998d99e7e7c895a2910f14f89443.1661599671.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-mt7621.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index 2580b28042be..114f98dcae5e 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -373,20 +373,7 @@ static int mt7621_spi_probe(struct platform_device *pdev) return ret; } - ret = spi_register_controller(master); - - return ret; -} - -static int mt7621_spi_remove(struct platform_device *pdev) -{ - struct spi_controller *master; - - master = dev_get_drvdata(&pdev->dev); - - spi_unregister_controller(master); - - return 0; + return devm_spi_register_controller(&pdev->dev, master); } MODULE_ALIAS("platform:" DRIVER_NAME); @@ -397,7 +384,6 @@ static struct platform_driver mt7621_spi_driver = { .of_match_table = mt7621_spi_match, }, .probe = mt7621_spi_probe, - .remove = mt7621_spi_remove, }; module_platform_driver(mt7621_spi_driver); -- cgit v1.2.3 From 4a5cc683543f5f6ed586944095c65cb4da4b9273 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 27 Aug 2022 13:42:40 +0200 Subject: spi: mt7621: Remove 'clk' from 'struct mt7621_spi' The 'clk' field in 'struct mt7621_spi' is useless, remove it. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/76ed0ef91479498b9a2d5ef539f80851cffdb4ea.1661599671.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-mt7621.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt7621.c b/drivers/spi/spi-mt7621.c index 114f98dcae5e..c4cc8e2f85e2 100644 --- a/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c @@ -55,7 +55,6 @@ struct mt7621_spi { void __iomem *base; unsigned int sys_freq; unsigned int speed; - struct clk *clk; int pending_write; }; @@ -361,9 +360,8 @@ static int mt7621_spi_probe(struct platform_device *pdev) rs = spi_controller_get_devdata(master); rs->base = base; - rs->clk = clk; rs->master = master; - rs->sys_freq = clk_get_rate(rs->clk); + rs->sys_freq = clk_get_rate(clk); rs->pending_write = 0; dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq); -- cgit v1.2.3 From 9477420efc41f60f06413cefa38f5bfd71ba64d8 Mon Sep 17 00:00:00 2001 From: Shreeya Patel Date: Tue, 30 Aug 2022 15:06:07 +0530 Subject: spi: amd: Fix speed selection If the current speed is equal to the requested speed by the device then return success. This patch fixes a bug introduced by the commit 3fe26121dc3a ("spi: amd: Configure device speed") which checks speed_hz instead of amd_spi->speed_hz. Fixes: 3fe26121dc3a ("spi: amd: Configure device speed") Signed-off-by: Shreeya Patel Link: https://lore.kernel.org/r/20220830093607.45484-1-shreeya.patel@collabora.com Signed-off-by: Mark Brown --- drivers/spi/spi-amd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 264633c5ce0b..e23121456c70 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -240,9 +240,6 @@ static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz) { unsigned int i, spd7_val, alt_spd; - if (speed_hz == amd_spi->speed_hz) - return 0; - if (speed_hz < AMD_SPI_MIN_HZ) return -EINVAL; @@ -250,7 +247,7 @@ static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz) if (speed_hz >= amd_spi_freq[i].speed_hz) break; - if (speed_hz == amd_spi_freq[i].speed_hz) + if (amd_spi->speed_hz == amd_spi_freq[i].speed_hz) return 0; amd_spi->speed_hz = amd_spi_freq[i].speed_hz; -- cgit v1.2.3 From eea0e7d20d6dab38522ac0a3af61fd92c53c34f6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 30 Aug 2022 21:28:20 +0300 Subject: spi: stm32-qspi: Replace of_gpio_named_count() by gpiod_count() As a preparation to unexport of_gpio_named_count(), convert the driver to use gpiod_count() instead. Signed-off-by: Andy Shevchenko Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20220830182821.47919-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 227f450aa5f0..5858f5f9c758 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -656,7 +656,7 @@ static int stm32_qspi_setup(struct spi_device *spi) mode = spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL); if ((mode == SPI_TX_OCTAL || mode == SPI_RX_OCTAL) || ((mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) && - of_gpio_named_count(qspi->dev->of_node, "cs-gpios") == -ENOENT)) { + gpiod_count(qspi->dev, "cs") == -ENOENT)) { dev_err(qspi->dev, "spi-rx-bus-width\\/spi-tx-bus-width\\/cs-gpios\n"); dev_err(qspi->dev, "configuration not supported\n"); @@ -681,7 +681,7 @@ static int stm32_qspi_setup(struct spi_device *spi) * are both set in spi->mode and "cs-gpios" properties is found in DT */ if (((spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL)) == (SPI_TX_OCTAL | SPI_RX_OCTAL)) && - of_gpio_named_count(qspi->dev->of_node, "cs-gpios")) { + gpiod_count(qspi->dev, "cs")) { qspi->cr_reg |= CR_DFM; dev_dbg(qspi->dev, "Dual flash mode enable"); } -- cgit v1.2.3 From c9448aa41ac7dd223a6bef79e71d6c168593ebb7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 30 Aug 2022 21:28:21 +0300 Subject: spi: stm32-qspi: Refactor dual flash mode enable check in ->setup() gpiod_count() either returns positive number of the CS or negative error code. In the stm32_qspi_setup() we check that configuration has enough CS for the dual flash mode and SPI mode is not changing over the lines of the code. Taking all above into considertion, refactor dual flash mode enable check by dropping unneeded CS check and reusing local mode variable. Signed-off-by: Andy Shevchenko Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20220830182821.47919-2-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 5858f5f9c758..9131660c1afb 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -680,8 +680,7 @@ static int stm32_qspi_setup(struct spi_device *spi) * Dual flash mode is only enable in case SPI_TX_OCTAL and SPI_TX_OCTAL * are both set in spi->mode and "cs-gpios" properties is found in DT */ - if (((spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL)) == (SPI_TX_OCTAL | SPI_RX_OCTAL)) && - gpiod_count(qspi->dev, "cs")) { + if (mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) { qspi->cr_reg |= CR_DFM; dev_dbg(qspi->dev, "Dual flash mode enable"); } -- cgit v1.2.3 From 70034320fdc597b8f58b4a43bb547f17c4c5557a Mon Sep 17 00:00:00 2001 From: Xu Qiang Date: Thu, 25 Aug 2022 06:53:23 +0000 Subject: spi: qup: add missing clk_disable_unprepare on error in spi_qup_resume() Add the missing clk_disable_unprepare() before return from spi_qup_resume() in the error handling case. Fixes: 64ff247a978f (“spi: Add Qualcomm QUP SPI controller support”) Signed-off-by: Xu Qiang Link: https://lore.kernel.org/r/20220825065324.68446-1-xuqiang36@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 00d6084306b4..ae4e67f152ec 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -1245,14 +1245,25 @@ static int spi_qup_resume(struct device *device) return ret; ret = clk_prepare_enable(controller->cclk); - if (ret) + if (ret) { + clk_disable_unprepare(controller->iclk); return ret; + } ret = spi_qup_set_state(controller, QUP_STATE_RESET); if (ret) - return ret; + goto disable_clk; + + ret = spi_master_resume(master); + if (ret) + goto disable_clk; - return spi_master_resume(master); + return 0; + +disable_clk: + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + return ret; } #endif /* CONFIG_PM_SLEEP */ -- cgit v1.2.3 From 494a22765ce479c9f8ad181c5d24cffda9f534bb Mon Sep 17 00:00:00 2001 From: Xu Qiang Date: Thu, 25 Aug 2022 06:53:24 +0000 Subject: spi: qup: add missing clk_disable_unprepare on error in spi_qup_pm_resume_runtime() Add the missing clk_disable_unprepare() before return from spi_qup_pm_resume_runtime() in the error handling case. Fixes: dae1a7700b34 (“spi: qup: Handle clocks in pm_runtime suspend and resume”) Signed-off-by: Xu Qiang Link: https://lore.kernel.org/r/20220825065324.68446-2-xuqiang36@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index ae4e67f152ec..7d89510dc3f0 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -1198,8 +1198,10 @@ static int spi_qup_pm_resume_runtime(struct device *device) return ret; ret = clk_prepare_enable(controller->cclk); - if (ret) + if (ret) { + clk_disable_unprepare(controller->iclk); return ret; + } /* Disable clocks auto gaiting */ config = readl_relaxed(controller->base + QUP_CONFIG); -- cgit v1.2.3 From 4b9ef436383e8aa910b71927b3f25ede9b190707 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Sep 2022 19:10:48 +0300 Subject: spi: nxp-fspi: Do not dereference fwnode in struct device In order to make the underneath API easier to change in the future, prevent users from dereferencing fwnode from struct device. Instead, use the specific dev_fwnode() API for that. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220906161048.39953-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 2b0301fc971c..d6a65a989ef8 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -588,7 +588,7 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f) { int ret; - if (is_acpi_node(f->dev->fwnode)) + if (is_acpi_node(dev_fwnode(f->dev))) return 0; ret = clk_prepare_enable(f->clk_en); @@ -606,7 +606,7 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f) static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f) { - if (is_acpi_node(f->dev->fwnode)) + if (is_acpi_node(dev_fwnode(f->dev))) return 0; clk_disable_unprepare(f->clk); @@ -1100,7 +1100,7 @@ static int nxp_fspi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, f); /* find the resources - configuration register address space */ - if (is_acpi_node(f->dev->fwnode)) + if (is_acpi_node(dev_fwnode(f->dev))) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); else res = platform_get_resource_byname(pdev, @@ -1113,7 +1113,7 @@ static int nxp_fspi_probe(struct platform_device *pdev) } /* find the resources - controller memory mapped space */ - if (is_acpi_node(f->dev->fwnode)) + if (is_acpi_node(dev_fwnode(f->dev))) res = platform_get_resource(pdev, IORESOURCE_MEM, 1); else res = platform_get_resource_byname(pdev, -- cgit v1.2.3 From 5e0531f6b90ac096fedaf5bd0eae0bb4e5a39da5 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Wed, 7 Sep 2022 16:11:25 +0200 Subject: spi: Add capability to perform some transfer with chipselect off Some components require a few clock cycles with chipselect off before or/and after the data transfer done with CS on. Typically IDT 801034 QUAD PCM CODEC datasheet states "Note *: CCLK should have one cycle before CS goes low, and two cycles after CS goes high". The cycles "before" are implicitely provided by all previous activity on the SPI bus. But the cycles "after" must be provided in order to terminate the SPI transfer. In order to use that kind of component, add a cs_off flag to spi_transfer struct. When this flag is set, the transfer is performed with chipselect off. This allows consummer to add a dummy transfer at the end of the transfer list which is performed with chipselect OFF, providing the required additional clock cycles. Signed-off-by: Christophe Leroy Link: https://lore.kernel.org/r/434165c46f06d802690208a11e7ea2500e8da4c7.1662558898.git.christophe.leroy@csgroup.eu Signed-off-by: Mark Brown --- drivers/spi/spi.c | 12 +++++++++--- include/linux/spi/spi.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 97487e1f27b5..c582ae4deab3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1435,7 +1435,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, struct spi_statistics __percpu *statm = ctlr->pcpu_statistics; struct spi_statistics __percpu *stats = msg->spi->pcpu_statistics; - spi_set_cs(msg->spi, true, false); + xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list); + spi_set_cs(msg->spi, !xfer->cs_off, false); SPI_STATISTICS_INCREMENT_FIELD(statm, messages); SPI_STATISTICS_INCREMENT_FIELD(stats, messages); @@ -1503,10 +1504,15 @@ fallback_pio: &msg->transfers)) { keep_cs = true; } else { - spi_set_cs(msg->spi, false, false); + if (!xfer->cs_off) + spi_set_cs(msg->spi, false, false); _spi_transfer_cs_change_delay(msg, xfer); - spi_set_cs(msg->spi, true, false); + if (!list_next_entry(xfer, transfer_list)->cs_off) + spi_set_cs(msg->spi, true, false); } + } else if (!list_is_last(&xfer->transfer_list, &msg->transfers) && + xfer->cs_off != list_next_entry(xfer, transfer_list)->cs_off) { + spi_set_cs(msg->spi, xfer->cs_off, false); } msg->actual_length += xfer->len; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e6c73d5ff1a8..6e6c62c59957 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -847,6 +847,7 @@ struct spi_res { * for this transfer. If 0 the default (from @spi_device) is used. * @dummy_data: indicates transfer is dummy bytes transfer. * @cs_change: affects chipselect after this transfer completes + * @cs_off: performs the transfer with chipselect off. * @cs_change_delay: delay between cs deassert and assert when * @cs_change is set and @spi_transfer is not the last in @spi_message * @delay: delay to be introduced after this transfer before @@ -959,6 +960,7 @@ struct spi_transfer { unsigned cs_change:1; unsigned tx_nbits:3; unsigned rx_nbits:3; + unsigned cs_off:1; #define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */ #define SPI_NBITS_DUAL 0x02 /* 2bits transfer */ #define SPI_NBITS_QUAD 0x04 /* 4bits transfer */ -- cgit v1.2.3 From 014eac3e93515ef37a794b1880340efd616d5768 Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Thu, 8 Sep 2022 01:04:29 +0000 Subject: spi: lpspi: Remove the unneeded result variable Return the value pm_runtime_force_suspend() directly instead of storing it in another redundant variable. Reported-by: Zeal Robot Signed-off-by: ye xingchen Link: https://lore.kernel.org/r/20220908010429.342875-1-ye.xingchen@zte.com.cn Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index cbbe8bbef90a..13147377b2d6 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -947,11 +947,8 @@ static int fsl_lpspi_remove(struct platform_device *pdev) static int __maybe_unused fsl_lpspi_suspend(struct device *dev) { - int ret; - pinctrl_pm_select_sleep_state(dev); - ret = pm_runtime_force_suspend(dev); - return ret; + return pm_runtime_force_suspend(dev); } static int __maybe_unused fsl_lpspi_resume(struct device *dev) -- cgit v1.2.3 From 36acf80fc0c4b5ebe6fa010b524d442ee7f08fd3 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 8 Sep 2022 14:18:03 +0200 Subject: spi: meson-spicc: do not rely on busy flag in pow2 clk ops Since [1], controller's busy flag isn't set anymore when the __spi_transfer_message_noqueue() is used instead of the __spi_pump_transfer_message() logic for spi_sync transfers. Since the pow2 clock ops were limited to only be available when a transfer is ongoing (between prepare_transfer_hardware and unprepare_transfer_hardware callbacks), the only way to track this down is to check for the controller cur_msg. [1] ae7d2346dc89 ("spi: Don't use the message queue if possible in spi_sync") Fixes: 09992025dacd ("spi: meson-spicc: add local pow2 clock ops to preserve rate between messages") Fixes: ae7d2346dc89 ("spi: Don't use the message queue if possible in spi_sync") Reported-by: Markus Schneider-Pargmann Signed-off-by: Neil Armstrong Tested-by: Markus Schneider-Pargmann Link: https://lore.kernel.org/r/20220908121803.919943-1-narmstrong@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spicc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index e4cb52e1fe26..6974a1c947aa 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -537,7 +537,7 @@ static unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw, struct clk_divider *divider = to_clk_divider(hw); struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); - if (!spicc->master->cur_msg || !spicc->master->busy) + if (!spicc->master->cur_msg) return 0; return clk_divider_ops.recalc_rate(hw, parent_rate); @@ -549,7 +549,7 @@ static int meson_spicc_pow2_determine_rate(struct clk_hw *hw, struct clk_divider *divider = to_clk_divider(hw); struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); - if (!spicc->master->cur_msg || !spicc->master->busy) + if (!spicc->master->cur_msg) return -EINVAL; return clk_divider_ops.determine_rate(hw, req); @@ -561,7 +561,7 @@ static int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate, struct clk_divider *divider = to_clk_divider(hw); struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider); - if (!spicc->master->cur_msg || !spicc->master->busy) + if (!spicc->master->cur_msg) return -EINVAL; return clk_divider_ops.set_rate(hw, rate, parent_rate); -- cgit v1.2.3 From 64ca1a034f00bf6366701df0af9194a6425d5406 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 18 Aug 2022 15:38:37 +0200 Subject: spi: fsl_spi: Convert to transfer_one Let the core handle all the chipselect bakery and replace transfer_one_message() by transfer_one() and prepare_message(). At the time being, there is fsl_spi_cs_control() to handle chipselects. That function handles both GPIO and non-GPIO chipselects. The GPIO chipselects will now be handled by the core directly, so only handle non-GPIO chipselects and hook it to ->set_cs Signed-off-by: Christophe Leroy Link: https://lore.kernel.org/r/753266abafe81722d86c3ddb8bac8ef1cb00fe8c.1660829841.git.christophe.leroy@csgroup.eu Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-spi.c | 157 +++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 114 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index bdf94cc7be1a..731624f157fc 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -111,32 +111,6 @@ static void fsl_spi_change_mode(struct spi_device *spi) local_irq_restore(flags); } -static void fsl_spi_chipselect(struct spi_device *spi, int value) -{ - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - struct fsl_spi_platform_data *pdata; - struct spi_mpc8xxx_cs *cs = spi->controller_state; - - pdata = spi->dev.parent->parent->platform_data; - - if (value == BITBANG_CS_INACTIVE) { - if (pdata->cs_control) - pdata->cs_control(spi, false); - } - - if (value == BITBANG_CS_ACTIVE) { - mpc8xxx_spi->rx_shift = cs->rx_shift; - mpc8xxx_spi->tx_shift = cs->tx_shift; - mpc8xxx_spi->get_rx = cs->get_rx; - mpc8xxx_spi->get_tx = cs->get_tx; - - fsl_spi_change_mode(spi); - - if (pdata->cs_control) - pdata->cs_control(spi, true); - } -} - static void fsl_spi_qe_cpu_set_shifts(u32 *rx_shift, u32 *tx_shift, int bits_per_word, int msb_first) { @@ -354,15 +328,11 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t, return mpc8xxx_spi->count; } -static int fsl_spi_do_one_msg(struct spi_master *master, - struct spi_message *m) +static int fsl_spi_prepare_message(struct spi_controller *ctlr, + struct spi_message *m) { - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); - struct spi_device *spi = m->spi; - struct spi_transfer *t, *first; - unsigned int cs_change; - const int nsecs = 50; - int status, last_bpw; + struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(ctlr); + struct spi_transfer *t; /* * In CPU mode, optimize large byte transfers to use larger @@ -378,62 +348,30 @@ static int fsl_spi_do_one_msg(struct spi_master *master, t->bits_per_word = 16; } } + return 0; +} - /* Don't allow changes if CS is active */ - cs_change = 1; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (cs_change) - first = t; - cs_change = t->cs_change; - if (first->speed_hz != t->speed_hz) { - dev_err(&spi->dev, - "speed_hz cannot change while CS is active\n"); - return -EINVAL; - } - } - - last_bpw = -1; - cs_change = 1; - status = -EINVAL; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (cs_change || last_bpw != t->bits_per_word) - status = fsl_spi_setup_transfer(spi, t); - if (status < 0) - break; - last_bpw = t->bits_per_word; - - if (cs_change) { - fsl_spi_chipselect(spi, BITBANG_CS_ACTIVE); - ndelay(nsecs); - } - cs_change = t->cs_change; - if (t->len) - status = fsl_spi_bufs(spi, t, m->is_dma_mapped); - if (status) { - status = -EMSGSIZE; - break; - } - m->actual_length += t->len; - - spi_transfer_delay_exec(t); - - if (cs_change) { - ndelay(nsecs); - fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); - ndelay(nsecs); - } - } +static int fsl_spi_transfer_one(struct spi_controller *controller, + struct spi_device *spi, + struct spi_transfer *t) +{ + int status; - m->status = status; + status = fsl_spi_setup_transfer(spi, t); + if (status < 0) + return status; + if (t->len) + status = fsl_spi_bufs(spi, t, !!t->tx_dma || !!t->rx_dma); + if (status > 0) + return -EMSGSIZE; - if (status || !cs_change) { - ndelay(nsecs); - fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); - } + return status; +} - fsl_spi_setup_transfer(spi, NULL); - spi_finalize_current_message(master); - return 0; +static int fsl_spi_unprepare_message(struct spi_controller *controller, + struct spi_message *msg) +{ + return fsl_spi_setup_transfer(msg->spi, NULL); } static int fsl_spi_setup(struct spi_device *spi) @@ -482,9 +420,6 @@ static int fsl_spi_setup(struct spi_device *spi) return retval; } - /* Initialize chipselect - might be active for SPI_CS_HIGH mode */ - fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE); - return 0; } @@ -557,9 +492,7 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) u32 slvsel; u16 cs = spi->chip_select; - if (spi->cs_gpiod) { - gpiod_set_value(spi->cs_gpiod, on); - } else if (cs < mpc8xxx_spi->native_chipselects) { + if (cs < mpc8xxx_spi->native_chipselects) { slvsel = mpc8xxx_spi_read_reg(®_base->slvsel); slvsel = on ? (slvsel | (1 << cs)) : (slvsel & ~(1 << cs)); mpc8xxx_spi_write_reg(®_base->slvsel, slvsel); @@ -568,7 +501,6 @@ static void fsl_spi_grlib_cs_control(struct spi_device *spi, bool on) static void fsl_spi_grlib_probe(struct device *dev) { - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master = dev_get_drvdata(dev); struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); struct fsl_spi_reg __iomem *reg_base = mpc8xxx_spi->reg_base; @@ -588,7 +520,18 @@ static void fsl_spi_grlib_probe(struct device *dev) mpc8xxx_spi_write_reg(®_base->slvsel, 0xffffffff); } master->num_chipselect = mpc8xxx_spi->native_chipselects; - pdata->cs_control = fsl_spi_grlib_cs_control; + master->set_cs = fsl_spi_grlib_cs_control; +} + +static void fsl_spi_cs_control(struct spi_device *spi, bool on) +{ + struct device *dev = spi->dev.parent->parent; + struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); + struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); + + if (WARN_ON_ONCE(!pinfo->immr_spi_cs)) + return; + iowrite32be(on ? 0 : SPI_BOOT_SEL_BIT, pinfo->immr_spi_cs); } static struct spi_master *fsl_spi_probe(struct device *dev, @@ -613,8 +556,11 @@ static struct spi_master *fsl_spi_probe(struct device *dev, master->setup = fsl_spi_setup; master->cleanup = fsl_spi_cleanup; - master->transfer_one_message = fsl_spi_do_one_msg; + master->prepare_message = fsl_spi_prepare_message; + master->transfer_one = fsl_spi_transfer_one; + master->unprepare_message = fsl_spi_unprepare_message; master->use_gpio_descriptors = true; + master->set_cs = fsl_spi_cs_control; mpc8xxx_spi = spi_master_get_devdata(master); mpc8xxx_spi->max_bits_per_word = 32; @@ -688,21 +634,6 @@ err: return ERR_PTR(ret); } -static void fsl_spi_cs_control(struct spi_device *spi, bool on) -{ - if (spi->cs_gpiod) { - gpiod_set_value(spi->cs_gpiod, on); - } else { - struct device *dev = spi->dev.parent->parent; - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); - struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata); - - if (WARN_ON_ONCE(!pinfo->immr_spi_cs)) - return; - iowrite32be(on ? 0 : SPI_BOOT_SEL_BIT, pinfo->immr_spi_cs); - } -} - static int of_fsl_spi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; @@ -744,12 +675,10 @@ static int of_fsl_spi_probe(struct platform_device *ofdev) ret = gpiod_count(dev, "cs"); if (ret < 0) ret = 0; - if (ret == 0 && !spisel_boot) { + if (ret == 0 && !spisel_boot) pdata->max_chipselect = 1; - } else { + else pdata->max_chipselect = ret + spisel_boot; - pdata->cs_control = fsl_spi_cs_control; - } } ret = of_address_to_resource(np, 0, &mem); -- cgit v1.2.3 From a0c4b120431172490793fb21d43c908b35fd3e50 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Sep 2022 18:33:33 +0300 Subject: spi: mpc52xx: Replace of_gpio_count() by gpiod_count() As a preparation to unexport of_gpio_named_count(), convert the driver to use gpiod_count() instead. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220914153333.37701-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index bc5e36fd4288..97cdd6545ee1 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -438,7 +439,7 @@ static int mpc52xx_spi_probe(struct platform_device *op) ms->irq1 = irq_of_parse_and_map(op->dev.of_node, 1); ms->state = mpc52xx_spi_fsmstate_idle; ms->ipb_freq = mpc5xxx_get_bus_frequency(&op->dev); - ms->gpio_cs_count = of_gpio_count(op->dev.of_node); + ms->gpio_cs_count = gpiod_count(&op->dev, NULL); if (ms->gpio_cs_count > 0) { master->num_chipselect = ms->gpio_cs_count; ms->gpio_cs = kmalloc_array(ms->gpio_cs_count, -- cgit v1.2.3 From f4ca8c88c2c7ea3ea17c6fdfcc1af4b007403833 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 17 Sep 2022 20:25:04 +0800 Subject: spi: omap2-mcspi: Switch to use dev_err_probe() helper In the probe path, dev_err() can be replace with dev_err_probe() which will check if error code is -EPROBE_DEFER and prints the error name. Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20220917122504.1896302-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index c42e59df38fe..80e79f3aa0c4 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1509,10 +1509,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev) } status = platform_get_irq(pdev, 0); - if (status == -EPROBE_DEFER) - goto free_master; - if (status < 0) { - dev_err(&pdev->dev, "no irq resource found\n"); + if (status) { + dev_err_probe(&pdev->dev, status, "no irq resource found\n"); goto free_master; } init_completion(&mcspi->txdone); -- cgit v1.2.3 From b85ad8a54e0a446b3daa7f526e4996ddb6d4373f Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Fri, 16 Sep 2022 13:39:48 +0200 Subject: spi: spi-loopback-test: Add test to trigger DMA/PIO mixing Add a test where a small and a large transfer in a message hit the same cache line. This test currently fails on spi-s3c64xx on in DMA mode since it ends up mixing DMA and PIO without proper cache maintenance. Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20220916113951.228398-2-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index 4d4f77a186a9..dd7de8fa37d0 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -313,6 +313,33 @@ static struct spi_test spi_tests[] = { }, }, }, + { + .description = "three tx+rx transfers with overlapping cache lines", + .fill_option = FILL_COUNT_8, + /* + * This should be large enough for the controller driver to + * choose to transfer it with DMA. + */ + .iterate_len = { 512, -1 }, + .iterate_transfer_mask = BIT(1), + .transfer_count = 3, + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + .rx_buf = RX(0), + }, + { + .tx_buf = TX(1), + .rx_buf = RX(1), + }, + { + .len = 1, + .tx_buf = TX(513), + .rx_buf = RX(513), + }, + }, + }, { /* end of tests sequence */ } }; -- cgit v1.2.3 From e0437512081282559f5c50591f487149c31f867c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 21 Sep 2022 10:24:51 +0300 Subject: spi: omap2-mcspi: Fix probe so driver works again This condition was accidentally changed from "if (status < 0)" to "if (status)". The platform_get_irq() function returns non-zero positive values on success so, unfortunately, the driver could not be used. Change the condition back to how it was. Fixes: f4ca8c88c2c7 ("spi: omap2-mcspi: Switch to use dev_err_probe() helper") Signed-off-by: Dan Carpenter Reviewed-by: Yang Yingliang Link: https://lore.kernel.org/r/Yyq8Q/kd301wVzg8@kili Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 80e79f3aa0c4..6ba9b0d7710b 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1509,7 +1509,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) } status = platform_get_irq(pdev, 0); - if (status) { + if (status < 0) { dev_err_probe(&pdev->dev, status, "no irq resource found\n"); goto free_master; } -- cgit v1.2.3 From 077dac343b54babfd56b1a52cf1f091518118957 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 22 Sep 2022 04:08:07 +0000 Subject: spi: meson-spicc: make symbol 'meson_spicc_pow2_clk_ops' static The sparse tool complains as follows: drivers/spi/spi-meson-spicc.c:570:22: warning: symbol 'meson_spicc_pow2_clk_ops' was not declared. Should it be static? This symbol is not used outside of spi-meson-spicc.c, so marks it static. Signed-off-by: Wei Yongjun Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20220922040807.1409540-1-weiyongjun@huaweicloud.com Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spicc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c index 6974a1c947aa..bad201510a99 100644 --- a/drivers/spi/spi-meson-spicc.c +++ b/drivers/spi/spi-meson-spicc.c @@ -567,7 +567,7 @@ static int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate, return clk_divider_ops.set_rate(hw, rate, parent_rate); } -const struct clk_ops meson_spicc_pow2_clk_ops = { +static const struct clk_ops meson_spicc_pow2_clk_ops = { .recalc_rate = meson_spicc_pow2_recalc_rate, .determine_rate = meson_spicc_pow2_determine_rate, .set_rate = meson_spicc_pow2_set_rate, -- cgit v1.2.3 From 478cc2fc3dd782f7935bc0ab84c198691ea83fa3 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 20 Sep 2022 19:44:48 +0800 Subject: spi: xtensa-xtfpga: Switch to use devm_spi_alloc_master() Switch to use devm_spi_alloc_master() to simpify error path. Signed-off-by: Yang Yingliang Tested-by: Max Filippov Acked-by: Max Filippov Link: https://lore.kernel.org/r/20220920114448.2681053-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-xtensa-xtfpga.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c index fc2b5eb7d614..2fa7608f94cd 100644 --- a/drivers/spi/spi-xtensa-xtfpga.c +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -83,7 +83,7 @@ static int xtfpga_spi_probe(struct platform_device *pdev) int ret; struct spi_master *master; - master = spi_alloc_master(&pdev->dev, sizeof(struct xtfpga_spi)); + master = devm_spi_alloc_master(&pdev->dev, sizeof(struct xtfpga_spi)); if (!master) return -ENOMEM; @@ -97,30 +97,24 @@ static int xtfpga_spi_probe(struct platform_device *pdev) xspi->bitbang.chipselect = xtfpga_spi_chipselect; xspi->bitbang.txrx_word[SPI_MODE_0] = xtfpga_spi_txrx_word; xspi->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(xspi->regs)) { - ret = PTR_ERR(xspi->regs); - goto err; - } + if (IS_ERR(xspi->regs)) + return PTR_ERR(xspi->regs); xtfpga_spi_write32(xspi, XTFPGA_SPI_START, 0); usleep_range(1000, 2000); if (xtfpga_spi_read32(xspi, XTFPGA_SPI_BUSY)) { dev_err(&pdev->dev, "Device stuck in busy state\n"); - ret = -EBUSY; - goto err; + return -EBUSY; } ret = spi_bitbang_start(&xspi->bitbang); if (ret < 0) { dev_err(&pdev->dev, "spi_bitbang_start failed\n"); - goto err; + return ret; } platform_set_drvdata(pdev, master); return 0; -err: - spi_master_put(master); - return ret; } static int xtfpga_spi_remove(struct platform_device *pdev) -- cgit v1.2.3 From 9671847f93a5291ad85f88210fb56e1a946b757b Mon Sep 17 00:00:00 2001 From: Shang XiaoJing Date: Fri, 23 Sep 2022 18:17:26 +0800 Subject: spi: cadence: Remove redundant dev_err call devm_ioremap_resource() prints error message in itself. Remove the dev_err call to avoid redundant error message. Signed-off-by: Shang XiaoJing Link: https://lore.kernel.org/r/20220923101726.19420-1-shangxiaojing@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-xspi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c index 3ab19be83095..9e187f9c6c95 100644 --- a/drivers/spi/spi-cadence-xspi.c +++ b/drivers/spi/spi-cadence-xspi.c @@ -565,10 +565,8 @@ static int cdns_xspi_probe(struct platform_device *pdev) 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"); + if (IS_ERR(cdns_xspi->sdmabase)) return PTR_ERR(cdns_xspi->sdmabase); - } cdns_xspi->sdmasize = resource_size(res); cdns_xspi->auxbase = devm_platform_ioremap_resource_byname(pdev, "aux"); -- cgit v1.2.3 From 2f3a896b0a416bbda633c98212f6490cfcfff310 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 22 Sep 2022 21:51:37 -0700 Subject: spi: spi-mpc52xx: switch to using gpiod API This switches the driver to use gpiod API instead of legacy gpio API, which will brings us close to removing of_get_gpio() and other OF-specific old APIs. No functional change intended beyond some differences in error messages. Signed-off-by: Dmitry Torokhov Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/Yy07WbMAG4bPgYNd@google.com Signed-off-by: Mark Brown --- drivers/spi/spi-mpc52xx.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 97cdd6545ee1..cb075c1acbee 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -18,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -90,7 +90,7 @@ struct mpc52xx_spi { const u8 *tx_buf; int cs_change; int gpio_cs_count; - unsigned int *gpio_cs; + struct gpio_desc **gpio_cs; }; /* @@ -102,9 +102,10 @@ static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value) if (ms->gpio_cs_count > 0) { cs = ms->message->spi->chip_select; - gpio_set_value(ms->gpio_cs[cs], value ? 0 : 1); - } else + gpiod_set_value(ms->gpio_cs[cs], value); + } else { out_8(ms->regs + SPI_PORTDATA, value ? 0 : 0x08); + } } /* @@ -386,10 +387,10 @@ static int mpc52xx_spi_probe(struct platform_device *op) { struct spi_master *master; struct mpc52xx_spi *ms; + struct gpio_desc *gpio_cs; void __iomem *regs; u8 ctrl1; int rc, i = 0; - int gpio_cs; /* MMIO registers */ dev_dbg(&op->dev, "probing mpc5200 SPI device\n"); @@ -451,23 +452,16 @@ static int mpc52xx_spi_probe(struct platform_device *op) } for (i = 0; i < ms->gpio_cs_count; i++) { - gpio_cs = of_get_gpio(op->dev.of_node, i); - if (!gpio_is_valid(gpio_cs)) { - dev_err(&op->dev, - "could not parse the gpio field in oftree\n"); - rc = -ENODEV; - goto err_gpio; - } - - rc = gpio_request(gpio_cs, dev_name(&op->dev)); + gpio_cs = gpiod_get_index(&op->dev, + NULL, i, GPIOD_OUT_LOW); + rc = PTR_ERR_OR_ZERO(gpio_cs); if (rc) { dev_err(&op->dev, - "can't request spi cs gpio #%d on gpio line %d\n", - i, gpio_cs); + "failed to get spi cs gpio #%d: %d\n", + i, rc); goto err_gpio; } - gpio_direction_output(gpio_cs, 1); ms->gpio_cs[i] = gpio_cs; } } @@ -508,7 +502,7 @@ static int mpc52xx_spi_probe(struct platform_device *op) dev_err(&ms->master->dev, "initialization failed\n"); err_gpio: while (i-- > 0) - gpio_free(ms->gpio_cs[i]); + gpiod_put(ms->gpio_cs[i]); kfree(ms->gpio_cs); err_alloc_gpio: @@ -529,7 +523,7 @@ static int mpc52xx_spi_remove(struct platform_device *op) free_irq(ms->irq1, ms); for (i = 0; i < ms->gpio_cs_count; i++) - gpio_free(ms->gpio_cs[i]); + gpiod_put(ms->gpio_cs[i]); kfree(ms->gpio_cs); spi_unregister_master(master); -- cgit v1.2.3 From 04e0456f778de550a14d222d1a9ae0625511244d Mon Sep 17 00:00:00 2001 From: Shang XiaoJing Date: Fri, 23 Sep 2022 18:16:32 +0800 Subject: spi: aspeed: Remove redundant dev_err call devm_ioremap_resource() prints error message in itself. Remove the dev_err call to avoid redundant error message. Signed-off-by: Shang XiaoJing Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/r/20220923101632.19170-1-shangxiaojing@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-aspeed-smc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 3e891bf22470..a334e89add86 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -736,10 +736,8 @@ static int aspeed_spi_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); aspi->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(aspi->regs)) { - dev_err(dev, "missing AHB register window\n"); + if (IS_ERR(aspi->regs)) return PTR_ERR(aspi->regs); - } res = platform_get_resource(pdev, IORESOURCE_MEM, 1); aspi->ahb_base = devm_ioremap_resource(dev, res); -- cgit v1.2.3 From c79ce0a2824bc987ee4cd19f6a0a4e1eb493a8d8 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Thu, 22 Sep 2022 23:02:32 +0800 Subject: spi: img-spfi: using pm_runtime_resume_and_get instead of pm_runtime_get_sync Using the newest pm_runtime_resume_and_get is more appropriate for simplifing code here. Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20220922150232.115843-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 71376b6df89d..bfd12247f173 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -730,11 +730,9 @@ static int img_spfi_resume(struct device *dev) struct img_spfi *spfi = spi_master_get_devdata(master); int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) return ret; - } spfi_reset(spfi); pm_runtime_put(dev); -- cgit v1.2.3 From 2d0645817436ed2e527d967701ee354630d43e94 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 20 Sep 2022 19:46:15 +0800 Subject: spi: xilinx: Switch to use devm_spi_alloc_master() Switch to use devm_spi_alloc_master() to simpify error path. Signed-off-by: Yang Yingliang Reviewed-by: Lukas Wunner Link: https://lore.kernel.org/r/20220920114615.2681751-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-xilinx.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 523edfdf5dcd..7377d3b81302 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -421,7 +421,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) return -EINVAL; } - master = spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi)); + master = devm_spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi)); if (!master) return -ENODEV; @@ -439,10 +439,8 @@ static int xilinx_spi_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); xspi->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(xspi->regs)) { - ret = PTR_ERR(xspi->regs); - goto put_master; - } + if (IS_ERR(xspi->regs)) + return PTR_ERR(xspi->regs); master->bus_num = pdev->id; master->num_chipselect = num_cs; @@ -472,14 +470,13 @@ static int xilinx_spi_probe(struct platform_device *pdev) xspi->irq = platform_get_irq(pdev, 0); if (xspi->irq < 0 && xspi->irq != -ENXIO) { - ret = xspi->irq; - goto put_master; + return xspi->irq; } else if (xspi->irq >= 0) { /* Register for SPI Interrupt */ ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0, dev_name(&pdev->dev), xspi); if (ret) - goto put_master; + return ret; } /* SPI controller initializations */ @@ -488,7 +485,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) ret = spi_bitbang_start(&xspi->bitbang); if (ret) { dev_err(&pdev->dev, "spi_bitbang_start FAILED\n"); - goto put_master; + return ret; } dev_info(&pdev->dev, "at %pR, irq=%d\n", res, xspi->irq); @@ -500,11 +497,6 @@ static int xilinx_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); return 0; - -put_master: - spi_master_put(master); - - return ret; } static int xilinx_spi_remove(struct platform_device *pdev) -- cgit v1.2.3 From a6bfc42f30d11f22d2dacb2362d6069643b15393 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Tue, 20 Sep 2022 22:22:16 +0800 Subject: spi: s3c24xx: Switch to use devm_spi_alloc_master() Switch to use devm_spi_alloc_master() to simpify error path. Signed-off-by: Yang Yingliang Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220920142216.3002291-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 660aa866af06..ef25b5e93900 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -449,7 +449,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) struct spi_master *master; int err = 0; - master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); + master = devm_spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); if (master == NULL) { dev_err(&pdev->dev, "No memory for spi_master\n"); return -ENOMEM; @@ -463,8 +463,7 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) if (pdata == NULL) { dev_err(&pdev->dev, "No platform data supplied\n"); - err = -ENOENT; - goto err_no_pdata; + return -ENOENT; } platform_set_drvdata(pdev, hw); @@ -499,29 +498,24 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) /* find and map our resources */ hw->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(hw->regs)) { - err = PTR_ERR(hw->regs); - goto err_no_pdata; - } + if (IS_ERR(hw->regs)) + return PTR_ERR(hw->regs); hw->irq = platform_get_irq(pdev, 0); - if (hw->irq < 0) { - err = -ENOENT; - goto err_no_pdata; - } + if (hw->irq < 0) + return -ENOENT; err = devm_request_irq(&pdev->dev, hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); if (err) { dev_err(&pdev->dev, "Cannot claim IRQ\n"); - goto err_no_pdata; + return err; } hw->clk = devm_clk_get(&pdev->dev, "spi"); if (IS_ERR(hw->clk)) { dev_err(&pdev->dev, "No clock for device\n"); - err = PTR_ERR(hw->clk); - goto err_no_pdata; + return PTR_ERR(hw->clk); } s3c24xx_spi_initialsetup(hw); @@ -539,8 +533,6 @@ static int s3c24xx_spi_probe(struct platform_device *pdev) err_register: clk_disable(hw->clk); - err_no_pdata: - spi_master_put(hw->master); return err; } -- cgit v1.2.3 From 4d0ef0a1c35189a6e8377d8ee8310ea5ef22c5f3 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Sat, 24 Sep 2022 20:13:07 +0800 Subject: spi: cadence-quadspi: Fix PM disable depth imbalance in cqspi_probe The pm_runtime_enable will increase power disable depth. Thus a pairing decrement is needed on the error handling path to keep it balanced according to context. Fixes:73d5fe0462702 ("spi: cadence-quadspi: Remove spi_master_put() in probe failure path") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20220924121310.78331-2-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 72b1a5a2298c..106c09ffa425 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1619,7 +1619,7 @@ static int cqspi_probe(struct platform_device *pdev) pm_runtime_enable(dev); ret = pm_runtime_resume_and_get(dev); if (ret < 0) - return ret; + goto probe_pm_failed; ret = clk_prepare_enable(cqspi->clk); if (ret) { @@ -1712,6 +1712,7 @@ probe_reset_failed: clk_disable_unprepare(cqspi->clk); probe_clk_failed: pm_runtime_put_sync(dev); +probe_pm_failed: pm_runtime_disable(dev); return ret; } -- cgit v1.2.3 From 618d815fc93477b1675878f3c04ff32657cc18b4 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Sat, 24 Sep 2022 20:13:08 +0800 Subject: spi: dw: Fix PM disable depth imbalance in dw_spi_bt1_probe The pm_runtime_enable will increase power disable depth. Thus a pairing decrement is needed on the error handling path to keep it balanced according to context. Fixes:abf00907538e2 ("spi: dw: Add Baikal-T1 SPI Controller glue driver") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20220924121310.78331-3-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-bt1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c index c06553416123..3fb89dee595e 100644 --- a/drivers/spi/spi-dw-bt1.c +++ b/drivers/spi/spi-dw-bt1.c @@ -293,8 +293,10 @@ static int dw_spi_bt1_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = dw_spi_add_host(&pdev->dev, dws); - if (ret) + if (ret) { + pm_runtime_disable(&pdev->dev); goto err_disable_clk; + } platform_set_drvdata(pdev, dwsbt1); -- cgit v1.2.3 From 29f65f2171c85a9633daa380df14009a365f42f2 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Sat, 24 Sep 2022 20:13:09 +0800 Subject: spi/omap100k:Fix PM disable depth imbalance in omap1_spi100k_probe The pm_runtime_enable will increase power disable depth. Thus a pairing decrement is needed on the error handling path to keep it balanced according to context. Fixes:db91841b58f9a ("spi/omap100k: Convert to runtime PM") Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20220924121310.78331-4-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-omap-100k.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 20b047172965..061f7394e5b9 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -412,6 +412,7 @@ static int omap1_spi100k_probe(struct platform_device *pdev) return status; err_fck: + pm_runtime_disable(&pdev->dev); clk_disable_unprepare(spi100k->fck); err_ick: clk_disable_unprepare(spi100k->ick); -- cgit v1.2.3 From f96087a38cca6f3bb4c7cf582b949016aeb59d0e Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 24 Sep 2022 21:18:52 +0800 Subject: spi: spi-fsl-dspi: Use devm_platform_get_and_ioremap_resource() Use the devm_platform_get_and_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20220924131854.964923-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index fd004c9db9dc..a33e547b7d39 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1294,8 +1294,7 @@ static int dspi_probe(struct platform_device *pdev) else ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) { ret = PTR_ERR(base); goto out_ctlr_put; -- cgit v1.2.3 From c9e1bb724d884b12a4c0d1dc9f802946cf427a92 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 24 Sep 2022 21:18:53 +0800 Subject: spi: spi-fsl-lpspi: Use devm_platform_get_and_ioremap_resource() Use the devm_platform_get_and_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20220924131854.964923-2-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 13147377b2d6..e8c1c8a4c6c8 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -855,8 +855,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) init_completion(&fsl_lpspi->xfer_done); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fsl_lpspi->base = devm_ioremap_resource(&pdev->dev, res); + fsl_lpspi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(fsl_lpspi->base)) { ret = PTR_ERR(fsl_lpspi->base); goto out_controller_put; -- cgit v1.2.3 From fc13b5a25e18b0de5e04b6f5616c60d71d2610ee Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Sat, 24 Sep 2022 21:18:54 +0800 Subject: spi: spi-fsl-qspi: Use devm_platform_ioremap_resource_byname() Use the devm_platform_ioremap_resource_byname() helper instead of calling platform_get_resource_byname() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20220924131854.964923-3-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-qspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index 46ae46a944c5..85cc71ba624a 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -867,8 +867,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, q); /* find the resources */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); - q->iobase = devm_ioremap_resource(dev, res); + q->iobase = devm_platform_ioremap_resource_byname(pdev, "QuadSPI"); if (IS_ERR(q->iobase)) { ret = PTR_ERR(q->iobase); goto err_put_ctrl; -- cgit v1.2.3 From 309e98548c2b144512d0a212f2d786ae9694f5e4 Mon Sep 17 00:00:00 2001 From: zhichao.liu Date: Tue, 27 Sep 2022 16:32:48 +0800 Subject: spi: mt65xx: Add dma max segment size declaration Add spi dma max segment size declaration according to spi hardware capability, instead of 64KB by system default setting, to improve bus bandwidth for mass data transmission. Signed-off-by: zhichao.liu Link: https://lore.kernel.org/r/20220927083248.25404-1-zhichao.liu@mediatek.com Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 0a3b9f7eed30..11aeae7fe7fc 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -1184,6 +1184,11 @@ static int mtk_spi_probe(struct platform_device *pdev) if (!dev->dma_mask) dev->dma_mask = &dev->coherent_dma_mask; + if (mdata->dev_comp->ipm_design) + dma_set_max_seg_size(dev, SZ_16M); + else + dma_set_max_seg_size(dev, SZ_256K); + ret = devm_request_irq(dev, irq, mtk_spi_interrupt, IRQF_TRIGGER_NONE, dev_name(dev), master); if (ret) -- cgit v1.2.3 From f25723dcef4a38f6a39e17afeabd1adf6402230e Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 27 Sep 2022 13:21:14 +0200 Subject: spi: Save current RX and TX DMA devices Save the current RX and TX DMA devices to avoid having to duplicate the logic to pick them, since we'll need access to them in some more functions to fix a bug in the cache handling. Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20220927112117.77599-2-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 19 ++++--------------- include/linux/spi/spi.h | 4 ++++ 2 files changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c582ae4deab3..a492a2a16fed 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1147,6 +1147,8 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) } } + ctlr->cur_rx_dma_dev = rx_dev; + ctlr->cur_tx_dma_dev = tx_dev; ctlr->cur_msg_mapped = true; return 0; @@ -1154,26 +1156,13 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg) { + struct device *rx_dev = ctlr->cur_rx_dma_dev; + struct device *tx_dev = ctlr->cur_tx_dma_dev; struct spi_transfer *xfer; - struct device *tx_dev, *rx_dev; if (!ctlr->cur_msg_mapped || !ctlr->can_dma) return 0; - if (ctlr->dma_tx) - tx_dev = ctlr->dma_tx->device->dev; - else if (ctlr->dma_map_dev) - tx_dev = ctlr->dma_map_dev; - else - tx_dev = ctlr->dev.parent; - - if (ctlr->dma_rx) - rx_dev = ctlr->dma_rx->device->dev; - else if (ctlr->dma_map_dev) - rx_dev = ctlr->dma_map_dev; - else - rx_dev = ctlr->dev.parent; - list_for_each_entry(xfer, &msg->transfers, transfer_list) { if (!ctlr->can_dma(ctlr, msg->spi, xfer)) continue; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 19acd4c9c7e3..4b4041e9895a 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -378,6 +378,8 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch * @cleanup: frees controller-specific state * @can_dma: determine whether this controller supports DMA * @dma_map_dev: device which can be used for DMA mapping + * @cur_rx_dma_dev: device which is currently used for RX DMA mapping + * @cur_tx_dma_dev: device which is currently used for TX DMA mapping * @queued: whether this controller is providing an internal message queue * @kworker: pointer to thread struct for message pump * @pump_messages: work struct for scheduling work to the message pump @@ -609,6 +611,8 @@ struct spi_controller { struct spi_device *spi, struct spi_transfer *xfer); struct device *dma_map_dev; + struct device *cur_rx_dma_dev; + struct device *cur_tx_dma_dev; /* * These hooks are for drivers that want to use the generic -- cgit v1.2.3 From 0c17ba73c08ff2690c1eff8df374b6709eed55ce Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 27 Sep 2022 13:21:15 +0200 Subject: spi: Fix cache corruption due to DMA/PIO overlap The SPI core DMA mapping support performs cache management once for the entire message and not between transfers, and this leads to cache corruption if a message has two or more RX transfers with both transfers targeting the same cache line, and the controller driver decides to handle one using DMA and the other using PIO (for example, because one is much larger than the other). Fix it by syncing before/after the actual transfers. This also means that we can skip the sync during the map/unmap of the message. Fixes: 99adef310f68 ("spi: Provide core support for DMA mapping transfers") Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20220927112117.77599-3-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 109 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 21 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index a492a2a16fed..3b7efdd6a694 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1010,9 +1010,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) } #ifdef CONFIG_HAS_DMA -int spi_map_buf(struct spi_controller *ctlr, struct device *dev, - struct sg_table *sgt, void *buf, size_t len, - enum dma_data_direction dir) +static int spi_map_buf_attrs(struct spi_controller *ctlr, struct device *dev, + struct sg_table *sgt, void *buf, size_t len, + enum dma_data_direction dir, unsigned long attrs) { const bool vmalloced_buf = is_vmalloc_addr(buf); unsigned int max_seg_size = dma_get_max_seg_size(dev); @@ -1078,28 +1078,39 @@ int spi_map_buf(struct spi_controller *ctlr, struct device *dev, sg = sg_next(sg); } - ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir); - if (!ret) - ret = -ENOMEM; + ret = dma_map_sgtable(dev, sgt, dir, attrs); if (ret < 0) { sg_free_table(sgt); return ret; } - sgt->nents = ret; - return 0; } -void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev, - struct sg_table *sgt, enum dma_data_direction dir) +int spi_map_buf(struct spi_controller *ctlr, struct device *dev, + struct sg_table *sgt, void *buf, size_t len, + enum dma_data_direction dir) +{ + return spi_map_buf_attrs(ctlr, dev, sgt, buf, len, dir, 0); +} + +static void spi_unmap_buf_attrs(struct spi_controller *ctlr, + struct device *dev, struct sg_table *sgt, + enum dma_data_direction dir, + unsigned long attrs) { if (sgt->orig_nents) { - dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir); + dma_unmap_sgtable(dev, sgt, dir, attrs); sg_free_table(sgt); } } +void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev, + struct sg_table *sgt, enum dma_data_direction dir) +{ + spi_unmap_buf_attrs(ctlr, dev, sgt, dir, 0); +} + static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) { struct device *tx_dev, *rx_dev; @@ -1124,24 +1135,30 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) rx_dev = ctlr->dev.parent; list_for_each_entry(xfer, &msg->transfers, transfer_list) { + /* The sync is done before each transfer. */ + unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC; + if (!ctlr->can_dma(ctlr, msg->spi, xfer)) continue; if (xfer->tx_buf != NULL) { - ret = spi_map_buf(ctlr, tx_dev, &xfer->tx_sg, - (void *)xfer->tx_buf, xfer->len, - DMA_TO_DEVICE); + ret = spi_map_buf_attrs(ctlr, tx_dev, &xfer->tx_sg, + (void *)xfer->tx_buf, + xfer->len, DMA_TO_DEVICE, + attrs); if (ret != 0) return ret; } if (xfer->rx_buf != NULL) { - ret = spi_map_buf(ctlr, rx_dev, &xfer->rx_sg, - xfer->rx_buf, xfer->len, - DMA_FROM_DEVICE); + ret = spi_map_buf_attrs(ctlr, rx_dev, &xfer->rx_sg, + xfer->rx_buf, xfer->len, + DMA_FROM_DEVICE, attrs); if (ret != 0) { - spi_unmap_buf(ctlr, tx_dev, &xfer->tx_sg, - DMA_TO_DEVICE); + spi_unmap_buf_attrs(ctlr, tx_dev, + &xfer->tx_sg, DMA_TO_DEVICE, + attrs); + return ret; } } @@ -1164,17 +1181,52 @@ static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg) return 0; list_for_each_entry(xfer, &msg->transfers, transfer_list) { + /* The sync has already been done after each transfer. */ + unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC; + if (!ctlr->can_dma(ctlr, msg->spi, xfer)) continue; - spi_unmap_buf(ctlr, rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE); - spi_unmap_buf(ctlr, tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); + spi_unmap_buf_attrs(ctlr, rx_dev, &xfer->rx_sg, + DMA_FROM_DEVICE, attrs); + spi_unmap_buf_attrs(ctlr, tx_dev, &xfer->tx_sg, + DMA_TO_DEVICE, attrs); } ctlr->cur_msg_mapped = false; return 0; } + +static void spi_dma_sync_for_device(struct spi_controller *ctlr, + struct spi_transfer *xfer) +{ + struct device *rx_dev = ctlr->cur_rx_dma_dev; + struct device *tx_dev = ctlr->cur_tx_dma_dev; + + if (!ctlr->cur_msg_mapped) + return; + + if (xfer->tx_sg.orig_nents) + dma_sync_sgtable_for_device(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); + if (xfer->rx_sg.orig_nents) + dma_sync_sgtable_for_device(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE); +} + +static void spi_dma_sync_for_cpu(struct spi_controller *ctlr, + struct spi_transfer *xfer) +{ + struct device *rx_dev = ctlr->cur_rx_dma_dev; + struct device *tx_dev = ctlr->cur_tx_dma_dev; + + if (!ctlr->cur_msg_mapped) + return; + + if (xfer->rx_sg.orig_nents) + dma_sync_sgtable_for_cpu(rx_dev, &xfer->rx_sg, DMA_FROM_DEVICE); + if (xfer->tx_sg.orig_nents) + dma_sync_sgtable_for_cpu(tx_dev, &xfer->tx_sg, DMA_TO_DEVICE); +} #else /* !CONFIG_HAS_DMA */ static inline int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) @@ -1187,6 +1239,16 @@ static inline int __spi_unmap_msg(struct spi_controller *ctlr, { return 0; } + +static void spi_dma_sync_for_device(struct spi_controller *ctrl, + struct spi_transfer *xfer) +{ +} + +static void spi_dma_sync_for_cpu(struct spi_controller *ctrl, + struct spi_transfer *xfer) +{ +} #endif /* !CONFIG_HAS_DMA */ static inline int spi_unmap_msg(struct spi_controller *ctlr, @@ -1445,8 +1507,11 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, reinit_completion(&ctlr->xfer_completion); fallback_pio: + spi_dma_sync_for_device(ctlr, xfer); ret = ctlr->transfer_one(ctlr, msg->spi, xfer); if (ret < 0) { + spi_dma_sync_for_cpu(ctlr, xfer); + if (ctlr->cur_msg_mapped && (xfer->error & SPI_TRANS_FAIL_NO_START)) { __spi_unmap_msg(ctlr, msg); @@ -1469,6 +1534,8 @@ fallback_pio: if (ret < 0) msg->status = ret; } + + spi_dma_sync_for_cpu(ctlr, xfer); } else { if (xfer->len) dev_err(&msg->spi->dev, -- cgit v1.2.3 From 8d699ff95534747e394e0830399b8d5dcf03e738 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 27 Sep 2022 13:21:16 +0200 Subject: spi: Split transfers larger than max size A couple of drivers call spi_split_transfers_maxsize() from their ->prepare_message() callbacks to split transfers which are too big for them to handle. Add support in the core to do this based on ->max_transfer_size() to avoid code duplication. Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20220927112117.77599-4-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3b7efdd6a694..a4771f69f5c3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1649,6 +1649,15 @@ static int __spi_pump_transfer_message(struct spi_controller *ctlr, trace_spi_message_start(msg); + ret = spi_split_transfers_maxsize(ctlr, msg, + spi_max_transfer_size(msg->spi), + GFP_KERNEL | GFP_DMA); + if (ret) { + msg->status = ret; + spi_finalize_current_message(ctlr); + return ret; + } + if (ctlr->prepare_message) { ret = ctlr->prepare_message(ctlr, msg); if (ret) { -- cgit v1.2.3 From 1224e29572f655facfcd850cf0f0a4784f36a903 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 27 Sep 2022 13:21:17 +0200 Subject: spi: s3c64xx: Fix large transfers with DMA The COUNT_VALUE in the PACKET_CNT register is 16-bit so the maximum value is 65535. Asking the driver to transfer a larger size currently leads to the DMA transfer timing out. Implement ->max_transfer_size() and have the core split the transfer as needed. Fixes: 230d42d422e7 ("spi: Add s3c64xx SPI Controller driver") Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20220927112117.77599-5-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 651c35dd9124..71d324ec9a70 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -84,6 +84,7 @@ #define S3C64XX_SPI_ST_TX_FIFORDY (1<<0) #define S3C64XX_SPI_PACKET_CNT_EN (1<<16) +#define S3C64XX_SPI_PACKET_CNT_MASK GENMASK(15, 0) #define S3C64XX_SPI_PND_TX_UNDERRUN_CLR (1<<4) #define S3C64XX_SPI_PND_TX_OVERRUN_CLR (1<<3) @@ -711,6 +712,13 @@ static int s3c64xx_spi_prepare_message(struct spi_master *master, return 0; } +static size_t s3c64xx_spi_max_transfer_size(struct spi_device *spi) +{ + struct spi_controller *ctlr = spi->controller; + + return ctlr->can_dma ? S3C64XX_SPI_PACKET_CNT_MASK : SIZE_MAX; +} + static int s3c64xx_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) @@ -1152,6 +1160,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->prepare_message = s3c64xx_spi_prepare_message; master->transfer_one = s3c64xx_spi_transfer_one; + master->max_transfer_size = s3c64xx_spi_max_transfer_size; master->num_chipselect = sci->num_cs; master->use_gpio_descriptors = true; master->dma_alignment = 8; -- cgit v1.2.3 From 28366dd2ecb2c47cfd706a0743dd78f287f1abd7 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Wed, 28 Sep 2022 22:52:56 +0800 Subject: spi: spi-gxp: Use devm_platform_ioremap_resource() Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately. Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20220928145256.1879256-1-yangyingliang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-gxp.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-gxp.c b/drivers/spi/spi-gxp.c index 9ea355f7d64f..15b110183839 100644 --- a/drivers/spi/spi-gxp.c +++ b/drivers/spi/spi-gxp.c @@ -254,7 +254,6 @@ static int gxp_spifi_probe(struct platform_device *pdev) const struct gxp_spi_data *data; struct spi_controller *ctlr; struct gxp_spi *spifi; - struct resource *res; int ret; data = of_device_get_match_data(&pdev->dev); @@ -269,18 +268,15 @@ static int gxp_spifi_probe(struct platform_device *pdev) spifi->data = data; spifi->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - spifi->reg_base = devm_ioremap_resource(&pdev->dev, res); + spifi->reg_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(spifi->reg_base)) return PTR_ERR(spifi->reg_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - spifi->dat_base = devm_ioremap_resource(&pdev->dev, res); + spifi->dat_base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(spifi->dat_base)) return PTR_ERR(spifi->dat_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - spifi->dir_base = devm_ioremap_resource(&pdev->dev, res); + spifi->dir_base = devm_platform_ioremap_resource(pdev, 2); if (IS_ERR(spifi->dir_base)) return PTR_ERR(spifi->dir_base); -- cgit v1.2.3 From 8e9204cddcc3fea9affcfa411715ba4f66e97587 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 30 Sep 2022 13:34:08 +0200 Subject: spi: Ensure that sg_table won't be used after being freed SPI code checks for non-zero sgt->orig_nents to determine if the buffer has been DMA-mapped. Ensure that sg_table is really zeroed after free to avoid potential NULL pointer dereference if the given SPI xfer object is reused again without being DMA-mapped. Fixes: 0c17ba73c08f ("spi: Fix cache corruption due to DMA/PIO overlap") Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20220930113408.19720-1-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index a4771f69f5c3..29a3098cc157 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1102,6 +1102,8 @@ static void spi_unmap_buf_attrs(struct spi_controller *ctlr, if (sgt->orig_nents) { dma_unmap_sgtable(dev, sgt, dir, attrs); sg_free_table(sgt); + sgt->orig_nents = 0; + sgt->nents = 0; } } -- cgit v1.2.3