/* * NXP FlexSPI(FSPI) controller driver. * * Copyright 2019-2020 NXP * Copyright 2020 Puresoftware Ltd. * * FlexSPI is a flexsible SPI host controller which supports two SPI * channels and up to 4 external devices. Each channel supports * Single/Dual/Quad/Octal mode data transfer (1/2/4/8 bidirectional * data lines). * * FlexSPI controller is driven by the LUT(Look-up Table) registers * LUT registers are a look-up-table for sequences of instructions. * A valid sequence consists of four LUT registers. * Maximum 32 LUT sequences can be programmed simultaneously. * * LUTs are being created at run-time based on the commands passed * from the spi-mem framework, thus using single LUT index. * * Software triggered Flash read/write access by IP Bus. * * Memory mapped read access by AHB Bus. * * Based on SPI MEM interface and spi-fsl-qspi.c driver. * * Author: * Yogesh Narayan Gaur <yogeshnarayan.gaur@nxp.com> * Boris Brezillon <bbrezillon@kernel.org> * Frieder Schrempf <frieder.schrempf@kontron.de>
*/
/* * Calculate number of required PAD bits for LUT register. * * The pad stands for the number of IO lines [0:7]. * For example, the octal read needs eight IO lines, * so you should use LUT_PAD(8). This macro * returns 3 i.e. use eight (2^3) IP lines for read.
*/ #define LUT_PAD(x) (fls(x) - 1)
/* * Macro for constructing the LUT entries with the following * register layout: * * --------------------------------------------------- * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | * ---------------------------------------------------
*/ #define PAD_SHIFT 8 #define INSTR_SHIFT 10 #define OPRND_SHIFT 16
/* * R/W functions for big- or little-endian registers: * The FSPI controller's endianness is independent of * the CPU core's endianness. So far, although the CPU * core is little-endian the FSPI controller can use * big-endian or little-endian.
*/ staticvoid fspi_writel(struct nxp_fspi *f, u32 val, void __iomem *addr)
{ if (f->devtype_data->little_endian)
iowrite32(val, addr); else
iowrite32be(val, addr);
}
ret = nxp_fspi_check_buswidth(f, op->cmd.buswidth);
if (op->addr.nbytes)
ret |= nxp_fspi_check_buswidth(f, op->addr.buswidth);
if (op->dummy.nbytes)
ret |= nxp_fspi_check_buswidth(f, op->dummy.buswidth);
if (op->data.nbytes)
ret |= nxp_fspi_check_buswidth(f, op->data.buswidth);
if (ret) returnfalse;
/* * The number of address bytes should be equal to or less than 4 bytes.
*/ if (op->addr.nbytes > 4) returnfalse;
/* * If requested address value is greater than controller assigned * memory mapped space, return error as it didn't fit in the range * of assigned address space.
*/ if (op->addr.val >= f->memmap_phy_size) returnfalse;
/* Max 64 dummy clock cycles supported */ if (op->dummy.buswidth &&
(op->dummy.nbytes * 8 / op->dummy.buswidth > 64)) returnfalse;
/* Max data length, check controller limits and alignment */ if (op->data.dir == SPI_MEM_DATA_IN &&
(op->data.nbytes > f->devtype_data->ahb_buf_size ||
(op->data.nbytes > f->devtype_data->rxfifo - 4 &&
!IS_ALIGNED(op->data.nbytes, 8)))) returnfalse;
if (op->data.dir == SPI_MEM_DATA_OUT &&
op->data.nbytes > f->devtype_data->txfifo) returnfalse;
/* * If the target device content being changed by Write/Erase, need to * invalidate the AHB buffer. This can be achieved by doing the reset * of controller after setting MCR0[SWRESET] bit.
*/ staticinlinevoid nxp_fspi_invalid(struct nxp_fspi *f)
{
u32 reg; int ret;
/* dummy bytes, if needed */ if (op->dummy.nbytes) {
lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_DUMMY, /* * Due to FlexSPI controller limitation number of PAD for dummy * buswidth needs to be programmed as equal to data buswidth.
*/
LUT_PAD(op->data.buswidth),
op->dummy.nbytes * 8 /
op->dummy.buswidth);
lutidx++;
}
/* fill LUT */ for (i = 0; i < ARRAY_SIZE(lutval); i++) {
target_lut_reg = FSPI_LUT_BASE + lut_offset + i * 4;
fspi_writel(f, lutval[i], base + target_lut_reg);
}
/* * Sample Clock source selection for Flash Reading * Four modes defined by fspi: * mode 0: Dummy Read strobe generated by FlexSPI Controller * and loopback internally * mode 1: Dummy Read strobe generated by FlexSPI Controller * and loopback from DQS pad * mode 2: Reserved * mode 3: Flash provided Read strobe and input from DQS pad * * fspi default use mode 0 after reset
*/ staticvoid nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f, bool op_is_dtr)
{
u32 reg;
/* * For 8D-8D-8D mode, need to use mode 3 (Flash provided Read * strobe and input from DQS pad), otherwise read operaton may * meet issue. * This mode require flash device connect the DQS pad on board. * For other modes, still use mode 0, keep align with before. * spi_nor_suspend will disable 8D-8D-8D mode, also need to * change the mode back to mode 0.
*/
reg = fspi_readl(f, f->iobase + FSPI_MCR0); if (op_is_dtr) {
reg |= FSPI_MCR0_RXCLKSRC(3);
f->max_rate = 166000000;
} else { /*select mode 0 */
reg &= ~FSPI_MCR0_RXCLKSRC(3);
f->max_rate = 66000000;
}
fspi_writel(f, reg, f->iobase + FSPI_MCR0);
}
staticvoid nxp_fspi_dll_calibration(struct nxp_fspi *f)
{ int ret;
/* Reset the DLL, set the DLLRESET to 1 and then set to 0 */
fspi_writel(f, FSPI_DLLACR_DLLRESET, f->iobase + FSPI_DLLACR);
fspi_writel(f, FSPI_DLLBCR_DLLRESET, f->iobase + FSPI_DLLBCR);
fspi_writel(f, 0, f->iobase + FSPI_DLLACR);
fspi_writel(f, 0, f->iobase + FSPI_DLLBCR);
/* * Enable the DLL calibration mode. * The delay target for slave delay line is: * ((SLVDLYTARGET+1) * 1/32 * clock cycle of reference clock. * When clock rate > 100MHz, recommend SLVDLYTARGET is 0xF, which * means half of clock cycle of reference clock.
*/
fspi_writel(f, FSPI_DLLACR_DLLEN | FSPI_DLLACR_SLVDLY(0xF),
f->iobase + FSPI_DLLACR);
fspi_writel(f, FSPI_DLLBCR_DLLEN | FSPI_DLLBCR_SLVDLY(0xF),
f->iobase + FSPI_DLLBCR);
/* Wait to get REF/SLV lock */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_STS2, FSPI_STS2_AB_LOCK,
0, POLL_TOUT, true); if (ret)
dev_warn(f->dev, "DLL lock failed, please fix it!\n");
/* * For ERR050272, DLL lock status bit is not accurate, * wait for 4us more as a workaround.
*/
udelay(4);
}
/* * In FlexSPI controller, flash access is based on value of FSPI_FLSHXXCR0 * register and start base address of the target device. * * (Higher address) * -------- <-- FLSHB2CR0 * | B2 | * | | * B2 start address --> -------- <-- FLSHB1CR0 * | B1 | * | | * B1 start address --> -------- <-- FLSHA2CR0 * | A2 | * | | * A2 start address --> -------- <-- FLSHA1CR0 * | A1 | * | | * A1 start address --> -------- (Lower address) * * * Start base address defines the starting address range for given CS and * FSPI_FLSHXXCR0 defines the size of the target device connected at given CS. * * But, different targets are having different combinations of number of CS, * some targets only have single CS or two CS covering controller's full * memory mapped space area. * Thus, implementation is being done as independent of the size and number * of the connected target device. * Assign controller memory mapped space size as the size to the connected * target device. * Mark FLSHxxCR0 as zero initially and then assign value only to the selected * chip-select Flash configuration register. * * For e.g. to access CS2 (B1), FLSHB1CR0 register would be equal to the * memory mapped size of the controller. * Value for rest of the CS FLSHxxCR0 register would be zero. *
*/ staticvoid nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, conststruct spi_mem_op *op)
{ /* flexspi only support one DTR mode: 8D-8D-8D */ bool op_is_dtr = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr; unsignedlong rate = op->max_freq; int ret;
uint64_t size_kb;
/* * Return when following condition all meet, * 1, if previously selected target device is same as current * requested target device. * 2, the DTR or STR mode do not change. * 3, previous operation max rate equals current one. * * For other case, need to re-config.
*/ if ((f->selected == spi_get_chipselect(spi, 0)) &&
(!!(f->flags & FSPI_DTR_MODE) == op_is_dtr) &&
(f->pre_op_rate == op->max_freq)) return;
if (op_is_dtr) {
f->flags |= FSPI_DTR_MODE; /* For DTR mode, flexspi will default div 2 and output to device. * so here to config the root clock to 2 * device rate.
*/
rate = rate * 2;
} else {
f->flags &= ~FSPI_DTR_MODE;
}
nxp_fspi_clk_disable_unprep(f);
ret = clk_set_rate(f->clk, rate); if (ret) return;
ret = nxp_fspi_clk_prep_enable(f); if (ret) return;
/* * If clock rate > 100MHz, then switch from DLL override mode to * DLL calibration mode.
*/ if (rate > 100000000)
nxp_fspi_dll_calibration(f);
/* clear the TX FIFO. */
fspi_writel(f, FSPI_IPTXFCR_CLR, base + FSPI_IPTXFCR);
/* * Default value of water mark level is 8 bytes, hence in single * write request controller can write max 8 bytes of data.
*/
for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 8); i += 8) { /* Wait for TXFIFO empty */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
FSPI_INTR_IPTXWE, 0,
POLL_TOUT, true);
WARN_ON(ret);
fspi_writel(f, *(u32 *) (buf + i), base + FSPI_TFDR);
fspi_writel(f, *(u32 *) (buf + i + 4), base + FSPI_TFDR + 4);
fspi_writel(f, FSPI_INTR_IPTXWE, base + FSPI_INTR);
}
if (i < op->data.nbytes) {
u32 data = 0; int j; int remaining = op->data.nbytes - i; /* Wait for TXFIFO empty */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
FSPI_INTR_IPTXWE, 0,
POLL_TOUT, true);
WARN_ON(ret);
for (j = 0; j < ALIGN(remaining, 4); j += 4) {
memcpy(&data, buf + i + j, min_t(int, 4, remaining - j));
fspi_writel(f, data, base + FSPI_TFDR + j);
}
fspi_writel(f, FSPI_INTR_IPTXWE, base + FSPI_INTR);
}
}
staticvoid nxp_fspi_read_rxfifo(struct nxp_fspi *f, conststruct spi_mem_op *op)
{ void __iomem *base = f->iobase; int i, ret; int len = op->data.nbytes;
u8 *buf = (u8 *) op->data.buf.in;
/* * Default value of water mark level is 8 bytes, hence in single * read request controller can read max 8 bytes of data.
*/ for (i = 0; i < ALIGN_DOWN(len, 8); i += 8) { /* Wait for RXFIFO available */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
FSPI_INTR_IPRXWA, 0,
POLL_TOUT, true);
WARN_ON(ret);
*(u32 *)(buf + i) = fspi_readl(f, base + FSPI_RFDR);
*(u32 *)(buf + i + 4) = fspi_readl(f, base + FSPI_RFDR + 4); /* move the FIFO pointer */
fspi_writel(f, FSPI_INTR_IPRXWA, base + FSPI_INTR);
}
if (i < len) {
u32 tmp; int size, j;
buf = op->data.buf.in + i; /* Wait for RXFIFO available */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_INTR,
FSPI_INTR_IPRXWA, 0,
POLL_TOUT, true);
WARN_ON(ret);
len = op->data.nbytes - i; for (j = 0; j < op->data.nbytes - i; j += 4) {
tmp = fspi_readl(f, base + FSPI_RFDR + j);
size = min(len, 4);
memcpy(buf + j, &tmp, size);
len -= size;
}
}
/* invalid the RXFIFO */
fspi_writel(f, FSPI_IPRXFCR_CLR, base + FSPI_IPRXFCR); /* move the FIFO pointer */
fspi_writel(f, FSPI_INTR_IPRXWA, base + FSPI_INTR);
}
reg = fspi_readl(f, base + FSPI_IPRXFCR); /* invalid RXFIFO first */
reg &= ~FSPI_IPRXFCR_DMA_EN;
reg = reg | FSPI_IPRXFCR_CLR;
fspi_writel(f, reg, base + FSPI_IPRXFCR);
init_completion(&f->c);
fspi_writel(f, op->addr.val, base + FSPI_IPCR0); /* * Always start the sequence at the same index since we update * the LUT at each exec_op() call. And also specify the DATA * length, since it's has not been specified in the LUT.
*/
seqid_lut = f->devtype_data->lut_num - 1;
fspi_writel(f, op->data.nbytes |
(seqid_lut << FSPI_IPCR1_SEQID_SHIFT) |
(seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
base + FSPI_IPCR1);
/* Trigger the LUT now. */
fspi_writel(f, FSPI_IPCMD_TRG, base + FSPI_IPCMD);
/* Wait for the interrupt. */ if (!wait_for_completion_timeout(&f->c, msecs_to_jiffies(1000)))
err = -ETIMEDOUT;
/* Invoke IP data read, if request is of data read. */ if (!err && op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
nxp_fspi_read_rxfifo(f, op);
err = pm_runtime_get_sync(f->dev); if (err < 0) {
dev_err(f->dev, "Failed to enable clock %d\n", __LINE__); return err;
}
/* Wait for controller being ready. */
err = fspi_readl_poll_tout(f, f->iobase + FSPI_STS0,
FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true);
WARN_ON(err);
nxp_fspi_select_mem(f, mem->spi, op);
nxp_fspi_prepare_lut(f, op); /* * If we have large chunks of data, we read them through the AHB bus by * accessing the mapped memory. In all other cases we use IP commands * to access the flash. Read via AHB bus may be corrupted due to * existence of an errata and therefore discard AHB read in such cases.
*/ if (op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
op->data.dir == SPI_MEM_DATA_IN &&
!needs_ip_only(f)) {
err = nxp_fspi_read_ahb(f, op);
} else { if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
nxp_fspi_fill_txfifo(f, op);
err = nxp_fspi_do_op(f, op);
}
/* Invalidate the data in the AHB buffer. */
nxp_fspi_invalid(f);
/* Limit data bytes to RX FIFO in case of IP read only */ if (op->data.dir == SPI_MEM_DATA_IN &&
needs_ip_only(f) &&
op->data.nbytes > f->devtype_data->rxfifo)
op->data.nbytes = f->devtype_data->rxfifo;
/* disable and unprepare clock to avoid glitch pass to controller */
nxp_fspi_clk_disable_unprep(f);
/* the default frequency, we will change it later if necessary. */
ret = clk_set_rate(f->clk, 20000000); if (ret) return ret;
ret = nxp_fspi_clk_prep_enable(f); if (ret) return ret;
/* * ERR050568: Flash access by FlexSPI AHB command may not work with * platform frequency equal to 300 MHz on LS1028A. * LS1028A reuses LX2160A compatible entry. Make errata applicable for * Layerscape LS1028A platform.
*/ if (of_device_is_compatible(f->dev->of_node, "nxp,lx2160a-fspi"))
erratum_err050568(f);
/* Reset the module */ /* w1c register, wait unit clear */
ret = fspi_readl_poll_tout(f, f->iobase + FSPI_MCR0,
FSPI_MCR0_SWRST, 0, POLL_TOUT, false);
WARN_ON(ret);
/* Disable the module */
fspi_writel(f, FSPI_MCR0_MDIS, base + FSPI_MCR0);
/* * Config the DLL register to default value, enable the target clock delay * line delay cell override mode, and use 1 fixed delay cell in DLL delay * chain, this is the suggested setting when clock rate < 100MHz.
*/
fspi_writel(f, FSPI_DLLACR_OVRDEN, base + FSPI_DLLACR);
fspi_writel(f, FSPI_DLLBCR_OVRDEN, base + FSPI_DLLBCR);
/* * Disable same device enable bit and configure all target devices * independently.
*/
reg = fspi_readl(f, f->iobase + FSPI_MCR2);
reg = reg & ~(FSPI_MCR2_SAMEDEVICEEN);
fspi_writel(f, reg, base + FSPI_MCR2);
/* AHB configuration for access buffer 0~7. */ for (i = 0; i < 7; i++)
fspi_writel(f, 0, base + FSPI_AHBRX_BUF0CR0 + 4 * i);
/* * Set ADATSZ with the maximum AHB buffer size to improve the read * performance.
*/
fspi_writel(f, (f->devtype_data->ahb_buf_size / 8 |
FSPI_AHBRXBUF0CR7_PREF), base + FSPI_AHBRX_BUF7CR0);
/* prefetch and no start address alignment limitation */
fspi_writel(f, FSPI_AHBCR_PREF_EN | FSPI_AHBCR_RDADDROPT,
base + FSPI_AHBCR);
/* Reset the FLSHxCR1 registers. */
reg = FSPI_FLSHXCR1_TCSH(0x3) | FSPI_FLSHXCR1_TCSS(0x3);
fspi_writel(f, reg, base + FSPI_FLSHA1CR1);
fspi_writel(f, reg, base + FSPI_FLSHA2CR1);
fspi_writel(f, reg, base + FSPI_FLSHB1CR1);
fspi_writel(f, reg, base + FSPI_FLSHB2CR1);
/* * The driver only uses one single LUT entry, that is updated on * each call of exec_op(). Index 0 is preset at boot with a basic * read operation, so let's use the last entry.
*/
seqid_lut = f->devtype_data->lut_num - 1; /* AHB Read - Set lut sequence ID for all CS. */
fspi_writel(f, seqid_lut, base + FSPI_FLSHA1CR2);
fspi_writel(f, seqid_lut, base + FSPI_FLSHA2CR2);
fspi_writel(f, seqid_lut, base + FSPI_FLSHB1CR2);
fspi_writel(f, seqid_lut, base + FSPI_FLSHB2CR2);
f->selected = -1;
/* enable the interrupt */
fspi_writel(f, FSPI_INTEN_IPCMDDONE, base + FSPI_INTEN);
// Set custom name derived from the platform_device of the controller. if (of_get_available_child_count(f->dev->of_node) == 1) return dev_name(f->dev);
name = devm_kasprintf(dev, GFP_KERNEL, "%s-%d", dev_name(f->dev),
spi_get_chipselect(mem->spi, 0));
if (!name) {
dev_err(dev, "failed to get memory for custom flash name\n"); return ERR_PTR(-ENOMEM);
}
f = spi_controller_get_devdata(ctlr);
f->dev = dev;
f->devtype_data = (struct nxp_fspi_devtype_data *)device_get_match_data(dev); if (!f->devtype_data) return -ENODEV;
platform_set_drvdata(pdev, f);
/* find the resources - configuration register address space */ if (is_acpi_node(dev_fwnode(f->dev)))
f->iobase = devm_platform_ioremap_resource(pdev, 0); else
f->iobase = devm_platform_ioremap_resource_byname(pdev, "fspi_base"); if (IS_ERR(f->iobase)) return PTR_ERR(f->iobase);
/* find the resources - controller memory mapped space */ if (is_acpi_node(dev_fwnode(f->dev)))
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); else
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "fspi_mmap"); if (!res) return -ENODEV;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.