diff options
Diffstat (limited to 'drivers/spi/spi-nxp-fspi.c')
-rw-r--r-- | drivers/spi/spi-nxp-fspi.c | 126 |
1 files changed, 109 insertions, 17 deletions
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index ab9035662717..a66fa97046ee 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -49,8 +49,11 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_qos.h> +#include <linux/regmap.h> #include <linux/sizes.h> +#include <linux/sys_soc.h> +#include <linux/mfd/syscon.h> #include <linux/spi/spi.h> #include <linux/spi/spi-mem.h> @@ -311,6 +314,11 @@ #define NXP_FSPI_MAX_CHIPSELECT 4 #define NXP_FSPI_MIN_IOMAP SZ_4M +#define DCFG_RCWSR1 0x100 + +/* Access flash memory using IP bus only */ +#define FSPI_QUIRK_USE_IP_ONLY BIT(0) + struct nxp_fspi_devtype_data { unsigned int rxfifo; unsigned int txfifo; @@ -319,7 +327,7 @@ struct nxp_fspi_devtype_data { bool little_endian; }; -static const struct nxp_fspi_devtype_data lx2160a_data = { +static struct nxp_fspi_devtype_data lx2160a_data = { .rxfifo = SZ_512, /* (64 * 64 bits) */ .txfifo = SZ_1K, /* (128 * 64 bits) */ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ @@ -327,7 +335,7 @@ static const struct nxp_fspi_devtype_data lx2160a_data = { .little_endian = true, /* little-endian */ }; -static const struct nxp_fspi_devtype_data imx8mm_data = { +static struct nxp_fspi_devtype_data imx8mm_data = { .rxfifo = SZ_512, /* (64 * 64 bits) */ .txfifo = SZ_1K, /* (128 * 64 bits) */ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ @@ -335,7 +343,7 @@ static const struct nxp_fspi_devtype_data imx8mm_data = { .little_endian = true, /* little-endian */ }; -static const struct nxp_fspi_devtype_data imx8qxp_data = { +static struct nxp_fspi_devtype_data imx8qxp_data = { .rxfifo = SZ_512, /* (64 * 64 bits) */ .txfifo = SZ_1K, /* (128 * 64 bits) */ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ @@ -343,6 +351,14 @@ static const struct nxp_fspi_devtype_data imx8qxp_data = { .little_endian = true, /* little-endian */ }; +static struct nxp_fspi_devtype_data imx8dxl_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = FSPI_QUIRK_USE_IP_ONLY, + .little_endian = true, /* little-endian */ +}; + struct nxp_fspi { void __iomem *iobase; void __iomem *ahb_addr; @@ -353,12 +369,17 @@ struct nxp_fspi { struct clk *clk, *clk_en; struct device *dev; struct completion c; - const struct nxp_fspi_devtype_data *devtype_data; + struct nxp_fspi_devtype_data *devtype_data; struct mutex lock; struct pm_qos_request pm_qos_req; int selected; }; +static inline int needs_ip_only(struct nxp_fspi *f) +{ + return f->devtype_data->quirks & FSPI_QUIRK_USE_IP_ONLY; +} + /* * R/W functions for big- or little-endian registers: * The FSPI controller's endianness is independent of @@ -553,8 +574,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f, for (i = 0; i < ARRAY_SIZE(lutval); i++) fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i)); - dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x]\n", - op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3]); + dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n", + op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes); /* lock LUT */ fspi_writel(f, FSPI_LUTKEY_VALUE, f->iobase + FSPI_LUTKEY); @@ -852,12 +873,14 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *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. + * 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) { + 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) @@ -888,9 +911,68 @@ static int nxp_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8); } + /* 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; + return 0; } +static void erratum_err050568(struct nxp_fspi *f) +{ + const struct soc_device_attribute ls1028a_soc_attr[] = { + { .family = "QorIQ LS1028A" }, + { /* sentinel */ } + }; + struct device_node *np; + struct regmap *map; + u32 val = 0, sysclk = 0; + int ret; + + /* Check for LS1028A family */ + if (!soc_device_match(ls1028a_soc_attr)) { + dev_dbg(f->dev, "Errata applicable only for LS1028A\n"); + return; + } + + /* Compute system clock frequency multiplier ratio */ + map = syscon_regmap_lookup_by_compatible("fsl,ls1028a-dcfg"); + if (IS_ERR(map)) { + dev_err(f->dev, "No syscon regmap\n"); + goto err; + } + + ret = regmap_read(map, DCFG_RCWSR1, &val); + if (ret < 0) + goto err; + + /* Strap bits 6:2 define SYS_PLL_RAT i.e frequency multiplier ratio */ + val = (val >> 2) & 0x1F; + WARN(val == 0, "Strapping is zero: Cannot determine ratio"); + + /* Compute system clock frequency */ + np = of_find_node_by_name(NULL, "clock-sysclk"); + if (!np) + goto err; + + if (of_property_read_u32(np, "clock-frequency", &sysclk)) + goto err; + + sysclk = (sysclk * val) / 1000000; /* Convert sysclk to Mhz */ + dev_dbg(f->dev, "val: 0x%08x, sysclk: %dMhz\n", val, sysclk); + + /* Use IP bus only if PLL is 300MHz */ + if (sysclk == 300) + f->devtype_data->quirks |= FSPI_QUIRK_USE_IP_ONLY; + + return; + +err: + dev_err(f->dev, "Errata cannot be executed. Read via IP bus may not work\n"); +} + static int nxp_fspi_default_setup(struct nxp_fspi *f) { void __iomem *base = f->iobase; @@ -909,6 +991,15 @@ static int nxp_fspi_default_setup(struct nxp_fspi *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, @@ -1012,7 +1103,7 @@ static int nxp_fspi_probe(struct platform_device *pdev) f = spi_controller_get_devdata(ctlr); f->dev = dev; - f->devtype_data = device_get_match_data(dev); + f->devtype_data = (struct nxp_fspi_devtype_data *)device_get_match_data(dev); if (!f->devtype_data) { ret = -ENODEV; goto err_put_ctrl; @@ -1033,12 +1124,6 @@ static int nxp_fspi_probe(struct platform_device *pdev) goto err_put_ctrl; } - /* Clear potential interrupts */ - reg = fspi_readl(f, f->iobase + FSPI_INTR); - if (reg) - fspi_writel(f, reg, f->iobase + FSPI_INTR); - - /* find the resources - controller memory mapped space */ if (is_acpi_node(f->dev->fwnode)) res = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -1076,6 +1161,11 @@ static int nxp_fspi_probe(struct platform_device *pdev) } } + /* Clear potential interrupts */ + reg = fspi_readl(f, f->iobase + FSPI_INTR); + if (reg) + fspi_writel(f, reg, f->iobase + FSPI_INTR); + /* find the irq */ ret = platform_get_irq(pdev, 0); if (ret < 0) @@ -1151,7 +1241,9 @@ static int nxp_fspi_resume(struct device *dev) static const struct of_device_id nxp_fspi_dt_ids[] = { { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, }, { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, + { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, }, { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, }, + { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); |