summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2022-10-03 15:39:46 -0400
committerTom Rini <trini@konsulko.com>2022-10-03 15:39:46 -0400
commit2d4591353452638132d711551fec3495b7644731 (patch)
treee12058de7f553e84f8d13e545f130c7a48973589 /drivers/spi
parent4debc57a3da6c3f4d3f89a637e99206f4cea0a96 (diff)
parent6ee6e15975cad3c99fad3a66223f3fd9287a369b (diff)
downloadu-boot-2d4591353452638132d711551fec3495b7644731.tar.gz
u-boot-2d4591353452638132d711551fec3495b7644731.tar.bz2
u-boot-2d4591353452638132d711551fec3495b7644731.zip
Merge branch 'next'
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig28
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/cadence_ospi_versal.c59
-rw-r--r--drivers/spi/cadence_qspi.c104
-rw-r--r--drivers/spi/cadence_qspi.h56
-rw-r--r--drivers/spi/cadence_qspi_apb.c231
-rw-r--r--drivers/spi/mtk_snfi_spi.c2
-rw-r--r--drivers/spi/mtk_spim.c701
-rw-r--r--drivers/spi/octeon_spi.c2
-rw-r--r--drivers/spi/spi-aspeed-smc.c1218
-rw-r--r--drivers/spi/spi-mem.c268
-rw-r--r--drivers/spi/stm32_qspi.c2
-rw-r--r--drivers/spi/zynqmp_gqspi.c222
13 files changed, 2623 insertions, 272 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 75b794548b..2f12081f88 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -40,6 +40,16 @@ config SPI_MEM
This extension is meant to simplify interaction with SPI memories
by providing an high-level interface to send memory-like commands.
+config SPI_DIRMAP
+ bool "SPI direct mapping"
+ depends on SPI_MEM
+ help
+ Enable the SPI direct mapping API. Most modern SPI controllers can
+ directly map a SPI memory (or a portion of the SPI memory) in the CPU
+ address space. Most of the time this brings significant performance
+ improvements as it automates the whole process of sending SPI memory
+ operations every time a new region is accessed.
+
if DM_SPI
config ALTERA_SPI
@@ -138,7 +148,7 @@ config CQSPI_REF_CLK
config CADENCE_OSPI_VERSAL
bool "Configure Versal OSPI"
- depends on ARCH_VERSAL && CADENCE_QSPI
+ depends on (ARCH_VERSAL || ARCH_VERSAL_NET) && CADENCE_QSPI
imply DM_GPIO
help
This option is used to enable Versal OSPI DMA operations which
@@ -276,6 +286,14 @@ config MTK_SNFI_SPI
used to access SPI memory devices like SPI-NOR or SPI-NAND on
platforms embedding this IP core, like MT7622/M7629.
+config MTK_SPIM
+ bool "Mediatek SPI-MEM master controller driver"
+ depends on SPI_MEM
+ help
+ Enable MediaTek SPI-MEM master controller driver. This driver mainly
+ supports SPI flashes. You can use single, dual or quad mode
+ transmission on this controller.
+
config MVEBU_A3700_SPI
bool "Marvell Armada 3700 SPI driver"
select CLK_ARMADA_3720
@@ -401,6 +419,14 @@ config SANDBOX_SPI
};
};
+config SPI_ASPEED_SMC
+ bool "ASPEED SPI flash controller driver"
+ depends on DM_SPI && SPI_MEM
+ default n
+ help
+ Enable ASPEED SPI flash controller driver for AST2500
+ and AST2600 SoCs.
+
config SPI_SIFIVE
bool "SiFive SPI driver"
help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 4de77c260a..50ba43550b 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
obj-$(CONFIG_CADENCE_OSPI_VERSAL) += cadence_ospi_versal.o
obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
obj-$(CONFIG_SOFT_SPI) += soft_spi.o
+obj-$(CONFIG_SPI_ASPEED_SMC) += spi-aspeed-smc.o
obj-$(CONFIG_SPI_MEM) += spi-mem.o
obj-$(CONFIG_TI_QSPI) += ti_qspi.o
obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o
@@ -43,6 +44,7 @@ obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o
obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o
obj-$(CONFIG_MTK_SNOR) += mtk_snor.o
+obj-$(CONFIG_MTK_SPIM) += mtk_spim.o
obj-$(CONFIG_MT7620_SPI) += mt7620_spi.o
obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o
obj-$(CONFIG_MSCC_BB_SPI) += mscc_bb_spi.o
diff --git a/drivers/spi/cadence_ospi_versal.c b/drivers/spi/cadence_ospi_versal.c
index 52bcad053f..a9547a8200 100644
--- a/drivers/spi/cadence_ospi_versal.c
+++ b/drivers/spi/cadence_ospi_versal.c
@@ -21,7 +21,7 @@
#define CMD_4BYTE_READ 0x13
#define CMD_4BYTE_FAST_READ 0x0C
-int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
u32 reg, ret, rx_rem, n_rx, bytes_to_dma, data;
@@ -34,86 +34,86 @@ int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat,
if (bytes_to_dma) {
cadence_qspi_apb_enable_linear_mode(false);
- reg = readl(plat->regbase + CQSPI_REG_CONFIG);
+ reg = readl(priv->regbase + CQSPI_REG_CONFIG);
reg |= CQSPI_REG_CONFIG_ENBL_DMA;
- writel(reg, plat->regbase + CQSPI_REG_CONFIG);
+ writel(reg, priv->regbase + CQSPI_REG_CONFIG);
- writel(bytes_to_dma, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
+ writel(bytes_to_dma, priv->regbase + CQSPI_REG_INDIRECTRDBYTES);
writel(CQSPI_DFLT_INDIR_TRIG_ADDR_RANGE,
- plat->regbase + CQSPI_REG_INDIR_TRIG_ADDR_RANGE);
+ priv->regbase + CQSPI_REG_INDIR_TRIG_ADDR_RANGE);
writel(CQSPI_DFLT_DMA_PERIPH_CFG,
- plat->regbase + CQSPI_REG_DMA_PERIPH_CFG);
- writel((unsigned long)rxbuf, plat->regbase +
+ priv->regbase + CQSPI_REG_DMA_PERIPH_CFG);
+ writel((unsigned long)rxbuf, priv->regbase +
CQSPI_DMA_DST_ADDR_REG);
- writel(plat->trigger_address, plat->regbase +
+ writel(priv->trigger_address, priv->regbase +
CQSPI_DMA_SRC_RD_ADDR_REG);
- writel(bytes_to_dma, plat->regbase +
+ writel(bytes_to_dma, priv->regbase +
CQSPI_DMA_DST_SIZE_REG);
flush_dcache_range((unsigned long)rxbuf,
(unsigned long)rxbuf + bytes_to_dma);
writel(CQSPI_DFLT_DST_CTRL_REG_VAL,
- plat->regbase + CQSPI_DMA_DST_CTRL_REG);
+ priv->regbase + CQSPI_DMA_DST_CTRL_REG);
/* Start the indirect read transfer */
- writel(CQSPI_REG_INDIRECTRD_START, plat->regbase +
+ writel(CQSPI_REG_INDIRECTRD_START, priv->regbase +
CQSPI_REG_INDIRECTRD);
/* Wait for dma to complete transfer */
- ret = cadence_qspi_apb_wait_for_dma_cmplt(plat);
+ ret = cadence_qspi_apb_wait_for_dma_cmplt(priv);
if (ret)
return ret;
/* Clear indirect completion status */
- writel(CQSPI_REG_INDIRECTRD_DONE, plat->regbase +
+ writel(CQSPI_REG_INDIRECTRD_DONE, priv->regbase +
CQSPI_REG_INDIRECTRD);
rxbuf += bytes_to_dma;
}
if (rx_rem) {
- reg = readl(plat->regbase + CQSPI_REG_CONFIG);
+ reg = readl(priv->regbase + CQSPI_REG_CONFIG);
reg &= ~CQSPI_REG_CONFIG_ENBL_DMA;
- writel(reg, plat->regbase + CQSPI_REG_CONFIG);
+ writel(reg, priv->regbase + CQSPI_REG_CONFIG);
- reg = readl(plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
+ reg = readl(priv->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
reg += bytes_to_dma;
- writel(reg, plat->regbase + CQSPI_REG_CMDADDRESS);
+ writel(reg, priv->regbase + CQSPI_REG_CMDADDRESS);
- addr_bytes = readl(plat->regbase + CQSPI_REG_SIZE) &
+ addr_bytes = readl(priv->regbase + CQSPI_REG_SIZE) &
CQSPI_REG_SIZE_ADDRESS_MASK;
opcode = CMD_4BYTE_FAST_READ;
dummy_cycles = 8;
writel((dummy_cycles << CQSPI_REG_RD_INSTR_DUMMY_LSB) | opcode,
- plat->regbase + CQSPI_REG_RD_INSTR);
+ priv->regbase + CQSPI_REG_RD_INSTR);
reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB);
reg |= (addr_bytes & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) <<
CQSPI_REG_CMDCTRL_ADD_BYTES_LSB;
reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
- dummy_cycles = (readl(plat->regbase + CQSPI_REG_RD_INSTR) >>
+ dummy_cycles = (readl(priv->regbase + CQSPI_REG_RD_INSTR) >>
CQSPI_REG_RD_INSTR_DUMMY_LSB) &
CQSPI_REG_RD_INSTR_DUMMY_MASK;
reg |= (dummy_cycles & CQSPI_REG_CMDCTRL_DUMMY_MASK) <<
CQSPI_REG_CMDCTRL_DUMMY_LSB;
reg |= (((rx_rem - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK) <<
CQSPI_REG_CMDCTRL_RD_BYTES_LSB);
- ret = cadence_qspi_apb_exec_flash_cmd(plat->regbase, reg);
+ ret = cadence_qspi_apb_exec_flash_cmd(priv->regbase, reg);
if (ret)
return ret;
- data = readl(plat->regbase + CQSPI_REG_CMDREADDATALOWER);
+ data = readl(priv->regbase + CQSPI_REG_CMDREADDATALOWER);
memcpy(rxbuf, &data, rx_rem);
}
return 0;
}
-int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_plat *plat)
+int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_priv *priv)
{
u32 timeout = CQSPI_DMA_TIMEOUT;
- while (!(readl(plat->regbase + CQSPI_DMA_DST_I_STS_REG) &
+ while (!(readl(priv->regbase + CQSPI_DMA_DST_I_STS_REG) &
CQSPI_DMA_DST_I_STS_DONE) && timeout--)
udelay(1);
@@ -122,14 +122,15 @@ int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_plat *plat)
return -ETIMEDOUT;
}
- writel(readl(plat->regbase + CQSPI_DMA_DST_I_STS_REG),
- plat->regbase + CQSPI_DMA_DST_I_STS_REG);
+ writel(readl(priv->regbase + CQSPI_DMA_DST_I_STS_REG),
+ priv->regbase + CQSPI_DMA_DST_I_STS_REG);
return 0;
}
#if defined(CONFIG_DM_GPIO)
-int cadence_spi_versal_flash_reset(struct udevice *dev)
+int cadence_qspi_versal_flash_reset(struct udevice *dev)
{
+#ifndef CONFIG_ARCH_VERSAL_NET
struct gpio_desc gpio;
u32 reset_gpio;
int ret;
@@ -165,11 +166,11 @@ int cadence_spi_versal_flash_reset(struct udevice *dev)
/* Set value 1 to pin */
dm_gpio_set_value(&gpio, 1);
udelay(1);
-
+#endif
return 0;
}
#else
-int cadence_spi_versal_flash_reset(struct udevice *dev)
+int cadence_qspi_versal_flash_reset(struct udevice *dev)
{
/* CRP WPROT */
writel(0, WPROT_CRP);
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 907f5dadc4..ab0a681c83 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -29,7 +29,7 @@
#define CQSPI_READ 2
#define CQSPI_WRITE 3
-__weak int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat,
+__weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
return 0;
@@ -42,36 +42,40 @@ __weak int cadence_qspi_versal_flash_reset(struct udevice *dev)
static int cadence_spi_write_speed(struct udevice *bus, uint hz)
{
- struct cadence_spi_plat *plat = dev_get_plat(bus);
struct cadence_spi_priv *priv = dev_get_priv(bus);
cadence_qspi_apb_config_baudrate_div(priv->regbase,
- plat->ref_clk_hz, hz);
+ priv->ref_clk_hz, hz);
/* Reconfigure delay timing if speed is changed. */
- cadence_qspi_apb_delay(priv->regbase, plat->ref_clk_hz, hz,
- plat->tshsl_ns, plat->tsd2d_ns,
- plat->tchsh_ns, plat->tslch_ns);
+ cadence_qspi_apb_delay(priv->regbase, priv->ref_clk_hz, hz,
+ priv->tshsl_ns, priv->tsd2d_ns,
+ priv->tchsh_ns, priv->tslch_ns);
return 0;
}
-static int cadence_spi_read_id(struct cadence_spi_plat *plat, u8 len,
+static int cadence_spi_read_id(struct cadence_spi_priv *priv, u8 len,
u8 *idcode)
{
+ int err;
+
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(len, idcode, 1));
- return cadence_qspi_apb_command_read(plat, &op);
+ err = cadence_qspi_apb_command_read_setup(priv, &op);
+ if (!err)
+ err = cadence_qspi_apb_command_read(priv, &op);
+
+ return err;
}
/* Calibration sequence to determine the read data capture delay register */
static int spi_calibration(struct udevice *bus, uint hz)
{
struct cadence_spi_priv *priv = dev_get_priv(bus);
- struct cadence_spi_plat *plat = dev_get_plat(bus);
void *base = priv->regbase;
unsigned int idcode = 0, temp = 0;
int err = 0, i, range_lo = -1, range_hi = -1;
@@ -86,7 +90,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
cadence_qspi_apb_controller_enable(base);
/* read the ID which will be our golden value */
- err = cadence_spi_read_id(plat, 3, (u8 *)&idcode);
+ err = cadence_spi_read_id(priv, 3, (u8 *)&idcode);
if (err) {
puts("SF: Calibration failed (read)\n");
return err;
@@ -105,7 +109,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
cadence_qspi_apb_controller_enable(base);
/* issue a RDID to get the ID value */
- err = cadence_spi_read_id(plat, 3, (u8 *)&temp);
+ err = cadence_spi_read_id(priv, 3, (u8 *)&temp);
if (err) {
puts("SF: Calibration failed (read)\n");
return err;
@@ -147,13 +151,11 @@ static int spi_calibration(struct udevice *bus, uint hz)
static int cadence_spi_set_speed(struct udevice *bus, uint hz)
{
- struct cadence_spi_plat *plat = dev_get_plat(bus);
struct cadence_spi_priv *priv = dev_get_priv(bus);
int err;
- if (!hz || hz > plat->max_hz)
- hz = plat->max_hz;
-
+ if (!hz || hz > priv->max_hz)
+ hz = priv->max_hz;
/* Disable QSPI */
cadence_qspi_apb_controller_disable(priv->regbase);
@@ -161,10 +163,10 @@ static int cadence_spi_set_speed(struct udevice *bus, uint hz)
* If the device tree already provides a read delay value, use that
* instead of calibrating.
*/
- if (plat->read_delay >= 0) {
+ if (priv->read_delay >= 0) {
cadence_spi_write_speed(bus, hz);
cadence_qspi_apb_readdata_capture(priv->regbase, 1,
- plat->read_delay);
+ priv->read_delay);
} else if (priv->previous_hz != hz ||
priv->qspi_calibrated_hz != hz ||
priv->qspi_calibrated_cs != spi_chip_select(bus)) {
@@ -195,29 +197,44 @@ static int cadence_spi_probe(struct udevice *bus)
struct clk clk;
int ret;
- priv->regbase = plat->regbase;
- priv->ahbbase = plat->ahbbase;
+ priv->regbase = plat->regbase;
+ priv->ahbbase = plat->ahbbase;
+ priv->is_dma = plat->is_dma;
+ priv->is_decoded_cs = plat->is_decoded_cs;
+ priv->fifo_depth = plat->fifo_depth;
+ priv->fifo_width = plat->fifo_width;
+ priv->trigger_address = plat->trigger_address;
+ priv->read_delay = plat->read_delay;
+ priv->ahbsize = plat->ahbsize;
+ priv->max_hz = plat->max_hz;
+
+ priv->page_size = plat->page_size;
+ priv->block_size = plat->block_size;
+ priv->tshsl_ns = plat->tshsl_ns;
+ priv->tsd2d_ns = plat->tsd2d_ns;
+ priv->tchsh_ns = plat->tchsh_ns;
+ priv->tslch_ns = plat->tslch_ns;
if (CONFIG_IS_ENABLED(ZYNQMP_FIRMWARE))
xilinx_pm_request(PM_REQUEST_NODE, PM_DEV_OSPI,
ZYNQMP_PM_CAPABILITY_ACCESS, ZYNQMP_PM_MAX_QOS,
ZYNQMP_PM_REQUEST_ACK_NO, NULL);
- if (plat->ref_clk_hz == 0) {
+ if (priv->ref_clk_hz == 0) {
ret = clk_get_by_index(bus, 0, &clk);
if (ret) {
#ifdef CONFIG_HAS_CQSPI_REF_CLK
- plat->ref_clk_hz = CONFIG_CQSPI_REF_CLK;
+ priv->ref_clk_hz = CONFIG_CQSPI_REF_CLK;
#elif defined(CONFIG_ARCH_SOCFPGA)
- plat->ref_clk_hz = cm_get_qspi_controller_clk_hz();
+ priv->ref_clk_hz = cm_get_qspi_controller_clk_hz();
#else
return ret;
#endif
} else {
- plat->ref_clk_hz = clk_get_rate(&clk);
+ priv->ref_clk_hz = clk_get_rate(&clk);
clk_free(&clk);
- if (IS_ERR_VALUE(plat->ref_clk_hz))
- return plat->ref_clk_hz;
+ if (IS_ERR_VALUE(priv->ref_clk_hz))
+ return priv->ref_clk_hz;
}
}
@@ -226,16 +243,16 @@ static int cadence_spi_probe(struct udevice *bus)
reset_deassert_bulk(priv->resets);
if (!priv->qspi_is_init) {
- cadence_qspi_apb_controller_init(plat);
+ cadence_qspi_apb_controller_init(priv);
priv->qspi_is_init = 1;
}
- plat->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, plat->ref_clk_hz);
+ priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz);
if (CONFIG_IS_ENABLED(ARCH_VERSAL)) {
/* Versal platform uses spi calibration to set read delay */
- if (plat->read_delay >= 0)
- plat->read_delay = -1;
+ if (priv->read_delay >= 0)
+ priv->read_delay = -1;
/* Reset ospi flash device */
ret = cadence_qspi_versal_flash_reset(bus);
if (ret)
@@ -258,7 +275,6 @@ static int cadence_spi_remove(struct udevice *dev)
static int cadence_spi_set_mode(struct udevice *bus, uint mode)
{
- struct cadence_spi_plat *plat = dev_get_plat(bus);
struct cadence_spi_priv *priv = dev_get_priv(bus);
/* Disable QSPI */
@@ -268,7 +284,7 @@ static int cadence_spi_set_mode(struct udevice *bus, uint mode)
cadence_qspi_apb_set_clk_mode(priv->regbase, mode);
/* Enable Direct Access Controller */
- if (plat->use_dac_mode)
+ if (priv->use_dac_mode)
cadence_qspi_apb_dac_mode_enable(priv->regbase);
/* Enable QSPI */
@@ -281,7 +297,6 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
const struct spi_mem_op *op)
{
struct udevice *bus = spi->dev->parent;
- struct cadence_spi_plat *plat = dev_get_plat(bus);
struct cadence_spi_priv *priv = dev_get_priv(bus);
void *base = priv->regbase;
int err = 0;
@@ -289,7 +304,7 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
/* Set Chip select */
cadence_qspi_apb_chipselect(base, spi_chip_select(spi->dev),
- plat->is_decoded_cs);
+ priv->is_decoded_cs);
if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) {
if (!op->addr.nbytes)
@@ -305,28 +320,28 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
switch (mode) {
case CQSPI_STIG_READ:
- err = cadence_qspi_apb_command_read_setup(plat, op);
+ err = cadence_qspi_apb_command_read_setup(priv, op);
if (!err)
- err = cadence_qspi_apb_command_read(plat, op);
+ err = cadence_qspi_apb_command_read(priv, op);
break;
case CQSPI_STIG_WRITE:
- err = cadence_qspi_apb_command_write_setup(plat, op);
+ err = cadence_qspi_apb_command_write_setup(priv, op);
if (!err)
- err = cadence_qspi_apb_command_write(plat, op);
+ err = cadence_qspi_apb_command_write(priv, op);
break;
case CQSPI_READ:
- err = cadence_qspi_apb_read_setup(plat, op);
+ err = cadence_qspi_apb_read_setup(priv, op);
if (!err) {
- if (plat->is_dma)
- err = cadence_qspi_apb_dma_read(plat, op);
+ if (priv->is_dma)
+ err = cadence_qspi_apb_dma_read(priv, op);
else
- err = cadence_qspi_apb_read_execute(plat, op);
+ err = cadence_qspi_apb_read_execute(priv, op);
}
break;
case CQSPI_WRITE:
- err = cadence_qspi_apb_write_setup(plat, op);
+ err = cadence_qspi_apb_write_setup(priv, op);
if (!err)
- err = cadence_qspi_apb_write_execute(plat, op);
+ err = cadence_qspi_apb_write_execute(priv, op);
break;
default:
err = -1;
@@ -359,6 +374,7 @@ static bool cadence_spi_mem_supports_op(struct spi_slave *slave,
static int cadence_spi_of_to_plat(struct udevice *bus)
{
struct cadence_spi_plat *plat = dev_get_plat(bus);
+ struct cadence_spi_priv *priv = dev_get_priv(bus);
ofnode subnode;
plat->regbase = (void *)devfdt_get_addr_index(bus, 0);
@@ -372,7 +388,7 @@ static int cadence_spi_of_to_plat(struct udevice *bus)
0);
/* Use DAC mode only when MMIO window is at least 8M wide */
if (plat->ahbsize >= SZ_8M)
- plat->use_dac_mode = true;
+ priv->use_dac_mode = true;
plat->is_dma = dev_read_bool(bus, "cdns,is-dma");
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index c8d16bb0e4..1c59d1a9d9 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -198,7 +198,6 @@
CQSPI_REG_SDRAMLEVEL_WR_LSB) & CQSPI_REG_SDRAMLEVEL_WR_MASK)
struct cadence_spi_plat {
- unsigned int ref_clk_hz;
unsigned int max_hz;
void *regbase;
void *ahbbase;
@@ -209,7 +208,6 @@ struct cadence_spi_plat {
fdt_addr_t ahbsize;
bool use_dac_mode;
int read_delay;
- u32 wr_delay;
/* Flash parameters */
u32 page_size;
@@ -219,17 +217,18 @@ struct cadence_spi_plat {
u32 tchsh_ns;
u32 tslch_ns;
- /* Transaction protocol parameters. */
- u8 inst_width;
- u8 addr_width;
- u8 data_width;
- bool dtr;
bool is_dma;
};
struct cadence_spi_priv {
+ unsigned int ref_clk_hz;
+ unsigned int max_hz;
void *regbase;
void *ahbbase;
+ unsigned int fifo_depth;
+ unsigned int fifo_width;
+ unsigned int trigger_address;
+ fdt_addr_t ahbsize;
size_t cmd_len;
u8 cmd_buf[32];
size_t data_len;
@@ -238,32 +237,53 @@ struct cadence_spi_priv {
unsigned int qspi_calibrated_hz;
unsigned int qspi_calibrated_cs;
unsigned int previous_hz;
+ u32 wr_delay;
+ int read_delay;
struct reset_ctl_bulk *resets;
+ u32 page_size;
+ u32 block_size;
+ u32 tshsl_ns;
+ u32 tsd2d_ns;
+ u32 tchsh_ns;
+ u32 tslch_ns;
+ u8 edge_mode;
+ u8 dll_mode;
+ bool extra_dummy;
+ bool ddr_init;
+ bool is_decoded_cs;
+ bool use_dac_mode;
+ bool is_dma;
+
+ /* Transaction protocol parameters. */
+ u8 inst_width;
+ u8 addr_width;
+ u8 data_width;
+ bool dtr;
};
/* Functions call declaration */
-void cadence_qspi_apb_controller_init(struct cadence_spi_plat *plat);
+void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv);
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_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_read_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_command_read(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_command_write_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_write_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_command_write(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_read_execute(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_read_execute(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_write_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_write_execute(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
void cadence_qspi_apb_chipselect(void *reg_base,
@@ -279,9 +299,9 @@ void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy);
void cadence_qspi_apb_readdata_capture(void *reg_base,
unsigned int bypass, unsigned int delay);
unsigned int cm_get_qspi_controller_clk_hz(void);
-int cadence_qspi_apb_dma_read(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
-int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_plat *plat);
+int cadence_qspi_apb_wait_for_dma_cmplt(struct cadence_spi_priv *priv);
int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg);
int cadence_qspi_versal_flash_reset(struct udevice *dev);
void cadence_qspi_apb_enable_linear_mode(bool enable);
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index c00755050e..cfae5dcbda 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -83,13 +83,13 @@ static unsigned int cadence_qspi_calc_dummy(const struct spi_mem_op *op,
return dummy_clk;
}
-static u32 cadence_qspi_calc_rdreg(struct cadence_spi_plat *plat)
+static u32 cadence_qspi_calc_rdreg(struct cadence_spi_priv *priv)
{
u32 rdreg = 0;
- rdreg |= plat->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB;
- rdreg |= plat->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB;
- rdreg |= plat->data_width << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
+ rdreg |= priv->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB;
+ rdreg |= priv->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB;
+ rdreg |= priv->data_width << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
return rdreg;
}
@@ -115,27 +115,27 @@ static int cadence_qspi_buswidth_to_inst_type(u8 buswidth)
}
}
-static int cadence_qspi_set_protocol(struct cadence_spi_plat *plat,
+static int cadence_qspi_set_protocol(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
int ret;
- plat->dtr = op->data.dtr && op->cmd.dtr && op->addr.dtr;
+ priv->dtr = op->data.dtr && op->cmd.dtr && op->addr.dtr;
ret = cadence_qspi_buswidth_to_inst_type(op->cmd.buswidth);
if (ret < 0)
return ret;
- plat->inst_width = ret;
+ priv->inst_width = ret;
ret = cadence_qspi_buswidth_to_inst_type(op->addr.buswidth);
if (ret < 0)
return ret;
- plat->addr_width = ret;
+ priv->addr_width = ret;
ret = cadence_qspi_buswidth_to_inst_type(op->data.buswidth);
if (ret < 0)
return ret;
- plat->data_width = ret;
+ priv->data_width = ret;
return 0;
}
@@ -314,31 +314,31 @@ void cadence_qspi_apb_delay(void *reg_base,
cadence_qspi_apb_controller_enable(reg_base);
}
-void cadence_qspi_apb_controller_init(struct cadence_spi_plat *plat)
+void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv)
{
unsigned reg;
- cadence_qspi_apb_controller_disable(plat->regbase);
+ cadence_qspi_apb_controller_disable(priv->regbase);
/* Configure the device size and address bytes */
- reg = readl(plat->regbase + CQSPI_REG_SIZE);
+ reg = readl(priv->regbase + CQSPI_REG_SIZE);
/* Clear the previous value */
reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB);
reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB);
- reg |= (plat->page_size << CQSPI_REG_SIZE_PAGE_LSB);
- reg |= (plat->block_size << CQSPI_REG_SIZE_BLOCK_LSB);
- writel(reg, plat->regbase + CQSPI_REG_SIZE);
+ reg |= (priv->page_size << CQSPI_REG_SIZE_PAGE_LSB);
+ reg |= (priv->block_size << CQSPI_REG_SIZE_BLOCK_LSB);
+ writel(reg, priv->regbase + CQSPI_REG_SIZE);
/* Configure the remap address register, no remap */
- writel(0, plat->regbase + CQSPI_REG_REMAP);
+ writel(0, priv->regbase + CQSPI_REG_REMAP);
/* Indirect mode configurations */
- writel(plat->fifo_depth / 2, plat->regbase + CQSPI_REG_SRAMPARTITION);
+ writel(priv->fifo_depth / 2, priv->regbase + CQSPI_REG_SRAMPARTITION);
/* Disable all interrupts */
- writel(0, plat->regbase + CQSPI_REG_IRQMASK);
+ writel(0, priv->regbase + CQSPI_REG_IRQMASK);
- cadence_qspi_apb_controller_enable(plat->regbase);
+ cadence_qspi_apb_controller_enable(priv->regbase);
}
int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg)
@@ -370,7 +370,7 @@ int cadence_qspi_apb_exec_flash_cmd(void *reg_base, unsigned int reg)
return 0;
}
-static int cadence_qspi_setup_opcode_ext(struct cadence_spi_plat *plat,
+static int cadence_qspi_setup_opcode_ext(struct cadence_spi_priv *priv,
const struct spi_mem_op *op,
unsigned int shift)
{
@@ -383,15 +383,15 @@ static int cadence_qspi_setup_opcode_ext(struct cadence_spi_plat *plat,
/* Opcode extension is the LSB. */
ext = op->cmd.opcode & 0xff;
- reg = readl(plat->regbase + CQSPI_REG_OP_EXT_LOWER);
+ reg = readl(priv->regbase + CQSPI_REG_OP_EXT_LOWER);
reg &= ~(0xff << shift);
reg |= ext << shift;
- writel(reg, plat->regbase + CQSPI_REG_OP_EXT_LOWER);
+ writel(reg, priv->regbase + CQSPI_REG_OP_EXT_LOWER);
return 0;
}
-static int cadence_qspi_enable_dtr(struct cadence_spi_plat *plat,
+static int cadence_qspi_enable_dtr(struct cadence_spi_priv *priv,
const struct spi_mem_op *op,
unsigned int shift,
bool enable)
@@ -399,14 +399,14 @@ static int cadence_qspi_enable_dtr(struct cadence_spi_plat *plat,
unsigned int reg;
int ret;
- reg = readl(plat->regbase + CQSPI_REG_CONFIG);
+ reg = readl(priv->regbase + CQSPI_REG_CONFIG);
if (enable) {
reg |= CQSPI_REG_CONFIG_DTR_PROTO;
reg |= CQSPI_REG_CONFIG_DUAL_OPCODE;
/* Set up command opcode extension. */
- ret = cadence_qspi_setup_opcode_ext(plat, op, shift);
+ ret = cadence_qspi_setup_opcode_ext(priv, op, shift);
if (ret)
return ret;
} else {
@@ -414,37 +414,37 @@ static int cadence_qspi_enable_dtr(struct cadence_spi_plat *plat,
reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE;
}
- writel(reg, plat->regbase + CQSPI_REG_CONFIG);
+ writel(reg, priv->regbase + CQSPI_REG_CONFIG);
return 0;
}
-int cadence_qspi_apb_command_read_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_read_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
int ret;
unsigned int reg;
- ret = cadence_qspi_set_protocol(plat, op);
+ ret = cadence_qspi_set_protocol(priv, op);
if (ret)
return ret;
- ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_STIG_LSB,
- plat->dtr);
+ ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_STIG_LSB,
+ priv->dtr);
if (ret)
return ret;
- reg = cadence_qspi_calc_rdreg(plat);
- writel(reg, plat->regbase + CQSPI_REG_RD_INSTR);
+ reg = cadence_qspi_calc_rdreg(priv);
+ writel(reg, priv->regbase + CQSPI_REG_RD_INSTR);
return 0;
}
/* For command RDID, RDSR. */
-int cadence_qspi_apb_command_read(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_read(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
- void *reg_base = plat->regbase;
+ void *reg_base = priv->regbase;
unsigned int reg;
unsigned int read_len;
int status;
@@ -458,7 +458,7 @@ int cadence_qspi_apb_command_read(struct cadence_spi_plat *plat,
return -EINVAL;
}
- if (plat->dtr)
+ if (priv->dtr)
opcode = op->cmd.opcode >> 8;
else
opcode = op->cmd.opcode;
@@ -466,7 +466,7 @@ int cadence_qspi_apb_command_read(struct cadence_spi_plat *plat,
reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
/* Set up dummy cycles. */
- dummy_clk = cadence_qspi_calc_dummy(op, plat->dtr);
+ dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr);
if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
return -ENOTSUPP;
@@ -499,29 +499,29 @@ int cadence_qspi_apb_command_read(struct cadence_spi_plat *plat,
return 0;
}
-int cadence_qspi_apb_command_write_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_write_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
int ret;
unsigned int reg;
- ret = cadence_qspi_set_protocol(plat, op);
+ ret = cadence_qspi_set_protocol(priv, op);
if (ret)
return ret;
- ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_STIG_LSB,
- plat->dtr);
+ ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_STIG_LSB,
+ priv->dtr);
if (ret)
return ret;
- reg = cadence_qspi_calc_rdreg(plat);
- writel(reg, plat->regbase + CQSPI_REG_RD_INSTR);
+ reg = cadence_qspi_calc_rdreg(priv);
+ writel(reg, priv->regbase + CQSPI_REG_RD_INSTR);
return 0;
}
/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */
-int cadence_qspi_apb_command_write(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
unsigned int reg = 0;
@@ -529,7 +529,7 @@ int cadence_qspi_apb_command_write(struct cadence_spi_plat *plat,
unsigned int wr_len;
unsigned int txlen = op->data.nbytes;
const void *txbuf = op->data.buf.out;
- void *reg_base = plat->regbase;
+ void *reg_base = priv->regbase;
u32 addr;
u8 opcode;
@@ -547,7 +547,7 @@ int cadence_qspi_apb_command_write(struct cadence_spi_plat *plat,
return -EINVAL;
}
- if (plat->dtr)
+ if (priv->dtr)
opcode = op->cmd.opcode >> 8;
else
opcode = op->cmd.opcode;
@@ -579,7 +579,7 @@ int cadence_qspi_apb_command_write(struct cadence_spi_plat *plat,
}
/* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */
-int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
unsigned int reg;
@@ -589,33 +589,33 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
int ret;
u8 opcode;
- ret = cadence_qspi_set_protocol(plat, op);
+ ret = cadence_qspi_set_protocol(priv, op);
if (ret)
return ret;
- ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_READ_LSB,
- plat->dtr);
+ ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_READ_LSB,
+ priv->dtr);
if (ret)
return ret;
/* Setup the indirect trigger address */
- writel(plat->trigger_address,
- plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
+ writel(priv->trigger_address,
+ priv->regbase + CQSPI_REG_INDIRECTTRIGGER);
/* Configure the opcode */
- if (plat->dtr)
+ if (priv->dtr)
opcode = op->cmd.opcode >> 8;
else
opcode = op->cmd.opcode;
rd_reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
- rd_reg |= cadence_qspi_calc_rdreg(plat);
+ rd_reg |= cadence_qspi_calc_rdreg(priv);
- writel(op->addr.val, plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
+ writel(op->addr.val, priv->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
if (dummy_bytes) {
/* Convert to clock cycles. */
- dummy_clk = cadence_qspi_calc_dummy(op, plat->dtr);
+ dummy_clk = cadence_qspi_calc_dummy(op, priv->dtr);
if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
return -ENOTSUPP;
@@ -625,30 +625,30 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
<< CQSPI_REG_RD_INSTR_DUMMY_LSB;
}
- writel(rd_reg, plat->regbase + CQSPI_REG_RD_INSTR);
+ writel(rd_reg, priv->regbase + CQSPI_REG_RD_INSTR);
/* set device size */
- reg = readl(plat->regbase + CQSPI_REG_SIZE);
+ reg = readl(priv->regbase + CQSPI_REG_SIZE);
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
reg |= (op->addr.nbytes - 1);
- writel(reg, plat->regbase + CQSPI_REG_SIZE);
+ writel(reg, priv->regbase + CQSPI_REG_SIZE);
return 0;
}
-static u32 cadence_qspi_get_rd_sram_level(struct cadence_spi_plat *plat)
+static u32 cadence_qspi_get_rd_sram_level(struct cadence_spi_priv *priv)
{
- u32 reg = readl(plat->regbase + CQSPI_REG_SDRAMLEVEL);
+ u32 reg = readl(priv->regbase + CQSPI_REG_SDRAMLEVEL);
reg >>= CQSPI_REG_SDRAMLEVEL_RD_LSB;
return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK;
}
-static int cadence_qspi_wait_for_data(struct cadence_spi_plat *plat)
+static int cadence_qspi_wait_for_data(struct cadence_spi_priv *priv)
{
unsigned int timeout = 10000;
u32 reg;
while (timeout--) {
- reg = cadence_qspi_get_rd_sram_level(plat);
+ reg = cadence_qspi_get_rd_sram_level(priv);
if (reg)
return reg;
udelay(1);
@@ -658,21 +658,21 @@ static int cadence_qspi_wait_for_data(struct cadence_spi_plat *plat)
}
static int
-cadence_qspi_apb_indirect_read_execute(struct cadence_spi_plat *plat,
+cadence_qspi_apb_indirect_read_execute(struct cadence_spi_priv *priv,
unsigned int n_rx, u8 *rxbuf)
{
unsigned int remaining = n_rx;
unsigned int bytes_to_read = 0;
int ret;
- writel(n_rx, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
+ writel(n_rx, priv->regbase + CQSPI_REG_INDIRECTRDBYTES);
/* Start the indirect read transfer */
writel(CQSPI_REG_INDIRECTRD_START,
- plat->regbase + CQSPI_REG_INDIRECTRD);
+ priv->regbase + CQSPI_REG_INDIRECTRD);
while (remaining > 0) {
- ret = cadence_qspi_wait_for_data(plat);
+ ret = cadence_qspi_wait_for_data(priv);
if (ret < 0) {
printf("Indirect write timed out (%i)\n", ret);
goto failrd;
@@ -681,7 +681,7 @@ cadence_qspi_apb_indirect_read_execute(struct cadence_spi_plat *plat,
bytes_to_read = ret;
while (bytes_to_read != 0) {
- bytes_to_read *= plat->fifo_width;
+ bytes_to_read *= priv->fifo_width;
bytes_to_read = bytes_to_read > remaining ?
remaining : bytes_to_read;
/*
@@ -689,18 +689,18 @@ cadence_qspi_apb_indirect_read_execute(struct cadence_spi_plat *plat,
* data abort.
*/
if (((uintptr_t)rxbuf % 4) || (bytes_to_read % 4))
- readsb(plat->ahbbase, rxbuf, bytes_to_read);
+ readsb(priv->ahbbase, rxbuf, bytes_to_read);
else
- readsl(plat->ahbbase, rxbuf,
+ readsl(priv->ahbbase, rxbuf,
bytes_to_read >> 2);
rxbuf += bytes_to_read;
remaining -= bytes_to_read;
- bytes_to_read = cadence_qspi_get_rd_sram_level(plat);
+ bytes_to_read = cadence_qspi_get_rd_sram_level(priv);
}
}
/* Check indirect done status */
- ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTRD,
+ ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTRD,
CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0);
if (ret) {
printf("Indirect read completion error (%i)\n", ret);
@@ -709,10 +709,10 @@ cadence_qspi_apb_indirect_read_execute(struct cadence_spi_plat *plat,
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTRD_DONE,
- plat->regbase + CQSPI_REG_INDIRECTRD);
+ priv->regbase + CQSPI_REG_INDIRECTRD);
/* Check indirect done status */
- ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTRD,
+ ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTRD,
CQSPI_REG_INDIRECTRD_DONE, 0, 10, 0);
if (ret) {
printf("Indirect read clear completion error (%i)\n", ret);
@@ -724,11 +724,11 @@ cadence_qspi_apb_indirect_read_execute(struct cadence_spi_plat *plat,
failrd:
/* Cancel the indirect read */
writel(CQSPI_REG_INDIRECTRD_CANCEL,
- plat->regbase + CQSPI_REG_INDIRECTRD);
+ priv->regbase + CQSPI_REG_INDIRECTRD);
return ret;
}
-int cadence_qspi_apb_read_execute(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_read_execute(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
u64 from = op->addr.val;
@@ -738,57 +738,57 @@ int cadence_qspi_apb_read_execute(struct cadence_spi_plat *plat,
if (CONFIG_IS_ENABLED(ARCH_VERSAL))
cadence_qspi_apb_enable_linear_mode(true);
- if (plat->use_dac_mode && (from + len < plat->ahbsize)) {
+ if (priv->use_dac_mode && (from + len < priv->ahbsize)) {
if (len < 256 ||
- dma_memcpy(buf, plat->ahbbase + from, len) < 0) {
- memcpy_fromio(buf, plat->ahbbase + from, len);
+ dma_memcpy(buf, priv->ahbbase + from, len) < 0) {
+ memcpy_fromio(buf, priv->ahbbase + from, len);
}
- if (!cadence_qspi_wait_idle(plat->regbase))
+ if (!cadence_qspi_wait_idle(priv->regbase))
return -EIO;
return 0;
}
- return cadence_qspi_apb_indirect_read_execute(plat, len, buf);
+ return cadence_qspi_apb_indirect_read_execute(priv, len, buf);
}
/* Opcode + Address (3/4 bytes) */
-int cadence_qspi_apb_write_setup(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
unsigned int reg;
int ret;
u8 opcode;
- ret = cadence_qspi_set_protocol(plat, op);
+ ret = cadence_qspi_set_protocol(priv, op);
if (ret)
return ret;
- ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_WRITE_LSB,
- plat->dtr);
+ ret = cadence_qspi_enable_dtr(priv, op, CQSPI_REG_OP_EXT_WRITE_LSB,
+ priv->dtr);
if (ret)
return ret;
/* Setup the indirect trigger address */
- writel(plat->trigger_address,
- plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
+ writel(priv->trigger_address,
+ priv->regbase + CQSPI_REG_INDIRECTTRIGGER);
/* Configure the opcode */
- if (plat->dtr)
+ if (priv->dtr)
opcode = op->cmd.opcode >> 8;
else
opcode = op->cmd.opcode;
reg = opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB;
- reg |= plat->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB;
- reg |= plat->addr_width << CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB;
- writel(reg, plat->regbase + CQSPI_REG_WR_INSTR);
+ reg |= priv->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB;
+ reg |= priv->addr_width << CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB;
+ writel(reg, priv->regbase + CQSPI_REG_WR_INSTR);
- reg = cadence_qspi_calc_rdreg(plat);
- writel(reg, plat->regbase + CQSPI_REG_RD_INSTR);
+ reg = cadence_qspi_calc_rdreg(priv);
+ writel(reg, priv->regbase + CQSPI_REG_RD_INSTR);
- writel(op->addr.val, plat->regbase + CQSPI_REG_INDIRECTWRSTARTADDR);
+ writel(op->addr.val, priv->regbase + CQSPI_REG_INDIRECTWRSTARTADDR);
- if (plat->dtr) {
+ if (priv->dtr) {
/*
* Some flashes like the cypress Semper flash expect a 4-byte
* dummy address with the Read SR command in DTR mode, but this
@@ -797,23 +797,23 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_plat *plat,
* controller's side. spi-nor will take care of polling the
* status register.
*/
- reg = readl(plat->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
+ reg = readl(priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL;
- writel(reg, plat->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
+ writel(reg, priv->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
}
- reg = readl(plat->regbase + CQSPI_REG_SIZE);
+ reg = readl(priv->regbase + CQSPI_REG_SIZE);
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
reg |= (op->addr.nbytes - 1);
- writel(reg, plat->regbase + CQSPI_REG_SIZE);
+ writel(reg, priv->regbase + CQSPI_REG_SIZE);
return 0;
}
static int
-cadence_qspi_apb_indirect_write_execute(struct cadence_spi_plat *plat,
+cadence_qspi_apb_indirect_write_execute(struct cadence_spi_priv *priv,
unsigned int n_tx, const u8 *txbuf)
{
- unsigned int page_size = plat->page_size;
+ unsigned int page_size = priv->page_size;
unsigned int remaining = n_tx;
const u8 *bb_txbuf = txbuf;
void *bounce_buf = NULL;
@@ -833,27 +833,27 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_plat *plat,
}
/* Configure the indirect read transfer bytes */
- writel(n_tx, plat->regbase + CQSPI_REG_INDIRECTWRBYTES);
+ writel(n_tx, priv->regbase + CQSPI_REG_INDIRECTWRBYTES);
/* Start the indirect write transfer */
writel(CQSPI_REG_INDIRECTWR_START,
- plat->regbase + CQSPI_REG_INDIRECTWR);
+ priv->regbase + CQSPI_REG_INDIRECTWR);
/*
* Some delay is required for the above bit to be internally
* synchronized by the QSPI module.
*/
- ndelay(plat->wr_delay);
+ ndelay(priv->wr_delay);
while (remaining > 0) {
write_bytes = remaining > page_size ? page_size : remaining;
- writesl(plat->ahbbase, bb_txbuf, write_bytes >> 2);
+ writesl(priv->ahbbase, bb_txbuf, write_bytes >> 2);
if (write_bytes % 4)
- writesb(plat->ahbbase,
+ writesb(priv->ahbbase,
bb_txbuf + rounddown(write_bytes, 4),
write_bytes % 4);
- ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_SDRAMLEVEL,
+ ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_SDRAMLEVEL,
CQSPI_REG_SDRAMLEVEL_WR_MASK <<
CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0);
if (ret) {
@@ -866,7 +866,7 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_plat *plat,
}
/* Check indirect done status */
- ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTWR,
+ ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTWR,
CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0);
if (ret) {
printf("Indirect write completion error (%i)\n", ret);
@@ -875,10 +875,10 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_plat *plat,
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTWR_DONE,
- plat->regbase + CQSPI_REG_INDIRECTWR);
+ priv->regbase + CQSPI_REG_INDIRECTWR);
/* Check indirect done status */
- ret = wait_for_bit_le32(plat->regbase + CQSPI_REG_INDIRECTWR,
+ ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTWR,
CQSPI_REG_INDIRECTWR_DONE, 0, 10, 0);
if (ret) {
printf("Indirect write clear completion error (%i)\n", ret);
@@ -892,13 +892,13 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_plat *plat,
failwr:
/* Cancel the indirect write */
writel(CQSPI_REG_INDIRECTWR_CANCEL,
- plat->regbase + CQSPI_REG_INDIRECTWR);
+ priv->regbase + CQSPI_REG_INDIRECTWR);
if (bounce_buf)
free(bounce_buf);
return ret;
}
-int cadence_qspi_apb_write_execute(struct cadence_spi_plat *plat,
+int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)
{
u32 to = op->addr.val;
@@ -916,14 +916,15 @@ int cadence_qspi_apb_write_execute(struct cadence_spi_plat *plat,
* mode. So, we can not use direct mode when in DTR mode for writing
* data.
*/
- if (!plat->dtr && plat->use_dac_mode && (to + len < plat->ahbsize)) {
- memcpy_toio(plat->ahbbase + to, buf, len);
- if (!cadence_qspi_wait_idle(plat->regbase))
+ cadence_qspi_apb_enable_linear_mode(true);
+ if (!priv->dtr && priv->use_dac_mode && (to + len < priv->ahbsize)) {
+ memcpy_toio(priv->ahbbase + to, buf, len);
+ if (!cadence_qspi_wait_idle(priv->regbase))
return -EIO;
return 0;
}
- return cadence_qspi_apb_indirect_write_execute(plat, len, buf);
+ return cadence_qspi_apb_indirect_write_execute(priv, len, buf);
}
void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy)
diff --git a/drivers/spi/mtk_snfi_spi.c b/drivers/spi/mtk_snfi_spi.c
index 65d0ce0981..5ea62776b4 100644
--- a/drivers/spi/mtk_snfi_spi.c
+++ b/drivers/spi/mtk_snfi_spi.c
@@ -202,7 +202,7 @@ static int mtk_snfi_exec_op(struct spi_slave *slave,
int addr_sh;
int ret;
- WATCHDOG_RESET();
+ schedule();
ret = mtk_snfi_mac_reset(priv);
if (ret)
diff --git a/drivers/spi/mtk_spim.c b/drivers/spi/mtk_spim.c
new file mode 100644
index 0000000000..b45ef529a5
--- /dev/null
+++ b/drivers/spi/mtk_spim.c
@@ -0,0 +1,701 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All Rights Reserved.
+ *
+ * Author: SkyLake.Huang <skylake.huang@mediatek.com>
+ */
+
+#include <clk.h>
+#include <cpu_func.h>
+#include <div64.h>
+#include <dm.h>
+#include <spi.h>
+#include <spi-mem.h>
+#include <stdbool.h>
+#include <watchdog.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/pinctrl.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#define SPI_CFG0_REG 0x0000
+#define SPI_CFG1_REG 0x0004
+#define SPI_TX_SRC_REG 0x0008
+#define SPI_RX_DST_REG 0x000c
+#define SPI_TX_DATA_REG 0x0010
+#define SPI_RX_DATA_REG 0x0014
+#define SPI_CMD_REG 0x0018
+#define SPI_IRQ_REG 0x001c
+#define SPI_STATUS_REG 0x0020
+#define SPI_PAD_SEL_REG 0x0024
+#define SPI_CFG2_REG 0x0028
+#define SPI_TX_SRC_REG_64 0x002c
+#define SPI_RX_DST_REG_64 0x0030
+#define SPI_CFG3_IPM_REG 0x0040
+
+#define SPI_CFG0_SCK_HIGH_OFFSET 0
+#define SPI_CFG0_SCK_LOW_OFFSET 8
+#define SPI_CFG0_CS_HOLD_OFFSET 16
+#define SPI_CFG0_CS_SETUP_OFFSET 24
+#define SPI_ADJUST_CFG0_CS_HOLD_OFFSET 0
+#define SPI_ADJUST_CFG0_CS_SETUP_OFFSET 16
+
+#define SPI_CFG1_CS_IDLE_OFFSET 0
+#define SPI_CFG1_PACKET_LOOP_OFFSET 8
+#define SPI_CFG1_PACKET_LENGTH_OFFSET 16
+#define SPI_CFG1_GET_TICKDLY_OFFSET 29
+
+#define SPI_CFG1_GET_TICKDLY_MASK GENMASK(31, 29)
+#define SPI_CFG1_CS_IDLE_MASK 0xff
+#define SPI_CFG1_PACKET_LOOP_MASK 0xff00
+#define SPI_CFG1_PACKET_LENGTH_MASK 0x3ff0000
+#define SPI_CFG1_IPM_PACKET_LENGTH_MASK GENMASK(31, 16)
+#define SPI_CFG2_SCK_HIGH_OFFSET 0
+#define SPI_CFG2_SCK_LOW_OFFSET 16
+#define SPI_CFG2_SCK_HIGH_MASK GENMASK(15, 0)
+#define SPI_CFG2_SCK_LOW_MASK GENMASK(31, 16)
+
+#define SPI_CMD_ACT BIT(0)
+#define SPI_CMD_RESUME BIT(1)
+#define SPI_CMD_RST BIT(2)
+#define SPI_CMD_PAUSE_EN BIT(4)
+#define SPI_CMD_DEASSERT BIT(5)
+#define SPI_CMD_SAMPLE_SEL BIT(6)
+#define SPI_CMD_CS_POL BIT(7)
+#define SPI_CMD_CPHA BIT(8)
+#define SPI_CMD_CPOL BIT(9)
+#define SPI_CMD_RX_DMA BIT(10)
+#define SPI_CMD_TX_DMA BIT(11)
+#define SPI_CMD_TXMSBF BIT(12)
+#define SPI_CMD_RXMSBF BIT(13)
+#define SPI_CMD_RX_ENDIAN BIT(14)
+#define SPI_CMD_TX_ENDIAN BIT(15)
+#define SPI_CMD_FINISH_IE BIT(16)
+#define SPI_CMD_PAUSE_IE BIT(17)
+#define SPI_CMD_IPM_NONIDLE_MODE BIT(19)
+#define SPI_CMD_IPM_SPIM_LOOP BIT(21)
+#define SPI_CMD_IPM_GET_TICKDLY_OFFSET 22
+
+#define SPI_CMD_IPM_GET_TICKDLY_MASK GENMASK(24, 22)
+
+#define PIN_MODE_CFG(x) ((x) / 2)
+
+#define SPI_CFG3_IPM_PIN_MODE_OFFSET 0
+#define SPI_CFG3_IPM_HALF_DUPLEX_DIR BIT(2)
+#define SPI_CFG3_IPM_HALF_DUPLEX_EN BIT(3)
+#define SPI_CFG3_IPM_XMODE_EN BIT(4)
+#define SPI_CFG3_IPM_NODATA_FLAG BIT(5)
+#define SPI_CFG3_IPM_CMD_BYTELEN_OFFSET 8
+#define SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET 12
+#define SPI_CFG3_IPM_DUMMY_BYTELEN_OFFSET 16
+
+#define SPI_CFG3_IPM_CMD_PIN_MODE_MASK GENMASK(1, 0)
+#define SPI_CFG3_IPM_CMD_BYTELEN_MASK GENMASK(11, 8)
+#define SPI_CFG3_IPM_ADDR_BYTELEN_MASK GENMASK(15, 12)
+#define SPI_CFG3_IPM_DUMMY_BYTELEN_MASK GENMASK(19, 16)
+
+#define MT8173_SPI_MAX_PAD_SEL 3
+
+#define MTK_SPI_PAUSE_INT_STATUS 0x2
+
+#define MTK_SPI_IDLE 0
+#define MTK_SPI_PAUSED 1
+
+#define MTK_SPI_MAX_FIFO_SIZE 32U
+#define MTK_SPI_PACKET_SIZE 1024
+#define MTK_SPI_IPM_PACKET_SIZE SZ_64K
+#define MTK_SPI_IPM_PACKET_LOOP SZ_256
+
+#define MTK_SPI_32BITS_MASK 0xffffffff
+
+#define DMA_ADDR_EXT_BITS 36
+#define DMA_ADDR_DEF_BITS 32
+
+#define CLK_TO_US(freq, clkcnt) DIV_ROUND_UP((clkcnt), (freq) / 1000000)
+
+/* struct mtk_spim_capability
+ * @enhance_timing: Some IC design adjust cfg register to enhance time accuracy
+ * @dma_ext: Some IC support DMA addr extension
+ * @ipm_design: The IPM IP design improves some features, and supports dual/quad mode
+ * @support_quad: Whether quad mode is supported
+ */
+struct mtk_spim_capability {
+ bool enhance_timing;
+ bool dma_ext;
+ bool ipm_design;
+ bool support_quad;
+};
+
+/* struct mtk_spim_priv
+ * @base: Base address of the spi controller
+ * @state: Controller state
+ * @sel_clk: Pad clock
+ * @spi_clk: Core clock
+ * @xfer_len: Current length of data for transfer
+ * @hw_cap: Controller capabilities
+ * @tick_dly: Used to postpone SPI sampling time
+ * @sample_sel: Sample edge of MISO
+ * @dev: udevice of this spi controller
+ * @tx_dma: Tx DMA address
+ * @rx_dma: Rx DMA address
+ */
+struct mtk_spim_priv {
+ void __iomem *base;
+ u32 state;
+ struct clk sel_clk, spi_clk;
+ u32 xfer_len;
+ struct mtk_spim_capability hw_cap;
+ u32 tick_dly;
+ u32 sample_sel;
+
+ struct device *dev;
+ dma_addr_t tx_dma;
+ dma_addr_t rx_dma;
+};
+
+static void mtk_spim_reset(struct mtk_spim_priv *priv)
+{
+ /* set the software reset bit in SPI_CMD_REG. */
+ setbits_le32(priv->base + SPI_CMD_REG, SPI_CMD_RST);
+ clrbits_le32(priv->base + SPI_CMD_REG, SPI_CMD_RST);
+}
+
+static int mtk_spim_hw_init(struct spi_slave *slave)
+{
+ struct udevice *bus = dev_get_parent(slave->dev);
+ struct mtk_spim_priv *priv = dev_get_priv(bus);
+ u16 cpha, cpol;
+ u32 reg_val;
+
+ cpha = slave->mode & SPI_CPHA ? 1 : 0;
+ cpol = slave->mode & SPI_CPOL ? 1 : 0;
+
+ if (priv->hw_cap.enhance_timing) {
+ if (priv->hw_cap.ipm_design) {
+ /* CFG3 reg only used for spi-mem,
+ * here write to default value
+ */
+ writel(0x0, priv->base + SPI_CFG3_IPM_REG);
+ clrsetbits_le32(priv->base + SPI_CMD_REG,
+ SPI_CMD_IPM_GET_TICKDLY_MASK,
+ priv->tick_dly <<
+ SPI_CMD_IPM_GET_TICKDLY_OFFSET);
+ } else {
+ clrsetbits_le32(priv->base + SPI_CFG1_REG,
+ SPI_CFG1_GET_TICKDLY_MASK,
+ priv->tick_dly <<
+ SPI_CFG1_GET_TICKDLY_OFFSET);
+ }
+ }
+
+ reg_val = readl(priv->base + SPI_CMD_REG);
+ if (priv->hw_cap.ipm_design) {
+ /* SPI transfer without idle time until packet length done */
+ reg_val |= SPI_CMD_IPM_NONIDLE_MODE;
+ if (slave->mode & SPI_LOOP)
+ reg_val |= SPI_CMD_IPM_SPIM_LOOP;
+ else
+ reg_val &= ~SPI_CMD_IPM_SPIM_LOOP;
+ }
+
+ if (cpha)
+ reg_val |= SPI_CMD_CPHA;
+ else
+ reg_val &= ~SPI_CMD_CPHA;
+ if (cpol)
+ reg_val |= SPI_CMD_CPOL;
+ else
+ reg_val &= ~SPI_CMD_CPOL;
+
+ /* set the mlsbx and mlsbtx */
+ if (slave->mode & SPI_LSB_FIRST) {
+ reg_val &= ~SPI_CMD_TXMSBF;
+ reg_val &= ~SPI_CMD_RXMSBF;
+ } else {
+ reg_val |= SPI_CMD_TXMSBF;
+ reg_val |= SPI_CMD_RXMSBF;
+ }
+
+ /* do not reverse tx/rx endian */
+ reg_val &= ~SPI_CMD_TX_ENDIAN;
+ reg_val &= ~SPI_CMD_RX_ENDIAN;
+
+ if (priv->hw_cap.enhance_timing) {
+ /* set CS polarity */
+ if (slave->mode & SPI_CS_HIGH)
+ reg_val |= SPI_CMD_CS_POL;
+ else
+ reg_val &= ~SPI_CMD_CS_POL;
+
+ if (priv->sample_sel)
+ reg_val |= SPI_CMD_SAMPLE_SEL;
+ else
+ reg_val &= ~SPI_CMD_SAMPLE_SEL;
+ }
+
+ /* disable dma mode */
+ reg_val &= ~(SPI_CMD_TX_DMA | SPI_CMD_RX_DMA);
+
+ /* disable deassert mode */
+ reg_val &= ~SPI_CMD_DEASSERT;
+
+ writel(reg_val, priv->base + SPI_CMD_REG);
+
+ return 0;
+}
+
+static void mtk_spim_prepare_transfer(struct mtk_spim_priv *priv,
+ u32 speed_hz)
+{
+ u32 spi_clk_hz, div, sck_time, cs_time, reg_val;
+
+ spi_clk_hz = clk_get_rate(&priv->spi_clk);
+ if (speed_hz <= spi_clk_hz / 4)
+ div = DIV_ROUND_UP(spi_clk_hz, speed_hz);
+ else
+ div = 4;
+
+ sck_time = (div + 1) / 2;
+ cs_time = sck_time * 2;
+
+ if (priv->hw_cap.enhance_timing) {
+ reg_val = ((sck_time - 1) & 0xffff)
+ << SPI_CFG2_SCK_HIGH_OFFSET;
+ reg_val |= ((sck_time - 1) & 0xffff)
+ << SPI_CFG2_SCK_LOW_OFFSET;
+ writel(reg_val, priv->base + SPI_CFG2_REG);
+
+ reg_val = ((cs_time - 1) & 0xffff)
+ << SPI_ADJUST_CFG0_CS_HOLD_OFFSET;
+ reg_val |= ((cs_time - 1) & 0xffff)
+ << SPI_ADJUST_CFG0_CS_SETUP_OFFSET;
+ writel(reg_val, priv->base + SPI_CFG0_REG);
+ } else {
+ reg_val = ((sck_time - 1) & 0xff)
+ << SPI_CFG0_SCK_HIGH_OFFSET;
+ reg_val |= ((sck_time - 1) & 0xff) << SPI_CFG0_SCK_LOW_OFFSET;
+ reg_val |= ((cs_time - 1) & 0xff) << SPI_CFG0_CS_HOLD_OFFSET;
+ reg_val |= ((cs_time - 1) & 0xff) << SPI_CFG0_CS_SETUP_OFFSET;
+ writel(reg_val, priv->base + SPI_CFG0_REG);
+ }
+
+ reg_val = readl(priv->base + SPI_CFG1_REG);
+ reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
+ reg_val |= ((cs_time - 1) & 0xff) << SPI_CFG1_CS_IDLE_OFFSET;
+ writel(reg_val, priv->base + SPI_CFG1_REG);
+}
+
+/**
+ * mtk_spim_setup_packet() - setup packet format.
+ * @priv: controller priv
+ *
+ * This controller sents/receives data in packets. The packet size is
+ * configurable.
+ *
+ * This function calculates the maximum packet size available for current
+ * data, and calculates the number of packets required to sent/receive data
+ * as much as possible.
+ */
+static void mtk_spim_setup_packet(struct mtk_spim_priv *priv)
+{
+ u32 packet_size, packet_loop, reg_val;
+
+ /* Calculate maximum packet size */
+ if (priv->hw_cap.ipm_design)
+ packet_size = min_t(u32,
+ priv->xfer_len,
+ MTK_SPI_IPM_PACKET_SIZE);
+ else
+ packet_size = min_t(u32,
+ priv->xfer_len,
+ MTK_SPI_PACKET_SIZE);
+
+ /* Calculates number of packets to sent/receive */
+ packet_loop = priv->xfer_len / packet_size;
+
+ reg_val = readl(priv->base + SPI_CFG1_REG);
+ if (priv->hw_cap.ipm_design)
+ reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK;
+ else
+ reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK;
+
+ reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET;
+
+ reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK;
+
+ reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET;
+
+ writel(reg_val, priv->base + SPI_CFG1_REG);
+}
+
+static void mtk_spim_enable_transfer(struct mtk_spim_priv *priv)
+{
+ u32 cmd;
+
+ cmd = readl(priv->base + SPI_CMD_REG);
+ if (priv->state == MTK_SPI_IDLE)
+ cmd |= SPI_CMD_ACT;
+ else
+ cmd |= SPI_CMD_RESUME;
+ writel(cmd, priv->base + SPI_CMD_REG);
+}
+
+static bool mtk_spim_supports_op(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ struct udevice *bus = dev_get_parent(slave->dev);
+ struct mtk_spim_priv *priv = dev_get_priv(bus);
+
+ if (op->cmd.buswidth == 0 || op->cmd.buswidth > 4 ||
+ op->addr.buswidth > 4 || op->dummy.buswidth > 4 ||
+ op->data.buswidth > 4)
+ return false;
+
+ if (!priv->hw_cap.support_quad && (op->cmd.buswidth > 2 ||
+ op->addr.buswidth > 2 || op->dummy.buswidth > 2 ||
+ op->data.buswidth > 2))
+ return false;
+
+ if (op->addr.nbytes && op->dummy.nbytes &&
+ op->addr.buswidth != op->dummy.buswidth)
+ return false;
+
+ if (op->addr.nbytes + op->dummy.nbytes > 16)
+ return false;
+
+ if (op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) {
+ if (op->data.nbytes / MTK_SPI_IPM_PACKET_SIZE >
+ MTK_SPI_IPM_PACKET_LOOP ||
+ op->data.nbytes % MTK_SPI_IPM_PACKET_SIZE != 0)
+ return false;
+ }
+
+ return true;
+}
+
+static void mtk_spim_setup_dma_xfer(struct mtk_spim_priv *priv,
+ const struct spi_mem_op *op)
+{
+ writel((u32)(priv->tx_dma & MTK_SPI_32BITS_MASK),
+ priv->base + SPI_TX_SRC_REG);
+
+ if (priv->hw_cap.dma_ext)
+ writel((u32)(priv->tx_dma >> 32),
+ priv->base + SPI_TX_SRC_REG_64);
+
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ writel((u32)(priv->rx_dma & MTK_SPI_32BITS_MASK),
+ priv->base + SPI_RX_DST_REG);
+
+ if (priv->hw_cap.dma_ext)
+ writel((u32)(priv->rx_dma >> 32),
+ priv->base + SPI_RX_DST_REG_64);
+ }
+}
+
+static int mtk_spim_transfer_wait(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ struct udevice *bus = dev_get_parent(slave->dev);
+ struct mtk_spim_priv *priv = dev_get_priv(bus);
+ u32 sck_l, sck_h, spi_bus_clk, clk_count, reg;
+ ulong us = 1;
+ int ret = 0;
+
+ if (op->data.dir == SPI_MEM_NO_DATA)
+ clk_count = 32;
+ else
+ clk_count = op->data.nbytes;
+
+ spi_bus_clk = clk_get_rate(&priv->spi_clk);
+ sck_l = readl(priv->base + SPI_CFG2_REG) >> SPI_CFG2_SCK_LOW_OFFSET;
+ sck_h = readl(priv->base + SPI_CFG2_REG) & SPI_CFG2_SCK_HIGH_MASK;
+ do_div(spi_bus_clk, sck_l + sck_h + 2);
+
+ us = CLK_TO_US(spi_bus_clk, clk_count * 8);
+ us += 1000 * 1000; /* 1s tolerance */
+
+ if (us > UINT_MAX)
+ us = UINT_MAX;
+
+ ret = readl_poll_timeout(priv->base + SPI_STATUS_REG, reg,
+ reg & 0x1, us);
+ if (ret < 0) {
+ dev_err(priv->dev, "transfer timeout, val: 0x%lx\n", us);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int mtk_spim_exec_op(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ struct udevice *bus = dev_get_parent(slave->dev);
+ struct mtk_spim_priv *priv = dev_get_priv(bus);
+ u32 reg_val, nio = 1, tx_size;
+ char *tx_tmp_buf;
+ char *rx_tmp_buf;
+ int i, ret = 0;
+
+ mtk_spim_reset(priv);
+ mtk_spim_hw_init(slave);
+ mtk_spim_prepare_transfer(priv, slave->max_hz);
+
+ reg_val = readl(priv->base + SPI_CFG3_IPM_REG);
+ /* opcode byte len */
+ reg_val &= ~SPI_CFG3_IPM_CMD_BYTELEN_MASK;
+ reg_val |= 1 << SPI_CFG3_IPM_CMD_BYTELEN_OFFSET;
+
+ /* addr & dummy byte len */
+ if (op->addr.nbytes || op->dummy.nbytes)
+ reg_val |= (op->addr.nbytes + op->dummy.nbytes) <<
+ SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET;
+
+ /* data byte len */
+ if (!op->data.nbytes) {
+ reg_val |= SPI_CFG3_IPM_NODATA_FLAG;
+ writel(0, priv->base + SPI_CFG1_REG);
+ } else {
+ reg_val &= ~SPI_CFG3_IPM_NODATA_FLAG;
+ priv->xfer_len = op->data.nbytes;
+ mtk_spim_setup_packet(priv);
+ }
+
+ if (op->addr.nbytes || op->dummy.nbytes) {
+ if (op->addr.buswidth == 1 || op->dummy.buswidth == 1)
+ reg_val |= SPI_CFG3_IPM_XMODE_EN;
+ else
+ reg_val &= ~SPI_CFG3_IPM_XMODE_EN;
+ }
+
+ if (op->addr.buswidth == 2 ||
+ op->dummy.buswidth == 2 ||
+ op->data.buswidth == 2)
+ nio = 2;
+ else if (op->addr.buswidth == 4 ||
+ op->dummy.buswidth == 4 ||
+ op->data.buswidth == 4)
+ nio = 4;
+
+ reg_val &= ~SPI_CFG3_IPM_CMD_PIN_MODE_MASK;
+ reg_val |= PIN_MODE_CFG(nio) << SPI_CFG3_IPM_PIN_MODE_OFFSET;
+
+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
+ else
+ reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR;
+ writel(reg_val, priv->base + SPI_CFG3_IPM_REG);
+
+ tx_size = 1 + op->addr.nbytes + op->dummy.nbytes;
+ if (op->data.dir == SPI_MEM_DATA_OUT)
+ tx_size += op->data.nbytes;
+
+ tx_size = max(tx_size, (u32)32);
+
+ /* Fill up tx data */
+ tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL);
+ if (!tx_tmp_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ tx_tmp_buf[0] = op->cmd.opcode;
+
+ if (op->addr.nbytes) {
+ for (i = 0; i < op->addr.nbytes; i++)
+ tx_tmp_buf[i + 1] = op->addr.val >>
+ (8 * (op->addr.nbytes - i - 1));
+ }
+
+ if (op->dummy.nbytes)
+ memset(tx_tmp_buf + op->addr.nbytes + 1, 0xff,
+ op->dummy.nbytes);
+
+ if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
+ memcpy(tx_tmp_buf + op->dummy.nbytes + op->addr.nbytes + 1,
+ op->data.buf.out, op->data.nbytes);
+ /* Finish filling up tx data */
+
+ priv->tx_dma = dma_map_single(tx_tmp_buf, tx_size, DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->dev, priv->tx_dma)) {
+ ret = -ENOMEM;
+ goto tx_free;
+ }
+
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ if (!IS_ALIGNED((size_t)op->data.buf.in, 4)) {
+ rx_tmp_buf = kzalloc(op->data.nbytes, GFP_KERNEL);
+ if (!rx_tmp_buf) {
+ ret = -ENOMEM;
+ goto tx_unmap;
+ }
+ } else {
+ rx_tmp_buf = op->data.buf.in;
+ }
+
+ priv->rx_dma = dma_map_single(rx_tmp_buf, op->data.nbytes,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(priv->dev, priv->rx_dma)) {
+ ret = -ENOMEM;
+ goto rx_free;
+ }
+ }
+
+ reg_val = readl(priv->base + SPI_CMD_REG);
+ reg_val |= SPI_CMD_TX_DMA;
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ reg_val |= SPI_CMD_RX_DMA;
+
+ writel(reg_val, priv->base + SPI_CMD_REG);
+
+ mtk_spim_setup_dma_xfer(priv, op);
+
+ mtk_spim_enable_transfer(priv);
+
+ /* Wait for the interrupt. */
+ ret = mtk_spim_transfer_wait(slave, op);
+ if (ret)
+ goto rx_unmap;
+
+ if (op->data.dir == SPI_MEM_DATA_IN &&
+ !IS_ALIGNED((size_t)op->data.buf.in, 4))
+ memcpy(op->data.buf.in, rx_tmp_buf, op->data.nbytes);
+
+rx_unmap:
+ /* spi disable dma */
+ reg_val = readl(priv->base + SPI_CMD_REG);
+ reg_val &= ~SPI_CMD_TX_DMA;
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ reg_val &= ~SPI_CMD_RX_DMA;
+ writel(reg_val, priv->base + SPI_CMD_REG);
+
+ writel(0, priv->base + SPI_TX_SRC_REG);
+ writel(0, priv->base + SPI_RX_DST_REG);
+
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ dma_unmap_single(priv->rx_dma,
+ op->data.nbytes, DMA_FROM_DEVICE);
+rx_free:
+ if (op->data.dir == SPI_MEM_DATA_IN &&
+ !IS_ALIGNED((size_t)op->data.buf.in, 4))
+ kfree(rx_tmp_buf);
+tx_unmap:
+ dma_unmap_single(priv->tx_dma,
+ tx_size, DMA_TO_DEVICE);
+tx_free:
+ kfree(tx_tmp_buf);
+exit:
+ return ret;
+}
+
+static int mtk_spim_adjust_op_size(struct spi_slave *slave,
+ struct spi_mem_op *op)
+{
+ int opcode_len;
+
+ if (!op->data.nbytes)
+ return 0;
+
+ if (op->data.dir != SPI_MEM_NO_DATA) {
+ opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes;
+ if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) {
+ op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE - opcode_len;
+ /* force data buffer dma-aligned. */
+ op->data.nbytes -= op->data.nbytes % 4;
+ }
+ }
+
+ return 0;
+}
+
+static int mtk_spim_get_attr(struct mtk_spim_priv *priv, struct udevice *dev)
+{
+ int ret;
+
+ priv->hw_cap.enhance_timing = dev_read_bool(dev, "enhance_timing");
+ priv->hw_cap.dma_ext = dev_read_bool(dev, "dma_ext");
+ priv->hw_cap.ipm_design = dev_read_bool(dev, "ipm_design");
+ priv->hw_cap.support_quad = dev_read_bool(dev, "support_quad");
+
+ ret = dev_read_u32(dev, "tick_dly", &priv->tick_dly);
+ if (ret < 0)
+ dev_err(priv->dev, "tick dly not set.\n");
+
+ ret = dev_read_u32(dev, "sample_sel", &priv->sample_sel);
+ if (ret < 0)
+ dev_err(priv->dev, "sample sel not set.\n");
+
+ return ret;
+}
+
+static int mtk_spim_probe(struct udevice *dev)
+{
+ struct mtk_spim_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->base = (void __iomem *)devfdt_get_addr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ mtk_spim_get_attr(priv, dev);
+
+ ret = clk_get_by_name(dev, "sel-clk", &priv->sel_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to get sel-clk\n");
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "spi-clk", &priv->spi_clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to get spi-clk\n");
+ return ret;
+ }
+
+ clk_enable(&priv->sel_clk);
+ clk_enable(&priv->spi_clk);
+
+ return 0;
+}
+
+static int mtk_spim_set_speed(struct udevice *dev, uint speed)
+{
+ return 0;
+}
+
+static int mtk_spim_set_mode(struct udevice *dev, uint mode)
+{
+ return 0;
+}
+
+static const struct spi_controller_mem_ops mtk_spim_mem_ops = {
+ .adjust_op_size = mtk_spim_adjust_op_size,
+ .supports_op = mtk_spim_supports_op,
+ .exec_op = mtk_spim_exec_op
+};
+
+static const struct dm_spi_ops mtk_spim_ops = {
+ .mem_ops = &mtk_spim_mem_ops,
+ .set_speed = mtk_spim_set_speed,
+ .set_mode = mtk_spim_set_mode,
+};
+
+static const struct udevice_id mtk_spim_ids[] = {
+ { .compatible = "mediatek,ipm-spi" },
+ {}
+};
+
+U_BOOT_DRIVER(mtk_spim) = {
+ .name = "mtk_spim",
+ .id = UCLASS_SPI,
+ .of_match = mtk_spim_ids,
+ .ops = &mtk_spim_ops,
+ .priv_auto = sizeof(struct mtk_spim_priv),
+ .probe = mtk_spim_probe,
+};
diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c
index c2a7ee232b..4bc38beaa6 100644
--- a/drivers/spi/octeon_spi.c
+++ b/drivers/spi/octeon_spi.c
@@ -126,7 +126,7 @@ static void octeon_spi_wait_ready(struct udevice *dev)
do {
mpi_sts = readq(base + MPI_STS);
- WATCHDOG_RESET();
+ schedule();
} while (mpi_sts & MPI_STS_BUSY);
debug("%s(%s)\n", __func__, dev->name);
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
new file mode 100644
index 0000000000..a3c9633382
--- /dev/null
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -0,0 +1,1218 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ASPEED FMC/SPI Controller driver
+ *
+ * Copyright (c) 2022 ASPEED Corporation.
+ * Copyright (c) 2022 IBM Corporation.
+ *
+ * Author:
+ * Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
+ * Cedric Le Goater <clg@kaod.org>
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/sizes.h>
+#include <malloc.h>
+#include <spi.h>
+#include <spi-mem.h>
+
+#define ASPEED_SPI_MAX_CS 5
+
+#define CTRL_IO_SINGLE_DATA 0
+#define CTRL_IO_QUAD_DATA BIT(30)
+#define CTRL_IO_DUAL_DATA BIT(29)
+
+#define CTRL_IO_MODE_USER GENMASK(1, 0)
+#define CTRL_IO_MODE_CMD_READ BIT(0)
+#define CTRL_IO_MODE_CMD_WRITE BIT(1)
+#define CTRL_STOP_ACTIVE BIT(2)
+
+struct aspeed_spi_regs {
+ u32 conf; /* 0x00 CE Type Setting */
+ u32 ctrl; /* 0x04 CE Control */
+ u32 intr_ctrl; /* 0x08 Interrupt Control and Status */
+ u32 cmd_ctrl; /* 0x0c Command Control */
+ u32 ce_ctrl[ASPEED_SPI_MAX_CS]; /* 0x10 .. 0x20 CEx Control */
+ u32 _reserved0[3]; /* .. */
+ u32 segment_addr[ASPEED_SPI_MAX_CS]; /* 0x30 .. 0x40 Segment Address */
+ u32 _reserved1[3]; /* .. */
+ u32 soft_rst_cmd_ctrl; /* 0x50 Auto Soft-Reset Command Control */
+ u32 _reserved2[11]; /* .. */
+ u32 dma_ctrl; /* 0x80 DMA Control/Status */
+ u32 dma_flash_addr; /* 0x84 DMA Flash Side Address */
+ u32 dma_dram_addr; /* 0x88 DMA DRAM Side Address */
+ u32 dma_len; /* 0x8c DMA Length Register */
+ u32 dma_checksum; /* 0x90 Checksum Calculation Result */
+ u32 timings[ASPEED_SPI_MAX_CS]; /* 0x94 Read Timing Compensation */
+};
+
+struct aspeed_spi_plat {
+ u8 max_cs;
+ void __iomem *ahb_base; /* AHB address base for all flash devices. */
+ fdt_size_t ahb_sz; /* Overall AHB window size for all flash device. */
+ u32 hclk_rate; /* AHB clock rate */
+};
+
+struct aspeed_spi_flash {
+ void __iomem *ahb_base;
+ u32 ahb_decoded_sz;
+ u32 ce_ctrl_user;
+ u32 ce_ctrl_read;
+ u32 max_freq;
+};
+
+struct aspeed_spi_priv {
+ u32 num_cs;
+ struct aspeed_spi_regs *regs;
+ struct aspeed_spi_info *info;
+ struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
+ bool fixed_decoded_range;
+};
+
+struct aspeed_spi_info {
+ u32 io_mode_mask;
+ u32 max_bus_width;
+ u32 min_decoded_sz;
+ u32 clk_ctrl_mask;
+ void (*set_4byte)(struct udevice *bus, u32 cs);
+ u32 (*segment_start)(struct udevice *bus, u32 reg);
+ u32 (*segment_end)(struct udevice *bus, u32 reg);
+ u32 (*segment_reg)(u32 start, u32 end);
+ int (*adjust_decoded_sz)(struct udevice *bus);
+ u32 (*get_clk_setting)(struct udevice *dev, uint hz);
+};
+
+struct aspeed_spi_decoded_range {
+ u32 cs;
+ u32 ahb_base;
+ u32 sz;
+};
+
+static const struct aspeed_spi_info ast2400_spi_info;
+static const struct aspeed_spi_info ast2500_fmc_info;
+static const struct aspeed_spi_info ast2500_spi_info;
+static int aspeed_spi_decoded_range_config(struct udevice *bus);
+static int aspeed_spi_trim_decoded_size(struct udevice *bus);
+
+static u32 aspeed_spi_get_io_mode(u32 bus_width)
+{
+ switch (bus_width) {
+ case 1:
+ return CTRL_IO_SINGLE_DATA;
+ case 2:
+ return CTRL_IO_DUAL_DATA;
+ case 4:
+ return CTRL_IO_QUAD_DATA;
+ default:
+ /* keep in default value */
+ return CTRL_IO_SINGLE_DATA;
+ }
+}
+
+static u32 ast2400_spi_segment_start(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ u32 start_offset = ((reg >> 16) & 0xff) << 23;
+
+ if (start_offset == 0)
+ return (u32)plat->ahb_base;
+
+ return (u32)plat->ahb_base + start_offset;
+}
+
+static u32 ast2400_spi_segment_end(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ u32 end_offset = ((reg >> 24) & 0xff) << 23;
+
+ /* Meaningless end_offset, set to physical ahb base. */
+ if (end_offset == 0)
+ return (u32)plat->ahb_base;
+
+ return (u32)plat->ahb_base + end_offset;
+}
+
+static u32 ast2400_spi_segment_reg(u32 start, u32 end)
+{
+ if (start == end)
+ return 0;
+
+ return ((((start) >> 23) & 0xff) << 16) | ((((end) >> 23) & 0xff) << 24);
+}
+
+static void ast2400_fmc_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 reg_val;
+
+ reg_val = readl(&priv->regs->ctrl);
+ reg_val |= 0x1 << cs;
+ writel(reg_val, &priv->regs->ctrl);
+}
+
+static void ast2400_spi_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct aspeed_spi_flash *flash = &priv->flashes[cs];
+
+ flash->ce_ctrl_read |= BIT(13);
+ writel(flash->ce_ctrl_read, &priv->regs->ctrl);
+}
+
+/* Transfer maximum clock frequency to register setting */
+static u32 ast2400_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+ struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+ u32 hclk_clk = plat->hclk_rate;
+ u32 hclk_div = 0x0000; /* default value */
+ u32 i;
+ bool found = false;
+ /* HCLK/1 .. HCLK/16 */
+ u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+ 11, 3, 10, 2, 9, 1, 8, 0};
+
+ /* FMC/SPIR10[11:8] */
+ for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+ if (hclk_clk / (i + 1) <= max_hz) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ hclk_div = hclk_masks[i] << 8;
+ priv->flashes[slave_plat->cs].max_freq = hclk_clk / (i + 1);
+ }
+
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
+ hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: %d (mask %x), speed: %d\n",
+ i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+ }
+
+ return hclk_div;
+}
+
+static u32 ast2500_spi_segment_start(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ u32 start_offset = ((reg >> 16) & 0xff) << 23;
+
+ if (start_offset == 0)
+ return (u32)plat->ahb_base;
+
+ return (u32)plat->ahb_base + start_offset;
+}
+
+static u32 ast2500_spi_segment_end(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ u32 end_offset = ((reg >> 24) & 0xff) << 23;
+
+ /* Meaningless end_offset, set to physical ahb base. */
+ if (end_offset == 0)
+ return (u32)plat->ahb_base;
+
+ return (u32)plat->ahb_base + end_offset;
+}
+
+static u32 ast2500_spi_segment_reg(u32 start, u32 end)
+{
+ if (start == end)
+ return 0;
+
+ return ((((start) >> 23) & 0xff) << 16) | ((((end) >> 23) & 0xff) << 24);
+}
+
+static void ast2500_spi_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 reg_val;
+
+ reg_val = readl(&priv->regs->ctrl);
+ reg_val |= 0x1 << cs;
+ writel(reg_val, &priv->regs->ctrl);
+}
+
+/*
+ * For AST2500, the minimum address decoded size for each CS
+ * is 8MB instead of zero. This address decoded size is
+ * mandatory for each CS no matter whether it will be used.
+ * This is a HW limitation.
+ */
+static int ast2500_adjust_decoded_size(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct aspeed_spi_flash *flashes = &priv->flashes[0];
+ int ret;
+ int i;
+ int cs;
+ u32 pre_sz;
+ u32 lack_sz;
+
+ /* Assign min_decoded_sz to unused CS. */
+ for (cs = priv->num_cs; cs < plat->max_cs; cs++)
+ flashes[cs].ahb_decoded_sz = priv->info->min_decoded_sz;
+
+ /*
+ * If commnad mode or normal mode is used, the start address of a
+ * decoded range should be multiple of its related flash size.
+ * Namely, the total decoded size from flash 0 to flash N should
+ * be multiple of the size of flash (N + 1).
+ */
+ for (cs = priv->num_cs - 1; cs >= 0; cs--) {
+ pre_sz = 0;
+ for (i = 0; i < cs; i++)
+ pre_sz += flashes[i].ahb_decoded_sz;
+
+ if (flashes[cs].ahb_decoded_sz != 0 &&
+ (pre_sz % flashes[cs].ahb_decoded_sz) != 0) {
+ lack_sz = flashes[cs].ahb_decoded_sz -
+ (pre_sz % flashes[cs].ahb_decoded_sz);
+ flashes[0].ahb_decoded_sz += lack_sz;
+ }
+ }
+
+ ret = aspeed_spi_trim_decoded_size(bus);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static u32 ast2500_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+ struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+ u32 hclk_clk = plat->hclk_rate;
+ u32 hclk_div = 0x0000; /* default value */
+ u32 i;
+ bool found = false;
+ /* HCLK/1 .. HCLK/16 */
+ u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+ 11, 3, 10, 2, 9, 1, 8, 0};
+
+ /* FMC/SPIR10[11:8] */
+ for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+ if (hclk_clk / (i + 1) <= max_hz) {
+ found = true;
+ priv->flashes[slave_plat->cs].max_freq =
+ hclk_clk / (i + 1);
+ break;
+ }
+ }
+
+ if (found) {
+ hclk_div = hclk_masks[i] << 8;
+ goto end;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+ if (hclk_clk / ((i + 1) * 4) <= max_hz) {
+ found = true;
+ priv->flashes[slave_plat->cs].max_freq =
+ hclk_clk / ((i + 1) * 4);
+ break;
+ }
+ }
+
+ if (found)
+ hclk_div = BIT(13) | (hclk_masks[i] << 8);
+
+end:
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
+ hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "h_div: %d (mask %x), speed: %d\n",
+ i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+ }
+
+ return hclk_div;
+}
+
+static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ u32 start_offset = (reg << 16) & 0x0ff00000;
+
+ if (start_offset == 0)
+ return (u32)plat->ahb_base;
+
+ return (u32)plat->ahb_base + start_offset;
+}
+
+static u32 ast2600_spi_segment_end(struct udevice *bus, u32 reg)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ u32 end_offset = reg & 0x0ff00000;
+
+ /* Meaningless end_offset, set to physical ahb base. */
+ if (end_offset == 0)
+ return (u32)plat->ahb_base;
+
+ return (u32)plat->ahb_base + end_offset + 0x100000;
+}
+
+static u32 ast2600_spi_segment_reg(u32 start, u32 end)
+{
+ if (start == end)
+ return 0;
+
+ return ((start & 0x0ff00000) >> 16) | ((end - 0x100000) & 0x0ff00000);
+}
+
+static void ast2600_spi_chip_set_4byte(struct udevice *bus, u32 cs)
+{
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 reg_val;
+
+ reg_val = readl(&priv->regs->ctrl);
+ reg_val |= 0x11 << cs;
+ writel(reg_val, &priv->regs->ctrl);
+}
+
+static int ast2600_adjust_decoded_size(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct aspeed_spi_flash *flashes = &priv->flashes[0];
+ int ret;
+ int i;
+ int cs;
+ u32 pre_sz;
+ u32 lack_sz;
+
+ /* Close unused CS. */
+ for (cs = priv->num_cs; cs < plat->max_cs; cs++)
+ flashes[cs].ahb_decoded_sz = 0;
+
+ /*
+ * If commnad mode or normal mode is used, the start address of a
+ * decoded range should be multiple of its related flash size.
+ * Namely, the total decoded size from flash 0 to flash N should
+ * be multiple of the size of flash (N + 1).
+ */
+ for (cs = priv->num_cs - 1; cs >= 0; cs--) {
+ pre_sz = 0;
+ for (i = 0; i < cs; i++)
+ pre_sz += flashes[i].ahb_decoded_sz;
+
+ if (flashes[cs].ahb_decoded_sz != 0 &&
+ (pre_sz % flashes[cs].ahb_decoded_sz) != 0) {
+ lack_sz = flashes[cs].ahb_decoded_sz -
+ (pre_sz % flashes[cs].ahb_decoded_sz);
+ flashes[0].ahb_decoded_sz += lack_sz;
+ }
+ }
+
+ ret = aspeed_spi_trim_decoded_size(bus);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static u32 ast2600_get_clk_setting(struct udevice *dev, uint max_hz)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(dev->parent);
+ struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+ u32 hclk_clk = plat->hclk_rate;
+ u32 hclk_div = 0x0400; /* default value */
+ u32 i, j;
+ bool found = false;
+ /* HCLK/1 .. HCLK/16 */
+ u32 hclk_masks[] = {15, 7, 14, 6, 13, 5, 12, 4,
+ 11, 3, 10, 2, 9, 1, 8, 0};
+
+ /* FMC/SPIR10[27:24] */
+ for (j = 0; j < 0xf; j++) {
+ /* FMC/SPIR10[11:8] */
+ for (i = 0; i < ARRAY_SIZE(hclk_masks); i++) {
+ if (i == 0 && j == 0)
+ continue;
+
+ if (hclk_clk / (i + 1 + (j * 16)) <= max_hz) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ hclk_div = ((j << 24) | hclk_masks[i] << 8);
+ priv->flashes[slave_plat->cs].max_freq =
+ hclk_clk / (i + 1 + j * 16);
+ break;
+ }
+ }
+
+ dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", found ? "yes" : "no",
+ hclk_clk, max_hz);
+
+ if (found) {
+ dev_dbg(dev, "base_clk: %d, h_div: %d (mask %x), speed: %d\n",
+ j, i + 1, hclk_masks[i], priv->flashes[slave_plat->cs].max_freq);
+ }
+
+ return hclk_div;
+}
+
+/*
+ * As the flash size grows up, we need to trim some decoded
+ * size if needed for the sake of conforming the maximum
+ * decoded size. We trim the decoded size from the largest
+ * CS in order to avoid affecting the default boot up sequence
+ * from CS0 where command mode or normal mode is used.
+ * Notice, if a CS decoded size is trimmed, command mode may
+ * not work perfectly on that CS.
+ */
+static int aspeed_spi_trim_decoded_size(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct aspeed_spi_flash *flashes = &priv->flashes[0];
+ u32 total_sz;
+ int cs = plat->max_cs - 1;
+ u32 i;
+
+ do {
+ total_sz = 0;
+ for (i = 0; i < plat->max_cs; i++)
+ total_sz += flashes[i].ahb_decoded_sz;
+
+ if (flashes[cs].ahb_decoded_sz <= priv->info->min_decoded_sz)
+ cs--;
+
+ if (cs < 0)
+ return -ENOMEM;
+
+ if (total_sz > plat->ahb_sz) {
+ flashes[cs].ahb_decoded_sz -=
+ priv->info->min_decoded_sz;
+ total_sz -= priv->info->min_decoded_sz;
+ }
+ } while (total_sz > plat->ahb_sz);
+
+ return 0;
+}
+
+static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf,
+ size_t len)
+{
+ size_t offset = 0;
+
+ if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) &&
+ IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
+ readsl(ahb_base, buf, len >> 2);
+ offset = len & ~0x3;
+ len -= offset;
+ }
+
+ readsb(ahb_base, (u8 *)buf + offset, len);
+
+ return 0;
+}
+
+static int aspeed_spi_write_to_ahb(void __iomem *ahb_base, const void *buf,
+ size_t len)
+{
+ size_t offset = 0;
+
+ if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) &&
+ IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
+ writesl(ahb_base, buf, len >> 2);
+ offset = len & ~0x3;
+ len -= offset;
+ }
+
+ writesb(ahb_base, (u8 *)buf + offset, len);
+
+ return 0;
+}
+
+/*
+ * Currently, only support 1-1-1, 1-1-2 or 1-1-4
+ * SPI NOR flash operation format.
+ */
+static bool aspeed_spi_supports_op(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ struct udevice *bus = slave->dev->parent;
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+
+ if (op->cmd.buswidth > 1)
+ return false;
+
+ if (op->addr.nbytes != 0) {
+ if (op->addr.buswidth > 1)
+ return false;
+ if (op->addr.nbytes < 3 || op->addr.nbytes > 4)
+ return false;
+ }
+
+ if (op->dummy.nbytes != 0) {
+ if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
+ return false;
+ }
+
+ if (op->data.nbytes != 0 &&
+ op->data.buswidth > priv->info->max_bus_width)
+ return false;
+
+ if (!spi_mem_default_supports_op(slave, op))
+ return false;
+
+ return true;
+}
+
+static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave,
+ const struct spi_mem_op *op)
+{
+ struct udevice *dev = slave->dev;
+ struct udevice *bus = dev->parent;
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev);
+ u32 cs = slave_plat->cs;
+ u32 ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
+ u32 ce_ctrl_val;
+ struct aspeed_spi_flash *flash = &priv->flashes[cs];
+ u8 dummy_data[16] = {0};
+ u8 addr[4] = {0};
+ int i;
+
+ dev_dbg(dev, "cmd:%x(%d),addr:%llx(%d),dummy:%d(%d),data_len:0x%x(%d)\n",
+ op->cmd.opcode, op->cmd.buswidth, op->addr.val,
+ op->addr.buswidth, op->dummy.nbytes, op->dummy.buswidth,
+ op->data.nbytes, op->data.buswidth);
+
+ if (priv->info == &ast2400_spi_info)
+ ce_ctrl_reg = (u32)&priv->regs->ctrl;
+
+ /*
+ * Set controller to 4-byte address mode
+ * if flash is in 4-byte address mode.
+ */
+ if (op->cmd.opcode == SPINOR_OP_EN4B)
+ priv->info->set_4byte(bus, cs);
+
+ /* Start user mode */
+ ce_ctrl_val = flash->ce_ctrl_user;
+ writel(ce_ctrl_val, ce_ctrl_reg);
+ ce_ctrl_val &= (~CTRL_STOP_ACTIVE);
+ writel(ce_ctrl_val, ce_ctrl_reg);
+
+ /* Send command */
+ aspeed_spi_write_to_ahb(flash->ahb_base, &op->cmd.opcode, 1);
+
+ /* Send address */
+ for (i = op->addr.nbytes; i > 0; i--) {
+ addr[op->addr.nbytes - i] =
+ ((u32)op->addr.val >> ((i - 1) * 8)) & 0xff;
+ }
+
+ /* Change io_mode */
+ ce_ctrl_val &= ~priv->info->io_mode_mask;
+ ce_ctrl_val |= aspeed_spi_get_io_mode(op->addr.buswidth);
+ writel(ce_ctrl_val, ce_ctrl_reg);
+ aspeed_spi_write_to_ahb(flash->ahb_base, addr, op->addr.nbytes);
+
+ /* Send dummy cycles */
+ aspeed_spi_write_to_ahb(flash->ahb_base, dummy_data, op->dummy.nbytes);
+
+ /* Change io_mode */
+ ce_ctrl_val &= ~priv->info->io_mode_mask;
+ ce_ctrl_val |= aspeed_spi_get_io_mode(op->data.buswidth);
+ writel(ce_ctrl_val, ce_ctrl_reg);
+
+ /* Send data */
+ if (op->data.dir == SPI_MEM_DATA_OUT) {
+ aspeed_spi_write_to_ahb(flash->ahb_base, op->data.buf.out,
+ op->data.nbytes);
+ } else {
+ aspeed_spi_read_from_ahb(flash->ahb_base, op->data.buf.in,
+ op->data.nbytes);
+ }
+
+ ce_ctrl_val |= CTRL_STOP_ACTIVE;
+ writel(ce_ctrl_val, ce_ctrl_reg);
+
+ /* Restore controller setting. */
+ writel(flash->ce_ctrl_read, ce_ctrl_reg);
+
+ return 0;
+}
+
+static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
+{
+ int ret = 0;
+ struct udevice *dev = desc->slave->dev;
+ struct udevice *bus = dev->parent;
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+ const struct aspeed_spi_info *info = priv->info;
+ struct spi_mem_op op_tmpl = desc->info.op_tmpl;
+ u32 i;
+ u32 cs = slave_plat->cs;
+ u32 cmd_io_conf;
+ u32 ce_ctrl_reg;
+
+ if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) {
+ /*
+ * dirmap_write is not supported currently due to a HW
+ * limitation for command write mode: The written data
+ * length should be multiple of 4-byte.
+ */
+ return -EOPNOTSUPP;
+ }
+
+ ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs];
+ if (info == &ast2400_spi_info)
+ ce_ctrl_reg = (u32)&priv->regs->ctrl;
+
+ if (desc->info.length > 0x1000000)
+ priv->info->set_4byte(bus, cs);
+
+ /* AST2400 SPI1 doesn't have decoded address segment register. */
+ if (info != &ast2400_spi_info) {
+ priv->flashes[cs].ahb_decoded_sz = desc->info.length;
+
+ for (i = 0; i < priv->num_cs; i++) {
+ dev_dbg(dev, "cs: %d, sz: 0x%x\n", i,
+ priv->flashes[cs].ahb_decoded_sz);
+ }
+
+ ret = aspeed_spi_decoded_range_config(bus);
+ if (ret)
+ return ret;
+ }
+
+ cmd_io_conf = aspeed_spi_get_io_mode(op_tmpl.data.buswidth) |
+ op_tmpl.cmd.opcode << 16 |
+ ((op_tmpl.dummy.nbytes) & 0x3) << 6 |
+ ((op_tmpl.dummy.nbytes) & 0x4) << 14 |
+ CTRL_IO_MODE_CMD_READ;
+
+ priv->flashes[cs].ce_ctrl_read &= priv->info->clk_ctrl_mask;
+ priv->flashes[cs].ce_ctrl_read |= cmd_io_conf;
+
+ writel(priv->flashes[cs].ce_ctrl_read, ce_ctrl_reg);
+
+ dev_dbg(dev, "read bus width: %d ce_ctrl_val: 0x%08x\n",
+ op_tmpl.data.buswidth, priv->flashes[cs].ce_ctrl_read);
+
+ return ret;
+}
+
+static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct udevice *dev = desc->slave->dev;
+ struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+ u32 cs = slave_plat->cs;
+ int ret;
+
+ dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%x\n",
+ desc->info.op_tmpl.cmd.opcode, offs, len);
+
+ if (priv->flashes[cs].ahb_decoded_sz < offs + len ||
+ (offs % 4) != 0) {
+ ret = aspeed_spi_exec_op_user_mode(desc->slave,
+ &desc->info.op_tmpl);
+ if (ret != 0)
+ return 0;
+ } else {
+ memcpy_fromio(buf, priv->flashes[cs].ahb_base + offs, len);
+ }
+
+ return len;
+}
+
+static struct aspeed_spi_flash *aspeed_spi_get_flash(struct udevice *dev)
+{
+ struct udevice *bus = dev->parent;
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 cs = slave_plat->cs;
+
+ if (cs >= plat->max_cs) {
+ dev_err(dev, "invalid CS %u\n", cs);
+ return NULL;
+ }
+
+ return &priv->flashes[cs];
+}
+
+static void aspeed_spi_decoded_base_calculate(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 cs;
+
+ if (priv->fixed_decoded_range)
+ return;
+
+ priv->flashes[0].ahb_base = plat->ahb_base;
+
+ for (cs = 1; cs < plat->max_cs; cs++) {
+ priv->flashes[cs].ahb_base =
+ priv->flashes[cs - 1].ahb_base +
+ priv->flashes[cs - 1].ahb_decoded_sz;
+ }
+}
+
+static void aspeed_spi_decoded_range_set(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 decoded_reg_val;
+ u32 start_addr, end_addr;
+ u32 cs;
+
+ for (cs = 0; cs < plat->max_cs; cs++) {
+ start_addr = (u32)priv->flashes[cs].ahb_base;
+ end_addr = (u32)priv->flashes[cs].ahb_base +
+ priv->flashes[cs].ahb_decoded_sz;
+
+ decoded_reg_val = priv->info->segment_reg(start_addr, end_addr);
+
+ writel(decoded_reg_val, &priv->regs->segment_addr[cs]);
+
+ dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n",
+ cs, decoded_reg_val, start_addr, end_addr);
+ }
+}
+
+static int aspeed_spi_decoded_range_config(struct udevice *bus)
+{
+ int ret = 0;
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+
+ if (priv->info->adjust_decoded_sz &&
+ !priv->fixed_decoded_range) {
+ ret = priv->info->adjust_decoded_sz(bus);
+ if (ret != 0)
+ return ret;
+ }
+
+ aspeed_spi_decoded_base_calculate(bus);
+ aspeed_spi_decoded_range_set(bus);
+
+ return ret;
+}
+
+static int aspeed_spi_decoded_ranges_sanity(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 cs;
+ u32 total_sz = 0;
+
+ /* Check overall size. */
+ for (cs = 0; cs < plat->max_cs; cs++)
+ total_sz += priv->flashes[cs].ahb_decoded_sz;
+
+ if (total_sz > plat->ahb_sz) {
+ dev_err(bus, "invalid total size 0x%08x\n", total_sz);
+ return -EINVAL;
+ }
+
+ /* Check each decoded range size for AST2500. */
+ if (priv->info == &ast2500_fmc_info ||
+ priv->info == &ast2500_spi_info) {
+ for (cs = 0; cs < plat->max_cs; cs++) {
+ if (priv->flashes[cs].ahb_decoded_sz <
+ priv->info->min_decoded_sz) {
+ dev_err(bus, "insufficient decoded range.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ /*
+ * Check overlay. Here, we assume the deccded ranges and
+ * address base are monotonic increasing with CE#.
+ */
+ for (cs = plat->max_cs - 1; cs > 0; cs--) {
+ if ((u32)priv->flashes[cs].ahb_base != 0 &&
+ (u32)priv->flashes[cs].ahb_base <
+ (u32)priv->flashes[cs - 1].ahb_base +
+ priv->flashes[cs - 1].ahb_decoded_sz) {
+ dev_err(bus, "decoded range overlay 0x%08x 0x%08x\n",
+ (u32)priv->flashes[cs].ahb_base,
+ (u32)priv->flashes[cs - 1].ahb_base);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int aspeed_spi_read_fixed_decoded_ranges(struct udevice *bus)
+{
+ int ret = 0;
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ const char *range_prop = "decoded-ranges";
+ struct aspeed_spi_decoded_range ranges[ASPEED_SPI_MAX_CS];
+ const struct property *prop;
+ u32 prop_sz;
+ u32 count;
+ u32 i;
+
+ priv->fixed_decoded_range = false;
+
+ prop = dev_read_prop(bus, range_prop, &prop_sz);
+ if (!prop)
+ return 0;
+
+ count = prop_sz / sizeof(struct aspeed_spi_decoded_range);
+ if (count > plat->max_cs || count < priv->num_cs) {
+ dev_err(bus, "invalid '%s' property %d %d\n",
+ range_prop, count, priv->num_cs);
+ return -EINVAL;
+ }
+
+ ret = dev_read_u32_array(bus, range_prop, (u32 *)ranges, count * 3);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < count; i++) {
+ priv->flashes[ranges[i].cs].ahb_base =
+ (void __iomem *)ranges[i].ahb_base;
+ priv->flashes[ranges[i].cs].ahb_decoded_sz =
+ ranges[i].sz;
+ }
+
+ for (i = 0; i < plat->max_cs; i++) {
+ dev_dbg(bus, "ahb_base: 0x%p, size: 0x%08x\n",
+ priv->flashes[i].ahb_base,
+ priv->flashes[i].ahb_decoded_sz);
+ }
+
+ ret = aspeed_spi_decoded_ranges_sanity(bus);
+ if (ret != 0)
+ return ret;
+
+ priv->fixed_decoded_range = true;
+
+ return 0;
+}
+
+/*
+ * Initialize SPI controller for each chip select.
+ * Here, only the minimum decode range is configured
+ * in order to get device (SPI NOR flash) information
+ * at the early stage.
+ */
+static int aspeed_spi_ctrl_init(struct udevice *bus)
+{
+ int ret;
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ u32 cs;
+ u32 reg_val;
+ u32 decoded_sz;
+
+ /* Enable write capability for all CS. */
+ reg_val = readl(&priv->regs->conf);
+ if (priv->info == &ast2400_spi_info) {
+ writel(reg_val | BIT(0), &priv->regs->conf);
+ } else {
+ writel(reg_val | (GENMASK(plat->max_cs - 1, 0) << 16),
+ &priv->regs->conf);
+ }
+
+ memset(priv->flashes, 0x0,
+ sizeof(struct aspeed_spi_flash) * ASPEED_SPI_MAX_CS);
+
+ /* Initial user mode. */
+ for (cs = 0; cs < priv->num_cs; cs++) {
+ priv->flashes[cs].ce_ctrl_user &= priv->info->clk_ctrl_mask;
+ priv->flashes[cs].ce_ctrl_user |=
+ (CTRL_STOP_ACTIVE | CTRL_IO_MODE_USER);
+ }
+
+ /*
+ * SPI1 on AST2400 only supports CS0.
+ * It is unnecessary to configure segment address register.
+ */
+ if (priv->info == &ast2400_spi_info) {
+ priv->flashes[cs].ahb_base = plat->ahb_base;
+ priv->flashes[cs].ahb_decoded_sz = 0x10000000;
+ return 0;
+ }
+
+
+ ret = aspeed_spi_read_fixed_decoded_ranges(bus);
+ if (ret != 0)
+ return ret;
+
+ if (!priv->fixed_decoded_range) {
+ /* Assign basic AHB decoded size for each CS. */
+ for (cs = 0; cs < plat->max_cs; cs++) {
+ reg_val = readl(&priv->regs->segment_addr[cs]);
+ decoded_sz = priv->info->segment_end(bus, reg_val) -
+ priv->info->segment_start(bus, reg_val);
+
+ if (decoded_sz < priv->info->min_decoded_sz)
+ decoded_sz = priv->info->min_decoded_sz;
+
+ priv->flashes[cs].ahb_decoded_sz = decoded_sz;
+ }
+ }
+
+ ret = aspeed_spi_decoded_range_config(bus);
+
+ return ret;
+}
+
+static const struct aspeed_spi_info ast2400_fmc_info = {
+ .io_mode_mask = 0x70000000,
+ .max_bus_width = 2,
+ .min_decoded_sz = 0x800000,
+ .clk_ctrl_mask = 0x00002f00,
+ .set_4byte = ast2400_fmc_chip_set_4byte,
+ .segment_start = ast2400_spi_segment_start,
+ .segment_end = ast2400_spi_segment_end,
+ .segment_reg = ast2400_spi_segment_reg,
+ .get_clk_setting = ast2400_get_clk_setting,
+};
+
+static const struct aspeed_spi_info ast2400_spi_info = {
+ .io_mode_mask = 0x70000000,
+ .max_bus_width = 2,
+ .min_decoded_sz = 0x800000,
+ .clk_ctrl_mask = 0x00000f00,
+ .set_4byte = ast2400_spi_chip_set_4byte,
+ .segment_start = ast2400_spi_segment_start,
+ .segment_end = ast2400_spi_segment_end,
+ .segment_reg = ast2400_spi_segment_reg,
+ .get_clk_setting = ast2400_get_clk_setting,
+};
+
+static const struct aspeed_spi_info ast2500_fmc_info = {
+ .io_mode_mask = 0x70000000,
+ .max_bus_width = 2,
+ .min_decoded_sz = 0x800000,
+ .clk_ctrl_mask = 0x00002f00,
+ .set_4byte = ast2500_spi_chip_set_4byte,
+ .segment_start = ast2500_spi_segment_start,
+ .segment_end = ast2500_spi_segment_end,
+ .segment_reg = ast2500_spi_segment_reg,
+ .adjust_decoded_sz = ast2500_adjust_decoded_size,
+ .get_clk_setting = ast2500_get_clk_setting,
+};
+
+/*
+ * There are some different between FMC and SPI controllers.
+ * For example, DMA operation, but this isn't implemented currently.
+ */
+static const struct aspeed_spi_info ast2500_spi_info = {
+ .io_mode_mask = 0x70000000,
+ .max_bus_width = 2,
+ .min_decoded_sz = 0x800000,
+ .clk_ctrl_mask = 0x00002f00,
+ .set_4byte = ast2500_spi_chip_set_4byte,
+ .segment_start = ast2500_spi_segment_start,
+ .segment_end = ast2500_spi_segment_end,
+ .segment_reg = ast2500_spi_segment_reg,
+ .adjust_decoded_sz = ast2500_adjust_decoded_size,
+ .get_clk_setting = ast2500_get_clk_setting,
+};
+
+static const struct aspeed_spi_info ast2600_fmc_info = {
+ .io_mode_mask = 0xf0000000,
+ .max_bus_width = 4,
+ .min_decoded_sz = 0x200000,
+ .clk_ctrl_mask = 0x0f000f00,
+ .set_4byte = ast2600_spi_chip_set_4byte,
+ .segment_start = ast2600_spi_segment_start,
+ .segment_end = ast2600_spi_segment_end,
+ .segment_reg = ast2600_spi_segment_reg,
+ .adjust_decoded_sz = ast2600_adjust_decoded_size,
+ .get_clk_setting = ast2600_get_clk_setting,
+};
+
+static const struct aspeed_spi_info ast2600_spi_info = {
+ .io_mode_mask = 0xf0000000,
+ .max_bus_width = 4,
+ .min_decoded_sz = 0x200000,
+ .clk_ctrl_mask = 0x0f000f00,
+ .set_4byte = ast2600_spi_chip_set_4byte,
+ .segment_start = ast2600_spi_segment_start,
+ .segment_end = ast2600_spi_segment_end,
+ .segment_reg = ast2600_spi_segment_reg,
+ .adjust_decoded_sz = ast2600_adjust_decoded_size,
+ .get_clk_setting = ast2600_get_clk_setting,
+};
+
+static int aspeed_spi_claim_bus(struct udevice *dev)
+{
+ struct udevice *bus = dev->parent;
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+ struct aspeed_spi_priv *priv = dev_get_priv(dev->parent);
+ struct aspeed_spi_flash *flash = &priv->flashes[slave_plat->cs];
+ u32 clk_setting;
+
+ dev_dbg(bus, "%s: claim bus CS%u\n", bus->name, slave_plat->cs);
+
+ if (flash->max_freq == 0) {
+ clk_setting = priv->info->get_clk_setting(dev, slave_plat->max_hz);
+ flash->ce_ctrl_user &= ~(priv->info->clk_ctrl_mask);
+ flash->ce_ctrl_user |= clk_setting;
+ flash->ce_ctrl_read &= ~(priv->info->clk_ctrl_mask);
+ flash->ce_ctrl_read |= clk_setting;
+ }
+
+ return 0;
+}
+
+static int aspeed_spi_release_bus(struct udevice *dev)
+{
+ struct udevice *bus = dev->parent;
+ struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+
+ dev_dbg(bus, "%s: release bus CS%u\n", bus->name, slave_plat->cs);
+
+ if (!aspeed_spi_get_flash(dev))
+ return -ENODEV;
+
+ return 0;
+}
+
+static int aspeed_spi_set_mode(struct udevice *bus, uint mode)
+{
+ dev_dbg(bus, "%s: setting mode to %x\n", bus->name, mode);
+
+ return 0;
+}
+
+static int aspeed_spi_set_speed(struct udevice *bus, uint hz)
+{
+ dev_dbg(bus, "%s: setting speed to %u\n", bus->name, hz);
+ /*
+ * ASPEED SPI controller supports multiple CS with different
+ * clock frequency. We cannot distinguish which CS here.
+ * Thus, the related implementation is postponed to claim_bus.
+ */
+
+ return 0;
+}
+
+static int apseed_spi_of_to_plat(struct udevice *bus)
+{
+ struct aspeed_spi_plat *plat = dev_get_plat(bus);
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ int ret;
+ struct clk hclk;
+
+ priv->regs = (void __iomem *)devfdt_get_addr_index(bus, 0);
+ if ((u32)priv->regs == FDT_ADDR_T_NONE) {
+ dev_err(bus, "wrong ctrl base\n");
+ return -ENODEV;
+ }
+
+ plat->ahb_base =
+ (void __iomem *)devfdt_get_addr_size_index(bus, 1, &plat->ahb_sz);
+ if ((u32)plat->ahb_base == FDT_ADDR_T_NONE) {
+ dev_err(bus, "wrong AHB base\n");
+ return -ENODEV;
+ }
+
+ plat->max_cs = dev_read_u32_default(bus, "num-cs", ASPEED_SPI_MAX_CS);
+ if (plat->max_cs > ASPEED_SPI_MAX_CS)
+ return -EINVAL;
+
+ ret = clk_get_by_index(bus, 0, &hclk);
+ if (ret < 0) {
+ dev_err(bus, "%s could not get clock: %d\n", bus->name, ret);
+ return ret;
+ }
+
+ plat->hclk_rate = clk_get_rate(&hclk);
+ clk_free(&hclk);
+
+ dev_dbg(bus, "ctrl_base = 0x%x, ahb_base = 0x%p, size = 0x%lx\n",
+ (u32)priv->regs, plat->ahb_base, plat->ahb_sz);
+ dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n",
+ plat->hclk_rate / 1000000, plat->max_cs);
+
+ return 0;
+}
+
+static int aspeed_spi_probe(struct udevice *bus)
+{
+ int ret;
+ struct aspeed_spi_priv *priv = dev_get_priv(bus);
+ struct udevice *dev;
+
+ priv->info = (struct aspeed_spi_info *)dev_get_driver_data(bus);
+
+ priv->num_cs = 0;
+ for (device_find_first_child(bus, &dev); dev;
+ device_find_next_child(&dev)) {
+ priv->num_cs++;
+ }
+
+ if (priv->num_cs > ASPEED_SPI_MAX_CS)
+ return -EINVAL;
+
+ ret = aspeed_spi_ctrl_init(bus);
+
+ return ret;
+}
+
+static const struct spi_controller_mem_ops aspeed_spi_mem_ops = {
+ .supports_op = aspeed_spi_supports_op,
+ .exec_op = aspeed_spi_exec_op_user_mode,
+ .dirmap_create = aspeed_spi_dirmap_create,
+ .dirmap_read = aspeed_spi_dirmap_read,
+};
+
+static const struct dm_spi_ops aspeed_spi_ops = {
+ .claim_bus = aspeed_spi_claim_bus,
+ .release_bus = aspeed_spi_release_bus,
+ .set_speed = aspeed_spi_set_speed,
+ .set_mode = aspeed_spi_set_mode,
+ .mem_ops = &aspeed_spi_mem_ops,
+};
+
+static const struct udevice_id aspeed_spi_ids[] = {
+ { .compatible = "aspeed,ast2400-fmc", .data = (ulong)&ast2400_fmc_info, },
+ { .compatible = "aspeed,ast2400-spi", .data = (ulong)&ast2400_spi_info, },
+ { .compatible = "aspeed,ast2500-fmc", .data = (ulong)&ast2500_fmc_info, },
+ { .compatible = "aspeed,ast2500-spi", .data = (ulong)&ast2500_spi_info, },
+ { .compatible = "aspeed,ast2600-fmc", .data = (ulong)&ast2600_fmc_info, },
+ { .compatible = "aspeed,ast2600-spi", .data = (ulong)&ast2600_spi_info, },
+ { }
+};
+
+U_BOOT_DRIVER(aspeed_spi) = {
+ .name = "aspeed_spi_smc",
+ .id = UCLASS_SPI,
+ .of_match = aspeed_spi_ids,
+ .ops = &aspeed_spi_ops,
+ .of_to_plat = apseed_spi_of_to_plat,
+ .plat_auto = sizeof(struct aspeed_spi_plat),
+ .priv_auto = sizeof(struct aspeed_spi_priv),
+ .probe = aspeed_spi_probe,
+};
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 9c1ede1b61..8e8995fc53 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -21,6 +21,8 @@
#include <spi.h>
#include <spi-mem.h>
#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/bug.h>
#endif
#ifndef __UBOOT__
@@ -491,6 +493,272 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
}
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
+static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct spi_mem_op op = desc->info.op_tmpl;
+ int ret;
+
+ op.addr.val = desc->info.offset + offs;
+ op.data.buf.in = buf;
+ op.data.nbytes = len;
+ ret = spi_mem_adjust_op_size(desc->slave, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(desc->slave, &op);
+ if (ret)
+ return ret;
+
+ return op.data.nbytes;
+}
+
+static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf)
+{
+ struct spi_mem_op op = desc->info.op_tmpl;
+ int ret;
+
+ op.addr.val = desc->info.offset + offs;
+ op.data.buf.out = buf;
+ op.data.nbytes = len;
+ ret = spi_mem_adjust_op_size(desc->slave, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(desc->slave, &op);
+ if (ret)
+ return ret;
+
+ return op.data.nbytes;
+}
+
+/**
+ * spi_mem_dirmap_create() - Create a direct mapping descriptor
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * This function is creating a direct mapping descriptor which can then be used
+ * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
+ * If the SPI controller driver does not support direct mapping, this function
+ * falls back to an implementation using spi_mem_exec_op(), so that the caller
+ * doesn't have to bother implementing a fallback on his own.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_slave *slave,
+ const struct spi_mem_dirmap_info *info)
+{
+ struct udevice *bus = slave->dev->parent;
+ struct dm_spi_ops *ops = spi_get_ops(bus);
+ struct spi_mem_dirmap_desc *desc;
+ int ret = -EOPNOTSUPP;
+
+ /* Make sure the number of address cycles is between 1 and 8 bytes. */
+ if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
+ return ERR_PTR(-EINVAL);
+
+ /* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
+ if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
+ return ERR_PTR(-EINVAL);
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return ERR_PTR(-ENOMEM);
+
+ desc->slave = slave;
+ desc->info = *info;
+ if (ops->mem_ops && ops->mem_ops->dirmap_create)
+ ret = ops->mem_ops->dirmap_create(desc);
+
+ if (ret) {
+ desc->nodirmap = true;
+ if (!spi_mem_supports_op(desc->slave, &desc->info.op_tmpl))
+ ret = -EOPNOTSUPP;
+ else
+ ret = 0;
+ }
+
+ if (ret) {
+ kfree(desc);
+ return ERR_PTR(ret);
+ }
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
+
+/**
+ * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
+ * @desc: the direct mapping descriptor to destroy
+ *
+ * This function destroys a direct mapping descriptor previously created by
+ * spi_mem_dirmap_create().
+ */
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
+{
+ struct udevice *bus = desc->slave->dev->parent;
+ struct dm_spi_ops *ops = spi_get_ops(bus);
+
+ if (!desc->nodirmap && ops->mem_ops && ops->mem_ops->dirmap_destroy)
+ ops->mem_ops->dirmap_destroy(desc);
+
+ kfree(desc);
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
+
+#ifndef __UBOOT__
+static void devm_spi_mem_dirmap_release(struct udevice *dev, void *res)
+{
+ struct spi_mem_dirmap_desc *desc = *(struct spi_mem_dirmap_desc **)res;
+
+ spi_mem_dirmap_destroy(desc);
+}
+
+/**
+ * devm_spi_mem_dirmap_create() - Create a direct mapping descriptor and attach
+ * it to a device
+ * @dev: device the dirmap desc will be attached to
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * devm_ variant of the spi_mem_dirmap_create() function. See
+ * spi_mem_dirmap_create() for more details.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+devm_spi_mem_dirmap_create(struct udevice *dev, struct spi_slave *slave,
+ const struct spi_mem_dirmap_info *info)
+{
+ struct spi_mem_dirmap_desc **ptr, *desc;
+
+ ptr = devres_alloc(devm_spi_mem_dirmap_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ desc = spi_mem_dirmap_create(slave, info);
+ if (IS_ERR(desc)) {
+ devres_free(ptr);
+ } else {
+ *ptr = desc;
+ devres_add(dev, ptr);
+ }
+
+ return desc;
+}
+EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_create);
+
+static int devm_spi_mem_dirmap_match(struct udevice *dev, void *res, void *data)
+{
+ struct spi_mem_dirmap_desc **ptr = res;
+
+ if (WARN_ON(!ptr || !*ptr))
+ return 0;
+
+ return *ptr == data;
+}
+
+/**
+ * devm_spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor attached
+ * to a device
+ * @dev: device the dirmap desc is attached to
+ * @desc: the direct mapping descriptor to destroy
+ *
+ * devm_ variant of the spi_mem_dirmap_destroy() function. See
+ * spi_mem_dirmap_destroy() for more details.
+ */
+void devm_spi_mem_dirmap_destroy(struct udevice *dev,
+ struct spi_mem_dirmap_desc *desc)
+{
+ devres_release(dev, devm_spi_mem_dirmap_release,
+ devm_spi_mem_dirmap_match, desc);
+}
+EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_destroy);
+#endif /* __UBOOT__ */
+
+/**
+ * spi_mem_dirmap_read() - Read data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start reading from. Note that this is not an absolute
+ * offset, but the offset within the direct mapping which already has
+ * its own offset
+ * @len: length in bytes
+ * @buf: destination buffer. This buffer must be DMA-able
+ *
+ * This function reads data from a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data read from the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_read() again when that happens.
+ */
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, void *buf)
+{
+ struct udevice *bus = desc->slave->dev->parent;
+ struct dm_spi_ops *ops = spi_get_ops(bus);
+ ssize_t ret;
+
+ if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ if (desc->nodirmap)
+ ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
+ else if (ops->mem_ops && ops->mem_ops->dirmap_read)
+ ret = ops->mem_ops->dirmap_read(desc, offs, len, buf);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
+
+/**
+ * spi_mem_dirmap_write() - Write data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start writing from. Note that this is not an absolute
+ * offset, but the offset within the direct mapping which already has
+ * its own offset
+ * @len: length in bytes
+ * @buf: source buffer. This buffer must be DMA-able
+ *
+ * This function writes data to a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data written to the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_write() again when that happens.
+ */
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+ u64 offs, size_t len, const void *buf)
+{
+ struct udevice *bus = desc->slave->dev->parent;
+ struct dm_spi_ops *ops = spi_get_ops(bus);
+ ssize_t ret;
+
+ if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
+ return -EINVAL;
+
+ if (!len)
+ return 0;
+
+ if (desc->nodirmap)
+ ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
+ else if (ops->mem_ops && ops->mem_ops->dirmap_write)
+ ret = ops->mem_ops->dirmap_write(desc, offs, len, buf);
+ else
+ ret = -EOPNOTSUPP;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
+
#ifndef __UBOOT__
static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
{
diff --git a/drivers/spi/stm32_qspi.c b/drivers/spi/stm32_qspi.c
index ceba413727..90c207d518 100644
--- a/drivers/spi/stm32_qspi.c
+++ b/drivers/spi/stm32_qspi.c
@@ -172,7 +172,7 @@ static int _stm32_qspi_wait_cmd(struct stm32_qspi_priv *priv,
static void _stm32_qspi_read_fifo(u8 *val, void __iomem *addr)
{
*val = readb(addr);
- WATCHDOG_RESET();
+ schedule();
}
static void _stm32_qspi_write_fifo(u8 *val, void __iomem *addr)
diff --git a/drivers/spi/zynqmp_gqspi.c b/drivers/spi/zynqmp_gqspi.c
index c772bae3cc..d3cc8554b8 100644
--- a/drivers/spi/zynqmp_gqspi.c
+++ b/drivers/spi/zynqmp_gqspi.c
@@ -22,6 +22,8 @@
#include <dm/device_compat.h>
#include <linux/bitops.h>
#include <linux/err.h>
+#include <linux/sizes.h>
+#include <zynqmp_firmware.h>
#define GQSPI_GFIFO_STRT_MODE_MASK BIT(29)
#define GQSPI_CONFIG_MODE_EN_MASK (3 << 30)
@@ -102,8 +104,10 @@
#define TAP_DLY_BYPASS_LQSPI_RX_VALUE 0x1
#define TAP_DLY_BYPASS_LQSPI_RX_SHIFT 2
#define GQSPI_DATA_DLY_ADJ_OFST 0x000001F8
-#define IOU_TAPDLY_BYPASS_OFST 0xFF180390
+#define IOU_TAPDLY_BYPASS_OFST !IS_ENABLED(CONFIG_ARCH_VERSAL) ? \
+ 0xFF180390 : 0xF103003C
#define GQSPI_LPBK_DLY_ADJ_LPBK_MASK 0x00000020
+#define GQSPI_FREQ_37_5MHZ 37500000
#define GQSPI_FREQ_40MHZ 40000000
#define GQSPI_FREQ_100MHZ 100000000
#define GQSPI_FREQ_150MHZ 150000000
@@ -163,6 +167,7 @@ struct zynqmp_qspi_plat {
struct zynqmp_qspi_dma_regs *dma_regs;
u32 frequency;
u32 speed_hz;
+ unsigned int io_mode;
};
struct zynqmp_qspi_priv {
@@ -171,6 +176,7 @@ struct zynqmp_qspi_priv {
const void *tx_buf;
void *rx_buf;
unsigned int len;
+ unsigned int io_mode;
int bytes_to_transfer;
int bytes_to_receive;
const struct spi_mem_op *op;
@@ -187,6 +193,8 @@ static int zynqmp_qspi_of_to_plat(struct udevice *bus)
plat->dma_regs = (struct zynqmp_qspi_dma_regs *)
(dev_read_addr(bus) + GQSPI_DMA_REG_OFFSET);
+ plat->io_mode = dev_read_bool(bus, "has-io-mode");
+
return 0;
}
@@ -206,8 +214,11 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi_priv *priv)
config_reg = readl(&regs->confr);
config_reg &= ~(GQSPI_GFIFO_STRT_MODE_MASK |
GQSPI_CONFIG_MODE_EN_MASK);
- config_reg |= GQSPI_CONFIG_DMA_MODE | GQSPI_GFIFO_WP_HOLD |
- GQSPI_DFLT_BAUD_RATE_DIV | GQSPI_GFIFO_STRT_MODE_MASK;
+ config_reg |= GQSPI_GFIFO_WP_HOLD | GQSPI_DFLT_BAUD_RATE_DIV;
+ config_reg |= GQSPI_GFIFO_STRT_MODE_MASK;
+ if (!priv->io_mode)
+ config_reg |= GQSPI_CONFIG_DMA_MODE;
+
writel(config_reg, &regs->confr);
writel(GQSPI_ENABLE_ENABLE_MASK, &regs->enbr);
@@ -297,28 +308,42 @@ void zynqmp_qspi_set_tapdelay(struct udevice *bus, u32 baudrateval)
debug("%s, req_hz:%d, clk_rate:%d, baudrateval:%d\n",
__func__, reqhz, clk_rate, baudrateval);
- if (reqhz < GQSPI_FREQ_40MHZ) {
- zynqmp_mmio_read(IOU_TAPDLY_BYPASS_OFST, &tapdlybypass);
- tapdlybypass |= (TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
- TAP_DLY_BYPASS_LQSPI_RX_SHIFT);
- } else if (reqhz <= GQSPI_FREQ_100MHZ) {
- zynqmp_mmio_read(IOU_TAPDLY_BYPASS_OFST, &tapdlybypass);
- tapdlybypass |= (TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
- TAP_DLY_BYPASS_LQSPI_RX_SHIFT);
- lpbkdlyadj = readl(&regs->lpbkdly);
- lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_LPBK_MASK);
- datadlyadj = readl(&regs->gqspidlyadj);
- datadlyadj |= ((GQSPI_USE_DATA_DLY << GQSPI_USE_DATA_DLY_SHIFT)
- | (GQSPI_DATA_DLY_ADJ_VALUE <<
- GQSPI_DATA_DLY_ADJ_SHIFT));
- } else if (reqhz <= GQSPI_FREQ_150MHZ) {
- lpbkdlyadj = readl(&regs->lpbkdly);
- lpbkdlyadj |= ((GQSPI_LPBK_DLY_ADJ_LPBK_MASK) |
- GQSPI_LPBK_DLY_ADJ_DLY_0);
+ if (!(IS_ENABLED(CONFIG_ARCH_VERSAL) ||
+ IS_ENABLED(CONFIG_ARCH_VERSAL_NET))) {
+ if (reqhz <= GQSPI_FREQ_40MHZ) {
+ tapdlybypass = TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
+ TAP_DLY_BYPASS_LQSPI_RX_SHIFT;
+ } else if (reqhz <= GQSPI_FREQ_100MHZ) {
+ tapdlybypass = TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
+ TAP_DLY_BYPASS_LQSPI_RX_SHIFT;
+ lpbkdlyadj = GQSPI_LPBK_DLY_ADJ_LPBK_MASK;
+ datadlyadj = (GQSPI_USE_DATA_DLY <<
+ GQSPI_USE_DATA_DLY_SHIFT) |
+ (GQSPI_DATA_DLY_ADJ_VALUE <<
+ GQSPI_DATA_DLY_ADJ_SHIFT);
+ } else if (reqhz <= GQSPI_FREQ_150MHZ) {
+ lpbkdlyadj = GQSPI_LPBK_DLY_ADJ_LPBK_MASK |
+ GQSPI_LPBK_DLY_ADJ_DLY_0;
+ }
+ zynqmp_mmio_write(IOU_TAPDLY_BYPASS_OFST,
+ IOU_TAPDLY_BYPASS_MASK, tapdlybypass);
+ } else {
+ if (reqhz <= GQSPI_FREQ_37_5MHZ) {
+ tapdlybypass = TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
+ TAP_DLY_BYPASS_LQSPI_RX_SHIFT;
+ } else if (reqhz <= GQSPI_FREQ_100MHZ) {
+ tapdlybypass = TAP_DLY_BYPASS_LQSPI_RX_VALUE <<
+ TAP_DLY_BYPASS_LQSPI_RX_SHIFT;
+ lpbkdlyadj = GQSPI_LPBK_DLY_ADJ_LPBK_MASK;
+ datadlyadj = GQSPI_USE_DATA_DLY <<
+ GQSPI_USE_DATA_DLY_SHIFT;
+ } else if (reqhz <= GQSPI_FREQ_150MHZ) {
+ lpbkdlyadj = GQSPI_LPBK_DLY_ADJ_LPBK_MASK |
+ (GQSPI_LPBK_DLY_ADJ_DLY_1 <<
+ GQSPI_LPBK_DLY_ADJ_DLY_1_SHIFT);
+ }
+ writel(tapdlybypass, IOU_TAPDLY_BYPASS_OFST);
}
-
- zynqmp_mmio_write(IOU_TAPDLY_BYPASS_OFST, IOU_TAPDLY_BYPASS_MASK,
- tapdlybypass);
writel(lpbkdlyadj, &regs->lpbkdly);
writel(datadlyadj, &regs->gqspidlyadj);
}
@@ -372,6 +397,7 @@ static int zynqmp_qspi_probe(struct udevice *bus)
priv->regs = plat->regs;
priv->dma_regs = plat->dma_regs;
+ priv->io_mode = plat->io_mode;
ret = clk_get_by_index(bus, 0, &clk);
if (ret < 0) {
@@ -409,8 +435,7 @@ static int zynqmp_qspi_set_mode(struct udevice *bus, uint mode)
debug("%s\n", __func__);
/* Set the SPI Clock phase and polarities */
confr = readl(&regs->confr);
- confr &= ~(GQSPI_CONFIG_CPHA_MASK |
- GQSPI_CONFIG_CPOL_MASK);
+ confr &= ~(GQSPI_CONFIG_CPHA_MASK | GQSPI_CONFIG_CPOL_MASK);
if (mode & SPI_CPHA)
confr |= GQSPI_CONFIG_CPHA_MASK;
@@ -554,8 +579,7 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv)
gen_fifo_cmd = zynqmp_qspi_bus_select(priv);
gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth);
- gen_fifo_cmd |= GQSPI_GFIFO_TX |
- GQSPI_GFIFO_DATA_XFR_MASK;
+ gen_fifo_cmd |= GQSPI_GFIFO_TX | GQSPI_GFIFO_DATA_XFR_MASK;
while (priv->len) {
len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
@@ -564,11 +588,9 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv)
debug("GFIFO_CMD_TX:0x%x\n", gen_fifo_cmd);
if (gen_fifo_cmd & GQSPI_GFIFO_EXP_MASK)
- ret = zynqmp_qspi_fill_tx_fifo(priv,
- 1 << len);
+ ret = zynqmp_qspi_fill_tx_fifo(priv, 1 << len);
else
- ret = zynqmp_qspi_fill_tx_fifo(priv,
- len);
+ ret = zynqmp_qspi_fill_tx_fifo(priv, len);
if (ret)
return ret;
@@ -576,44 +598,119 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv)
return ret;
}
+static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv,
+ u32 gen_fifo_cmd, u32 *buf)
+{
+ u32 len;
+ u32 actuallen = priv->len;
+ u32 config_reg, ier, isr;
+ u32 timeout = GQSPI_TIMEOUT;
+ struct zynqmp_qspi_regs *regs = priv->regs;
+ u32 last_bits;
+ u32 *traverse = buf;
+
+ while (priv->len) {
+ len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
+ /* If exponent bit is set, reset immediate to be 2^len */
+ if (gen_fifo_cmd & GQSPI_GFIFO_EXP_MASK)
+ priv->bytes_to_receive = (1 << len);
+ else
+ priv->bytes_to_receive = len;
+ zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
+ debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd);
+ /* Manual start */
+ config_reg = readl(&regs->confr);
+ config_reg |= GQSPI_STRT_GEN_FIFO;
+ writel(config_reg, &regs->confr);
+ /* Enable RX interrupts for IO mode */
+ ier = readl(&regs->ier);
+ ier |= GQSPI_IXR_ALL_MASK;
+ writel(ier, &regs->ier);
+ while (priv->bytes_to_receive && timeout) {
+ isr = readl(&regs->isr);
+ if (isr & GQSPI_IXR_RXNEMTY_MASK) {
+ if (priv->bytes_to_receive >= 4) {
+ *traverse = readl(&regs->drxr);
+ traverse++;
+ priv->bytes_to_receive -= 4;
+ } else {
+ last_bits = readl(&regs->drxr);
+ memcpy(traverse, &last_bits,
+ priv->bytes_to_receive);
+ priv->bytes_to_receive = 0;
+ }
+ timeout = GQSPI_TIMEOUT;
+ } else {
+ udelay(1);
+ timeout--;
+ }
+ }
+
+ debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
+ (unsigned long)buf, (unsigned long)priv->rx_buf,
+ *buf, actuallen);
+ if (!timeout) {
+ printf("IO timeout: %d\n", readl(&regs->isr));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
u32 gen_fifo_cmd, u32 *buf)
{
u32 addr;
u32 size;
u32 actuallen = priv->len;
+ u32 totallen = priv->len;
int ret = 0;
struct zynqmp_qspi_dma_regs *dma_regs = priv->dma_regs;
- writel((unsigned long)buf, &dma_regs->dmadst);
- writel(roundup(priv->len, GQSPI_DMA_ALIGN), &dma_regs->dmasize);
- writel(GQSPI_DMA_DST_I_STS_MASK, &dma_regs->dmaier);
- addr = (unsigned long)buf;
- size = roundup(priv->len, GQSPI_DMA_ALIGN);
- flush_dcache_range(addr, addr + size);
+ while (totallen) {
+ if (totallen >= SZ_512M)
+ priv->len = SZ_256M;
+ else
+ priv->len = totallen;
- while (priv->len) {
- zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
- zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
+ totallen -= priv->len; /* Save remaining bytes length to read */
+ actuallen = priv->len; /* Actual number of bytes reading */
- debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd);
- }
+ writel((unsigned long)buf, &dma_regs->dmadst);
+ writel(roundup(priv->len, GQSPI_DMA_ALIGN), &dma_regs->dmasize);
+ writel(GQSPI_DMA_DST_I_STS_MASK, &dma_regs->dmaier);
+ addr = (unsigned long)buf;
+ size = roundup(priv->len, GQSPI_DMA_ALIGN);
+ flush_dcache_range(addr, addr + size);
- ret = wait_for_bit_le32(&dma_regs->dmaisr, GQSPI_DMA_DST_I_STS_DONE,
- 1, GQSPI_TIMEOUT, 1);
- if (ret) {
- printf("DMA Timeout:0x%x\n", readl(&dma_regs->dmaisr));
- return -ETIMEDOUT;
- }
+ while (priv->len) {
+ zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
+ zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
+
+ debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd);
+ }
+
+ ret = wait_for_bit_le32(&dma_regs->dmaisr,
+ GQSPI_DMA_DST_I_STS_DONE, 1,
+ GQSPI_TIMEOUT, 1);
+ if (ret) {
+ printf("DMA Timeout:0x%x\n", readl(&dma_regs->dmaisr));
+ return -ETIMEDOUT;
+ }
- writel(GQSPI_DMA_DST_I_STS_DONE, &dma_regs->dmaisr);
+ writel(GQSPI_DMA_DST_I_STS_DONE, &dma_regs->dmaisr);
- debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
- (unsigned long)buf, (unsigned long)priv->rx_buf, *buf,
- actuallen);
+ debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
+ (unsigned long)buf, (unsigned long)priv->rx_buf, *buf,
+ actuallen);
- if (buf != priv->rx_buf)
- memcpy(priv->rx_buf, buf, actuallen);
+ if (buf != priv->rx_buf)
+ memcpy(priv->rx_buf, buf, actuallen);
+
+ buf = (u32 *)((u8 *)buf + actuallen);
+ priv->rx_buf = (u8 *)priv->rx_buf + actuallen;
+ }
return 0;
}
@@ -626,21 +723,22 @@ static int zynqmp_qspi_genfifo_fill_rx(struct zynqmp_qspi_priv *priv)
gen_fifo_cmd = zynqmp_qspi_bus_select(priv);
gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(priv->op->data.buswidth);
- gen_fifo_cmd |= GQSPI_GFIFO_RX |
- GQSPI_GFIFO_DATA_XFR_MASK;
+ gen_fifo_cmd |= GQSPI_GFIFO_RX | GQSPI_GFIFO_DATA_XFR_MASK;
/*
* Check if receive buffer is aligned to 4 byte and length
* is multiples of four byte as we are using dma to receive.
*/
- if (!((unsigned long)priv->rx_buf & (GQSPI_DMA_ALIGN - 1)) &&
- !(actuallen % GQSPI_DMA_ALIGN)) {
+ if ((!((unsigned long)priv->rx_buf & (GQSPI_DMA_ALIGN - 1)) &&
+ !(actuallen % GQSPI_DMA_ALIGN)) || priv->io_mode) {
buf = (u32 *)priv->rx_buf;
- return zynqmp_qspi_start_dma(priv, gen_fifo_cmd, buf);
+ if (priv->io_mode)
+ return zynqmp_qspi_start_io(priv, gen_fifo_cmd, buf);
+ else
+ return zynqmp_qspi_start_dma(priv, gen_fifo_cmd, buf);
}
- ALLOC_CACHE_ALIGN_BUFFER(u8, tmp, roundup(priv->len,
- GQSPI_DMA_ALIGN));
+ ALLOC_CACHE_ALIGN_BUFFER(u8, tmp, roundup(priv->len, GQSPI_DMA_ALIGN));
buf = (u32 *)tmp;
return zynqmp_qspi_start_dma(priv, gen_fifo_cmd, buf);
}