From: Siva Durga Prasad Paladugu Date: Sat, 11 Aug 2018 08:52:38 +0000 (+0530) Subject: spi: cadence_qspi: Add support for page program using STIG write X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f8e0279def175a3ea85d0b21dbe8bd5ed232594a;p=thirdparty%2Fu-boot.git spi: cadence_qspi: Add support for page program using STIG write This patch supports page programming using STIG write. This is needed if controller support only STIG writes for page program instead of regular indirect write support. So, this patch gets the controller capability from dt using "cdns,is-stig-pgm" property. This patch also modifies stig write routine to support for page programming. Signed-off-by: Siva Durga Prasad Paladugu Signed-off-by: Michal Simek --- diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 17b1932445e..7413d52ecfb 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 233793d4dce..de57c7def8f 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 e7b67c91bd3..c96476039b1 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) */