1
0
mirror of https://xff.cz/git/u-boot/ synced 2025-09-01 00:32:04 +02:00

spi: cadence-qspi: Add direct mode support

Add support for Direct Access Controller mode of Cadence QSPI. This
allows MMIO access to SPI NOR flash providing better read performance.
Direct mode is only exercised if AHB window size is greater than 8MB.
Support for flash address remapping is also not supported at the moment
and can be added in future.

For better performance, driver uses DMA to copy data from flash in
direct mode using dma_memcpy().

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Tested-by: Simon Goldschmidt <simon.k.r.goldschmidt@gmail.com>
Acked-by: Jagan Teki <jagan@amarulasolutions.com>
This commit is contained in:
Vignesh Raghavendra
2020-01-27 10:36:40 +05:30
committed by Jagan Teki
parent d640772021
commit ffab212123
3 changed files with 91 additions and 33 deletions

View File

@@ -13,12 +13,13 @@
#include <spi.h> #include <spi.h>
#include <spi-mem.h> #include <spi-mem.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sizes.h>
#include "cadence_qspi.h" #include "cadence_qspi.h"
#define CQSPI_STIG_READ 0 #define CQSPI_STIG_READ 0
#define CQSPI_STIG_WRITE 1 #define CQSPI_STIG_WRITE 1
#define CQSPI_INDIRECT_READ 2 #define CQSPI_READ 2
#define CQSPI_INDIRECT_WRITE 3 #define CQSPI_WRITE 3
static int cadence_spi_write_speed(struct udevice *bus, uint hz) 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) 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); struct cadence_spi_priv *priv = dev_get_priv(bus);
/* Disable QSPI */ /* Disable QSPI */
@@ -198,6 +200,10 @@ static int cadence_spi_set_mode(struct udevice *bus, uint mode)
/* Set SPI mode */ /* Set SPI mode */
cadence_qspi_apb_set_clk_mode(priv->regbase, 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 */ /* Enable QSPI */
cadence_qspi_apb_controller_enable(priv->regbase); 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) if (!op->addr.nbytes)
mode = CQSPI_STIG_READ; mode = CQSPI_STIG_READ;
else else
mode = CQSPI_INDIRECT_READ; mode = CQSPI_READ;
} else { } else {
if (!op->addr.nbytes || !op->data.buf.out) if (!op->addr.nbytes || !op->data.buf.out)
mode = CQSPI_STIG_WRITE; mode = CQSPI_STIG_WRITE;
else else
mode = CQSPI_INDIRECT_WRITE; mode = CQSPI_WRITE;
} }
switch (mode) { switch (mode) {
@@ -237,19 +243,15 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
case CQSPI_STIG_WRITE: case CQSPI_STIG_WRITE:
err = cadence_qspi_apb_command_write(base, op); err = cadence_qspi_apb_command_write(base, op);
break; break;
case CQSPI_INDIRECT_READ: case CQSPI_READ:
err = cadence_qspi_apb_indirect_read_setup(plat, op); err = cadence_qspi_apb_read_setup(plat, op);
if (!err) { if (!err)
err = cadence_qspi_apb_indirect_read_execute err = cadence_qspi_apb_read_execute(plat, op);
(plat, op->data.nbytes, op->data.buf.in);
}
break; break;
case CQSPI_INDIRECT_WRITE: case CQSPI_WRITE:
err = cadence_qspi_apb_indirect_write_setup(plat, op); err = cadence_qspi_apb_write_setup(plat, op);
if (!err) { if (!err)
err = cadence_qspi_apb_indirect_write_execute err = cadence_qspi_apb_write_execute(plat, op);
(plat, op->data.nbytes, op->data.buf.out);
}
break; break;
default: default:
err = -1; err = -1;
@@ -267,13 +269,17 @@ static int cadence_spi_ofdata_to_platdata(struct udevice *bus)
int ret; int ret;
plat->regbase = (void *)devfdt_get_addr_index(bus, 0); 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->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_depth = dev_read_u32_default(bus, "cdns,fifo-depth", 128);
plat->fifo_width = dev_read_u32_default(bus, "cdns,fifo-width", 4); plat->fifo_width = dev_read_u32_default(bus, "cdns,fifo-width", 4);
plat->trigger_address = dev_read_u32_default(bus, plat->trigger_address = dev_read_u32_default(bus,
"cdns,trigger-address", "cdns,trigger-address",
0); 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 */ /* All other paramters are embedded in the child node */
subnode = dev_read_first_subnode(bus); subnode = dev_read_first_subnode(bus);

View File

@@ -24,6 +24,8 @@ struct cadence_spi_platdata {
u32 fifo_depth; u32 fifo_depth;
u32 fifo_width; u32 fifo_width;
u32 trigger_address; u32 trigger_address;
fdt_addr_t ahbsize;
bool use_dac_mode;
/* Flash parameters */ /* Flash parameters */
u32 page_size; 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_init(struct cadence_spi_platdata *plat);
void cadence_qspi_apb_controller_enable(void *reg_base_addr); void cadence_qspi_apb_controller_enable(void *reg_base_addr);
void cadence_qspi_apb_controller_disable(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, int cadence_qspi_apb_command_read(void *reg_base_addr,
const struct spi_mem_op *op); const struct spi_mem_op *op);
int cadence_qspi_apb_command_write(void *reg_base_addr, int cadence_qspi_apb_command_write(void *reg_base_addr,
const struct spi_mem_op *op); const struct spi_mem_op *op);
int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, int cadence_qspi_apb_read_setup(struct cadence_spi_platdata *plat,
const struct spi_mem_op *op); const struct spi_mem_op *op);
int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, int cadence_qspi_apb_read_execute(struct cadence_spi_platdata *plat,
unsigned int rxlen, u8 *rxbuf); const struct spi_mem_op *op);
int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, int cadence_qspi_apb_write_setup(struct cadence_spi_platdata *plat,
const struct spi_mem_op *op); const struct spi_mem_op *op);
int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, int cadence_qspi_apb_write_execute(struct cadence_spi_platdata *plat,
unsigned int txlen, const u8 *txbuf); const struct spi_mem_op *op);
void cadence_qspi_apb_chipselect(void *reg_base, void cadence_qspi_apb_chipselect(void *reg_base,
unsigned int chip_select, unsigned int decoder_enable); unsigned int chip_select, unsigned int decoder_enable);

View File

@@ -27,6 +27,7 @@
#include <common.h> #include <common.h>
#include <asm/io.h> #include <asm/io.h>
#include <dma.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <wait_bit.h> #include <wait_bit.h>
#include <spi.h> #include <spi.h>
@@ -189,6 +190,15 @@ void cadence_qspi_apb_controller_disable(void *reg_base)
writel(reg, reg_base + CQSPI_REG_CONFIG); 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). */ /* Return 1 if idle, otherwise return 0 (busy). */
static unsigned int cadence_qspi_wait_idle(void *reg_base) 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) */ /* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */
int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat, int cadence_qspi_apb_read_setup(struct cadence_spi_platdata *plat,
const struct spi_mem_op *op) const struct spi_mem_op *op)
{ {
unsigned int reg; unsigned int reg;
unsigned int rd_reg; unsigned int rd_reg;
@@ -577,8 +587,9 @@ static int cadence_qspi_wait_for_data(struct cadence_spi_platdata *plat)
return -ETIMEDOUT; return -ETIMEDOUT;
} }
int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat, static int
unsigned int n_rx, u8 *rxbuf) cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat,
unsigned int n_rx, u8 *rxbuf)
{ {
unsigned int remaining = n_rx; unsigned int remaining = n_rx;
unsigned int bytes_to_read = 0; unsigned int bytes_to_read = 0;
@@ -639,9 +650,29 @@ failrd:
return ret; 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) */ /* Opcode + Address (3/4 bytes) */
int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat, int cadence_qspi_apb_write_setup(struct cadence_spi_platdata *plat,
const struct spi_mem_op *op) const struct spi_mem_op *op)
{ {
unsigned int reg; unsigned int reg;
@@ -662,8 +693,9 @@ int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat,
return 0; return 0;
} }
int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat, static int
unsigned int n_tx, const u8 *txbuf) 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 page_size = plat->page_size;
unsigned int remaining = n_tx; unsigned int remaining = n_tx;
@@ -735,6 +767,23 @@ failwr:
return ret; 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) void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy)
{ {
unsigned int reg; unsigned int reg;