summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-nxp-fspi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-nxp-fspi.c')
-rw-r--r--drivers/spi/spi-nxp-fspi.c115
1 files changed, 104 insertions, 11 deletions
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index ab9035662717..6e6c2403944d 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;
@@ -1151,7 +1242,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);