Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-microblaze.git] / drivers / spi / spi-nxp-fspi.c
index ab90356..6e6c240 100644 (file)
 #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>
 
 #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);