diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 17b1932445..7413d52ecf 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -228,7 +228,7 @@ static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen, mode = CQSPI_INDIRECT_READ; } else if (dout && !(flags & SPI_XFER_BEGIN)) { /* write */ - if (!CQSPI_IS_ADDR(priv->cmd_len)) + if (!CQSPI_IS_ADDR(priv->cmd_len) || plat->stg_pgm) mode = CQSPI_STIG_WRITE; else mode = CQSPI_INDIRECT_WRITE; @@ -242,9 +242,10 @@ static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen, break; case CQSPI_STIG_WRITE: - err = cadence_qspi_apb_command_write(base, - priv->cmd_len, cmd_buf, - data_bytes, dout); + err = cadence_qspi_apb_command_write(plat, + priv->cmd_len, + cmd_buf, + data_bytes, dout); break; case CQSPI_INDIRECT_READ: err = cadence_qspi_apb_indirect_read_setup(plat, @@ -292,6 +293,7 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus) plat->trigger_address = fdtdec_get_uint(blob, node, "cdns,trigger-address", 0); plat->is_dma = fdtdec_get_bool(blob, node, "cdns,is-dma"); + plat->stg_pgm = fdtdec_get_bool(blob, node, "cdns,is-stig-pgm"); /* All other paramters are embedded in the child node */ subnode = fdt_first_subnode(blob, node); diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index 233793d4dc..de57c7def8 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -30,6 +30,7 @@ struct cadence_spi_platdata { u32 tchsh_ns; u32 tslch_ns; bool is_dma; + bool stg_pgm; }; struct cadence_spi_priv { @@ -52,9 +53,9 @@ void cadence_qspi_apb_controller_disable(void *reg_base_addr); int cadence_qspi_apb_command_read(void *reg_base_addr, unsigned int cmdlen, const u8 *cmdbuf, unsigned int rxlen, u8 *rxbuf); -int cadence_qspi_apb_command_write(void *reg_base_addr, - unsigned int cmdlen, const u8 *cmdbuf, - unsigned int txlen, const u8 *txbuf); +int cadence_qspi_apb_command_write(struct cadence_spi_platdata *plat, + unsigned int cmdlen, const u8 *cmdbuf, + unsigned int txlen, const u8 *txbuf); int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, unsigned int cmdlen, unsigned int rx_width, const u8 *cmdbuf); diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index e7b67c91bd..c96476039b 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -493,20 +493,36 @@ int cadence_qspi_apb_command_read(void *reg_base, } /* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */ -int cadence_qspi_apb_command_write(void *reg_base, unsigned int cmdlen, - const u8 *cmdbuf, unsigned int txlen, const u8 *txbuf) +int cadence_qspi_apb_command_write(struct cadence_spi_platdata *plat, + unsigned int cmdlen, const u8 *cmdbuf, + unsigned int txlen, const u8 *txbuf) { + void *reg_base = plat->regbase; unsigned int reg = 0; - unsigned int addr_value; + unsigned int addr_value = 0; unsigned int wr_data; unsigned int wr_len; + bool pageprgm = false; + unsigned int pgmlen = 0; + int ret; - if (!cmdlen || cmdlen > 5 || txlen > 8 || cmdbuf == NULL) { + if (!cmdlen || cmdlen > 5 || cmdbuf == NULL) { printf("QSPI: Invalid input arguments cmdlen %d txlen %d\n", cmdlen, txlen); return -EINVAL; } + if (txlen > 8) { + if (plat->stg_pgm) { + pageprgm = true; + pgmlen = txlen; + txlen = 8; + } else { + printf("%s Invalid txlen %d\n", __func__, txlen); + return -EINVAL; + } + } + reg |= cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; if (cmdlen == 4 || cmdlen == 5) { @@ -540,10 +556,58 @@ int cadence_qspi_apb_command_write(void *reg_base, unsigned int cmdlen, writel(wr_data, reg_base + CQSPI_REG_CMDWRITEDATAUPPER); } + + if (pageprgm) { + pgmlen -= txlen; + txbuf += wr_len; + addr_value += txlen; + } } /* Execute the command */ - return cadence_qspi_apb_exec_flash_cmd(reg_base, reg); + ret = cadence_qspi_apb_exec_flash_cmd(reg_base, reg); + if (ret) + return ret; + + while (pgmlen) { + reg = 0x6 << CQSPI_REG_CMDCTRL_OPCODE_LSB; + ret = cadence_qspi_apb_exec_flash_cmd(reg_base, reg); + if (ret) + return ret; + + reg = cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; + reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); + reg |= ((cmdlen - 2) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) + << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; + writel(addr_value, reg_base + CQSPI_REG_CMDADDRESS); + + reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB); + reg |= ((txlen - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK) + << CQSPI_REG_CMDCTRL_WR_BYTES_LSB; + wr_len = txlen > 4 ? 4 : txlen; + memcpy(&wr_data, txbuf, wr_len); + writel(wr_data, reg_base + + CQSPI_REG_CMDWRITEDATALOWER); + + if (txlen > 4) { + txbuf += wr_len; + wr_len = txlen - wr_len; + memcpy(&wr_data, txbuf, wr_len); + writel(wr_data, reg_base + + CQSPI_REG_CMDWRITEDATAUPPER); + } + + pgmlen -= txlen; + txbuf += wr_len; + addr_value += txlen; + txlen = pgmlen > 8 ? 8 : pgmlen; + + ret = cadence_qspi_apb_exec_flash_cmd(reg_base, reg); + if (ret) + return ret; + } + + return 0; } /* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */