All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V1 0/5] Add the Quadspi driver for vf610-twr
@ 2013-08-19  4:09 ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:09 UTC (permalink / raw)
  To: broonie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

The patch set is based on Li Xiaochun's init version.
http://marc.info/?l=linux-arm-kernel&m=137181252311126&w=2

(0) What is the Quadspi controller?

    The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
    one single or two external serial flash devices, each with up to 4
    bidirectional data lines.

(1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
    The LUT registers are a look-up-table for sequences of instructions.
    A valid sequence consists of four LUT registers.

(2) The definition of the LUT register shows below:

    ---------------------------------------------------
    | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
    ---------------------------------------------------

    There are several types of INSTRx, such as:
    	CMD	: the SPI NOR command.
	ADDR	: the address for the SPI NOR command.
	DUMMY	: the dummy cycles needed by the SPI NOR command.
	....

(3) We connect the NOR the QuadSPI now. I am not sure, but i think the
    QuadSPI will be only used for the NOR. We may connect other devices
    to it. But, for the reason of (2), we have to parse out the SPI NOR
    command for the QuadSPI.

(4) Test this driver with the JFFS2 and UBIFS with the Spansion s25fl128s :

    For jffs2:
         #flash_eraseall /dev/mtd0
         #mount -t jffs2 /dev/mtdblock0 tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

    For ubifs:
         #flash_eraseall /dev/mtd0
    	 #ubiattach /dev/ubi_ctrl -m 0
    	 #ubimkvol /dev/ubi0 -N test -m
         #mount -t ubifs ubi0:test tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

 (5) TODO:
     1.) add DDR QUAD read. 
     2.) support two nor chips

changelog:

init --> v1:
 	[1] since the ic bug in the IPS read,
            abandon the IPS read, use the AHB read.
	[2] change the mtd code, add the quad read support.
	[3] add the quad read support the QuadSpi driver.
	[4] misc.


Huang Shijie (5):
  mtd: m25p80: move the spi-nor commands to a header
  mtd: m25p80: add support for Spansion s25fl128s chip
  mtd: m25p80: add the quad-read support
  spi: Add Freescale QuadSpi driver
  ARM: dts: vf610-twr: Add SPI NOR support

 Documentation/devicetree/bindings/mtd/m25p80.txt   |    5 +
 .../devicetree/bindings/spi/fsl-quadspi.txt        |   27 +
 arch/arm/boot/dts/vf610-twr.dts                    |   22 +
 drivers/mtd/devices/m25p80.c                       |   93 ++-
 drivers/spi/Kconfig                                |    7 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-fsl-quadspi.c                      |  930 ++++++++++++++++++++
 include/linux/mtd/spi-nor.h                        |   58 ++
 8 files changed, 1103 insertions(+), 40 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/fsl-quadspi.txt
 create mode 100644 drivers/spi/spi-fsl-quadspi.c
 create mode 100644 include/linux/mtd/spi-nor.h

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 0/5] Add the Quadspi driver for vf610-twr
@ 2013-08-19  4:09 ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:09 UTC (permalink / raw)
  To: linux-arm-kernel

The patch set is based on Li Xiaochun's init version.
http://marc.info/?l=linux-arm-kernel&m=137181252311126&w=2

(0) What is the Quadspi controller?

    The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
    one single or two external serial flash devices, each with up to 4
    bidirectional data lines.

(1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
    The LUT registers are a look-up-table for sequences of instructions.
    A valid sequence consists of four LUT registers.

(2) The definition of the LUT register shows below:

    ---------------------------------------------------
    | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
    ---------------------------------------------------

    There are several types of INSTRx, such as:
    	CMD	: the SPI NOR command.
	ADDR	: the address for the SPI NOR command.
	DUMMY	: the dummy cycles needed by the SPI NOR command.
	....

(3) We connect the NOR the QuadSPI now. I am not sure, but i think the
    QuadSPI will be only used for the NOR. We may connect other devices
    to it. But, for the reason of (2), we have to parse out the SPI NOR
    command for the QuadSPI.

(4) Test this driver with the JFFS2 and UBIFS with the Spansion s25fl128s :

    For jffs2:
         #flash_eraseall /dev/mtd0
         #mount -t jffs2 /dev/mtdblock0 tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

    For ubifs:
         #flash_eraseall /dev/mtd0
    	 #ubiattach /dev/ubi_ctrl -m 0
    	 #ubimkvol /dev/ubi0 -N test -m
         #mount -t ubifs ubi0:test tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

 (5) TODO:
     1.) add DDR QUAD read. 
     2.) support two nor chips

changelog:

init --> v1:
 	[1] since the ic bug in the IPS read,
            abandon the IPS read, use the AHB read.
	[2] change the mtd code, add the quad read support.
	[3] add the quad read support the QuadSpi driver.
	[4] misc.


Huang Shijie (5):
  mtd: m25p80: move the spi-nor commands to a header
  mtd: m25p80: add support for Spansion s25fl128s chip
  mtd: m25p80: add the quad-read support
  spi: Add Freescale QuadSpi driver
  ARM: dts: vf610-twr: Add SPI NOR support

 Documentation/devicetree/bindings/mtd/m25p80.txt   |    5 +
 .../devicetree/bindings/spi/fsl-quadspi.txt        |   27 +
 arch/arm/boot/dts/vf610-twr.dts                    |   22 +
 drivers/mtd/devices/m25p80.c                       |   93 ++-
 drivers/spi/Kconfig                                |    7 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-fsl-quadspi.c                      |  930 ++++++++++++++++++++
 include/linux/mtd/spi-nor.h                        |   58 ++
 8 files changed, 1103 insertions(+), 40 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/fsl-quadspi.txt
 create mode 100644 drivers/spi/spi-fsl-quadspi.c
 create mode 100644 include/linux/mtd/spi-nor.h

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 1/5] mtd: m25p80: move the spi-nor commands to a header
  2013-08-19  4:09 ` Huang Shijie
@ 2013-08-19  4:09   ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:09 UTC (permalink / raw)
  To: broonie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

Why should move the spi-nor commands to a header?

The reason is caused by the Freescale's Quadspi controller.

(0) What is the Quadspi controller?

    The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
    one single or two external serial flash devices, each with up to 4
    bidirectional data lines.

(1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
    The LUT registers are a look-up-table for sequences of instructions.
    A valid sequence consists of four LUT registers.

(2) The definition of the LUT register shows below:

    ---------------------------------------------------
    | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
    ---------------------------------------------------

    There are several types of INSTRx, such as:
    	CMD	: the SPI NOR command.
	ADDR	: the address for the SPI NOR command.
	DUMMY	: the dummy cycles needed by the SPI NOR command.
	....

(3) We should pre-populate the LUT table before the Quadspi controller
    starts to work. Take the SPI Write Enable command for example, we
    should set the LUT table like this:

         INSTR0 = CMD; PAD0 = 0; OPRND0 = 0x06 (SPI Write Enable command)

(4) Conclusion:
    We need to move the SPI NOR commands to a separate header which can be
    used the m25p80.c and the drivers, such Quadspi controller.
    (It seems the spear-smi.c driver also needs this header.)

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/devices/m25p80.c |   41 +--------------------------------
 include/linux/mtd/spi-nor.h  |   52 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 40 deletions(-)
 create mode 100644 include/linux/mtd/spi-nor.h

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index b5190c4..124578a 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -30,51 +30,12 @@
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
 #include <linux/of_platform.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
-/* Flash opcodes. */
-#define	OPCODE_WREN		0x06	/* Write enable */
-#define	OPCODE_RDSR		0x05	/* Read status register */
-#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
-#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
-#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
-#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
-#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
-#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
-#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
-#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
-#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
-
-/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */
-#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */
-#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */
-#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
-
-/* Used for SST flashes only. */
-#define	OPCODE_BP		0x02	/* Byte program */
-#define	OPCODE_WRDI		0x04	/* Write disable */
-#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
-
-/* Used for Macronix and Winbond flashes. */
-#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
-#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
-
-/* Used for Spansion flashes only. */
-#define	OPCODE_BRWR		0x17	/* Bank register write */
-
-/* Status Register bits. */
-#define	SR_WIP			1	/* Write in progress */
-#define	SR_WEL			2	/* Write enable latch */
-/* meaning of other SR_* bits may differ between vendors */
-#define	SR_BP0			4	/* Block protect 0 */
-#define	SR_BP1			8	/* Block protect 1 */
-#define	SR_BP2			0x10	/* Block protect 2 */
-#define	SR_SRWD			0x80	/* SR write protect */
-
 /* Define max times to check status register before we give up. */
 #define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */
 #define	MAX_CMD_SIZE		5
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
new file mode 100644
index 0000000..b420a5b
--- /dev/null
+++ b/include/linux/mtd/spi-nor.h
@@ -0,0 +1,52 @@
+/*
+ * This header contains the SPI-NOR commands and the relative macros
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __LINUX_MTD_SPI_NOR_H
+#define __LINUX_MTD_SPI_NOR_H
+
+/* Flash opcodes. */
+#define	OPCODE_WREN		0x06	/* Write enable */
+#define	OPCODE_RDSR		0x05	/* Read status register */
+#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
+#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
+#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
+#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
+#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
+#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
+#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
+#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
+
+/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
+#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */
+#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */
+#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
+
+/* Used for SST flashes only. */
+#define	OPCODE_BP		0x02	/* Byte program */
+#define	OPCODE_WRDI		0x04	/* Write disable */
+#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
+
+/* Used for Macronix and Winbond flashes. */
+#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
+#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
+
+/* Used for Spansion flashes only. */
+#define	OPCODE_BRWR		0x17	/* Bank register write */
+
+/* Status Register bits. */
+#define	SR_WIP			1	/* Write in progress */
+#define	SR_WEL			2	/* Write enable latch */
+/* meaning of other SR_* bits may differ between vendors */
+#define	SR_BP0			4	/* Block protect 0 */
+#define	SR_BP1			8	/* Block protect 1 */
+#define	SR_BP2			0x10	/* Block protect 2 */
+#define	SR_SRWD			0x80	/* SR write protect */
+
+#endif /* __LINUX_MTD_SPI_NOR_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 1/5] mtd: m25p80: move the spi-nor commands to a header
@ 2013-08-19  4:09   ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:09 UTC (permalink / raw)
  To: linux-arm-kernel

Why should move the spi-nor commands to a header?

The reason is caused by the Freescale's Quadspi controller.

(0) What is the Quadspi controller?

    The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
    one single or two external serial flash devices, each with up to 4
    bidirectional data lines.

(1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
    The LUT registers are a look-up-table for sequences of instructions.
    A valid sequence consists of four LUT registers.

(2) The definition of the LUT register shows below:

    ---------------------------------------------------
    | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
    ---------------------------------------------------

    There are several types of INSTRx, such as:
    	CMD	: the SPI NOR command.
	ADDR	: the address for the SPI NOR command.
	DUMMY	: the dummy cycles needed by the SPI NOR command.
	....

(3) We should pre-populate the LUT table before the Quadspi controller
    starts to work. Take the SPI Write Enable command for example, we
    should set the LUT table like this:

         INSTR0 = CMD; PAD0 = 0; OPRND0 = 0x06 (SPI Write Enable command)

(4) Conclusion:
    We need to move the SPI NOR commands to a separate header which can be
    used the m25p80.c and the drivers, such Quadspi controller.
    (It seems the spear-smi.c driver also needs this header.)

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/devices/m25p80.c |   41 +--------------------------------
 include/linux/mtd/spi-nor.h  |   52 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+), 40 deletions(-)
 create mode 100644 include/linux/mtd/spi-nor.h

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index b5190c4..124578a 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -30,51 +30,12 @@
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
 #include <linux/of_platform.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
 
-/* Flash opcodes. */
-#define	OPCODE_WREN		0x06	/* Write enable */
-#define	OPCODE_RDSR		0x05	/* Read status register */
-#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
-#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
-#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
-#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
-#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
-#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
-#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
-#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
-#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
-
-/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */
-#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */
-#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */
-#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
-
-/* Used for SST flashes only. */
-#define	OPCODE_BP		0x02	/* Byte program */
-#define	OPCODE_WRDI		0x04	/* Write disable */
-#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
-
-/* Used for Macronix and Winbond flashes. */
-#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
-#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
-
-/* Used for Spansion flashes only. */
-#define	OPCODE_BRWR		0x17	/* Bank register write */
-
-/* Status Register bits. */
-#define	SR_WIP			1	/* Write in progress */
-#define	SR_WEL			2	/* Write enable latch */
-/* meaning of other SR_* bits may differ between vendors */
-#define	SR_BP0			4	/* Block protect 0 */
-#define	SR_BP1			8	/* Block protect 1 */
-#define	SR_BP2			0x10	/* Block protect 2 */
-#define	SR_SRWD			0x80	/* SR write protect */
-
 /* Define max times to check status register before we give up. */
 #define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */
 #define	MAX_CMD_SIZE		5
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
new file mode 100644
index 0000000..b420a5b
--- /dev/null
+++ b/include/linux/mtd/spi-nor.h
@@ -0,0 +1,52 @@
+/*
+ * This header contains the SPI-NOR commands and the relative macros
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __LINUX_MTD_SPI_NOR_H
+#define __LINUX_MTD_SPI_NOR_H
+
+/* Flash opcodes. */
+#define	OPCODE_WREN		0x06	/* Write enable */
+#define	OPCODE_RDSR		0x05	/* Read status register */
+#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
+#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
+#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
+#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
+#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
+#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
+#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
+#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
+
+/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
+#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */
+#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */
+#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
+
+/* Used for SST flashes only. */
+#define	OPCODE_BP		0x02	/* Byte program */
+#define	OPCODE_WRDI		0x04	/* Write disable */
+#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
+
+/* Used for Macronix and Winbond flashes. */
+#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
+#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
+
+/* Used for Spansion flashes only. */
+#define	OPCODE_BRWR		0x17	/* Bank register write */
+
+/* Status Register bits. */
+#define	SR_WIP			1	/* Write in progress */
+#define	SR_WEL			2	/* Write enable latch */
+/* meaning of other SR_* bits may differ between vendors */
+#define	SR_BP0			4	/* Block protect 0 */
+#define	SR_BP1			8	/* Block protect 1 */
+#define	SR_BP2			0x10	/* Block protect 2 */
+#define	SR_SRWD			0x80	/* SR write protect */
+
+#endif /* __LINUX_MTD_SPI_NOR_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 2/5] mtd: m25p80: add support for Spansion s25fl128s chip
  2013-08-19  4:09 ` Huang Shijie
@ 2013-08-19  4:10   ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: broonie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/devices/m25p80.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 124578a..f3598c1 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -742,6 +742,7 @@ static const struct spi_device_id m25p_ids[] = {
 	{ "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
 	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
 	{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
+	{ "s25fl128s", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
 	{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
 	{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
 	{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 2/5] mtd: m25p80: add support for Spansion s25fl128s chip
@ 2013-08-19  4:10   ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/mtd/devices/m25p80.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 124578a..f3598c1 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -742,6 +742,7 @@ static const struct spi_device_id m25p_ids[] = {
 	{ "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
 	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
 	{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
+	{ "s25fl128s", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
 	{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
 	{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
 	{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-19  4:09 ` Huang Shijie
@ 2013-08-19  4:10   ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: broonie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

This patch adds the quad read support:

(1) Add the relative commands:
      OPCODE_QIOR, OPCODE_4QIOR, OPCODE_RDCR,

    also add the relative macro for the Configuartion Register.

(2) add the "m25p,quad-read" property for the m25p80 driver
    If the dts has the "m25p,quad-read" property, the kernel will
    set the Quad bit of the configuration register, and when the
    setting is suceeded, we set the read opcode with OPCODE_QIOR.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 Documentation/devicetree/bindings/mtd/m25p80.txt |    5 ++
 drivers/mtd/devices/m25p80.c                     |   51 ++++++++++++++++++++++
 include/linux/mtd/spi-nor.h                      |    6 +++
 3 files changed, 62 insertions(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt
index 6d3d576..b33313f 100644
--- a/Documentation/devicetree/bindings/mtd/m25p80.txt
+++ b/Documentation/devicetree/bindings/mtd/m25p80.txt
@@ -17,6 +17,11 @@ Optional properties:
                    Refer to your chips' datasheet to check if this is supported
                    by your chip.
 
+- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
+                   of the usual "read" opcode. This opcode is not supported by
+                   all chips and support for it can not be detected at runtime.
+                   Refer to your chips' datasheet to check if this is supported
+                   by your chip.
 Example:
 
 	flash: m25p80@0 {
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index f3598c1..4bc9b1b 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -103,6 +103,40 @@ static int write_sr(struct m25p *flash, u8 val)
 }
 
 /*
+ * Read the configuration register, returning its value in the location
+ * Return the configuration register value.
+ * Returns negative if error occurred.
+ */
+static int read_cr(struct m25p *flash)
+{
+	u8 code = OPCODE_RDCR;
+	int ret;
+	u8 val;
+
+	ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
+	if (ret < 0) {
+		dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
+		return ret;
+	}
+	return val;
+}
+
+/*
+ * Write status register and configuration register with 2 bytes
+ * The first byte will be written to the status register, while the second byte
+ * will be written to the configuration register.
+ * Returns negative if error occurred.
+ */
+static int write_sr_cr(struct m25p *flash, u16 val)
+{
+	flash->command[0] = OPCODE_WRSR;
+	flash->command[1] = 0;
+	flash->command[2] = (val >> 8);
+
+	return spi_write(flash->spi, flash->command, 3);
+}
+
+/*
  * Set write enable latch with Write Enable command.
  * Returns negative if error occurred.
  */
@@ -880,6 +914,8 @@ static int m25p_probe(struct spi_device *spi)
 	unsigned			i;
 	struct mtd_part_parser_data	ppdata;
 	struct device_node __maybe_unused *np = spi->dev.of_node;
+	u16 sr_cr;
+	int ret;
 
 #ifdef CONFIG_MTD_OF_PARTS
 	if (!of_device_is_available(np))
@@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
 	else
 		flash->read_opcode = OPCODE_NORM_READ;
 
+	/* Try to enable the Quad Read */
+	if (np && of_property_read_bool(np, "m25p,quad-read")) {
+		/* The configuration register is set by the second byte. */
+		sr_cr = CR_QUAD << 8;
+
+		/* Write the QUAD bit to the Configuration Register. */
+		write_enable(flash);
+		if (write_sr_cr(flash, sr_cr) == 0) {
+			/* read back and check it */
+			ret = read_cr(flash);
+			if (ret > 0 && (ret & CR_QUAD))
+				flash->read_opcode = OPCODE_QIOR;
+		}
+	}
+
 	flash->program_opcode = OPCODE_PP;
 
 	if (info->addr_width)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index b420a5b..d5b189d 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -39,6 +39,9 @@
 
 /* Used for Spansion flashes only. */
 #define	OPCODE_BRWR		0x17	/* Bank register write */
+#define OPCODE_QIOR		0xeb	/* Quad read */
+#define OPCODE_4QIOR		0xec	/* Quad read */
+#define	OPCODE_RDCR		0x35	/* Read configuration register */
 
 /* Status Register bits. */
 #define	SR_WIP			1	/* Write in progress */
@@ -49,4 +52,7 @@
 #define	SR_BP2			0x10	/* Block protect 2 */
 #define	SR_SRWD			0x80	/* SR write protect */
 
+/* Configuration Register bits. */
+#define CR_QUAD			0x2	/* Quad I/O */
+
 #endif /* __LINUX_MTD_SPI_NOR_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-19  4:10   ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds the quad read support:

(1) Add the relative commands:
      OPCODE_QIOR, OPCODE_4QIOR, OPCODE_RDCR,

    also add the relative macro for the Configuartion Register.

(2) add the "m25p,quad-read" property for the m25p80 driver
    If the dts has the "m25p,quad-read" property, the kernel will
    set the Quad bit of the configuration register, and when the
    setting is suceeded, we set the read opcode with OPCODE_QIOR.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 Documentation/devicetree/bindings/mtd/m25p80.txt |    5 ++
 drivers/mtd/devices/m25p80.c                     |   51 ++++++++++++++++++++++
 include/linux/mtd/spi-nor.h                      |    6 +++
 3 files changed, 62 insertions(+), 0 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt
index 6d3d576..b33313f 100644
--- a/Documentation/devicetree/bindings/mtd/m25p80.txt
+++ b/Documentation/devicetree/bindings/mtd/m25p80.txt
@@ -17,6 +17,11 @@ Optional properties:
                    Refer to your chips' datasheet to check if this is supported
                    by your chip.
 
+- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
+                   of the usual "read" opcode. This opcode is not supported by
+                   all chips and support for it can not be detected at runtime.
+                   Refer to your chips' datasheet to check if this is supported
+                   by your chip.
 Example:
 
 	flash: m25p80 at 0 {
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index f3598c1..4bc9b1b 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -103,6 +103,40 @@ static int write_sr(struct m25p *flash, u8 val)
 }
 
 /*
+ * Read the configuration register, returning its value in the location
+ * Return the configuration register value.
+ * Returns negative if error occurred.
+ */
+static int read_cr(struct m25p *flash)
+{
+	u8 code = OPCODE_RDCR;
+	int ret;
+	u8 val;
+
+	ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
+	if (ret < 0) {
+		dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
+		return ret;
+	}
+	return val;
+}
+
+/*
+ * Write status register and configuration register with 2 bytes
+ * The first byte will be written to the status register, while the second byte
+ * will be written to the configuration register.
+ * Returns negative if error occurred.
+ */
+static int write_sr_cr(struct m25p *flash, u16 val)
+{
+	flash->command[0] = OPCODE_WRSR;
+	flash->command[1] = 0;
+	flash->command[2] = (val >> 8);
+
+	return spi_write(flash->spi, flash->command, 3);
+}
+
+/*
  * Set write enable latch with Write Enable command.
  * Returns negative if error occurred.
  */
@@ -880,6 +914,8 @@ static int m25p_probe(struct spi_device *spi)
 	unsigned			i;
 	struct mtd_part_parser_data	ppdata;
 	struct device_node __maybe_unused *np = spi->dev.of_node;
+	u16 sr_cr;
+	int ret;
 
 #ifdef CONFIG_MTD_OF_PARTS
 	if (!of_device_is_available(np))
@@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
 	else
 		flash->read_opcode = OPCODE_NORM_READ;
 
+	/* Try to enable the Quad Read */
+	if (np && of_property_read_bool(np, "m25p,quad-read")) {
+		/* The configuration register is set by the second byte. */
+		sr_cr = CR_QUAD << 8;
+
+		/* Write the QUAD bit to the Configuration Register. */
+		write_enable(flash);
+		if (write_sr_cr(flash, sr_cr) == 0) {
+			/* read back and check it */
+			ret = read_cr(flash);
+			if (ret > 0 && (ret & CR_QUAD))
+				flash->read_opcode = OPCODE_QIOR;
+		}
+	}
+
 	flash->program_opcode = OPCODE_PP;
 
 	if (info->addr_width)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index b420a5b..d5b189d 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -39,6 +39,9 @@
 
 /* Used for Spansion flashes only. */
 #define	OPCODE_BRWR		0x17	/* Bank register write */
+#define OPCODE_QIOR		0xeb	/* Quad read */
+#define OPCODE_4QIOR		0xec	/* Quad read */
+#define	OPCODE_RDCR		0x35	/* Read configuration register */
 
 /* Status Register bits. */
 #define	SR_WIP			1	/* Write in progress */
@@ -49,4 +52,7 @@
 #define	SR_BP2			0x10	/* Block protect 2 */
 #define	SR_SRWD			0x80	/* SR write protect */
 
+/* Configuration Register bits. */
+#define CR_QUAD			0x2	/* Quad I/O */
+
 #endif /* __LINUX_MTD_SPI_NOR_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
  2013-08-19  4:09 ` Huang Shijie
@ 2013-08-19  4:10   ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: broonie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

(0) What is the Quadspi controller?

    The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
    one single or two external serial flash devices, each with up to 4
    bidirectional data lines.

(1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
    The LUT registers are a look-up-table for sequences of instructions.
    A valid sequence consists of four LUT registers.

(2) The definition of the LUT register shows below:

    ---------------------------------------------------
    | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
    ---------------------------------------------------

    There are several types of INSTRx, such as:
    	CMD	: the SPI NOR command.
	ADDR	: the address for the SPI NOR command.
	DUMMY	: the dummy cycles needed by the SPI NOR command.
	....

(3) We connect the NOR the QuadSPI now. I am not sure, but i think the
    QuadSPI will be only used for the NOR. We may connect other devices
    to it. But, for the reason of (2), we have to parse out the SPI NOR
    command for the QuadSPI.

(4) Test this driver with the JFFS2 and UBIFS:

    For jffs2:
         #flash_eraseall /dev/mtd0
         #mount -t jffs2 /dev/mtdblock0 tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

    For ubifs:
         #flash_eraseall /dev/mtd0
    	 #ubiattach /dev/ubi_ctrl -m 0
    	 #ubimkvol /dev/ubi0 -N test -m
         #mount -t ubifs ubi0:test tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 .../devicetree/bindings/spi/fsl-quadspi.txt        |   27 +
 drivers/spi/Kconfig                                |    7 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-fsl-quadspi.c                      |  930 ++++++++++++++++++++
 4 files changed, 965 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/fsl-quadspi.txt
 create mode 100644 drivers/spi/spi-fsl-quadspi.c

diff --git a/Documentation/devicetree/bindings/spi/fsl-quadspi.txt b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
new file mode 100644
index 0000000..e5bfa82
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
@@ -0,0 +1,27 @@
+* Freescale Quad Serial Peripheral Interface(QuadSPI)
+
+Required properties:
+- compatible : Should be "fsl,vf610-qspi"
+- reg : Offset and length of the register set for the device
+- interrupts : Should contain the interrupt for the device
+- fsl,spi-num-chipselects : Contains the number of the chipselect
+- clocks : The clocks needed by the QuadSPI controller
+- clock-names : the name of the clocks
+
+Optional properties:
+- fsl,nor-size : The NOR size used by the QuadSPI mapping.
+
+Example:
+
+qspi0: quadspi@40044000 {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	compatible = "fsl,vf610-qspi";
+	reg = <0x40044000 0x1000>;
+	interrupts = <0 24 0x04>;
+	clocks = <&clks VF610_CLK_QSPI0_EN>,
+		<&clks VF610_CLK_QSPI0>;
+	clock-names = "qspi_en", "qspi";
+	fsl,nor-size = <0x1000000>;
+	status = "disabled";
+};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 92b2373..dc38063 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -187,6 +187,13 @@ config SPI_FALCON
 	  has only been tested with m25p80 type chips. The hardware has no
 	  support for other types of SPI peripherals.
 
+config SPI_FSL_QUADSPI
+	tristate "Freescale Quad SPI controller"
+	depends on ARCH_MXC
+	help
+	  This enables support for the Quad SPI controller in master mode.
+	  We only connect the NOR to this controller now.
+
 config SPI_GPIO
 	tristate "GPIO-based bitbanging SPI Master"
 	depends on GPIOLIB
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index b25f385..7fe505c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SPI_FSL_ESPI)		+= spi-fsl-espi.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi-fsl-spi.o
 obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
+obj-$(CONFIG_SPI_FSL_QUADSPI)           += spi-fsl-quadspi.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
diff --git a/drivers/spi/spi-fsl-quadspi.c b/drivers/spi/spi-fsl-quadspi.c
new file mode 100644
index 0000000..de71a4e
--- /dev/null
+++ b/drivers/spi/spi-fsl-quadspi.c
@@ -0,0 +1,930 @@
+/*
+ * Freescale Quad SPI driver.
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/completion.h>
+#include <linux/mtd/spi-nor.h>
+
+/* The registers */
+#define QUADSPI_MCR			0x00
+#define QUADSPI_MCR_MDIS_SHIFT		14
+#define QUADSPI_MCR_MDIS_MASK		(1 << QUADSPI_MCR_MDIS_SHIFT)
+#define QUADSPI_MCR_CLR_TXF_SHIFT	11
+#define QUADSPI_MCR_CLR_TXF_MASK	(1 << QUADSPI_MCR_CLR_TXF_SHIFT)
+#define QUADSPI_MCR_CLR_RXF_SHIFT	10
+#define QUADSPI_MCR_CLR_RXF_MASK	(1 << QUADSPI_MCR_CLR_RXF_SHIFT)
+#define QUADSPI_MCR_DDR_EN_SHIFT	7
+#define QUADSPI_MCR_DDR_EN_MASK		(1 << QUADSPI_MCR_DDR_EN_SHIFT)
+#define QUADSPI_MCR_RESERVED_SHIFT	16
+#define QUADSPI_MCR_RESERVED_MASK	(0xF << QUADSPI_MCR_RESERVED_SHIFT)
+#define QUADSPI_MCR_SWRSTHD_SHIFT	1
+#define QUADSPI_MCR_SWRSTHD_MASK	(1 << QUADSPI_MCR_SWRSTHD_SHIFT)
+#define QUADSPI_MCR_SWRSTSD_SHIFT	0
+#define QUADSPI_MCR_SWRSTSD_MASK	(1 << QUADSPI_MCR_SWRSTSD_SHIFT)
+
+#define QUADSPI_IPCR			0x08
+#define QUADSPI_IPCR_SEQID_SHIFT	24
+#define QUADSPI_IPCR_SEQID_MASK		(0xF << QUADSPI_IPCR_SEQID_SHIFT)
+
+#define QUADSPI_BUF0CR			0x10
+#define QUADSPI_BUF1CR			0x14
+#define QUADSPI_BUF2CR			0x18
+#define QUADSPI_BUFXCR_INVALID_MSTRID	0xe
+
+#define QUADSPI_BUF3CR			0x1c
+#define QUADSPI_BUF3CR_ALLMST_SHIFT	31
+#define QUADSPI_BUF3CR_ALLMST		(1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
+
+#define QUADSPI_BFGENCR			0x20
+#define QUADSPI_BFGENCR_PAR_EN_SHIFT	16
+#define QUADSPI_BFGENCR_PAR_EN_MASK	(1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT))
+#define QUADSPI_BFGENCR_SEQID_SHIFT	12
+#define QUADSPI_BFGENCR_SEQID_MASK	(0xF << QUADSPI_BFGENCR_SEQID_SHIFT)
+
+#define QUADSPI_BUF0IND			0x30
+#define QUADSPI_BUF1IND			0x34
+#define QUADSPI_BUF2IND			0x38
+#define QUADSPI_SFAR			0x100
+
+#define QUADSPI_SMPR			0x108
+#define QUADSPI_SMPR_DDRSMP_SHIFT	16
+#define QUADSPI_SMPR_DDRSMP_MASK	(7 << QUADSPI_SMPR_DDRSMP_SHIFT)
+#define QUADSPI_SMPR_FSDLY_SHIFT	6
+#define QUADSPI_SMPR_FSDLY_MASK		(1 << QUADSPI_SMPR_FSDLY_SHIFT)
+#define QUADSPI_SMPR_FSPHS_SHIFT	5
+#define QUADSPI_SMPR_FSPHS_MASK		(1 << QUADSPI_SMPR_FSPHS_SHIFT)
+#define QUADSPI_SMPR_HSENA_SHIFT	0
+#define QUADSPI_SMPR_HSENA_MASK		(1 << QUADSPI_SMPR_HSENA_SHIFT)
+
+#define QUADSPI_RBSR			0x10c
+#define QUADSPI_RBSR_RDBFL_SHIFT	8
+#define QUADSPI_RBSR_RDBFL_MASK		(0x3F << QUADSPI_RBSR_RDBFL_SHIFT)
+
+#define QUADSPI_RBCT			0x110
+#define QUADSPI_RBCT_WMRK_MASK		0x1F
+#define QUADSPI_RBCT_RXBRD_SHIFT	8
+#define QUADSPI_RBCT_RXBRD_USEIPS	(0x1 << QUADSPI_RBCT_RXBRD_SHIFT)
+
+#define QUADSPI_TBSR			0x150
+#define QUADSPI_TBDR			0x154
+
+#define QUADSPI_SR			0x15c
+#define QUADSPI_SR_TXFULL_SHIFT		27
+#define QUADSPI_SR_TXFULL_MASK		(1 << QUADSPI_SR_TXFULL_SHIFT)
+#define QUADSPI_SR_AHBTRN_SHIFT		6
+#define QUADSPI_SR_AHBTRN_MASK		(1 << QUADSPI_SR_AHBTRN_SHIFT)
+#define QUADSPI_SR_AHB_ACC_SHIFT	2
+#define QUADSPI_SR_AHB_ACC_MASK		(1 << QUADSPI_SR_AHB_ACC_SHIFT)
+#define QUADSPI_SR_IP_ACC_SHIFT		1
+#define QUADSPI_SR_IP_ACC_MASK		(1 << QUADSPI_SR_IP_ACC_SHIFT)
+#define QUADSPI_SR_BUSY_SHIFT		0
+#define QUADSPI_SR_BUSY_MASK		(1 << QUADSPI_SR_BUSY_SHIFT)
+
+#define QUADSPI_FR			0x160
+#define QUADSPI_FR_TFF_MASK		0x1
+
+#define QUADSPI_SFA1AD			0x180
+#define QUADSPI_SFA2AD			0x184
+#define QUADSPI_SFB1AD			0x188
+#define QUADSPI_SFB2AD			0x18c
+#define QUADSPI_RBDR			0x200
+
+#define QUADSPI_LUTKEY			0x300
+#define QUADSPI_LUTKEY_VALUE		0x5AF05AF0
+
+#define QUADSPI_LCKCR			0x304
+#define QUADSPI_LCKER_LOCK		0x1
+#define QUADSPI_LCKER_UNLOCK		0x2
+
+#define QUADSPI_RSER			0x164
+#define QUADSPI_RSER_TFIE       	(0x1 << 0)
+
+#define QUADSPI_LUT_BASE		0x310
+
+/* Field definitions for LUT register. */
+#define OPRND0_SHIFT		0
+#define PAD0_SHIFT		8
+#define INSTR0_SHIFT		10
+#define OPRND1_SHIFT		16
+
+/* Instruction set for the LUT register. */
+#define CMD			1
+#define ADDR			2
+#define DUMMY			3
+#define MODE			4
+#define MODE2			5
+#define MODE4			6
+#define READ			7
+#define WRITE			8
+#define JMP_ON_CS		9
+#define ADDR_DDR		10
+#define MODE_DDR		11
+#define MODE2_DDR		12
+#define MODE4_DDR		13
+
+/*
+ * The PAD definitions for LUT register.
+ *
+ * The pad stands for the lines number of IO[0:3].
+ * For example, the Quad read need four IO lines, so you should
+ * set PAD4 which means we use four IO lines.
+ */
+#define PAD1			0
+#define PAD2			1
+#define PAD4			2
+
+/* Oprands for the LUT register. */
+#define ADDR24BIT		0x18
+
+/* Macros for constructing the LUT register. */
+#define QUADSPI_LUT0(ins, pad, opr)					\
+		(((opr) << OPRND0_SHIFT) | ((pad) << PAD0_SHIFT) |	\
+		((ins) << INSTR0_SHIFT))
+
+#define QUADSPI_LUT1(ins, pad, opr) \
+		(QUADSPI_LUT0((ins), (pad), (opr)) << OPRND1_SHIFT)
+
+/* other macros for LUT register. */
+#define QUADSPI_LUT(x)          (QUADSPI_LUT_BASE + (x) * 4)
+#define QUADSPI_LUT_NUM		64
+
+/* SEQID */
+#define SEQID_QUAD_READ		0
+#define SEQID_WREN		1
+#define SEQID_FAST_READ 	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
+
+struct fsl_qspi_handler {
+	int (*setup)(struct spi_device *);
+	int (*do_one_msg)(struct spi_master *, struct spi_message *);
+};
+
+enum fsl_qspi_devtype {
+	FSL_QUADSPI_VYBRID,
+	FSL_QUADSPI_IMX6SLX
+};
+
+struct fsl_qspi_devtype_data {
+	enum fsl_qspi_devtype devtype;
+	u32 memmap_base;
+	int rxfifo;
+	int txfifo;
+};
+
+static struct fsl_qspi_devtype_data vybrid_data = {
+	.devtype = FSL_QUADSPI_VYBRID,
+	.memmap_base = 0x20000000,
+	.rxfifo = 128,
+	.txfifo = 64
+};
+
+struct fsl_qspi {
+	void __iomem *iobase;
+	struct clk *clk, *clk_en;
+	struct device *dev;
+	struct fsl_qspi_handler *h;
+	struct completion c;
+	struct fsl_qspi_devtype_data *devtype_data;
+	void __iomem *ahb_base; /* Used when read from AHB bus */
+	unsigned int addr;
+	u32 nor_size; /* for mapping */
+	u8 cmd;
+	unsigned int quad_read_enabled:1;
+};
+
+static inline int is_vybrid_qspi(struct fsl_qspi *q)
+{
+	return q->devtype_data->devtype == FSL_QUADSPI_VYBRID;
+}
+
+/*
+ * An IC bug makes us to re-arrange the 32-bit data.
+ * The following chips, such as IMX6SLX, have fixed this bug.
+ */
+static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
+{
+	return is_vybrid_qspi(q) ? __swab32(a) : a;
+}
+
+static inline void qspi_unlock_lut(struct fsl_qspi *q)
+{
+	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+	writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
+}
+
+static inline void qspi_lock_lut(struct fsl_qspi *q)
+{
+	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+	writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
+}
+
+static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id)
+{
+	struct fsl_qspi *q = dev_id;
+	u32 reg;
+
+	/* clear interrupt */
+	reg = readl(q->iobase + QUADSPI_FR);
+	writel(reg, q->iobase + QUADSPI_FR);
+
+	if (reg & QUADSPI_FR_TFF_MASK)
+		complete(&q->c);
+
+	dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x\n", reg);
+	return IRQ_HANDLED;
+}
+
+/* Init the LUT table. */
+static void fsl_qspi_init_lut(struct fsl_qspi *q)
+{
+	void *__iomem base = q->iobase;
+	int rxfifo = q->devtype_data->rxfifo;
+	u32 lut_base;
+	int i;
+
+	qspi_unlock_lut(q);
+
+	/* Clear all the LUT table */
+	for (i = 0; i < QUADSPI_LUT_NUM; i++)
+		writel(0, base + QUADSPI_LUT_BASE + i * 4);
+
+	/* Quad Read */
+	lut_base = SEQID_QUAD_READ * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_QIOR)
+			| QUADSPI_LUT1(ADDR, PAD4, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+	writel(QUADSPI_LUT0(MODE, PAD4, 0xff) | QUADSPI_LUT1(DUMMY, PAD4, 4),
+			base + QUADSPI_LUT(lut_base + 1));
+	writel(QUADSPI_LUT0(READ, PAD4, rxfifo),
+			base + QUADSPI_LUT(lut_base + 2));
+
+	/* Write enable */
+	lut_base = SEQID_WREN * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WREN),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Fast Read */
+	lut_base = SEQID_FAST_READ * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_FAST_READ)
+			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+	writel(QUADSPI_LUT0(DUMMY, PAD1, 8) | QUADSPI_LUT1(READ, PAD1, rxfifo),
+			base + QUADSPI_LUT(lut_base + 1));
+
+	/* Page Program */
+	lut_base = SEQID_PP * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_PP)
+			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+	writel(QUADSPI_LUT0(WRITE, PAD1, 0),
+			base + QUADSPI_LUT(lut_base + 1));
+
+	/* Read Status */
+	lut_base = SEQID_RDSR * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDSR)
+			| QUADSPI_LUT1(READ, PAD1, 0x1),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Erase a sector */
+	lut_base = SEQID_SE * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_SE)
+			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Erase the whole chip */
+	lut_base = SEQID_CHIP_ERASE * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_CHIP_ERASE),
+			base + QUADSPI_LUT(lut_base));
+
+	/* READ ID */
+	lut_base = SEQID_RDID * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDID)
+			| QUADSPI_LUT1(READ, PAD1, 0x8),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Write Register */
+	lut_base = SEQID_WRSR * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WRSR)
+			| QUADSPI_LUT1(WRITE, PAD1, 0x2),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Read Configuration Register */
+	lut_base = SEQID_RDCR * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDCR)
+			| QUADSPI_LUT1(READ, PAD1, 0x1),
+			base + QUADSPI_LUT(lut_base));
+	qspi_lock_lut(q);
+}
+
+/* Get the SEQID for the command */
+static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
+{
+	switch (cmd) {
+	case OPCODE_WREN:
+		return SEQID_WREN;
+	case OPCODE_RDSR:
+		return SEQID_RDSR;
+	case OPCODE_SE:
+		return SEQID_SE;
+	case OPCODE_CHIP_ERASE:
+		return SEQID_CHIP_ERASE;
+	case OPCODE_PP:
+		return SEQID_PP;
+	case OPCODE_RDID:
+		return SEQID_RDID;
+	case OPCODE_WRSR:
+		return SEQID_WRSR;
+	case OPCODE_RDCR:
+		return SEQID_RDCR;
+	default:
+		dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
+		break;
+	}
+	return -1;
+}
+
+static int
+fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
+{
+	int seqid;
+	u32 reg;
+	int err;
+
+	init_completion(&q->c);
+	dev_dbg(q->dev, "to @%.8x, len:%d, cmd:%.2x\n", addr, len, cmd);
+
+	/* save the reg */
+	reg = readl(q->iobase + QUADSPI_MCR);
+
+	writel(q->devtype_data->memmap_base + addr, q->iobase + QUADSPI_SFAR);
+	writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
+			q->iobase + QUADSPI_RBCT);
+	writel(QUADSPI_MCR_CLR_RXF_MASK | QUADSPI_MCR_RESERVED_MASK,
+			q->iobase + QUADSPI_MCR);
+
+	/* trigger the LUT now */
+	seqid = fsl_qspi_get_seqid(q, cmd);
+	writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len,
+		q->iobase + QUADSPI_IPCR);
+
+	/* Wait for the interrupt. */
+	err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000));
+	if (!err) {
+		dev_err(q->dev,
+			"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
+			cmd, addr, readl(q->iobase + QUADSPI_FR),
+			readl(q->iobase + QUADSPI_SR));
+		err = -ETIMEDOUT;
+	} else {
+		err = 0;
+	}
+
+	/* restore the MCR */
+	writel(reg, q->iobase + QUADSPI_MCR);
+
+	return err;
+}
+
+/* Get the address from the tx buffer. */
+static unsigned int
+fsl_qspi_get_addr(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	unsigned int addr;
+	u8 *buf = (u8 *)t->tx_buf;
+
+	/* 3-byte address */
+	if (q->nor_size <= SZ_16M)
+		addr = (buf[1] << 16) | (buf[2] << 8) |  buf[3];
+	return addr;
+}
+
+/* Read out the data from the buffer registers. */
+static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u32 *rxbuf)
+{
+	u32 tmp;
+	int i = 0;
+
+	while (len > 0) {
+		tmp = readl(q->iobase + QUADSPI_RBDR + i * 4);
+		*rxbuf = fsl_qspi_endian_xchg(q, tmp);
+		dev_dbg(q->dev, "rcv: 0x%.8x, tmp : 0x%.8x\n", *rxbuf, tmp);
+
+		rxbuf++;
+		len -= 4;
+		i++;
+	}
+}
+
+/* Read out the data directly from the AHB buffer.*/
+static int fsl_qspi_read_data_ahb(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	dev_dbg(q->dev, "cmd [%x],read from 0x%.8x,len:%d\n",
+		q->cmd, q->addr, t->len);
+	memcpy(t->rx_buf, q->ahb_base + q->addr, t->len);
+	return 0;
+}
+
+static u32 fsl_qspi_read_sr(struct fsl_qspi *q)
+{
+	u32 val = -EINVAL;
+	int ret;
+
+	ret = fsl_qspi_runcmd(q, OPCODE_RDSR, 0, 1);
+	if (!ret)
+		fsl_qspi_read_data(q, 1, &val);
+	return val;
+}
+
+static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
+{
+	unsigned long deadline;
+	u32 sr;
+
+	deadline = jiffies + msecs_to_jiffies(40000);
+
+	do {
+		if ((sr = fsl_qspi_read_sr(q)) < 0)
+			break;
+		else if (!(sr & SR_WIP))
+			return 0;
+
+		cond_resched();
+
+	} while (!time_after_eq(jiffies, deadline));
+
+	return 1;
+}
+
+/*
+ * If we have changed the content of the flash by writing or erasing,
+ * we need to invalidate the AHB buffer. If we do not do so, we may read out
+ * the wrong data. The spec tells us reset the AHB domain and Serial Flash
+ * domain at the same time.
+ */
+static inline void fsl_qspi_invalid(struct fsl_qspi *q)
+{
+	u32 reg;
+
+	reg = readl(q->iobase + QUADSPI_MCR);
+	reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
+	writel(reg, q->iobase + QUADSPI_MCR);
+
+	/*
+	 * The minimum delay : 1 AHB + 2 SFCK clocks.
+	 * Delay 1 us is enough.
+	 */
+	udelay(1);
+
+	reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
+	writel(reg, q->iobase + QUADSPI_MCR);
+}
+
+static int fsl_qspi_nor_write(struct fsl_qspi *q, u32 *txbuf, unsigned count)
+{
+	unsigned int addr = q->addr;
+	int txfifo_size = q->devtype_data->txfifo;
+	int ret = 0;
+	int tx_size;
+	u32 tmp;
+	int i, j;
+	u8 cmd = q->cmd;
+
+	q->cmd = -1; /* clear the cmd */
+	dev_dbg(q->dev, "to @%.8x, len : %d\n", addr, count);
+
+	while (count > 0) {
+		tx_size = (count > txfifo_size) ? txfifo_size : count;
+
+		/* clear the TX FIFO. */
+		tmp = readl(q->iobase + QUADSPI_MCR);
+		writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR);
+
+		/* fill the TX data to the FIFO */
+		for (j = 0, i = ((tx_size + 3) / 4); j < i; j++) {
+			tmp = fsl_qspi_endian_xchg(q, *txbuf);
+			writel(tmp, q->iobase + QUADSPI_TBDR);
+			txbuf++;
+		}
+
+		/* Trigger it */
+		ret = fsl_qspi_runcmd(q, cmd, addr, tx_size);
+
+		addr += tx_size;
+		count -= tx_size;
+
+		/*
+		 * If the TX FIFO is smaller then the size of Page Program,
+		 * we have to wait until this Write is finished.
+		 * For example, the TX FIFO is 64 bytes in the Vybrid,
+		 * but the Page Program may writes 265 bytes per time.
+		 * We are lucky that some chip(IMX6SLX) has increase the TX FIFO
+		 * to 512 bytes.
+		 *
+		 * If we can change the @m25p->page_size, we can remove the
+		 * following code.
+		 */
+		if (count > 0) {
+			ret = fsl_qspi_wait_till_ready(q);
+			if (ret) {
+				dev_err(q->dev, "Reading SR, err:%d\n", ret);
+				break;
+			}
+
+			/* Write Enable again. */
+			ret = fsl_qspi_runcmd(q, OPCODE_WREN, 0, 0);
+			if (ret) {
+				dev_err(q->dev, "Write Enable, err:%d\n", ret);
+				break;
+			}
+		}
+	}
+	return ret;
+}
+
+/* Switch to Quad read now. */
+static inline void fsl_qspi_enable_quad_read(struct fsl_qspi *q)
+{
+	writel(SEQID_QUAD_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
+		q->iobase + QUADSPI_BFGENCR);
+	q->quad_read_enabled = 1;
+}
+
+static int fsl_qspi_nor_tx(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	unsigned int addr = 0;
+	bool need_invalid = false;
+	int ret = 0;
+	u32 val;
+	u8 cmd;
+
+	/* This is the second spi_transfer for Page Program. */
+	if (q->cmd == OPCODE_PP) {
+		ret = fsl_qspi_nor_write(q, (u32 *)t->tx_buf, t->len);
+		need_invalid = true;
+		goto qspi_tx_out;
+	}
+
+	cmd = *(u8 *)t->tx_buf;
+	dev_dbg(q->dev, "NOR cmd is [0x%.2x], len : %d.\n", cmd, t->len);
+
+	switch (cmd) {
+	case OPCODE_SE:
+		addr = fsl_qspi_get_addr(q, t);
+		/* fall through */
+	case OPCODE_CHIP_ERASE:
+		need_invalid = true;
+	case OPCODE_WREN:
+		ret = fsl_qspi_runcmd(q, cmd, addr, 0);
+		q->cmd = -1;
+		break;
+
+	case OPCODE_QIOR:
+		if (!q->quad_read_enabled)
+			fsl_qspi_enable_quad_read(q);
+		/* fall through */
+	case OPCODE_FAST_READ:
+	case OPCODE_PP:
+		q->cmd = cmd;
+		q->addr = fsl_qspi_get_addr(q, t);
+		break;
+
+	case OPCODE_WRSR:
+		q->addr = 0;
+		q->cmd = cmd;
+		/* skip the cmd */
+		memcpy((void *) &val, ((u8 *)t->tx_buf) + 1, t->len -1);
+		ret = fsl_qspi_nor_write(q, &val, t->len - 1);
+		break;
+
+	default:
+		q->cmd = cmd;
+		break;
+	}
+
+qspi_tx_out:
+	if (need_invalid)
+		fsl_qspi_invalid(q);
+	return ret;
+}
+
+static int fsl_qspi_nor_rx(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	int ret = 0;
+
+	switch (q->cmd) {
+	case OPCODE_RDSR:
+	case OPCODE_RDCR:
+	case OPCODE_RDID:
+		ret = fsl_qspi_runcmd(q, q->cmd, 0, t->len);
+		if (!ret)
+			fsl_qspi_read_data(q, t->len, t->rx_buf);
+		break;
+
+	case OPCODE_QIOR:
+	case OPCODE_FAST_READ:
+		ret = fsl_qspi_read_data_ahb(q, t);
+		break;
+	default:
+		dev_err(q->dev, "Unsupported cmd : %x\n", q->cmd);
+		return -EINVAL;
+	}
+	return ret;
+}
+
+static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
+		struct spi_message *m)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(master);
+	struct spi_transfer *t;
+	int ret = 0;
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->rx_buf && t->tx_buf) {
+			dev_err(q->dev,
+				"Can't send and receive simultaneously\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		if (t->tx_buf) {
+			ret = fsl_qspi_nor_tx(q, t);
+			if (!ret)
+				m->actual_length += t->len;
+			continue;
+		}
+
+		if (t->rx_buf) {
+			ret = fsl_qspi_nor_rx(q, t);
+			if (!ret)
+				m->actual_length += t->len;
+		}
+	}
+
+	m->status = ret;
+	spi_finalize_current_message(master);
+	return ret;
+}
+
+/*
+ * There are two different ways to read out the data from the flash:
+ *  the "IP Command Read" and the "AHB Command Read".
+ *
+ * The IC guy suggests we use the "AHB Command Read" which is faster
+ * then the "IP Command Read". (What's more is that there is a bug in
+ * the "IP Command Read" in the Vybrid.)
+ *
+ * After we set up the registers for the "AHB Command Read", we can use
+ * the memcpy to read the data directly. A "missed" access to the buffer
+ * causes the controller to clear the buffer, and use the sequence pointed
+ * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash.
+ */
+static int fsl_qspi_init_abh_read(struct fsl_qspi *q)
+{
+	void __iomem *base = q->iobase;
+	u32 memmap_base = q->devtype_data->memmap_base;
+	int nor_size = q->nor_size;
+
+	/* Map the SPI NOR to accessiable address */
+	writel(nor_size | memmap_base, base + QUADSPI_SFA1AD);
+	writel(nor_size | memmap_base, base + QUADSPI_SFA2AD);
+	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB1AD);
+	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB2AD);
+
+	/* AHB configuration for access buffer 0/1/2 .*/
+	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
+	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
+	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
+	writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR);
+
+	/* We only use the buffer3 */
+	writel(0, base + QUADSPI_BUF0IND);
+	writel(0, base + QUADSPI_BUF1IND);
+	writel(0, base + QUADSPI_BUF2IND);
+
+	/* Set the default lut sequence for AHB Read. */
+	writel(SEQID_FAST_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
+		base + QUADSPI_BFGENCR);
+
+	/* Map the AHB address for read. */
+	q->ahb_base = ioremap(memmap_base, nor_size);
+	if (!q->ahb_base)
+		return -ENOMEM;
+	return 0;
+}
+
+static int fsl_qspi_nor_setup(struct spi_device *spi)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
+	void __iomem *base = q->iobase;
+	u32 reg_val, smpr_val;
+	int ret;
+
+	writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_MDIS_MASK,
+		base + QUADSPI_MCR);
+
+	reg_val = readl(base + QUADSPI_SMPR);
+	writel(reg_val & ~(QUADSPI_SMPR_FSDLY_MASK
+			| QUADSPI_SMPR_FSPHS_MASK
+			| QUADSPI_SMPR_HSENA_MASK), base + QUADSPI_SMPR);
+
+	writel(QUADSPI_MCR_RESERVED_MASK, base + QUADSPI_MCR);
+
+	fsl_qspi_init_lut(q);
+	ret = fsl_qspi_init_abh_read(q);
+	if (ret < 0)
+		return ret;
+
+	reg_val = 0;
+	reg_val |= QUADSPI_MCR_RESERVED_MASK;
+	smpr_val = readl(base + QUADSPI_SMPR);
+	smpr_val &= ~QUADSPI_SMPR_DDRSMP_MASK;
+	writel(smpr_val, base + QUADSPI_SMPR);
+	reg_val &= ~QUADSPI_MCR_DDR_EN_MASK;
+	writel(reg_val, base + QUADSPI_MCR);
+
+	/* enable the interrupt */
+	writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
+	return 0;
+}
+
+/* We only support the NOR now. */
+static struct fsl_qspi_handler fsl_qspi_nor_handler = {
+	.setup = fsl_qspi_nor_setup,
+	.do_one_msg = fsl_qspi_nor_do_one_msg,
+};
+
+static int fsl_qspi_setup(struct spi_device *spi)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
+
+	return q->h->setup(spi);
+}
+
+static int fsl_qspi_do_one_msg(struct spi_master *master,
+		struct spi_message *m)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(master);
+
+	return q->h->do_one_msg(master, m);
+}
+
+static struct of_device_id fsl_qspi_dt_ids[] = {
+	{ .compatible = "fsl,vf610-qspi", .data = (void*)&vybrid_data, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
+
+static int fsl_qspi_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct spi_master *master;
+	struct fsl_qspi *q;
+	struct resource *res;
+	int num_cs, ret = 0;
+	const struct of_device_id *of_id =
+			of_match_device(fsl_qspi_dt_ids, &pdev->dev);
+
+	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get the spi-mum-chipselects\n");
+		return ret;
+	}
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*q));
+	if (!master)
+		return -ENOMEM;
+	q = spi_master_get_devdata(master);
+
+	ret = of_property_read_u32(np, "fsl,nor-size", &q->nor_size);
+	if (ret < 0)
+		dev_dbg(&pdev->dev, "can't get the nor size.\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	q->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(q->iobase)) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = PTR_ERR(q->iobase);
+		goto map_failed;
+	}
+
+	q->clk_en = devm_clk_get(&pdev->dev, "qspi_en");
+	q->clk = devm_clk_get(&pdev->dev, "qspi");
+	if (IS_ERR(q->clk_en) || IS_ERR(q->clk)) {
+		dev_err(&pdev->dev, "failed to get clocks\n");
+		ret = -ENOENT;
+		goto map_failed;
+	}
+
+	ret = clk_prepare_enable(q->clk_en);
+	if (ret) {
+		dev_err(&pdev->dev, "can not enable the qspi_en clock\n");
+		goto map_failed;
+	}
+
+	ret = clk_prepare_enable(q->clk);
+	if (ret) {
+		clk_disable_unprepare(q->clk_en);
+		dev_err(&pdev->dev, "can not enable the qspi clock\n");
+		goto map_failed;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get the irq\n");
+		goto irq_failed;
+	}
+
+	ret = devm_request_irq(&pdev->dev, ret,
+			fsl_qspi_irq_handler, 0, pdev->name, q);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request irq.\n");
+		goto irq_failed;
+	}
+
+	q->dev = &pdev->dev;
+	q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
+
+	/* The default handler is for NOR. */
+	q->h = &fsl_qspi_nor_handler;
+
+	master->bus_num = pdev->id;
+	master->num_chipselect = num_cs;
+	master->dev.of_node = pdev->dev.of_node;
+
+	master->setup = fsl_qspi_setup;
+	master->transfer_one_message = fsl_qspi_do_one_msg;
+	platform_set_drvdata(pdev, master);
+
+	ret = spi_register_master(master);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register the spi master.\n");
+		goto irq_failed;
+	}
+	dev_info(&pdev->dev, "QuadSPI bus driver\n");
+	return 0;
+
+irq_failed:
+	clk_disable_unprepare(q->clk);
+	clk_disable_unprepare(q->clk_en);
+map_failed:
+	spi_master_put(master);
+
+	dev_err(&pdev->dev, "Freescale QuadSPI probe failed\n");
+	return ret;
+}
+
+static int fsl_qspi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct fsl_qspi *q = spi_master_get_devdata(master);
+
+	/* disable the hardware */
+	writel(0x0, q->iobase + QUADSPI_MCR);
+	writel(0x0, q->iobase + QUADSPI_RSER);
+
+	clk_disable_unprepare(q->clk);
+	clk_disable_unprepare(q->clk_en);
+	spi_master_put(master);
+	return 0;
+}
+
+static struct platform_driver fsl_qspi_driver = {
+	.driver = {
+		.name	= "fsl-quadspi",
+		.owner	= THIS_MODULE,
+		.of_match_table = fsl_qspi_dt_ids,
+	},
+	.probe          = fsl_qspi_probe,
+	.remove		= fsl_qspi_remove,
+};
+module_platform_driver(fsl_qspi_driver);
+
+MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
@ 2013-08-19  4:10   ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: linux-arm-kernel

(0) What is the Quadspi controller?

    The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
    one single or two external serial flash devices, each with up to 4
    bidirectional data lines.

(1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
    The LUT registers are a look-up-table for sequences of instructions.
    A valid sequence consists of four LUT registers.

(2) The definition of the LUT register shows below:

    ---------------------------------------------------
    | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
    ---------------------------------------------------

    There are several types of INSTRx, such as:
    	CMD	: the SPI NOR command.
	ADDR	: the address for the SPI NOR command.
	DUMMY	: the dummy cycles needed by the SPI NOR command.
	....

(3) We connect the NOR the QuadSPI now. I am not sure, but i think the
    QuadSPI will be only used for the NOR. We may connect other devices
    to it. But, for the reason of (2), we have to parse out the SPI NOR
    command for the QuadSPI.

(4) Test this driver with the JFFS2 and UBIFS:

    For jffs2:
         #flash_eraseall /dev/mtd0
         #mount -t jffs2 /dev/mtdblock0 tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

    For ubifs:
         #flash_eraseall /dev/mtd0
    	 #ubiattach /dev/ubi_ctrl -m 0
    	 #ubimkvol /dev/ubi0 -N test -m
         #mount -t ubifs ubi0:test tmp
         #bonnie++ -d tmp -u 0 -s 10 -r 5

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 .../devicetree/bindings/spi/fsl-quadspi.txt        |   27 +
 drivers/spi/Kconfig                                |    7 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-fsl-quadspi.c                      |  930 ++++++++++++++++++++
 4 files changed, 965 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/fsl-quadspi.txt
 create mode 100644 drivers/spi/spi-fsl-quadspi.c

diff --git a/Documentation/devicetree/bindings/spi/fsl-quadspi.txt b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
new file mode 100644
index 0000000..e5bfa82
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
@@ -0,0 +1,27 @@
+* Freescale Quad Serial Peripheral Interface(QuadSPI)
+
+Required properties:
+- compatible : Should be "fsl,vf610-qspi"
+- reg : Offset and length of the register set for the device
+- interrupts : Should contain the interrupt for the device
+- fsl,spi-num-chipselects : Contains the number of the chipselect
+- clocks : The clocks needed by the QuadSPI controller
+- clock-names : the name of the clocks
+
+Optional properties:
+- fsl,nor-size : The NOR size used by the QuadSPI mapping.
+
+Example:
+
+qspi0: quadspi at 40044000 {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	compatible = "fsl,vf610-qspi";
+	reg = <0x40044000 0x1000>;
+	interrupts = <0 24 0x04>;
+	clocks = <&clks VF610_CLK_QSPI0_EN>,
+		<&clks VF610_CLK_QSPI0>;
+	clock-names = "qspi_en", "qspi";
+	fsl,nor-size = <0x1000000>;
+	status = "disabled";
+};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 92b2373..dc38063 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -187,6 +187,13 @@ config SPI_FALCON
 	  has only been tested with m25p80 type chips. The hardware has no
 	  support for other types of SPI peripherals.
 
+config SPI_FSL_QUADSPI
+	tristate "Freescale Quad SPI controller"
+	depends on ARCH_MXC
+	help
+	  This enables support for the Quad SPI controller in master mode.
+	  We only connect the NOR to this controller now.
+
 config SPI_GPIO
 	tristate "GPIO-based bitbanging SPI Master"
 	depends on GPIOLIB
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index b25f385..7fe505c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_SPI_FSL_ESPI)		+= spi-fsl-espi.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi-fsl-spi.o
 obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
+obj-$(CONFIG_SPI_FSL_QUADSPI)           += spi-fsl-quadspi.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
diff --git a/drivers/spi/spi-fsl-quadspi.c b/drivers/spi/spi-fsl-quadspi.c
new file mode 100644
index 0000000..de71a4e
--- /dev/null
+++ b/drivers/spi/spi-fsl-quadspi.c
@@ -0,0 +1,930 @@
+/*
+ * Freescale Quad SPI driver.
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/completion.h>
+#include <linux/mtd/spi-nor.h>
+
+/* The registers */
+#define QUADSPI_MCR			0x00
+#define QUADSPI_MCR_MDIS_SHIFT		14
+#define QUADSPI_MCR_MDIS_MASK		(1 << QUADSPI_MCR_MDIS_SHIFT)
+#define QUADSPI_MCR_CLR_TXF_SHIFT	11
+#define QUADSPI_MCR_CLR_TXF_MASK	(1 << QUADSPI_MCR_CLR_TXF_SHIFT)
+#define QUADSPI_MCR_CLR_RXF_SHIFT	10
+#define QUADSPI_MCR_CLR_RXF_MASK	(1 << QUADSPI_MCR_CLR_RXF_SHIFT)
+#define QUADSPI_MCR_DDR_EN_SHIFT	7
+#define QUADSPI_MCR_DDR_EN_MASK		(1 << QUADSPI_MCR_DDR_EN_SHIFT)
+#define QUADSPI_MCR_RESERVED_SHIFT	16
+#define QUADSPI_MCR_RESERVED_MASK	(0xF << QUADSPI_MCR_RESERVED_SHIFT)
+#define QUADSPI_MCR_SWRSTHD_SHIFT	1
+#define QUADSPI_MCR_SWRSTHD_MASK	(1 << QUADSPI_MCR_SWRSTHD_SHIFT)
+#define QUADSPI_MCR_SWRSTSD_SHIFT	0
+#define QUADSPI_MCR_SWRSTSD_MASK	(1 << QUADSPI_MCR_SWRSTSD_SHIFT)
+
+#define QUADSPI_IPCR			0x08
+#define QUADSPI_IPCR_SEQID_SHIFT	24
+#define QUADSPI_IPCR_SEQID_MASK		(0xF << QUADSPI_IPCR_SEQID_SHIFT)
+
+#define QUADSPI_BUF0CR			0x10
+#define QUADSPI_BUF1CR			0x14
+#define QUADSPI_BUF2CR			0x18
+#define QUADSPI_BUFXCR_INVALID_MSTRID	0xe
+
+#define QUADSPI_BUF3CR			0x1c
+#define QUADSPI_BUF3CR_ALLMST_SHIFT	31
+#define QUADSPI_BUF3CR_ALLMST		(1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
+
+#define QUADSPI_BFGENCR			0x20
+#define QUADSPI_BFGENCR_PAR_EN_SHIFT	16
+#define QUADSPI_BFGENCR_PAR_EN_MASK	(1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT))
+#define QUADSPI_BFGENCR_SEQID_SHIFT	12
+#define QUADSPI_BFGENCR_SEQID_MASK	(0xF << QUADSPI_BFGENCR_SEQID_SHIFT)
+
+#define QUADSPI_BUF0IND			0x30
+#define QUADSPI_BUF1IND			0x34
+#define QUADSPI_BUF2IND			0x38
+#define QUADSPI_SFAR			0x100
+
+#define QUADSPI_SMPR			0x108
+#define QUADSPI_SMPR_DDRSMP_SHIFT	16
+#define QUADSPI_SMPR_DDRSMP_MASK	(7 << QUADSPI_SMPR_DDRSMP_SHIFT)
+#define QUADSPI_SMPR_FSDLY_SHIFT	6
+#define QUADSPI_SMPR_FSDLY_MASK		(1 << QUADSPI_SMPR_FSDLY_SHIFT)
+#define QUADSPI_SMPR_FSPHS_SHIFT	5
+#define QUADSPI_SMPR_FSPHS_MASK		(1 << QUADSPI_SMPR_FSPHS_SHIFT)
+#define QUADSPI_SMPR_HSENA_SHIFT	0
+#define QUADSPI_SMPR_HSENA_MASK		(1 << QUADSPI_SMPR_HSENA_SHIFT)
+
+#define QUADSPI_RBSR			0x10c
+#define QUADSPI_RBSR_RDBFL_SHIFT	8
+#define QUADSPI_RBSR_RDBFL_MASK		(0x3F << QUADSPI_RBSR_RDBFL_SHIFT)
+
+#define QUADSPI_RBCT			0x110
+#define QUADSPI_RBCT_WMRK_MASK		0x1F
+#define QUADSPI_RBCT_RXBRD_SHIFT	8
+#define QUADSPI_RBCT_RXBRD_USEIPS	(0x1 << QUADSPI_RBCT_RXBRD_SHIFT)
+
+#define QUADSPI_TBSR			0x150
+#define QUADSPI_TBDR			0x154
+
+#define QUADSPI_SR			0x15c
+#define QUADSPI_SR_TXFULL_SHIFT		27
+#define QUADSPI_SR_TXFULL_MASK		(1 << QUADSPI_SR_TXFULL_SHIFT)
+#define QUADSPI_SR_AHBTRN_SHIFT		6
+#define QUADSPI_SR_AHBTRN_MASK		(1 << QUADSPI_SR_AHBTRN_SHIFT)
+#define QUADSPI_SR_AHB_ACC_SHIFT	2
+#define QUADSPI_SR_AHB_ACC_MASK		(1 << QUADSPI_SR_AHB_ACC_SHIFT)
+#define QUADSPI_SR_IP_ACC_SHIFT		1
+#define QUADSPI_SR_IP_ACC_MASK		(1 << QUADSPI_SR_IP_ACC_SHIFT)
+#define QUADSPI_SR_BUSY_SHIFT		0
+#define QUADSPI_SR_BUSY_MASK		(1 << QUADSPI_SR_BUSY_SHIFT)
+
+#define QUADSPI_FR			0x160
+#define QUADSPI_FR_TFF_MASK		0x1
+
+#define QUADSPI_SFA1AD			0x180
+#define QUADSPI_SFA2AD			0x184
+#define QUADSPI_SFB1AD			0x188
+#define QUADSPI_SFB2AD			0x18c
+#define QUADSPI_RBDR			0x200
+
+#define QUADSPI_LUTKEY			0x300
+#define QUADSPI_LUTKEY_VALUE		0x5AF05AF0
+
+#define QUADSPI_LCKCR			0x304
+#define QUADSPI_LCKER_LOCK		0x1
+#define QUADSPI_LCKER_UNLOCK		0x2
+
+#define QUADSPI_RSER			0x164
+#define QUADSPI_RSER_TFIE       	(0x1 << 0)
+
+#define QUADSPI_LUT_BASE		0x310
+
+/* Field definitions for LUT register. */
+#define OPRND0_SHIFT		0
+#define PAD0_SHIFT		8
+#define INSTR0_SHIFT		10
+#define OPRND1_SHIFT		16
+
+/* Instruction set for the LUT register. */
+#define CMD			1
+#define ADDR			2
+#define DUMMY			3
+#define MODE			4
+#define MODE2			5
+#define MODE4			6
+#define READ			7
+#define WRITE			8
+#define JMP_ON_CS		9
+#define ADDR_DDR		10
+#define MODE_DDR		11
+#define MODE2_DDR		12
+#define MODE4_DDR		13
+
+/*
+ * The PAD definitions for LUT register.
+ *
+ * The pad stands for the lines number of IO[0:3].
+ * For example, the Quad read need four IO lines, so you should
+ * set PAD4 which means we use four IO lines.
+ */
+#define PAD1			0
+#define PAD2			1
+#define PAD4			2
+
+/* Oprands for the LUT register. */
+#define ADDR24BIT		0x18
+
+/* Macros for constructing the LUT register. */
+#define QUADSPI_LUT0(ins, pad, opr)					\
+		(((opr) << OPRND0_SHIFT) | ((pad) << PAD0_SHIFT) |	\
+		((ins) << INSTR0_SHIFT))
+
+#define QUADSPI_LUT1(ins, pad, opr) \
+		(QUADSPI_LUT0((ins), (pad), (opr)) << OPRND1_SHIFT)
+
+/* other macros for LUT register. */
+#define QUADSPI_LUT(x)          (QUADSPI_LUT_BASE + (x) * 4)
+#define QUADSPI_LUT_NUM		64
+
+/* SEQID */
+#define SEQID_QUAD_READ		0
+#define SEQID_WREN		1
+#define SEQID_FAST_READ 	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
+
+struct fsl_qspi_handler {
+	int (*setup)(struct spi_device *);
+	int (*do_one_msg)(struct spi_master *, struct spi_message *);
+};
+
+enum fsl_qspi_devtype {
+	FSL_QUADSPI_VYBRID,
+	FSL_QUADSPI_IMX6SLX
+};
+
+struct fsl_qspi_devtype_data {
+	enum fsl_qspi_devtype devtype;
+	u32 memmap_base;
+	int rxfifo;
+	int txfifo;
+};
+
+static struct fsl_qspi_devtype_data vybrid_data = {
+	.devtype = FSL_QUADSPI_VYBRID,
+	.memmap_base = 0x20000000,
+	.rxfifo = 128,
+	.txfifo = 64
+};
+
+struct fsl_qspi {
+	void __iomem *iobase;
+	struct clk *clk, *clk_en;
+	struct device *dev;
+	struct fsl_qspi_handler *h;
+	struct completion c;
+	struct fsl_qspi_devtype_data *devtype_data;
+	void __iomem *ahb_base; /* Used when read from AHB bus */
+	unsigned int addr;
+	u32 nor_size; /* for mapping */
+	u8 cmd;
+	unsigned int quad_read_enabled:1;
+};
+
+static inline int is_vybrid_qspi(struct fsl_qspi *q)
+{
+	return q->devtype_data->devtype == FSL_QUADSPI_VYBRID;
+}
+
+/*
+ * An IC bug makes us to re-arrange the 32-bit data.
+ * The following chips, such as IMX6SLX, have fixed this bug.
+ */
+static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
+{
+	return is_vybrid_qspi(q) ? __swab32(a) : a;
+}
+
+static inline void qspi_unlock_lut(struct fsl_qspi *q)
+{
+	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+	writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
+}
+
+static inline void qspi_lock_lut(struct fsl_qspi *q)
+{
+	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+	writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
+}
+
+static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id)
+{
+	struct fsl_qspi *q = dev_id;
+	u32 reg;
+
+	/* clear interrupt */
+	reg = readl(q->iobase + QUADSPI_FR);
+	writel(reg, q->iobase + QUADSPI_FR);
+
+	if (reg & QUADSPI_FR_TFF_MASK)
+		complete(&q->c);
+
+	dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x\n", reg);
+	return IRQ_HANDLED;
+}
+
+/* Init the LUT table. */
+static void fsl_qspi_init_lut(struct fsl_qspi *q)
+{
+	void *__iomem base = q->iobase;
+	int rxfifo = q->devtype_data->rxfifo;
+	u32 lut_base;
+	int i;
+
+	qspi_unlock_lut(q);
+
+	/* Clear all the LUT table */
+	for (i = 0; i < QUADSPI_LUT_NUM; i++)
+		writel(0, base + QUADSPI_LUT_BASE + i * 4);
+
+	/* Quad Read */
+	lut_base = SEQID_QUAD_READ * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_QIOR)
+			| QUADSPI_LUT1(ADDR, PAD4, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+	writel(QUADSPI_LUT0(MODE, PAD4, 0xff) | QUADSPI_LUT1(DUMMY, PAD4, 4),
+			base + QUADSPI_LUT(lut_base + 1));
+	writel(QUADSPI_LUT0(READ, PAD4, rxfifo),
+			base + QUADSPI_LUT(lut_base + 2));
+
+	/* Write enable */
+	lut_base = SEQID_WREN * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WREN),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Fast Read */
+	lut_base = SEQID_FAST_READ * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_FAST_READ)
+			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+	writel(QUADSPI_LUT0(DUMMY, PAD1, 8) | QUADSPI_LUT1(READ, PAD1, rxfifo),
+			base + QUADSPI_LUT(lut_base + 1));
+
+	/* Page Program */
+	lut_base = SEQID_PP * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_PP)
+			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+	writel(QUADSPI_LUT0(WRITE, PAD1, 0),
+			base + QUADSPI_LUT(lut_base + 1));
+
+	/* Read Status */
+	lut_base = SEQID_RDSR * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDSR)
+			| QUADSPI_LUT1(READ, PAD1, 0x1),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Erase a sector */
+	lut_base = SEQID_SE * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_SE)
+			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Erase the whole chip */
+	lut_base = SEQID_CHIP_ERASE * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_CHIP_ERASE),
+			base + QUADSPI_LUT(lut_base));
+
+	/* READ ID */
+	lut_base = SEQID_RDID * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDID)
+			| QUADSPI_LUT1(READ, PAD1, 0x8),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Write Register */
+	lut_base = SEQID_WRSR * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WRSR)
+			| QUADSPI_LUT1(WRITE, PAD1, 0x2),
+			base + QUADSPI_LUT(lut_base));
+
+	/* Read Configuration Register */
+	lut_base = SEQID_RDCR * 4;
+	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDCR)
+			| QUADSPI_LUT1(READ, PAD1, 0x1),
+			base + QUADSPI_LUT(lut_base));
+	qspi_lock_lut(q);
+}
+
+/* Get the SEQID for the command */
+static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
+{
+	switch (cmd) {
+	case OPCODE_WREN:
+		return SEQID_WREN;
+	case OPCODE_RDSR:
+		return SEQID_RDSR;
+	case OPCODE_SE:
+		return SEQID_SE;
+	case OPCODE_CHIP_ERASE:
+		return SEQID_CHIP_ERASE;
+	case OPCODE_PP:
+		return SEQID_PP;
+	case OPCODE_RDID:
+		return SEQID_RDID;
+	case OPCODE_WRSR:
+		return SEQID_WRSR;
+	case OPCODE_RDCR:
+		return SEQID_RDCR;
+	default:
+		dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
+		break;
+	}
+	return -1;
+}
+
+static int
+fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
+{
+	int seqid;
+	u32 reg;
+	int err;
+
+	init_completion(&q->c);
+	dev_dbg(q->dev, "to @%.8x, len:%d, cmd:%.2x\n", addr, len, cmd);
+
+	/* save the reg */
+	reg = readl(q->iobase + QUADSPI_MCR);
+
+	writel(q->devtype_data->memmap_base + addr, q->iobase + QUADSPI_SFAR);
+	writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
+			q->iobase + QUADSPI_RBCT);
+	writel(QUADSPI_MCR_CLR_RXF_MASK | QUADSPI_MCR_RESERVED_MASK,
+			q->iobase + QUADSPI_MCR);
+
+	/* trigger the LUT now */
+	seqid = fsl_qspi_get_seqid(q, cmd);
+	writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len,
+		q->iobase + QUADSPI_IPCR);
+
+	/* Wait for the interrupt. */
+	err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000));
+	if (!err) {
+		dev_err(q->dev,
+			"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
+			cmd, addr, readl(q->iobase + QUADSPI_FR),
+			readl(q->iobase + QUADSPI_SR));
+		err = -ETIMEDOUT;
+	} else {
+		err = 0;
+	}
+
+	/* restore the MCR */
+	writel(reg, q->iobase + QUADSPI_MCR);
+
+	return err;
+}
+
+/* Get the address from the tx buffer. */
+static unsigned int
+fsl_qspi_get_addr(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	unsigned int addr;
+	u8 *buf = (u8 *)t->tx_buf;
+
+	/* 3-byte address */
+	if (q->nor_size <= SZ_16M)
+		addr = (buf[1] << 16) | (buf[2] << 8) |  buf[3];
+	return addr;
+}
+
+/* Read out the data from the buffer registers. */
+static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u32 *rxbuf)
+{
+	u32 tmp;
+	int i = 0;
+
+	while (len > 0) {
+		tmp = readl(q->iobase + QUADSPI_RBDR + i * 4);
+		*rxbuf = fsl_qspi_endian_xchg(q, tmp);
+		dev_dbg(q->dev, "rcv: 0x%.8x, tmp : 0x%.8x\n", *rxbuf, tmp);
+
+		rxbuf++;
+		len -= 4;
+		i++;
+	}
+}
+
+/* Read out the data directly from the AHB buffer.*/
+static int fsl_qspi_read_data_ahb(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	dev_dbg(q->dev, "cmd [%x],read from 0x%.8x,len:%d\n",
+		q->cmd, q->addr, t->len);
+	memcpy(t->rx_buf, q->ahb_base + q->addr, t->len);
+	return 0;
+}
+
+static u32 fsl_qspi_read_sr(struct fsl_qspi *q)
+{
+	u32 val = -EINVAL;
+	int ret;
+
+	ret = fsl_qspi_runcmd(q, OPCODE_RDSR, 0, 1);
+	if (!ret)
+		fsl_qspi_read_data(q, 1, &val);
+	return val;
+}
+
+static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
+{
+	unsigned long deadline;
+	u32 sr;
+
+	deadline = jiffies + msecs_to_jiffies(40000);
+
+	do {
+		if ((sr = fsl_qspi_read_sr(q)) < 0)
+			break;
+		else if (!(sr & SR_WIP))
+			return 0;
+
+		cond_resched();
+
+	} while (!time_after_eq(jiffies, deadline));
+
+	return 1;
+}
+
+/*
+ * If we have changed the content of the flash by writing or erasing,
+ * we need to invalidate the AHB buffer. If we do not do so, we may read out
+ * the wrong data. The spec tells us reset the AHB domain and Serial Flash
+ * domain@the same time.
+ */
+static inline void fsl_qspi_invalid(struct fsl_qspi *q)
+{
+	u32 reg;
+
+	reg = readl(q->iobase + QUADSPI_MCR);
+	reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
+	writel(reg, q->iobase + QUADSPI_MCR);
+
+	/*
+	 * The minimum delay : 1 AHB + 2 SFCK clocks.
+	 * Delay 1 us is enough.
+	 */
+	udelay(1);
+
+	reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
+	writel(reg, q->iobase + QUADSPI_MCR);
+}
+
+static int fsl_qspi_nor_write(struct fsl_qspi *q, u32 *txbuf, unsigned count)
+{
+	unsigned int addr = q->addr;
+	int txfifo_size = q->devtype_data->txfifo;
+	int ret = 0;
+	int tx_size;
+	u32 tmp;
+	int i, j;
+	u8 cmd = q->cmd;
+
+	q->cmd = -1; /* clear the cmd */
+	dev_dbg(q->dev, "to @%.8x, len : %d\n", addr, count);
+
+	while (count > 0) {
+		tx_size = (count > txfifo_size) ? txfifo_size : count;
+
+		/* clear the TX FIFO. */
+		tmp = readl(q->iobase + QUADSPI_MCR);
+		writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR);
+
+		/* fill the TX data to the FIFO */
+		for (j = 0, i = ((tx_size + 3) / 4); j < i; j++) {
+			tmp = fsl_qspi_endian_xchg(q, *txbuf);
+			writel(tmp, q->iobase + QUADSPI_TBDR);
+			txbuf++;
+		}
+
+		/* Trigger it */
+		ret = fsl_qspi_runcmd(q, cmd, addr, tx_size);
+
+		addr += tx_size;
+		count -= tx_size;
+
+		/*
+		 * If the TX FIFO is smaller then the size of Page Program,
+		 * we have to wait until this Write is finished.
+		 * For example, the TX FIFO is 64 bytes in the Vybrid,
+		 * but the Page Program may writes 265 bytes per time.
+		 * We are lucky that some chip(IMX6SLX) has increase the TX FIFO
+		 * to 512 bytes.
+		 *
+		 * If we can change the @m25p->page_size, we can remove the
+		 * following code.
+		 */
+		if (count > 0) {
+			ret = fsl_qspi_wait_till_ready(q);
+			if (ret) {
+				dev_err(q->dev, "Reading SR, err:%d\n", ret);
+				break;
+			}
+
+			/* Write Enable again. */
+			ret = fsl_qspi_runcmd(q, OPCODE_WREN, 0, 0);
+			if (ret) {
+				dev_err(q->dev, "Write Enable, err:%d\n", ret);
+				break;
+			}
+		}
+	}
+	return ret;
+}
+
+/* Switch to Quad read now. */
+static inline void fsl_qspi_enable_quad_read(struct fsl_qspi *q)
+{
+	writel(SEQID_QUAD_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
+		q->iobase + QUADSPI_BFGENCR);
+	q->quad_read_enabled = 1;
+}
+
+static int fsl_qspi_nor_tx(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	unsigned int addr = 0;
+	bool need_invalid = false;
+	int ret = 0;
+	u32 val;
+	u8 cmd;
+
+	/* This is the second spi_transfer for Page Program. */
+	if (q->cmd == OPCODE_PP) {
+		ret = fsl_qspi_nor_write(q, (u32 *)t->tx_buf, t->len);
+		need_invalid = true;
+		goto qspi_tx_out;
+	}
+
+	cmd = *(u8 *)t->tx_buf;
+	dev_dbg(q->dev, "NOR cmd is [0x%.2x], len : %d.\n", cmd, t->len);
+
+	switch (cmd) {
+	case OPCODE_SE:
+		addr = fsl_qspi_get_addr(q, t);
+		/* fall through */
+	case OPCODE_CHIP_ERASE:
+		need_invalid = true;
+	case OPCODE_WREN:
+		ret = fsl_qspi_runcmd(q, cmd, addr, 0);
+		q->cmd = -1;
+		break;
+
+	case OPCODE_QIOR:
+		if (!q->quad_read_enabled)
+			fsl_qspi_enable_quad_read(q);
+		/* fall through */
+	case OPCODE_FAST_READ:
+	case OPCODE_PP:
+		q->cmd = cmd;
+		q->addr = fsl_qspi_get_addr(q, t);
+		break;
+
+	case OPCODE_WRSR:
+		q->addr = 0;
+		q->cmd = cmd;
+		/* skip the cmd */
+		memcpy((void *) &val, ((u8 *)t->tx_buf) + 1, t->len -1);
+		ret = fsl_qspi_nor_write(q, &val, t->len - 1);
+		break;
+
+	default:
+		q->cmd = cmd;
+		break;
+	}
+
+qspi_tx_out:
+	if (need_invalid)
+		fsl_qspi_invalid(q);
+	return ret;
+}
+
+static int fsl_qspi_nor_rx(struct fsl_qspi *q, struct spi_transfer *t)
+{
+	int ret = 0;
+
+	switch (q->cmd) {
+	case OPCODE_RDSR:
+	case OPCODE_RDCR:
+	case OPCODE_RDID:
+		ret = fsl_qspi_runcmd(q, q->cmd, 0, t->len);
+		if (!ret)
+			fsl_qspi_read_data(q, t->len, t->rx_buf);
+		break;
+
+	case OPCODE_QIOR:
+	case OPCODE_FAST_READ:
+		ret = fsl_qspi_read_data_ahb(q, t);
+		break;
+	default:
+		dev_err(q->dev, "Unsupported cmd : %x\n", q->cmd);
+		return -EINVAL;
+	}
+	return ret;
+}
+
+static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
+		struct spi_message *m)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(master);
+	struct spi_transfer *t;
+	int ret = 0;
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->rx_buf && t->tx_buf) {
+			dev_err(q->dev,
+				"Can't send and receive simultaneously\n");
+			ret = -EINVAL;
+			break;
+		}
+
+		if (t->tx_buf) {
+			ret = fsl_qspi_nor_tx(q, t);
+			if (!ret)
+				m->actual_length += t->len;
+			continue;
+		}
+
+		if (t->rx_buf) {
+			ret = fsl_qspi_nor_rx(q, t);
+			if (!ret)
+				m->actual_length += t->len;
+		}
+	}
+
+	m->status = ret;
+	spi_finalize_current_message(master);
+	return ret;
+}
+
+/*
+ * There are two different ways to read out the data from the flash:
+ *  the "IP Command Read" and the "AHB Command Read".
+ *
+ * The IC guy suggests we use the "AHB Command Read" which is faster
+ * then the "IP Command Read". (What's more is that there is a bug in
+ * the "IP Command Read" in the Vybrid.)
+ *
+ * After we set up the registers for the "AHB Command Read", we can use
+ * the memcpy to read the data directly. A "missed" access to the buffer
+ * causes the controller to clear the buffer, and use the sequence pointed
+ * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash.
+ */
+static int fsl_qspi_init_abh_read(struct fsl_qspi *q)
+{
+	void __iomem *base = q->iobase;
+	u32 memmap_base = q->devtype_data->memmap_base;
+	int nor_size = q->nor_size;
+
+	/* Map the SPI NOR to accessiable address */
+	writel(nor_size | memmap_base, base + QUADSPI_SFA1AD);
+	writel(nor_size | memmap_base, base + QUADSPI_SFA2AD);
+	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB1AD);
+	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB2AD);
+
+	/* AHB configuration for access buffer 0/1/2 .*/
+	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
+	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
+	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
+	writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR);
+
+	/* We only use the buffer3 */
+	writel(0, base + QUADSPI_BUF0IND);
+	writel(0, base + QUADSPI_BUF1IND);
+	writel(0, base + QUADSPI_BUF2IND);
+
+	/* Set the default lut sequence for AHB Read. */
+	writel(SEQID_FAST_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
+		base + QUADSPI_BFGENCR);
+
+	/* Map the AHB address for read. */
+	q->ahb_base = ioremap(memmap_base, nor_size);
+	if (!q->ahb_base)
+		return -ENOMEM;
+	return 0;
+}
+
+static int fsl_qspi_nor_setup(struct spi_device *spi)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
+	void __iomem *base = q->iobase;
+	u32 reg_val, smpr_val;
+	int ret;
+
+	writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_MDIS_MASK,
+		base + QUADSPI_MCR);
+
+	reg_val = readl(base + QUADSPI_SMPR);
+	writel(reg_val & ~(QUADSPI_SMPR_FSDLY_MASK
+			| QUADSPI_SMPR_FSPHS_MASK
+			| QUADSPI_SMPR_HSENA_MASK), base + QUADSPI_SMPR);
+
+	writel(QUADSPI_MCR_RESERVED_MASK, base + QUADSPI_MCR);
+
+	fsl_qspi_init_lut(q);
+	ret = fsl_qspi_init_abh_read(q);
+	if (ret < 0)
+		return ret;
+
+	reg_val = 0;
+	reg_val |= QUADSPI_MCR_RESERVED_MASK;
+	smpr_val = readl(base + QUADSPI_SMPR);
+	smpr_val &= ~QUADSPI_SMPR_DDRSMP_MASK;
+	writel(smpr_val, base + QUADSPI_SMPR);
+	reg_val &= ~QUADSPI_MCR_DDR_EN_MASK;
+	writel(reg_val, base + QUADSPI_MCR);
+
+	/* enable the interrupt */
+	writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
+	return 0;
+}
+
+/* We only support the NOR now. */
+static struct fsl_qspi_handler fsl_qspi_nor_handler = {
+	.setup = fsl_qspi_nor_setup,
+	.do_one_msg = fsl_qspi_nor_do_one_msg,
+};
+
+static int fsl_qspi_setup(struct spi_device *spi)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
+
+	return q->h->setup(spi);
+}
+
+static int fsl_qspi_do_one_msg(struct spi_master *master,
+		struct spi_message *m)
+{
+	struct fsl_qspi *q = spi_master_get_devdata(master);
+
+	return q->h->do_one_msg(master, m);
+}
+
+static struct of_device_id fsl_qspi_dt_ids[] = {
+	{ .compatible = "fsl,vf610-qspi", .data = (void*)&vybrid_data, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
+
+static int fsl_qspi_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct spi_master *master;
+	struct fsl_qspi *q;
+	struct resource *res;
+	int num_cs, ret = 0;
+	const struct of_device_id *of_id =
+			of_match_device(fsl_qspi_dt_ids, &pdev->dev);
+
+	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get the spi-mum-chipselects\n");
+		return ret;
+	}
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*q));
+	if (!master)
+		return -ENOMEM;
+	q = spi_master_get_devdata(master);
+
+	ret = of_property_read_u32(np, "fsl,nor-size", &q->nor_size);
+	if (ret < 0)
+		dev_dbg(&pdev->dev, "can't get the nor size.\n");
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	q->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(q->iobase)) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = PTR_ERR(q->iobase);
+		goto map_failed;
+	}
+
+	q->clk_en = devm_clk_get(&pdev->dev, "qspi_en");
+	q->clk = devm_clk_get(&pdev->dev, "qspi");
+	if (IS_ERR(q->clk_en) || IS_ERR(q->clk)) {
+		dev_err(&pdev->dev, "failed to get clocks\n");
+		ret = -ENOENT;
+		goto map_failed;
+	}
+
+	ret = clk_prepare_enable(q->clk_en);
+	if (ret) {
+		dev_err(&pdev->dev, "can not enable the qspi_en clock\n");
+		goto map_failed;
+	}
+
+	ret = clk_prepare_enable(q->clk);
+	if (ret) {
+		clk_disable_unprepare(q->clk_en);
+		dev_err(&pdev->dev, "can not enable the qspi clock\n");
+		goto map_failed;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get the irq\n");
+		goto irq_failed;
+	}
+
+	ret = devm_request_irq(&pdev->dev, ret,
+			fsl_qspi_irq_handler, 0, pdev->name, q);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request irq.\n");
+		goto irq_failed;
+	}
+
+	q->dev = &pdev->dev;
+	q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
+
+	/* The default handler is for NOR. */
+	q->h = &fsl_qspi_nor_handler;
+
+	master->bus_num = pdev->id;
+	master->num_chipselect = num_cs;
+	master->dev.of_node = pdev->dev.of_node;
+
+	master->setup = fsl_qspi_setup;
+	master->transfer_one_message = fsl_qspi_do_one_msg;
+	platform_set_drvdata(pdev, master);
+
+	ret = spi_register_master(master);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register the spi master.\n");
+		goto irq_failed;
+	}
+	dev_info(&pdev->dev, "QuadSPI bus driver\n");
+	return 0;
+
+irq_failed:
+	clk_disable_unprepare(q->clk);
+	clk_disable_unprepare(q->clk_en);
+map_failed:
+	spi_master_put(master);
+
+	dev_err(&pdev->dev, "Freescale QuadSPI probe failed\n");
+	return ret;
+}
+
+static int fsl_qspi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct fsl_qspi *q = spi_master_get_devdata(master);
+
+	/* disable the hardware */
+	writel(0x0, q->iobase + QUADSPI_MCR);
+	writel(0x0, q->iobase + QUADSPI_RSER);
+
+	clk_disable_unprepare(q->clk);
+	clk_disable_unprepare(q->clk_en);
+	spi_master_put(master);
+	return 0;
+}
+
+static struct platform_driver fsl_qspi_driver = {
+	.driver = {
+		.name	= "fsl-quadspi",
+		.owner	= THIS_MODULE,
+		.of_match_table = fsl_qspi_dt_ids,
+	},
+	.probe          = fsl_qspi_probe,
+	.remove		= fsl_qspi_remove,
+};
+module_platform_driver(fsl_qspi_driver);
+
+MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 5/5] ARM: dts: vf610-twr: Add SPI NOR support
  2013-08-19  4:09 ` Huang Shijie
@ 2013-08-19  4:10   ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: broonie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

vf610-twr has a s25fl128s SPI NOR flash connected to QuadSpi0.

Add support for it.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 arch/arm/boot/dts/vf610-twr.dts |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/arch/arm/boot/dts/vf610-twr.dts b/arch/arm/boot/dts/vf610-twr.dts
index b3905f5..d57ce8e 100644
--- a/arch/arm/boot/dts/vf610-twr.dts
+++ b/arch/arm/boot/dts/vf610-twr.dts
@@ -55,3 +55,25 @@
 	pinctrl-0 = <&pinctrl_uart1_1>;
 	status = "okay";
 };
+
+&qspi0 {
+	fsl,spi-num-chipselects = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_qspi0_1>;
+	fsl,nor-size = <0x1000000>;
+	status = "okay";
+
+	flash: s25fl128s@0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "spansion,s25fl128s";
+		spi-max-frequency = <20000000>;
+		m25p,quad-read = <1>;
+		reg = <0>;
+
+		partition@0 {
+			label = "s25fl128s";
+			reg = <0x0 0x1000000>;
+		};
+	};
+};
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* [PATCH V1 5/5] ARM: dts: vf610-twr: Add SPI NOR support
@ 2013-08-19  4:10   ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-19  4:10 UTC (permalink / raw)
  To: linux-arm-kernel

vf610-twr has a s25fl128s SPI NOR flash connected to QuadSpi0.

Add support for it.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 arch/arm/boot/dts/vf610-twr.dts |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/arch/arm/boot/dts/vf610-twr.dts b/arch/arm/boot/dts/vf610-twr.dts
index b3905f5..d57ce8e 100644
--- a/arch/arm/boot/dts/vf610-twr.dts
+++ b/arch/arm/boot/dts/vf610-twr.dts
@@ -55,3 +55,25 @@
 	pinctrl-0 = <&pinctrl_uart1_1>;
 	status = "okay";
 };
+
+&qspi0 {
+	fsl,spi-num-chipselects = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_qspi0_1>;
+	fsl,nor-size = <0x1000000>;
+	status = "okay";
+
+	flash: s25fl128s at 0 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "spansion,s25fl128s";
+		spi-max-frequency = <20000000>;
+		m25p,quad-read = <1>;
+		reg = <0>;
+
+		partition at 0 {
+			label = "s25fl128s";
+			reg = <0x0 0x1000000>;
+		};
+	};
+};
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
  2013-08-19  4:10   ` Huang Shijie
@ 2013-08-22 19:21     ` Brian Norris
  -1 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-22 19:21 UTC (permalink / raw)
  To: Huang Shijie
  Cc: devicetree, b44548, dedekind1, b18965, linux-spi, broonie,
	linux-mtd, kernel, shawn.guo, dwmw2, linux-arm-kernel

Adding devicetree@vger.kernel.org

You might want to split out the devicetree binding documentation as a
separate patch from the driver submission, so that DT binding reviewers
will have an easier time.

On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:
> (0) What is the Quadspi controller?
> 
>     The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
>     one single or two external serial flash devices, each with up to 4
>     bidirectional data lines.
> 
> (1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
>     The LUT registers are a look-up-table for sequences of instructions.
>     A valid sequence consists of four LUT registers.
> 
> (2) The definition of the LUT register shows below:
> 
>     ---------------------------------------------------
>     | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
>     ---------------------------------------------------
> 
>     There are several types of INSTRx, such as:
>     	CMD	: the SPI NOR command.
> 	ADDR	: the address for the SPI NOR command.
> 	DUMMY	: the dummy cycles needed by the SPI NOR command.
> 	....
> 
> (3) We connect the NOR the QuadSPI now. I am not sure, but i think the
>     QuadSPI will be only used for the NOR. We may connect other devices
>     to it. But, for the reason of (2), we have to parse out the SPI NOR
>     command for the QuadSPI.
> 
> (4) Test this driver with the JFFS2 and UBIFS:
> 
>     For jffs2:
>          #flash_eraseall /dev/mtd0
>          #mount -t jffs2 /dev/mtdblock0 tmp
>          #bonnie++ -d tmp -u 0 -s 10 -r 5
> 
>     For ubifs:
>          #flash_eraseall /dev/mtd0
>     	 #ubiattach /dev/ubi_ctrl -m 0
>     	 #ubimkvol /dev/ubi0 -N test -m
>          #mount -t ubifs ubi0:test tmp
>          #bonnie++ -d tmp -u 0 -s 10 -r 5
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
>  .../devicetree/bindings/spi/fsl-quadspi.txt        |   27 +
>  drivers/spi/Kconfig                                |    7 +
>  drivers/spi/Makefile                               |    1 +
>  drivers/spi/spi-fsl-quadspi.c                      |  930 ++++++++++++++++++++
>  4 files changed, 965 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/spi/fsl-quadspi.txt
>  create mode 100644 drivers/spi/spi-fsl-quadspi.c
> 
> diff --git a/Documentation/devicetree/bindings/spi/fsl-quadspi.txt b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
> new file mode 100644
> index 0000000..e5bfa82
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
> @@ -0,0 +1,27 @@
> +* Freescale Quad Serial Peripheral Interface(QuadSPI)
> +
> +Required properties:
> +- compatible : Should be "fsl,vf610-qspi"
> +- reg : Offset and length of the register set for the device
> +- interrupts : Should contain the interrupt for the device
> +- fsl,spi-num-chipselects : Contains the number of the chipselect

Can this controller support more than one chip? If so, then the nor-size
property makes even less sense. See below.

> +- clocks : The clocks needed by the QuadSPI controller
> +- clock-names : the name of the clocks
> +
> +Optional properties:
> +- fsl,nor-size : The NOR size used by the QuadSPI mapping.

Why does the size of the NOR flash need to be in the controller's device
node? Shouldn't this be detected at run-time if possible? Or at least
included as a property of m25p80, if absolutely required?

> +
> +Example:
> +
> +qspi0: quadspi@40044000 {
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +	compatible = "fsl,vf610-qspi";
> +	reg = <0x40044000 0x1000>;
> +	interrupts = <0 24 0x04>;
> +	clocks = <&clks VF610_CLK_QSPI0_EN>,
> +		<&clks VF610_CLK_QSPI0>;
> +	clock-names = "qspi_en", "qspi";
> +	fsl,nor-size = <0x1000000>;
> +	status = "disabled";
> +};
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 92b2373..dc38063 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -187,6 +187,13 @@ config SPI_FALCON
>  	  has only been tested with m25p80 type chips. The hardware has no
>  	  support for other types of SPI peripherals.
>  
> +config SPI_FSL_QUADSPI
> +	tristate "Freescale Quad SPI controller"
> +	depends on ARCH_MXC
> +	help
> +	  This enables support for the Quad SPI controller in master mode.
> +	  We only connect the NOR to this controller now.
> +
>  config SPI_GPIO
>  	tristate "GPIO-based bitbanging SPI Master"
>  	depends on GPIOLIB
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index b25f385..7fe505c 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_SPI_FSL_ESPI)		+= spi-fsl-espi.o
>  obj-$(CONFIG_SPI_FSL_SPI)		+= spi-fsl-spi.o
>  obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o
>  obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
> +obj-$(CONFIG_SPI_FSL_QUADSPI)           += spi-fsl-quadspi.o
>  obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
>  obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
>  obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
> diff --git a/drivers/spi/spi-fsl-quadspi.c b/drivers/spi/spi-fsl-quadspi.c
> new file mode 100644
> index 0000000..de71a4e
> --- /dev/null
> +++ b/drivers/spi/spi-fsl-quadspi.c
> @@ -0,0 +1,930 @@
> +/*
> + * Freescale Quad SPI driver.
> + *
> + * Copyright (C) 2013 Freescale Semiconductor, Inc.
> + *
> + * 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
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/errno.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/spi/spi.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/timer.h>
> +#include <linux/jiffies.h>
> +#include <linux/completion.h>
> +#include <linux/mtd/spi-nor.h>
> +
> +/* The registers */
> +#define QUADSPI_MCR			0x00
> +#define QUADSPI_MCR_MDIS_SHIFT		14
> +#define QUADSPI_MCR_MDIS_MASK		(1 << QUADSPI_MCR_MDIS_SHIFT)
> +#define QUADSPI_MCR_CLR_TXF_SHIFT	11
> +#define QUADSPI_MCR_CLR_TXF_MASK	(1 << QUADSPI_MCR_CLR_TXF_SHIFT)
> +#define QUADSPI_MCR_CLR_RXF_SHIFT	10
> +#define QUADSPI_MCR_CLR_RXF_MASK	(1 << QUADSPI_MCR_CLR_RXF_SHIFT)
> +#define QUADSPI_MCR_DDR_EN_SHIFT	7
> +#define QUADSPI_MCR_DDR_EN_MASK		(1 << QUADSPI_MCR_DDR_EN_SHIFT)
> +#define QUADSPI_MCR_RESERVED_SHIFT	16
> +#define QUADSPI_MCR_RESERVED_MASK	(0xF << QUADSPI_MCR_RESERVED_SHIFT)
> +#define QUADSPI_MCR_SWRSTHD_SHIFT	1
> +#define QUADSPI_MCR_SWRSTHD_MASK	(1 << QUADSPI_MCR_SWRSTHD_SHIFT)
> +#define QUADSPI_MCR_SWRSTSD_SHIFT	0
> +#define QUADSPI_MCR_SWRSTSD_MASK	(1 << QUADSPI_MCR_SWRSTSD_SHIFT)
> +
> +#define QUADSPI_IPCR			0x08
> +#define QUADSPI_IPCR_SEQID_SHIFT	24
> +#define QUADSPI_IPCR_SEQID_MASK		(0xF << QUADSPI_IPCR_SEQID_SHIFT)
> +
> +#define QUADSPI_BUF0CR			0x10
> +#define QUADSPI_BUF1CR			0x14
> +#define QUADSPI_BUF2CR			0x18
> +#define QUADSPI_BUFXCR_INVALID_MSTRID	0xe
> +
> +#define QUADSPI_BUF3CR			0x1c
> +#define QUADSPI_BUF3CR_ALLMST_SHIFT	31
> +#define QUADSPI_BUF3CR_ALLMST		(1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
> +
> +#define QUADSPI_BFGENCR			0x20
> +#define QUADSPI_BFGENCR_PAR_EN_SHIFT	16
> +#define QUADSPI_BFGENCR_PAR_EN_MASK	(1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT))
> +#define QUADSPI_BFGENCR_SEQID_SHIFT	12
> +#define QUADSPI_BFGENCR_SEQID_MASK	(0xF << QUADSPI_BFGENCR_SEQID_SHIFT)
> +
> +#define QUADSPI_BUF0IND			0x30
> +#define QUADSPI_BUF1IND			0x34
> +#define QUADSPI_BUF2IND			0x38
> +#define QUADSPI_SFAR			0x100
> +
> +#define QUADSPI_SMPR			0x108
> +#define QUADSPI_SMPR_DDRSMP_SHIFT	16
> +#define QUADSPI_SMPR_DDRSMP_MASK	(7 << QUADSPI_SMPR_DDRSMP_SHIFT)
> +#define QUADSPI_SMPR_FSDLY_SHIFT	6
> +#define QUADSPI_SMPR_FSDLY_MASK		(1 << QUADSPI_SMPR_FSDLY_SHIFT)
> +#define QUADSPI_SMPR_FSPHS_SHIFT	5
> +#define QUADSPI_SMPR_FSPHS_MASK		(1 << QUADSPI_SMPR_FSPHS_SHIFT)
> +#define QUADSPI_SMPR_HSENA_SHIFT	0
> +#define QUADSPI_SMPR_HSENA_MASK		(1 << QUADSPI_SMPR_HSENA_SHIFT)
> +
> +#define QUADSPI_RBSR			0x10c
> +#define QUADSPI_RBSR_RDBFL_SHIFT	8
> +#define QUADSPI_RBSR_RDBFL_MASK		(0x3F << QUADSPI_RBSR_RDBFL_SHIFT)
> +
> +#define QUADSPI_RBCT			0x110
> +#define QUADSPI_RBCT_WMRK_MASK		0x1F
> +#define QUADSPI_RBCT_RXBRD_SHIFT	8
> +#define QUADSPI_RBCT_RXBRD_USEIPS	(0x1 << QUADSPI_RBCT_RXBRD_SHIFT)
> +
> +#define QUADSPI_TBSR			0x150
> +#define QUADSPI_TBDR			0x154
> +
> +#define QUADSPI_SR			0x15c
> +#define QUADSPI_SR_TXFULL_SHIFT		27
> +#define QUADSPI_SR_TXFULL_MASK		(1 << QUADSPI_SR_TXFULL_SHIFT)
> +#define QUADSPI_SR_AHBTRN_SHIFT		6
> +#define QUADSPI_SR_AHBTRN_MASK		(1 << QUADSPI_SR_AHBTRN_SHIFT)
> +#define QUADSPI_SR_AHB_ACC_SHIFT	2
> +#define QUADSPI_SR_AHB_ACC_MASK		(1 << QUADSPI_SR_AHB_ACC_SHIFT)
> +#define QUADSPI_SR_IP_ACC_SHIFT		1
> +#define QUADSPI_SR_IP_ACC_MASK		(1 << QUADSPI_SR_IP_ACC_SHIFT)
> +#define QUADSPI_SR_BUSY_SHIFT		0
> +#define QUADSPI_SR_BUSY_MASK		(1 << QUADSPI_SR_BUSY_SHIFT)
> +
> +#define QUADSPI_FR			0x160
> +#define QUADSPI_FR_TFF_MASK		0x1
> +
> +#define QUADSPI_SFA1AD			0x180
> +#define QUADSPI_SFA2AD			0x184
> +#define QUADSPI_SFB1AD			0x188
> +#define QUADSPI_SFB2AD			0x18c
> +#define QUADSPI_RBDR			0x200
> +
> +#define QUADSPI_LUTKEY			0x300
> +#define QUADSPI_LUTKEY_VALUE		0x5AF05AF0
> +
> +#define QUADSPI_LCKCR			0x304
> +#define QUADSPI_LCKER_LOCK		0x1
> +#define QUADSPI_LCKER_UNLOCK		0x2
> +
> +#define QUADSPI_RSER			0x164
> +#define QUADSPI_RSER_TFIE       	(0x1 << 0)
> +
> +#define QUADSPI_LUT_BASE		0x310
> +
> +/* Field definitions for LUT register. */
> +#define OPRND0_SHIFT		0
> +#define PAD0_SHIFT		8
> +#define INSTR0_SHIFT		10
> +#define OPRND1_SHIFT		16
> +
> +/* Instruction set for the LUT register. */
> +#define CMD			1
> +#define ADDR			2
> +#define DUMMY			3
> +#define MODE			4
> +#define MODE2			5
> +#define MODE4			6
> +#define READ			7
> +#define WRITE			8
> +#define JMP_ON_CS		9
> +#define ADDR_DDR		10
> +#define MODE_DDR		11
> +#define MODE2_DDR		12
> +#define MODE4_DDR		13
> +
> +/*
> + * The PAD definitions for LUT register.
> + *
> + * The pad stands for the lines number of IO[0:3].
> + * For example, the Quad read need four IO lines, so you should
> + * set PAD4 which means we use four IO lines.
> + */
> +#define PAD1			0
> +#define PAD2			1
> +#define PAD4			2
> +
> +/* Oprands for the LUT register. */
> +#define ADDR24BIT		0x18
> +
> +/* Macros for constructing the LUT register. */
> +#define QUADSPI_LUT0(ins, pad, opr)					\
> +		(((opr) << OPRND0_SHIFT) | ((pad) << PAD0_SHIFT) |	\
> +		((ins) << INSTR0_SHIFT))
> +
> +#define QUADSPI_LUT1(ins, pad, opr) \
> +		(QUADSPI_LUT0((ins), (pad), (opr)) << OPRND1_SHIFT)
> +
> +/* other macros for LUT register. */
> +#define QUADSPI_LUT(x)          (QUADSPI_LUT_BASE + (x) * 4)
> +#define QUADSPI_LUT_NUM		64
> +
> +/* SEQID */
> +#define SEQID_QUAD_READ		0
> +#define SEQID_WREN		1
> +#define SEQID_FAST_READ 	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
> +
> +struct fsl_qspi_handler {
> +	int (*setup)(struct spi_device *);
> +	int (*do_one_msg)(struct spi_master *, struct spi_message *);
> +};
> +
> +enum fsl_qspi_devtype {
> +	FSL_QUADSPI_VYBRID,
> +	FSL_QUADSPI_IMX6SLX
> +};
> +
> +struct fsl_qspi_devtype_data {
> +	enum fsl_qspi_devtype devtype;
> +	u32 memmap_base;
> +	int rxfifo;
> +	int txfifo;
> +};
> +
> +static struct fsl_qspi_devtype_data vybrid_data = {
> +	.devtype = FSL_QUADSPI_VYBRID,
> +	.memmap_base = 0x20000000,
> +	.rxfifo = 128,
> +	.txfifo = 64
> +};
> +
> +struct fsl_qspi {
> +	void __iomem *iobase;
> +	struct clk *clk, *clk_en;
> +	struct device *dev;
> +	struct fsl_qspi_handler *h;
> +	struct completion c;
> +	struct fsl_qspi_devtype_data *devtype_data;
> +	void __iomem *ahb_base; /* Used when read from AHB bus */
> +	unsigned int addr;
> +	u32 nor_size; /* for mapping */
> +	u8 cmd;
> +	unsigned int quad_read_enabled:1;
> +};
> +
> +static inline int is_vybrid_qspi(struct fsl_qspi *q)
> +{
> +	return q->devtype_data->devtype == FSL_QUADSPI_VYBRID;
> +}
> +
> +/*
> + * An IC bug makes us to re-arrange the 32-bit data.
> + * The following chips, such as IMX6SLX, have fixed this bug.
> + */
> +static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
> +{
> +	return is_vybrid_qspi(q) ? __swab32(a) : a;
> +}
> +
> +static inline void qspi_unlock_lut(struct fsl_qspi *q)
> +{
> +	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
> +	writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
> +}
> +
> +static inline void qspi_lock_lut(struct fsl_qspi *q)
> +{
> +	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
> +	writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
> +}
> +
> +static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id)
> +{
> +	struct fsl_qspi *q = dev_id;
> +	u32 reg;
> +
> +	/* clear interrupt */
> +	reg = readl(q->iobase + QUADSPI_FR);
> +	writel(reg, q->iobase + QUADSPI_FR);
> +
> +	if (reg & QUADSPI_FR_TFF_MASK)
> +		complete(&q->c);
> +
> +	dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x\n", reg);
> +	return IRQ_HANDLED;
> +}
> +
> +/* Init the LUT table. */
> +static void fsl_qspi_init_lut(struct fsl_qspi *q)
> +{
> +	void *__iomem base = q->iobase;
> +	int rxfifo = q->devtype_data->rxfifo;
> +	u32 lut_base;
> +	int i;
> +
> +	qspi_unlock_lut(q);
> +
> +	/* Clear all the LUT table */
> +	for (i = 0; i < QUADSPI_LUT_NUM; i++)
> +		writel(0, base + QUADSPI_LUT_BASE + i * 4);
> +
> +	/* Quad Read */
> +	lut_base = SEQID_QUAD_READ * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_QIOR)
> +			| QUADSPI_LUT1(ADDR, PAD4, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +	writel(QUADSPI_LUT0(MODE, PAD4, 0xff) | QUADSPI_LUT1(DUMMY, PAD4, 4),
> +			base + QUADSPI_LUT(lut_base + 1));
> +	writel(QUADSPI_LUT0(READ, PAD4, rxfifo),
> +			base + QUADSPI_LUT(lut_base + 2));
> +
> +	/* Write enable */
> +	lut_base = SEQID_WREN * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WREN),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Fast Read */
> +	lut_base = SEQID_FAST_READ * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_FAST_READ)
> +			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +	writel(QUADSPI_LUT0(DUMMY, PAD1, 8) | QUADSPI_LUT1(READ, PAD1, rxfifo),
> +			base + QUADSPI_LUT(lut_base + 1));
> +
> +	/* Page Program */
> +	lut_base = SEQID_PP * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_PP)
> +			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +	writel(QUADSPI_LUT0(WRITE, PAD1, 0),
> +			base + QUADSPI_LUT(lut_base + 1));
> +
> +	/* Read Status */
> +	lut_base = SEQID_RDSR * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDSR)
> +			| QUADSPI_LUT1(READ, PAD1, 0x1),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Erase a sector */
> +	lut_base = SEQID_SE * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_SE)
> +			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Erase the whole chip */
> +	lut_base = SEQID_CHIP_ERASE * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_CHIP_ERASE),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* READ ID */
> +	lut_base = SEQID_RDID * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDID)
> +			| QUADSPI_LUT1(READ, PAD1, 0x8),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Write Register */
> +	lut_base = SEQID_WRSR * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WRSR)
> +			| QUADSPI_LUT1(WRITE, PAD1, 0x2),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Read Configuration Register */
> +	lut_base = SEQID_RDCR * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDCR)
> +			| QUADSPI_LUT1(READ, PAD1, 0x1),
> +			base + QUADSPI_LUT(lut_base));
> +	qspi_lock_lut(q);
> +}
> +
> +/* Get the SEQID for the command */
> +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> +{
> +	switch (cmd) {
> +	case OPCODE_WREN:
> +		return SEQID_WREN;
> +	case OPCODE_RDSR:
> +		return SEQID_RDSR;
> +	case OPCODE_SE:
> +		return SEQID_SE;
> +	case OPCODE_CHIP_ERASE:
> +		return SEQID_CHIP_ERASE;
> +	case OPCODE_PP:
> +		return SEQID_PP;
> +	case OPCODE_RDID:
> +		return SEQID_RDID;
> +	case OPCODE_WRSR:
> +		return SEQID_WRSR;
> +	case OPCODE_RDCR:
> +		return SEQID_RDCR;
> +	default:
> +		dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
> +		break;
> +	}
> +	return -1;
> +}
> +
> +static int
> +fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
> +{
> +	int seqid;
> +	u32 reg;
> +	int err;
> +
> +	init_completion(&q->c);
> +	dev_dbg(q->dev, "to @%.8x, len:%d, cmd:%.2x\n", addr, len, cmd);
> +
> +	/* save the reg */
> +	reg = readl(q->iobase + QUADSPI_MCR);
> +
> +	writel(q->devtype_data->memmap_base + addr, q->iobase + QUADSPI_SFAR);
> +	writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
> +			q->iobase + QUADSPI_RBCT);
> +	writel(QUADSPI_MCR_CLR_RXF_MASK | QUADSPI_MCR_RESERVED_MASK,
> +			q->iobase + QUADSPI_MCR);
> +
> +	/* trigger the LUT now */
> +	seqid = fsl_qspi_get_seqid(q, cmd);
> +	writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len,
> +		q->iobase + QUADSPI_IPCR);
> +
> +	/* Wait for the interrupt. */
> +	err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000));
> +	if (!err) {
> +		dev_err(q->dev,
> +			"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
> +			cmd, addr, readl(q->iobase + QUADSPI_FR),
> +			readl(q->iobase + QUADSPI_SR));
> +		err = -ETIMEDOUT;
> +	} else {
> +		err = 0;
> +	}
> +
> +	/* restore the MCR */
> +	writel(reg, q->iobase + QUADSPI_MCR);
> +
> +	return err;
> +}
> +
> +/* Get the address from the tx buffer. */
> +static unsigned int
> +fsl_qspi_get_addr(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	unsigned int addr;
> +	u8 *buf = (u8 *)t->tx_buf;
> +
> +	/* 3-byte address */
> +	if (q->nor_size <= SZ_16M)
> +		addr = (buf[1] << 16) | (buf[2] << 8) |  buf[3];
> +	return addr;
> +}
> +
> +/* Read out the data from the buffer registers. */
> +static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u32 *rxbuf)
> +{
> +	u32 tmp;
> +	int i = 0;
> +
> +	while (len > 0) {
> +		tmp = readl(q->iobase + QUADSPI_RBDR + i * 4);
> +		*rxbuf = fsl_qspi_endian_xchg(q, tmp);
> +		dev_dbg(q->dev, "rcv: 0x%.8x, tmp : 0x%.8x\n", *rxbuf, tmp);
> +
> +		rxbuf++;
> +		len -= 4;
> +		i++;
> +	}
> +}
> +
> +/* Read out the data directly from the AHB buffer.*/
> +static int fsl_qspi_read_data_ahb(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	dev_dbg(q->dev, "cmd [%x],read from 0x%.8x,len:%d\n",
> +		q->cmd, q->addr, t->len);
> +	memcpy(t->rx_buf, q->ahb_base + q->addr, t->len);
> +	return 0;
> +}
> +
> +static u32 fsl_qspi_read_sr(struct fsl_qspi *q)
> +{
> +	u32 val = -EINVAL;
> +	int ret;
> +
> +	ret = fsl_qspi_runcmd(q, OPCODE_RDSR, 0, 1);
> +	if (!ret)
> +		fsl_qspi_read_data(q, 1, &val);
> +	return val;
> +}
> +
> +static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
> +{
> +	unsigned long deadline;
> +	u32 sr;
> +
> +	deadline = jiffies + msecs_to_jiffies(40000);
> +
> +	do {
> +		if ((sr = fsl_qspi_read_sr(q)) < 0)
> +			break;
> +		else if (!(sr & SR_WIP))
> +			return 0;
> +
> +		cond_resched();
> +
> +	} while (!time_after_eq(jiffies, deadline));
> +
> +	return 1;
> +}
> +
> +/*
> + * If we have changed the content of the flash by writing or erasing,
> + * we need to invalidate the AHB buffer. If we do not do so, we may read out
> + * the wrong data. The spec tells us reset the AHB domain and Serial Flash
> + * domain at the same time.
> + */
> +static inline void fsl_qspi_invalid(struct fsl_qspi *q)
> +{
> +	u32 reg;
> +
> +	reg = readl(q->iobase + QUADSPI_MCR);
> +	reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
> +	writel(reg, q->iobase + QUADSPI_MCR);
> +
> +	/*
> +	 * The minimum delay : 1 AHB + 2 SFCK clocks.
> +	 * Delay 1 us is enough.
> +	 */
> +	udelay(1);
> +
> +	reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
> +	writel(reg, q->iobase + QUADSPI_MCR);
> +}
> +
> +static int fsl_qspi_nor_write(struct fsl_qspi *q, u32 *txbuf, unsigned count)
> +{
> +	unsigned int addr = q->addr;
> +	int txfifo_size = q->devtype_data->txfifo;
> +	int ret = 0;
> +	int tx_size;
> +	u32 tmp;
> +	int i, j;
> +	u8 cmd = q->cmd;
> +
> +	q->cmd = -1; /* clear the cmd */
> +	dev_dbg(q->dev, "to @%.8x, len : %d\n", addr, count);
> +
> +	while (count > 0) {
> +		tx_size = (count > txfifo_size) ? txfifo_size : count;
> +
> +		/* clear the TX FIFO. */
> +		tmp = readl(q->iobase + QUADSPI_MCR);
> +		writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR);
> +
> +		/* fill the TX data to the FIFO */
> +		for (j = 0, i = ((tx_size + 3) / 4); j < i; j++) {
> +			tmp = fsl_qspi_endian_xchg(q, *txbuf);
> +			writel(tmp, q->iobase + QUADSPI_TBDR);
> +			txbuf++;
> +		}
> +
> +		/* Trigger it */
> +		ret = fsl_qspi_runcmd(q, cmd, addr, tx_size);
> +
> +		addr += tx_size;
> +		count -= tx_size;
> +
> +		/*
> +		 * If the TX FIFO is smaller then the size of Page Program,
> +		 * we have to wait until this Write is finished.
> +		 * For example, the TX FIFO is 64 bytes in the Vybrid,
> +		 * but the Page Program may writes 265 bytes per time.
> +		 * We are lucky that some chip(IMX6SLX) has increase the TX FIFO
> +		 * to 512 bytes.
> +		 *
> +		 * If we can change the @m25p->page_size, we can remove the
> +		 * following code.
> +		 */
> +		if (count > 0) {
> +			ret = fsl_qspi_wait_till_ready(q);
> +			if (ret) {
> +				dev_err(q->dev, "Reading SR, err:%d\n", ret);
> +				break;
> +			}
> +
> +			/* Write Enable again. */
> +			ret = fsl_qspi_runcmd(q, OPCODE_WREN, 0, 0);
> +			if (ret) {
> +				dev_err(q->dev, "Write Enable, err:%d\n", ret);
> +				break;
> +			}
> +		}
> +	}
> +	return ret;
> +}
> +
> +/* Switch to Quad read now. */
> +static inline void fsl_qspi_enable_quad_read(struct fsl_qspi *q)
> +{
> +	writel(SEQID_QUAD_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
> +		q->iobase + QUADSPI_BFGENCR);
> +	q->quad_read_enabled = 1;
> +}
> +
> +static int fsl_qspi_nor_tx(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	unsigned int addr = 0;
> +	bool need_invalid = false;
> +	int ret = 0;
> +	u32 val;
> +	u8 cmd;
> +
> +	/* This is the second spi_transfer for Page Program. */
> +	if (q->cmd == OPCODE_PP) {
> +		ret = fsl_qspi_nor_write(q, (u32 *)t->tx_buf, t->len);
> +		need_invalid = true;
> +		goto qspi_tx_out;
> +	}
> +
> +	cmd = *(u8 *)t->tx_buf;
> +	dev_dbg(q->dev, "NOR cmd is [0x%.2x], len : %d.\n", cmd, t->len);
> +
> +	switch (cmd) {
> +	case OPCODE_SE:
> +		addr = fsl_qspi_get_addr(q, t);
> +		/* fall through */
> +	case OPCODE_CHIP_ERASE:
> +		need_invalid = true;
> +	case OPCODE_WREN:
> +		ret = fsl_qspi_runcmd(q, cmd, addr, 0);
> +		q->cmd = -1;
> +		break;
> +
> +	case OPCODE_QIOR:
> +		if (!q->quad_read_enabled)
> +			fsl_qspi_enable_quad_read(q);
> +		/* fall through */
> +	case OPCODE_FAST_READ:
> +	case OPCODE_PP:
> +		q->cmd = cmd;
> +		q->addr = fsl_qspi_get_addr(q, t);
> +		break;
> +
> +	case OPCODE_WRSR:
> +		q->addr = 0;
> +		q->cmd = cmd;
> +		/* skip the cmd */
> +		memcpy((void *) &val, ((u8 *)t->tx_buf) + 1, t->len -1);
> +		ret = fsl_qspi_nor_write(q, &val, t->len - 1);
> +		break;
> +
> +	default:
> +		q->cmd = cmd;
> +		break;
> +	}
> +
> +qspi_tx_out:
> +	if (need_invalid)
> +		fsl_qspi_invalid(q);
> +	return ret;
> +}
> +
> +static int fsl_qspi_nor_rx(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	int ret = 0;
> +
> +	switch (q->cmd) {
> +	case OPCODE_RDSR:
> +	case OPCODE_RDCR:
> +	case OPCODE_RDID:
> +		ret = fsl_qspi_runcmd(q, q->cmd, 0, t->len);
> +		if (!ret)
> +			fsl_qspi_read_data(q, t->len, t->rx_buf);
> +		break;
> +
> +	case OPCODE_QIOR:
> +	case OPCODE_FAST_READ:
> +		ret = fsl_qspi_read_data_ahb(q, t);
> +		break;
> +	default:
> +		dev_err(q->dev, "Unsupported cmd : %x\n", q->cmd);
> +		return -EINVAL;
> +	}
> +	return ret;
> +}
> +
> +static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
> +		struct spi_message *m)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +	struct spi_transfer *t;
> +	int ret = 0;
> +
> +	list_for_each_entry(t, &m->transfers, transfer_list) {
> +		if (t->rx_buf && t->tx_buf) {
> +			dev_err(q->dev,
> +				"Can't send and receive simultaneously\n");
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		if (t->tx_buf) {
> +			ret = fsl_qspi_nor_tx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +			continue;
> +		}
> +
> +		if (t->rx_buf) {
> +			ret = fsl_qspi_nor_rx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +		}
> +	}
> +
> +	m->status = ret;
> +	spi_finalize_current_message(master);
> +	return ret;
> +}
> +
> +/*
> + * There are two different ways to read out the data from the flash:
> + *  the "IP Command Read" and the "AHB Command Read".
> + *
> + * The IC guy suggests we use the "AHB Command Read" which is faster
> + * then the "IP Command Read". (What's more is that there is a bug in
> + * the "IP Command Read" in the Vybrid.)
> + *
> + * After we set up the registers for the "AHB Command Read", we can use
> + * the memcpy to read the data directly. A "missed" access to the buffer
> + * causes the controller to clear the buffer, and use the sequence pointed
> + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash.
> + */
> +static int fsl_qspi_init_abh_read(struct fsl_qspi *q)
> +{
> +	void __iomem *base = q->iobase;
> +	u32 memmap_base = q->devtype_data->memmap_base;
> +	int nor_size = q->nor_size;
> +
> +	/* Map the SPI NOR to accessiable address */
> +	writel(nor_size | memmap_base, base + QUADSPI_SFA1AD);
> +	writel(nor_size | memmap_base, base + QUADSPI_SFA2AD);
> +	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB1AD);
> +	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB2AD);
> +
> +	/* AHB configuration for access buffer 0/1/2 .*/
> +	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
> +	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
> +	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
> +	writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR);
> +
> +	/* We only use the buffer3 */
> +	writel(0, base + QUADSPI_BUF0IND);
> +	writel(0, base + QUADSPI_BUF1IND);
> +	writel(0, base + QUADSPI_BUF2IND);
> +
> +	/* Set the default lut sequence for AHB Read. */
> +	writel(SEQID_FAST_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
> +		base + QUADSPI_BFGENCR);
> +
> +	/* Map the AHB address for read. */
> +	q->ahb_base = ioremap(memmap_base, nor_size);
> +	if (!q->ahb_base)
> +		return -ENOMEM;
> +	return 0;
> +}
> +
> +static int fsl_qspi_nor_setup(struct spi_device *spi)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
> +	void __iomem *base = q->iobase;
> +	u32 reg_val, smpr_val;
> +	int ret;
> +
> +	writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_MDIS_MASK,
> +		base + QUADSPI_MCR);
> +
> +	reg_val = readl(base + QUADSPI_SMPR);
> +	writel(reg_val & ~(QUADSPI_SMPR_FSDLY_MASK
> +			| QUADSPI_SMPR_FSPHS_MASK
> +			| QUADSPI_SMPR_HSENA_MASK), base + QUADSPI_SMPR);
> +
> +	writel(QUADSPI_MCR_RESERVED_MASK, base + QUADSPI_MCR);
> +
> +	fsl_qspi_init_lut(q);
> +	ret = fsl_qspi_init_abh_read(q);
> +	if (ret < 0)
> +		return ret;
> +
> +	reg_val = 0;
> +	reg_val |= QUADSPI_MCR_RESERVED_MASK;
> +	smpr_val = readl(base + QUADSPI_SMPR);
> +	smpr_val &= ~QUADSPI_SMPR_DDRSMP_MASK;
> +	writel(smpr_val, base + QUADSPI_SMPR);
> +	reg_val &= ~QUADSPI_MCR_DDR_EN_MASK;
> +	writel(reg_val, base + QUADSPI_MCR);
> +
> +	/* enable the interrupt */
> +	writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
> +	return 0;
> +}
> +
> +/* We only support the NOR now. */
> +static struct fsl_qspi_handler fsl_qspi_nor_handler = {
> +	.setup = fsl_qspi_nor_setup,
> +	.do_one_msg = fsl_qspi_nor_do_one_msg,
> +};
> +
> +static int fsl_qspi_setup(struct spi_device *spi)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
> +
> +	return q->h->setup(spi);
> +}
> +
> +static int fsl_qspi_do_one_msg(struct spi_master *master,
> +		struct spi_message *m)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +
> +	return q->h->do_one_msg(master, m);
> +}
> +
> +static struct of_device_id fsl_qspi_dt_ids[] = {
> +	{ .compatible = "fsl,vf610-qspi", .data = (void*)&vybrid_data, },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
> +
> +static int fsl_qspi_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct spi_master *master;
> +	struct fsl_qspi *q;
> +	struct resource *res;
> +	int num_cs, ret = 0;
> +	const struct of_device_id *of_id =
> +			of_match_device(fsl_qspi_dt_ids, &pdev->dev);
> +
> +	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "can't get the spi-mum-chipselects\n");
> +		return ret;
> +	}
> +
> +	master = spi_alloc_master(&pdev->dev, sizeof(*q));
> +	if (!master)
> +		return -ENOMEM;
> +	q = spi_master_get_devdata(master);
> +
> +	ret = of_property_read_u32(np, "fsl,nor-size", &q->nor_size);
> +	if (ret < 0)
> +		dev_dbg(&pdev->dev, "can't get the nor size.\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	q->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(q->iobase)) {
> +		dev_err(&pdev->dev, "ioremap failed\n");
> +		ret = PTR_ERR(q->iobase);
> +		goto map_failed;
> +	}
> +
> +	q->clk_en = devm_clk_get(&pdev->dev, "qspi_en");
> +	q->clk = devm_clk_get(&pdev->dev, "qspi");
> +	if (IS_ERR(q->clk_en) || IS_ERR(q->clk)) {
> +		dev_err(&pdev->dev, "failed to get clocks\n");
> +		ret = -ENOENT;
> +		goto map_failed;
> +	}
> +
> +	ret = clk_prepare_enable(q->clk_en);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can not enable the qspi_en clock\n");
> +		goto map_failed;
> +	}
> +
> +	ret = clk_prepare_enable(q->clk);
> +	if (ret) {
> +		clk_disable_unprepare(q->clk_en);
> +		dev_err(&pdev->dev, "can not enable the qspi clock\n");
> +		goto map_failed;
> +	}
> +
> +	ret = platform_get_irq(pdev, 0);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to get the irq\n");
> +		goto irq_failed;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, ret,
> +			fsl_qspi_irq_handler, 0, pdev->name, q);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to request irq.\n");
> +		goto irq_failed;
> +	}
> +
> +	q->dev = &pdev->dev;
> +	q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
> +
> +	/* The default handler is for NOR. */
> +	q->h = &fsl_qspi_nor_handler;
> +
> +	master->bus_num = pdev->id;
> +	master->num_chipselect = num_cs;
> +	master->dev.of_node = pdev->dev.of_node;
> +
> +	master->setup = fsl_qspi_setup;
> +	master->transfer_one_message = fsl_qspi_do_one_msg;
> +	platform_set_drvdata(pdev, master);
> +
> +	ret = spi_register_master(master);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register the spi master.\n");
> +		goto irq_failed;
> +	}
> +	dev_info(&pdev->dev, "QuadSPI bus driver\n");
> +	return 0;
> +
> +irq_failed:
> +	clk_disable_unprepare(q->clk);
> +	clk_disable_unprepare(q->clk_en);
> +map_failed:
> +	spi_master_put(master);
> +
> +	dev_err(&pdev->dev, "Freescale QuadSPI probe failed\n");
> +	return ret;
> +}
> +
> +static int fsl_qspi_remove(struct platform_device *pdev)
> +{
> +	struct spi_master *master = platform_get_drvdata(pdev);
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +
> +	/* disable the hardware */
> +	writel(0x0, q->iobase + QUADSPI_MCR);
> +	writel(0x0, q->iobase + QUADSPI_RSER);
> +
> +	clk_disable_unprepare(q->clk);
> +	clk_disable_unprepare(q->clk_en);
> +	spi_master_put(master);
> +	return 0;
> +}
> +
> +static struct platform_driver fsl_qspi_driver = {
> +	.driver = {
> +		.name	= "fsl-quadspi",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = fsl_qspi_dt_ids,
> +	},
> +	.probe          = fsl_qspi_probe,
> +	.remove		= fsl_qspi_remove,
> +};
> +module_platform_driver(fsl_qspi_driver);
> +
> +MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.7.1
> 
> 

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
@ 2013-08-22 19:21     ` Brian Norris
  0 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-22 19:21 UTC (permalink / raw)
  To: linux-arm-kernel

Adding devicetree at vger.kernel.org

You might want to split out the devicetree binding documentation as a
separate patch from the driver submission, so that DT binding reviewers
will have an easier time.

On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:
> (0) What is the Quadspi controller?
> 
>     The Quadspi(Quad Serial Peripheral Interface) acts as an interface to
>     one single or two external serial flash devices, each with up to 4
>     bidirectional data lines.
> 
> (1) The Quadspi controller is driven by the LUT(Look-up Table) registers.
>     The LUT registers are a look-up-table for sequences of instructions.
>     A valid sequence consists of four LUT registers.
> 
> (2) The definition of the LUT register shows below:
> 
>     ---------------------------------------------------
>     | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
>     ---------------------------------------------------
> 
>     There are several types of INSTRx, such as:
>     	CMD	: the SPI NOR command.
> 	ADDR	: the address for the SPI NOR command.
> 	DUMMY	: the dummy cycles needed by the SPI NOR command.
> 	....
> 
> (3) We connect the NOR the QuadSPI now. I am not sure, but i think the
>     QuadSPI will be only used for the NOR. We may connect other devices
>     to it. But, for the reason of (2), we have to parse out the SPI NOR
>     command for the QuadSPI.
> 
> (4) Test this driver with the JFFS2 and UBIFS:
> 
>     For jffs2:
>          #flash_eraseall /dev/mtd0
>          #mount -t jffs2 /dev/mtdblock0 tmp
>          #bonnie++ -d tmp -u 0 -s 10 -r 5
> 
>     For ubifs:
>          #flash_eraseall /dev/mtd0
>     	 #ubiattach /dev/ubi_ctrl -m 0
>     	 #ubimkvol /dev/ubi0 -N test -m
>          #mount -t ubifs ubi0:test tmp
>          #bonnie++ -d tmp -u 0 -s 10 -r 5
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
>  .../devicetree/bindings/spi/fsl-quadspi.txt        |   27 +
>  drivers/spi/Kconfig                                |    7 +
>  drivers/spi/Makefile                               |    1 +
>  drivers/spi/spi-fsl-quadspi.c                      |  930 ++++++++++++++++++++
>  4 files changed, 965 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/spi/fsl-quadspi.txt
>  create mode 100644 drivers/spi/spi-fsl-quadspi.c
> 
> diff --git a/Documentation/devicetree/bindings/spi/fsl-quadspi.txt b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
> new file mode 100644
> index 0000000..e5bfa82
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt
> @@ -0,0 +1,27 @@
> +* Freescale Quad Serial Peripheral Interface(QuadSPI)
> +
> +Required properties:
> +- compatible : Should be "fsl,vf610-qspi"
> +- reg : Offset and length of the register set for the device
> +- interrupts : Should contain the interrupt for the device
> +- fsl,spi-num-chipselects : Contains the number of the chipselect

Can this controller support more than one chip? If so, then the nor-size
property makes even less sense. See below.

> +- clocks : The clocks needed by the QuadSPI controller
> +- clock-names : the name of the clocks
> +
> +Optional properties:
> +- fsl,nor-size : The NOR size used by the QuadSPI mapping.

Why does the size of the NOR flash need to be in the controller's device
node? Shouldn't this be detected at run-time if possible? Or at least
included as a property of m25p80, if absolutely required?

> +
> +Example:
> +
> +qspi0: quadspi at 40044000 {
> +	#address-cells = <1>;
> +	#size-cells = <0>;
> +	compatible = "fsl,vf610-qspi";
> +	reg = <0x40044000 0x1000>;
> +	interrupts = <0 24 0x04>;
> +	clocks = <&clks VF610_CLK_QSPI0_EN>,
> +		<&clks VF610_CLK_QSPI0>;
> +	clock-names = "qspi_en", "qspi";
> +	fsl,nor-size = <0x1000000>;
> +	status = "disabled";
> +};
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 92b2373..dc38063 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -187,6 +187,13 @@ config SPI_FALCON
>  	  has only been tested with m25p80 type chips. The hardware has no
>  	  support for other types of SPI peripherals.
>  
> +config SPI_FSL_QUADSPI
> +	tristate "Freescale Quad SPI controller"
> +	depends on ARCH_MXC
> +	help
> +	  This enables support for the Quad SPI controller in master mode.
> +	  We only connect the NOR to this controller now.
> +
>  config SPI_GPIO
>  	tristate "GPIO-based bitbanging SPI Master"
>  	depends on GPIOLIB
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index b25f385..7fe505c 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -37,6 +37,7 @@ obj-$(CONFIG_SPI_FSL_ESPI)		+= spi-fsl-espi.o
>  obj-$(CONFIG_SPI_FSL_SPI)		+= spi-fsl-spi.o
>  obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o
>  obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
> +obj-$(CONFIG_SPI_FSL_QUADSPI)           += spi-fsl-quadspi.o
>  obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
>  obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
>  obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
> diff --git a/drivers/spi/spi-fsl-quadspi.c b/drivers/spi/spi-fsl-quadspi.c
> new file mode 100644
> index 0000000..de71a4e
> --- /dev/null
> +++ b/drivers/spi/spi-fsl-quadspi.c
> @@ -0,0 +1,930 @@
> +/*
> + * Freescale Quad SPI driver.
> + *
> + * Copyright (C) 2013 Freescale Semiconductor, Inc.
> + *
> + * 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
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/errno.h>
> +#include <linux/platform_device.h>
> +#include <linux/sched.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/spi/spi.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/timer.h>
> +#include <linux/jiffies.h>
> +#include <linux/completion.h>
> +#include <linux/mtd/spi-nor.h>
> +
> +/* The registers */
> +#define QUADSPI_MCR			0x00
> +#define QUADSPI_MCR_MDIS_SHIFT		14
> +#define QUADSPI_MCR_MDIS_MASK		(1 << QUADSPI_MCR_MDIS_SHIFT)
> +#define QUADSPI_MCR_CLR_TXF_SHIFT	11
> +#define QUADSPI_MCR_CLR_TXF_MASK	(1 << QUADSPI_MCR_CLR_TXF_SHIFT)
> +#define QUADSPI_MCR_CLR_RXF_SHIFT	10
> +#define QUADSPI_MCR_CLR_RXF_MASK	(1 << QUADSPI_MCR_CLR_RXF_SHIFT)
> +#define QUADSPI_MCR_DDR_EN_SHIFT	7
> +#define QUADSPI_MCR_DDR_EN_MASK		(1 << QUADSPI_MCR_DDR_EN_SHIFT)
> +#define QUADSPI_MCR_RESERVED_SHIFT	16
> +#define QUADSPI_MCR_RESERVED_MASK	(0xF << QUADSPI_MCR_RESERVED_SHIFT)
> +#define QUADSPI_MCR_SWRSTHD_SHIFT	1
> +#define QUADSPI_MCR_SWRSTHD_MASK	(1 << QUADSPI_MCR_SWRSTHD_SHIFT)
> +#define QUADSPI_MCR_SWRSTSD_SHIFT	0
> +#define QUADSPI_MCR_SWRSTSD_MASK	(1 << QUADSPI_MCR_SWRSTSD_SHIFT)
> +
> +#define QUADSPI_IPCR			0x08
> +#define QUADSPI_IPCR_SEQID_SHIFT	24
> +#define QUADSPI_IPCR_SEQID_MASK		(0xF << QUADSPI_IPCR_SEQID_SHIFT)
> +
> +#define QUADSPI_BUF0CR			0x10
> +#define QUADSPI_BUF1CR			0x14
> +#define QUADSPI_BUF2CR			0x18
> +#define QUADSPI_BUFXCR_INVALID_MSTRID	0xe
> +
> +#define QUADSPI_BUF3CR			0x1c
> +#define QUADSPI_BUF3CR_ALLMST_SHIFT	31
> +#define QUADSPI_BUF3CR_ALLMST		(1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
> +
> +#define QUADSPI_BFGENCR			0x20
> +#define QUADSPI_BFGENCR_PAR_EN_SHIFT	16
> +#define QUADSPI_BFGENCR_PAR_EN_MASK	(1 << (QUADSPI_BFGENCR_PAR_EN_SHIFT))
> +#define QUADSPI_BFGENCR_SEQID_SHIFT	12
> +#define QUADSPI_BFGENCR_SEQID_MASK	(0xF << QUADSPI_BFGENCR_SEQID_SHIFT)
> +
> +#define QUADSPI_BUF0IND			0x30
> +#define QUADSPI_BUF1IND			0x34
> +#define QUADSPI_BUF2IND			0x38
> +#define QUADSPI_SFAR			0x100
> +
> +#define QUADSPI_SMPR			0x108
> +#define QUADSPI_SMPR_DDRSMP_SHIFT	16
> +#define QUADSPI_SMPR_DDRSMP_MASK	(7 << QUADSPI_SMPR_DDRSMP_SHIFT)
> +#define QUADSPI_SMPR_FSDLY_SHIFT	6
> +#define QUADSPI_SMPR_FSDLY_MASK		(1 << QUADSPI_SMPR_FSDLY_SHIFT)
> +#define QUADSPI_SMPR_FSPHS_SHIFT	5
> +#define QUADSPI_SMPR_FSPHS_MASK		(1 << QUADSPI_SMPR_FSPHS_SHIFT)
> +#define QUADSPI_SMPR_HSENA_SHIFT	0
> +#define QUADSPI_SMPR_HSENA_MASK		(1 << QUADSPI_SMPR_HSENA_SHIFT)
> +
> +#define QUADSPI_RBSR			0x10c
> +#define QUADSPI_RBSR_RDBFL_SHIFT	8
> +#define QUADSPI_RBSR_RDBFL_MASK		(0x3F << QUADSPI_RBSR_RDBFL_SHIFT)
> +
> +#define QUADSPI_RBCT			0x110
> +#define QUADSPI_RBCT_WMRK_MASK		0x1F
> +#define QUADSPI_RBCT_RXBRD_SHIFT	8
> +#define QUADSPI_RBCT_RXBRD_USEIPS	(0x1 << QUADSPI_RBCT_RXBRD_SHIFT)
> +
> +#define QUADSPI_TBSR			0x150
> +#define QUADSPI_TBDR			0x154
> +
> +#define QUADSPI_SR			0x15c
> +#define QUADSPI_SR_TXFULL_SHIFT		27
> +#define QUADSPI_SR_TXFULL_MASK		(1 << QUADSPI_SR_TXFULL_SHIFT)
> +#define QUADSPI_SR_AHBTRN_SHIFT		6
> +#define QUADSPI_SR_AHBTRN_MASK		(1 << QUADSPI_SR_AHBTRN_SHIFT)
> +#define QUADSPI_SR_AHB_ACC_SHIFT	2
> +#define QUADSPI_SR_AHB_ACC_MASK		(1 << QUADSPI_SR_AHB_ACC_SHIFT)
> +#define QUADSPI_SR_IP_ACC_SHIFT		1
> +#define QUADSPI_SR_IP_ACC_MASK		(1 << QUADSPI_SR_IP_ACC_SHIFT)
> +#define QUADSPI_SR_BUSY_SHIFT		0
> +#define QUADSPI_SR_BUSY_MASK		(1 << QUADSPI_SR_BUSY_SHIFT)
> +
> +#define QUADSPI_FR			0x160
> +#define QUADSPI_FR_TFF_MASK		0x1
> +
> +#define QUADSPI_SFA1AD			0x180
> +#define QUADSPI_SFA2AD			0x184
> +#define QUADSPI_SFB1AD			0x188
> +#define QUADSPI_SFB2AD			0x18c
> +#define QUADSPI_RBDR			0x200
> +
> +#define QUADSPI_LUTKEY			0x300
> +#define QUADSPI_LUTKEY_VALUE		0x5AF05AF0
> +
> +#define QUADSPI_LCKCR			0x304
> +#define QUADSPI_LCKER_LOCK		0x1
> +#define QUADSPI_LCKER_UNLOCK		0x2
> +
> +#define QUADSPI_RSER			0x164
> +#define QUADSPI_RSER_TFIE       	(0x1 << 0)
> +
> +#define QUADSPI_LUT_BASE		0x310
> +
> +/* Field definitions for LUT register. */
> +#define OPRND0_SHIFT		0
> +#define PAD0_SHIFT		8
> +#define INSTR0_SHIFT		10
> +#define OPRND1_SHIFT		16
> +
> +/* Instruction set for the LUT register. */
> +#define CMD			1
> +#define ADDR			2
> +#define DUMMY			3
> +#define MODE			4
> +#define MODE2			5
> +#define MODE4			6
> +#define READ			7
> +#define WRITE			8
> +#define JMP_ON_CS		9
> +#define ADDR_DDR		10
> +#define MODE_DDR		11
> +#define MODE2_DDR		12
> +#define MODE4_DDR		13
> +
> +/*
> + * The PAD definitions for LUT register.
> + *
> + * The pad stands for the lines number of IO[0:3].
> + * For example, the Quad read need four IO lines, so you should
> + * set PAD4 which means we use four IO lines.
> + */
> +#define PAD1			0
> +#define PAD2			1
> +#define PAD4			2
> +
> +/* Oprands for the LUT register. */
> +#define ADDR24BIT		0x18
> +
> +/* Macros for constructing the LUT register. */
> +#define QUADSPI_LUT0(ins, pad, opr)					\
> +		(((opr) << OPRND0_SHIFT) | ((pad) << PAD0_SHIFT) |	\
> +		((ins) << INSTR0_SHIFT))
> +
> +#define QUADSPI_LUT1(ins, pad, opr) \
> +		(QUADSPI_LUT0((ins), (pad), (opr)) << OPRND1_SHIFT)
> +
> +/* other macros for LUT register. */
> +#define QUADSPI_LUT(x)          (QUADSPI_LUT_BASE + (x) * 4)
> +#define QUADSPI_LUT_NUM		64
> +
> +/* SEQID */
> +#define SEQID_QUAD_READ		0
> +#define SEQID_WREN		1
> +#define SEQID_FAST_READ 	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
> +
> +struct fsl_qspi_handler {
> +	int (*setup)(struct spi_device *);
> +	int (*do_one_msg)(struct spi_master *, struct spi_message *);
> +};
> +
> +enum fsl_qspi_devtype {
> +	FSL_QUADSPI_VYBRID,
> +	FSL_QUADSPI_IMX6SLX
> +};
> +
> +struct fsl_qspi_devtype_data {
> +	enum fsl_qspi_devtype devtype;
> +	u32 memmap_base;
> +	int rxfifo;
> +	int txfifo;
> +};
> +
> +static struct fsl_qspi_devtype_data vybrid_data = {
> +	.devtype = FSL_QUADSPI_VYBRID,
> +	.memmap_base = 0x20000000,
> +	.rxfifo = 128,
> +	.txfifo = 64
> +};
> +
> +struct fsl_qspi {
> +	void __iomem *iobase;
> +	struct clk *clk, *clk_en;
> +	struct device *dev;
> +	struct fsl_qspi_handler *h;
> +	struct completion c;
> +	struct fsl_qspi_devtype_data *devtype_data;
> +	void __iomem *ahb_base; /* Used when read from AHB bus */
> +	unsigned int addr;
> +	u32 nor_size; /* for mapping */
> +	u8 cmd;
> +	unsigned int quad_read_enabled:1;
> +};
> +
> +static inline int is_vybrid_qspi(struct fsl_qspi *q)
> +{
> +	return q->devtype_data->devtype == FSL_QUADSPI_VYBRID;
> +}
> +
> +/*
> + * An IC bug makes us to re-arrange the 32-bit data.
> + * The following chips, such as IMX6SLX, have fixed this bug.
> + */
> +static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
> +{
> +	return is_vybrid_qspi(q) ? __swab32(a) : a;
> +}
> +
> +static inline void qspi_unlock_lut(struct fsl_qspi *q)
> +{
> +	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
> +	writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
> +}
> +
> +static inline void qspi_lock_lut(struct fsl_qspi *q)
> +{
> +	writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
> +	writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
> +}
> +
> +static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id)
> +{
> +	struct fsl_qspi *q = dev_id;
> +	u32 reg;
> +
> +	/* clear interrupt */
> +	reg = readl(q->iobase + QUADSPI_FR);
> +	writel(reg, q->iobase + QUADSPI_FR);
> +
> +	if (reg & QUADSPI_FR_TFF_MASK)
> +		complete(&q->c);
> +
> +	dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x\n", reg);
> +	return IRQ_HANDLED;
> +}
> +
> +/* Init the LUT table. */
> +static void fsl_qspi_init_lut(struct fsl_qspi *q)
> +{
> +	void *__iomem base = q->iobase;
> +	int rxfifo = q->devtype_data->rxfifo;
> +	u32 lut_base;
> +	int i;
> +
> +	qspi_unlock_lut(q);
> +
> +	/* Clear all the LUT table */
> +	for (i = 0; i < QUADSPI_LUT_NUM; i++)
> +		writel(0, base + QUADSPI_LUT_BASE + i * 4);
> +
> +	/* Quad Read */
> +	lut_base = SEQID_QUAD_READ * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_QIOR)
> +			| QUADSPI_LUT1(ADDR, PAD4, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +	writel(QUADSPI_LUT0(MODE, PAD4, 0xff) | QUADSPI_LUT1(DUMMY, PAD4, 4),
> +			base + QUADSPI_LUT(lut_base + 1));
> +	writel(QUADSPI_LUT0(READ, PAD4, rxfifo),
> +			base + QUADSPI_LUT(lut_base + 2));
> +
> +	/* Write enable */
> +	lut_base = SEQID_WREN * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WREN),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Fast Read */
> +	lut_base = SEQID_FAST_READ * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_FAST_READ)
> +			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +	writel(QUADSPI_LUT0(DUMMY, PAD1, 8) | QUADSPI_LUT1(READ, PAD1, rxfifo),
> +			base + QUADSPI_LUT(lut_base + 1));
> +
> +	/* Page Program */
> +	lut_base = SEQID_PP * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_PP)
> +			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +	writel(QUADSPI_LUT0(WRITE, PAD1, 0),
> +			base + QUADSPI_LUT(lut_base + 1));
> +
> +	/* Read Status */
> +	lut_base = SEQID_RDSR * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDSR)
> +			| QUADSPI_LUT1(READ, PAD1, 0x1),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Erase a sector */
> +	lut_base = SEQID_SE * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_SE)
> +			| QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Erase the whole chip */
> +	lut_base = SEQID_CHIP_ERASE * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_CHIP_ERASE),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* READ ID */
> +	lut_base = SEQID_RDID * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDID)
> +			| QUADSPI_LUT1(READ, PAD1, 0x8),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Write Register */
> +	lut_base = SEQID_WRSR * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WRSR)
> +			| QUADSPI_LUT1(WRITE, PAD1, 0x2),
> +			base + QUADSPI_LUT(lut_base));
> +
> +	/* Read Configuration Register */
> +	lut_base = SEQID_RDCR * 4;
> +	writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDCR)
> +			| QUADSPI_LUT1(READ, PAD1, 0x1),
> +			base + QUADSPI_LUT(lut_base));
> +	qspi_lock_lut(q);
> +}
> +
> +/* Get the SEQID for the command */
> +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
> +{
> +	switch (cmd) {
> +	case OPCODE_WREN:
> +		return SEQID_WREN;
> +	case OPCODE_RDSR:
> +		return SEQID_RDSR;
> +	case OPCODE_SE:
> +		return SEQID_SE;
> +	case OPCODE_CHIP_ERASE:
> +		return SEQID_CHIP_ERASE;
> +	case OPCODE_PP:
> +		return SEQID_PP;
> +	case OPCODE_RDID:
> +		return SEQID_RDID;
> +	case OPCODE_WRSR:
> +		return SEQID_WRSR;
> +	case OPCODE_RDCR:
> +		return SEQID_RDCR;
> +	default:
> +		dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
> +		break;
> +	}
> +	return -1;
> +}
> +
> +static int
> +fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len)
> +{
> +	int seqid;
> +	u32 reg;
> +	int err;
> +
> +	init_completion(&q->c);
> +	dev_dbg(q->dev, "to @%.8x, len:%d, cmd:%.2x\n", addr, len, cmd);
> +
> +	/* save the reg */
> +	reg = readl(q->iobase + QUADSPI_MCR);
> +
> +	writel(q->devtype_data->memmap_base + addr, q->iobase + QUADSPI_SFAR);
> +	writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS,
> +			q->iobase + QUADSPI_RBCT);
> +	writel(QUADSPI_MCR_CLR_RXF_MASK | QUADSPI_MCR_RESERVED_MASK,
> +			q->iobase + QUADSPI_MCR);
> +
> +	/* trigger the LUT now */
> +	seqid = fsl_qspi_get_seqid(q, cmd);
> +	writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len,
> +		q->iobase + QUADSPI_IPCR);
> +
> +	/* Wait for the interrupt. */
> +	err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000));
> +	if (!err) {
> +		dev_err(q->dev,
> +			"cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n",
> +			cmd, addr, readl(q->iobase + QUADSPI_FR),
> +			readl(q->iobase + QUADSPI_SR));
> +		err = -ETIMEDOUT;
> +	} else {
> +		err = 0;
> +	}
> +
> +	/* restore the MCR */
> +	writel(reg, q->iobase + QUADSPI_MCR);
> +
> +	return err;
> +}
> +
> +/* Get the address from the tx buffer. */
> +static unsigned int
> +fsl_qspi_get_addr(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	unsigned int addr;
> +	u8 *buf = (u8 *)t->tx_buf;
> +
> +	/* 3-byte address */
> +	if (q->nor_size <= SZ_16M)
> +		addr = (buf[1] << 16) | (buf[2] << 8) |  buf[3];
> +	return addr;
> +}
> +
> +/* Read out the data from the buffer registers. */
> +static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u32 *rxbuf)
> +{
> +	u32 tmp;
> +	int i = 0;
> +
> +	while (len > 0) {
> +		tmp = readl(q->iobase + QUADSPI_RBDR + i * 4);
> +		*rxbuf = fsl_qspi_endian_xchg(q, tmp);
> +		dev_dbg(q->dev, "rcv: 0x%.8x, tmp : 0x%.8x\n", *rxbuf, tmp);
> +
> +		rxbuf++;
> +		len -= 4;
> +		i++;
> +	}
> +}
> +
> +/* Read out the data directly from the AHB buffer.*/
> +static int fsl_qspi_read_data_ahb(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	dev_dbg(q->dev, "cmd [%x],read from 0x%.8x,len:%d\n",
> +		q->cmd, q->addr, t->len);
> +	memcpy(t->rx_buf, q->ahb_base + q->addr, t->len);
> +	return 0;
> +}
> +
> +static u32 fsl_qspi_read_sr(struct fsl_qspi *q)
> +{
> +	u32 val = -EINVAL;
> +	int ret;
> +
> +	ret = fsl_qspi_runcmd(q, OPCODE_RDSR, 0, 1);
> +	if (!ret)
> +		fsl_qspi_read_data(q, 1, &val);
> +	return val;
> +}
> +
> +static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
> +{
> +	unsigned long deadline;
> +	u32 sr;
> +
> +	deadline = jiffies + msecs_to_jiffies(40000);
> +
> +	do {
> +		if ((sr = fsl_qspi_read_sr(q)) < 0)
> +			break;
> +		else if (!(sr & SR_WIP))
> +			return 0;
> +
> +		cond_resched();
> +
> +	} while (!time_after_eq(jiffies, deadline));
> +
> +	return 1;
> +}
> +
> +/*
> + * If we have changed the content of the flash by writing or erasing,
> + * we need to invalidate the AHB buffer. If we do not do so, we may read out
> + * the wrong data. The spec tells us reset the AHB domain and Serial Flash
> + * domain at the same time.
> + */
> +static inline void fsl_qspi_invalid(struct fsl_qspi *q)
> +{
> +	u32 reg;
> +
> +	reg = readl(q->iobase + QUADSPI_MCR);
> +	reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
> +	writel(reg, q->iobase + QUADSPI_MCR);
> +
> +	/*
> +	 * The minimum delay : 1 AHB + 2 SFCK clocks.
> +	 * Delay 1 us is enough.
> +	 */
> +	udelay(1);
> +
> +	reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
> +	writel(reg, q->iobase + QUADSPI_MCR);
> +}
> +
> +static int fsl_qspi_nor_write(struct fsl_qspi *q, u32 *txbuf, unsigned count)
> +{
> +	unsigned int addr = q->addr;
> +	int txfifo_size = q->devtype_data->txfifo;
> +	int ret = 0;
> +	int tx_size;
> +	u32 tmp;
> +	int i, j;
> +	u8 cmd = q->cmd;
> +
> +	q->cmd = -1; /* clear the cmd */
> +	dev_dbg(q->dev, "to @%.8x, len : %d\n", addr, count);
> +
> +	while (count > 0) {
> +		tx_size = (count > txfifo_size) ? txfifo_size : count;
> +
> +		/* clear the TX FIFO. */
> +		tmp = readl(q->iobase + QUADSPI_MCR);
> +		writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR);
> +
> +		/* fill the TX data to the FIFO */
> +		for (j = 0, i = ((tx_size + 3) / 4); j < i; j++) {
> +			tmp = fsl_qspi_endian_xchg(q, *txbuf);
> +			writel(tmp, q->iobase + QUADSPI_TBDR);
> +			txbuf++;
> +		}
> +
> +		/* Trigger it */
> +		ret = fsl_qspi_runcmd(q, cmd, addr, tx_size);
> +
> +		addr += tx_size;
> +		count -= tx_size;
> +
> +		/*
> +		 * If the TX FIFO is smaller then the size of Page Program,
> +		 * we have to wait until this Write is finished.
> +		 * For example, the TX FIFO is 64 bytes in the Vybrid,
> +		 * but the Page Program may writes 265 bytes per time.
> +		 * We are lucky that some chip(IMX6SLX) has increase the TX FIFO
> +		 * to 512 bytes.
> +		 *
> +		 * If we can change the @m25p->page_size, we can remove the
> +		 * following code.
> +		 */
> +		if (count > 0) {
> +			ret = fsl_qspi_wait_till_ready(q);
> +			if (ret) {
> +				dev_err(q->dev, "Reading SR, err:%d\n", ret);
> +				break;
> +			}
> +
> +			/* Write Enable again. */
> +			ret = fsl_qspi_runcmd(q, OPCODE_WREN, 0, 0);
> +			if (ret) {
> +				dev_err(q->dev, "Write Enable, err:%d\n", ret);
> +				break;
> +			}
> +		}
> +	}
> +	return ret;
> +}
> +
> +/* Switch to Quad read now. */
> +static inline void fsl_qspi_enable_quad_read(struct fsl_qspi *q)
> +{
> +	writel(SEQID_QUAD_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
> +		q->iobase + QUADSPI_BFGENCR);
> +	q->quad_read_enabled = 1;
> +}
> +
> +static int fsl_qspi_nor_tx(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	unsigned int addr = 0;
> +	bool need_invalid = false;
> +	int ret = 0;
> +	u32 val;
> +	u8 cmd;
> +
> +	/* This is the second spi_transfer for Page Program. */
> +	if (q->cmd == OPCODE_PP) {
> +		ret = fsl_qspi_nor_write(q, (u32 *)t->tx_buf, t->len);
> +		need_invalid = true;
> +		goto qspi_tx_out;
> +	}
> +
> +	cmd = *(u8 *)t->tx_buf;
> +	dev_dbg(q->dev, "NOR cmd is [0x%.2x], len : %d.\n", cmd, t->len);
> +
> +	switch (cmd) {
> +	case OPCODE_SE:
> +		addr = fsl_qspi_get_addr(q, t);
> +		/* fall through */
> +	case OPCODE_CHIP_ERASE:
> +		need_invalid = true;
> +	case OPCODE_WREN:
> +		ret = fsl_qspi_runcmd(q, cmd, addr, 0);
> +		q->cmd = -1;
> +		break;
> +
> +	case OPCODE_QIOR:
> +		if (!q->quad_read_enabled)
> +			fsl_qspi_enable_quad_read(q);
> +		/* fall through */
> +	case OPCODE_FAST_READ:
> +	case OPCODE_PP:
> +		q->cmd = cmd;
> +		q->addr = fsl_qspi_get_addr(q, t);
> +		break;
> +
> +	case OPCODE_WRSR:
> +		q->addr = 0;
> +		q->cmd = cmd;
> +		/* skip the cmd */
> +		memcpy((void *) &val, ((u8 *)t->tx_buf) + 1, t->len -1);
> +		ret = fsl_qspi_nor_write(q, &val, t->len - 1);
> +		break;
> +
> +	default:
> +		q->cmd = cmd;
> +		break;
> +	}
> +
> +qspi_tx_out:
> +	if (need_invalid)
> +		fsl_qspi_invalid(q);
> +	return ret;
> +}
> +
> +static int fsl_qspi_nor_rx(struct fsl_qspi *q, struct spi_transfer *t)
> +{
> +	int ret = 0;
> +
> +	switch (q->cmd) {
> +	case OPCODE_RDSR:
> +	case OPCODE_RDCR:
> +	case OPCODE_RDID:
> +		ret = fsl_qspi_runcmd(q, q->cmd, 0, t->len);
> +		if (!ret)
> +			fsl_qspi_read_data(q, t->len, t->rx_buf);
> +		break;
> +
> +	case OPCODE_QIOR:
> +	case OPCODE_FAST_READ:
> +		ret = fsl_qspi_read_data_ahb(q, t);
> +		break;
> +	default:
> +		dev_err(q->dev, "Unsupported cmd : %x\n", q->cmd);
> +		return -EINVAL;
> +	}
> +	return ret;
> +}
> +
> +static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
> +		struct spi_message *m)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +	struct spi_transfer *t;
> +	int ret = 0;
> +
> +	list_for_each_entry(t, &m->transfers, transfer_list) {
> +		if (t->rx_buf && t->tx_buf) {
> +			dev_err(q->dev,
> +				"Can't send and receive simultaneously\n");
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		if (t->tx_buf) {
> +			ret = fsl_qspi_nor_tx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +			continue;
> +		}
> +
> +		if (t->rx_buf) {
> +			ret = fsl_qspi_nor_rx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +		}
> +	}
> +
> +	m->status = ret;
> +	spi_finalize_current_message(master);
> +	return ret;
> +}
> +
> +/*
> + * There are two different ways to read out the data from the flash:
> + *  the "IP Command Read" and the "AHB Command Read".
> + *
> + * The IC guy suggests we use the "AHB Command Read" which is faster
> + * then the "IP Command Read". (What's more is that there is a bug in
> + * the "IP Command Read" in the Vybrid.)
> + *
> + * After we set up the registers for the "AHB Command Read", we can use
> + * the memcpy to read the data directly. A "missed" access to the buffer
> + * causes the controller to clear the buffer, and use the sequence pointed
> + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash.
> + */
> +static int fsl_qspi_init_abh_read(struct fsl_qspi *q)
> +{
> +	void __iomem *base = q->iobase;
> +	u32 memmap_base = q->devtype_data->memmap_base;
> +	int nor_size = q->nor_size;
> +
> +	/* Map the SPI NOR to accessiable address */
> +	writel(nor_size | memmap_base, base + QUADSPI_SFA1AD);
> +	writel(nor_size | memmap_base, base + QUADSPI_SFA2AD);
> +	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB1AD);
> +	writel((nor_size * 2) | memmap_base, base + QUADSPI_SFB2AD);
> +
> +	/* AHB configuration for access buffer 0/1/2 .*/
> +	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
> +	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
> +	writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
> +	writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR);
> +
> +	/* We only use the buffer3 */
> +	writel(0, base + QUADSPI_BUF0IND);
> +	writel(0, base + QUADSPI_BUF1IND);
> +	writel(0, base + QUADSPI_BUF2IND);
> +
> +	/* Set the default lut sequence for AHB Read. */
> +	writel(SEQID_FAST_READ << QUADSPI_BFGENCR_SEQID_SHIFT,
> +		base + QUADSPI_BFGENCR);
> +
> +	/* Map the AHB address for read. */
> +	q->ahb_base = ioremap(memmap_base, nor_size);
> +	if (!q->ahb_base)
> +		return -ENOMEM;
> +	return 0;
> +}
> +
> +static int fsl_qspi_nor_setup(struct spi_device *spi)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
> +	void __iomem *base = q->iobase;
> +	u32 reg_val, smpr_val;
> +	int ret;
> +
> +	writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_MDIS_MASK,
> +		base + QUADSPI_MCR);
> +
> +	reg_val = readl(base + QUADSPI_SMPR);
> +	writel(reg_val & ~(QUADSPI_SMPR_FSDLY_MASK
> +			| QUADSPI_SMPR_FSPHS_MASK
> +			| QUADSPI_SMPR_HSENA_MASK), base + QUADSPI_SMPR);
> +
> +	writel(QUADSPI_MCR_RESERVED_MASK, base + QUADSPI_MCR);
> +
> +	fsl_qspi_init_lut(q);
> +	ret = fsl_qspi_init_abh_read(q);
> +	if (ret < 0)
> +		return ret;
> +
> +	reg_val = 0;
> +	reg_val |= QUADSPI_MCR_RESERVED_MASK;
> +	smpr_val = readl(base + QUADSPI_SMPR);
> +	smpr_val &= ~QUADSPI_SMPR_DDRSMP_MASK;
> +	writel(smpr_val, base + QUADSPI_SMPR);
> +	reg_val &= ~QUADSPI_MCR_DDR_EN_MASK;
> +	writel(reg_val, base + QUADSPI_MCR);
> +
> +	/* enable the interrupt */
> +	writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
> +	return 0;
> +}
> +
> +/* We only support the NOR now. */
> +static struct fsl_qspi_handler fsl_qspi_nor_handler = {
> +	.setup = fsl_qspi_nor_setup,
> +	.do_one_msg = fsl_qspi_nor_do_one_msg,
> +};
> +
> +static int fsl_qspi_setup(struct spi_device *spi)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(spi->master);
> +
> +	return q->h->setup(spi);
> +}
> +
> +static int fsl_qspi_do_one_msg(struct spi_master *master,
> +		struct spi_message *m)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +
> +	return q->h->do_one_msg(master, m);
> +}
> +
> +static struct of_device_id fsl_qspi_dt_ids[] = {
> +	{ .compatible = "fsl,vf610-qspi", .data = (void*)&vybrid_data, },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
> +
> +static int fsl_qspi_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct spi_master *master;
> +	struct fsl_qspi *q;
> +	struct resource *res;
> +	int num_cs, ret = 0;
> +	const struct of_device_id *of_id =
> +			of_match_device(fsl_qspi_dt_ids, &pdev->dev);
> +
> +	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "can't get the spi-mum-chipselects\n");
> +		return ret;
> +	}
> +
> +	master = spi_alloc_master(&pdev->dev, sizeof(*q));
> +	if (!master)
> +		return -ENOMEM;
> +	q = spi_master_get_devdata(master);
> +
> +	ret = of_property_read_u32(np, "fsl,nor-size", &q->nor_size);
> +	if (ret < 0)
> +		dev_dbg(&pdev->dev, "can't get the nor size.\n");
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	q->iobase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(q->iobase)) {
> +		dev_err(&pdev->dev, "ioremap failed\n");
> +		ret = PTR_ERR(q->iobase);
> +		goto map_failed;
> +	}
> +
> +	q->clk_en = devm_clk_get(&pdev->dev, "qspi_en");
> +	q->clk = devm_clk_get(&pdev->dev, "qspi");
> +	if (IS_ERR(q->clk_en) || IS_ERR(q->clk)) {
> +		dev_err(&pdev->dev, "failed to get clocks\n");
> +		ret = -ENOENT;
> +		goto map_failed;
> +	}
> +
> +	ret = clk_prepare_enable(q->clk_en);
> +	if (ret) {
> +		dev_err(&pdev->dev, "can not enable the qspi_en clock\n");
> +		goto map_failed;
> +	}
> +
> +	ret = clk_prepare_enable(q->clk);
> +	if (ret) {
> +		clk_disable_unprepare(q->clk_en);
> +		dev_err(&pdev->dev, "can not enable the qspi clock\n");
> +		goto map_failed;
> +	}
> +
> +	ret = platform_get_irq(pdev, 0);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to get the irq\n");
> +		goto irq_failed;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, ret,
> +			fsl_qspi_irq_handler, 0, pdev->name, q);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to request irq.\n");
> +		goto irq_failed;
> +	}
> +
> +	q->dev = &pdev->dev;
> +	q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
> +
> +	/* The default handler is for NOR. */
> +	q->h = &fsl_qspi_nor_handler;
> +
> +	master->bus_num = pdev->id;
> +	master->num_chipselect = num_cs;
> +	master->dev.of_node = pdev->dev.of_node;
> +
> +	master->setup = fsl_qspi_setup;
> +	master->transfer_one_message = fsl_qspi_do_one_msg;
> +	platform_set_drvdata(pdev, master);
> +
> +	ret = spi_register_master(master);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register the spi master.\n");
> +		goto irq_failed;
> +	}
> +	dev_info(&pdev->dev, "QuadSPI bus driver\n");
> +	return 0;
> +
> +irq_failed:
> +	clk_disable_unprepare(q->clk);
> +	clk_disable_unprepare(q->clk_en);
> +map_failed:
> +	spi_master_put(master);
> +
> +	dev_err(&pdev->dev, "Freescale QuadSPI probe failed\n");
> +	return ret;
> +}
> +
> +static int fsl_qspi_remove(struct platform_device *pdev)
> +{
> +	struct spi_master *master = platform_get_drvdata(pdev);
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +
> +	/* disable the hardware */
> +	writel(0x0, q->iobase + QUADSPI_MCR);
> +	writel(0x0, q->iobase + QUADSPI_RSER);
> +
> +	clk_disable_unprepare(q->clk);
> +	clk_disable_unprepare(q->clk_en);
> +	spi_master_put(master);
> +	return 0;
> +}
> +
> +static struct platform_driver fsl_qspi_driver = {
> +	.driver = {
> +		.name	= "fsl-quadspi",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = fsl_qspi_dt_ids,
> +	},
> +	.probe          = fsl_qspi_probe,
> +	.remove		= fsl_qspi_remove,
> +};
> +module_platform_driver(fsl_qspi_driver);
> +
> +MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.7.1
> 
> 

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-19  4:10   ` Huang Shijie
@ 2013-08-22 19:34     ` Brian Norris
  -1 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-22 19:34 UTC (permalink / raw)
  To: Huang Shijie
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	broonie, linux-mtd, kernel, shawn.guo, dwmw2, wangyuhang,
	linux-arm-kernel

Adding others (keep devicetree@vger.kernel.org in the loop)

On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:
> This patch adds the quad read support:
> 
> (1) Add the relative commands:
>       OPCODE_QIOR, OPCODE_4QIOR, OPCODE_RDCR,
> 
>     also add the relative macro for the Configuartion Register.
> 
> (2) add the "m25p,quad-read" property for the m25p80 driver
>     If the dts has the "m25p,quad-read" property, the kernel will
>     set the Quad bit of the configuration register, and when the
>     setting is suceeded, we set the read opcode with OPCODE_QIOR.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
>  Documentation/devicetree/bindings/mtd/m25p80.txt |    5 ++
>  drivers/mtd/devices/m25p80.c                     |   51 ++++++++++++++++++++++
>  include/linux/mtd/spi-nor.h                      |    6 +++
>  3 files changed, 62 insertions(+), 0 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt
> index 6d3d576..b33313f 100644
> --- a/Documentation/devicetree/bindings/mtd/m25p80.txt
> +++ b/Documentation/devicetree/bindings/mtd/m25p80.txt
> @@ -17,6 +17,11 @@ Optional properties:
>                     Refer to your chips' datasheet to check if this is supported
>                     by your chip.
>  
> +- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
> +                   of the usual "read" opcode. This opcode is not supported by
> +                   all chips and support for it can not be detected at runtime.
> +                   Refer to your chips' datasheet to check if this is supported
> +                   by your chip.

Why can't this be detected at runtime? We added a "no fast read" flag to
the device table, so why not "dual/quad mode supported"? And believe it
or not, not all m25p80 users have device tree. So it isn't very logical
to tie this support to device-tree only.

>  Example:
>  
>  	flash: m25p80@0 {
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index f3598c1..4bc9b1b 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -103,6 +103,40 @@ static int write_sr(struct m25p *flash, u8 val)
>  }
>  
>  /*
> + * Read the configuration register, returning its value in the location
> + * Return the configuration register value.
> + * Returns negative if error occurred.
> + */
> +static int read_cr(struct m25p *flash)
> +{
> +	u8 code = OPCODE_RDCR;
> +	int ret;
> +	u8 val;
> +
> +	ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
> +	if (ret < 0) {
> +		dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
> +		return ret;
> +	}
> +	return val;
> +}
> +
> +/*
> + * Write status register and configuration register with 2 bytes
> + * The first byte will be written to the status register, while the second byte
> + * will be written to the configuration register.
> + * Returns negative if error occurred.
> + */
> +static int write_sr_cr(struct m25p *flash, u16 val)
> +{
> +	flash->command[0] = OPCODE_WRSR;
> +	flash->command[1] = 0;
> +	flash->command[2] = (val >> 8);
> +
> +	return spi_write(flash->spi, flash->command, 3);
> +}
> +
> +/*
>   * Set write enable latch with Write Enable command.
>   * Returns negative if error occurred.
>   */
> @@ -880,6 +914,8 @@ static int m25p_probe(struct spi_device *spi)
>  	unsigned			i;
>  	struct mtd_part_parser_data	ppdata;
>  	struct device_node __maybe_unused *np = spi->dev.of_node;
> +	u16 sr_cr;
> +	int ret;
>  
>  #ifdef CONFIG_MTD_OF_PARTS
>  	if (!of_device_is_available(np))
> @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>  	else
>  		flash->read_opcode = OPCODE_NORM_READ;
>  
> +	/* Try to enable the Quad Read */
> +	if (np && of_property_read_bool(np, "m25p,quad-read")) {
> +		/* The configuration register is set by the second byte. */
> +		sr_cr = CR_QUAD << 8;
> +
> +		/* Write the QUAD bit to the Configuration Register. */
> +		write_enable(flash);
> +		if (write_sr_cr(flash, sr_cr) == 0) {
> +			/* read back and check it */
> +			ret = read_cr(flash);
> +			if (ret > 0 && (ret & CR_QUAD))
> +				flash->read_opcode = OPCODE_QIOR;

This is not correct. You are assuming that the SPI master knows to read
with 4 IO lines, instead of the traditional 1 line; IOW, you are
assuming that:
(1) if the slave DT node has "quad-read", then the whole system supports
    it (bad design; you're putting assumptions about the "parent" node
    in the child)
(2) the controller driver will act as your new driver does -- that it
    will decode these commands and recognize that the quad read command
    should be run on 4 IO lines, not just 1

What you're really missing from device-tree (and the SPI subystem in
general) is how to detect those SPI controllers which support dual and
quad mode transfers, and how to communicate that a particular SPI
transaction should be performed on 1 or 4 IO lines. We shouldn't have
this just hacked in via assumptions.

See Wang Yuhang's patch set for adding proper dual/quad support to the
SPI subsystem. It seems to be addressing (1) and (2) in a better way.

  http://thread.gmane.org/gmane.linux.kernel.spi.devel/14420

(I can't find patch 2/2)

> +		}
> +	}
> +
>  	flash->program_opcode = OPCODE_PP;
>  
>  	if (info->addr_width)
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index b420a5b..d5b189d 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -39,6 +39,9 @@
>  
>  /* Used for Spansion flashes only. */
>  #define	OPCODE_BRWR		0x17	/* Bank register write */
> +#define OPCODE_QIOR		0xeb	/* Quad read */
> +#define OPCODE_4QIOR		0xec	/* Quad read */

Use tab between 'define' and 'OPCODE...', like OPCODE_RDCR below.

> +#define	OPCODE_RDCR		0x35	/* Read configuration register */
>  
>  /* Status Register bits. */
>  #define	SR_WIP			1	/* Write in progress */
> @@ -49,4 +52,7 @@
>  #define	SR_BP2			0x10	/* Block protect 2 */
>  #define	SR_SRWD			0x80	/* SR write protect */
>  
> +/* Configuration Register bits. */
> +#define CR_QUAD			0x2	/* Quad I/O */
> +
>  #endif /* __LINUX_MTD_SPI_NOR_H */

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-22 19:34     ` Brian Norris
  0 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-22 19:34 UTC (permalink / raw)
  To: linux-arm-kernel

Adding others (keep devicetree at vger.kernel.org in the loop)

On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:
> This patch adds the quad read support:
> 
> (1) Add the relative commands:
>       OPCODE_QIOR, OPCODE_4QIOR, OPCODE_RDCR,
> 
>     also add the relative macro for the Configuartion Register.
> 
> (2) add the "m25p,quad-read" property for the m25p80 driver
>     If the dts has the "m25p,quad-read" property, the kernel will
>     set the Quad bit of the configuration register, and when the
>     setting is suceeded, we set the read opcode with OPCODE_QIOR.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
>  Documentation/devicetree/bindings/mtd/m25p80.txt |    5 ++
>  drivers/mtd/devices/m25p80.c                     |   51 ++++++++++++++++++++++
>  include/linux/mtd/spi-nor.h                      |    6 +++
>  3 files changed, 62 insertions(+), 0 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt
> index 6d3d576..b33313f 100644
> --- a/Documentation/devicetree/bindings/mtd/m25p80.txt
> +++ b/Documentation/devicetree/bindings/mtd/m25p80.txt
> @@ -17,6 +17,11 @@ Optional properties:
>                     Refer to your chips' datasheet to check if this is supported
>                     by your chip.
>  
> +- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
> +                   of the usual "read" opcode. This opcode is not supported by
> +                   all chips and support for it can not be detected at runtime.
> +                   Refer to your chips' datasheet to check if this is supported
> +                   by your chip.

Why can't this be detected at runtime? We added a "no fast read" flag to
the device table, so why not "dual/quad mode supported"? And believe it
or not, not all m25p80 users have device tree. So it isn't very logical
to tie this support to device-tree only.

>  Example:
>  
>  	flash: m25p80 at 0 {
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index f3598c1..4bc9b1b 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -103,6 +103,40 @@ static int write_sr(struct m25p *flash, u8 val)
>  }
>  
>  /*
> + * Read the configuration register, returning its value in the location
> + * Return the configuration register value.
> + * Returns negative if error occurred.
> + */
> +static int read_cr(struct m25p *flash)
> +{
> +	u8 code = OPCODE_RDCR;
> +	int ret;
> +	u8 val;
> +
> +	ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
> +	if (ret < 0) {
> +		dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
> +		return ret;
> +	}
> +	return val;
> +}
> +
> +/*
> + * Write status register and configuration register with 2 bytes
> + * The first byte will be written to the status register, while the second byte
> + * will be written to the configuration register.
> + * Returns negative if error occurred.
> + */
> +static int write_sr_cr(struct m25p *flash, u16 val)
> +{
> +	flash->command[0] = OPCODE_WRSR;
> +	flash->command[1] = 0;
> +	flash->command[2] = (val >> 8);
> +
> +	return spi_write(flash->spi, flash->command, 3);
> +}
> +
> +/*
>   * Set write enable latch with Write Enable command.
>   * Returns negative if error occurred.
>   */
> @@ -880,6 +914,8 @@ static int m25p_probe(struct spi_device *spi)
>  	unsigned			i;
>  	struct mtd_part_parser_data	ppdata;
>  	struct device_node __maybe_unused *np = spi->dev.of_node;
> +	u16 sr_cr;
> +	int ret;
>  
>  #ifdef CONFIG_MTD_OF_PARTS
>  	if (!of_device_is_available(np))
> @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>  	else
>  		flash->read_opcode = OPCODE_NORM_READ;
>  
> +	/* Try to enable the Quad Read */
> +	if (np && of_property_read_bool(np, "m25p,quad-read")) {
> +		/* The configuration register is set by the second byte. */
> +		sr_cr = CR_QUAD << 8;
> +
> +		/* Write the QUAD bit to the Configuration Register. */
> +		write_enable(flash);
> +		if (write_sr_cr(flash, sr_cr) == 0) {
> +			/* read back and check it */
> +			ret = read_cr(flash);
> +			if (ret > 0 && (ret & CR_QUAD))
> +				flash->read_opcode = OPCODE_QIOR;

This is not correct. You are assuming that the SPI master knows to read
with 4 IO lines, instead of the traditional 1 line; IOW, you are
assuming that:
(1) if the slave DT node has "quad-read", then the whole system supports
    it (bad design; you're putting assumptions about the "parent" node
    in the child)
(2) the controller driver will act as your new driver does -- that it
    will decode these commands and recognize that the quad read command
    should be run on 4 IO lines, not just 1

What you're really missing from device-tree (and the SPI subystem in
general) is how to detect those SPI controllers which support dual and
quad mode transfers, and how to communicate that a particular SPI
transaction should be performed on 1 or 4 IO lines. We shouldn't have
this just hacked in via assumptions.

See Wang Yuhang's patch set for adding proper dual/quad support to the
SPI subsystem. It seems to be addressing (1) and (2) in a better way.

  http://thread.gmane.org/gmane.linux.kernel.spi.devel/14420

(I can't find patch 2/2)

> +		}
> +	}
> +
>  	flash->program_opcode = OPCODE_PP;
>  
>  	if (info->addr_width)
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index b420a5b..d5b189d 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -39,6 +39,9 @@
>  
>  /* Used for Spansion flashes only. */
>  #define	OPCODE_BRWR		0x17	/* Bank register write */
> +#define OPCODE_QIOR		0xeb	/* Quad read */
> +#define OPCODE_4QIOR		0xec	/* Quad read */

Use tab between 'define' and 'OPCODE...', like OPCODE_RDCR below.

> +#define	OPCODE_RDCR		0x35	/* Read configuration register */
>  
>  /* Status Register bits. */
>  #define	SR_WIP			1	/* Write in progress */
> @@ -49,4 +52,7 @@
>  #define	SR_BP2			0x10	/* Block protect 2 */
>  #define	SR_SRWD			0x80	/* SR write protect */
>  
> +/* Configuration Register bits. */
> +#define CR_QUAD			0x2	/* Quad I/O */
> +
>  #endif /* __LINUX_MTD_SPI_NOR_H */

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-22 19:34     ` Brian Norris
@ 2013-08-22 19:55       ` Mark Brown
  -1 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-22 19:55 UTC (permalink / raw)
  To: Brian Norris
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, shawn.guo, dwmw2, wangyuhang,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 2271 bytes --]

On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
> On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:

> > +- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
> > +                   of the usual "read" opcode. This opcode is not supported by
> > +                   all chips and support for it can not be detected at runtime.
> > +                   Refer to your chips' datasheet to check if this is supported
> > +                   by your chip.

> Why can't this be detected at runtime? We added a "no fast read" flag to
> the device table, so why not "dual/quad mode supported"? And believe it
> or not, not all m25p80 users have device tree. So it isn't very logical
> to tie this support to device-tree only.

There needs to be some way of saying if the additional data lines are
actually wired up or not; it could be a negative property (flagging if
the lines are not present) but that runs the risk of breaking systems
if a driver acquires the ability to support extra data lines but a
system doesn't have it.

This should be a generic property for all quad devices to use, though,
since the same thing applies everywhere.

> This is not correct. You are assuming that the SPI master knows to read
> with 4 IO lines, instead of the traditional 1 line; IOW, you are
> assuming that:
> (1) if the slave DT node has "quad-read", then the whole system supports
>     it (bad design; you're putting assumptions about the "parent" node
>     in the child)

This is fine, the device tree is for the board as a whole not for the
individual chips - if the board doesn't support quad read the device
tree shouldn't configure the chip for quad mode.  It really needs to be
a slave property since a system could opt to connect some devices with
extra data lines and some without on the same SPI bus.

> What you're really missing from device-tree (and the SPI subystem in
> general) is how to detect those SPI controllers which support dual and
> quad mode transfers, and how to communicate that a particular SPI
> transaction should be performed on 1 or 4 IO lines. We shouldn't have
> this just hacked in via assumptions.

That bit does need to be fixed in this driver, yes.  The SPI core now
has quad mode support.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-22 19:55       ` Mark Brown
  0 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-22 19:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
> On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:

> > +- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
> > +                   of the usual "read" opcode. This opcode is not supported by
> > +                   all chips and support for it can not be detected at runtime.
> > +                   Refer to your chips' datasheet to check if this is supported
> > +                   by your chip.

> Why can't this be detected at runtime? We added a "no fast read" flag to
> the device table, so why not "dual/quad mode supported"? And believe it
> or not, not all m25p80 users have device tree. So it isn't very logical
> to tie this support to device-tree only.

There needs to be some way of saying if the additional data lines are
actually wired up or not; it could be a negative property (flagging if
the lines are not present) but that runs the risk of breaking systems
if a driver acquires the ability to support extra data lines but a
system doesn't have it.

This should be a generic property for all quad devices to use, though,
since the same thing applies everywhere.

> This is not correct. You are assuming that the SPI master knows to read
> with 4 IO lines, instead of the traditional 1 line; IOW, you are
> assuming that:
> (1) if the slave DT node has "quad-read", then the whole system supports
>     it (bad design; you're putting assumptions about the "parent" node
>     in the child)

This is fine, the device tree is for the board as a whole not for the
individual chips - if the board doesn't support quad read the device
tree shouldn't configure the chip for quad mode.  It really needs to be
a slave property since a system could opt to connect some devices with
extra data lines and some without on the same SPI bus.

> What you're really missing from device-tree (and the SPI subystem in
> general) is how to detect those SPI controllers which support dual and
> quad mode transfers, and how to communicate that a particular SPI
> transaction should be performed on 1 or 4 IO lines. We shouldn't have
> this just hacked in via assumptions.

That bit does need to be fixed in this driver, yes.  The SPI core now
has quad mode support.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130822/9b508c5f/attachment.sig>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-22 19:55       ` Mark Brown
@ 2013-08-22 20:29         ` Marek Vasut
  -1 siblings, 0 replies; 68+ messages in thread
From: Marek Vasut @ 2013-08-22 20:29 UTC (permalink / raw)
  To: Mark Brown
  Cc: devicetree, shawn.guo, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, Brian Norris, dwmw2, wangyuhang,
	linux-arm-kernel

Dear Mark Brown,

> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
> > On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:
> > > +- m25p,quad-read : Use the "quad read" opcode to read data from the
> > > chip instead +                   of the usual "read" opcode. This
> > > opcode is not supported by +                   all chips and support
> > > for it can not be detected at runtime. +                   Refer to
> > > your chips' datasheet to check if this is supported +                 
> > >  by your chip.
> > 
> > Why can't this be detected at runtime? We added a "no fast read" flag to
> > the device table, so why not "dual/quad mode supported"? And believe it
> > or not, not all m25p80 users have device tree. So it isn't very logical
> > to tie this support to device-tree only.
> 
> There needs to be some way of saying if the additional data lines are
> actually wired up or not; it could be a negative property (flagging if
> the lines are not present) but that runs the risk of breaking systems
> if a driver acquires the ability to support extra data lines but a
> system doesn't have it.
> 
> This should be a generic property for all quad devices to use, though,
> since the same thing applies everywhere.

Full ACK, "m25p,dual-read" and "m25p,quad-read" sound like a good prop names?

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-22 20:29         ` Marek Vasut
  0 siblings, 0 replies; 68+ messages in thread
From: Marek Vasut @ 2013-08-22 20:29 UTC (permalink / raw)
  To: linux-arm-kernel

Dear Mark Brown,

> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
> > On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:
> > > +- m25p,quad-read : Use the "quad read" opcode to read data from the
> > > chip instead +                   of the usual "read" opcode. This
> > > opcode is not supported by +                   all chips and support
> > > for it can not be detected at runtime. +                   Refer to
> > > your chips' datasheet to check if this is supported +                 
> > >  by your chip.
> > 
> > Why can't this be detected at runtime? We added a "no fast read" flag to
> > the device table, so why not "dual/quad mode supported"? And believe it
> > or not, not all m25p80 users have device tree. So it isn't very logical
> > to tie this support to device-tree only.
> 
> There needs to be some way of saying if the additional data lines are
> actually wired up or not; it could be a negative property (flagging if
> the lines are not present) but that runs the risk of breaking systems
> if a driver acquires the ability to support extra data lines but a
> system doesn't have it.
> 
> This should be a generic property for all quad devices to use, though,
> since the same thing applies everywhere.

Full ACK, "m25p,dual-read" and "m25p,quad-read" sound like a good prop names?

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-22 20:29         ` Marek Vasut
@ 2013-08-22 23:36           ` Mark Brown
  -1 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-22 23:36 UTC (permalink / raw)
  To: Marek Vasut
  Cc: devicetree, shawn.guo, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, Brian Norris, dwmw2, wangyuhang,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 664 bytes --]

On Thu, Aug 22, 2013 at 10:29:01PM +0200, Marek Vasut wrote:
> > On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:

> > This should be a generic property for all quad devices to use, though,
> > since the same thing applies everywhere.

> Full ACK, "m25p,dual-read" and "m25p,quad-read" sound like a good prop names?

I was actually thinking something more generic than that - putting the
property at the SPI generic bindings level.  Though if all flashes with
this dual/quad read functionality have the prefix m25p the above would
work also, at the minute this does seem to be mostly used by flash (I
bet someone's got some DSPs or something though).

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-22 23:36           ` Mark Brown
  0 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-22 23:36 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 22, 2013 at 10:29:01PM +0200, Marek Vasut wrote:
> > On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:

> > This should be a generic property for all quad devices to use, though,
> > since the same thing applies everywhere.

> Full ACK, "m25p,dual-read" and "m25p,quad-read" sound like a good prop names?

I was actually thinking something more generic than that - putting the
property at the SPI generic bindings level.  Though if all flashes with
this dual/quad read functionality have the prefix m25p the above would
work also, at the minute this does seem to be mostly used by flash (I
bet someone's got some DSPs or something though).
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130823/815351d5/attachment-0001.sig>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-22 23:36           ` Mark Brown
@ 2013-08-22 23:58             ` Marek Vasut
  -1 siblings, 0 replies; 68+ messages in thread
From: Marek Vasut @ 2013-08-22 23:58 UTC (permalink / raw)
  To: Mark Brown
  Cc: devicetree, shawn.guo, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, Brian Norris, dwmw2, wangyuhang,
	linux-arm-kernel

Dear Mark Brown,

> On Thu, Aug 22, 2013 at 10:29:01PM +0200, Marek Vasut wrote:
> > > On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
> > > 
> > > This should be a generic property for all quad devices to use, though,
> > > since the same thing applies everywhere.
> > 
> > Full ACK, "m25p,dual-read" and "m25p,quad-read" sound like a good prop
> > names?
> 
> I was actually thinking something more generic than that - putting the
> property at the SPI generic bindings level.  Though if all flashes with
> this dual/quad read functionality have the prefix m25p the above would
> work also, at the minute this does seem to be mostly used by flash (I
> bet someone's got some DSPs or something though).

Ah! So you mean the SPI controller would provide information that it can do 
dual/quad transfers? But then, the additional pins can only be wired to certain 
chips (controller by certain CS lines).

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-22 23:58             ` Marek Vasut
  0 siblings, 0 replies; 68+ messages in thread
From: Marek Vasut @ 2013-08-22 23:58 UTC (permalink / raw)
  To: linux-arm-kernel

Dear Mark Brown,

> On Thu, Aug 22, 2013 at 10:29:01PM +0200, Marek Vasut wrote:
> > > On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
> > > 
> > > This should be a generic property for all quad devices to use, though,
> > > since the same thing applies everywhere.
> > 
> > Full ACK, "m25p,dual-read" and "m25p,quad-read" sound like a good prop
> > names?
> 
> I was actually thinking something more generic than that - putting the
> property at the SPI generic bindings level.  Though if all flashes with
> this dual/quad read functionality have the prefix m25p the above would
> work also, at the minute this does seem to be mostly used by flash (I
> bet someone's got some DSPs or something though).

Ah! So you mean the SPI controller would provide information that it can do 
dual/quad transfers? But then, the additional pins can only be wired to certain 
chips (controller by certain CS lines).

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
  2013-08-22 19:21     ` Brian Norris
@ 2013-08-23  2:14       ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  2:14 UTC (permalink / raw)
  To: Brian Norris
  Cc: devicetree, b44548, dedekind1, b18965, linux-spi, broonie,
	linux-mtd, kernel, shawn.guo, dwmw2, linux-arm-kernel

于 2013年08月23日 03:21, Brian Norris 写道:
> Can this controller support more than one chip? If so, then the nor-size
> property makes even less sense. See below.
>
yes.

this controller can supports two same chips at the same time.
>> >  +- clocks : The clocks needed by the QuadSPI controller
>> >  +- clock-names : the name of the clocks
>> >  +
>> >  +Optional properties:
>> >  +- fsl,nor-size : The NOR size used by the QuadSPI mapping.
> Why does the size of the NOR flash need to be in the controller's device
> node? Shouldn't this be detected at run-time if possible? Or at least
yes, i can parse out the NOR size by the DT node.

I can remove this DT property in the next version.


> included as a property of m25p80, if absolutely required?
the m25p80 has already contains the NOR size by: sector_size * n_sectors.
but as a spi driver , there is no API i can use to get the NOR size.

thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
@ 2013-08-23  2:14       ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  2:14 UTC (permalink / raw)
  To: linux-arm-kernel

? 2013?08?23? 03:21, Brian Norris ??:
> Can this controller support more than one chip? If so, then the nor-size
> property makes even less sense. See below.
>
yes.

this controller can supports two same chips at the same time.
>> >  +- clocks : The clocks needed by the QuadSPI controller
>> >  +- clock-names : the name of the clocks
>> >  +
>> >  +Optional properties:
>> >  +- fsl,nor-size : The NOR size used by the QuadSPI mapping.
> Why does the size of the NOR flash need to be in the controller's device
> node? Shouldn't this be detected at run-time if possible? Or at least
yes, i can parse out the NOR size by the DT node.

I can remove this DT property in the next version.


> included as a property of m25p80, if absolutely required?
the m25p80 has already contains the NOR size by: sector_size * n_sectors.
but as a spi driver , there is no API i can use to get the NOR size.

thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-22 19:55       ` Mark Brown
@ 2013-08-23  6:26         ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  6:26 UTC (permalink / raw)
  To: Mark Brown
  Cc: Marek Vasut, devicetree, shawn.guo, b44548, dedekind1, b18965,
	linux-spi, linux-mtd, kernel, Brian Norris, dwmw2, wangyuhang,
	linux-arm-kernel

于 2013年08月23日 03:55, Mark Brown 写道:
> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
>> On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:
>
>> Why can't this be detected at runtime? We added a "no fast read" flag to
>> the device table, so why not "dual/quad mode supported"? And believe it
>> or not, not all m25p80 users have device tree. So it isn't very logical
>> to tie this support to device-tree only.
>
Hi Brian:
My only question here is do we need to add some flags , such as "quad
read", to
the device table? If you think we need to, i will add a patch for it.

(Just as Brown said, when the DTS configurates the "m25p,quad-read"
property, it
means : Both the board and the NOR support the Quad read.)


>> What you're really missing from device-tree (and the SPI subystem in
>> general) is how to detect those SPI controllers which support dual and
>> quad mode transfers, and how to communicate that a particular SPI
>> transaction should be performed on 1 or 4 IO lines. We shouldn't have
>> this just hacked in via assumptions.
> That bit does need to be fixed in this driver, yes.  The SPI core now
> has quad mode support.
yes, i do not need these bits in this driver.


thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23  6:26         ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  6:26 UTC (permalink / raw)
  To: linux-arm-kernel

? 2013?08?23? 03:55, Mark Brown ??:
> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
>> On Mon, Aug 19, 2013 at 12:10:01PM +0800, Huang Shijie wrote:
>
>> Why can't this be detected at runtime? We added a "no fast read" flag to
>> the device table, so why not "dual/quad mode supported"? And believe it
>> or not, not all m25p80 users have device tree. So it isn't very logical
>> to tie this support to device-tree only.
>
Hi Brian:
My only question here is do we need to add some flags , such as "quad
read", to
the device table? If you think we need to, i will add a patch for it.

(Just as Brown said, when the DTS configurates the "m25p,quad-read"
property, it
means : Both the board and the NOR support the Quad read.)


>> What you're really missing from device-tree (and the SPI subystem in
>> general) is how to detect those SPI controllers which support dual and
>> quad mode transfers, and how to communicate that a particular SPI
>> transaction should be performed on 1 or 4 IO lines. We shouldn't have
>> this just hacked in via assumptions.
> That bit does need to be fixed in this driver, yes.  The SPI core now
> has quad mode support.
yes, i do not need these bits in this driver.


thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
  2013-08-22 19:21     ` Brian Norris
@ 2013-08-23  6:59       ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  6:59 UTC (permalink / raw)
  To: Brian Norris
  Cc: devicetree, b44548, dedekind1, b18965, linux-spi, broonie,
	linux-mtd, kernel, shawn.guo, dwmw2, linux-arm-kernel

于 2013年08月23日 03:21, Brian Norris 写道:
> Why does the size of the NOR flash need to be in the controller's device
> node? Shouldn't this be detected at run-time if possible? Or at least
> included as a property of m25p80, if absolutely required?
>
I suddenly realize that i need this property.

Since we may connect other devices, not NOR flash, to the Quadspi.

So this property shows us we connect a NOR to the Quadspi.



thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
@ 2013-08-23  6:59       ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  6:59 UTC (permalink / raw)
  To: linux-arm-kernel

? 2013?08?23? 03:21, Brian Norris ??:
> Why does the size of the NOR flash need to be in the controller's device
> node? Shouldn't this be detected at run-time if possible? Or at least
> included as a property of m25p80, if absolutely required?
>
I suddenly realize that i need this property.

Since we may connect other devices, not NOR flash, to the Quadspi.

So this property shows us we connect a NOR to the Quadspi.



thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-19  4:10   ` Huang Shijie
@ 2013-08-23  9:05     ` yuhang wang
  -1 siblings, 0 replies; 68+ messages in thread
From: yuhang wang @ 2013-08-23  9:05 UTC (permalink / raw)
  To: Huang Shijie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Mark Brown,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

Hi, Shijie

2013/8/19 Huang Shijie <b32955@freescale.com>:
> This patch adds the quad read support:
>
> (1) Add the relative commands:
>       OPCODE_QIOR, OPCODE_4QIOR, OPCODE_RDCR,
>
>     also add the relative macro for the Configuartion Register.
>
> (2) add the "m25p,quad-read" property for the m25p80 driver
>     If the dts has the "m25p,quad-read" property, the kernel will
>     set the Quad bit of the configuration register, and when the
>     setting is suceeded, we set the read opcode with OPCODE_QIOR.
>
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
>  Documentation/devicetree/bindings/mtd/m25p80.txt |    5 ++
>  drivers/mtd/devices/m25p80.c                     |   51 ++++++++++++++++++++++
>  include/linux/mtd/spi-nor.h                      |    6 +++
>  3 files changed, 62 insertions(+), 0 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt
> index 6d3d576..b33313f 100644
> --- a/Documentation/devicetree/bindings/mtd/m25p80.txt
> +++ b/Documentation/devicetree/bindings/mtd/m25p80.txt
> @@ -17,6 +17,11 @@ Optional properties:
>                     Refer to your chips' datasheet to check if this is supported
>                     by your chip.
>
> +- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
> +                   of the usual "read" opcode. This opcode is not supported by
> +                   all chips and support for it can not be detected at runtime.
> +                   Refer to your chips' datasheet to check if this is supported
> +                   by your chip.
>  Example:
>
>         flash: m25p80@0 {
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index f3598c1..4bc9b1b 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -103,6 +103,40 @@ static int write_sr(struct m25p *flash, u8 val)
>  }
>
>  /*
> + * Read the configuration register, returning its value in the location
> + * Return the configuration register value.
> + * Returns negative if error occurred.
> + */
> +static int read_cr(struct m25p *flash)
> +{
> +       u8 code = OPCODE_RDCR;
> +       int ret;
> +       u8 val;
> +
> +       ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
> +       if (ret < 0) {
> +               dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
> +               return ret;
> +       }
> +       return val;
> +}
> +
> +/*
> + * Write status register and configuration register with 2 bytes
> + * The first byte will be written to the status register, while the second byte
> + * will be written to the configuration register.
> + * Returns negative if error occurred.
> + */
> +static int write_sr_cr(struct m25p *flash, u16 val)
> +{
> +       flash->command[0] = OPCODE_WRSR;
> +       flash->command[1] = 0;
> +       flash->command[2] = (val >> 8);
> +
> +       return spi_write(flash->spi, flash->command, 3);
> +}
> +
> +/*
>   * Set write enable latch with Write Enable command.
>   * Returns negative if error occurred.
>   */
> @@ -880,6 +914,8 @@ static int m25p_probe(struct spi_device *spi)
>         unsigned                        i;
>         struct mtd_part_parser_data     ppdata;
>         struct device_node __maybe_unused *np = spi->dev.of_node;
> +       u16 sr_cr;
> +       int ret;
>
>  #ifdef CONFIG_MTD_OF_PARTS
>         if (!of_device_is_available(np))
> @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>         else
>                 flash->read_opcode = OPCODE_NORM_READ;
>
> +       /* Try to enable the Quad Read */
> +       if (np && of_property_read_bool(np, "m25p,quad-read")) {
> +               /* The configuration register is set by the second byte. */
> +               sr_cr = CR_QUAD << 8;
> +
> +               /* Write the QUAD bit to the Configuration Register. */
> +               write_enable(flash);
> +               if (write_sr_cr(flash, sr_cr) == 0) {
> +                       /* read back and check it */
> +                       ret = read_cr(flash);
> +                       if (ret > 0 && (ret & CR_QUAD))
> +                               flash->read_opcode = OPCODE_QIOR;
> +               }
> +       }
> +

Well, M25p80.c support lots of flash devices, so driver should be as
general as possible. Firstly not all the devices m25p80 supports set
quad mode as your sequence, perhaps write_sr_cr can not match all the
m25p80 flash. Secondly, why you only support QIOR(high performance)
not QOR or DOR. Maybe QIOR seems too special, so what if user want to
use QOR if he set quad mode in DTS.

Another point, if command changed to OPCODE_QIOR, there should also
should be some correct in m25p_read. such as the number of dummy data.
QIOR can support read without read command if set the certain bit in
transfer, these aspects did not reflect in your patch.

>         flash->program_opcode = OPCODE_PP;
>
>         if (info->addr_width)
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index b420a5b..d5b189d 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -39,6 +39,9 @@
>
>  /* Used for Spansion flashes only. */
>  #define        OPCODE_BRWR             0x17    /* Bank register write */
> +#define OPCODE_QIOR            0xeb    /* Quad read */
> +#define OPCODE_4QIOR           0xec    /* Quad read */
> +#define        OPCODE_RDCR             0x35    /* Read configuration register */
>
>  /* Status Register bits. */
>  #define        SR_WIP                  1       /* Write in progress */
> @@ -49,4 +52,7 @@
>  #define        SR_BP2                  0x10    /* Block protect 2 */
>  #define        SR_SRWD                 0x80    /* SR write protect */
>
> +/* Configuration Register bits. */
> +#define CR_QUAD                        0x2     /* Quad I/O */
> +
>  #endif /* __LINUX_MTD_SPI_NOR_H */
> --
> 1.7.1
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-spi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23  9:05     ` yuhang wang
  0 siblings, 0 replies; 68+ messages in thread
From: yuhang wang @ 2013-08-23  9:05 UTC (permalink / raw)
  To: linux-arm-kernel

Hi, Shijie

2013/8/19 Huang Shijie <b32955@freescale.com>:
> This patch adds the quad read support:
>
> (1) Add the relative commands:
>       OPCODE_QIOR, OPCODE_4QIOR, OPCODE_RDCR,
>
>     also add the relative macro for the Configuartion Register.
>
> (2) add the "m25p,quad-read" property for the m25p80 driver
>     If the dts has the "m25p,quad-read" property, the kernel will
>     set the Quad bit of the configuration register, and when the
>     setting is suceeded, we set the read opcode with OPCODE_QIOR.
>
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
>  Documentation/devicetree/bindings/mtd/m25p80.txt |    5 ++
>  drivers/mtd/devices/m25p80.c                     |   51 ++++++++++++++++++++++
>  include/linux/mtd/spi-nor.h                      |    6 +++
>  3 files changed, 62 insertions(+), 0 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/mtd/m25p80.txt b/Documentation/devicetree/bindings/mtd/m25p80.txt
> index 6d3d576..b33313f 100644
> --- a/Documentation/devicetree/bindings/mtd/m25p80.txt
> +++ b/Documentation/devicetree/bindings/mtd/m25p80.txt
> @@ -17,6 +17,11 @@ Optional properties:
>                     Refer to your chips' datasheet to check if this is supported
>                     by your chip.
>
> +- m25p,quad-read : Use the "quad read" opcode to read data from the chip instead
> +                   of the usual "read" opcode. This opcode is not supported by
> +                   all chips and support for it can not be detected at runtime.
> +                   Refer to your chips' datasheet to check if this is supported
> +                   by your chip.
>  Example:
>
>         flash: m25p80 at 0 {
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index f3598c1..4bc9b1b 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -103,6 +103,40 @@ static int write_sr(struct m25p *flash, u8 val)
>  }
>
>  /*
> + * Read the configuration register, returning its value in the location
> + * Return the configuration register value.
> + * Returns negative if error occurred.
> + */
> +static int read_cr(struct m25p *flash)
> +{
> +       u8 code = OPCODE_RDCR;
> +       int ret;
> +       u8 val;
> +
> +       ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
> +       if (ret < 0) {
> +               dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
> +               return ret;
> +       }
> +       return val;
> +}
> +
> +/*
> + * Write status register and configuration register with 2 bytes
> + * The first byte will be written to the status register, while the second byte
> + * will be written to the configuration register.
> + * Returns negative if error occurred.
> + */
> +static int write_sr_cr(struct m25p *flash, u16 val)
> +{
> +       flash->command[0] = OPCODE_WRSR;
> +       flash->command[1] = 0;
> +       flash->command[2] = (val >> 8);
> +
> +       return spi_write(flash->spi, flash->command, 3);
> +}
> +
> +/*
>   * Set write enable latch with Write Enable command.
>   * Returns negative if error occurred.
>   */
> @@ -880,6 +914,8 @@ static int m25p_probe(struct spi_device *spi)
>         unsigned                        i;
>         struct mtd_part_parser_data     ppdata;
>         struct device_node __maybe_unused *np = spi->dev.of_node;
> +       u16 sr_cr;
> +       int ret;
>
>  #ifdef CONFIG_MTD_OF_PARTS
>         if (!of_device_is_available(np))
> @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>         else
>                 flash->read_opcode = OPCODE_NORM_READ;
>
> +       /* Try to enable the Quad Read */
> +       if (np && of_property_read_bool(np, "m25p,quad-read")) {
> +               /* The configuration register is set by the second byte. */
> +               sr_cr = CR_QUAD << 8;
> +
> +               /* Write the QUAD bit to the Configuration Register. */
> +               write_enable(flash);
> +               if (write_sr_cr(flash, sr_cr) == 0) {
> +                       /* read back and check it */
> +                       ret = read_cr(flash);
> +                       if (ret > 0 && (ret & CR_QUAD))
> +                               flash->read_opcode = OPCODE_QIOR;
> +               }
> +       }
> +

Well, M25p80.c support lots of flash devices, so driver should be as
general as possible. Firstly not all the devices m25p80 supports set
quad mode as your sequence, perhaps write_sr_cr can not match all the
m25p80 flash. Secondly, why you only support QIOR(high performance)
not QOR or DOR. Maybe QIOR seems too special, so what if user want to
use QOR if he set quad mode in DTS.

Another point, if command changed to OPCODE_QIOR, there should also
should be some correct in m25p_read. such as the number of dummy data.
QIOR can support read without read command if set the certain bit in
transfer, these aspects did not reflect in your patch.

>         flash->program_opcode = OPCODE_PP;
>
>         if (info->addr_width)
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index b420a5b..d5b189d 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -39,6 +39,9 @@
>
>  /* Used for Spansion flashes only. */
>  #define        OPCODE_BRWR             0x17    /* Bank register write */
> +#define OPCODE_QIOR            0xeb    /* Quad read */
> +#define OPCODE_4QIOR           0xec    /* Quad read */
> +#define        OPCODE_RDCR             0x35    /* Read configuration register */
>
>  /* Status Register bits. */
>  #define        SR_WIP                  1       /* Write in progress */
> @@ -49,4 +52,7 @@
>  #define        SR_BP2                  0x10    /* Block protect 2 */
>  #define        SR_SRWD                 0x80    /* SR write protect */
>
> +/* Configuration Register bits. */
> +#define CR_QUAD                        0x2     /* Quad I/O */
> +
>  #endif /* __LINUX_MTD_SPI_NOR_H */
> --
> 1.7.1
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-spi" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23  9:05     ` yuhang wang
@ 2013-08-23  9:25       ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  9:25 UTC (permalink / raw)
  To: yuhang wang
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Mark Brown,
	linux-mtd, kernel, computersforpeace, dwmw2, linux-arm-kernel

于 2013年08月23日 17:05, yuhang wang 写道:
>> +       u16 sr_cr;
>> >  +       int ret;
>> >
>> >    #ifdef CONFIG_MTD_OF_PARTS
>> >           if (!of_device_is_available(np))
>> >  @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>> >           else
>> >                   flash->read_opcode = OPCODE_NORM_READ;
>> >
>> >  +       /* Try to enable the Quad Read */
>> >  +       if (np&&  of_property_read_bool(np, "m25p,quad-read")) {
>> >  +               /* The configuration register is set by the second byte. */
>> >  +               sr_cr = CR_QUAD<<  8;
>> >  +
>> >  +               /* Write the QUAD bit to the Configuration Register. */
>> >  +               write_enable(flash);
>> >  +               if (write_sr_cr(flash, sr_cr) == 0) {
>> >  +                       /* read back and check it */
>> >  +                       ret = read_cr(flash);
>> >  +                       if (ret>  0&&  (ret&  CR_QUAD))
>> >  +                               flash->read_opcode = OPCODE_QIOR;
>> >  +               }
>> >  +       }
>> >  +
> Well, M25p80.c support lots of flash devices, so driver should be as
> general as possible. Firstly not all the devices m25p80 supports set
> quad mode as your sequence, perhaps write_sr_cr can not match all the
It does not matter the NOR flash supports the write_sr_cr() or not,
If the NOR flash does not support the write_sr_cr(), it may fails, and 
you will not set the OPCODE_QIOR for the
m25p80_read.

> m25p80 flash. Secondly, why you only support QIOR(high performance)
> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
> use QOR if he set quad mode in DTS.
>
Frankly speaking, i am reluctant to support the QIOR, it is a little 
slow. :)

So the the QIOR is lowest speed for QUADSPI controller, and i do not 
want to support the DOR.

In my new version, i add the support for DDR QIOR read which is the 
double rate of the QIOR.

The user should knows if the NOR flash supports the quad-read or not, 
and set the proper DT.

> Another point, if command changed to OPCODE_QIOR, there should also
> should be some correct in m25p_read. such as the number of dummy data.
I only need to change the read opcode.
> QIOR can support read without read command if set the certain bit in
> transfer, these aspects did not reflect in your patch.
>
For the Quadspi, it will handle the dummy by the LUT sequence, such as 
DDR QUAD read, the LUT sequence will
set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read 
to set the dummy.


thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23  9:25       ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-23  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

? 2013?08?23? 17:05, yuhang wang ??:
>> +       u16 sr_cr;
>> >  +       int ret;
>> >
>> >    #ifdef CONFIG_MTD_OF_PARTS
>> >           if (!of_device_is_available(np))
>> >  @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>> >           else
>> >                   flash->read_opcode = OPCODE_NORM_READ;
>> >
>> >  +       /* Try to enable the Quad Read */
>> >  +       if (np&&  of_property_read_bool(np, "m25p,quad-read")) {
>> >  +               /* The configuration register is set by the second byte. */
>> >  +               sr_cr = CR_QUAD<<  8;
>> >  +
>> >  +               /* Write the QUAD bit to the Configuration Register. */
>> >  +               write_enable(flash);
>> >  +               if (write_sr_cr(flash, sr_cr) == 0) {
>> >  +                       /* read back and check it */
>> >  +                       ret = read_cr(flash);
>> >  +                       if (ret>  0&&  (ret&  CR_QUAD))
>> >  +                               flash->read_opcode = OPCODE_QIOR;
>> >  +               }
>> >  +       }
>> >  +
> Well, M25p80.c support lots of flash devices, so driver should be as
> general as possible. Firstly not all the devices m25p80 supports set
> quad mode as your sequence, perhaps write_sr_cr can not match all the
It does not matter the NOR flash supports the write_sr_cr() or not,
If the NOR flash does not support the write_sr_cr(), it may fails, and 
you will not set the OPCODE_QIOR for the
m25p80_read.

> m25p80 flash. Secondly, why you only support QIOR(high performance)
> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
> use QOR if he set quad mode in DTS.
>
Frankly speaking, i am reluctant to support the QIOR, it is a little 
slow. :)

So the the QIOR is lowest speed for QUADSPI controller, and i do not 
want to support the DOR.

In my new version, i add the support for DDR QIOR read which is the 
double rate of the QIOR.

The user should knows if the NOR flash supports the quad-read or not, 
and set the proper DT.

> Another point, if command changed to OPCODE_QIOR, there should also
> should be some correct in m25p_read. such as the number of dummy data.
I only need to change the read opcode.
> QIOR can support read without read command if set the certain bit in
> transfer, these aspects did not reflect in your patch.
>
For the Quadspi, it will handle the dummy by the LUT sequence, such as 
DDR QUAD read, the LUT sequence will
set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read 
to set the dummy.


thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-22 23:58             ` Marek Vasut
@ 2013-08-23  9:41               ` Mark Brown
  -1 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23  9:41 UTC (permalink / raw)
  To: Marek Vasut
  Cc: devicetree, shawn.guo, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, Brian Norris, dwmw2, wangyuhang,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 913 bytes --]

On Fri, Aug 23, 2013 at 01:58:05AM +0200, Marek Vasut wrote:

> > I was actually thinking something more generic than that - putting the
> > property at the SPI generic bindings level.  Though if all flashes with
> > this dual/quad read functionality have the prefix m25p the above would
> > work also, at the minute this does seem to be mostly used by flash (I
> > bet someone's got some DSPs or something though).

> Ah! So you mean the SPI controller would provide information that it can do 
> dual/quad transfers? But then, the additional pins can only be wired to certain 
> chips (controller by certain CS lines).

No, not exactly - I just meant that the property on the child node
should be one that's consistent over all chips and could hopefully be
implemented in the SPI core as part of instantiating the device in DT.
Which probably just means stripping or changing the vendor prefix.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23  9:41               ` Mark Brown
  0 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23  9:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 23, 2013 at 01:58:05AM +0200, Marek Vasut wrote:

> > I was actually thinking something more generic than that - putting the
> > property at the SPI generic bindings level.  Though if all flashes with
> > this dual/quad read functionality have the prefix m25p the above would
> > work also, at the minute this does seem to be mostly used by flash (I
> > bet someone's got some DSPs or something though).

> Ah! So you mean the SPI controller would provide information that it can do 
> dual/quad transfers? But then, the additional pins can only be wired to certain 
> chips (controller by certain CS lines).

No, not exactly - I just meant that the property on the child node
should be one that's consistent over all chips and could hopefully be
implemented in the SPI core as part of instantiating the device in DT.
Which probably just means stripping or changing the vendor prefix.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130823/b412be30/attachment.sig>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23  9:25       ` Huang Shijie
@ 2013-08-23  9:57         ` Sourav Poddar
  -1 siblings, 0 replies; 68+ messages in thread
From: Sourav Poddar @ 2013-08-23  9:57 UTC (permalink / raw)
  To: Huang Shijie
  Cc: computersforpeace, b44548, dedekind1, b18965, linux-spi,
	Mark Brown, linux-mtd, kernel, shawn.guo, dwmw2, yuhang wang,
	linux-arm-kernel

On Friday 23 August 2013 02:55 PM, Huang Shijie wrote:
> 于 2013年08月23日 17:05, yuhang wang 写道:
>>> +       u16 sr_cr;
>>> >  +       int ret;
>>> >
>>> >    #ifdef CONFIG_MTD_OF_PARTS
>>> >           if (!of_device_is_available(np))
>>> >  @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>>> >           else
>>> >                   flash->read_opcode = OPCODE_NORM_READ;
>>> >
>>> >  +       /* Try to enable the Quad Read */
>>> >  +       if (np&&  of_property_read_bool(np, "m25p,quad-read")) {
>>> >  +               /* The configuration register is set by the 
>>> second byte. */
>>> >  +               sr_cr = CR_QUAD<<  8;
>>> >  +
>>> >  +               /* Write the QUAD bit to the Configuration 
>>> Register. */
>>> >  +               write_enable(flash);
>>> >  +               if (write_sr_cr(flash, sr_cr) == 0) {
>>> >  +                       /* read back and check it */
>>> >  +                       ret = read_cr(flash);
>>> >  +                       if (ret>  0&&  (ret&  CR_QUAD))
>>> >  +                               flash->read_opcode = OPCODE_QIOR;
>>> >  +               }
>>> >  +       }
>>> >  +
>> Well, M25p80.c support lots of flash devices, so driver should be as
>> general as possible. Firstly not all the devices m25p80 supports set
>> quad mode as your sequence, perhaps write_sr_cr can not match all the
> It does not matter the NOR flash supports the write_sr_cr() or not,
> If the NOR flash does not support the write_sr_cr(), it may fails, and 
> you will not set the OPCODE_QIOR for the
> m25p80_read.
>
>> m25p80 flash. Secondly, why you only support QIOR(high performance)
>> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
>> use QOR if he set quad mode in DTS.
>>
> Frankly speaking, i am reluctant to support the QIOR, it is a little 
> slow. :)
>
You should add QOR opcodes also in your patch, so we have the complete set.
> So the the QIOR is lowest speed for QUADSPI controller, and i do not 
> want to support the DOR.
>
> In my new version, i add the support for DDR QIOR read which is the 
> double rate of the QIOR.
>
> The user should knows if the NOR flash supports the quad-read or not, 
> and set the proper DT.
>
>> Another point, if command changed to OPCODE_QIOR, there should also
>> should be some correct in m25p_read. such as the number of dummy data.
> I only need to change the read opcode.
>> QIOR can support read without read command if set the certain bit in
>> transfer, these aspects did not reflect in your patch.
>>
> For the Quadspi, it will handle the dummy by the LUT sequence, such as 
> DDR QUAD read, the LUT sequence will
> set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read 
> to set the dummy.
>
>
> thanks
> Huang Shijie
>
>
>
>
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23  9:57         ` Sourav Poddar
  0 siblings, 0 replies; 68+ messages in thread
From: Sourav Poddar @ 2013-08-23  9:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Friday 23 August 2013 02:55 PM, Huang Shijie wrote:
> ? 2013?08?23? 17:05, yuhang wang ??:
>>> +       u16 sr_cr;
>>> >  +       int ret;
>>> >
>>> >    #ifdef CONFIG_MTD_OF_PARTS
>>> >           if (!of_device_is_available(np))
>>> >  @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>>> >           else
>>> >                   flash->read_opcode = OPCODE_NORM_READ;
>>> >
>>> >  +       /* Try to enable the Quad Read */
>>> >  +       if (np&&  of_property_read_bool(np, "m25p,quad-read")) {
>>> >  +               /* The configuration register is set by the 
>>> second byte. */
>>> >  +               sr_cr = CR_QUAD<<  8;
>>> >  +
>>> >  +               /* Write the QUAD bit to the Configuration 
>>> Register. */
>>> >  +               write_enable(flash);
>>> >  +               if (write_sr_cr(flash, sr_cr) == 0) {
>>> >  +                       /* read back and check it */
>>> >  +                       ret = read_cr(flash);
>>> >  +                       if (ret>  0&&  (ret&  CR_QUAD))
>>> >  +                               flash->read_opcode = OPCODE_QIOR;
>>> >  +               }
>>> >  +       }
>>> >  +
>> Well, M25p80.c support lots of flash devices, so driver should be as
>> general as possible. Firstly not all the devices m25p80 supports set
>> quad mode as your sequence, perhaps write_sr_cr can not match all the
> It does not matter the NOR flash supports the write_sr_cr() or not,
> If the NOR flash does not support the write_sr_cr(), it may fails, and 
> you will not set the OPCODE_QIOR for the
> m25p80_read.
>
>> m25p80 flash. Secondly, why you only support QIOR(high performance)
>> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
>> use QOR if he set quad mode in DTS.
>>
> Frankly speaking, i am reluctant to support the QIOR, it is a little 
> slow. :)
>
You should add QOR opcodes also in your patch, so we have the complete set.
> So the the QIOR is lowest speed for QUADSPI controller, and i do not 
> want to support the DOR.
>
> In my new version, i add the support for DDR QIOR read which is the 
> double rate of the QIOR.
>
> The user should knows if the NOR flash supports the quad-read or not, 
> and set the proper DT.
>
>> Another point, if command changed to OPCODE_QIOR, there should also
>> should be some correct in m25p_read. such as the number of dummy data.
> I only need to change the read opcode.
>> QIOR can support read without read command if set the certain bit in
>> transfer, these aspects did not reflect in your patch.
>>
> For the Quadspi, it will handle the dummy by the LUT sequence, such as 
> DDR QUAD read, the LUT sequence will
> set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read 
> to set the dummy.
>
>
> thanks
> Huang Shijie
>
>
>
>
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23  9:41               ` Mark Brown
@ 2013-08-23 10:42                 ` Marek Vasut
  -1 siblings, 0 replies; 68+ messages in thread
From: Marek Vasut @ 2013-08-23 10:42 UTC (permalink / raw)
  To: Mark Brown
  Cc: devicetree, shawn.guo, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, Brian Norris, dwmw2, wangyuhang,
	linux-arm-kernel

Dear Mark Brown,

> On Fri, Aug 23, 2013 at 01:58:05AM +0200, Marek Vasut wrote:
> > > I was actually thinking something more generic than that - putting the
> > > property at the SPI generic bindings level.  Though if all flashes with
> > > this dual/quad read functionality have the prefix m25p the above would
> > > work also, at the minute this does seem to be mostly used by flash (I
> > > bet someone's got some DSPs or something though).
> > 
> > Ah! So you mean the SPI controller would provide information that it can
> > do dual/quad transfers? But then, the additional pins can only be wired
> > to certain chips (controller by certain CS lines).
> 
> No, not exactly - I just meant that the property on the child node
> should be one that's consistent over all chips and could hopefully be
> implemented in the SPI core as part of instantiating the device in DT.
> Which probably just means stripping or changing the vendor prefix.

Ah right, got you now. Thanks!

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 10:42                 ` Marek Vasut
  0 siblings, 0 replies; 68+ messages in thread
From: Marek Vasut @ 2013-08-23 10:42 UTC (permalink / raw)
  To: linux-arm-kernel

Dear Mark Brown,

> On Fri, Aug 23, 2013 at 01:58:05AM +0200, Marek Vasut wrote:
> > > I was actually thinking something more generic than that - putting the
> > > property at the SPI generic bindings level.  Though if all flashes with
> > > this dual/quad read functionality have the prefix m25p the above would
> > > work also, at the minute this does seem to be mostly used by flash (I
> > > bet someone's got some DSPs or something though).
> > 
> > Ah! So you mean the SPI controller would provide information that it can
> > do dual/quad transfers? But then, the additional pins can only be wired
> > to certain chips (controller by certain CS lines).
> 
> No, not exactly - I just meant that the property on the child node
> should be one that's consistent over all chips and could hopefully be
> implemented in the SPI core as part of instantiating the device in DT.
> Which probably just means stripping or changing the vendor prefix.

Ah right, got you now. Thanks!

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-22 19:55       ` Mark Brown
@ 2013-08-23 11:23         ` Brian Norris
  -1 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-23 11:23 UTC (permalink / raw)
  To: Mark Brown
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, shawn.guo, dwmw2, wangyuhang,
	linux-arm-kernel

On 08/22/2013 12:55 PM, Mark Brown wrote:
> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
>> What you're really missing from device-tree (and the SPI subystem in
>> general) is how to detect those SPI controllers which support dual and
>> quad mode transfers, and how to communicate that a particular SPI
>> transaction should be performed on 1 or 4 IO lines. We shouldn't have
>> this just hacked in via assumptions.
>
> That bit does need to be fixed in this driver, yes.  The SPI core now
> has quad mode support.

Where? Perhaps I'm missing the obvious, but I don't see SPI core quad 
support in linux-next or in Linus' tree.

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 11:23         ` Brian Norris
  0 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-23 11:23 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/22/2013 12:55 PM, Mark Brown wrote:
> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
>> What you're really missing from device-tree (and the SPI subystem in
>> general) is how to detect those SPI controllers which support dual and
>> quad mode transfers, and how to communicate that a particular SPI
>> transaction should be performed on 1 or 4 IO lines. We shouldn't have
>> this just hacked in via assumptions.
>
> That bit does need to be fixed in this driver, yes.  The SPI core now
> has quad mode support.

Where? Perhaps I'm missing the obvious, but I don't see SPI core quad 
support in linux-next or in Linus' tree.

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23 11:23         ` Brian Norris
@ 2013-08-23 11:27           ` Sourav Poddar
  -1 siblings, 0 replies; 68+ messages in thread
From: Sourav Poddar @ 2013-08-23 11:27 UTC (permalink / raw)
  To: Brian Norris
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, Mark Brown, linux-mtd, kernel, shawn.guo, dwmw2,
	wangyuhang, linux-arm-kernel

Hi,
On Friday 23 August 2013 04:53 PM, Brian Norris wrote:
> On 08/22/2013 12:55 PM, Mark Brown wrote:
>> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
>>> What you're really missing from device-tree (and the SPI subystem in
>>> general) is how to detect those SPI controllers which support dual and
>>> quad mode transfers, and how to communicate that a particular SPI
>>> transaction should be performed on 1 or 4 IO lines. We shouldn't have
>>> this just hacked in via assumptions.
>>
>> That bit does need to be fixed in this driver, yes.  The SPI core now
>> has quad mode support.
>
> Where? Perhaps I'm missing the obvious, but I don't see SPI core quad 
> support in linux-next or in Linus' tree.
>
You can find it here in Mark's tree:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
branch: remotes/brownspi/spi-next
> Brian
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 11:27           ` Sourav Poddar
  0 siblings, 0 replies; 68+ messages in thread
From: Sourav Poddar @ 2013-08-23 11:27 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,
On Friday 23 August 2013 04:53 PM, Brian Norris wrote:
> On 08/22/2013 12:55 PM, Mark Brown wrote:
>> On Thu, Aug 22, 2013 at 12:34:53PM -0700, Brian Norris wrote:
>>> What you're really missing from device-tree (and the SPI subystem in
>>> general) is how to detect those SPI controllers which support dual and
>>> quad mode transfers, and how to communicate that a particular SPI
>>> transaction should be performed on 1 or 4 IO lines. We shouldn't have
>>> this just hacked in via assumptions.
>>
>> That bit does need to be fixed in this driver, yes.  The SPI core now
>> has quad mode support.
>
> Where? Perhaps I'm missing the obvious, but I don't see SPI core quad 
> support in linux-next or in Linus' tree.
>
You can find it here in Mark's tree:
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
branch: remotes/brownspi/spi-next
> Brian
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23 11:23         ` Brian Norris
@ 2013-08-23 11:30           ` Mark Brown
  -1 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23 11:30 UTC (permalink / raw)
  To: Brian Norris
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, shawn.guo, dwmw2, wangyuhang,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 590 bytes --]

On Fri, Aug 23, 2013 at 04:23:44AM -0700, Brian Norris wrote:
> On 08/22/2013 12:55 PM, Mark Brown wrote:

> >That bit does need to be fixed in this driver, yes.  The SPI core now
> >has quad mode support.

> Where? Perhaps I'm missing the obvious, but I don't see SPI core
> quad support in linux-next or in Linus' tree.

It went in yesterday, -next doesn't seem to have been updated today (I
suspect Stephen may be on vacation, I'd need to check his announcement
mail from yesterday to confirm).  The SPI tree is:

  git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 11:30           ` Mark Brown
  0 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23 11:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 23, 2013 at 04:23:44AM -0700, Brian Norris wrote:
> On 08/22/2013 12:55 PM, Mark Brown wrote:

> >That bit does need to be fixed in this driver, yes.  The SPI core now
> >has quad mode support.

> Where? Perhaps I'm missing the obvious, but I don't see SPI core
> quad support in linux-next or in Linus' tree.

It went in yesterday, -next doesn't seem to have been updated today (I
suspect Stephen may be on vacation, I'd need to check his announcement
mail from yesterday to confirm).  The SPI tree is:

  git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130823/8f26d531/attachment.sig>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23  9:41               ` Mark Brown
@ 2013-08-23 11:46                 ` Brian Norris
  -1 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-23 11:46 UTC (permalink / raw)
  To: Mark Brown
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, shawn.guo, dwmw2, wangyuhang,
	linux-arm-kernel

On 08/23/2013 02:41 AM, Mark Brown wrote:
> On Fri, Aug 23, 2013 at 01:58:05AM +0200, Marek Vasut wrote:
>>> I was actually thinking something more generic than that - putting the
>>> property at the SPI generic bindings level.  Though if all flashes with
>>> this dual/quad read functionality have the prefix m25p the above would
>>> work also, at the minute this does seem to be mostly used by flash (I
>>> bet someone's got some DSPs or something though).
> 
>> Ah! So you mean the SPI controller would provide information that it can do
>> dual/quad transfers? But then, the additional pins can only be wired to certain
>> chips (controller by certain CS lines).
> 
> No, not exactly - I just meant that the property on the child node
> should be one that's consistent over all chips and could hopefully be
> implemented in the SPI core as part of instantiating the device in DT.
> Which probably just means stripping or changing the vendor prefix.

(Now that I've been pointed to the support merged into the SPI tree...)

Aren't the following new DT properties (for the SPI slave) sufficient?

spi-rx-nbits
spi-tx-nbits

We can leave the detection of which flash chips support which modes to 
software (m25p80.c) where it belongs, IMO.

They're already in the following commit:

commit f477b7fb13df2b843997559ff34e87d054ba6538
Author: wangyuhang <wangyuhang2014@gmail.com>
Date:   Sun Aug 11 18:15:17 2013 +0800

    spi: DUAL and QUAD support
    
    fix the previous patch some mistake below:
    1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using
       "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the
       previous way to get the property in @of_register_spi_devices().
    2. Change the value of transfer bit macro(SPI_NBITS_SINGLE, SPI_NBITS_DUAL
       SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires.
    3. Add the following check
       (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond the
          single, dual and quad.
       (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode
          example: if @spi_device->mode = DUAL, then tx/rx_nbits can not be set
                   to QUAD(SPI_NBITS_QUAD)
       (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be in
          single(SPI_NBITS_SINGLE)
    
    Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
    Signed-off-by: Mark Brown <broonie@linaro.org>

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 11:46                 ` Brian Norris
  0 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-23 11:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/23/2013 02:41 AM, Mark Brown wrote:
> On Fri, Aug 23, 2013 at 01:58:05AM +0200, Marek Vasut wrote:
>>> I was actually thinking something more generic than that - putting the
>>> property at the SPI generic bindings level.  Though if all flashes with
>>> this dual/quad read functionality have the prefix m25p the above would
>>> work also, at the minute this does seem to be mostly used by flash (I
>>> bet someone's got some DSPs or something though).
> 
>> Ah! So you mean the SPI controller would provide information that it can do
>> dual/quad transfers? But then, the additional pins can only be wired to certain
>> chips (controller by certain CS lines).
> 
> No, not exactly - I just meant that the property on the child node
> should be one that's consistent over all chips and could hopefully be
> implemented in the SPI core as part of instantiating the device in DT.
> Which probably just means stripping or changing the vendor prefix.

(Now that I've been pointed to the support merged into the SPI tree...)

Aren't the following new DT properties (for the SPI slave) sufficient?

spi-rx-nbits
spi-tx-nbits

We can leave the detection of which flash chips support which modes to 
software (m25p80.c) where it belongs, IMO.

They're already in the following commit:

commit f477b7fb13df2b843997559ff34e87d054ba6538
Author: wangyuhang <wangyuhang2014@gmail.com>
Date:   Sun Aug 11 18:15:17 2013 +0800

    spi: DUAL and QUAD support
    
    fix the previous patch some mistake below:
    1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using
       "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the
       previous way to get the property in @of_register_spi_devices().
    2. Change the value of transfer bit macro(SPI_NBITS_SINGLE, SPI_NBITS_DUAL
       SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires.
    3. Add the following check
       (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond the
          single, dual and quad.
       (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode
          example: if @spi_device->mode = DUAL, then tx/rx_nbits can not be set
                   to QUAD(SPI_NBITS_QUAD)
       (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be in
          single(SPI_NBITS_SINGLE)
    
    Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
    Signed-off-by: Mark Brown <broonie@linaro.org>

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23 11:46                 ` Brian Norris
@ 2013-08-23 11:53                   ` Brian Norris
  -1 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-23 11:53 UTC (permalink / raw)
  To: Mark Brown
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, shawn.guo, dwmw2, wangyuhang,
	linux-arm-kernel

On 08/23/2013 04:46 AM, Brian Norris wrote:
> (Now that I've been pointed to the support merged into the SPI tree...)
>
> Aren't the following new DT properties (for the SPI slave) sufficient?
>
> spi-rx-nbits
> spi-tx-nbits
...
> They're already in the following commit:
>
> commit f477b7fb13df2b843997559ff34e87d054ba6538
> Author: wangyuhang <wangyuhang2014@gmail.com>
> Date:   Sun Aug 11 18:15:17 2013 +0800
>
>      spi: DUAL and QUAD support
>
>      fix the previous patch some mistake below:
>      1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using
>         "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the
>         previous way to get the property in @of_register_spi_devices().
>      2. Change the value of transfer bit macro(SPI_NBITS_SINGLE, SPI_NBITS_DUAL
>         SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires.
>      3. Add the following check
>         (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond the
>            single, dual and quad.
>         (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode
>            example: if @spi_device->mode = DUAL, then tx/rx_nbits can not be set
>                     to QUAD(SPI_NBITS_QUAD)
>         (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be in
>            single(SPI_NBITS_SINGLE)
>
>      Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
>      Signed-off-by: Mark Brown <broonie@linaro.org>

Speaking of which, the new device-tree properties are not documented in 
Documentation/devicetree/bindings/spi/spi-bus.txt yet.

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 11:53                   ` Brian Norris
  0 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-23 11:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/23/2013 04:46 AM, Brian Norris wrote:
> (Now that I've been pointed to the support merged into the SPI tree...)
>
> Aren't the following new DT properties (for the SPI slave) sufficient?
>
> spi-rx-nbits
> spi-tx-nbits
...
> They're already in the following commit:
>
> commit f477b7fb13df2b843997559ff34e87d054ba6538
> Author: wangyuhang <wangyuhang2014@gmail.com>
> Date:   Sun Aug 11 18:15:17 2013 +0800
>
>      spi: DUAL and QUAD support
>
>      fix the previous patch some mistake below:
>      1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using
>         "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the
>         previous way to get the property in @of_register_spi_devices().
>      2. Change the value of transfer bit macro(SPI_NBITS_SINGLE, SPI_NBITS_DUAL
>         SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires.
>      3. Add the following check
>         (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond the
>            single, dual and quad.
>         (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode
>            example: if @spi_device->mode = DUAL, then tx/rx_nbits can not be set
>                     to QUAD(SPI_NBITS_QUAD)
>         (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be in
>            single(SPI_NBITS_SINGLE)
>
>      Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
>      Signed-off-by: Mark Brown <broonie@linaro.org>

Speaking of which, the new device-tree properties are not documented in 
Documentation/devicetree/bindings/spi/spi-bus.txt yet.

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23 11:53                   ` Brian Norris
@ 2013-08-23 12:01                     ` Mark Brown
  -1 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23 12:01 UTC (permalink / raw)
  To: Brian Norris
  Cc: Marek Vasut, devicetree, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, shawn.guo, dwmw2, wangyuhang,
	linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 450 bytes --]

On Fri, Aug 23, 2013 at 04:53:09AM -0700, Brian Norris wrote:
> On 08/23/2013 04:46 AM, Brian Norris wrote:

> >     Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
> >     Signed-off-by: Mark Brown <broonie@linaro.org>

> Speaking of which, the new device-tree properties are not documented
> in Documentation/devicetree/bindings/spi/spi-bus.txt yet.

Yes, indeed - if you look at the review you'll see I asked for a
followup patch doing that.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 12:01                     ` Mark Brown
  0 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23 12:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 23, 2013 at 04:53:09AM -0700, Brian Norris wrote:
> On 08/23/2013 04:46 AM, Brian Norris wrote:

> >     Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
> >     Signed-off-by: Mark Brown <broonie@linaro.org>

> Speaking of which, the new device-tree properties are not documented
> in Documentation/devicetree/bindings/spi/spi-bus.txt yet.

Yes, indeed - if you look at the review you'll see I asked for a
followup patch doing that.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130823/21873702/attachment.sig>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23 11:53                   ` Brian Norris
@ 2013-08-23 13:20                     ` yuhang wang
  -1 siblings, 0 replies; 68+ messages in thread
From: yuhang wang @ 2013-08-23 13:20 UTC (permalink / raw)
  To: Brian Norris
  Cc: Marek Vasut, devicetree, b44548, Artem Bityutskiy, b18965,
	linux-spi, Huang Shijie, Mark Brown, linux-mtd, kernel,
	Shawn Guo, David Woodhouse, linux-arm-kernel

Hi,

2013/8/23 Brian Norris <computersforpeace@gmail.com>:
> On 08/23/2013 04:46 AM, Brian Norris wrote:
>>
>> (Now that I've been pointed to the support merged into the SPI tree...)
>>
>> Aren't the following new DT properties (for the SPI slave) sufficient?
>>
>> spi-rx-nbits
>> spi-tx-nbits
>
> ...
>
>> They're already in the following commit:
>>
>> commit f477b7fb13df2b843997559ff34e87d054ba6538
>> Author: wangyuhang <wangyuhang2014@gmail.com>
>> Date:   Sun Aug 11 18:15:17 2013 +0800
>>
>>      spi: DUAL and QUAD support
>>
>>      fix the previous patch some mistake below:
>>      1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using
>>         "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the
>>         previous way to get the property in @of_register_spi_devices().
>>      2. Change the value of transfer bit macro(SPI_NBITS_SINGLE,
>> SPI_NBITS_DUAL
>>         SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires.
>>      3. Add the following check
>>         (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond
>> the
>>            single, dual and quad.
>>         (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode
>>            example: if @spi_device->mode = DUAL, then tx/rx_nbits can not
>> be set
>>                     to QUAD(SPI_NBITS_QUAD)
>>         (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be
>> in
>>            single(SPI_NBITS_SINGLE)
>>
>>      Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
>>      Signed-off-by: Mark Brown <broonie@linaro.org>
>
>
> Speaking of which, the new device-tree properties are not documented in
> Documentation/devicetree/bindings/spi/spi-bus.txt yet.
>
> Brian

Sorry, because I don't have the entire environment in my own PC, so I
will update the document patch as soon as possible when I go back to
company.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 13:20                     ` yuhang wang
  0 siblings, 0 replies; 68+ messages in thread
From: yuhang wang @ 2013-08-23 13:20 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

2013/8/23 Brian Norris <computersforpeace@gmail.com>:
> On 08/23/2013 04:46 AM, Brian Norris wrote:
>>
>> (Now that I've been pointed to the support merged into the SPI tree...)
>>
>> Aren't the following new DT properties (for the SPI slave) sufficient?
>>
>> spi-rx-nbits
>> spi-tx-nbits
>
> ...
>
>> They're already in the following commit:
>>
>> commit f477b7fb13df2b843997559ff34e87d054ba6538
>> Author: wangyuhang <wangyuhang2014@gmail.com>
>> Date:   Sun Aug 11 18:15:17 2013 +0800
>>
>>      spi: DUAL and QUAD support
>>
>>      fix the previous patch some mistake below:
>>      1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using
>>         "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the
>>         previous way to get the property in @of_register_spi_devices().
>>      2. Change the value of transfer bit macro(SPI_NBITS_SINGLE,
>> SPI_NBITS_DUAL
>>         SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires.
>>      3. Add the following check
>>         (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond
>> the
>>            single, dual and quad.
>>         (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode
>>            example: if @spi_device->mode = DUAL, then tx/rx_nbits can not
>> be set
>>                     to QUAD(SPI_NBITS_QUAD)
>>         (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be
>> in
>>            single(SPI_NBITS_SINGLE)
>>
>>      Signed-off-by: wangyuhang <wangyuhang2014@gmail.com>
>>      Signed-off-by: Mark Brown <broonie@linaro.org>
>
>
> Speaking of which, the new device-tree properties are not documented in
> Documentation/devicetree/bindings/spi/spi-bus.txt yet.
>
> Brian

Sorry, because I don't have the entire environment in my own PC, so I
will update the document patch as soon as possible when I go back to
company.

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23  9:25       ` Huang Shijie
@ 2013-08-23 13:59         ` yuhang wang
  -1 siblings, 0 replies; 68+ messages in thread
From: yuhang wang @ 2013-08-23 13:59 UTC (permalink / raw)
  To: Huang Shijie
  Cc: Shawn Guo, b44548, Artem Bityutskiy, b18965, linux-spi,
	Mark Brown, linux-mtd, kernel, Brian Norris, David Woodhouse,
	linux-arm-kernel

Hi, Shijie

2013/8/23 Huang Shijie <b32955@freescale.com>:
> 于 2013年08月23日 17:05, yuhang wang 写道:
>>>
>>> +       u16 sr_cr;
>>> >  +       int ret;
>>> >
>>> >    #ifdef CONFIG_MTD_OF_PARTS
>>> >           if (!of_device_is_available(np))
>>> >  @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>>> >           else
>>> >                   flash->read_opcode = OPCODE_NORM_READ;
>>> >
>>> >  +       /* Try to enable the Quad Read */
>>> >  +       if (np&&  of_property_read_bool(np, "m25p,quad-read")) {
>>>
>>> >  +               /* The configuration register is set by the second
>>> > byte. */
>>> >  +               sr_cr = CR_QUAD<<  8;
>>> >  +
>>> >  +               /* Write the QUAD bit to the Configuration Register.
>>> > */
>>> >  +               write_enable(flash);
>>> >  +               if (write_sr_cr(flash, sr_cr) == 0) {
>>> >  +                       /* read back and check it */
>>> >  +                       ret = read_cr(flash);
>>> >  +                       if (ret>  0&&  (ret&  CR_QUAD))
>>>
>>> >  +                               flash->read_opcode = OPCODE_QIOR;
>>> >  +               }
>>> >  +       }
>>> >  +
>>
>> Well, M25p80.c support lots of flash devices, so driver should be as
>> general as possible. Firstly not all the devices m25p80 supports set
>> quad mode as your sequence, perhaps write_sr_cr can not match all the
>
> It does not matter the NOR flash supports the write_sr_cr() or not,
> If the NOR flash does not support the write_sr_cr(), it may fails, and you
> will not set the OPCODE_QIOR for the
> m25p80_read.
>
>
So your purpose of the patch is to make m25p80 support quad read or
just support QIOR? if it's the previous one, when set quad support in
DT, but it is possible that quad mode set failed and m25p80 driver
still read in single mode. In such case, user won't get any error
message, so user won't know  what transfer mode the flash works in. Or
you just aimed to support QIOR, so the name in DT(quad read) seems not
appropriate.

>> m25p80 flash. Secondly, why you only support QIOR(high performance)
>> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
>> use QOR if he set quad mode in DTS.
>>
> Frankly speaking, i am reluctant to support the QIOR, it is a little slow.
> :)
>
> So the the QIOR is lowest speed for QUADSPI controller, and i do not want to
> support the DOR.
>
> In my new version, i add the support for DDR QIOR read which is the double
> rate of the QIOR.
>
> The user should knows if the NOR flash supports the quad-read or not, and
> set the proper DT.
>
>
It is slow in your spi system, but to m25p80 it should be general.
Maybe some others will use that function. So I think it is better to
supplement the other operations.

>> Another point, if command changed to OPCODE_QIOR, there should also
>> should be some correct in m25p_read. such as the number of dummy data.
>
> I only need to change the read opcode.
>
>> QIOR can support read without read command if set the certain bit in
>> transfer, these aspects did not reflect in your patch.
>>
> For the Quadspi, it will handle the dummy by the LUT sequence, such as DDR
> QUAD read, the LUT sequence will
> set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read to
> set the dummy.
>
>
Also the same point to above, it is OK to your spi controller, but
your current m25p80 patch can not content others. If I don't have the
SPI controller which support LUT sequence, so my spi controller driver
rely on the info that m25p80 provides, then your patch won't work.

Best regards

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 13:59         ` yuhang wang
  0 siblings, 0 replies; 68+ messages in thread
From: yuhang wang @ 2013-08-23 13:59 UTC (permalink / raw)
  To: linux-arm-kernel

Hi, Shijie

2013/8/23 Huang Shijie <b32955@freescale.com>:
> ? 2013?08?23? 17:05, yuhang wang ??:
>>>
>>> +       u16 sr_cr;
>>> >  +       int ret;
>>> >
>>> >    #ifdef CONFIG_MTD_OF_PARTS
>>> >           if (!of_device_is_available(np))
>>> >  @@ -1014,6 +1050,21 @@ static int m25p_probe(struct spi_device *spi)
>>> >           else
>>> >                   flash->read_opcode = OPCODE_NORM_READ;
>>> >
>>> >  +       /* Try to enable the Quad Read */
>>> >  +       if (np&&  of_property_read_bool(np, "m25p,quad-read")) {
>>>
>>> >  +               /* The configuration register is set by the second
>>> > byte. */
>>> >  +               sr_cr = CR_QUAD<<  8;
>>> >  +
>>> >  +               /* Write the QUAD bit to the Configuration Register.
>>> > */
>>> >  +               write_enable(flash);
>>> >  +               if (write_sr_cr(flash, sr_cr) == 0) {
>>> >  +                       /* read back and check it */
>>> >  +                       ret = read_cr(flash);
>>> >  +                       if (ret>  0&&  (ret&  CR_QUAD))
>>>
>>> >  +                               flash->read_opcode = OPCODE_QIOR;
>>> >  +               }
>>> >  +       }
>>> >  +
>>
>> Well, M25p80.c support lots of flash devices, so driver should be as
>> general as possible. Firstly not all the devices m25p80 supports set
>> quad mode as your sequence, perhaps write_sr_cr can not match all the
>
> It does not matter the NOR flash supports the write_sr_cr() or not,
> If the NOR flash does not support the write_sr_cr(), it may fails, and you
> will not set the OPCODE_QIOR for the
> m25p80_read.
>
>
So your purpose of the patch is to make m25p80 support quad read or
just support QIOR? if it's the previous one, when set quad support in
DT, but it is possible that quad mode set failed and m25p80 driver
still read in single mode. In such case, user won't get any error
message, so user won't know  what transfer mode the flash works in. Or
you just aimed to support QIOR, so the name in DT(quad read) seems not
appropriate.

>> m25p80 flash. Secondly, why you only support QIOR(high performance)
>> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
>> use QOR if he set quad mode in DTS.
>>
> Frankly speaking, i am reluctant to support the QIOR, it is a little slow.
> :)
>
> So the the QIOR is lowest speed for QUADSPI controller, and i do not want to
> support the DOR.
>
> In my new version, i add the support for DDR QIOR read which is the double
> rate of the QIOR.
>
> The user should knows if the NOR flash supports the quad-read or not, and
> set the proper DT.
>
>
It is slow in your spi system, but to m25p80 it should be general.
Maybe some others will use that function. So I think it is better to
supplement the other operations.

>> Another point, if command changed to OPCODE_QIOR, there should also
>> should be some correct in m25p_read. such as the number of dummy data.
>
> I only need to change the read opcode.
>
>> QIOR can support read without read command if set the certain bit in
>> transfer, these aspects did not reflect in your patch.
>>
> For the Quadspi, it will handle the dummy by the LUT sequence, such as DDR
> QUAD read, the LUT sequence will
> set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read to
> set the dummy.
>
>
Also the same point to above, it is OK to your spi controller, but
your current m25p80 patch can not content others. If I don't have the
SPI controller which support LUT sequence, so my spi controller driver
rely on the info that m25p80 provides, then your patch won't work.

Best regards

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-24  2:45           ` Huang Shijie
@ 2013-08-23 15:59             ` Sourav Poddar
  -1 siblings, 0 replies; 68+ messages in thread
From: Sourav Poddar @ 2013-08-23 15:59 UTC (permalink / raw)
  To: Huang Shijie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	Mark Brown, linux-mtd, kernel, computersforpeace, dwmw2,
	yuhang wang, linux-arm-kernel

On Saturday 24 August 2013 08:15 AM, Huang Shijie wrote:
> On Fri, Aug 23, 2013 at 03:27:43PM +0530, Sourav Poddar wrote:
>> On Friday 23 August 2013 02:55 PM, Huang Shijie wrote:
>>> 于 2013年08月23日 17:05, yuhang wang 写道:
>>>
>> You should add QOR opcodes also in your patch, so we have the complete set.
> yes, i have already supported the QIOR opcode in my driver.
>
I meant opcode 0x6b Quad output read which spansion flash supports.
> thanks
> Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-23 15:59             ` Sourav Poddar
  0 siblings, 0 replies; 68+ messages in thread
From: Sourav Poddar @ 2013-08-23 15:59 UTC (permalink / raw)
  To: linux-arm-kernel

On Saturday 24 August 2013 08:15 AM, Huang Shijie wrote:
> On Fri, Aug 23, 2013 at 03:27:43PM +0530, Sourav Poddar wrote:
>> On Friday 23 August 2013 02:55 PM, Huang Shijie wrote:
>>> ? 2013?08?23? 17:05, yuhang wang ??:
>>>
>> You should add QOR opcodes also in your patch, so we have the complete set.
> yes, i have already supported the QIOR opcode in my driver.
>
I meant opcode 0x6b Quad output read which spansion flash supports.
> thanks
> Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
  2013-08-19  4:10   ` Huang Shijie
@ 2013-08-23 16:44     ` Mark Brown
  -1 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23 16:44 UTC (permalink / raw)
  To: Huang Shijie
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, linux-mtd,
	kernel, computersforpeace, dwmw2, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 2250 bytes --]

On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:

Looks good apart from the issues people identified already and a few
small things below:

> +/* Instruction set for the LUT register. */
> +#define CMD			1
> +#define ADDR			2
> +#define DUMMY			3
> +#define MODE			4
> +#define MODE2			5
> +#define MODE4			6
> +#define READ			7
> +#define WRITE			8
> +#define JMP_ON_CS		9
> +#define ADDR_DDR		10
> +#define MODE_DDR		11
> +#define MODE2_DDR		12
> +#define MODE4_DDR		13

Most of the defines in the driver ought to be namespaced to avoid
collisions with other things defining them.  FSL_QSPI_ or something for
example.

> +	default:
> +		dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
> +		break;
> +	}
> +	return -1;

Return a proper error code.

> +static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
> +{
> +	unsigned long deadline;
> +	u32 sr;
> +
> +	deadline = jiffies + msecs_to_jiffies(40000);
> +
> +	do {
> +		if ((sr = fsl_qspi_read_sr(q)) < 0)
> +			break;
> +		else if (!(sr & SR_WIP))
> +			return 0;
> +
> +		cond_resched();
> +
> +	} while (!time_after_eq(jiffies, deadline));
> +
> +	return 1;
> +}

Return an error code if we time out?

> +static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
> +		struct spi_message *m)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +	struct spi_transfer *t;
> +	int ret = 0;
> +
> +	list_for_each_entry(t, &m->transfers, transfer_list) {
> +		if (t->rx_buf && t->tx_buf) {
> +			dev_err(q->dev,
> +				"Can't send and receive simultaneously\n");
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		if (t->tx_buf) {
> +			ret = fsl_qspi_nor_tx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +			continue;
> +		}
> +
> +		if (t->rx_buf) {
> +			ret = fsl_qspi_nor_rx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +		}
> +	}

The driver should flag SPI_HALF_DUPLEX since it doesn't support
simultaneous RX and TX.

> +	q->clk_en = devm_clk_get(&pdev->dev, "qspi_en");
> +	q->clk = devm_clk_get(&pdev->dev, "qspi");
> +	if (IS_ERR(q->clk_en) || IS_ERR(q->clk)) {
> +		dev_err(&pdev->dev, "failed to get clocks\n");
> +		ret = -ENOENT;
> +		goto map_failed;
> +	}

Should use the actual returned error value from devm_clk_get().

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
@ 2013-08-23 16:44     ` Mark Brown
  0 siblings, 0 replies; 68+ messages in thread
From: Mark Brown @ 2013-08-23 16:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:

Looks good apart from the issues people identified already and a few
small things below:

> +/* Instruction set for the LUT register. */
> +#define CMD			1
> +#define ADDR			2
> +#define DUMMY			3
> +#define MODE			4
> +#define MODE2			5
> +#define MODE4			6
> +#define READ			7
> +#define WRITE			8
> +#define JMP_ON_CS		9
> +#define ADDR_DDR		10
> +#define MODE_DDR		11
> +#define MODE2_DDR		12
> +#define MODE4_DDR		13

Most of the defines in the driver ought to be namespaced to avoid
collisions with other things defining them.  FSL_QSPI_ or something for
example.

> +	default:
> +		dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
> +		break;
> +	}
> +	return -1;

Return a proper error code.

> +static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
> +{
> +	unsigned long deadline;
> +	u32 sr;
> +
> +	deadline = jiffies + msecs_to_jiffies(40000);
> +
> +	do {
> +		if ((sr = fsl_qspi_read_sr(q)) < 0)
> +			break;
> +		else if (!(sr & SR_WIP))
> +			return 0;
> +
> +		cond_resched();
> +
> +	} while (!time_after_eq(jiffies, deadline));
> +
> +	return 1;
> +}

Return an error code if we time out?

> +static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
> +		struct spi_message *m)
> +{
> +	struct fsl_qspi *q = spi_master_get_devdata(master);
> +	struct spi_transfer *t;
> +	int ret = 0;
> +
> +	list_for_each_entry(t, &m->transfers, transfer_list) {
> +		if (t->rx_buf && t->tx_buf) {
> +			dev_err(q->dev,
> +				"Can't send and receive simultaneously\n");
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		if (t->tx_buf) {
> +			ret = fsl_qspi_nor_tx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +			continue;
> +		}
> +
> +		if (t->rx_buf) {
> +			ret = fsl_qspi_nor_rx(q, t);
> +			if (!ret)
> +				m->actual_length += t->len;
> +		}
> +	}

The driver should flag SPI_HALF_DUPLEX since it doesn't support
simultaneous RX and TX.

> +	q->clk_en = devm_clk_get(&pdev->dev, "qspi_en");
> +	q->clk = devm_clk_get(&pdev->dev, "qspi");
> +	if (IS_ERR(q->clk_en) || IS_ERR(q->clk)) {
> +		dev_err(&pdev->dev, "failed to get clocks\n");
> +		ret = -ENOENT;
> +		goto map_failed;
> +	}

Should use the actual returned error value from devm_clk_get().
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20130823/9cbe573e/attachment.sig>

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23  9:57         ` Sourav Poddar
@ 2013-08-24  2:45           ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-24  2:45 UTC (permalink / raw)
  To: Sourav Poddar
  Cc: shawn.guo, b44548, dedekind1, b18965, linux-spi, Huang Shijie,
	Mark Brown, linux-mtd, kernel, computersforpeace, dwmw2,
	yuhang wang, linux-arm-kernel

On Fri, Aug 23, 2013 at 03:27:43PM +0530, Sourav Poddar wrote:
> On Friday 23 August 2013 02:55 PM, Huang Shijie wrote:
> >于 2013年08月23日 17:05, yuhang wang 写道:
> >
> You should add QOR opcodes also in your patch, so we have the complete set.

yes, i have already supported the QIOR opcode in my driver.

thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-24  2:45           ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-24  2:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 23, 2013 at 03:27:43PM +0530, Sourav Poddar wrote:
> On Friday 23 August 2013 02:55 PM, Huang Shijie wrote:
> >? 2013?08?23? 17:05, yuhang wang ??:
> >
> You should add QOR opcodes also in your patch, so we have the complete set.

yes, i have already supported the QIOR opcode in my driver.

thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 3/5] mtd: m25p80: add the quad-read support
  2013-08-23 13:59         ` yuhang wang
@ 2013-08-24  3:01           ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-24  3:01 UTC (permalink / raw)
  To: yuhang wang
  Cc: Brian Norris, b44548, Artem Bityutskiy, b18965, linux-spi,
	Huang Shijie, Mark Brown, linux-mtd, kernel, Shawn Guo,
	David Woodhouse, linux-arm-kernel

On Fri, Aug 23, 2013 at 09:59:09PM +0800, yuhang wang wrote:
> Hi, Shijie
> 
> >>> >  +
> >>
> >> Well, M25p80.c support lots of flash devices, so driver should be as
> >> general as possible. Firstly not all the devices m25p80 supports set
> >> quad mode as your sequence, perhaps write_sr_cr can not match all the
> >
> > It does not matter the NOR flash supports the write_sr_cr() or not,
> > If the NOR flash does not support the write_sr_cr(), it may fails, and you
> > will not set the OPCODE_QIOR for the
> > m25p80_read.
> >
> >
> So your purpose of the patch is to make m25p80 support quad read or
> just support QIOR? if it's the previous one, when set quad support in

The patch makes the m25p80 could supports the Quad read.
it is okay if the quad-read mode set fails.

> DT, but it is possible that quad mode set failed and m25p80 driver
> still read in single mode. In such case, user won't get any error
For the Quadspi driver, if the quad read mode set failed, it will still
run in the Fast Read mode.

> message, so user won't know  what transfer mode the flash works in. Or

do you need a warning when the quad read set fails?

> you just aimed to support QIOR, so the name in DT(quad read) seems not
> appropriate.

sorry, could you explain a little more? how can make the DT seems more
appropriate ?

thanks

> 
> >> m25p80 flash. Secondly, why you only support QIOR(high performance)
> >> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
> >> use QOR if he set quad mode in DTS.
> >>
> > Frankly speaking, i am reluctant to support the QIOR, it is a little slow.
> > :)
> >
> > So the the QIOR is lowest speed for QUADSPI controller, and i do not want to
> > support the DOR.
> >
> > In my new version, i add the support for DDR QIOR read which is the double
> > rate of the QIOR.
> >
> > The user should knows if the NOR flash supports the quad-read or not, and
> > set the proper DT.
> >
> >
> It is slow in your spi system, but to m25p80 it should be general.
> Maybe some others will use that function. So I think it is better to
> supplement the other operations.

it is the other driver's responsibility to add the bits info or the
dummy info to the m25p80 code.


> 
> >> Another point, if command changed to OPCODE_QIOR, there should also
> >> should be some correct in m25p_read. such as the number of dummy data.
> >
> > I only need to change the read opcode.
> >
> >> QIOR can support read without read command if set the certain bit in
> >> transfer, these aspects did not reflect in your patch.
> >>
> > For the Quadspi, it will handle the dummy by the LUT sequence, such as DDR
> > QUAD read, the LUT sequence will
> > set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read to
> > set the dummy.
> >
> >
> Also the same point to above, it is OK to your spi controller, but
> your current m25p80 patch can not content others. If I don't have the
> SPI controller which support LUT sequence, so my spi controller driver
> rely on the info that m25p80 provides, then your patch won't work.

dido.

you can submit a patch to fix this issue if your SPI controller can not
works on this patch.

It is over-design for this patch set to add the bits info/dummy info to
the m25p80 code. 

thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 3/5] mtd: m25p80: add the quad-read support
@ 2013-08-24  3:01           ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-24  3:01 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 23, 2013 at 09:59:09PM +0800, yuhang wang wrote:
> Hi, Shijie
> 
> >>> >  +
> >>
> >> Well, M25p80.c support lots of flash devices, so driver should be as
> >> general as possible. Firstly not all the devices m25p80 supports set
> >> quad mode as your sequence, perhaps write_sr_cr can not match all the
> >
> > It does not matter the NOR flash supports the write_sr_cr() or not,
> > If the NOR flash does not support the write_sr_cr(), it may fails, and you
> > will not set the OPCODE_QIOR for the
> > m25p80_read.
> >
> >
> So your purpose of the patch is to make m25p80 support quad read or
> just support QIOR? if it's the previous one, when set quad support in

The patch makes the m25p80 could supports the Quad read.
it is okay if the quad-read mode set fails.

> DT, but it is possible that quad mode set failed and m25p80 driver
> still read in single mode. In such case, user won't get any error
For the Quadspi driver, if the quad read mode set failed, it will still
run in the Fast Read mode.

> message, so user won't know  what transfer mode the flash works in. Or

do you need a warning when the quad read set fails?

> you just aimed to support QIOR, so the name in DT(quad read) seems not
> appropriate.

sorry, could you explain a little more? how can make the DT seems more
appropriate ?

thanks

> 
> >> m25p80 flash. Secondly, why you only support QIOR(high performance)
> >> not QOR or DOR. Maybe QIOR seems too special, so what if user want to
> >> use QOR if he set quad mode in DTS.
> >>
> > Frankly speaking, i am reluctant to support the QIOR, it is a little slow.
> > :)
> >
> > So the the QIOR is lowest speed for QUADSPI controller, and i do not want to
> > support the DOR.
> >
> > In my new version, i add the support for DDR QIOR read which is the double
> > rate of the QIOR.
> >
> > The user should knows if the NOR flash supports the quad-read or not, and
> > set the proper DT.
> >
> >
> It is slow in your spi system, but to m25p80 it should be general.
> Maybe some others will use that function. So I think it is better to
> supplement the other operations.

it is the other driver's responsibility to add the bits info or the
dummy info to the m25p80 code.


> 
> >> Another point, if command changed to OPCODE_QIOR, there should also
> >> should be some correct in m25p_read. such as the number of dummy data.
> >
> > I only need to change the read opcode.
> >
> >> QIOR can support read without read command if set the certain bit in
> >> transfer, these aspects did not reflect in your patch.
> >>
> > For the Quadspi, it will handle the dummy by the LUT sequence, such as DDR
> > QUAD read, the LUT sequence will
> > set proper dummy (6 cycles for S25FL128S). I do not need the m25p_read to
> > set the dummy.
> >
> >
> Also the same point to above, it is OK to your spi controller, but
> your current m25p80 patch can not content others. If I don't have the
> SPI controller which support LUT sequence, so my spi controller driver
> rely on the info that m25p80 provides, then your patch won't work.

dido.

you can submit a patch to fix this issue if your SPI controller can not
works on this patch.

It is over-design for this patch set to add the bits info/dummy info to
the m25p80 code. 

thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
  2013-08-23 16:44     ` Mark Brown
@ 2013-08-24  7:11       ` Brian Norris
  -1 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-24  7:11 UTC (permalink / raw)
  To: Mark Brown, Huang Shijie
  Cc: b44548, dedekind1, b18965, linux-spi, linux-mtd, kernel,
	shawn.guo, dwmw2, linux-arm-kernel

On 08/23/2013 09:44 AM, Mark Brown wrote:
> On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:
>> +static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
>> +{
>> +	unsigned long deadline;
>> +	u32 sr;
>> +
>> +	deadline = jiffies + msecs_to_jiffies(40000);
>> +
>> +	do {
>> +		if ((sr = fsl_qspi_read_sr(q)) < 0)
>> +			break;
>> +		else if (!(sr & SR_WIP))
>> +			return 0;
>> +
>> +		cond_resched();
>> +
>> +	} while (!time_after_eq(jiffies, deadline));
>> +
>> +	return 1;
>> +}
>
> Return an error code if we time out?

You also need to check that you didn't complete between your last 
fsl_qspi_read_sr() and the time_arter_eq() check. Theoretically, there 
could be a lot of time between them (due to cond_resched(), for 
instance). 40000 milliseconds is also a lot of time, but still...

IOW, add a check before the "return -ESOMETHING", something like this:

if (!(fsl_qspi_read_sr(q) & SR_WIP))
	return 0;

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
@ 2013-08-24  7:11       ` Brian Norris
  0 siblings, 0 replies; 68+ messages in thread
From: Brian Norris @ 2013-08-24  7:11 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/23/2013 09:44 AM, Mark Brown wrote:
> On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:
>> +static int fsl_qspi_wait_till_ready(struct fsl_qspi *q)
>> +{
>> +	unsigned long deadline;
>> +	u32 sr;
>> +
>> +	deadline = jiffies + msecs_to_jiffies(40000);
>> +
>> +	do {
>> +		if ((sr = fsl_qspi_read_sr(q)) < 0)
>> +			break;
>> +		else if (!(sr & SR_WIP))
>> +			return 0;
>> +
>> +		cond_resched();
>> +
>> +	} while (!time_after_eq(jiffies, deadline));
>> +
>> +	return 1;
>> +}
>
> Return an error code if we time out?

You also need to check that you didn't complete between your last 
fsl_qspi_read_sr() and the time_arter_eq() check. Theoretically, there 
could be a lot of time between them (due to cond_resched(), for 
instance). 40000 milliseconds is also a lot of time, but still...

IOW, add a check before the "return -ESOMETHING", something like this:

if (!(fsl_qspi_read_sr(q) & SR_WIP))
	return 0;

Brian

^ permalink raw reply	[flat|nested] 68+ messages in thread

* Re: [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
  2013-08-23 16:44     ` Mark Brown
@ 2013-08-24 13:42       ` Huang Shijie
  -1 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-24 13:42 UTC (permalink / raw)
  To: Mark Brown
  Cc: computersforpeace, b44548, dedekind1, b18965, linux-spi,
	Huang Shijie, linux-mtd, kernel, shawn.guo, dwmw2,
	linux-arm-kernel

On Fri, Aug 23, 2013 at 05:44:42PM +0100, Mark Brown wrote:
> On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:
> 
> > +static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
> > +		struct spi_message *m)
> > +{
> > +	struct fsl_qspi *q = spi_master_get_devdata(master);
> > +	struct spi_transfer *t;
> > +	int ret = 0;
> > +
> > +	list_for_each_entry(t, &m->transfers, transfer_list) {
> > +		if (t->rx_buf && t->tx_buf) {
> > +			dev_err(q->dev,
> > +				"Can't send and receive simultaneously\n");
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +
> > +		if (t->tx_buf) {
> > +			ret = fsl_qspi_nor_tx(q, t);
> > +			if (!ret)
> > +				m->actual_length += t->len;
> > +			continue;
> > +		}
> > +
> > +		if (t->rx_buf) {
> > +			ret = fsl_qspi_nor_rx(q, t);
> > +			if (!ret)
> > +				m->actual_length += t->len;
> > +		}
> > +	}
> 
> The driver should flag SPI_HALF_DUPLEX since it doesn't support
> simultaneous RX and TX.
> 
okay, i will add the flag in the next version.


thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

* [PATCH V1 4/5] spi: Add Freescale QuadSpi driver
@ 2013-08-24 13:42       ` Huang Shijie
  0 siblings, 0 replies; 68+ messages in thread
From: Huang Shijie @ 2013-08-24 13:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 23, 2013 at 05:44:42PM +0100, Mark Brown wrote:
> On Mon, Aug 19, 2013 at 12:10:02PM +0800, Huang Shijie wrote:
> 
> > +static int fsl_qspi_nor_do_one_msg(struct spi_master *master,
> > +		struct spi_message *m)
> > +{
> > +	struct fsl_qspi *q = spi_master_get_devdata(master);
> > +	struct spi_transfer *t;
> > +	int ret = 0;
> > +
> > +	list_for_each_entry(t, &m->transfers, transfer_list) {
> > +		if (t->rx_buf && t->tx_buf) {
> > +			dev_err(q->dev,
> > +				"Can't send and receive simultaneously\n");
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +
> > +		if (t->tx_buf) {
> > +			ret = fsl_qspi_nor_tx(q, t);
> > +			if (!ret)
> > +				m->actual_length += t->len;
> > +			continue;
> > +		}
> > +
> > +		if (t->rx_buf) {
> > +			ret = fsl_qspi_nor_rx(q, t);
> > +			if (!ret)
> > +				m->actual_length += t->len;
> > +		}
> > +	}
> 
> The driver should flag SPI_HALF_DUPLEX since it doesn't support
> simultaneous RX and TX.
> 
okay, i will add the flag in the next version.


thanks
Huang Shijie

^ permalink raw reply	[flat|nested] 68+ messages in thread

end of thread, other threads:[~2013-08-24 13:42 UTC | newest]

Thread overview: 68+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-19  4:09 [PATCH V1 0/5] Add the Quadspi driver for vf610-twr Huang Shijie
2013-08-19  4:09 ` Huang Shijie
2013-08-19  4:09 ` [PATCH V1 1/5] mtd: m25p80: move the spi-nor commands to a header Huang Shijie
2013-08-19  4:09   ` Huang Shijie
2013-08-19  4:10 ` [PATCH V1 2/5] mtd: m25p80: add support for Spansion s25fl128s chip Huang Shijie
2013-08-19  4:10   ` Huang Shijie
2013-08-19  4:10 ` [PATCH V1 3/5] mtd: m25p80: add the quad-read support Huang Shijie
2013-08-19  4:10   ` Huang Shijie
2013-08-22 19:34   ` Brian Norris
2013-08-22 19:34     ` Brian Norris
2013-08-22 19:55     ` Mark Brown
2013-08-22 19:55       ` Mark Brown
2013-08-22 20:29       ` Marek Vasut
2013-08-22 20:29         ` Marek Vasut
2013-08-22 23:36         ` Mark Brown
2013-08-22 23:36           ` Mark Brown
2013-08-22 23:58           ` Marek Vasut
2013-08-22 23:58             ` Marek Vasut
2013-08-23  9:41             ` Mark Brown
2013-08-23  9:41               ` Mark Brown
2013-08-23 10:42               ` Marek Vasut
2013-08-23 10:42                 ` Marek Vasut
2013-08-23 11:46               ` Brian Norris
2013-08-23 11:46                 ` Brian Norris
2013-08-23 11:53                 ` Brian Norris
2013-08-23 11:53                   ` Brian Norris
2013-08-23 12:01                   ` Mark Brown
2013-08-23 12:01                     ` Mark Brown
2013-08-23 13:20                   ` yuhang wang
2013-08-23 13:20                     ` yuhang wang
2013-08-23  6:26       ` Huang Shijie
2013-08-23  6:26         ` Huang Shijie
2013-08-23 11:23       ` Brian Norris
2013-08-23 11:23         ` Brian Norris
2013-08-23 11:27         ` Sourav Poddar
2013-08-23 11:27           ` Sourav Poddar
2013-08-23 11:30         ` Mark Brown
2013-08-23 11:30           ` Mark Brown
2013-08-23  9:05   ` yuhang wang
2013-08-23  9:05     ` yuhang wang
2013-08-23  9:25     ` Huang Shijie
2013-08-23  9:25       ` Huang Shijie
2013-08-23  9:57       ` Sourav Poddar
2013-08-23  9:57         ` Sourav Poddar
2013-08-24  2:45         ` Huang Shijie
2013-08-24  2:45           ` Huang Shijie
2013-08-23 15:59           ` Sourav Poddar
2013-08-23 15:59             ` Sourav Poddar
2013-08-23 13:59       ` yuhang wang
2013-08-23 13:59         ` yuhang wang
2013-08-24  3:01         ` Huang Shijie
2013-08-24  3:01           ` Huang Shijie
2013-08-19  4:10 ` [PATCH V1 4/5] spi: Add Freescale QuadSpi driver Huang Shijie
2013-08-19  4:10   ` Huang Shijie
2013-08-22 19:21   ` Brian Norris
2013-08-22 19:21     ` Brian Norris
2013-08-23  2:14     ` Huang Shijie
2013-08-23  2:14       ` Huang Shijie
2013-08-23  6:59     ` Huang Shijie
2013-08-23  6:59       ` Huang Shijie
2013-08-23 16:44   ` Mark Brown
2013-08-23 16:44     ` Mark Brown
2013-08-24  7:11     ` Brian Norris
2013-08-24  7:11       ` Brian Norris
2013-08-24 13:42     ` Huang Shijie
2013-08-24 13:42       ` Huang Shijie
2013-08-19  4:10 ` [PATCH V1 5/5] ARM: dts: vf610-twr: Add SPI NOR support Huang Shijie
2013-08-19  4:10   ` Huang Shijie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.