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.c168
1 files changed, 146 insertions, 22 deletions
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index 36a44a837031..e9237d0a9770 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -52,6 +52,11 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
+#include <linux/pm_runtime.h>
+
+/* runtime pm timeout */
+#define FSPI_RPM_TIMEOUT 50 /* 50ms */
+
/*
* 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
@@ -307,6 +312,10 @@
#define POLL_TOUT 5000
#define NXP_FSPI_MAX_CHIPSELECT 4
+#define NXP_FSPI_MIN_IOMAP SZ_4M
+
+/* access memory via IPS only due to this errata */
+#define NXP_FSPI_QUIRK_ERR050601 BIT(0)
struct nxp_fspi_devtype_data {
unsigned int rxfifo;
@@ -324,11 +333,37 @@ static const struct nxp_fspi_devtype_data lx2160a_data = {
.little_endian = true, /* little-endian */
};
+static const 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) */
+ .quirks = 0,
+ .little_endian = true, /* little-endian */
+};
+
+static const 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) */
+ .quirks = 0,
+ .little_endian = true, /* little-endian */
+};
+
+static const 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 = NXP_FSPI_QUIRK_ERR050601,
+ .little_endian = true, /* little-endian */
+};
+
struct nxp_fspi {
void __iomem *iobase;
void __iomem *ahb_addr;
u32 memmap_phy;
u32 memmap_phy_size;
+ u32 memmap_start;
+ u32 memmap_len;
struct clk *clk, *clk_en;
struct device *dev;
struct completion c;
@@ -336,8 +371,15 @@ struct nxp_fspi {
struct mutex lock;
struct pm_qos_request pm_qos_req;
int selected;
+#define FSPI_INITILIZED (1 << 0)
+ int flags;
};
+static inline int nxp_fspi_ips_access_only(struct nxp_fspi *f)
+{
+ return f->devtype_data->quirks & NXP_FSPI_QUIRK_ERR050601;
+}
+
/*
* R/W functions for big- or little-endian registers:
* The FSPI controller's endianness is independent of
@@ -641,12 +683,35 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
f->selected = spi->chip_select;
}
-static void nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
+static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
{
+ u32 start = op->addr.val;
u32 len = op->data.nbytes;
+ /* if necessary, ioremap before AHB read */
+ if ((!f->ahb_addr) || start < f->memmap_start ||
+ start + len > f->memmap_start + f->memmap_len) {
+ if (f->ahb_addr)
+ iounmap(f->ahb_addr);
+
+ f->memmap_start = start;
+ f->memmap_len = len > NXP_FSPI_MIN_IOMAP ?
+ len : NXP_FSPI_MIN_IOMAP;
+
+ f->ahb_addr = ioremap_wc(f->memmap_phy + f->memmap_start,
+ f->memmap_len);
+
+ if (!f->ahb_addr) {
+ dev_err(f->dev, "failed to alloc memory\n");
+ return -ENOMEM;
+ }
+ }
+
/* Read out the data directly from the AHB buffer. */
- memcpy_fromio(op->data.buf.in, (f->ahb_addr + op->addr.val), len);
+ memcpy_fromio(op->data.buf.in,
+ f->ahb_addr + start - f->memmap_start, len);
+
+ return 0;
}
static void nxp_fspi_fill_txfifo(struct nxp_fspi *f,
@@ -791,6 +856,12 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
mutex_lock(&f->lock);
+ err = pm_runtime_get_sync(f->dev);
+ if (err < 0) {
+ dev_err(f->dev, "Failed to enable clock %d\n", __LINE__);
+ goto err_mutex;
+ }
+
/* Wait for controller being ready. */
err = fspi_readl_poll_tout(f, f->iobase + FSPI_STS0,
FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true);
@@ -802,11 +873,13 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *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.
+ * IP commands to access the flash, but ahb read won't be used for dxl
+ * since IC errta.
*/
- if (op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
+ if (!nxp_fspi_ips_access_only(f) &&
+ op->data.nbytes > (f->devtype_data->rxfifo - 4) &&
op->data.dir == SPI_MEM_DATA_IN) {
- nxp_fspi_read_ahb(f, op);
+ 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);
@@ -817,8 +890,13 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
/* Invalidate the data in the AHB buffer. */
nxp_fspi_invalid(f);
+ pm_runtime_mark_last_busy(f->dev);
+ pm_runtime_put_autosuspend(f->dev);
+
mutex_unlock(&f->lock);
+err_mutex:
+ mutex_unlock(&f->lock);
return err;
}
@@ -834,6 +912,10 @@ static int nxp_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
op->data.nbytes = f->devtype_data->ahb_buf_size;
else if (op->data.nbytes > (f->devtype_data->rxfifo - 4))
op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8);
+ /* dxl won't use ahb to access data, limit to rxfifo size */
+ if (nxp_fspi_ips_access_only(f) &&
+ op->data.nbytes > f->devtype_data->rxfifo)
+ op->data.nbytes = f->devtype_data->rxfifo;
}
return 0;
@@ -871,8 +953,9 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
fspi_writel(f, FSPI_DLLBCR_OVRDEN, base + FSPI_DLLBCR);
/* enable module */
- fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) | FSPI_MCR0_IP_TIMEOUT(0xFF),
- base + FSPI_MCR0);
+ fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
+ FSPI_MCR0_IP_TIMEOUT(0xFF) | (u32) FSPI_MCR0_OCTCOMB_EN,
+ base + FSPI_MCR0);
/*
* Disable same device enable bit and configure all slave devices
@@ -977,9 +1060,8 @@ static int nxp_fspi_probe(struct platform_device *pdev)
/* find the resources - controller memory mapped space */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fspi_mmap");
- f->ahb_addr = devm_ioremap_resource(dev, res);
- if (IS_ERR(f->ahb_addr)) {
- ret = PTR_ERR(f->ahb_addr);
+ if (IS_ERR(res)) {
+ ret = PTR_ERR(res);
goto err_put_ctrl;
}
@@ -1000,9 +1082,14 @@ static int nxp_fspi_probe(struct platform_device *pdev)
goto err_put_ctrl;
}
- ret = nxp_fspi_clk_prep_enable(f);
- if (ret) {
- dev_err(dev, "can not enable the clock\n");
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, FSPI_RPM_TIMEOUT);
+ pm_runtime_use_autosuspend(dev);
+
+ /* enable clock */
+ ret = pm_runtime_get_sync(f->dev);
+ if (ret < 0) {
+ dev_err(f->dev, "Failed to enable clock %d\n", __LINE__);
goto err_put_ctrl;
}
@@ -1037,13 +1124,19 @@ static int nxp_fspi_probe(struct platform_device *pdev)
if (ret)
goto err_destroy_mutex;
+ pm_runtime_mark_last_busy(f->dev);
+ pm_runtime_put_autosuspend(f->dev);
+
+ /* indicate the controller has been initialized */
+ f->flags |= FSPI_INITILIZED;
+
return 0;
err_destroy_mutex:
mutex_destroy(&f->lock);
err_disable_clk:
- nxp_fspi_clk_disable_unprep(f);
+ pm_runtime_disable(dev);
err_put_ctrl:
spi_controller_put(ctlr);
@@ -1063,34 +1156,65 @@ static int nxp_fspi_remove(struct platform_device *pdev)
mutex_destroy(&f->lock);
+ if (f->ahb_addr)
+ iounmap(f->ahb_addr);
+
return 0;
}
-static int nxp_fspi_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int nxp_fspi_initialized(struct nxp_fspi *f)
+{
+ return f->flags & FSPI_INITILIZED;
+}
+
+static int nxp_fspi_need_reinit(struct nxp_fspi *f)
{
+ /* we always use the controller in combination mode, so we check this */
+ /* register bit to determine if the controller once lost power, such as */
+ /* suspend/resume, and need to be re-init */
+
+ return !(readl(f->iobase + FSPI_MCR0) & FSPI_MCR0_OCTCOMB_EN);
+}
+
+
+int nxp_fspi_runtime_suspend(struct device *dev)
+{
+ struct nxp_fspi *f = dev_get_drvdata(dev);
+
+ nxp_fspi_clk_disable_unprep(f);
+
return 0;
}
-static int nxp_fspi_resume(struct device *dev)
+int nxp_fspi_runtime_resume(struct device *dev)
{
struct nxp_fspi *f = dev_get_drvdata(dev);
- nxp_fspi_default_setup(f);
+ nxp_fspi_clk_prep_enable(f);
+
+ if (nxp_fspi_initialized(f) && nxp_fspi_need_reinit(f))
+ nxp_fspi_default_setup(f);
return 0;
}
+static const struct dev_pm_ops nxp_fspi_pm_ops = {
+ SET_RUNTIME_PM_OPS(nxp_fspi_runtime_suspend, nxp_fspi_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+#endif /* CONFIG_PM */
+
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,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
+ { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
-static const struct dev_pm_ops nxp_fspi_pm_ops = {
- .suspend = nxp_fspi_suspend,
- .resume = nxp_fspi_resume,
-};
-
static struct platform_driver nxp_fspi_driver = {
.driver = {
.name = "nxp-fspi",