IMX6UL-EVK U-Boot/Linux QSPI boot

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

IMX6UL-EVK U-Boot/Linux QSPI boot

5,151 Views
vitaliyavramenk
Contributor III

Hello,

I could face strange Linux behaviour when  U-boot  comes  from QSPI flash chip.

Linux kernel and ROOTFS are both on USB flash drive.

When  the board starts U-Boot  from micro-SD Linux boots fine from USB flash drive  and can recognize QSPI flash chip

I can see the following line in Linux log

[ 1.261435] fsl-quadspi 21e0000.qspi: n25q256a (32768 Kbytes)

When  the board starts U-Boot  from QSPI flash chip Linux boots fine from USB flash drive but can NOT recognize QSPI Flash chip

[    1.251058] fsl-quadspi 21e0000.qspi: unrecognized JEDEC id bytes: ff, ff, ff

[    1.258431] fsl-quadspi 21e0000.qspi: Freescale QuadSPI probe failed

[    1.265431] fsl-quadspi: probe of 21e0000.qspi failed with error -2

U-boot was downloaded to QSPI flash chip using mfgtool2-yocto-mx-evk-qspi-nor-n25q256a.vbs

Any help would be appreciated

Thanks in advance

Labels (1)
Tags (4)
5 Replies

1,874 Views
marcusfolkesson
Contributor I

Hello Vitaliy,

When you are booting from QSPI, the SPL and U-Boot will setup the QSPI controller in DDR mode which currently is not supported by the kernel.

The kernel can therefor not identify the chip (just reading ff:ff:ff in the JEDEC ID bytes).

This patch add DDR-support for QSPI to Linux 4.8.

However, this is for n25q512ax3, you need to add the SPI_NOR_DDR_QUAD_READ flag to your chip (n25q256a). In other words, you need to do the corresponding change for your chip:

- { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+ { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SPI_NOR_DDR_QUAD_READ |

Cheers,

Marcus Folkesson

--------------------------------------------------------------

From 01c70b110b9b956fbebab94aeb5c2d151c67cd57 Mon Sep 17 00:00:00 2001
From: Marcus Folkesson <marcus.folkesson@gmail.com>
Date: Thu, 29 Sep 2016 17:34:02 +0200
Subject: [PATCH] spi-nor: fsl-quadspi: add support for DDR

Signed-off-by: Marcus Folkesson <marcus.folkesson@gmail.com>
---
drivers/mtd/spi-nor/fsl-quadspi.c | 166 ++++++++++++++++++++++++++++++++++----
drivers/mtd/spi-nor/spi-nor.c | 69 +++++++++++++++-
include/linux/mtd/spi-nor.h | 4 +
3 files changed, 219 insertions(+), 20 deletions(-)

diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 5c82e4e..92c2926 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -34,6 +34,8 @@
#define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0)
/* Controller needs 4x internal clock */
#define QUADSPI_QUIRK_4X_INT_CLK (1 << 1)
+/* Controller needs DDR delay */
+#define QUADSPI_QUIRK_DDR_DELAY (1 << 2)
/*
* TKT253890, Controller needs driver to fill txfifo till 16 byte to
* trigger data transfer even though extern data will not transferred.
@@ -44,6 +46,9 @@

/* 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 +66,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)
@@ -166,6 +176,8 @@
#define LUT_FSL_WRITE_DDR 15
#define LUT_DATA_LEARN 16

+
+
/*
* The PAD definitions for LUT register.
*
@@ -205,6 +217,8 @@
#define SEQID_RDCR 9
#define SEQID_EN4B 10
#define SEQID_BRWR 11
+#define SEQID_RD_EVCR 12
+#define SEQID_WD_EVCR 13

#define QUADSPI_MIN_IOMAP SZ_4M

@@ -238,6 +252,7 @@ static struct fsl_qspi_devtype_data imx6sx_data = {
.txfifo = 512,
.ahb_buf_size = 1024,
.driver_data = QUADSPI_QUIRK_4X_INT_CLK
+ | QUADSPI_QUIRK_DDR_DELAY
| QUADSPI_QUIRK_TKT245618,
};

@@ -285,6 +300,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;
};
@@ -294,6 +310,11 @@ static inline int needs_swap_endian(struct fsl_qspi *q)
return q->devtype_data->driver_data & QUADSPI_QUIRK_SWAP_ENDIAN;
}

+static inline int needs_ddr_delay(struct fsl_qspi *q)
+{
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_DDR_DELAY;
+}
+
static inline int needs_4x_clock(struct fsl_qspi *q)
{
return q->devtype_data->driver_data & QUADSPI_QUIRK_4X_INT_CLK;
@@ -372,8 +393,11 @@ 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 cmd, dummy;
+ u8 op, dm;
int i;

fsl_qspi_unlock_lut(q);
@@ -384,22 +408,51 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)

/* 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;
+ 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_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);
+ }
}

- qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
- base + QUADSPI_LUT(lut_base));
- qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo),
- base + QUADSPI_LUT(lut_base + 1));

/* Write enable */
lut_base = SEQID_WREN * 4;
@@ -476,6 +529,13 @@ 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;
+ qspi_writel(q ,LUT0(CMD, PAD1, SPINOR_OP_RD_EVCR), base + QUADSPI_LUT(lut_base));
+
+ /* Write EVCR register */
+ lut_base = SEQID_WD_EVCR * 4;
+ qspi_writel(q ,LUT0(CMD, PAD1, SPINOR_OP_WD_EVCR), base + QUADSPI_LUT(lut_base));
fsl_qspi_lock_lut(q);
}

@@ -483,6 +543,10 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
{
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:
return SEQID_QUAD_READ;
case SPINOR_OP_WREN:
@@ -491,6 +555,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,6 +572,10 @@ 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;
@@ -681,6 +750,9 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
int seqid;
+ u32 reg;
+ u32 reg2;
+ struct spi_nor *nor = &q->nor[0];

/* AHB configuration for access buffer 0/1/2 .*/
qspi_writel(q, QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
@@ -704,8 +776,38 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
seqid = fsl_qspi_get_seqid(q, q->nor[0].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 = qspi_readl(q ,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 = qspi_readl(q ,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 (needs_ddr_delay(q) &&
+ (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 = qspi_readl(q ,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 */
static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q)
{
@@ -757,6 +859,14 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *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 = qspi_readl(q ,base + QUADSPI_MCR);
+ qspi_writel(q ,~(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);
@@ -979,7 +1089,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
struct resource *res;
struct spi_nor *nor;
struct mtd_info *mtd;
+ enum read_mode mode = SPI_NOR_QUAD;
int ret, i = 0;
+ u32 dummy = 0;

q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
if (!q)
@@ -1021,6 +1133,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 +1170,10 @@ 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;
+ char modalias[40];
+ u32 dummy = 0;
+
/* skip the holes */
if (!q->has_second_chip)
i *= 2;
@@ -1073,15 +1195,25 @@ static int fsl_qspi_probe(struct platform_device *pdev)
nor->prepare = fsl_qspi_prep;
nor->unprepare = fsl_qspi_unprep;

+ ret = of_modalias_node(np, modalias, sizeof(modalias));
+ if (ret < 0)
+ goto mutex_failed;
+
ret = of_property_read_u32(np, "spi-max-frequency",
&q->clk_rate);
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;

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d0fc165..ebb85c4 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -75,6 +75,7 @@ struct flash_info {
* bit. Must be used with
* SPI_NOR_HAS_LOCK.
*/
+#define SPI_NOR_DDR_QUAD_READ 0x100 /* Flash supports DDR Quad Read */
};

#define JEDEC_MFR(info) ((info)->id[0])
@@ -146,6 +147,17 @@ static int read_cr(struct spi_nor *nor)
static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
{
switch (nor->flash_read) {
+ case SPI_NOR_DDR_QUAD:
+ /*
+ * The m25p80.c can not support the DDR quad read.
+ * We set the dummy cycles to 8 by default. The SPI NOR
+ * controller driver can set it in its child DT node.
+ * We parse it out here.
+ */
+ /* XXX: Since no reference to an of node is in struct spi_nor
+ * anymore, we simply return what spi-nor,ddr-quad-read-dummy
+ * was suppost to be */
+ return 6;
case SPI_NOR_FAST:
case SPI_NOR_DUAL:
case SPI_NOR_QUAD:
@@ -885,7 +897,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
- { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+ { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SPI_NOR_DDR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
{ "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
{ "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },

@@ -1264,6 +1276,34 @@ static int spansion_quad_enable(struct spi_nor *nor)

return 0;
}
+static int set_ddr_quad_mode(struct spi_nor *nor, struct flash_info *info)
+{
+ int status;
+
+ switch (JEDEC_MFR(info)) {
+ case CFI_MFR_AMD: /* Spansion, actually */
+ status = spansion_quad_enable(nor);
+ if (status) {
+ dev_err(nor->dev,
+ "Spansion DDR quad-read not enabled\n");
+ return status;
+ }
+ return status;
+ case CFI_MFR_MACRONIX:
+ status = macronix_quad_enable(nor);
+ if (status) {
+ dev_err(nor->dev,
+ "Macronix DDR quad-read not enabled\n");
+ return status;
+ }
+ return status;
+ case CFI_MFR_ST: /* Micron, actually */
+ /* DTR quad read works with the Extended SPI protocol. */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}

static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
{
@@ -1433,8 +1473,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (info->flags & SPI_NOR_NO_FR)
nor->flash_read = SPI_NOR_NORMAL;

- /* Quad/Dual-read mode takes precedence over fast/normal */
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
+ /* DDR Quad/Quad/Dual-read mode takes precedence over fast/normal */
+ if (mode == SPI_NOR_DDR_QUAD && info->flags & SPI_NOR_DDR_QUAD_READ) {
+ ret = set_ddr_quad_mode(nor, info);
+ if (ret) {
+ dev_err(dev, "DDR quad mode not supported\n");
+ return ret;
+ }
+ nor->flash_read = SPI_NOR_DDR_QUAD;
+ dev_err(dev, "mode == SPI_NOR_DDR_QUAD\n");
+ } else if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
ret = set_quad_mode(nor, info);
if (ret) {
dev_err(dev, "quad mode not supported\n");
@@ -1447,6 +1495,18 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)

/* Default commands */
switch (nor->flash_read) {
+ case SPI_NOR_DDR_QUAD:
+ if (JEDEC_MFR(info) == CFI_MFR_AMD) { /* Spansion */
+ nor->read_opcode = SPINOR_OP_READ_1_4_4_D;
+ } else if (JEDEC_MFR(info) == CFI_MFR_ST) {
+ nor->read_opcode = SPINOR_OP_READ_1_1_4_D;
+ } else if (JEDEC_MFR(info) == CFI_MFR_MACRONIX) {
+ nor->read_opcode = SPINOR_OP_READ_1_4_4_D;
+ } else {
+ dev_err(dev, "DDR Quad Read is not supported.\n");
+ return -EINVAL;
+ }
+ break;
case SPI_NOR_QUAD:
nor->read_opcode = SPINOR_OP_READ_1_1_4;
break;
@@ -1474,6 +1534,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
/* Dedicated 4-byte command set */
switch (nor->flash_read) {
+ case SPI_NOR_DDR_QUAD:
+ nor->read_opcode = SPINOR_OP_READ4_1_4_4_D;
+ break;
case SPI_NOR_QUAD:
nor->read_opcode = SPINOR_OP_READ4_1_1_4;
break;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index c425c7b..e55d6bd 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -45,6 +45,8 @@
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_1_4_D 0x6d /* Read data bytes (DDR Quad SPI) */
+#define SPINOR_OP_READ_1_4_4_D 0xed /* Read data bytes (DDR Quad SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
@@ -60,6 +62,7 @@
#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ4_1_4_4_D 0xee /* Read data bytes (DDR Quad SPI) */
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */

@@ -105,6 +108,7 @@ enum read_mode {
SPI_NOR_FAST,
SPI_NOR_DUAL,
SPI_NOR_QUAD,
+ SPI_NOR_DDR_QUAD,
};

#define SPI_NOR_MAX_CMD_SIZE 8
--
2.8.0

1,874 Views
igorpadykov
NXP Employee
NXP Employee

Hi Vitaly

if you think that official bsp has some issue, then it is

necessary to create new community thread and provide

detailed steps how this could be reproduced on nxp reference board

with demo image from i.MX6 product page :

i.MX 6 Series Software and Development Tool|NXP

Note, supported functionality is given in documentation provided with

BSP package: Release Notes, Linux Manual, e.t.c.

Best regards

igor

0 Kudos

1,874 Views
vitaliyavramenk
Contributor III

Hi Igor,

OK,  it is clear now.

By the way, I could see one minor issue even with

Linux 4.1.15 from  http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/log/?h=imx_4.1.15_1.0.0_ga

At Power ON  "eth1' interface always starts without problems.

But If I execute a 'reboot' command  then 'eth1' periodically does not start.

'ifconfig -a' shows 

eth1      Link encap:Ethernet  HWaddr 00:04:9F:04:20:01

          BROADCAST MULTICAST  MTU:1500  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

With kernels 4.6 and 4.6.3 I have not seen that issue.

0 Kudos

1,874 Views
vitaliyavramenk
Contributor III

The issue exists in Linux kernels 4.6 and 4.6.3 from www.kernel.org.

Everything works with Linux 4.1.15 from  http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/log/?h=imx_4.1.15_1.0.0_ga

0 Kudos

1,874 Views
igorpadykov
NXP Employee
NXP Employee

Hi Vitaly

kernels 4.6 and 4.6.3 are not officially supported, please look at i.MX6 product

page for current supported versions:

i.MX 6 Series Software and Development Tool|NXP

please post that on www.kernel.org mail list.

Best regards

igor

-----------------------------------------------------------------------------------------------------------------------

Note: If this post answers your question, please click the Correct Answer button. Thank you!

-----------------------------------------------------------------------------------------------------------------------