2015-07-08 20:21:59

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 0/7] fsl-quadspi: Allow additional device combinations.

This patch series fixes and extends the fsl-quadspi driver to allow for
non-contiguous and non-homogeneous SPI NOR device layouts. The current
driver assumes that attached devices are all of the same type, and are
populated "in order" beginning with QSPIn_A_CS0.

The updated driver and DT changes make use of the 'reg' property
for each flash to determine the interface and chip select to which each
device is attached.

Patchset was tested on a custom VF610-based board with a single SPI NOR
connected to QSPI0_B_CS0, and also on a Rev. H TWR board with dual SPI
NOR devices connected to QSPI0_A_CS0 and QSPI0_B_CS0. MTD tests passed
for all devices (readtest, speedtest, and stresstest), and independent
access to both devices was verified on the TWR system.

Regards,
-Cory


Changes since RFC:
- Rebased onto 4.2-rc1
- Moved the qspi1 node under aips1 where it belongs.


Cory Tusar (7):
ARM: dts: vf610: Add missing QuadSPI register mapping and names.
ARM: dts: vfxxx: Include support for qspi1 functionality.
mtd: fsl-quadspi: Support both 24- and 32-bit addressed commands.
mtd: fsl-quadspi: Use per-device clk_rate.
mtd: fsl-quadspi: Allow non-contiguous flash layouts.
mtd: spi-nor: Add support for Micron MT25QL02GC serial flash.
ARM: dts: vf610-twr: Enable QSPI and map flash devices.

.../devicetree/bindings/mtd/fsl-quadspi.txt | 15 +-
arch/arm/boot/dts/vf610-twr.dts | 39 +++
arch/arm/boot/dts/vfxxx.dtsi | 16 +-
drivers/mtd/spi-nor/fsl-quadspi.c | 313 +++++++++++----------
drivers/mtd/spi-nor/spi-nor.c | 1 +
5 files changed, 232 insertions(+), 152 deletions(-)

--
2.3.6


2015-07-08 20:23:42

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 1/7] ARM: dts: vf610: Add missing QuadSPI register mapping and names.

Both 'reg' and 'reg-names' are required properties according to binding
documentation, and both should contain two items.

Signed-off-by: Cory Tusar <[email protected]>
Acked-by: Stefan Agner <[email protected]>
---
arch/arm/boot/dts/vfxxx.dtsi | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/vfxxx.dtsi b/arch/arm/boot/dts/vfxxx.dtsi
index 4aa3351..089a263 100644
--- a/arch/arm/boot/dts/vfxxx.dtsi
+++ b/arch/arm/boot/dts/vfxxx.dtsi
@@ -242,7 +242,8 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,vf610-qspi";
- reg = <0x40044000 0x1000>;
+ reg = <0x40044000 0x1000>, <0x20000000 0x10000000>;
+ reg-names = "QuadSPI", "QuadSPI-memory";
interrupts = <24 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks VF610_CLK_QSPI0_EN>,
<&clks VF610_CLK_QSPI0>;
--
2.3.6

2015-07-08 20:22:37

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 2/7] ARM: dts: vfxxx: Include support for qspi1 functionality.

This commit extends the existing Vybrid QSPI devicetree implementation
to also describe the qspi1 functional block.

Signed-off-by: Cory Tusar <[email protected]>
Reviewed-by: Stefan Agner <[email protected]>
---
arch/arm/boot/dts/vfxxx.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/arch/arm/boot/dts/vfxxx.dtsi b/arch/arm/boot/dts/vfxxx.dtsi
index 089a263..9554f3d 100644
--- a/arch/arm/boot/dts/vfxxx.dtsi
+++ b/arch/arm/boot/dts/vfxxx.dtsi
@@ -489,6 +489,19 @@
status = "disabled";
};

+ qspi1: quadspi@400c4000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,vf610-qspi";
+ reg = <0x400c4000 0x1000>, <0x50000000 0x10000000>;
+ reg-names = "QuadSPI", "QuadSPI-memory";
+ interrupts = <25 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks VF610_CLK_QSPI1_EN>,
+ <&clks VF610_CLK_QSPI1>;
+ clock-names = "qspi_en", "qspi";
+ status = "disabled";
+ };
+
fec0: ethernet@400d0000 {
compatible = "fsl,mvf600-fec";
reg = <0x400d0000 0x1000>;
--
2.3.6

2015-07-08 20:22:14

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 3/7] mtd: fsl-quadspi: Support both 24- and 32-bit addressed commands.

The current fsl-quadspi implementation assumes that all connected
devices are of the same size and type. This commit adds lookup table
entries for both 24- and 32-bit addressed variants of the read, sector
erase, and page program operations as a precursor to later changes which
generalize the flash layout parsing logic and allow for non-contiguous
and non-homogeneous chip combinations.

Signed-off-by: Cory Tusar <[email protected]>
---
drivers/mtd/spi-nor/fsl-quadspi.c | 116 ++++++++++++++++++++------------------
1 file changed, 60 insertions(+), 56 deletions(-)

diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 52a872f..4b8038b 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -178,18 +178,21 @@
#define QUADSPI_LUT_NUM 64

/* SEQID -- we can have 16 seqids at most. */
-#define SEQID_QUAD_READ 0
-#define SEQID_WREN 1
-#define SEQID_WRDI 2
-#define SEQID_RDSR 3
-#define SEQID_SE 4
-#define SEQID_CHIP_ERASE 5
-#define SEQID_PP 6
-#define SEQID_RDID 7
-#define SEQID_WRSR 8
-#define SEQID_RDCR 9
-#define SEQID_EN4B 10
-#define SEQID_BRWR 11
+#define SEQID_QUAD_READ_24 0
+#define SEQID_QUAD_READ_32 1
+#define SEQID_WREN 2
+#define SEQID_WRDI 3
+#define SEQID_RDSR 4
+#define SEQID_SE_24 5
+#define SEQID_SE_32 5
+#define SEQID_CHIP_ERASE 7
+#define SEQID_PP_24 8
+#define SEQID_PP_32 8
+#define SEQID_RDID 9
+#define SEQID_WRSR 10
+#define SEQID_RDCR 11
+#define SEQID_EN4B 12
+#define SEQID_BRWR 13

enum fsl_qspi_devtype {
FSL_QUADSPI_VYBRID,
@@ -287,7 +290,6 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
void __iomem *base = q->iobase;
int rxfifo = q->devtype_data->rxfifo;
u32 lut_base;
- u8 cmd, addrlen, dummy;
int i;

fsl_qspi_unlock_lut(q);
@@ -297,22 +299,16 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
writel(0, base + QUADSPI_LUT_BASE + i * 4);

/* 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;
- }
+ lut_base = SEQID_QUAD_READ_24 * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_READ_1_1_4) | LUT1(ADDR, PAD1, ADDR24BIT),
+ base + QUADSPI_LUT(lut_base));
+ writel(LUT0(DUMMY, PAD1, 8) | LUT1(READ, PAD4, rxfifo),
+ base + QUADSPI_LUT(lut_base + 1));

- writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ lut_base = SEQID_QUAD_READ_32 * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_READ_1_1_4) | LUT1(ADDR, PAD1, ADDR32BIT),
base + QUADSPI_LUT(lut_base));
- writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo),
+ writel(LUT0(DUMMY, PAD1, 8) | LUT1(READ, PAD4, rxfifo),
base + QUADSPI_LUT(lut_base + 1));

/* Write enable */
@@ -320,18 +316,13 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base));

/* 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;
- }
+ lut_base = SEQID_PP_24 * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_PP) | LUT1(ADDR, PAD1, ADDR24BIT),
+ base + QUADSPI_LUT(lut_base));
+ writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));

- writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ lut_base = SEQID_PP_32 * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_PP) | LUT1(ADDR, PAD1, ADDR32BIT),
base + QUADSPI_LUT(lut_base));
writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));

@@ -341,18 +332,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
base + QUADSPI_LUT(lut_base));

/* Erase a sector */
- lut_base = SEQID_SE * 4;
-
- if (q->nor_size <= SZ_16M) {
- cmd = SPINOR_OP_SE;
- addrlen = ADDR24BIT;
- } else {
- /* use the 4-byte address */
- cmd = SPINOR_OP_SE;
- addrlen = ADDR32BIT;
- }
+ lut_base = SEQID_SE_24 * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_SE) | LUT1(ADDR, PAD1, ADDR24BIT),
+ base + QUADSPI_LUT(lut_base));

- writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
+ lut_base = SEQID_SE_32 * 4;
+ writel(LUT0(CMD, PAD1, SPINOR_OP_SE) | LUT1(ADDR, PAD1, ADDR32BIT),
base + QUADSPI_LUT(lut_base));

/* Erase the whole chip */
@@ -391,11 +376,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
}

/* Get the SEQID for the command */
-static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
+static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd, u8 addr_width)
{
switch (cmd) {
case SPINOR_OP_READ_1_1_4:
- return SEQID_QUAD_READ;
+ if (addr_width == 3)
+ return SEQID_QUAD_READ_24;
+ if (addr_width == 4)
+ return SEQID_QUAD_READ_32;
+ dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
+ addr_width, cmd);
+ break;
case SPINOR_OP_WREN:
return SEQID_WREN;
case SPINOR_OP_WRDI:
@@ -403,11 +394,23 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
case SPINOR_OP_RDSR:
return SEQID_RDSR;
case SPINOR_OP_SE:
- return SEQID_SE;
+ if (addr_width == 3)
+ return SEQID_SE_24;
+ if (addr_width == 4)
+ return SEQID_SE_32;
+ dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
+ addr_width, cmd);
+ break;
case SPINOR_OP_CHIP_ERASE:
return SEQID_CHIP_ERASE;
case SPINOR_OP_PP:
- return SEQID_PP;
+ if (addr_width == 3)
+ return SEQID_PP_24;
+ if (addr_width == 4)
+ return SEQID_PP_32;
+ dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
+ addr_width, cmd);
+ break;
case SPINOR_OP_RDID:
return SEQID_RDID;
case SPINOR_OP_WRSR:
@@ -456,7 +459,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
} while (1);

/* trigger the LUT now */
- seqid = fsl_qspi_get_seqid(q, cmd);
+ seqid = fsl_qspi_get_seqid(q, cmd, q->nor[0].addr_width);
writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR);

/* Wait for the interrupt. */
@@ -601,9 +604,10 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
writel(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, q->nor[0].read_opcode,
+ q->nor[0].addr_width);
writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
- q->iobase + QUADSPI_BFGENCR);
+ q->iobase + QUADSPI_BFGENCR);
}

/* We use this function to do some basic init for spi_nor_scan(). */
--
2.3.6

2015-07-08 20:22:20

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 4/7] mtd: fsl-quadspi: Use per-device clk_rate.

The current fsl-quadspi implementation re-parses the 'spi-max-frequency'
property for each device, potentially allowing for an earlier, lower
frequency to be overwritten with a greater value. This commit modifies
the parsing logic to extract the clock frequency for each flash device
and then configures for that frequency as part of the prepare() method
prior to accessing a given device.

Signed-off-by: Cory Tusar <[email protected]>
---
drivers/mtd/spi-nor/fsl-quadspi.c | 41 +++++++++++++++------------------------
1 file changed, 16 insertions(+), 25 deletions(-)

diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 4b8038b..d0cf0b7 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -233,7 +233,7 @@ struct fsl_qspi {
struct fsl_qspi_devtype_data *devtype_data;
u32 nor_size;
u32 nor_num;
- u32 clk_rate;
+ u32 clk_rate[FSL_QSPI_MAX_CHIP];
unsigned int chip_base_addr; /* We may support two chips. */
bool has_second_chip;
};
@@ -645,25 +645,13 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
return 0;
}

-static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
+static void fsl_qspi_nor_setup_last(struct fsl_qspi *q)
{
- unsigned long rate = q->clk_rate;
- int ret;
-
- if (is_imx6sx_qspi(q))
- rate *= 4;
-
- ret = clk_set_rate(q->clk, rate);
- if (ret)
- return ret;
-
/* Init the LUT table again. */
fsl_qspi_init_lut(q);

/* Init for AHB read */
fsl_qspi_init_abh_read(q);
-
- return 0;
}

static const struct of_device_id fsl_qspi_dt_ids[] = {
@@ -763,6 +751,8 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct fsl_qspi *q = nor->priv;
+ int nor_idx = nor - q->nor;
+ unsigned long rate = q->clk_rate[nor_idx];
int ret;

ret = clk_enable(q->clk_en);
@@ -775,6 +765,16 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
return ret;
}

+ if (is_imx6sx_qspi(q))
+ rate *= 4;
+
+ ret = clk_set_rate(q->clk, rate);
+ if (ret) {
+ clk_disable(q->clk);
+ clk_disable(q->clk_en);
+ return ret;
+ }
+
fsl_qspi_set_base_addr(q, nor);
return 0;
}
@@ -899,7 +899,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
goto irq_failed;

ret = of_property_read_u32(np, "spi-max-frequency",
- &q->clk_rate);
+ &q->clk_rate[i]);
if (ret < 0)
goto irq_failed;

@@ -939,21 +939,12 @@ static int fsl_qspi_probe(struct platform_device *pdev)
}

/* finish the rest init. */
- ret = fsl_qspi_nor_setup_last(q);
- if (ret)
- goto last_init_failed;
+ fsl_qspi_nor_setup_last(q);

clk_disable(q->clk);
clk_disable(q->clk_en);
return 0;

-last_init_failed:
- for (i = 0; i < q->nor_num; i++) {
- /* skip the holes */
- if (!q->has_second_chip)
- i *= 2;
- mtd_device_unregister(&q->mtd[i]);
- }
irq_failed:
clk_disable_unprepare(q->clk);
clk_failed:
--
2.3.6

2015-07-08 20:22:48

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 5/7] mtd: fsl-quadspi: Allow non-contiguous flash layouts.

The current fsl-quadspi implementation assumes that the first NOR device
is always physically connected to QSPIn_A_CS0, and that all connected
devices are of the same type.

This commit separates the DT parsing and flash probe logic into two
passes, allowing devices of different sizes and types to be attached in
arbitrary order to the various chip selects associated with each QSPI
interface.

Tested on a custom VF610-based board with a single SPI NOR connected to
QSPI0_B_CS0, and also on a Rev. H TWR board with dual SPI NOR devices
connected to QSPI0_A_CS0 and QSPI0_B_CS0.

Signed-off-by: Cory Tusar <[email protected]>
---
.../devicetree/bindings/mtd/fsl-quadspi.txt | 15 +-
drivers/mtd/spi-nor/fsl-quadspi.c | 166 +++++++++++++--------
2 files changed, 106 insertions(+), 75 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
index 4461dc7..e392156 100644
--- a/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
+++ b/Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
@@ -9,15 +9,6 @@ Required properties:
- clocks : The clocks needed by the QuadSPI controller
- clock-names : the name of the clocks

-Optional properties:
- - fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B.
- Each bus can be connected with two NOR flashes.
- Most of the time, each bus only has one NOR flash
- connected, this is the default case.
- But if there are two NOR flashes connected to the
- bus, you should enable this property.
- (Please check the board's schematic.)
-
Example:

qspi0: quadspi@40044000 {
@@ -30,6 +21,12 @@ qspi0: quadspi@40044000 {
clock-names = "qspi_en", "qspi";

flash0: s25fl128s@0 {
+ reg = <0>; /* QSPI0_A_CS0 */
+ ....
+ };
+
+ flash1: s25fl128s@3 {
+ reg = <3>; /* QSPI0_B_CS1 */
....
};
};
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index d0cf0b7..3c378f0 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -221,6 +221,7 @@ static struct fsl_qspi_devtype_data imx6sx_data = {
};

#define FSL_QSPI_MAX_CHIP 4
+#define MODALIAS_NBYTES 40
struct fsl_qspi {
struct mtd_info mtd[FSL_QSPI_MAX_CHIP];
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
@@ -231,11 +232,10 @@ struct fsl_qspi {
struct device *dev;
struct completion c;
struct fsl_qspi_devtype_data *devtype_data;
- u32 nor_size;
- u32 nor_num;
+ char modalias[FSL_QSPI_MAX_CHIP][MODALIAS_NBYTES];
+ u32 nor_size[FSL_QSPI_MAX_CHIP];
u32 clk_rate[FSL_QSPI_MAX_CHIP];
unsigned int chip_base_addr; /* We may support two chips. */
- bool has_second_chip;
};

static inline int is_vybrid_qspi(struct fsl_qspi *q)
@@ -429,8 +429,9 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd, u8 addr_width)
}

static int
-fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
+fsl_qspi_runcmd(struct spi_nor *nor, u8 cmd, unsigned int addr, int len)
{
+ struct fsl_qspi *q = nor->priv;
void __iomem *base = q->iobase;
int seqid;
u32 reg, reg2;
@@ -459,7 +460,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
} while (1);

/* trigger the LUT now */
- seqid = fsl_qspi_get_seqid(q, cmd, q->nor[0].addr_width);
+ seqid = fsl_qspi_get_seqid(q, cmd, nor->addr_width);
writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR);

/* Wait for the interrupt. */
@@ -550,7 +551,7 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
}

/* Trigger it */
- ret = fsl_qspi_runcmd(q, opcode, to, count);
+ ret = fsl_qspi_runcmd(nor, opcode, to, count);

if (ret == 0 && retlen)
*retlen += count;
@@ -560,13 +561,17 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,

static void fsl_qspi_set_map_addr(struct fsl_qspi *q)
{
- int nor_size = q->nor_size;
void __iomem *base = q->iobase;
-
- writel(nor_size + q->memmap_phy, base + QUADSPI_SFA1AD);
- writel(nor_size * 2 + q->memmap_phy, base + QUADSPI_SFA2AD);
- writel(nor_size * 3 + q->memmap_phy, base + QUADSPI_SFB1AD);
- writel(nor_size * 4 + q->memmap_phy, base + QUADSPI_SFB2AD);
+ int offset = 0;
+
+ offset += q->nor_size[0];
+ writel(q->memmap_phy + offset, base + QUADSPI_SFA1AD);
+ offset += q->nor_size[1];
+ writel(q->memmap_phy + offset, base + QUADSPI_SFA2AD);
+ offset += q->nor_size[2];
+ writel(q->memmap_phy + offset, base + QUADSPI_SFB1AD);
+ offset += q->nor_size[3];
+ writel(q->memmap_phy + offset, base + QUADSPI_SFB2AD);
}

/*
@@ -585,7 +590,6 @@ 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;
- int seqid;

/* AHB configuration for access buffer 0/1/2 .*/
writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
@@ -602,12 +606,6 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
writel(0, base + QUADSPI_BUF0IND);
writel(0, base + QUADSPI_BUF1IND);
writel(0, base + QUADSPI_BUF2IND);
-
- /* Set the default lut sequence for AHB Read. */
- seqid = fsl_qspi_get_seqid(q, q->nor[0].read_opcode,
- q->nor[0].addr_width);
- writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
- q->iobase + QUADSPI_BFGENCR);
}

/* We use this function to do some basic init for spi_nor_scan(). */
@@ -639,6 +637,15 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
base + QUADSPI_MCR);

+ /*
+ * Set up a "dummy" address mapping that can be used to access chips
+ * at each CSn during probe.
+ */
+ writel(q->memmap_phy + 1 * SZ_4K, base + QUADSPI_SFA1AD);
+ writel(q->memmap_phy + 2 * SZ_4K, base + QUADSPI_SFA2AD);
+ writel(q->memmap_phy + 3 * SZ_4K, base + QUADSPI_SFB1AD);
+ writel(q->memmap_phy + 4 * SZ_4K, base + QUADSPI_SFB2AD);
+
/* enable the interrupt */
writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);

@@ -663,15 +670,22 @@ MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);

static void fsl_qspi_set_base_addr(struct fsl_qspi *q, struct spi_nor *nor)
{
- q->chip_base_addr = q->nor_size * (nor - q->nor);
+ int nor_idx = nor - q->nor;
+ int base_addr = 0;
+ int i;
+
+ for (i = 0; i < nor_idx; i++)
+ base_addr += q->nor_size[i];
+
+ q->chip_base_addr = base_addr;
}

static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
- int ret;
struct fsl_qspi *q = nor->priv;
+ int ret;

- ret = fsl_qspi_runcmd(q, opcode, 0, len);
+ ret = fsl_qspi_runcmd(nor, opcode, 0, len);
if (ret)
return ret;

@@ -680,13 +694,13 @@ static int fsl_qspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
}

static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
- int write_enable)
+ int write_enable)
{
struct fsl_qspi *q = nor->priv;
int ret;

if (!buf) {
- ret = fsl_qspi_runcmd(q, opcode, 0, 1);
+ ret = fsl_qspi_runcmd(nor, opcode, 0, 1);
if (ret)
return ret;

@@ -704,8 +718,8 @@ static int fsl_qspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
return ret;
}

-static void fsl_qspi_write(struct spi_nor *nor, loff_t to,
- size_t len, size_t *retlen, const u_char *buf)
+static void fsl_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
{
struct fsl_qspi *q = nor->priv;

@@ -716,15 +730,24 @@ static void fsl_qspi_write(struct spi_nor *nor, loff_t to,
fsl_qspi_invalid(q);
}

-static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
- size_t len, size_t *retlen, u_char *buf)
+static int fsl_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
{
struct fsl_qspi *q = nor->priv;
u8 cmd = nor->read_opcode;
+ int seqid;

dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);

+ /*
+ * Set the appropriate LUT sequence for an "AHB Read" from this
+ * particular NOR device.
+ */
+ seqid = fsl_qspi_get_seqid(q, nor->read_opcode, nor->addr_width);
+ writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
+ q->iobase + QUADSPI_BFGENCR);
+
/* Read out the data directly from the AHB buffer.*/
memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);

@@ -740,7 +763,7 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);

- ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
+ ret = fsl_qspi_runcmd(nor, nor->erase_opcode, offs, 0);
if (ret)
return ret;

@@ -804,10 +827,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (!q)
return -ENOMEM;

- q->nor_num = of_get_child_count(dev->of_node);
- if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP)
- return -ENODEV;
-
/* find the resources */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
q->iobase = devm_ioremap_resource(dev, res);
@@ -861,20 +880,11 @@ static int fsl_qspi_probe(struct platform_device *pdev)
q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
platform_set_drvdata(pdev, q);

- ret = fsl_qspi_nor_setup(q);
- if (ret)
- goto irq_failed;
-
- if (of_get_property(np, "fsl,qspi-has-second-chip", NULL))
- q->has_second_chip = true;
-
/* iterate the subnodes. */
for_each_available_child_of_node(dev->of_node, np) {
- char modalias[40];
-
- /* skip the holes */
- if (!q->has_second_chip)
- i *= 2;
+ ret = of_property_read_u32(np, "reg", &i);
+ if (ret || i >= FSL_QSPI_MAX_CHIP)
+ goto irq_failed;

nor = &q->nor[i];
mtd = &q->mtd[i];
@@ -894,7 +904,7 @@ 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));
+ ret = of_modalias_node(np, q->modalias[i], MODALIAS_NBYTES);
if (ret < 0)
goto irq_failed;

@@ -902,26 +912,39 @@ static int fsl_qspi_probe(struct platform_device *pdev)
&q->clk_rate[i]);
if (ret < 0)
goto irq_failed;
+ }

- /* set the chip address for READID */
- fsl_qspi_set_base_addr(q, nor);
+ /* Set up temporary 4 KiB mappings for use in second-pass probe. */
+ ret = fsl_qspi_nor_setup(q);
+ if (ret)
+ goto irq_failed;
+
+ for_each_available_child_of_node(dev->of_node, np) {
+ ret = of_property_read_u32(np, "reg", &i);
+ if (ret || i >= FSL_QSPI_MAX_CHIP)
+ goto pass2_failed;
+
+ nor = &q->nor[i];
+ mtd = &q->mtd[i];

- ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
+ /*
+ * Set a chip address (based on temporary 4 KiB mappings) to
+ * allow a READID operation to each CSn.
+ */
+ q->chip_base_addr = i * SZ_4K;
+
+ ret = spi_nor_scan(nor, q->modalias[i], SPI_NOR_QUAD);
if (ret)
- goto irq_failed;
+ goto pass2_failed;

ppdata.of_node = np;
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
if (ret)
- goto irq_failed;
+ goto pass2_failed;

/* Set the correct NOR size now. */
- if (q->nor_size == 0) {
- q->nor_size = mtd->size;
-
- /* Map the SPI NOR to accessiable address */
- fsl_qspi_set_map_addr(q);
- }
+ if (q->nor_size[i] == 0)
+ q->nor_size[i] = mtd->size;

/*
* The TX FIFO is 64 bytes in the Vybrid, but the Page Program
@@ -934,10 +957,14 @@ static int fsl_qspi_probe(struct platform_device *pdev)
*/
if (nor->page_size > q->devtype_data->txfifo)
nor->page_size = q->devtype_data->txfifo;
-
- i++;
}

+ /*
+ * Now that all chips have been probed, map the SPI NOR(s) to
+ * accessible addresses.
+ */
+ fsl_qspi_set_map_addr(q);
+
/* finish the rest init. */
fsl_qspi_nor_setup_last(q);

@@ -945,6 +972,15 @@ static int fsl_qspi_probe(struct platform_device *pdev)
clk_disable(q->clk_en);
return 0;

+pass2_failed:
+ /* Ensure that hardware is disabled before purging MTD devices. */
+ writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
+ writel(0x0, q->iobase + QUADSPI_RSER);
+
+ for (i = 0; i < FSL_QSPI_MAX_CHIP; i++) {
+ if (q->mtd[i].type)
+ mtd_device_unregister(&q->mtd[i]);
+ }
irq_failed:
clk_disable_unprepare(q->clk);
clk_failed:
@@ -957,17 +993,15 @@ static int fsl_qspi_remove(struct platform_device *pdev)
struct fsl_qspi *q = platform_get_drvdata(pdev);
int i;

- for (i = 0; i < q->nor_num; i++) {
- /* skip the holes */
- if (!q->has_second_chip)
- i *= 2;
- mtd_device_unregister(&q->mtd[i]);
- }
-
/* disable the hardware */
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
writel(0x0, q->iobase + QUADSPI_RSER);

+ for (i = 0; i < FSL_QSPI_MAX_CHIP; i++) {
+ if (q->mtd[i].type)
+ mtd_device_unregister(&q->mtd[i]);
+ }
+
clk_unprepare(q->clk);
clk_unprepare(q->clk_en);
return 0;
--
2.3.6

2015-07-08 20:23:32

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 6/7] mtd: spi-nor: Add support for Micron MT25QL02GC serial flash.

Add Micron (mt25ql02gc) 256 MiB flash to the list of supported devices.

Signed-off-by: Cory Tusar <[email protected]>
---
drivers/mtd/spi-nor/spi-nor.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d78831b..7a8896d 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -596,6 +596,7 @@ static const struct spi_device_id spi_nor_ids[] = {
{ "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) },
{ "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+ { "mt25ql02gc", INFO(0x20ba22, 0, 64 * 1024, 4096, SPI_NOR_QUAD_READ) },

/* PMC */
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
--
2.3.6

2015-07-08 20:23:16

by Cory Tusar

[permalink] [raw]
Subject: [PATCH v1 7/7] ARM: dts: vf610-twr: Enable QSPI and map flash devices.

This commit enables the qspi0 functional block, and maps the two
flash devices connected to QSPI0_A_CS0 and QSPI0_B_CS0 to individual MTD
devices.

Tested using mtd_readtest, mtd_speedtest, and mtd_stresstest on a Rev. H
TWR board.

Signed-off-by: Cory Tusar <[email protected]>
---
arch/arm/boot/dts/vf610-twr.dts | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)

diff --git a/arch/arm/boot/dts/vf610-twr.dts b/arch/arm/boot/dts/vf610-twr.dts
index 375ab23..6ae2e7c 100644
--- a/arch/arm/boot/dts/vf610-twr.dts
+++ b/arch/arm/boot/dts/vf610-twr.dts
@@ -246,6 +246,23 @@
>;
};

+ pinctrl_qspi0: qspi0grp {
+ fsl,pins = <
+ VF610_PAD_PTD0__QSPI0_A_QSCK 0x31e2
+ VF610_PAD_PTD1__QSPI0_A_CS0 0x31e2
+ VF610_PAD_PTD2__QSPI0_A_DATA3 0x31e3
+ VF610_PAD_PTD3__QSPI0_A_DATA2 0x31e3
+ VF610_PAD_PTD4__QSPI0_A_DATA1 0x31e3
+ VF610_PAD_PTD5__QSPI0_A_DATA0 0x31e3
+ VF610_PAD_PTD7__QSPI0_B_QSCK 0x31e2
+ VF610_PAD_PTD8__QSPI0_B_CS0 0x31e2
+ VF610_PAD_PTD9__QSPI0_B_DATA3 0x31e3
+ VF610_PAD_PTD10__QSPI0_B_DATA2 0x31e3
+ VF610_PAD_PTD11__QSPI0_B_DATA1 0x31e3
+ VF610_PAD_PTD12__QSPI0_B_DATA0 0x31e3
+ >;
+ };
+
pinctrl_sai2: sai2grp {
fsl,pins = <
VF610_PAD_PTA16__SAI2_TX_BCLK 0x02ed
@@ -280,6 +297,28 @@
status = "okay";
};

+&qspi0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_qspi0>;
+ status = "okay";
+
+ flash0: s25fl128s@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "spansion,s25fl128s";
+ spi-max-frequency = <66000000>;
+ reg = <0>;
+ };
+
+ flash1: s25fl128s@2 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "spansion,s25fl128s";
+ spi-max-frequency = <66000000>;
+ reg = <2>;
+ };
+};
+
&sai2 {
#sound-dai-cells = <0>;
pinctrl-names = "default";
--
2.3.6

2015-07-14 03:33:42

by Shawn Guo

[permalink] [raw]
Subject: Re: [PATCH v1 0/7] fsl-quadspi: Allow additional device combinations.

On Wed, Jul 08, 2015 at 04:21:14PM -0400, Cory Tusar wrote:
> Cory Tusar (7):
> ARM: dts: vf610: Add missing QuadSPI register mapping and names.
> ARM: dts: vfxxx: Include support for qspi1 functionality.

Applied these two, thanks.

2015-07-14 14:11:30

by Alexander Stein

[permalink] [raw]
Subject: Re: [PATCH v1 3/7] mtd: fsl-quadspi: Support both 24- and 32-bit addressed commands.

Hello Cory,

On Wednesday 08 July 2015 16:21:17, Cory Tusar wrote:
> The current fsl-quadspi implementation assumes that all connected
> devices are of the same size and type. This commit adds lookup table
> entries for both 24- and 32-bit addressed variants of the read, sector
> erase, and page program operations as a precursor to later changes which
> generalize the flash layout parsing logic and allow for non-contiguous
> and non-homogeneous chip combinations.
>
> Signed-off-by: Cory Tusar <[email protected]>
>
> [...]
> /* Get the SEQID for the command */
> -static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd, u8 addr_width)
> {
> switch (cmd) {
> case SPINOR_OP_READ_1_1_4:
> - return SEQID_QUAD_READ;
> + if (addr_width == 3)
> + return SEQID_QUAD_READ_24;
> + if (addr_width == 4)
> + return SEQID_QUAD_READ_32;
> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
> + addr_width, cmd);
> + break;

Does this actually work? What if q->nor[0].read_opcode is SPINOR_OP_READ4_1_1_4 (set in spi-nor.c for flashes using 4 byte address commands)?

> case SPINOR_OP_WREN:
> return SEQID_WREN;
> case SPINOR_OP_WRDI:
> @@ -403,11 +394,23 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> case SPINOR_OP_RDSR:
> return SEQID_RDSR;
> case SPINOR_OP_SE:
> - return SEQID_SE;
> + if (addr_width == 3)
> + return SEQID_SE_24;
> + if (addr_width == 4)
> + return SEQID_SE_32;
> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
> + addr_width, cmd);
> + break;

Same as for read command.

> case SPINOR_OP_CHIP_ERASE:
> return SEQID_CHIP_ERASE;
> case SPINOR_OP_PP:
> - return SEQID_PP;
> + if (addr_width == 3)
> + return SEQID_PP_24;
> + if (addr_width == 4)
> + return SEQID_PP_32;
> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
> + addr_width, cmd);
> + break;

See above too.

> case SPINOR_OP_RDID:
> return SEQID_RDID;
> case SPINOR_OP_WRSR:
> @@ -456,7 +459,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
> } while (1);
>
> /* trigger the LUT now */
> - seqid = fsl_qspi_get_seqid(q, cmd);
> + seqid = fsl_qspi_get_seqid(q, cmd, q->nor[0].addr_width);
> writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR);
>
> /* Wait for the interrupt. */
> @@ -601,9 +604,10 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
> writel(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, q->nor[0].read_opcode,
> + q->nor[0].addr_width);
> writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT,

Those changes can then be skipped you explicitly do switch/case for 4 byte address commands.

Best reagrds,
Alexander
--
Dipl.-Inf. Alexander Stein

SYS TEC electronic GmbH
Am Windrad 2
08468 Heinsdorfergrund
Tel.: 03765 38600-1156
Fax: 03765 38600-4100
Email: [email protected]
Website: http://www.systec-electronic.com

Managing Director: Dipl.-Phys. Siegmar Schmidt
Commercial registry: Amtsgericht Chemnitz, HRB 28082

2015-11-20 19:24:55

by Brian Norris

[permalink] [raw]
Subject: Re: [PATCH v1 3/7] mtd: fsl-quadspi: Support both 24- and 32-bit addressed commands.

Cory and Han,

Did this series get dropped on the floor? I recall Han arguing
previously that this controller is always used with two identical chips.
But apparently that is not the case.

If this request is not truly dead, I'd appreciate it if Han could
review/test.

Brian

On Wed, Jul 08, 2015 at 04:21:17PM -0400, Cory Tusar wrote:
> The current fsl-quadspi implementation assumes that all connected
> devices are of the same size and type. This commit adds lookup table
> entries for both 24- and 32-bit addressed variants of the read, sector
> erase, and page program operations as a precursor to later changes which
> generalize the flash layout parsing logic and allow for non-contiguous
> and non-homogeneous chip combinations.
>
> Signed-off-by: Cory Tusar <[email protected]>
> ---
> drivers/mtd/spi-nor/fsl-quadspi.c | 116 ++++++++++++++++++++------------------
> 1 file changed, 60 insertions(+), 56 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 52a872f..4b8038b 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -178,18 +178,21 @@
> #define QUADSPI_LUT_NUM 64
>
> /* SEQID -- we can have 16 seqids at most. */
> -#define SEQID_QUAD_READ 0
> -#define SEQID_WREN 1
> -#define SEQID_WRDI 2
> -#define SEQID_RDSR 3
> -#define SEQID_SE 4
> -#define SEQID_CHIP_ERASE 5
> -#define SEQID_PP 6
> -#define SEQID_RDID 7
> -#define SEQID_WRSR 8
> -#define SEQID_RDCR 9
> -#define SEQID_EN4B 10
> -#define SEQID_BRWR 11
> +#define SEQID_QUAD_READ_24 0
> +#define SEQID_QUAD_READ_32 1
> +#define SEQID_WREN 2
> +#define SEQID_WRDI 3
> +#define SEQID_RDSR 4
> +#define SEQID_SE_24 5
> +#define SEQID_SE_32 5
> +#define SEQID_CHIP_ERASE 7
> +#define SEQID_PP_24 8
> +#define SEQID_PP_32 8
> +#define SEQID_RDID 9
> +#define SEQID_WRSR 10
> +#define SEQID_RDCR 11
> +#define SEQID_EN4B 12
> +#define SEQID_BRWR 13
>
> enum fsl_qspi_devtype {
> FSL_QUADSPI_VYBRID,
> @@ -287,7 +290,6 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> void __iomem *base = q->iobase;
> int rxfifo = q->devtype_data->rxfifo;
> u32 lut_base;
> - u8 cmd, addrlen, dummy;
> int i;
>
> fsl_qspi_unlock_lut(q);
> @@ -297,22 +299,16 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> writel(0, base + QUADSPI_LUT_BASE + i * 4);
>
> /* 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;
> - }
> + lut_base = SEQID_QUAD_READ_24 * 4;
> + writel(LUT0(CMD, PAD1, SPINOR_OP_READ_1_1_4) | LUT1(ADDR, PAD1, ADDR24BIT),
> + base + QUADSPI_LUT(lut_base));
> + writel(LUT0(DUMMY, PAD1, 8) | LUT1(READ, PAD4, rxfifo),
> + base + QUADSPI_LUT(lut_base + 1));
>
> - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
> + lut_base = SEQID_QUAD_READ_32 * 4;
> + writel(LUT0(CMD, PAD1, SPINOR_OP_READ_1_1_4) | LUT1(ADDR, PAD1, ADDR32BIT),
> base + QUADSPI_LUT(lut_base));
> - writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo),
> + writel(LUT0(DUMMY, PAD1, 8) | LUT1(READ, PAD4, rxfifo),
> base + QUADSPI_LUT(lut_base + 1));
>
> /* Write enable */
> @@ -320,18 +316,13 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base));
>
> /* 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;
> - }
> + lut_base = SEQID_PP_24 * 4;
> + writel(LUT0(CMD, PAD1, SPINOR_OP_PP) | LUT1(ADDR, PAD1, ADDR24BIT),
> + base + QUADSPI_LUT(lut_base));
> + writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
>
> - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
> + lut_base = SEQID_PP_32 * 4;
> + writel(LUT0(CMD, PAD1, SPINOR_OP_PP) | LUT1(ADDR, PAD1, ADDR32BIT),
> base + QUADSPI_LUT(lut_base));
> writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
>
> @@ -341,18 +332,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> base + QUADSPI_LUT(lut_base));
>
> /* Erase a sector */
> - lut_base = SEQID_SE * 4;
> -
> - if (q->nor_size <= SZ_16M) {
> - cmd = SPINOR_OP_SE;
> - addrlen = ADDR24BIT;
> - } else {
> - /* use the 4-byte address */
> - cmd = SPINOR_OP_SE;
> - addrlen = ADDR32BIT;
> - }
> + lut_base = SEQID_SE_24 * 4;
> + writel(LUT0(CMD, PAD1, SPINOR_OP_SE) | LUT1(ADDR, PAD1, ADDR24BIT),
> + base + QUADSPI_LUT(lut_base));
>
> - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
> + lut_base = SEQID_SE_32 * 4;
> + writel(LUT0(CMD, PAD1, SPINOR_OP_SE) | LUT1(ADDR, PAD1, ADDR32BIT),
> base + QUADSPI_LUT(lut_base));
>
> /* Erase the whole chip */
> @@ -391,11 +376,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
> }
>
> /* Get the SEQID for the command */
> -static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd, u8 addr_width)
> {
> switch (cmd) {
> case SPINOR_OP_READ_1_1_4:
> - return SEQID_QUAD_READ;
> + if (addr_width == 3)
> + return SEQID_QUAD_READ_24;
> + if (addr_width == 4)
> + return SEQID_QUAD_READ_32;
> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
> + addr_width, cmd);
> + break;
> case SPINOR_OP_WREN:
> return SEQID_WREN;
> case SPINOR_OP_WRDI:
> @@ -403,11 +394,23 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> case SPINOR_OP_RDSR:
> return SEQID_RDSR;
> case SPINOR_OP_SE:
> - return SEQID_SE;
> + if (addr_width == 3)
> + return SEQID_SE_24;
> + if (addr_width == 4)
> + return SEQID_SE_32;
> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
> + addr_width, cmd);
> + break;
> case SPINOR_OP_CHIP_ERASE:
> return SEQID_CHIP_ERASE;
> case SPINOR_OP_PP:
> - return SEQID_PP;
> + if (addr_width == 3)
> + return SEQID_PP_24;
> + if (addr_width == 4)
> + return SEQID_PP_32;
> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
> + addr_width, cmd);
> + break;
> case SPINOR_OP_RDID:
> return SEQID_RDID;
> case SPINOR_OP_WRSR:
> @@ -456,7 +459,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
> } while (1);
>
> /* trigger the LUT now */
> - seqid = fsl_qspi_get_seqid(q, cmd);
> + seqid = fsl_qspi_get_seqid(q, cmd, q->nor[0].addr_width);
> writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR);
>
> /* Wait for the interrupt. */
> @@ -601,9 +604,10 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
> writel(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, q->nor[0].read_opcode,
> + q->nor[0].addr_width);
> writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
> - q->iobase + QUADSPI_BFGENCR);
> + q->iobase + QUADSPI_BFGENCR);
> }
>
> /* We use this function to do some basic init for spi_nor_scan(). */
> --
> 2.3.6
>

2015-11-20 19:38:15

by Cory Tusar

[permalink] [raw]
Subject: Re: [PATCH v1 3/7] mtd: fsl-quadspi: Support both 24- and 32-bit addressed commands.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 11/20/2015 02:24 PM, Brian Norris wrote:
> Cory and Han,
>
> Did this series get dropped on the floor? I recall Han arguing
> previously that this controller is always used with two identical chips.
> But apparently that is not the case.
>
> If this request is not truly dead, I'd appreciate it if Han could
> review/test.

Hi Brian,

Pretty sure this series will not apply & work cleanly with 4.4-rc1.

IIRC, there were also some changes (apart from these) that reworked
clocking and resulted in a fault the last time I tested this on actual
hardware.

I can see about rebasing and updating if there's additional interest.
I don't know that there's anything about the controller itself that
would preclude using different chips, apart from the overhead of having
to reconfigure the LUT when switching between devices...

We'd had a board which only included a device on QSPI0_B_CS0, whereas
the driver assumed that the channels would be populated in order (e.g.
QSPI0_A_CS0 first and then QSPI0_B_CS0)...that configuration was what
originally drove my changes.

- -Cory


> On Wed, Jul 08, 2015 at 04:21:17PM -0400, Cory Tusar wrote:
>> The current fsl-quadspi implementation assumes that all connected
>> devices are of the same size and type. This commit adds lookup table
>> entries for both 24- and 32-bit addressed variants of the read, sector
>> erase, and page program operations as a precursor to later changes which
>> generalize the flash layout parsing logic and allow for non-contiguous
>> and non-homogeneous chip combinations.
>>
>> Signed-off-by: Cory Tusar <[email protected]>
>> ---
>> drivers/mtd/spi-nor/fsl-quadspi.c | 116 ++++++++++++++++++++------------------
>> 1 file changed, 60 insertions(+), 56 deletions(-)
>>
>> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
>> index 52a872f..4b8038b 100644
>> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
>> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
>> @@ -178,18 +178,21 @@
>> #define QUADSPI_LUT_NUM 64
>>
>> /* SEQID -- we can have 16 seqids at most. */
>> -#define SEQID_QUAD_READ 0
>> -#define SEQID_WREN 1
>> -#define SEQID_WRDI 2
>> -#define SEQID_RDSR 3
>> -#define SEQID_SE 4
>> -#define SEQID_CHIP_ERASE 5
>> -#define SEQID_PP 6
>> -#define SEQID_RDID 7
>> -#define SEQID_WRSR 8
>> -#define SEQID_RDCR 9
>> -#define SEQID_EN4B 10
>> -#define SEQID_BRWR 11
>> +#define SEQID_QUAD_READ_24 0
>> +#define SEQID_QUAD_READ_32 1
>> +#define SEQID_WREN 2
>> +#define SEQID_WRDI 3
>> +#define SEQID_RDSR 4
>> +#define SEQID_SE_24 5
>> +#define SEQID_SE_32 5
>> +#define SEQID_CHIP_ERASE 7
>> +#define SEQID_PP_24 8
>> +#define SEQID_PP_32 8
>> +#define SEQID_RDID 9
>> +#define SEQID_WRSR 10
>> +#define SEQID_RDCR 11
>> +#define SEQID_EN4B 12
>> +#define SEQID_BRWR 13
>>
>> enum fsl_qspi_devtype {
>> FSL_QUADSPI_VYBRID,
>> @@ -287,7 +290,6 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
>> void __iomem *base = q->iobase;
>> int rxfifo = q->devtype_data->rxfifo;
>> u32 lut_base;
>> - u8 cmd, addrlen, dummy;
>> int i;
>>
>> fsl_qspi_unlock_lut(q);
>> @@ -297,22 +299,16 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
>> writel(0, base + QUADSPI_LUT_BASE + i * 4);
>>
>> /* 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;
>> - }
>> + lut_base = SEQID_QUAD_READ_24 * 4;
>> + writel(LUT0(CMD, PAD1, SPINOR_OP_READ_1_1_4) | LUT1(ADDR, PAD1, ADDR24BIT),
>> + base + QUADSPI_LUT(lut_base));
>> + writel(LUT0(DUMMY, PAD1, 8) | LUT1(READ, PAD4, rxfifo),
>> + base + QUADSPI_LUT(lut_base + 1));
>>
>> - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
>> + lut_base = SEQID_QUAD_READ_32 * 4;
>> + writel(LUT0(CMD, PAD1, SPINOR_OP_READ_1_1_4) | LUT1(ADDR, PAD1, ADDR32BIT),
>> base + QUADSPI_LUT(lut_base));
>> - writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD4, rxfifo),
>> + writel(LUT0(DUMMY, PAD1, 8) | LUT1(READ, PAD4, rxfifo),
>> base + QUADSPI_LUT(lut_base + 1));
>>
>> /* Write enable */
>> @@ -320,18 +316,13 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
>> writel(LUT0(CMD, PAD1, SPINOR_OP_WREN), base + QUADSPI_LUT(lut_base));
>>
>> /* 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;
>> - }
>> + lut_base = SEQID_PP_24 * 4;
>> + writel(LUT0(CMD, PAD1, SPINOR_OP_PP) | LUT1(ADDR, PAD1, ADDR24BIT),
>> + base + QUADSPI_LUT(lut_base));
>> + writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
>>
>> - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
>> + lut_base = SEQID_PP_32 * 4;
>> + writel(LUT0(CMD, PAD1, SPINOR_OP_PP) | LUT1(ADDR, PAD1, ADDR32BIT),
>> base + QUADSPI_LUT(lut_base));
>> writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1));
>>
>> @@ -341,18 +332,12 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
>> base + QUADSPI_LUT(lut_base));
>>
>> /* Erase a sector */
>> - lut_base = SEQID_SE * 4;
>> -
>> - if (q->nor_size <= SZ_16M) {
>> - cmd = SPINOR_OP_SE;
>> - addrlen = ADDR24BIT;
>> - } else {
>> - /* use the 4-byte address */
>> - cmd = SPINOR_OP_SE;
>> - addrlen = ADDR32BIT;
>> - }
>> + lut_base = SEQID_SE_24 * 4;
>> + writel(LUT0(CMD, PAD1, SPINOR_OP_SE) | LUT1(ADDR, PAD1, ADDR24BIT),
>> + base + QUADSPI_LUT(lut_base));
>>
>> - writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
>> + lut_base = SEQID_SE_32 * 4;
>> + writel(LUT0(CMD, PAD1, SPINOR_OP_SE) | LUT1(ADDR, PAD1, ADDR32BIT),
>> base + QUADSPI_LUT(lut_base));
>>
>> /* Erase the whole chip */
>> @@ -391,11 +376,17 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
>> }
>>
>> /* Get the SEQID for the command */
>> -static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
>> +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd, u8 addr_width)
>> {
>> switch (cmd) {
>> case SPINOR_OP_READ_1_1_4:
>> - return SEQID_QUAD_READ;
>> + if (addr_width == 3)
>> + return SEQID_QUAD_READ_24;
>> + if (addr_width == 4)
>> + return SEQID_QUAD_READ_32;
>> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
>> + addr_width, cmd);
>> + break;
>> case SPINOR_OP_WREN:
>> return SEQID_WREN;
>> case SPINOR_OP_WRDI:
>> @@ -403,11 +394,23 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
>> case SPINOR_OP_RDSR:
>> return SEQID_RDSR;
>> case SPINOR_OP_SE:
>> - return SEQID_SE;
>> + if (addr_width == 3)
>> + return SEQID_SE_24;
>> + if (addr_width == 4)
>> + return SEQID_SE_32;
>> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
>> + addr_width, cmd);
>> + break;
>> case SPINOR_OP_CHIP_ERASE:
>> return SEQID_CHIP_ERASE;
>> case SPINOR_OP_PP:
>> - return SEQID_PP;
>> + if (addr_width == 3)
>> + return SEQID_PP_24;
>> + if (addr_width == 4)
>> + return SEQID_PP_32;
>> + dev_err(q->dev, "Unsupported addr_width (%d) for cmd 0x%.2x\n",
>> + addr_width, cmd);
>> + break;
>> case SPINOR_OP_RDID:
>> return SEQID_RDID;
>> case SPINOR_OP_WRSR:
>> @@ -456,7 +459,7 @@ fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
>> } while (1);
>>
>> /* trigger the LUT now */
>> - seqid = fsl_qspi_get_seqid(q, cmd);
>> + seqid = fsl_qspi_get_seqid(q, cmd, q->nor[0].addr_width);
>> writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR);
>>
>> /* Wait for the interrupt. */
>> @@ -601,9 +604,10 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
>> writel(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, q->nor[0].read_opcode,
>> + q->nor[0].addr_width);
>> writel(seqid << QUADSPI_BFGENCR_SEQID_SHIFT,
>> - q->iobase + QUADSPI_BFGENCR);
>> + q->iobase + QUADSPI_BFGENCR);
>> }
>>
>> /* We use this function to do some basic init for spi_nor_scan(). */
>> --
>> 2.3.6
>>


- --
Cory Tusar
Principal
PID 1 Solutions, Inc.


"There are two ways of constructing a software design. One way is to
make it so simple that there are obviously no deficiencies, and the
other way is to make it so complicated that there are no obvious
deficiencies." --Sir Charles Anthony Richard Hoare

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iEYEARECAAYFAlZPdqIACgkQHT1tsfGwHJ/IOACdGu5LE0JC+jAjqa8k93vg4fU3
8KEAn2aGkmdWkq/7hfCOM8Nu+snBM5jG
=qm3K
-----END PGP SIGNATURE-----