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.c499
1 files changed, 355 insertions, 144 deletions
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index 1c1991a26c15..50a7e4916a60 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -47,8 +47,9 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <linux/regmap.h>
#include <linux/sizes.h>
@@ -58,12 +59,8 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
-/*
- * 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 (31).
- */
-#define SEQID_LUT 31
+/* runtime pm timeout */
+#define FSPI_RPM_TIMEOUT 50 /* 50ms */
/* Registers used by the driver */
#define FSPI_MCR0 0x00
@@ -214,9 +211,15 @@
#define FSPI_DLLACR 0xC0
#define FSPI_DLLACR_OVRDEN BIT(8)
+#define FSPI_DLLACR_SLVDLY(x) ((x) << 3)
+#define FSPI_DLLACR_DLLRESET BIT(1)
+#define FSPI_DLLACR_DLLEN BIT(0)
#define FSPI_DLLBCR 0xC4
#define FSPI_DLLBCR_OVRDEN BIT(8)
+#define FSPI_DLLBCR_SLVDLY(x) ((x) << 3)
+#define FSPI_DLLBCR_DLLRESET BIT(1)
+#define FSPI_DLLBCR_DLLEN BIT(0)
#define FSPI_STS0 0xE0
#define FSPI_STS0_DLPHB(x) ((x) << 8)
@@ -231,6 +234,16 @@
#define FSPI_STS1_AHB_ERRCD(x) ((x) << 8)
#define FSPI_STS1_AHB_ERRID(x) (x)
+#define FSPI_STS2 0xE8
+#define FSPI_STS2_BREFLOCK BIT(17)
+#define FSPI_STS2_BSLVLOCK BIT(16)
+#define FSPI_STS2_AREFLOCK BIT(1)
+#define FSPI_STS2_ASLVLOCK BIT(0)
+#define FSPI_STS2_AB_LOCK (FSPI_STS2_BREFLOCK | \
+ FSPI_STS2_BSLVLOCK | \
+ FSPI_STS2_AREFLOCK | \
+ FSPI_STS2_ASLVLOCK)
+
#define FSPI_AHBSPNST 0xEC
#define FSPI_AHBSPNST_DATLFT(x) ((x) << 16)
#define FSPI_AHBSPNST_BUFID(x) ((x) << 1)
@@ -248,9 +261,6 @@
#define FSPI_TFDR 0x180
#define FSPI_LUT_BASE 0x200
-#define FSPI_LUT_OFFSET (SEQID_LUT * 4 * 4)
-#define FSPI_LUT_REG(idx) \
- (FSPI_LUT_BASE + FSPI_LUT_OFFSET + (idx) * 4)
/* register map end */
@@ -320,12 +330,15 @@
/* Access flash memory using IP bus only */
#define FSPI_QUIRK_USE_IP_ONLY BIT(0)
+/* Disable DTR */
+#define FSPI_QUIRK_DISABLE_DTR BIT(1)
struct nxp_fspi_devtype_data {
unsigned int rxfifo;
unsigned int txfifo;
unsigned int ahb_buf_size;
unsigned int quirks;
+ unsigned int lut_num;
bool little_endian;
};
@@ -333,7 +346,8 @@ 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) */
- .quirks = 0,
+ .quirks = FSPI_QUIRK_DISABLE_DTR,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -342,6 +356,7 @@ static struct nxp_fspi_devtype_data imx8mm_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = 0,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -350,6 +365,7 @@ static struct nxp_fspi_devtype_data imx8qxp_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = 0,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -358,6 +374,16 @@ static struct nxp_fspi_devtype_data imx8dxl_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = FSPI_QUIRK_USE_IP_ONLY,
+ .lut_num = 32,
+ .little_endian = true, /* little-endian */
+};
+
+static struct nxp_fspi_devtype_data imx8ulp_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .lut_num = 16,
.little_endian = true, /* little-endian */
};
@@ -375,6 +401,13 @@ struct nxp_fspi {
struct mutex lock;
struct pm_qos_request pm_qos_req;
int selected;
+#define FSPI_NEED_INIT BIT(0)
+#define FSPI_DTR_MODE BIT(1)
+ int flags;
+ /* save the previous operation clock rate */
+ unsigned long pre_op_rate;
+ /* the max clock rate fspi output to device */
+ unsigned long max_rate;
};
static inline int needs_ip_only(struct nxp_fspi *f)
@@ -436,7 +469,7 @@ static int nxp_fspi_check_buswidth(struct nxp_fspi *f, u8 width)
static bool nxp_fspi_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
- struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->controller);
int ret;
ret = nxp_fspi_check_buswidth(f, op->cmd.buswidth);
@@ -505,7 +538,7 @@ static int fspi_readl_poll_tout(struct nxp_fspi *f, void __iomem *base,
}
/*
- * If the slave device content being changed by Write/Erase, need to
+ * 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.
*/
@@ -529,14 +562,25 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
void __iomem *base = f->iobase;
u32 lutval[4] = {};
int lutidx = 1, i;
+ u32 lut_offset = (f->devtype_data->lut_num - 1) * 4 * 4;
+ u32 target_lut_reg;
/* cmd */
- lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
- op->cmd.opcode);
+ if (op->cmd.dtr) {
+ lutval[0] |= LUT_DEF(0, LUT_CMD_DDR, LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode >> 8);
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_CMD_DDR,
+ LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode & 0xFF);
+ lutidx++;
+ } else {
+ lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
+ op->cmd.opcode);
+ }
/* addr bytes */
if (op->addr.nbytes) {
- lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_ADDR,
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, op->addr.dtr ? LUT_ADDR_DDR : LUT_ADDR,
LUT_PAD(op->addr.buswidth),
op->addr.nbytes * 8);
lutidx++;
@@ -544,7 +588,7 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
/* dummy bytes, if needed */
if (op->dummy.nbytes) {
- lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_DUMMY,
+ lutval[lutidx / 2] |= LUT_DEF(lutidx, op->dummy.dtr ? LUT_DUMMY_DDR : LUT_DUMMY,
/*
* Due to FlexSPI controller limitation number of PAD for dummy
* buswidth needs to be programmed as equal to data buswidth.
@@ -559,7 +603,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
if (op->data.nbytes) {
lutval[lutidx / 2] |= LUT_DEF(lutidx,
op->data.dir == SPI_MEM_DATA_IN ?
- LUT_NXP_READ : LUT_NXP_WRITE,
+ (op->data.dtr ? LUT_READ_DDR : LUT_NXP_READ) :
+ (op->data.dtr ? LUT_WRITE_DDR : LUT_NXP_WRITE),
LUT_PAD(op->data.buswidth),
0);
lutidx++;
@@ -573,10 +618,12 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
fspi_writel(f, FSPI_LCKER_UNLOCK, f->iobase + FSPI_LCKCR);
/* fill LUT */
- for (i = 0; i < ARRAY_SIZE(lutval); i++)
- fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
+ 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);
+ }
- dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n",
+ dev_dbg(f->dev, "CMD[%02x] lutval[0:%08x 1:%08x 2:%08x 3:%08x], size: 0x%08x\n",
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);
/* lock LUT */
@@ -604,20 +651,103 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f)
return 0;
}
-static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f)
+static void nxp_fspi_clk_disable_unprep(struct nxp_fspi *f)
{
if (is_acpi_node(dev_fwnode(f->dev)))
- return 0;
+ return;
clk_disable_unprepare(f->clk);
clk_disable_unprepare(f->clk_en);
- return 0;
+ return;
+}
+
+/*
+ * 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
+ */
+static void 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);
+}
+
+static void 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);
+}
+
+/*
+ * 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.
+ */
+static void nxp_fspi_dll_override(struct nxp_fspi *f)
+{
+ fspi_writel(f, FSPI_DLLACR_OVRDEN, f->iobase + FSPI_DLLACR);
+ fspi_writel(f, FSPI_DLLBCR_OVRDEN, f->iobase + FSPI_DLLBCR);
}
/*
* In FlexSPI controller, flash access is based on value of FSPI_FLSHXXCR0
- * register and start base address of the slave device.
+ * register and start base address of the target device.
*
* (Higher address)
* -------- <-- FLSHB2CR0
@@ -636,15 +766,15 @@ static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f)
*
*
* Start base address defines the starting address range for given CS and
- * FSPI_FLSHXXCR0 defines the size of the slave device connected at given CS.
+ * 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 slave device.
+ * of the connected target device.
* Assign controller memory mapped space size as the size to the connected
- * slave device.
+ * target device.
* Mark FLSHxxCR0 as zero initially and then assign value only to the selected
* chip-select Flash configuration register.
*
@@ -653,17 +783,27 @@ static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f)
* Value for rest of the CS FLSHxxCR0 register would be zero.
*
*/
-static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
+static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
+ const struct spi_mem_op *op)
{
- unsigned long rate = spi->max_speed_hz;
+ /* 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;
+ unsigned long rate = op->max_freq;
int ret;
uint64_t size_kb;
/*
- * Return, if previously selected slave device is same as current
- * requested slave device.
+ * 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->chip_select)
+ if ((f->selected == spi_get_chipselect(spi, 0)) &&
+ (!!(f->flags & FSPI_DTR_MODE) == op_is_dtr) &&
+ (f->pre_op_rate == op->max_freq))
return;
/* Reset FLSHxxCR0 registers */
@@ -676,9 +816,22 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
size_kb = FSPI_FLSHXCR0_SZ(f->memmap_phy_size);
fspi_writel(f, size_kb, f->iobase + FSPI_FLSHA1CR0 +
- 4 * spi->chip_select);
+ 4 * spi_get_chipselect(spi, 0));
+
+ dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0));
- dev_dbg(f->dev, "Slave device [CS:%x] selected\n", spi->chip_select);
+ nxp_fspi_select_rx_sample_clk_source(f, op_is_dtr);
+ rate = min(f->max_rate, op->max_freq);
+
+ 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);
@@ -690,7 +843,18 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
if (ret)
return;
- f->selected = spi->chip_select;
+ /*
+ * If clock rate > 100MHz, then switch from DLL override mode to
+ * DLL calibration mode.
+ */
+ if (rate > 100000000)
+ nxp_fspi_dll_calibration(f);
+ else
+ nxp_fspi_dll_override(f);
+
+ f->pre_op_rate = op->max_freq;
+
+ f->selected = spi_get_chipselect(spi, 0);
}
static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
@@ -705,10 +869,9 @@ static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
iounmap(f->ahb_addr);
f->memmap_start = start;
- f->memmap_len = len > NXP_FSPI_MIN_IOMAP ?
- len : NXP_FSPI_MIN_IOMAP;
+ f->memmap_len = max_t(u32, len, NXP_FSPI_MIN_IOMAP);
- f->ahb_addr = ioremap_wc(f->memmap_phy + f->memmap_start,
+ f->ahb_addr = ioremap(f->memmap_phy + f->memmap_start,
f->memmap_len);
if (!f->ahb_addr) {
@@ -754,14 +917,15 @@ static void nxp_fspi_fill_txfifo(struct nxp_fspi *f,
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(op->data.nbytes - i, 4); j += 4) {
- memcpy(&data, buf + i + j, 4);
+ 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);
@@ -824,7 +988,7 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
void __iomem *base = f->iobase;
int seqnum = 0;
int err = 0;
- u32 reg;
+ u32 reg, seqid_lut;
reg = fspi_readl(f, base + FSPI_IPRXFCR);
/* invalid RXFIFO first */
@@ -840,8 +1004,9 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
* 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) |
+ (seqid_lut << FSPI_IPCR1_SEQID_SHIFT) |
(seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
base + FSPI_IPCR1);
@@ -861,17 +1026,23 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
- struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->controller);
int err = 0;
- mutex_lock(&f->lock);
+ guard(mutex)(&f->lock);
+
+ 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);
+ nxp_fspi_select_mem(f, mem->spi, op);
nxp_fspi_prepare_lut(f, op);
/*
@@ -894,14 +1065,14 @@ 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);
- mutex_unlock(&f->lock);
+ pm_runtime_put_autosuspend(f->dev);
return err;
}
static int nxp_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
- struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->controller);
if (op->data.dir == SPI_MEM_DATA_OUT) {
if (op->data.nbytes > f->devtype_data->txfifo)
@@ -965,7 +1136,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
{
void __iomem *base = f->iobase;
int ret, i;
- u32 reg;
+ u32 reg, seqid_lut;
/* disable and unprepare clock to avoid glitch pass to controller */
nxp_fspi_clk_disable_unprep(f);
@@ -997,9 +1168,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
/* Disable the module */
fspi_writel(f, FSPI_MCR0_MDIS, base + FSPI_MCR0);
- /* Reset the DLL register to default value */
- fspi_writel(f, FSPI_DLLACR_OVRDEN, base + FSPI_DLLACR);
- fspi_writel(f, FSPI_DLLBCR_OVRDEN, base + FSPI_DLLBCR);
+ nxp_fspi_dll_override(f);
/* enable module */
fspi_writel(f, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
@@ -1007,7 +1176,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
base + FSPI_MCR0);
/*
- * Disable same device enable bit and configure all slave devices
+ * Disable same device enable bit and configure all target devices
* independently.
*/
reg = fspi_readl(f, f->iobase + FSPI_MCR2);
@@ -1029,11 +1198,24 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
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);
+ 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;
@@ -1045,7 +1227,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
static const char *nxp_fspi_get_name(struct spi_mem *mem)
{
- struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->master);
+ struct nxp_fspi *f = spi_controller_get_devdata(mem->spi->controller);
struct device *dev = &mem->spi->dev;
const char *name;
@@ -1055,7 +1237,7 @@ static const char *nxp_fspi_get_name(struct spi_mem *mem)
name = devm_kasprintf(dev, GFP_KERNEL,
"%s-%d", dev_name(f->dev),
- mem->spi->chip_select);
+ spi_get_chipselect(mem->spi, 0));
if (!name) {
dev_err(dev, "failed to get memory for custom flash name\n");
@@ -1072,17 +1254,46 @@ static const struct spi_controller_mem_ops nxp_fspi_mem_ops = {
.get_name = nxp_fspi_get_name,
};
+static const struct spi_controller_mem_caps nxp_fspi_mem_caps = {
+ .dtr = true,
+ .swap16 = false,
+ .per_op_freq = true,
+};
+
+static const struct spi_controller_mem_caps nxp_fspi_mem_caps_disable_dtr = {
+ .dtr = false,
+ .per_op_freq = true,
+};
+
+static void nxp_fspi_cleanup(void *data)
+{
+ struct nxp_fspi *f = data;
+
+ /* enable clock first since there is register access */
+ pm_runtime_get_sync(f->dev);
+
+ /* disable the hardware */
+ fspi_writel(f, FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0);
+
+ pm_runtime_disable(f->dev);
+ pm_runtime_put_noidle(f->dev);
+ nxp_fspi_clk_disable_unprep(f);
+
+ if (f->ahb_addr)
+ iounmap(f->ahb_addr);
+}
+
static int nxp_fspi_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr;
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
struct resource *res;
struct nxp_fspi *f;
- int ret;
+ int ret, irq;
u32 reg;
- ctlr = spi_alloc_master(&pdev->dev, sizeof(*f));
+ ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*f));
if (!ctlr)
return -ENOMEM;
@@ -1092,146 +1303,152 @@ static int nxp_fspi_probe(struct platform_device *pdev)
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) {
- ret = -ENODEV;
- goto err_put_ctrl;
- }
+ 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)))
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (is_acpi_node(fwnode))
+ f->iobase = devm_platform_ioremap_resource(pdev, 0);
else
- res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, "fspi_base");
-
- f->iobase = devm_ioremap_resource(dev, res);
- if (IS_ERR(f->iobase)) {
- ret = PTR_ERR(f->iobase);
- goto err_put_ctrl;
- }
+ 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)))
+ if (is_acpi_node(fwnode))
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
else
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "fspi_mmap");
-
- if (!res) {
- ret = -ENODEV;
- goto err_put_ctrl;
- }
+ if (!res)
+ return -ENODEV;
/* assign memory mapped starting address and mapped size. */
f->memmap_phy = res->start;
f->memmap_phy_size = resource_size(res);
/* find the clocks */
- if (dev_of_node(&pdev->dev)) {
+ if (is_of_node(fwnode)) {
f->clk_en = devm_clk_get(dev, "fspi_en");
- if (IS_ERR(f->clk_en)) {
- ret = PTR_ERR(f->clk_en);
- goto err_put_ctrl;
- }
+ if (IS_ERR(f->clk_en))
+ return PTR_ERR(f->clk_en);
f->clk = devm_clk_get(dev, "fspi");
- if (IS_ERR(f->clk)) {
- ret = PTR_ERR(f->clk);
- goto err_put_ctrl;
- }
-
- ret = nxp_fspi_clk_prep_enable(f);
- if (ret) {
- dev_err(dev, "can not enable the clock\n");
- goto err_put_ctrl;
- }
+ if (IS_ERR(f->clk))
+ return PTR_ERR(f->clk);
}
+ /* find the irq */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed to get irq source");
+
+ 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)
+ return dev_err_probe(dev, ret, "Failed to enable clock");
+
/* 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);
+ nxp_fspi_default_setup(f);
+
+ ret = pm_runtime_put_sync(dev);
if (ret < 0)
- goto err_disable_clk;
+ return dev_err_probe(dev, ret, "Failed to disable clock");
- ret = devm_request_irq(dev, ret,
+ ret = devm_request_irq(dev, irq,
nxp_fspi_irq_handler, 0, pdev->name, f);
- if (ret) {
- dev_err(dev, "failed to request irq: %d\n", ret);
- goto err_disable_clk;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request irq\n");
- mutex_init(&f->lock);
+ ret = devm_mutex_init(dev, &f->lock);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize lock\n");
ctlr->bus_num = -1;
ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT;
ctlr->mem_ops = &nxp_fspi_mem_ops;
- nxp_fspi_default_setup(f);
+ if (f->devtype_data->quirks & FSPI_QUIRK_DISABLE_DTR)
+ ctlr->mem_caps = &nxp_fspi_mem_caps_disable_dtr;
+ else
+ ctlr->mem_caps = &nxp_fspi_mem_caps;
- ctlr->dev.of_node = np;
+ device_set_node(&ctlr->dev, fwnode);
- ret = devm_spi_register_controller(&pdev->dev, ctlr);
+ ret = devm_add_action_or_reset(dev, nxp_fspi_cleanup, f);
if (ret)
- goto err_destroy_mutex;
+ return ret;
- return 0;
+ return devm_spi_register_controller(&pdev->dev, ctlr);
+}
-err_destroy_mutex:
- mutex_destroy(&f->lock);
+static int nxp_fspi_runtime_suspend(struct device *dev)
+{
+ struct nxp_fspi *f = dev_get_drvdata(dev);
-err_disable_clk:
nxp_fspi_clk_disable_unprep(f);
-err_put_ctrl:
- spi_controller_put(ctlr);
-
- dev_err(dev, "NXP FSPI probe failed\n");
- return ret;
+ return 0;
}
-static int nxp_fspi_remove(struct platform_device *pdev)
+static int nxp_fspi_runtime_resume(struct device *dev)
{
- struct nxp_fspi *f = platform_get_drvdata(pdev);
-
- /* disable the hardware */
- fspi_writel(f, FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0);
-
- nxp_fspi_clk_disable_unprep(f);
+ struct nxp_fspi *f = dev_get_drvdata(dev);
+ int ret;
- mutex_destroy(&f->lock);
+ ret = nxp_fspi_clk_prep_enable(f);
+ if (ret)
+ return ret;
- if (f->ahb_addr)
- iounmap(f->ahb_addr);
+ if (f->flags & FSPI_NEED_INIT) {
+ nxp_fspi_default_setup(f);
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ dev_err(dev, "select flexspi default pinctrl failed!\n");
+ f->flags &= ~FSPI_NEED_INIT;
+ }
- return 0;
+ return ret;
}
static int nxp_fspi_suspend(struct device *dev)
{
- return 0;
-}
-
-static int nxp_fspi_resume(struct device *dev)
-{
struct nxp_fspi *f = dev_get_drvdata(dev);
+ int ret;
- nxp_fspi_default_setup(f);
+ ret = pinctrl_pm_select_sleep_state(dev);
+ if (ret) {
+ dev_err(dev, "select flexspi sleep pinctrl failed!\n");
+ return ret;
+ }
- return 0;
+ f->flags |= FSPI_NEED_INIT;
+
+ return pm_runtime_force_suspend(dev);
}
+static const struct dev_pm_ops nxp_fspi_pm_ops = {
+ RUNTIME_PM_OPS(nxp_fspi_runtime_suspend, nxp_fspi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(nxp_fspi_suspend, pm_runtime_force_resume)
+};
+
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, },
+ { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
@@ -1244,20 +1461,14 @@ static const struct acpi_device_id nxp_fspi_acpi_ids[] = {
MODULE_DEVICE_TABLE(acpi, nxp_fspi_acpi_ids);
#endif
-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",
.of_match_table = nxp_fspi_dt_ids,
.acpi_match_table = ACPI_PTR(nxp_fspi_acpi_ids),
- .pm = &nxp_fspi_pm_ops,
+ .pm = pm_ptr(&nxp_fspi_pm_ops),
},
.probe = nxp_fspi_probe,
- .remove = nxp_fspi_remove,
};
module_platform_driver(nxp_fspi_driver);