diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 09:19:08 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-22 09:19:08 -0800 |
| commit | a2590d69893f232cbb79d149dbbb456a1febca22 (patch) | |
| tree | 870326105cf0ff810fe9778764b276324fc6b1b9 /drivers/spi/spi-cadence-quadspi.c | |
| parent | d6560052c2f73db59834e9a3c0aba20579aa7059 (diff) | |
| parent | eec262d179ff60e8d12298ab2f118661040e0bf5 (diff) | |
Merge tag 'spi-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"The main focus of this release from a framework point of view has been
spi-mem where we've acquired support for a few new hardware features
which enable better performance on suitable hardware.
Otherwise mostly thanks to Arnd's cleanup efforts on old platforms
we've removed several obsolete drivers which just about balance out
the newer drivers we've added this cycle.
Summary:
- Allow drivers to flag if they are unidirectional.
- Support for DTR mode and hardware acceleration of dummy cycles in
spi-mem.
- Support for Allwinder H616, Intel Lightning Mountain, nVidia Tegra
QuadSPI, Realtek RTL838x and RTL839x.
- Removal of obsolete EFM32, Txx9 and SIRF Prima and Atlas drivers"
* tag 'spi-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (76 commits)
spi: Skip zero-length transfers in spi_transfer_one_message()
spi: dw: Avoid stack content exposure
spi: cadence-quadspi: Use spi_mem_dtr_supports_op()
spi: spi-mem: add spi_mem_dtr_supports_op()
spi: atmel-quadspi: Disable the QSPI IP at suspend()
spi: pxa2xx: Add IDs for the controllers found on Intel Lynxpoint
spi: pxa2xx: Fix the controller numbering for Wildcat Point
spi: Change provied to provided in the file spi.h
spi: mediatek: add set_cs_timing support
spi: support CS timing for HW & SW mode
spi: add power control when set_cs_timing
spi: stm32: make spurious and overrun interrupts visible
spi: stm32h7: replace private SPI_1HZ_NS with NSEC_PER_SEC
spi: stm32: defer probe for reset
spi: stm32: driver uses reset controller only at init
spi: stm32h7: ensure message are smaller than max size
spi: stm32: use bitfield macros
spi: stm32: do not mandate cs_gpio
spi: stm32: properly handle 0 byte transfer
spi: clps711xx: remove redundant white-space
...
Diffstat (limited to 'drivers/spi/spi-cadence-quadspi.c')
| -rw-r--r-- | drivers/spi/spi-cadence-quadspi.c | 333 |
1 files changed, 290 insertions, 43 deletions
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index ba7d40c2922f..442cc7c53a47 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -52,6 +52,7 @@ struct cqspi_flash_pdata { u8 inst_width; u8 addr_width; u8 data_width; + bool dtr; u8 cs; }; @@ -75,6 +76,7 @@ struct cqspi_st { bool is_decoded_cs; u32 fifo_depth; u32 fifo_width; + u32 num_chipselect; bool rclk_en; u32 trigger_address; u32 wr_delay; @@ -111,6 +113,8 @@ struct cqspi_driver_platdata { #define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10 #define CQSPI_REG_CONFIG_DMA_MASK BIT(15) #define CQSPI_REG_CONFIG_BAUD_LSB 19 +#define CQSPI_REG_CONFIG_DTR_PROTO BIT(24) +#define CQSPI_REG_CONFIG_DUAL_OPCODE BIT(30) #define CQSPI_REG_CONFIG_IDLE_LSB 31 #define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF #define CQSPI_REG_CONFIG_BAUD_MASK 0xF @@ -173,6 +177,9 @@ struct cqspi_driver_platdata { #define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF #define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF +#define CQSPI_REG_WR_COMPLETION_CTRL 0x38 +#define CQSPI_REG_WR_DISABLE_AUTO_POLL BIT(14) + #define CQSPI_REG_IRQSTATUS 0x40 #define CQSPI_REG_IRQMASK 0x44 @@ -188,6 +195,7 @@ struct cqspi_driver_platdata { #define CQSPI_REG_CMDCTRL 0x90 #define CQSPI_REG_CMDCTRL_EXECUTE_MASK BIT(0) #define CQSPI_REG_CMDCTRL_INPROGRESS_MASK BIT(1) +#define CQSPI_REG_CMDCTRL_DUMMY_LSB 7 #define CQSPI_REG_CMDCTRL_WR_BYTES_LSB 12 #define CQSPI_REG_CMDCTRL_WR_EN_LSB 15 #define CQSPI_REG_CMDCTRL_ADD_BYTES_LSB 16 @@ -198,6 +206,7 @@ struct cqspi_driver_platdata { #define CQSPI_REG_CMDCTRL_WR_BYTES_MASK 0x7 #define CQSPI_REG_CMDCTRL_ADD_BYTES_MASK 0x3 #define CQSPI_REG_CMDCTRL_RD_BYTES_MASK 0x7 +#define CQSPI_REG_CMDCTRL_DUMMY_MASK 0x1F #define CQSPI_REG_INDIRECTWR 0x70 #define CQSPI_REG_INDIRECTWR_START_MASK BIT(0) @@ -214,6 +223,14 @@ struct cqspi_driver_platdata { #define CQSPI_REG_CMDWRITEDATALOWER 0xA8 #define CQSPI_REG_CMDWRITEDATAUPPER 0xAC +#define CQSPI_REG_POLLING_STATUS 0xB0 +#define CQSPI_REG_POLLING_STATUS_DUMMY_LSB 16 + +#define CQSPI_REG_OP_EXT_LOWER 0xE0 +#define CQSPI_REG_OP_EXT_READ_LSB 24 +#define CQSPI_REG_OP_EXT_WRITE_LSB 16 +#define CQSPI_REG_OP_EXT_STIG_LSB 0 + /* Interrupt status bits */ #define CQSPI_REG_IRQ_MODE_ERR BIT(0) #define CQSPI_REG_IRQ_UNDERFLOW BIT(1) @@ -288,6 +305,80 @@ static unsigned int cqspi_calc_rdreg(struct cqspi_flash_pdata *f_pdata) return rdreg; } +static unsigned int cqspi_calc_dummy(const struct spi_mem_op *op, bool dtr) +{ + unsigned int dummy_clk; + + dummy_clk = op->dummy.nbytes * (8 / op->dummy.buswidth); + if (dtr) + dummy_clk /= 2; + + return dummy_clk; +} + +static int cqspi_set_protocol(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op) +{ + f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE; + f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE; + f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; + f_pdata->dtr = op->data.dtr && op->cmd.dtr && op->addr.dtr; + + switch (op->data.buswidth) { + case 0: + break; + case 1: + f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; + break; + case 2: + f_pdata->data_width = CQSPI_INST_TYPE_DUAL; + break; + case 4: + f_pdata->data_width = CQSPI_INST_TYPE_QUAD; + break; + case 8: + f_pdata->data_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + + /* Right now we only support 8-8-8 DTR mode. */ + if (f_pdata->dtr) { + switch (op->cmd.buswidth) { + case 0: + break; + case 8: + f_pdata->inst_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + + switch (op->addr.buswidth) { + case 0: + break; + case 8: + f_pdata->addr_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + + switch (op->data.buswidth) { + case 0: + break; + case 8: + f_pdata->data_width = CQSPI_INST_TYPE_OCTAL; + break; + default: + return -EINVAL; + } + } + + return 0; +} + static int cqspi_wait_idle(struct cqspi_st *cqspi) { const unsigned int poll_idle_retry = 3; @@ -345,19 +436,85 @@ static int cqspi_exec_flash_cmd(struct cqspi_st *cqspi, unsigned int reg) return cqspi_wait_idle(cqspi); } +static int cqspi_setup_opcode_ext(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op, + unsigned int shift) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + void __iomem *reg_base = cqspi->iobase; + unsigned int reg; + u8 ext; + + if (op->cmd.nbytes != 2) + return -EINVAL; + + /* Opcode extension is the LSB. */ + ext = op->cmd.opcode & 0xff; + + reg = readl(reg_base + CQSPI_REG_OP_EXT_LOWER); + reg &= ~(0xff << shift); + reg |= ext << shift; + writel(reg, reg_base + CQSPI_REG_OP_EXT_LOWER); + + return 0; +} + +static int cqspi_enable_dtr(struct cqspi_flash_pdata *f_pdata, + const struct spi_mem_op *op, unsigned int shift, + bool enable) +{ + struct cqspi_st *cqspi = f_pdata->cqspi; + void __iomem *reg_base = cqspi->iobase; + unsigned int reg; + int ret; + + reg = readl(reg_base + CQSPI_REG_CONFIG); + + /* + * We enable dual byte opcode here. The callers have to set up the + * extension opcode based on which type of operation it is. + */ + if (enable) { + reg |= CQSPI_REG_CONFIG_DTR_PROTO; + reg |= CQSPI_REG_CONFIG_DUAL_OPCODE; + + /* Set up command opcode extension. */ + ret = cqspi_setup_opcode_ext(f_pdata, op, shift); + if (ret) + return ret; + } else { + reg &= ~CQSPI_REG_CONFIG_DTR_PROTO; + reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE; + } + + writel(reg, reg_base + CQSPI_REG_CONFIG); + + return cqspi_wait_idle(cqspi); +} + static int cqspi_command_read(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; u8 *rxbuf = op->data.buf.in; - u8 opcode = op->cmd.opcode; + u8 opcode; size_t n_rx = op->data.nbytes; unsigned int rdreg; unsigned int reg; + unsigned int dummy_clk; size_t read_len; int status; + status = cqspi_set_protocol(f_pdata, op); + if (status) + return status; + + status = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_STIG_LSB, + f_pdata->dtr); + if (status) + return status; + if (!n_rx || n_rx > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) { dev_err(&cqspi->pdev->dev, "Invalid input argument, len %zu rxbuf 0x%p\n", @@ -365,11 +522,24 @@ static int cqspi_command_read(struct cqspi_flash_pdata *f_pdata, return -EINVAL; } + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; + reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; rdreg = cqspi_calc_rdreg(f_pdata); writel(rdreg, reg_base + CQSPI_REG_RD_INSTR); + dummy_clk = cqspi_calc_dummy(op, f_pdata->dtr); + if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) + return -EOPNOTSUPP; + + if (dummy_clk) + reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK) + << CQSPI_REG_CMDCTRL_DUMMY_LSB; + reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); /* 0 means 1 byte. */ @@ -401,12 +571,22 @@ static int cqspi_command_write(struct cqspi_flash_pdata *f_pdata, { struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; - const u8 opcode = op->cmd.opcode; + u8 opcode; const u8 *txbuf = op->data.buf.out; size_t n_tx = op->data.nbytes; unsigned int reg; unsigned int data; size_t write_len; + int ret; + + ret = cqspi_set_protocol(f_pdata, op); + if (ret) + return ret; + + ret = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_STIG_LSB, + f_pdata->dtr); + if (ret) + return ret; if (n_tx > CQSPI_STIG_DATA_LEN_MAX || (n_tx && !txbuf)) { dev_err(&cqspi->pdev->dev, @@ -415,6 +595,14 @@ static int cqspi_command_write(struct cqspi_flash_pdata *f_pdata, return -EINVAL; } + reg = cqspi_calc_rdreg(f_pdata); + writel(reg, reg_base + CQSPI_REG_RD_INSTR); + + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; + reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; if (op->addr.nbytes) { @@ -454,14 +642,27 @@ static int cqspi_read_setup(struct cqspi_flash_pdata *f_pdata, void __iomem *reg_base = cqspi->iobase; unsigned int dummy_clk = 0; unsigned int reg; + int ret; + u8 opcode; + + ret = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_READ_LSB, + f_pdata->dtr); + if (ret) + return ret; - reg = op->cmd.opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; + + reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; reg |= cqspi_calc_rdreg(f_pdata); /* Setup dummy clock cycles */ - dummy_clk = op->dummy.nbytes * 8; + dummy_clk = cqspi_calc_dummy(op, f_pdata->dtr); + if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) - dummy_clk = CQSPI_DUMMY_CLKS_MAX; + return -EOPNOTSUPP; if (dummy_clk) reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) @@ -573,15 +774,43 @@ static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { unsigned int reg; + int ret; struct cqspi_st *cqspi = f_pdata->cqspi; void __iomem *reg_base = cqspi->iobase; + u8 opcode; + + ret = cqspi_enable_dtr(f_pdata, op, CQSPI_REG_OP_EXT_WRITE_LSB, + f_pdata->dtr); + if (ret) + return ret; + + if (f_pdata->dtr) + opcode = op->cmd.opcode >> 8; + else + opcode = op->cmd.opcode; /* Set opcode. */ - reg = op->cmd.opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; + reg = opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; + reg |= f_pdata->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB; + reg |= f_pdata->addr_width << CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB; writel(reg, reg_base + CQSPI_REG_WR_INSTR); reg = cqspi_calc_rdreg(f_pdata); writel(reg, reg_base + CQSPI_REG_RD_INSTR); + if (f_pdata->dtr) { + /* + * Some flashes like the cypress Semper flash expect a 4-byte + * dummy address with the Read SR command in DTR mode, but this + * controller does not support sending address with the Read SR + * command. So, disable write completion polling on the + * controller's side. spi-nor will take care of polling the + * status register. + */ + reg = readl(reg_base + CQSPI_REG_WR_COMPLETION_CTRL); + reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL; + writel(reg, reg_base + CQSPI_REG_WR_COMPLETION_CTRL); + } + reg = readl(reg_base + CQSPI_REG_SIZE); reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; reg |= (op->addr.nbytes - 1); @@ -835,35 +1064,6 @@ static void cqspi_configure(struct cqspi_flash_pdata *f_pdata, cqspi_controller_enable(cqspi, 1); } -static int cqspi_set_protocol(struct cqspi_flash_pdata *f_pdata, - const struct spi_mem_op *op) -{ - f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE; - f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE; - f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; - - if (op->data.dir == SPI_MEM_DATA_IN) { - switch (op->data.buswidth) { - case 1: - f_pdata->data_width = CQSPI_INST_TYPE_SINGLE; - break; - case 2: - f_pdata->data_width = CQSPI_INST_TYPE_DUAL; - break; - case 4: - f_pdata->data_width = CQSPI_INST_TYPE_QUAD; - break; - case 8: - f_pdata->data_width = CQSPI_INST_TYPE_OCTAL; - break; - default: - return -EINVAL; - } - } - - return 0; -} - static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata, const struct spi_mem_op *op) { @@ -881,7 +1081,16 @@ static ssize_t cqspi_write(struct cqspi_flash_pdata *f_pdata, if (ret) return ret; - if (cqspi->use_direct_mode && ((to + len) <= cqspi->ahb_size)) { + /* + * Some flashes like the Cypress Semper flash expect a dummy 4-byte + * address (all 0s) with the read status register command in DTR mode. + * But this controller does not support sending dummy address bytes to + * the flash when it is polling the write completion register in DTR + * mode. So, we can not use direct mode when in DTR mode for writing + * data. + */ + if (!f_pdata->dtr && cqspi->use_direct_mode && + ((to + len) <= cqspi->ahb_size)) { memcpy_toio(cqspi->ahb_base + to, buf, len); return cqspi_wait_idle(cqspi); } @@ -942,7 +1151,7 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata, dma_async_issue_pending(cqspi->rx_chan); if (!wait_for_completion_timeout(&cqspi->rx_dma_complete, - msecs_to_jiffies(len))) { + msecs_to_jiffies(max_t(size_t, len, 500)))) { dmaengine_terminate_sync(cqspi->rx_chan); dev_err(dev, "DMA wait_for_completion_timeout\n"); ret = -ETIMEDOUT; @@ -1010,6 +1219,26 @@ static int cqspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) return ret; } +static bool cqspi_supports_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + bool all_true, all_false; + + all_true = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && + op->data.dtr; + all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr && + !op->data.dtr; + + /* Mixed DTR modes not supported. */ + if (!(all_true || all_false)) + return false; + + if (all_true) + return spi_mem_dtr_supports_op(mem, op); + else + return spi_mem_default_supports_op(mem, op); +} + static int cqspi_of_get_flash_pdata(struct platform_device *pdev, struct cqspi_flash_pdata *f_pdata, struct device_node *np) @@ -1070,6 +1299,9 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi) return -ENXIO; } + if (of_property_read_u32(np, "num-cs", &cqspi->num_chipselect)) + cqspi->num_chipselect = CQSPI_MAX_CHIPSELECT; + cqspi->rclk_en = of_property_read_bool(np, "cdns,rclk-en"); return 0; @@ -1101,10 +1333,12 @@ static void cqspi_controller_init(struct cqspi_st *cqspi) writel(cqspi->fifo_depth * cqspi->fifo_width / 8, cqspi->iobase + CQSPI_REG_INDIRECTWRWATERMARK); - /* Enable Direct Access Controller */ - reg = readl(cqspi->iobase + CQSPI_REG_CONFIG); - reg |= CQSPI_REG_CONFIG_ENB_DIR_ACC_CTRL; - writel(reg, cqspi->iobase + CQSPI_REG_CONFIG); + /* Disable direct access controller */ + if (!cqspi->use_direct_mode) { + reg = readl(cqspi->iobase + CQSPI_REG_CONFIG); + reg &= ~CQSPI_REG_CONFIG_ENB_DIR_ACC_CTRL; + writel(reg, cqspi->iobase + CQSPI_REG_CONFIG); + } cqspi_controller_enable(cqspi, 1); } @@ -1138,6 +1372,7 @@ static const char *cqspi_get_name(struct spi_mem *mem) static const struct spi_controller_mem_ops cqspi_mem_ops = { .exec_op = cqspi_exec_mem_op, .get_name = cqspi_get_name, + .supports_op = cqspi_supports_mem_op, }; static int cqspi_setup_flash(struct cqspi_st *cqspi) @@ -1279,13 +1514,14 @@ static int cqspi_probe(struct platform_device *pdev) reset_control_deassert(rstc_ocp); cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); + master->max_speed_hz = cqspi->master_ref_clk_hz; ddata = of_device_get_match_data(dev); if (ddata) { if (ddata->quirks & CQSPI_NEEDS_WR_DELAY) - cqspi->wr_delay = 5 * DIV_ROUND_UP(NSEC_PER_SEC, + cqspi->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, cqspi->master_ref_clk_hz); if (ddata->hwcaps_mask & CQSPI_SUPPORTS_OCTAL) - master->mode_bits |= SPI_RX_OCTAL; + master->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL; if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE)) cqspi->use_direct_mode = true; } @@ -1302,6 +1538,8 @@ static int cqspi_probe(struct platform_device *pdev) cqspi->current_cs = -1; cqspi->sclk = 0; + master->num_chipselect = cqspi->num_chipselect; + ret = cqspi_setup_flash(cqspi); if (ret) { dev_err(dev, "failed to setup flash parameters %d\n", ret); @@ -1390,6 +1628,10 @@ static const struct cqspi_driver_platdata am654_ospi = { .quirks = CQSPI_NEEDS_WR_DELAY, }; +static const struct cqspi_driver_platdata intel_lgm_qspi = { + .quirks = CQSPI_DISABLE_DAC_MODE, +}; + static const struct of_device_id cqspi_dt_ids[] = { { .compatible = "cdns,qspi-nor", @@ -1403,6 +1645,10 @@ static const struct of_device_id cqspi_dt_ids[] = { .compatible = "ti,am654-ospi", .data = &am654_ospi, }, + { + .compatible = "intel,lgm-qspi", + .data = &intel_lgm_qspi, + }, { /* end of table */ } }; @@ -1427,3 +1673,4 @@ MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>"); MODULE_AUTHOR("Graham Moore <grmoore@opensource.altera.com>"); MODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>"); MODULE_AUTHOR("Vignesh Raghavendra <vigneshr@ti.com>"); +MODULE_AUTHOR("Pratyush Yadav <p.yadav@ti.com>"); |
