diff options
-rw-r--r-- | drivers/spi/cadence_qspi.c | 40 | ||||
-rw-r--r-- | drivers/spi/cadence_qspi.h | 19 | ||||
-rw-r--r-- | drivers/spi/cadence_qspi_apb.c | 65 |
3 files changed, 91 insertions, 33 deletions
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c index 86c910a8da..619fff70de 100644 --- a/drivers/spi/cadence_qspi.c +++ b/drivers/spi/cadence_qspi.c @@ -13,12 +13,13 @@ #include <spi.h> #include <spi-mem.h> #include <linux/errno.h> +#include <linux/sizes.h> #include "cadence_qspi.h" #define CQSPI_STIG_READ 0 #define CQSPI_STIG_WRITE 1 -#define CQSPI_INDIRECT_READ 2 -#define CQSPI_INDIRECT_WRITE 3 +#define CQSPI_READ 2 +#define CQSPI_WRITE 3 static int cadence_spi_write_speed(struct udevice *bus, uint hz) { @@ -190,6 +191,7 @@ static int cadence_spi_remove(struct udevice *dev) static int cadence_spi_set_mode(struct udevice *bus, uint mode) { + struct cadence_spi_platdata *plat = bus->platdata; struct cadence_spi_priv *priv = dev_get_priv(bus); /* Disable QSPI */ @@ -198,6 +200,10 @@ static int cadence_spi_set_mode(struct udevice *bus, uint mode) /* Set SPI mode */ cadence_qspi_apb_set_clk_mode(priv->regbase, mode); + /* Enable Direct Access Controller */ + if (plat->use_dac_mode) + cadence_qspi_apb_dac_mode_enable(priv->regbase); + /* Enable QSPI */ cadence_qspi_apb_controller_enable(priv->regbase); @@ -222,12 +228,12 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi, if (!op->addr.nbytes) mode = CQSPI_STIG_READ; else - mode = CQSPI_INDIRECT_READ; + mode = CQSPI_READ; } else { if (!op->addr.nbytes || !op->data.buf.out) mode = CQSPI_STIG_WRITE; else - mode = CQSPI_INDIRECT_WRITE; + mode = CQSPI_WRITE; } switch (mode) { @@ -237,19 +243,15 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi, case CQSPI_STIG_WRITE: err = cadence_qspi_apb_command_write(base, op); break; - case CQSPI_INDIRECT_READ: - err = cadence_qspi_apb_indirect_read_setup(plat, op); - if (!err) { - err = cadence_qspi_apb_indirect_read_execute - (plat, op->data.nbytes, op->data.buf.in); - } + case CQSPI_READ: + err = cadence_qspi_apb_read_setup(plat, op); + if (!err) + err = cadence_qspi_apb_read_execute(plat, op); break; - case CQSPI_INDIRECT_WRITE: - err = cadence_qspi_apb_indirect_write_setup(plat, op); - if (!err) { - err = cadence_qspi_apb_indirect_write_execute - (plat, op->data.nbytes, op->data.buf.out); - } + case CQSPI_WRITE: + err = cadence_qspi_apb_write_setup(plat, op); + if (!err) + err = cadence_qspi_apb_write_execute(plat, op); break; default: err = -1; @@ -267,13 +269,17 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus) int ret; plat->regbase = (void *)devfdt_get_addr_index(bus, 0); - plat->ahbbase = (void *)devfdt_get_addr_index(bus, 1); + plat->ahbbase = (void *)devfdt_get_addr_size_index(bus, 1, + &plat->ahbsize); plat->is_decoded_cs = dev_read_bool(bus, "cdns,is-decoded-cs"); plat->fifo_depth = dev_read_u32_default(bus, "cdns,fifo-depth", 128); plat->fifo_width = dev_read_u32_default(bus, "cdns,fifo-width", 4); plat->trigger_address = dev_read_u32_default(bus, "cdns,trigger-address", 0); + /* Use DAC mode only when MMIO window is at least 8M wide */ + if (plat->ahbsize >= SZ_8M) + plat->use_dac_mode = true; /* All other paramters are embedded in the child node */ subnode = dev_read_first_subnode(bus); diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h index d66201ec92..ae459c74a1 100644 --- a/drivers/spi/cadence_qspi.h +++ b/drivers/spi/cadence_qspi.h @@ -24,6 +24,8 @@ struct cadence_spi_platdata { u32 fifo_depth; u32 fifo_width; u32 trigger_address; + fdt_addr_t ahbsize; + bool use_dac_mode; /* Flash parameters */ u32 page_size; @@ -53,20 +55,21 @@ struct cadence_spi_priv { void cadence_qspi_apb_controller_init(struct cadence_spi_platdata *plat); void cadence_qspi_apb_controller_enable(void *reg_base_addr); void cadence_qspi_apb_controller_disable(void *reg_base_addr); +void cadence_qspi_apb_dac_mode_enable(void *reg_base); int cadence_qspi_apb_command_read(void *reg_base_addr, const struct spi_mem_op *op); int cadence_qspi_apb_command_write(void *reg_base_addr, const struct spi_mem_op *op); -int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, - const struct spi_mem_op *op); -int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, - unsigned int rxlen, u8 *rxbuf); -int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, - const struct spi_mem_op *op); -int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, - unsigned int txlen, const u8 *txbuf); +int cadence_qspi_apb_read_setup(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op); +int cadence_qspi_apb_read_execute(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op); +int cadence_qspi_apb_write_setup(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op); +int cadence_qspi_apb_write_execute(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op); void cadence_qspi_apb_chipselect(void *reg_base, unsigned int chip_select, unsigned int decoder_enable); diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c index 8dd0495dfc..a0e14f93e0 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -27,6 +27,7 @@ #include <common.h> #include <asm/io.h> +#include <dma.h> #include <linux/errno.h> #include <wait_bit.h> #include <spi.h> @@ -189,6 +190,15 @@ void cadence_qspi_apb_controller_disable(void *reg_base) writel(reg, reg_base + CQSPI_REG_CONFIG); } +void cadence_qspi_apb_dac_mode_enable(void *reg_base) +{ + unsigned int reg; + + reg = readl(reg_base + CQSPI_REG_CONFIG); + reg |= CQSPI_REG_CONFIG_DIRECT; + writel(reg, reg_base + CQSPI_REG_CONFIG); +} + /* Return 1 if idle, otherwise return 0 (busy). */ static unsigned int cadence_qspi_wait_idle(void *reg_base) { @@ -512,8 +522,8 @@ int cadence_qspi_apb_command_write(void *reg_base, const struct spi_mem_op *op) } /* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */ -int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, - const struct spi_mem_op *op) +int cadence_qspi_apb_read_setup(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op) { unsigned int reg; unsigned int rd_reg; @@ -577,8 +587,9 @@ static int cadence_qspi_wait_for_data(struct cadence_spi_platdata *plat) return -ETIMEDOUT; } -int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, - unsigned int n_rx, u8 *rxbuf) +static int +cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, + unsigned int n_rx, u8 *rxbuf) { unsigned int remaining = n_rx; unsigned int bytes_to_read = 0; @@ -639,9 +650,29 @@ failrd: return ret; } +int cadence_qspi_apb_read_execute(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op) +{ + u32 from = op->addr.val; + void *buf = op->data.buf.in; + size_t len = op->data.nbytes; + + if (plat->use_dac_mode && (from + len < plat->ahbsize)) { + if (len < 256 || + dma_memcpy(buf, plat->ahbbase + from, len) < 0) { + memcpy_fromio(buf, plat->ahbbase + from, len); + } + if (!cadence_qspi_wait_idle(plat->regbase)) + return -EIO; + return 0; + } + + return cadence_qspi_apb_indirect_read_execute(plat, len, buf); +} + /* Opcode + Address (3/4 bytes) */ -int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, - const struct spi_mem_op *op) +int cadence_qspi_apb_write_setup(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op) { unsigned int reg; @@ -662,8 +693,9 @@ int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, return 0; } -int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, - unsigned int n_tx, const u8 *txbuf) +static int +cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, + unsigned int n_tx, const u8 *txbuf) { unsigned int page_size = plat->page_size; unsigned int remaining = n_tx; @@ -735,6 +767,23 @@ failwr: return ret; } +int cadence_qspi_apb_write_execute(struct cadence_spi_platdata *plat, + const struct spi_mem_op *op) +{ + u32 to = op->addr.val; + const void *buf = op->data.buf.out; + size_t len = op->data.nbytes; + + if (plat->use_dac_mode && (to + len < plat->ahbsize)) { + memcpy_toio(plat->ahbbase + to, buf, len); + if (!cadence_qspi_wait_idle(plat->regbase)) + return -EIO; + return 0; + } + + return cadence_qspi_apb_indirect_write_execute(plat, len, buf); +} + void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy) { unsigned int reg; |