summaryrefslogtreecommitdiff
path: root/drivers/mtd/spi-nor/fsl-quadspi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/spi-nor/fsl-quadspi.c')
-rw-r--r--drivers/mtd/spi-nor/fsl-quadspi.c329
1 files changed, 286 insertions, 43 deletions
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 5c82e4ef1904..640eb1010dfc 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -1,7 +1,8 @@
/*
* Freescale QuadSPI driver.
*
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,9 +42,14 @@
#define QUADSPI_QUIRK_TKT253890 (1 << 2)
/* Controller cannot wake up from wait mode, TKT245618 */
#define QUADSPI_QUIRK_TKT245618 (1 << 3)
+/* Need low level code to control the clock */
+#define QUADSPI_QUIRK_LL_CLK (1 << 4)
/* The registers */
#define QUADSPI_MCR 0x00
+#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT 29
+#define MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK \
+ (1 << MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_SHIFT)
#define QUADSPI_MCR_RESERVED_SHIFT 16
#define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_SHIFT)
#define QUADSPI_MCR_MDIS_SHIFT 14
@@ -61,6 +67,11 @@
#define QUADSPI_MCR_SWRSTSD_SHIFT 0
#define QUADSPI_MCR_SWRSTSD_MASK (1 << QUADSPI_MCR_SWRSTSD_SHIFT)
+#define QUADSPI_FLSHCR 0x0c
+#define QUADSPI_FLSHCR_TDH_SHIFT 16
+#define QUADSPI_FLSHCR_TDH_MASK (3 << QUADSPI_FLSHCR_TDH_SHIFT)
+#define QUADSPI_FLSHCR_TDH_DDR_EN (1 << QUADSPI_FLSHCR_TDH_SHIFT)
+
#define QUADSPI_IPCR 0x08
#define QUADSPI_IPCR_SEQID_SHIFT 24
#define QUADSPI_IPCR_SEQID_MASK (0xF << QUADSPI_IPCR_SEQID_SHIFT)
@@ -205,15 +216,49 @@
#define SEQID_RDCR 9
#define SEQID_EN4B 10
#define SEQID_BRWR 11
+#define SEQID_RD_EVCR 12
+#define SEQID_WD_EVCR 13
+
+/* last two lut slots for dynamic luts*/
+#define SEQID_DYNAMIC_CMD0 14
+#define SEQID_DYNAMIC_CMD1 15
+
+#define QUADSPI_MIN_IOMAP SZ_4M
-#define QUADSPI_MIN_IOMAP SZ_4M
+/* dynamic lut configs */
+#define MAX_LUT_REGS 4
+struct lut_desc {
+ u8 cmd;
+ u32 lut[MAX_LUT_REGS];
+};
+/*
+ * define two lut_des in the struct because many commands use in pairs.
+ * To add a single command, just leave the second desc as blank.
+ */
+struct lut_desc_pair {
+ struct lut_desc lut_desc0;
+ struct lut_desc lut_desc1;
+};
+
+struct lut_desc_pair current_lut_pair;
+
+static const struct lut_desc_pair dynamic_lut_table[] = {
+ /* VCR RD/WR pair */
+ { {SPINOR_OP_RD_VCR, {LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR) |
+ LUT1(FSL_READ, PAD1, 0x1)} },
+ {SPINOR_OP_WR_VCR, {LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR) |
+ LUT1(FSL_WRITE, PAD1, 0x1)} },
+ },
+ {/* sentinel */},
+};
enum fsl_qspi_devtype {
FSL_QUADSPI_VYBRID,
FSL_QUADSPI_IMX6SX,
FSL_QUADSPI_IMX7D,
FSL_QUADSPI_IMX6UL,
FSL_QUADSPI_LS1021A,
+ FSL_QUADSPI_IMX7ULP,
};
struct fsl_qspi_devtype_data {
@@ -267,6 +312,15 @@ static struct fsl_qspi_devtype_data ls1021a_data = {
.driver_data = 0,
};
+static struct fsl_qspi_devtype_data imx7ulp_data = {
+ .devtype = FSL_QUADSPI_IMX7ULP,
+ .rxfifo = 64,
+ .txfifo = 64,
+ .ahb_buf_size = 128,
+ .driver_data = QUADSPI_QUIRK_LL_CLK
+ | QUADSPI_QUIRK_TKT253890
+};
+
#define FSL_QSPI_MAX_CHIP 4
struct fsl_qspi {
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
@@ -285,6 +339,7 @@ struct fsl_qspi {
unsigned int chip_base_addr; /* We may support two chips. */
bool has_second_chip;
bool big_endian;
+ u32 ddr_smp;
struct mutex lock;
struct pm_qos_request pm_qos_req;
};
@@ -309,6 +364,11 @@ static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618;
}
+static inline int needs_ll_handle_clock(struct fsl_qspi *q)
+{
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_LL_CLK;
+}
+
/*
* R/W functions for big- or little-endian registers:
* The qSPI controller's endian is independent of the CPU core's endian.
@@ -372,8 +432,10 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
int rxfifo = q->devtype_data->rxfifo;
+ struct spi_nor *nor = &q->nor[0];
+ u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
u32 lut_base;
- u8 cmd, addrlen, dummy;
+ u8 op, dm;
int i;
fsl_qspi_unlock_lut(q);
@@ -382,24 +444,57 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
for (i = 0; i < QUADSPI_LUT_NUM; i++)
qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
- /* Quad Read */
+ /* Quad Read and DDR Quad Read*/
lut_base = SEQID_QUAD_READ * 4;
-
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_READ_1_1_4;
- addrlen = ADDR24BIT;
- dummy = 8;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_READ_1_1_4;
- addrlen = ADDR32BIT;
- dummy = 8;
- }
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ op = nor->read_opcode;
+ dm = nor->read_dummy;
+ if (nor->flash_read == SPI_NOR_QUAD) {
+ if (op == SPINOR_OP_READ_1_1_4 || op == SPINOR_OP_READ4_1_1_4) {
+ /* read mode : 1-1-4 */
+ qspi_writel(q, LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(DUMMY, PAD1, dm) | LUT1(FSL_READ, PAD4, rxfifo),
+ base + QUADSPI_LUT(lut_base + 1));
+ } else {
+ dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op);
+ }
+ } else if (nor->flash_read == SPI_NOR_NORMAL) {
+ writel(LUT0(CMD, PAD1, op) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base));
- qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
+ writel(LUT0(FSL_READ, PAD1, rxfifo) | LUT1(JMP_ON_CS, PAD1, 0),
base + QUADSPI_LUT(lut_base + 1));
+ } else if (nor->flash_read == SPI_NOR_DDR_QUAD) {
+ if (op == SPINOR_OP_READ_1_4_4_D ||
+ op == SPINOR_OP_READ4_1_4_4_D) {
+ /* read mode : 1-4-4, such as Spansion s25fl128s. */
+ qspi_writel(q, LUT0(CMD, PAD1, op)
+ | LUT1(ADDR_DDR, PAD4, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(MODE_DDR, PAD4, 0xff)
+ | LUT1(DUMMY, PAD1, dm),
+ base + QUADSPI_LUT(lut_base + 1));
+
+ qspi_writel(q, LUT0(FSL_READ_DDR, PAD4, rxfifo)
+ | LUT1(JMP_ON_CS, PAD1, 0),
+ base + QUADSPI_LUT(lut_base + 2));
+ } else if (op == SPINOR_OP_READ_1_1_4_D) {
+ /* read mode : 1-1-4, such as Micron N25Q256A. */
+ qspi_writel(q, LUT0(CMD, PAD1, op)
+ | LUT1(ADDR_DDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
+
+ qspi_writel(q, LUT0(DUMMY, PAD1, dm)
+ | LUT1(FSL_READ_DDR, PAD4, rxfifo),
+ base + QUADSPI_LUT(lut_base + 1));
+
+ qspi_writel(q, LUT0(JMP_ON_CS, PAD1, 0),
+ base + QUADSPI_LUT(lut_base + 2));
+ } else {
+ dev_err(nor->dev, "Unsupported opcode : 0x%.2x\n", op);
+ }
+ }
/* Write enable */
lut_base = SEQID_WREN * 4;
@@ -408,18 +503,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Page Program */
lut_base = SEQID_PP * 4;
-
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_PP;
- addrlen = ADDR24BIT;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_PP;
- addrlen = ADDR32BIT;
- }
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
- base + QUADSPI_LUT(lut_base));
+ qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
base + QUADSPI_LUT(lut_base + 1));
@@ -431,12 +516,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Erase a sector */
lut_base = SEQID_SE * 4;
-
- cmd = q->nor[0].erase_opcode;
- addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
-
- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
- base + QUADSPI_LUT(lut_base));
+ qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) | LUT1(ADDR, PAD1, addrlen),
+ base + QUADSPI_LUT(lut_base));
/* Erase the whole chip */
lut_base = SEQID_CHIP_ERASE * 4;
@@ -476,14 +557,85 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
qspi_writel(q, LUT0(CMD, PAD1, SPINOR_OP_BRWR),
base + QUADSPI_LUT(lut_base));
+ /* Read EVCR register */
+ lut_base = SEQID_RD_EVCR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR), base + QUADSPI_LUT(lut_base));
+
+ /* Write EVCR register */
+ lut_base = SEQID_WD_EVCR * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR), base + QUADSPI_LUT(lut_base));
fsl_qspi_lock_lut(q);
}
+static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q);
+static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q);
+
+static int fsl_qspi_update_dynamic_lut(struct fsl_qspi *q, int index)
+{
+ void __iomem *base = q->iobase;
+ u32 lut_base;
+ int i;
+ int size;
+
+ fsl_qspi_unlock_lut(q);
+
+ lut_base = SEQID_DYNAMIC_CMD0 * 4;
+ size = ARRAY_SIZE(dynamic_lut_table[index].lut_desc0.lut);
+ for (i = 0; i < size; i++) {
+ writel(dynamic_lut_table[index].lut_desc0.lut[i],
+ base + QUADSPI_LUT(lut_base + i));
+ }
+
+ lut_base = SEQID_DYNAMIC_CMD1 * 4;
+ size = ARRAY_SIZE(dynamic_lut_table[index].lut_desc1.lut);
+ for (i = 0; i < size; i++) {
+ writel(dynamic_lut_table[index].lut_desc1.lut[i],
+ base + QUADSPI_LUT(lut_base + i));
+ }
+
+ fsl_qspi_lock_lut(q);
+
+ return 0;
+}
+
+static int fsl_qspi_search_dynamic_lut(struct fsl_qspi *q, u8 cmd)
+{
+ int i;
+ int ret = 0;
+
+ if (cmd == current_lut_pair.lut_desc0.cmd)
+ return SEQID_DYNAMIC_CMD0;
+ if (cmd == current_lut_pair.lut_desc1.cmd)
+ return SEQID_DYNAMIC_CMD1;
+ for (i = 0; i < ARRAY_SIZE(dynamic_lut_table); i++) {
+ if (cmd == dynamic_lut_table[i].lut_desc0.cmd)
+ ret = SEQID_DYNAMIC_CMD0;
+ if (cmd == dynamic_lut_table[i].lut_desc1.cmd)
+ ret = SEQID_DYNAMIC_CMD1;
+ if (ret) {
+ if (fsl_qspi_update_dynamic_lut(q, i)) {
+ pr_err(" failed to update dynamic lut\n");
+ return 0;
+ }
+ current_lut_pair = dynamic_lut_table[i];
+ return ret;
+ }
+ }
+ return ret;
+}
+
/* Get the SEQID for the command */
static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
{
+ int ret;
+
switch (cmd) {
+ case SPINOR_OP_READ_1_1_4_D:
+ case SPINOR_OP_READ_1_4_4_D:
+ case SPINOR_OP_READ4_1_4_4_D:
+ case SPINOR_OP_READ4_1_1_4:
case SPINOR_OP_READ_1_1_4:
+ case SPINOR_OP_READ:
return SEQID_QUAD_READ;
case SPINOR_OP_WREN:
return SEQID_WREN;
@@ -491,6 +643,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
return SEQID_WRDI;
case SPINOR_OP_RDSR:
return SEQID_RDSR;
+ case SPINOR_OP_BE_4K:
case SPINOR_OP_SE:
return SEQID_SE;
case SPINOR_OP_CHIP_ERASE:
@@ -507,9 +660,16 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
return SEQID_EN4B;
case SPINOR_OP_BRWR:
return SEQID_BRWR;
+ case SPINOR_OP_RD_EVCR:
+ return SEQID_RD_EVCR;
+ case SPINOR_OP_WD_EVCR:
+ return SEQID_WD_EVCR;
default:
if (cmd == q->nor[0].erase_opcode)
return SEQID_SE;
+ ret = fsl_qspi_search_dynamic_lut(q, cmd);
+ if (ret)
+ return ret;
dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
break;
}
@@ -680,6 +840,8 @@ static void fsl_qspi_set_map_addr(struct fsl_qspi *q)
static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
+ struct spi_nor *nor = &q->nor[0];
+ u32 reg, reg2;
int seqid;
/* AHB configuration for access buffer 0/1/2 .*/
@@ -701,9 +863,40 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
qspi_writel(q, 0, base + QUADSPI_BUF2IND);
/* Set the default lut sequence for AHB Read. */
- seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode);
+ seqid = fsl_qspi_get_seqid(q, nor->read_opcode);
qspi_writel(q, seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
q->iobase + QUADSPI_BFGENCR);
+
+ /* enable the DDR quad read */
+ if (nor->flash_read == SPI_NOR_DDR_QUAD) {
+ reg = readl(q->iobase + QUADSPI_MCR);
+
+ /* Firstly, disable the module */
+ qspi_writel(q, reg | QUADSPI_MCR_MDIS_MASK,
+ q->iobase + QUADSPI_MCR);
+
+ /* Set the Sampling Register for DDR */
+ reg2 = readl(q->iobase + QUADSPI_SMPR);
+ reg2 &= ~QUADSPI_SMPR_DDRSMP_MASK;
+ reg2 |= ((q->ddr_smp << QUADSPI_SMPR_DDRSMP_SHIFT) &
+ QUADSPI_SMPR_DDRSMP_MASK);
+ qspi_writel(q, reg2, q->iobase + QUADSPI_SMPR);
+
+ /* Enable the module again (enable the DDR too) */
+ reg |= QUADSPI_MCR_DDR_EN_MASK;
+ if (q->devtype_data->devtype == FSL_QUADSPI_IMX6SX)
+ reg |= MX6SX_QUADSPI_MCR_TX_DDR_DELAY_EN_MASK;
+
+ qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+
+ if ((q->devtype_data->devtype == FSL_QUADSPI_IMX6UL) ||
+ (q->devtype_data->devtype == FSL_QUADSPI_IMX7D)) {
+ reg = readl(q->iobase + QUADSPI_FLSHCR);
+ reg &= ~QUADSPI_FLSHCR_TDH_MASK;
+ reg |= QUADSPI_FLSHCR_TDH_DDR_EN;
+ qspi_writel(q, reg, q->iobase + QUADSPI_FLSHCR);
+ }
+ }
}
/* This function was used to prepare and enable QSPI clock */
@@ -750,13 +943,21 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
/* the default frequency, we will change it in the future. */
ret = clk_set_rate(q->clk, 66000000);
- if (ret)
+ if (ret && !needs_ll_handle_clock(q))
return ret;
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return ret;
+ if ((q->devtype_data->devtype == FSL_QUADSPI_IMX6UL) ||
+ (q->devtype_data->devtype == FSL_QUADSPI_IMX7D)) {
+ /* clear the DDR_EN bit for 6UL and 7D */
+ reg = readl(base + QUADSPI_MCR);
+ writel(~(QUADSPI_MCR_DDR_EN_MASK) & reg, base + QUADSPI_MCR);
+ udelay(1);
+ }
+
/* Reset the module */
qspi_writel(q, QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
base + QUADSPI_MCR);
@@ -800,7 +1001,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
fsl_qspi_clk_disable_unprep(q);
ret = clk_set_rate(q->clk, rate);
- if (ret)
+ if (ret && !needs_ll_handle_clock(q))
return ret;
ret = fsl_qspi_clk_prep_enable(q);
@@ -809,6 +1010,7 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
/* Init the LUT table again. */
fsl_qspi_init_lut(q);
+ fsl_qspi_update_dynamic_lut(q, 0);
/* Init for AHB read */
fsl_qspi_init_abh_read(q);
@@ -822,6 +1024,8 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, },
{ .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
{ .compatible = "fsl,ls1021a-qspi", .data = (void *)&ls1021a_data, },
+ { .compatible = "fsl,imx6ull-qspi", .data = (void *)&imx6ul_data, },
+ { .compatible = "fsl,imx7ulp-qspi", .data = (void *)&imx7ulp_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
@@ -887,6 +1091,7 @@ static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
{
struct fsl_qspi *q = nor->priv;
u8 cmd = nor->read_opcode;
+ int i, j;
/* if necessary,ioremap buffer before AHB read, */
if (!q->ahb_addr) {
@@ -921,9 +1126,20 @@ static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
len);
- /* Read out the data directly from the AHB buffer.*/
- memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
- len);
+ /* For non-8-byte alignment cases */
+ if (from % 8) {
+ j = 8 - (from & 0x7);
+ for (i = 0; i < j; ++i) {
+ memcpy(buf + i, q->ahb_addr + q->chip_base_addr + from
+ - q->memmap_offs + i, 1);
+ }
+ memcpy(buf + j, q->ahb_addr + q->chip_base_addr + from
+ - q->memmap_offs + j, len - j);
+ } else {
+ /* Read out the data directly from the AHB buffer.*/
+ memcpy(buf, q->ahb_addr + q->chip_base_addr + from
+ - q->memmap_offs, len);
+ }
return len;
}
@@ -997,6 +1213,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
/* find the resources */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
+ if (!res) {
+ dev_err(dev, "QuadSPI get resource IORESOURCE_MEM failed\n");
+ return -ENODEV;
+ }
q->iobase = devm_ioremap_resource(dev, res);
if (IS_ERR(q->iobase))
return PTR_ERR(q->iobase);
@@ -1004,6 +1224,11 @@ static int fsl_qspi_probe(struct platform_device *pdev)
q->big_endian = of_property_read_bool(np, "big-endian");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"QuadSPI-memory");
+ if (!res) {
+ dev_err(dev,
+ "QuadSPI-memory get resource IORESOURCE_MEM failed\n");
+ return -ENODEV;
+ }
if (!devm_request_mem_region(dev, res->start, resource_size(res),
res->name)) {
dev_err(dev, "can't request region for resource %pR\n", res);
@@ -1021,6 +1246,12 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (IS_ERR(q->clk))
return PTR_ERR(q->clk);
+ /* find ddrsmp value */
+ ret = of_property_read_u32(dev->of_node, "ddrsmp",
+ &q->ddr_smp);
+ if (ret)
+ q->ddr_smp = 0;
+
ret = fsl_qspi_clk_prep_enable(q);
if (ret) {
dev_err(dev, "can not enable the clock\n");
@@ -1052,6 +1283,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
/* iterate the subnodes. */
for_each_available_child_of_node(dev->of_node, np) {
+ enum read_mode mode = SPI_NOR_QUAD;
+ u32 dummy = 0;
+
/* skip the holes */
if (!q->has_second_chip)
i *= 2;
@@ -1078,10 +1312,16 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret < 0)
goto mutex_failed;
+ /* Can we enable the DDR Quad Read? */
+ ret = of_property_read_u32(np, "spi-nor,ddr-quad-read-dummy",
+ &dummy);
+ if (!ret && dummy > 0)
+ mode = SPI_NOR_DDR_QUAD;
+
/* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor);
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, mode);
if (ret)
goto mutex_failed;
@@ -1162,6 +1402,7 @@ static int fsl_qspi_remove(struct platform_device *pdev)
static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state)
{
+ pinctrl_pm_select_sleep_state(&pdev->dev);
return 0;
}
@@ -1170,6 +1411,8 @@ static int fsl_qspi_resume(struct platform_device *pdev)
int ret;
struct fsl_qspi *q = platform_get_drvdata(pdev);
+ pinctrl_pm_select_default_state(&pdev->dev);
+
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return ret;