diff options
author | Tom Rini <trini@konsulko.com> | 2022-10-03 15:39:46 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-10-03 15:39:46 -0400 |
commit | 2d4591353452638132d711551fec3495b7644731 (patch) | |
tree | e12058de7f553e84f8d13e545f130c7a48973589 /drivers/spi | |
parent | 4debc57a3da6c3f4d3f89a637e99206f4cea0a96 (diff) | |
parent | 6ee6e15975cad3c99fad3a66223f3fd9287a369b (diff) | |
download | u-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/Kconfig | 28 | ||||
-rw-r--r-- | drivers/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/spi/cadence_ospi_versal.c | 59 | ||||
-rw-r--r-- | drivers/spi/cadence_qspi.c | 104 | ||||
-rw-r--r-- | drivers/spi/cadence_qspi.h | 56 | ||||
-rw-r--r-- | drivers/spi/cadence_qspi_apb.c | 231 | ||||
-rw-r--r-- | drivers/spi/mtk_snfi_spi.c | 2 | ||||
-rw-r--r-- | drivers/spi/mtk_spim.c | 701 | ||||
-rw-r--r-- | drivers/spi/octeon_spi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-aspeed-smc.c | 1218 | ||||
-rw-r--r-- | drivers/spi/spi-mem.c | 268 | ||||
-rw-r--r-- | drivers/spi/stm32_qspi.c | 2 | ||||
-rw-r--r-- | drivers/spi/zynqmp_gqspi.c | 222 |
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(®s->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, ®s->confr); writel(GQSPI_ENABLE_ENABLE_MASK, ®s->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(®s->lpbkdly); - lpbkdlyadj |= (GQSPI_LPBK_DLY_ADJ_LPBK_MASK); - datadlyadj = readl(®s->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(®s->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, ®s->lpbkdly); writel(datadlyadj, ®s->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(®s->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(®s->confr); + config_reg |= GQSPI_STRT_GEN_FIFO; + writel(config_reg, ®s->confr); + /* Enable RX interrupts for IO mode */ + ier = readl(®s->ier); + ier |= GQSPI_IXR_ALL_MASK; + writel(ier, ®s->ier); + while (priv->bytes_to_receive && timeout) { + isr = readl(®s->isr); + if (isr & GQSPI_IXR_RXNEMTY_MASK) { + if (priv->bytes_to_receive >= 4) { + *traverse = readl(®s->drxr); + traverse++; + priv->bytes_to_receive -= 4; + } else { + last_bits = readl(®s->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(®s->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); } |