devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH linux-next v6 0/8] add driver for Atmel QSPI controller
@ 2015-09-09 13:24 Cyrille Pitchen
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
  2015-09-09 13:24 ` [PATCH linux-next v6 8/8] mtd: atmel-quadspi: " Cyrille Pitchen
  0 siblings, 2 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

ChangeLog

v6:
- add new patch which tries to read the JEDEC ID with different SPI protocols
  and different commands: 0x9f (SPI-1-1-1), 0xaf (SPI-4-4-4) and finally
  0xaf (SPI-2-2-2).
- remove the set_protocol() hook from struct spi_nor and add 4 new members
  instead of type enum spi_protocol:
  read_proto, write_proto, erase_proto, reg_proto.
  The relevant protocol value should be checked by the driver specific read(),
  write(), erase(), read_reg() and write_reg() hook implementations.
- remove unused hooks: write_xfer() and read_xfer().
- tune the op code for read, write and erase commands depending on the memory
  manufacturer.
- remove some previously added "Acked-by" since the protocol switch strategy
  has been changed but not discussed yet.

v5:
- remove unused inline functions qspi_read[bw]() and qspi_write[bw](),
  keep only qspi_readl() and qspi_writel().
- use reinit_completion() instead of init_completion() during run time,
  call init_completion() once for all in the probe().
- add a dev_warn() when trying to tune the number of dummy cycles for
  spi-nor of a not supported manufacturer then fall back to the default
  framework value.
- reword some comments.
- add "Acked-by: Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>" for patches 1 and 4.
- add "Acked-by: Bean Huo <beanhuo-AL4WhLSQfzjQT0dZR+AlfA@public.gmane.org>" for patch 1.

v4:
- add "OF && HAS_DMA" dependency in Kconfig for Atmel Quad SPI driver.
- return -ENOMEM instead of the return code of dma_mapping_error() as this
  function returns a boolean on ARM achitecture.
- add "Acked-by: Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>" for Atmel Quad
  SPI driver and its DT binding documentation.

v3:
- reword the comment which explains that spi_nor_set_protocol() is used by
  the spi-nor framework to notify lower layers, especially the (Q)SPI
  controller about a protocol change.
- change the definitions of register/bitfield macros in the Atmel QSPI
  controller driver: get rid of concatenation operator and use BIT and
  GENMASK macros when possible.
- use #define[SPACE] instead of #define[TAB]

v2:
- remove the patches to set the "latency code" of Spansion QSPI memories
  (support of Spansion memories may be submitted in later series).
- rename "qspi" node into "spi" in the DT example to fit ePAPR standard.
- remove the useless "qspi0" label from the DT node example.
- remove the leading 0 from the size of the second memory region to make
  it consistent with the size of the first memory region.
- indent the DT bindings documentation to make it more readable.
- remove the useless ".bus	= &platform_bus_type," line from the
  platform driver definition.

v1:

This series of patches add support for the new Atmel QSPI controller
embedded inside sama5d2x SoCs.

These patches were first developped for linux-3.18-at91 and tested on a
sama5d27 Xplained ultra board, which embeds a Micron n25q128a13 QSPI NOR
flash memory. Then the series was adapted for mainline.

Cyrille Pitchen (8):
  mtd: spi-nor: read JEDEC ID with multiple I/O protocols
  mtd: spi-nor: remove unused read_xfer/write_xfer hooks
  mtd: spi-nor: update the SPI protocol when enabling manufacturer Quad
    mode
  mtd: spi-nor: use optimized commands for read/write/erase operations
  Documentation: mtd: add a DT property to set the number of dummy
    cycles
  mtd: spi-nor: allow to tune the number of dummy cycles
  Documentation: atmel-quadspi: add binding file for Atmel QSPI driver
  mtd: atmel-quadspi: add driver for Atmel QSPI controller

 .../devicetree/bindings/mtd/atmel-quadspi.txt      |  29 +
 .../devicetree/bindings/mtd/jedec,spi-nor.txt      |   6 +
 drivers/mtd/spi-nor/Kconfig                        |   7 +
 drivers/mtd/spi-nor/Makefile                       |   1 +
 drivers/mtd/spi-nor/atmel-quadspi.c                | 889 +++++++++++++++++++++
 drivers/mtd/spi-nor/spi-nor.c                      | 356 +++++++--
 include/linux/mtd/spi-nor.h                        |  63 +-
 7 files changed, 1269 insertions(+), 82 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
 create mode 100644 drivers/mtd/spi-nor/atmel-quadspi.c

-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 1/8] mtd: spi-nor: read JEDEC ID with multiple I/O protocols
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
@ 2015-09-09 13:24   ` Cyrille Pitchen
       [not found]     ` <735e22efa25f6529bf8ed4a709c18a719176714b.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
  2015-09-09 13:24   ` [PATCH linux-next v6 2/8] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Cyrille Pitchen
                     ` (6 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

When their quad or dual I/O mode is enabled, Micron and Macronix spi-nor
memories don't reply to the regular Read ID (0x9f) command. Instead they
reply to a new dedicated command Read ID Multiple I/O (0xaf).

If the Read ID (0x9f) command fails (the read ID is all 1's or all 0's),
then the Read ID Multiple I/O (0xaf) is used, first with SPI 4-4-4 protocol
(supported by both Micron and Macronix memories), lately with SPI-2-2-2
protocol (supported only by Micron memories).

Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
 drivers/mtd/spi-nor/spi-nor.c | 28 +++++++++++++++++++++++++++-
 include/linux/mtd/spi-nor.h   | 23 +++++++++++++++++++++--
 2 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index f59aedfe1462..80a0db078aaa 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -703,8 +703,9 @@ static const struct flash_info spi_nor_ids[] = {
 
 static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
 {
-	int			tmp;
+	int			i, tmp;
 	u8			id[SPI_NOR_MAX_ID_LEN];
+	enum spi_protocol	proto[2] = {SPI_PROTO_4_4_4, SPI_PROTO_2_2_2};
 	const struct flash_info	*info;
 
 	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
@@ -713,6 +714,25 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
 		return ERR_PTR(tmp);
 	}
 
+	/* Special case for Micron/Macronix qspi nor. */
+	for (i = 0; i < ARRAY_SIZE(proto); ++i) {
+		if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
+		      (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00)))
+			break;
+
+		nor->erase_proto = proto[i];
+		nor->read_proto = proto[i];
+		nor->write_proto = proto[i];
+		nor->reg_proto = proto[i];
+		tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID,
+				    id, SPI_NOR_MAX_ID_LEN);
+		if (tmp < 0) {
+			dev_dbg(nor->dev,
+				" error %d reading JEDEC ID (MULTI IO)\n", tmp);
+			return ERR_PTR(tmp);
+		}
+	}
+
 	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
 		info = &spi_nor_ids[tmp];
 		if (info->id_len) {
@@ -1013,6 +1033,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	if (ret)
 		return ret;
 
+	/* Reset SPI protocol for all commands */
+	nor->erase_proto = SPI_PROTO_1_1_1;
+	nor->read_proto = SPI_PROTO_1_1_1;
+	nor->write_proto = SPI_PROTO_1_1_1;
+	nor->reg_proto = SPI_PROTO_1_1_1;
+
 	if (name)
 		info = spi_nor_match_id(name);
 	/* Try to auto-detect if chip name wasn't specified or not found */
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index e5409524bb0a..66a5f144728a 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -57,8 +57,9 @@
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
 
 /* Used for Micron flashes only. */
-#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
-#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */
+#define SPINOR_OP_MIO_RDID	0xaf	/* Multiple I/O Read JEDEC ID */
+#define SPINOR_OP_RD_EVCR	0x65    /* Read EVCR register */
+#define SPINOR_OP_WD_EVCR	0x61    /* Write EVCR register */
 
 /* Status Register bits. */
 #define SR_WIP			1	/* Write in progress */
@@ -87,6 +88,16 @@ enum read_mode {
 	SPI_NOR_QUAD,
 };
 
+enum spi_protocol {
+	SPI_PROTO_1_1_1,	/* SPI */
+	SPI_PROTO_1_1_2,	/* Dual Output */
+	SPI_PROTO_1_1_4,	/* Quad Output */
+	SPI_PROTO_1_2_2,	/* Dual IO */
+	SPI_PROTO_1_4_4,	/* Quad IO */
+	SPI_PROTO_2_2_2,	/* Dual Command */
+	SPI_PROTO_4_4_4,	/* Quad Command */
+};
+
 /**
  * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
  * @wren:		command for "Write Enable", or 0x00 for not required
@@ -142,6 +153,10 @@ enum spi_nor_option_flags {
  * @sst_write_second:	used by the SST write operation
  * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
  * @cfg:		used by the read_xfer/write_xfer
+ * @erase_proto:	the SPI protocol used by erase operations
+ * @read_proto:		the SPI protocol used by read operations
+ * @write_proto:	the SPI protocol used by write operations
+ * @reg_proto		the SPI protocol used by read_reg/write_reg operations
  * @cmd_buf:		used by the write_reg
  * @prepare:		[OPTIONAL] do some preparations for the
  *			read/write/erase/lock/unlock operations
@@ -169,6 +184,10 @@ struct spi_nor {
 	u8			read_opcode;
 	u8			read_dummy;
 	u8			program_opcode;
+	enum spi_protocol	erase_proto;
+	enum spi_protocol	read_proto;
+	enum spi_protocol	write_proto;
+	enum spi_protocol	reg_proto;
 	enum read_mode		flash_read;
 	bool			sst_write_second;
 	u32			flags;
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 2/8] mtd: spi-nor: remove unused read_xfer/write_xfer hooks
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
  2015-09-09 13:24   ` [PATCH linux-next v6 1/8] mtd: spi-nor: read JEDEC ID with multiple I/O protocols Cyrille Pitchen
@ 2015-09-09 13:24   ` Cyrille Pitchen
  2015-09-09 13:24   ` [PATCH linux-next v6 3/8] mtd: spi-nor: update the SPI protocol when enabling manufacturer Quad mode Cyrille Pitchen
                     ` (5 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

struct spi_nor_xfer_cfg and read_xfer/write_xfer hooks were never used by
any driver. Do some cleanup by removing them.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
 include/linux/mtd/spi-nor.h | 35 -----------------------------------
 1 file changed, 35 deletions(-)

diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 66a5f144728a..08e405cbb6af 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -98,33 +98,6 @@ enum spi_protocol {
 	SPI_PROTO_4_4_4,	/* Quad Command */
 };
 
-/**
- * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
- * @wren:		command for "Write Enable", or 0x00 for not required
- * @cmd:		command for operation
- * @cmd_pins:		number of pins to send @cmd (1, 2, 4)
- * @addr:		address for operation
- * @addr_pins:		number of pins to send @addr (1, 2, 4)
- * @addr_width:		number of address bytes
- *			(3,4, or 0 for address not required)
- * @mode:		mode data
- * @mode_pins:		number of pins to send @mode (1, 2, 4)
- * @mode_cycles:	number of mode cycles (0 for mode not required)
- * @dummy_cycles:	number of dummy cycles (0 for dummy not required)
- */
-struct spi_nor_xfer_cfg {
-	u8		wren;
-	u8		cmd;
-	u8		cmd_pins;
-	u32		addr;
-	u8		addr_pins;
-	u8		addr_width;
-	u8		mode;
-	u8		mode_pins;
-	u8		mode_cycles;
-	u8		dummy_cycles;
-};
-
 #define SPI_NOR_MAX_CMD_SIZE	8
 enum spi_nor_ops {
 	SPI_NOR_OPS_READ = 0,
@@ -152,7 +125,6 @@ enum spi_nor_option_flags {
  * @flash_read:		the mode of the read
  * @sst_write_second:	used by the SST write operation
  * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
- * @cfg:		used by the read_xfer/write_xfer
  * @erase_proto:	the SPI protocol used by erase operations
  * @read_proto:		the SPI protocol used by read operations
  * @write_proto:	the SPI protocol used by write operations
@@ -162,8 +134,6 @@ enum spi_nor_option_flags {
  *			read/write/erase/lock/unlock operations
  * @unprepare:		[OPTIONAL] do some post work after the
  *			read/write/erase/lock/unlock operations
- * @read_xfer:		[OPTIONAL] the read fundamental primitive
- * @write_xfer:		[OPTIONAL] the writefundamental primitive
  * @read_reg:		[DRIVER-SPECIFIC] read out the register
  * @write_reg:		[DRIVER-SPECIFIC] write data to the register
  * @read:		[DRIVER-SPECIFIC] read data from the SPI NOR
@@ -191,15 +161,10 @@ struct spi_nor {
 	enum read_mode		flash_read;
 	bool			sst_write_second;
 	u32			flags;
-	struct spi_nor_xfer_cfg	cfg;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
 
 	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
 	void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
-	int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
-			 u8 *buf, size_t len);
-	int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
-			  u8 *buf, size_t len);
 	int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
 	int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
 			int write_enable);
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 3/8] mtd: spi-nor: update the SPI protocol when enabling manufacturer Quad mode
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
  2015-09-09 13:24   ` [PATCH linux-next v6 1/8] mtd: spi-nor: read JEDEC ID with multiple I/O protocols Cyrille Pitchen
  2015-09-09 13:24   ` [PATCH linux-next v6 2/8] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Cyrille Pitchen
@ 2015-09-09 13:24   ` Cyrille Pitchen
  2015-09-09 13:24   ` [PATCH linux-next v6 4/8] mtd: spi-nor: use optimized commands for read/write/erase operations Cyrille Pitchen
                     ` (4 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

Micron:
Once their Quad SPI protocol enabled, Micron spi-nor memories expect all
commands to use the SPI 4-4-4 protocol. Also when the Dual SPI protocol is
enabled, all commands must use the SPI 2-2-2 protocol.

Macronix:
When the QPI mode is enabled, all commands must use the SPI 4-4-4 protocol.

Spansion:
When Quad I/O operations are enabled, the Fast Read Quad Output (0x6b / 0x6c)
commands use the SPI 1-1-4 protocol, the Page Program Quad Output (0x32 /
0x34) command use the SPI 1-1-4 protocol whereas other commands use the
SPI 1-1-1 protocol.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
 drivers/mtd/spi-nor/spi-nor.c | 74 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/mtd/spi-nor.h   |  1 +
 2 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 80a0db078aaa..4b36aada3f4c 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -891,6 +891,12 @@ static int macronix_quad_enable(struct spi_nor *nor)
 	nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
 	nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
 
+	/* Once QPI mode enabled, all commands use SPI 4-4-4 protocol. */
+	nor->erase_proto = SPI_PROTO_4_4_4;
+	nor->read_proto = SPI_PROTO_4_4_4;
+	nor->write_proto = SPI_PROTO_4_4_4;
+	nor->reg_proto = SPI_PROTO_4_4_4;
+
 	if (spi_nor_wait_till_ready(nor))
 		return 1;
 
@@ -938,10 +944,16 @@ static int spansion_quad_enable(struct spi_nor *nor)
 		return -EINVAL;
 	}
 
+	/* set read/write protocols */
+	nor->read_proto = SPI_PROTO_1_1_4;
+	nor->write_proto = SPI_PROTO_1_1_4;
+
 	return 0;
 }
 
-static int micron_quad_enable(struct spi_nor *nor)
+static int micron_set_protocol(struct spi_nor *nor,
+			       u8 mask, u8 value,
+			       enum spi_protocol proto)
 {
 	int ret;
 	u8 val;
@@ -954,14 +966,20 @@ static int micron_quad_enable(struct spi_nor *nor)
 
 	write_enable(nor);
 
-	/* set EVCR, enable quad I/O */
-	nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
+	/* set EVCR protocol bits */
+	nor->cmd_buf[0] = (val & ~mask) | value;
 	ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0);
 	if (ret < 0) {
 		dev_err(nor->dev, "error while writing EVCR register\n");
 		return ret;
 	}
 
+	/* switch protocol for ALL commands */
+	nor->erase_proto = proto;
+	nor->read_proto = proto;
+	nor->write_proto = proto;
+	nor->reg_proto = proto;
+
 	ret = spi_nor_wait_till_ready(nor);
 	if (ret)
 		return ret;
@@ -972,14 +990,23 @@ static int micron_quad_enable(struct spi_nor *nor)
 		dev_err(nor->dev, "error %d reading EVCR\n", ret);
 		return ret;
 	}
-	if (val & EVCR_QUAD_EN_MICRON) {
-		dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
+	if ((val & mask) != value) {
+		dev_err(nor->dev, "Micron EVCR protocol bits not valid\n");
 		return -EINVAL;
 	}
 
 	return 0;
 }
 
+static inline int micron_quad_enable(struct spi_nor *nor)
+{
+	/* Clear Quad bit to enable quad mode */
+	return micron_set_protocol(nor,
+				   EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+				   EVCR_DUAL_EN_MICRON,
+				   SPI_PROTO_4_4_4);
+}
+
 static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 {
 	int status;
@@ -1009,6 +1036,38 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
 	}
 }
 
+static inline int micron_dual_enable(struct spi_nor *nor)
+{
+	/* Clear Dual bit but keep Quad bit set to enable dual mode */
+	return micron_set_protocol(nor,
+				   EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+				   EVCR_QUAD_EN_MICRON,
+				   SPI_PROTO_2_2_2);
+}
+
+static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
+{
+	int status;
+
+	switch (JEDEC_MFR(info)) {
+	case CFI_MFR_ST:
+		status = micron_dual_enable(nor);
+		if (status) {
+			dev_err(nor->dev, "Micron dual-read not enabled\n");
+			return -EINVAL;
+		}
+		return status;
+	case CFI_MFR_MACRONIX:
+	case CFI_MFR_AMD:
+		nor->read_proto = SPI_PROTO_1_1_2;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static int spi_nor_check(struct spi_nor *nor)
 {
 	if (!nor->dev || !nor->read || !nor->write ||
@@ -1160,6 +1219,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 		}
 		nor->flash_read = SPI_NOR_QUAD;
 	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
+		ret = set_dual_mode(nor, info);
+		if (ret) {
+			dev_err(dev, "dual mode not supported\n");
+			return ret;
+		}
 		nor->flash_read = SPI_NOR_DUAL;
 	}
 
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 08e405cbb6af..ce81b0e2cb37 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -74,6 +74,7 @@
 
 /* Enhanced Volatile Configuration Register bits */
 #define EVCR_QUAD_EN_MICRON    0x80    /* Micron Quad I/O */
+#define EVCR_DUAL_EN_MICRON    0x40    /* Micron Dual I/O */
 
 /* Flag Status Register bits */
 #define FSR_READY		0x80
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 4/8] mtd: spi-nor: use optimized commands for read/write/erase operations
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
                     ` (2 preceding siblings ...)
  2015-09-09 13:24   ` [PATCH linux-next v6 3/8] mtd: spi-nor: update the SPI protocol when enabling manufacturer Quad mode Cyrille Pitchen
@ 2015-09-09 13:24   ` Cyrille Pitchen
  2015-09-10 10:28     ` Jonas Gorski
  2015-09-09 13:24   ` [PATCH linux-next v6 5/8] Documentation: mtd: add a DT property to set the number of dummy cycles Cyrille Pitchen
                     ` (3 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

The op codes used by the spi-nor framework are now tuned depending on the
memory manufacturer.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
 drivers/mtd/spi-nor/spi-nor.c | 156 +++++++++++++++++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h   |   6 ++
 2 files changed, 138 insertions(+), 24 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 4b36aada3f4c..820a2177ed5e 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -193,6 +193,8 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
 			write_disable(nor);
 
 		return status;
+	case CFI_MFR_AMD:
+		return 0;
 	default:
 		/* Spansion style */
 		nor->cmd_buf[0] = enable << 7;
@@ -945,7 +947,7 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	}
 
 	/* set read/write protocols */
-	nor->read_proto = SPI_PROTO_1_1_4;
+	nor->read_proto = SPI_PROTO_1_4_4;
 	nor->write_proto = SPI_PROTO_1_1_4;
 
 	return 0;
@@ -1059,7 +1061,7 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 		return status;
 	case CFI_MFR_MACRONIX:
 	case CFI_MFR_AMD:
-		nor->read_proto = SPI_PROTO_1_1_2;
+		nor->read_proto = SPI_PROTO_1_2_2;
 		break;
 	default:
 		break;
@@ -1068,6 +1070,130 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info)
 	return 0;
 }
 
+static void macronix_set_commands(struct spi_nor *nor)
+{
+	switch (nor->flash_read) {
+	case SPI_NOR_QUAD: /* QPI mode */
+		nor->read_opcode = SPINOR_OP_READ_1_4_4;
+		break;
+
+	case SPI_NOR_DUAL:
+		nor->read_opcode = SPINOR_OP_READ_1_2_2;
+		break;
+
+	case SPI_NOR_FAST:
+		nor->read_opcode = SPINOR_OP_READ_FAST;
+		break;
+
+	case SPI_NOR_NORMAL:
+	default:
+		nor->read_opcode = SPINOR_OP_READ;
+		break;
+	}
+
+	nor->program_opcode = SPINOR_OP_PP;
+}
+
+static void micron_set_commands(struct spi_nor *nor)
+{
+	switch (nor->flash_read) {
+	case SPI_NOR_QUAD: /* Quad I/O operations */
+		nor->read_opcode = SPINOR_OP_READ_1_4_4;
+		break;
+
+	case SPI_NOR_DUAL: /* Dual I/O operations */
+		nor->read_opcode = SPINOR_OP_READ_1_2_2;
+		break;
+
+	case SPI_NOR_FAST:
+		nor->read_opcode = SPINOR_OP_READ_FAST;
+		break;
+
+	case SPI_NOR_NORMAL:
+	default:
+		nor->read_opcode = SPINOR_OP_READ;
+		break;
+	}
+
+	nor->program_opcode = SPINOR_OP_PP;
+}
+
+static void spansion_set_commands(struct spi_nor *nor,
+				  const struct flash_info *info)
+{
+	bool addr_4byte = (nor->addr_width == 4);
+	struct mtd_info *mtd = nor->mtd;
+
+	switch (nor->flash_read) {
+	case SPI_NOR_QUAD:
+		if (addr_4byte) {
+			nor->read_opcode = SPINOR_OP_READ4_1_4_4;
+			nor->program_opcode = SPINOR_OP_PP_4B_1_1_4;
+		} else {
+			nor->read_opcode = SPINOR_OP_READ_1_4_4;
+			nor->program_opcode = SPINOR_OP_PP_1_1_4;
+		}
+		break;
+
+	case SPI_NOR_DUAL:
+		if (addr_4byte) {
+			nor->read_opcode = SPINOR_OP_READ4_1_2_2;
+			nor->program_opcode = SPINOR_OP_PP_4B;
+		} else {
+			nor->read_opcode = SPINOR_OP_READ_1_2_2;
+			nor->program_opcode = SPINOR_OP_PP;
+		}
+		break;
+
+	case SPI_NOR_FAST:
+		if (addr_4byte) {
+			nor->read_opcode = SPINOR_OP_READ4_FAST;
+			nor->program_opcode = SPINOR_OP_PP_4B;
+		} else {
+			nor->read_opcode = SPINOR_OP_READ_FAST;
+			nor->program_opcode = SPINOR_OP_PP;
+		}
+		break;
+
+	case SPI_NOR_NORMAL:
+	default:
+		if (addr_4byte) {
+			nor->read_opcode = SPINOR_OP_READ4;
+			nor->program_opcode = SPINOR_OP_PP_4B;
+		} else {
+			nor->read_opcode = SPINOR_OP_READ;
+			nor->program_opcode = SPINOR_OP_PP;
+		}
+		break;
+	}
+
+	if (addr_4byte) {
+		nor->erase_opcode = SPINOR_OP_SE_4B;
+		mtd->erasesize = info->sector_size;
+	}
+}
+
+static void tune_manufacturer_commands(struct spi_nor *nor,
+				       const struct flash_info *info)
+{
+	switch (JEDEC_MFR(info)) {
+	case CFI_MFR_MACRONIX:
+		macronix_set_commands(nor);
+		break;
+
+	case CFI_MFR_ST:
+		micron_set_commands(nor);
+		break;
+
+	case CFI_MFR_AMD:
+		spansion_set_commands(nor, info);
+		break;
+
+	default:
+		break;
+	}
+}
+
 static int spi_nor_check(struct spi_nor *nor)
 {
 	if (!nor->dev || !nor->read || !nor->write ||
@@ -1253,32 +1379,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	else if (mtd->size > 0x1000000) {
 		/* enable 4-byte addressing if the device exceeds 16MiB */
 		nor->addr_width = 4;
-		if (JEDEC_MFR(info) == CFI_MFR_AMD) {
-			/* Dedicated 4-byte command set */
-			switch (nor->flash_read) {
-			case SPI_NOR_QUAD:
-				nor->read_opcode = SPINOR_OP_READ4_1_1_4;
-				break;
-			case SPI_NOR_DUAL:
-				nor->read_opcode = SPINOR_OP_READ4_1_1_2;
-				break;
-			case SPI_NOR_FAST:
-				nor->read_opcode = SPINOR_OP_READ4_FAST;
-				break;
-			case SPI_NOR_NORMAL:
-				nor->read_opcode = SPINOR_OP_READ4;
-				break;
-			}
-			nor->program_opcode = SPINOR_OP_PP_4B;
-			/* No small sector erase for 4-byte command set */
-			nor->erase_opcode = SPINOR_OP_SE_4B;
-			mtd->erasesize = info->sector_size;
-		} else
-			set_4byte(nor, info, 1);
+		set_4byte(nor, info, 1);
 	} else {
 		nor->addr_width = 3;
 	}
 
+	/* Tune read, page program and erase commands */
+	tune_manufacturer_commands(nor, info);
+
 	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
 
 	dev_info(dev, "%s (%lld Kbytes)\n", info->name,
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index ce81b0e2cb37..f13cd2cb3ac5 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -25,8 +25,11 @@
 #define SPINOR_OP_READ		0x03	/* Read data bytes (low frequency) */
 #define SPINOR_OP_READ_FAST	0x0b	/* Read data bytes (high frequency) */
 #define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual SPI) */
+#define SPINOR_OP_READ_1_2_2	0xbb	/* Read data bytes (Dual SPI) */
 #define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_4_4	0xeb	/* Read data bytes (Quad SPI) */
 #define SPINOR_OP_PP		0x02	/* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4	0x32	/* Page program (up to 256 bytes) */
 #define SPINOR_OP_BE_4K		0x20	/* Erase 4KiB block */
 #define SPINOR_OP_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
 #define SPINOR_OP_BE_32K	0x52	/* Erase 32KiB block */
@@ -40,8 +43,11 @@
 #define SPINOR_OP_READ4		0x13	/* Read data bytes (low frequency) */
 #define SPINOR_OP_READ4_FAST	0x0c	/* Read data bytes (high frequency) */
 #define SPINOR_OP_READ4_1_1_2	0x3c	/* Read data bytes (Dual SPI) */
+#define SPINOR_OP_READ4_1_2_2	0xbc	/* Read data bytes (Dual SPI) */
 #define SPINOR_OP_READ4_1_1_4	0x6c	/* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ4_1_4_4	0xec	/* Read data bytes (Quad SPI) */
 #define SPINOR_OP_PP_4B		0x12	/* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_4B_1_1_4	0x34	/* Page Program (up to 512 bytes) */
 #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
 
 /* Used for SST flashes only. */
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 5/8] Documentation: mtd: add a DT property to set the number of dummy cycles
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
                     ` (3 preceding siblings ...)
  2015-09-09 13:24   ` [PATCH linux-next v6 4/8] mtd: spi-nor: use optimized commands for read/write/erase operations Cyrille Pitchen
@ 2015-09-09 13:24   ` Cyrille Pitchen
  2015-09-09 13:24   ` [PATCH linux-next v6 6/8] mtd: spi-nor: allow to tune " Cyrille Pitchen
                     ` (2 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

Depending on the SPI clock frequency, the Fast Read op code and the
Single/Dual Data Rate mode, the number of dummy cycles can be tuned to
improve transfer speed.
The actual number of dummy cycles is specific for each memory model and is
provided by the manufacturer thanks to the memory datasheet.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
 Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
index 2bee68103b01..4387567d8024 100644
--- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
+++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
@@ -19,6 +19,11 @@ Optional properties:
                    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.
+- m25p,num-dummy-cycles : Set the number of dummy cycles for Fast Read commands.
+                          Depending on the manufacturer additional dedicated
+                          commands are sent to the flash memory so the
+                          controller and the memory can agree on the number of
+                          dummy cycles to use.
 
 Example:
 
@@ -29,4 +34,5 @@ Example:
 		reg = <0>;
 		spi-max-frequency = <40000000>;
 		m25p,fast-read;
+		m25p,num-dummy-cycles = <8>;
 	};
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 6/8] mtd: spi-nor: allow to tune the number of dummy cycles
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
                     ` (4 preceding siblings ...)
  2015-09-09 13:24   ` [PATCH linux-next v6 5/8] Documentation: mtd: add a DT property to set the number of dummy cycles Cyrille Pitchen
@ 2015-09-09 13:24   ` Cyrille Pitchen
  2015-09-09 13:24   ` [PATCH linux-next v6 7/8] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen
  2015-09-10 10:36   ` [PATCH linux-next v6 0/8] add driver for Atmel QSPI controller Jonas Gorski
  7 siblings, 0 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

The number of dummy cycles used during Fast Read commands can be reduced
to improve transfer performances. Each manufacturer has a dedicated set of
registers to provide the memory with the exact number of dummy cycles it
should expect. Both the memory and the (Q)SPI controller must agree on
this number of dummy cycles.

The number of dummy cycles can be found into the memory datasheet and
mostly depends on the SPI clock frequency, the Fast Read op code and the
Single/Dual Data Rate mode.

Probing JEDEC Serial Flash Discoverable Parameters (SFDP) tables would
only provide the driver with a high enough number of dummy cycles for each
Fast Read command to be used for all clock frequencies: this solution
would not be optimized.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
---
 drivers/mtd/spi-nor/spi-nor.c | 102 ++++++++++++++++++++++++++++++++++--------
 include/linux/mtd/spi-nor.h   |   2 +
 2 files changed, 85 insertions(+), 19 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 820a2177ed5e..7405a1450dd1 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -121,24 +121,6 @@ static int read_cr(struct spi_nor *nor)
 }
 
 /*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
-	switch (nor->flash_read) {
-	case SPI_NOR_FAST:
-	case SPI_NOR_DUAL:
-	case SPI_NOR_QUAD:
-		return 8;
-	case SPI_NOR_NORMAL:
-		return 0;
-	}
-	return 0;
-}
-
-/*
  * Write status register 1 byte
  * Returns negative if error occurred.
  */
@@ -1194,6 +1176,86 @@ static void tune_manufacturer_commands(struct spi_nor *nor,
 	}
 }
 
+static int micron_set_dummy_cycles(struct spi_nor *nor)
+{
+	int ret;
+	u8 val, mask;
+
+	/* Read the Volatile Configuration Register (VCR). */
+	ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &val, 1);
+	if (ret < 0) {
+		dev_err(nor->dev, "error %d reading VCR\n", ret);
+		return ret;
+	}
+
+	write_enable(nor);
+
+	/* Update the number of dummy into the VCR. */
+	mask = GENMASK(7, 4);
+	val &= ~mask;
+	val |= (nor->read_dummy << 4) & mask;
+	ret = nor->write_reg(nor, SPINOR_OP_WR_VCR, &val, 1, 0);
+	if (ret < 0) {
+		dev_err(nor->dev, "error while writing VCR register\n");
+		return ret;
+	}
+
+	ret = spi_nor_wait_till_ready(nor);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * Dummy Cycle calculation for different type of read.
+ * It can be used to support more commands with
+ * different dummy cycle requirements.
+ */
+static int spi_nor_read_dummy_cycles(struct spi_nor *nor,
+				     const struct flash_info *info)
+{
+	struct device_node *np = nor->dev->of_node;
+	struct device *dev = nor->dev;
+	u32 num_dummy_cycles;
+
+	if (np && !of_property_read_u32(np, "m25p,num-dummy-cycles",
+					&num_dummy_cycles)) {
+		nor->read_dummy = num_dummy_cycles;
+
+		/*
+		 * This switch block might be moved at the end of the function,
+		 * once nor->read_dummy has been set, but it was not tested with
+		 * all Micron memories.
+		 * Now the "m25p,num-dummy-cycles" property needs to be
+		 * explicitly set in the device tree so the switch statement is
+		 * executed. This should avoid unwanted side effects and keep
+		 * backward compatibility.
+		 */
+		switch (JEDEC_MFR(info)) {
+		case CFI_MFR_ST:
+			return micron_set_dummy_cycles(nor);
+		default:
+			dev_warn(dev,
+				 "Tuning of the number of dummy cycles is not implemented for spi-nor of this manufacturer,\n"
+				 "ignoring the DT property value and falling back to the framework default settings.\n");
+			break;
+		}
+	}
+
+	/* Fallback to legacy code. */
+	switch (nor->flash_read) {
+	case SPI_NOR_FAST:
+	case SPI_NOR_DUAL:
+	case SPI_NOR_QUAD:
+		nor->read_dummy = 8;
+	case SPI_NOR_NORMAL:
+		nor->read_dummy = 0;
+	}
+
+	return 0;
+}
+
 static int spi_nor_check(struct spi_nor *nor)
 {
 	if (!nor->dev || !nor->read || !nor->write ||
@@ -1387,7 +1449,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 	/* Tune read, page program and erase commands */
 	tune_manufacturer_commands(nor, info);
 
-	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
+	ret = spi_nor_read_dummy_cycles(nor, info);
+	if (ret)
+		return ret;
 
 	dev_info(dev, "%s (%lld Kbytes)\n", info->name,
 			(long long)mtd->size >> 10);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index f13cd2cb3ac5..4a3f5d8655d8 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -66,6 +66,8 @@
 #define SPINOR_OP_MIO_RDID	0xaf	/* Multiple I/O Read JEDEC ID */
 #define SPINOR_OP_RD_EVCR	0x65    /* Read EVCR register */
 #define SPINOR_OP_WD_EVCR	0x61    /* Write EVCR register */
+#define SPINOR_OP_RD_VCR	0x85	/* Read VCR register */
+#define SPINOR_OP_WR_VCR	0x81	/* Write VCR register */
 
 /* Status Register bits. */
 #define SR_WIP			1	/* Write in progress */
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 7/8] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
                     ` (5 preceding siblings ...)
  2015-09-09 13:24   ` [PATCH linux-next v6 6/8] mtd: spi-nor: allow to tune " Cyrille Pitchen
@ 2015-09-09 13:24   ` Cyrille Pitchen
  2015-09-10 10:36   ` [PATCH linux-next v6 0/8] add driver for Atmel QSPI controller Jonas Gorski
  7 siblings, 0 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, marex-ynQEQJNshbs,
	ben-/+tVBieCtBitmTQ+vhA3Yw
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Cyrille Pitchen

This patch documents the DT bindings for the driver of the Atmel QSPI
controller embedded inside sama5d2x SoCs.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
Acked-by: Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
Acked-by: Marek Vasut <marex-ynQEQJNshbs@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
 .../devicetree/bindings/mtd/atmel-quadspi.txt      | 29 ++++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/atmel-quadspi.txt

diff --git a/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt b/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
new file mode 100644
index 000000000000..0b8d545bb198
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
@@ -0,0 +1,29 @@
+* Atmel Quad Serial Peripheral Interface (QSPI)
+
+Required properties:
+- compatible:     should be "atmel,sama5d2-qspi"
+- reg:            the first contains the register location and length,
+                  the second contains the memory mapping address and length
+- interrupts:     should contain the interrupt for the device
+- clocks:         the phandle of the clock needed by the QSPI controller
+- #address-cells: should be <1>
+- #size-cells:    should be <0>
+
+Example:
+
+spi@f0020000 {
+	compatible = "atmel,sama5d2-qspi";
+	reg = <0xf0020000 0x100>,
+	      <0xd0000000 0x8000000>;
+	interrupts = <52 IRQ_TYPE_LEVEL_HIGH 7>;
+	clocks = <&spi0_clk>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_spi0_default>;
+	status = "okay";
+
+	m25p80@0 {
+		...
+	};
+};
-- 
1.8.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH linux-next v6 8/8] mtd: atmel-quadspi: add driver for Atmel QSPI controller
  2015-09-09 13:24 [PATCH linux-next v6 0/8] add driver for Atmel QSPI controller Cyrille Pitchen
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
@ 2015-09-09 13:24 ` Cyrille Pitchen
  1 sibling, 0 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 13:24 UTC (permalink / raw)
  To: nicolas.ferre, broonie, linux-spi, dwmw2, computersforpeace,
	zajec5, beanhuo, juhosg, marex, ben
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree,
	linux-kernel, robh+dt, linux-mtd, galak, Cyrille Pitchen,
	linux-arm-kernel

This driver add support to the new Atmel QSPI controller embedded into
sama5d2x SoCs. It expects a NOR memory to be connected to the QSPI
controller.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
 drivers/mtd/spi-nor/Kconfig         |   7 +
 drivers/mtd/spi-nor/Makefile        |   1 +
 drivers/mtd/spi-nor/atmel-quadspi.c | 889 ++++++++++++++++++++++++++++++++++++
 3 files changed, 897 insertions(+)
 create mode 100644 drivers/mtd/spi-nor/atmel-quadspi.c

diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 89bf4c1faa2b..7a3d55429550 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -29,6 +29,13 @@ config SPI_FSL_QUADSPI
 	  This controller does not support generic SPI. It only supports
 	  SPI NOR.
 
+config SPI_ATMEL_QUADSPI
+	tristate "Atmel Quad SPI Controller"
+	depends on OF && HAS_DMA && (ARCH_AT91 || COMPILE_TEST)
+	help
+	  This enables support for the Quad SPI controller in master mode.
+	  We only connect the NOR to this controller now.
+
 config SPI_NXP_SPIFI
 	tristate "NXP SPI Flash Interface (SPIFI)"
 	depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index e53333ef8582..f5d23d7379bb 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor.o
 obj-$(CONFIG_SPI_FSL_QUADSPI)	+= fsl-quadspi.o
+obj-$(CONFIG_SPI_ATMEL_QUADSPI)	+= atmel-quadspi.o
 obj-$(CONFIG_SPI_NXP_SPIFI)	+= nxp-spifi.o
diff --git a/drivers/mtd/spi-nor/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
new file mode 100644
index 000000000000..dc0c1d8293d5
--- /dev/null
+++ b/drivers/mtd/spi-nor/atmel-quadspi.c
@@ -0,0 +1,889 @@
+/*
+ * Driver for Atmel QSPI Controller
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver is based on drivers/mtd/spi-nor/fsl-quadspi.c from Freescale.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/spi-nor.h>
+#include <linux/platform_data/atmel.h>
+#include <linux/platform_data/dma-atmel.h>
+#include <linux/of.h>
+
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+
+/* QSPI register offsets */
+#define QSPI_CR      0x0000  /* Control Register */
+#define QSPI_MR      0x0004  /* Mode Register */
+#define QSPI_RD      0x0008  /* Receive Data Register */
+#define QSPI_TD      0x000c  /* Transmit Data Register */
+#define QSPI_SR      0x0010  /* Status Register */
+#define QSPI_IER     0x0014  /* Interrupt Enable Register */
+#define QSPI_IDR     0x0018  /* Interrupt Disable Register */
+#define QSPI_IMR     0x001c  /* Interrupt Mask Register */
+#define QSPI_SCR     0x0020  /* Serial Clock Register */
+
+#define QSPI_IAR     0x0030  /* Instruction Address Register */
+#define QSPI_ICR     0x0034  /* Instruction Code Register */
+#define QSPI_IFR     0x0038  /* Instruction Frame Register */
+
+#define QSPI_SMR     0x0040  /* Scrambling Mode Register */
+#define QSPI_SKR     0x0044  /* Scrambling Key Register */
+
+#define QSPI_WPMR    0x00E4  /* Write Protection Mode Register */
+#define QSPI_WPSR    0x00E8  /* Write Protection Status Register */
+
+#define QSPI_VERSION 0x00FC  /* Version Register */
+
+
+/* Bitfields in QSPI_CR (Control Register) */
+#define QSPI_CR_QSPIEN                  BIT(0)
+#define QSPI_CR_QSPIDIS                 BIT(1)
+#define QSPI_CR_SWRST                   BIT(7)
+#define QSPI_CR_LASTXFER                BIT(24)
+
+/* Bitfields in QSPI_MR (Mode Register) */
+#define QSPI_MR_SSM                     BIT(0)
+#define QSPI_MR_LLB                     BIT(1)
+#define QSPI_MR_WDRBT                   BIT(2)
+#define QSPI_MR_SMRM                    BIT(3)
+#define QSPI_MR_CSMODE_MASK             GENMASK(5, 4)
+#define QSPI_MR_CSMODE_NOT_RELOADED     (0 << 4)
+#define QSPI_MR_CSMODE_LASTXFER         (1 << 4)
+#define QSPI_MR_CSMODE_SYSTEMATICALLY   (2 << 4)
+#define QSPI_MR_NBBITS_MASK             GENMASK(11, 8)
+#define QSPI_MR_NBBITS(n)               ((((n) - 8) << 8) & QSPI_MR_NBBITS_MASK)
+#define QSPI_MR_DLYBCT_MASK             GENMASK(23, 16)
+#define QSPI_MR_DLYBCT(n)               (((n) << 16) & QSPI_MR_DLYBCT_MASK)
+#define QSPI_MR_DLYCS_MASK              GENMASK(31, 24)
+#define QSPI_MR_DLYCS(n)                (((n) << 24) & QSPI_MR_DLYCS_MASK)
+
+/* Bitfields in QSPI_SR/QSPI_IER/QSPI_IDR/QSPI_IMR  */
+#define QSPI_SR_RDRF                    BIT(0)
+#define QSPI_SR_TDRE                    BIT(1)
+#define QSPI_SR_TXEMPTY                 BIT(2)
+#define QSPI_SR_OVRES                   BIT(3)
+#define QSPI_SR_CSR                     BIT(8)
+#define QSPI_SR_CSS                     BIT(9)
+#define QSPI_SR_INSTRE                  BIT(10)
+#define QSPI_SR_QSPIENS                 BIT(24)
+
+/* Bitfields in QSPI_SCR (Serial Clock Register) */
+#define QSPI_SCR_CPOL                   BIT(0)
+#define QSPI_SCR_CPHA                   BIT(1)
+#define QSPI_SCR_SCBR_MASK              GENMASK(15, 8)
+#define QSPI_SCR_SCBR(n)                (((n) << 8) & QSPI_SCR_SCBR_MASK)
+#define QSPI_SCR_DLYBS_MASK             GENMASK(23, 16)
+#define QSPI_SCR_DLYBS(n)               (((n) << 16) & QSPI_SCR_DLYBS_MASK)
+
+/* Bitfields in QSPI_ICR (Instruction Code Register) */
+#define QSPI_ICR_INST_MASK              GENMASK(7, 0)
+#define QSPI_ICR_INST(inst)             (((inst) << 0) & QSPI_ICR_INST_MASK)
+#define QSPI_ICR_OPT_MASK               GENMASK(23, 16)
+#define QSPI_ICR_OPT(opt)               (((opt) << 16) & QSPI_ICR_OPT_MASK)
+
+/* Bitfields in QSPI_IFR (Instruction Frame Register) */
+#define QSPI_IFR_WIDTH_MASK             GENMASK(2, 0)
+#define QSPI_IFR_WIDTH_SINGLE_BIT_SPI   (0 << 0)
+#define QSPI_IFR_WIDTH_DUAL_OUTPUT      (1 << 0)
+#define QSPI_IFR_WIDTH_QUAD_OUTPUT      (2 << 0)
+#define QSPI_IFR_WIDTH_DUAL_IO          (3 << 0)
+#define QSPI_IFR_WIDTH_QUAD_IO          (4 << 0)
+#define QSPI_IFR_WIDTH_DUAL_CMD         (5 << 0)
+#define QSPI_IFR_WIDTH_QUAD_CMD         (6 << 0)
+#define QSPI_IFR_INSTEN                 BIT(4)
+#define QSPI_IFR_ADDREN                 BIT(5)
+#define QSPI_IFR_OPTEN                  BIT(6)
+#define QSPI_IFR_DATAEN                 BIT(7)
+#define QSPI_IFR_OPTL_MASK              GENMASK(9, 8)
+#define QSPI_IFR_OPTL_1BIT              (0 << 8)
+#define QSPI_IFR_OPTL_2BIT              (1 << 8)
+#define QSPI_IFR_OPTL_4BIT              (2 << 8)
+#define QSPI_IFR_OPTL_8BIT              (3 << 8)
+#define QSPI_IFR_ADDRL                  BIT(10)
+#define QSPI_IFR_TFRTYP_MASK            GENMASK(13, 12)
+#define QSPI_IFR_TFRTYP_TRSFR_READ      (0 << 12)
+#define QSPI_IFR_TFRTYP_TRSFR_READ_MEM  (1 << 12)
+#define QSPI_IFR_TFRTYP_TRSFR_WRITE     (2 << 12)
+#define QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM (3 << 13)
+#define QSPI_IFR_CRM                    BIT(14)
+#define QSPI_IFR_NBDUM_MASK             GENMASK(20, 16)
+#define QSPI_IFR_NBDUM(n)               (((n) << 16) & QSPI_IFR_NBDUM_MASK)
+
+/* Bitfields in QSPI_SMR (Scrambling Mode Register) */
+#define QSPI_SMR_SCREN                  BIT(0)
+#define QSPI_SMR_RVDIS                  BIT(1)
+
+/* Bitfields in QSPI_WPMR (Write Protection Mode Register) */
+#define QSPI_WPMR_WPEN                  BIT(0)
+#define QSPI_WPMR_WPKEY_MASK            GENMASK(31, 8)
+#define QSPI_WPMR_WPKEY(wpkey)          (((wpkey) << 8) & QSPI_WPMR_WPKEY_MASK)
+
+/* Bitfields in QSPI_WPSR (Write Protection Status Register) */
+#define QSPI_WPSR_WPVS                  BIT(0)
+#define QSPI_WPSR_WPVSRC_MASK           GENMASK(15, 8)
+#define QSPI_WPSR_WPVSRC(src)           (((src) << 8) & QSPI_WPSR_WPVSRC)
+
+
+struct atmel_qspi {
+	void __iomem		*regs;
+	void __iomem		*mem;
+	dma_addr_t		phys_addr;
+	struct dma_chan		*chan;
+	struct clk		*clk;
+	struct platform_device	*pdev;
+	u32			pending;
+
+	struct mtd_info		mtd;
+	struct spi_nor		nor;
+	u32			clk_rate;
+	struct completion	cmd_completion;
+	struct completion	dma_completion;
+
+#ifdef DEBUG
+	u8			last_instruction;
+#endif
+};
+
+struct atmel_qspi_command {
+	u32	ifr;
+	union {
+		struct {
+			u32	instruction:1;
+			u32	address:3;
+			u32	mode:1;
+			u32	dummy:1;
+			u32	data:1;
+			u32	dma:1;
+			u32	reserved:24;
+		}		bits;
+		u32	word;
+	}	enable;
+	u8	instruction;
+	u8	mode;
+	u8	num_mode_cycles;
+	u8	num_dummy_cycles;
+	u32	address;
+
+	size_t		buf_len;
+	const void	*tx_buf;
+	void		*rx_buf;
+};
+
+/* Register access functions */
+static inline u32 qspi_readl(struct atmel_qspi *aq, u32 reg)
+{
+	return readl_relaxed(aq->regs + reg);
+}
+
+static inline void qspi_writel(struct atmel_qspi *aq, u32 reg, u32 value)
+{
+	writel_relaxed(value, aq->regs + reg);
+}
+
+
+#define QSPI_DMA_THRESHOLD	32
+
+static void atmel_qspi_dma_callback(void *arg)
+{
+	struct completion *dma_completion = arg;
+
+	complete(dma_completion);
+}
+
+static int atmel_qspi_run_dma_transfer(struct atmel_qspi *aq,
+				       const struct atmel_qspi_command *cmd)
+{
+	u32 offset = (cmd->enable.bits.address) ? cmd->address : 0;
+	struct dma_chan *chan = aq->chan;
+	struct device *dev = &aq->pdev->dev;
+	enum dma_data_direction direction;
+	dma_addr_t phys_addr, dst, src;
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	int err = 0;
+
+	if (cmd->tx_buf) {
+		direction = DMA_TO_DEVICE;
+		phys_addr = dma_map_single(dev, (void *)cmd->tx_buf,
+					   cmd->buf_len, direction);
+		src = phys_addr;
+		dst = aq->phys_addr + offset;
+	} else {
+		direction = DMA_FROM_DEVICE;
+		phys_addr = dma_map_single(dev, (void *)cmd->rx_buf,
+					   cmd->buf_len, direction);
+		src = aq->phys_addr + offset;
+		dst = phys_addr;
+	}
+	if (dma_mapping_error(dev, phys_addr))
+		return -ENOMEM;
+
+	desc = chan->device->device_prep_dma_memcpy(chan, dst, src,
+						    cmd->buf_len,
+						    DMA_PREP_INTERRUPT);
+	if (!desc) {
+		err = -ENOMEM;
+		goto unmap_single;
+	}
+
+	reinit_completion(&aq->dma_completion);
+	desc->callback = atmel_qspi_dma_callback;
+	desc->callback_param = &aq->dma_completion;
+	cookie = dmaengine_submit(desc);
+	err = dma_submit_error(cookie);
+	if (err)
+		goto unmap_single;
+	dma_async_issue_pending(chan);
+
+	if (!wait_for_completion_timeout(&aq->dma_completion,
+					 msecs_to_jiffies(1000)))
+		err = -ETIMEDOUT;
+
+	if (dma_async_is_tx_complete(chan, cookie, NULL, NULL) != DMA_COMPLETE)
+		err = -ETIMEDOUT;
+
+	if (err)
+		dmaengine_terminate_all(chan);
+unmap_single:
+	dma_unmap_single(dev, phys_addr, cmd->buf_len, direction);
+
+	return err;
+}
+
+static int atmel_qspi_run_transfer(struct atmel_qspi *aq,
+				   const struct atmel_qspi_command *cmd)
+{
+	void __iomem *ahb_mem;
+
+	/* First try a DMA transfer */
+	if (aq->chan && cmd->enable.bits.dma &&
+	    cmd->buf_len >= QSPI_DMA_THRESHOLD)
+		return atmel_qspi_run_dma_transfer(aq, cmd);
+
+	/* Then fallback to a PIO transfer */
+	ahb_mem = aq->mem;
+	if (cmd->enable.bits.address)
+		ahb_mem += cmd->address;
+	if (cmd->tx_buf)
+		memcpy_toio(ahb_mem, cmd->tx_buf, cmd->buf_len);
+	else
+		memcpy_fromio(cmd->rx_buf, ahb_mem, cmd->buf_len);
+
+	return 0;
+}
+
+#ifdef DEBUG
+static void atmel_qspi_debug_command(struct atmel_qspi *aq,
+				     const struct atmel_qspi_command *cmd)
+{
+	u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
+	size_t len = 0;
+	int i;
+
+	if (cmd->enable.bits.instruction) {
+		if (aq->last_instruction == cmd->instruction)
+			return;
+		aq->last_instruction = cmd->instruction;
+	}
+
+	if (cmd->enable.bits.instruction)
+		cmd_buf[len++] = cmd->instruction;
+
+	for (i = cmd->enable.bits.address-1; i >= 0; --i)
+		cmd_buf[len++] = (cmd->address >> (i << 3)) & 0xff;
+
+	if (cmd->enable.bits.mode)
+		cmd_buf[len++] = cmd->mode;
+
+	if (cmd->enable.bits.dummy) {
+		int num = cmd->num_dummy_cycles;
+
+		switch (cmd->ifr & QSPI_IFR_WIDTH_MASK) {
+		case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
+		case QSPI_IFR_WIDTH_DUAL_OUTPUT:
+		case QSPI_IFR_WIDTH_QUAD_OUTPUT:
+			num >>= 3;
+			break;
+		case QSPI_IFR_WIDTH_DUAL_IO:
+		case QSPI_IFR_WIDTH_DUAL_CMD:
+			num >>= 2;
+			break;
+		case QSPI_IFR_WIDTH_QUAD_IO:
+		case QSPI_IFR_WIDTH_QUAD_CMD:
+			num >>= 1;
+			break;
+		default:
+			return;
+		}
+
+		for (i = 0; i < num; ++i)
+			cmd_buf[len++] = 0;
+	}
+
+	/* Dump the SPI command */
+	print_hex_dump(KERN_DEBUG, "qspi cmd: ", DUMP_PREFIX_NONE,
+		       32, 1, cmd_buf, len, false);
+
+#ifdef VERBOSE_DEBUG
+	/* If verbose debug is enabled, also dump the TX data */
+	if (cmd->enable.bits.data && cmd->tx_buf)
+		print_hex_dump(KERN_DEBUG, "qspi tx : ", DUMP_PREFIX_NONE,
+			       32, 1, cmd->tx_buf, cmd->buf_len, false);
+#endif
+}
+#else
+#define atmel_qspi_debug_command(aq, cmd)
+#endif
+
+static int atmel_qspi_run_command(struct atmel_qspi *aq,
+				  const struct atmel_qspi_command *cmd)
+{
+	u32 iar, icr, ifr, sr;
+	int err = 0;
+
+	iar = 0;
+	icr = 0;
+	ifr = cmd->ifr;
+
+	/* Compute instruction parameters */
+	if (cmd->enable.bits.instruction) {
+		icr |= QSPI_ICR_INST(cmd->instruction);
+		ifr |= QSPI_IFR_INSTEN;
+	}
+
+	/* Compute address parameters */
+	switch (cmd->enable.bits.address) {
+	case 4:
+		ifr |= QSPI_IFR_ADDRL;
+		/* fall through to the 24bit (3 byte) address case. */
+	case 3:
+		iar = (cmd->enable.bits.data) ? 0 : cmd->address;
+		ifr |= QSPI_IFR_ADDREN;
+		break;
+	case 0:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Compute option parameters */
+	if (cmd->enable.bits.mode && cmd->num_mode_cycles) {
+		u32 mode_cycle_bits, mode_bits;
+
+		icr |= QSPI_ICR_OPT(cmd->mode);
+		ifr |= QSPI_IFR_OPTEN;
+
+		switch (ifr & QSPI_IFR_WIDTH_MASK) {
+		case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
+		case QSPI_IFR_WIDTH_DUAL_OUTPUT:
+		case QSPI_IFR_WIDTH_QUAD_OUTPUT:
+			mode_cycle_bits = 1;
+			break;
+		case QSPI_IFR_WIDTH_DUAL_IO:
+		case QSPI_IFR_WIDTH_DUAL_CMD:
+			mode_cycle_bits = 2;
+			break;
+		case QSPI_IFR_WIDTH_QUAD_IO:
+		case QSPI_IFR_WIDTH_QUAD_CMD:
+			mode_cycle_bits = 4;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		mode_bits = cmd->num_mode_cycles * mode_cycle_bits;
+		switch (mode_bits) {
+		case 1:
+			ifr |= QSPI_IFR_OPTL_1BIT;
+			break;
+
+		case 2:
+			ifr |= QSPI_IFR_OPTL_2BIT;
+			break;
+
+		case 4:
+			ifr |= QSPI_IFR_OPTL_4BIT;
+			break;
+
+		case 8:
+			ifr |= QSPI_IFR_OPTL_8BIT;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* Set number of dummy cycles */
+	if (cmd->enable.bits.dummy)
+		ifr |= QSPI_IFR_NBDUM(cmd->num_dummy_cycles);
+
+	/* Set data enable */
+	if (cmd->enable.bits.data) {
+		ifr |= QSPI_IFR_DATAEN;
+
+		/* Special case for Continuous Read Mode */
+		if (!cmd->tx_buf && !cmd->rx_buf)
+			ifr |= QSPI_IFR_CRM;
+	}
+
+	/* Set QSPI Instruction Frame registers */
+	atmel_qspi_debug_command(aq, cmd);
+	qspi_writel(aq, QSPI_IAR, iar);
+	qspi_writel(aq, QSPI_ICR, icr);
+	qspi_writel(aq, QSPI_IFR, ifr);
+
+	/* Skip to the final steps if there is no data */
+	if (!cmd->enable.bits.data)
+		goto no_data;
+
+	/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
+	(void)qspi_readl(aq, QSPI_IFR);
+
+	/* Stop here for continuous read */
+	if (!cmd->tx_buf && !cmd->rx_buf)
+		return 0;
+	/* Send/Receive data */
+	err = atmel_qspi_run_transfer(aq, cmd);
+
+	/* Release the chip-select */
+	qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER);
+
+	if (err)
+		return err;
+
+#if defined(DEBUG) && defined(VERBOSE_DEBUG)
+	/*
+	 * If verbose debug is enabled, also dump the RX data in addition to
+	 * the SPI command previously dumped by atmel_qspi_debug_command()
+	 */
+	if (cmd->rx_buf)
+		print_hex_dump(KERN_DEBUG, "qspi rx : ", DUMP_PREFIX_NONE,
+			       32, 1, cmd->rx_buf, cmd->buf_len, false);
+#endif
+no_data:
+	/* Poll INSTRuction End status */
+	sr = qspi_readl(aq, QSPI_SR);
+	if (sr & QSPI_SR_INSTRE)
+		return err;
+
+	/* Wait for INSTRuction End interrupt */
+	reinit_completion(&aq->cmd_completion);
+	aq->pending = 0;
+	qspi_writel(aq, QSPI_IER, QSPI_SR_INSTRE);
+	if (!wait_for_completion_timeout(&aq->cmd_completion,
+					 msecs_to_jiffies(1000)))
+		err = -ETIMEDOUT;
+	qspi_writel(aq, QSPI_IDR, QSPI_SR_INSTRE);
+
+	return err;
+}
+
+static int atmel_qspi_command_set_ifr(struct atmel_qspi_command *cmd,
+				      u32 ifr_tfrtyp,
+				      enum spi_protocol proto)
+{
+	cmd->ifr = ifr_tfrtyp;
+
+	switch (proto) {
+	case SPI_PROTO_1_1_1:
+		cmd->ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
+		break;
+	case SPI_PROTO_1_1_2:
+		cmd->ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
+		break;
+	case SPI_PROTO_1_1_4:
+		cmd->ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
+		break;
+	case SPI_PROTO_1_2_2:
+		cmd->ifr |= QSPI_IFR_WIDTH_DUAL_IO;
+		break;
+	case SPI_PROTO_1_4_4:
+		cmd->ifr |= QSPI_IFR_WIDTH_QUAD_IO;
+		break;
+	case SPI_PROTO_2_2_2:
+		cmd->ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
+		break;
+	case SPI_PROTO_4_4_4:
+		cmd->ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
+			       u8 *buf, int len)
+{
+	struct atmel_qspi *aq = nor->priv;
+	struct atmel_qspi_command cmd;
+	int ret;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.enable.bits.instruction = 1;
+	cmd.enable.bits.data = 1;
+	cmd.instruction = opcode;
+	cmd.rx_buf = buf;
+	cmd.buf_len = len;
+
+	ret = atmel_qspi_command_set_ifr(&cmd,
+					 QSPI_IFR_TFRTYP_TRSFR_READ,
+					 nor->reg_proto);
+	if (ret)
+		return ret;
+
+	return atmel_qspi_run_command(aq, &cmd);
+}
+
+static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
+				u8 *buf, int len,
+				int write_enable)
+{
+	struct atmel_qspi *aq = nor->priv;
+	struct atmel_qspi_command cmd;
+	int ret;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.enable.bits.instruction = 1;
+	cmd.enable.bits.data = (buf != NULL && len > 0);
+	cmd.instruction = opcode;
+	cmd.tx_buf = buf;
+	cmd.buf_len = len;
+
+	ret = atmel_qspi_command_set_ifr(&cmd,
+					 QSPI_IFR_TFRTYP_TRSFR_WRITE,
+					 nor->reg_proto);
+	if (ret)
+		return ret;
+
+	return atmel_qspi_run_command(aq, &cmd);
+}
+
+static void atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
+			     size_t *retlen, const u_char *write_buf)
+{
+	struct atmel_qspi *aq = nor->priv;
+	struct atmel_qspi_command cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.enable.bits.instruction = 1;
+	cmd.enable.bits.address = nor->addr_width;
+	cmd.enable.bits.data = 1;
+	cmd.enable.bits.dma = 1;
+	cmd.instruction = nor->program_opcode;
+	cmd.address = (u32)to;
+	cmd.tx_buf = write_buf;
+	cmd.buf_len = len;
+
+	if (atmel_qspi_command_set_ifr(&cmd,
+				       QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
+				       nor->write_proto))
+		return;
+
+	if (!atmel_qspi_run_command(aq, &cmd))
+		*retlen += len;
+}
+
+static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
+{
+	struct atmel_qspi *aq = nor->priv;
+	struct atmel_qspi_command cmd;
+	int ret;
+
+	dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
+		aq->mtd.erasesize / 1024, (u32)offs);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.enable.bits.instruction = 1;
+	cmd.enable.bits.address = nor->addr_width;
+	cmd.instruction = nor->erase_opcode;
+	cmd.address = (u32)offs;
+
+	ret = atmel_qspi_command_set_ifr(&cmd,
+					 QSPI_IFR_TFRTYP_TRSFR_WRITE,
+					 nor->erase_proto);
+	if (ret)
+		return ret;
+
+	return atmel_qspi_run_command(aq, &cmd);
+}
+
+static int atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
+			   size_t *retlen, u_char *read_buf)
+{
+	struct atmel_qspi *aq = nor->priv;
+	struct atmel_qspi_command cmd;
+	int ret;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.enable.bits.instruction = 1;
+	cmd.enable.bits.address = nor->addr_width;
+	cmd.enable.bits.dummy = (nor->read_dummy > 0);
+	cmd.enable.bits.data = 1;
+	cmd.enable.bits.dma = 1;
+	cmd.instruction = nor->read_opcode;
+	cmd.address = (u32)from;
+	cmd.num_dummy_cycles = nor->read_dummy;
+	cmd.rx_buf = read_buf;
+	cmd.buf_len = len;
+
+	ret = atmel_qspi_command_set_ifr(&cmd,
+					 QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
+					 nor->read_proto);
+	if (ret)
+		return ret;
+
+	ret = atmel_qspi_run_command(aq, &cmd);
+	if (ret)
+		return ret;
+
+	*retlen += len;
+	return 0;
+}
+
+static int atmel_qspi_init(struct atmel_qspi *aq)
+{
+	unsigned long src_rate;
+	u32 mr, scr, scbr;
+
+	/* Reset the QSPI controller */
+	qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST);
+
+	/* Set the QSPI controller in Serial Memory Mode */
+	mr = QSPI_MR_SSM | QSPI_MR_NBBITS(8);
+	qspi_writel(aq, QSPI_MR, mr);
+
+	src_rate = clk_get_rate(aq->clk);
+	if (!src_rate)
+		return -EINVAL;
+
+	/* Compute the QSPI baudrate */
+	scbr = DIV_ROUND_UP(src_rate, aq->clk_rate);
+	if (scbr > 0)
+		scbr--;
+	scr = QSPI_SCR_SCBR(scbr);
+	qspi_writel(aq, QSPI_SCR, scr);
+
+	/* Enable the QSPI controller */
+	qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN);
+
+	return 0;
+}
+
+static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
+{
+	struct atmel_qspi *aq = (struct atmel_qspi *)dev_id;
+	u32 status, mask, pending;
+
+	status = qspi_readl(aq, QSPI_SR);
+	mask = qspi_readl(aq, QSPI_IMR);
+	pending = status & mask;
+
+	if (!pending)
+		return IRQ_NONE;
+
+	aq->pending |= pending;
+	if (pending & QSPI_SR_INSTRE)
+		complete(&aq->cmd_completion);
+
+	return IRQ_HANDLED;
+}
+
+static int atmel_qspi_probe(struct platform_device *pdev)
+{
+	struct device_node *child, *np = pdev->dev.of_node;
+	struct mtd_part_parser_data ppdata;
+	struct atmel_qspi *aq;
+	struct resource *res;
+	dma_cap_mask_t mask;
+	struct spi_nor *nor;
+	struct mtd_info *mtd;
+	char modalias[40];
+	int irq, err = 0;
+
+	if (of_get_child_count(np) != 1)
+		return -ENODEV;
+	child = of_get_next_child(np, NULL);
+
+	aq = devm_kzalloc(&pdev->dev, sizeof(*aq), GFP_KERNEL);
+	if (!aq) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	platform_set_drvdata(pdev, aq);
+	init_completion(&aq->cmd_completion);
+	init_completion(&aq->dma_completion);
+	aq->pdev = pdev;
+
+	/* Map the registers */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	aq->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(aq->regs)) {
+		dev_err(&pdev->dev, "missing registers\n");
+		err = PTR_ERR(aq->regs);
+		goto exit;
+	}
+
+	/* Map the AHB memory */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	aq->mem = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(aq->mem)) {
+		dev_err(&pdev->dev, "missing AHB memory\n");
+		err = PTR_ERR(aq->regs);
+		goto exit;
+	}
+	aq->phys_addr = (dma_addr_t)res->start;
+
+	/* Get the peripheral clock */
+	aq->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(aq->clk)) {
+		dev_err(&pdev->dev, "missing peripheral clock\n");
+		err = PTR_ERR(aq->clk);
+		goto exit;
+	}
+
+	/* Enable the peripheral clock */
+	err = clk_prepare_enable(aq->clk);
+	if (err) {
+		dev_err(&pdev->dev, "failed to enable the peripheral clock\n");
+		goto exit;
+	}
+
+	/* Request the IRQ */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "missing IRQ\n");
+		err = irq;
+		goto disable_clk;
+	}
+	err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
+			       0, dev_name(&pdev->dev), aq);
+	if (err)
+		goto disable_clk;
+
+	/* Try to get a DMA channel for memcpy() operation */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+	aq->chan = dma_request_channel(mask, NULL, NULL);
+	if (!aq->chan)
+		dev_warn(&pdev->dev, "no available DMA channel\n");
+
+	/* Setup the spi-nor */
+	nor = &aq->nor;
+	mtd = &aq->mtd;
+
+	nor->mtd = mtd;
+	nor->dev = &pdev->dev;
+	nor->priv = aq;
+	mtd->priv = nor;
+
+	nor->read_reg = atmel_qspi_read_reg;
+	nor->write_reg = atmel_qspi_write_reg;
+	nor->read = atmel_qspi_read;
+	nor->write = atmel_qspi_write;
+	nor->erase = atmel_qspi_erase;
+
+	if (of_modalias_node(child, modalias, sizeof(modalias)) < 0) {
+		err = -ENODEV;
+		goto release_channel;
+	}
+
+	err = of_property_read_u32(child, "spi-max-frequency", &aq->clk_rate);
+	if (err < 0)
+		goto release_channel;
+
+	err = atmel_qspi_init(aq);
+	if (err)
+		goto release_channel;
+
+	nor->dev->of_node = child;
+	err = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
+	nor->dev->of_node = np;
+	if (err)
+		goto release_channel;
+
+	ppdata.of_node = child;
+	err = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+	if (err)
+		goto release_channel;
+
+	of_node_put(child);
+
+	return 0;
+
+release_channel:
+	if (aq->chan)
+		dma_release_channel(aq->chan);
+disable_clk:
+	clk_disable_unprepare(aq->clk);
+exit:
+	of_node_put(child);
+
+	return err;
+}
+
+static int atmel_qspi_remove(struct platform_device *pdev)
+{
+	struct atmel_qspi *aq = platform_get_drvdata(pdev);
+
+	mtd_device_unregister(&aq->mtd);
+	qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS);
+	if (aq->chan)
+		dma_release_channel(aq->chan);
+	clk_disable_unprepare(aq->clk);
+	return 0;
+}
+
+
+static const struct of_device_id atmel_qspi_dt_ids[] = {
+	{ .compatible = "atmel,sama5d2-qspi" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_qspi_dt_ids);
+
+static struct platform_driver atmel_qspi_driver = {
+	.driver = {
+		.name	= "atmel_qspi",
+		.of_match_table	= atmel_qspi_dt_ids,
+	},
+	.probe		= atmel_qspi_probe,
+	.remove		= atmel_qspi_remove,
+};
+module_platform_driver(atmel_qspi_driver);
+
+MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
+MODULE_DESCRIPTION("Atmel QSPI Controller driver");
+MODULE_LICENSE("GPL v2");
-- 
1.8.2.2

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

* Re: [PATCH linux-next v6 1/8] mtd: spi-nor: read JEDEC ID with multiple I/O protocols
       [not found]     ` <735e22efa25f6529bf8ed4a709c18a719176714b.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
@ 2015-09-09 13:59       ` Marek Vasut
       [not found]         ` <201509091559.40948.marex-ynQEQJNshbs@public.gmane.org>
  2015-09-10 10:25       ` Jonas Gorski
  1 sibling, 1 reply; 14+ messages in thread
From: Marek Vasut @ 2015-09-09 13:59 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, ben-/+tVBieCtBitmTQ+vhA3Yw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Wednesday, September 09, 2015 at 03:24:11 PM, Cyrille Pitchen wrote:
> When their quad or dual I/O mode is enabled, Micron and Macronix spi-nor
> memories don't reply to the regular Read ID (0x9f) command. Instead they
> reply to a new dedicated command Read ID Multiple I/O (0xaf).
> 
> If the Read ID (0x9f) command fails (the read ID is all 1's or all 0's),
> then the Read ID Multiple I/O (0xaf) is used, first with SPI 4-4-4 protocol
> (supported by both Micron and Macronix memories), lately with SPI-2-2-2
> protocol (supported only by Micron memories).
> 
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/mtd/spi-nor/spi-nor.c | 28 +++++++++++++++++++++++++++-
>  include/linux/mtd/spi-nor.h   | 23 +++++++++++++++++++++--
>  2 files changed, 48 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index f59aedfe1462..80a0db078aaa 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -703,8 +703,9 @@ static const struct flash_info spi_nor_ids[] = {
> 
>  static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
>  {
> -	int			tmp;
> +	int			i, tmp;
>  	u8			id[SPI_NOR_MAX_ID_LEN];
> +	enum spi_protocol	proto[2] = {SPI_PROTO_4_4_4, SPI_PROTO_2_2_2};
>  	const struct flash_info	*info;
> 
>  	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
> @@ -713,6 +714,25 @@ static const struct flash_info *spi_nor_read_id(struct
> spi_nor *nor) return ERR_PTR(tmp);
>  	}
> 
> +	/* Special case for Micron/Macronix qspi nor. */
> +	for (i = 0; i < ARRAY_SIZE(proto); ++i) {
> +		if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
> +		      (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00)))
> +			break;
> +
> +		nor->erase_proto = proto[i];
> +		nor->read_proto = proto[i];
> +		nor->write_proto = proto[i];
> +		nor->reg_proto = proto[i];
> +		tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID,
> +				    id, SPI_NOR_MAX_ID_LEN);
> +		if (tmp < 0) {
> +			dev_dbg(nor->dev,
> +				" error %d reading JEDEC ID (MULTI IO)\n", tmp);

Don't you have one space too much in front of the " error" ?

> +			return ERR_PTR(tmp);
> +		}
> +	}
> +
>  	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
>  		info = &spi_nor_ids[tmp];
>  		if (info->id_len) {

[...]
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH linux-next v6 1/8] mtd: spi-nor: read JEDEC ID with multiple I/O protocols
       [not found]         ` <201509091559.40948.marex-ynQEQJNshbs@public.gmane.org>
@ 2015-09-09 15:55           ` Cyrille Pitchen
  0 siblings, 0 replies; 14+ messages in thread
From: Cyrille Pitchen @ 2015-09-09 15:55 UTC (permalink / raw)
  To: Marek Vasut
  Cc: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w,
	broonie-DgEjT+Ai2ygdnm+yROfE0A, linux-spi-u79uwXL29TY76Z2rM5mHXA,
	dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	computersforpeace-Re5JQEeQqe8AvxtiuMwx3w,
	zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
	juhosg-p3rKhJxN3npAfugRpC6u6w, ben-/+tVBieCtBitmTQ+vhA3Yw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Marek,

Le 09/09/2015 15:59, Marek Vasut a écrit :
> On Wednesday, September 09, 2015 at 03:24:11 PM, Cyrille Pitchen wrote:
>> When their quad or dual I/O mode is enabled, Micron and Macronix spi-nor
>> memories don't reply to the regular Read ID (0x9f) command. Instead they
>> reply to a new dedicated command Read ID Multiple I/O (0xaf).
>>
>> If the Read ID (0x9f) command fails (the read ID is all 1's or all 0's),
>> then the Read ID Multiple I/O (0xaf) is used, first with SPI 4-4-4 protocol
>> (supported by both Micron and Macronix memories), lately with SPI-2-2-2
>> protocol (supported only by Micron memories).
>>
>> Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
>> ---
>>  drivers/mtd/spi-nor/spi-nor.c | 28 +++++++++++++++++++++++++++-
>>  include/linux/mtd/spi-nor.h   | 23 +++++++++++++++++++++--
>>  2 files changed, 48 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>> index f59aedfe1462..80a0db078aaa 100644
>> --- a/drivers/mtd/spi-nor/spi-nor.c
>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>> @@ -703,8 +703,9 @@ static const struct flash_info spi_nor_ids[] = {
>>
>>  static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
>>  {
>> -	int			tmp;
>> +	int			i, tmp;
>>  	u8			id[SPI_NOR_MAX_ID_LEN];
>> +	enum spi_protocol	proto[2] = {SPI_PROTO_4_4_4, SPI_PROTO_2_2_2};
>>  	const struct flash_info	*info;
>>
>>  	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
>> @@ -713,6 +714,25 @@ static const struct flash_info *spi_nor_read_id(struct
>> spi_nor *nor) return ERR_PTR(tmp);
>>  	}
>>
>> +	/* Special case for Micron/Macronix qspi nor. */
>> +	for (i = 0; i < ARRAY_SIZE(proto); ++i) {
>> +		if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
>> +		      (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00)))
>> +			break;
>> +
>> +		nor->erase_proto = proto[i];
>> +		nor->read_proto = proto[i];
>> +		nor->write_proto = proto[i];
>> +		nor->reg_proto = proto[i];
>> +		tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID,
>> +				    id, SPI_NOR_MAX_ID_LEN);
>> +		if (tmp < 0) {
>> +			dev_dbg(nor->dev,
>> +				" error %d reading JEDEC ID (MULTI IO)\n", tmp);
> 
> Don't you have one space too much in front of the " error" ?
> 

Probably, I've just copied and pasted the dev_dbg() message few lines above when
the regular SPINOR_OP_RDID fails, then appended the "(MULTI IO)" string to make
think consistent but I don't mind removing the leading space.

	tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
	if (tmp < 0) {
		dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
		return ERR_PTR(tmp);
	}

>> +			return ERR_PTR(tmp);
>> +		}
>> +	}
>> +
>>  	for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
>>  		info = &spi_nor_ids[tmp];
>>  		if (info->id_len) {
> 
> [...]
> 

Best Regards,

Cyrille
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH linux-next v6 1/8] mtd: spi-nor: read JEDEC ID with multiple I/O protocols
       [not found]     ` <735e22efa25f6529bf8ed4a709c18a719176714b.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
  2015-09-09 13:59       ` Marek Vasut
@ 2015-09-10 10:25       ` Jonas Gorski
  1 sibling, 0 replies; 14+ messages in thread
From: Jonas Gorski @ 2015-09-10 10:25 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, David Woodhouse, Brian Norris,
	Rafał Miłecki, Bean Huo (beanhuo), Gabor Juhos,
	Marek Vašut, Ben Hutchings,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	MTD Maling List

Hi,

On Wed, Sep 9, 2015 at 3:24 PM, Cyrille Pitchen
<cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org> wrote:
> When their quad or dual I/O mode is enabled, Micron and Macronix spi-nor
> memories don't reply to the regular Read ID (0x9f) command. Instead they
> reply to a new dedicated command Read ID Multiple I/O (0xaf).
>
> If the Read ID (0x9f) command fails (the read ID is all 1's or all 0's),
> then the Read ID Multiple I/O (0xaf) is used, first with SPI 4-4-4 protocol
> (supported by both Micron and Macronix memories), lately with SPI-2-2-2
> protocol (supported only by Micron memories).
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/mtd/spi-nor/spi-nor.c | 28 +++++++++++++++++++++++++++-
>  include/linux/mtd/spi-nor.h   | 23 +++++++++++++++++++++--
>  2 files changed, 48 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index f59aedfe1462..80a0db078aaa 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -703,8 +703,9 @@ static const struct flash_info spi_nor_ids[] = {
>
>  static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
>  {
> -       int                     tmp;
> +       int                     i, tmp;
>         u8                      id[SPI_NOR_MAX_ID_LEN];
> +       enum spi_protocol       proto[2] = {SPI_PROTO_4_4_4, SPI_PROTO_2_2_2};
>         const struct flash_info *info;
>
>         tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
> @@ -713,6 +714,25 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
>                 return ERR_PTR(tmp);
>         }
>
> +       /* Special case for Micron/Macronix qspi nor. */
> +       for (i = 0; i < ARRAY_SIZE(proto); ++i) {
> +               if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) ||
> +                     (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00)))
> +                       break;

you should probably do something like

               if (proto == SPI_PROTO_2_2_2 && !(nor->flags & SPI_NOR_DUAL))
                              continue;

               if (proto == SPI_PROTO_4_4_4 && !(nor->flags & SPI_NOR_QUAD))
                              continue;
> +
> +               nor->erase_proto = proto[i];
> +               nor->read_proto = proto[i];
> +               nor->write_proto = proto[i];
> +               nor->reg_proto = proto[i];
> +               tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID,
> +                                   id, SPI_NOR_MAX_ID_LEN);

Not sure if it has any releveance that the MIO_RDID commands can only
return the first three id bytes, and not the full id.

> +               if (tmp < 0) {
> +                       dev_dbg(nor->dev,
> +                               " error %d reading JEDEC ID (MULTI IO)\n", tmp);
> +                       return ERR_PTR(tmp);

Or continue here, as the controller might return an error if it does
not support the protocol.

> +               }
> +       }
> +
>         for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
>                 info = &spi_nor_ids[tmp];
>                 if (info->id_len) {
> @@ -1013,6 +1033,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
>         if (ret)
>                 return ret;
>
> +       /* Reset SPI protocol for all commands */
> +       nor->erase_proto = SPI_PROTO_1_1_1;
> +       nor->read_proto = SPI_PROTO_1_1_1;
> +       nor->write_proto = SPI_PROTO_1_1_1;
> +       nor->reg_proto = SPI_PROTO_1_1_1;
> +
>         if (name)
>                 info = spi_nor_match_id(name);
>         /* Try to auto-detect if chip name wasn't specified or not found */
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index e5409524bb0a..66a5f144728a 100644
> --- a/include/linux/mtd/spi-nor.h
> +++ b/include/linux/mtd/spi-nor.h
> @@ -57,8 +57,9 @@
>  #define SPINOR_OP_BRWR         0x17    /* Bank register write */
>
>  /* Used for Micron flashes only. */
> -#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
> -#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */
> +#define SPINOR_OP_MIO_RDID     0xaf    /* Multiple I/O Read JEDEC ID */
> +#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
> +#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */
>
>  /* Status Register bits. */
>  #define SR_WIP                 1       /* Write in progress */
> @@ -87,6 +88,16 @@ enum read_mode {
>         SPI_NOR_QUAD,
>  };
>
> +enum spi_protocol {
> +       SPI_PROTO_1_1_1,        /* SPI */
> +       SPI_PROTO_1_1_2,        /* Dual Output */
> +       SPI_PROTO_1_1_4,        /* Quad Output */
> +       SPI_PROTO_1_2_2,        /* Dual IO */
> +       SPI_PROTO_1_4_4,        /* Quad IO */
> +       SPI_PROTO_2_2_2,        /* Dual Command */
> +       SPI_PROTO_4_4_4,        /* Quad Command */
> +};

I wonder if the spi-nor controller should somehow store which of these
protocols it can support, so we can check for support before using any
of the dual/quad (i/o) commands.

Also I think you should split out the new readid command usage from
the rest of the SPI_PROTO stuff, as they are only semirelated.

And for good definsive programming, you should first make the spi-nor
controller drivers aware of the protocol stuff before starting to use
it, else you risk breaking controllers silently.


> +
>  /**
>   * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
>   * @wren:              command for "Write Enable", or 0x00 for not required
> @@ -142,6 +153,10 @@ enum spi_nor_option_flags {
>   * @sst_write_second:  used by the SST write operation
>   * @flags:             flag options for the current SPI-NOR (SNOR_F_*)
>   * @cfg:               used by the read_xfer/write_xfer
> + * @erase_proto:       the SPI protocol used by erase operations
> + * @read_proto:                the SPI protocol used by read operations
> + * @write_proto:       the SPI protocol used by write operations
> + * @reg_proto          the SPI protocol used by read_reg/write_reg operations
>   * @cmd_buf:           used by the write_reg
>   * @prepare:           [OPTIONAL] do some preparations for the
>   *                     read/write/erase/lock/unlock operations
> @@ -169,6 +184,10 @@ struct spi_nor {
>         u8                      read_opcode;
>         u8                      read_dummy;
>         u8                      program_opcode;
> +       enum spi_protocol       erase_proto;
> +       enum spi_protocol       read_proto;
> +       enum spi_protocol       write_proto;
> +       enum spi_protocol       reg_proto;
>         enum read_mode          flash_read;
>         bool                    sst_write_second;
>         u32                     flags;



Regards
Jonas
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH linux-next v6 4/8] mtd: spi-nor: use optimized commands for read/write/erase operations
  2015-09-09 13:24   ` [PATCH linux-next v6 4/8] mtd: spi-nor: use optimized commands for read/write/erase operations Cyrille Pitchen
@ 2015-09-10 10:28     ` Jonas Gorski
  0 siblings, 0 replies; 14+ messages in thread
From: Jonas Gorski @ 2015-09-10 10:28 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: nicolas.ferre, Mark Brown, linux-spi, David Woodhouse,
	Brian Norris, Rafał Miłecki, Bean Huo (beanhuo),
	Gabor Juhos, Marek Vašut, Ben Hutchings,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	MTD Maling List

On Wed, Sep 9, 2015 at 3:24 PM, Cyrille Pitchen
<cyrille.pitchen@atmel.com> wrote:
> The op codes used by the spi-nor framework are now tuned depending on the
> memory manufacturer.
>
> Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>

To be honest, I'm not sure if it's worth it using the dual/quad I/O
commands on spansion, as they have different dummy cycle requirements
than the fast/dual/quad reads.

E.g. on S25FL128S, Fast/dual/quad is fine with 8 dummy cycles up to
104 MHz, but Quad I/O needs 1 cycle for <= 50 MHz, 4 for <= 90 MHz,
and 5 for <= 104 MHz. Which means we would need a lot more logic in
there or setting it from dts, and for existing users already using
dual/quad mode this would break it. And for m25p80 it would mean to
split the cmd + address into two transfers, all to save 20~30 clock
cycles, which will likely be dwarfed by the savings of the
dual/quadness of the data.

Apart from the fact that I would assume many spi-controllers will have
problems with dummy cycles that aren't a multiple of 8, and would
therefore require bitshifting the data when reading, which will
definitely eat up any time savings from the dual/quad written address.

And for the quad program command the datasheet says it only has any
performance improvement if running at less than 12 MHz, so probably
not much to gain for most users.


Jonas

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

* Re: [PATCH linux-next v6 0/8] add driver for Atmel QSPI controller
       [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
                     ` (6 preceding siblings ...)
  2015-09-09 13:24   ` [PATCH linux-next v6 7/8] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen
@ 2015-09-10 10:36   ` Jonas Gorski
  7 siblings, 0 replies; 14+ messages in thread
From: Jonas Gorski @ 2015-09-10 10:36 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w, Mark Brown,
	linux-spi-u79uwXL29TY76Z2rM5mHXA, David Woodhouse, Brian Norris,
	Rafał Miłecki, Bean Huo (beanhuo), Gabor Juhos,
	Marek Vašut, Ben Hutchings,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	MTD Maling List

Hi,

On Wed, Sep 9, 2015 at 3:24 PM, Cyrille Pitchen
<cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org> wrote:
> This series of patches add support for the new Atmel QSPI controller
> embedded inside sama5d2x SoCs.
>
> These patches were first developped for linux-3.18-at91 and tested on a
> sama5d27 Xplained ultra board, which embeds a Micron n25q128a13 QSPI NOR
> flash memory. Then the series was adapted for mainline.

All in all, this looks a lot better now compared to the previous
version. What I'm still missing is some definsive programming to
ensure we don't break existing users, or update the current users to
not break. The issues I currently see are

1) We don't check if a spi-nor controller actually supports a certain
protocol, just if SPI_NOR_DUAL/_QUAD is set
2) We currently set these new protocols, but none of the current
spi-nor controller drivers are aware of the protocols, which has the
possiblity of silently breaking things (or not, since they will then
fail to read correct data)
3) m25p80 currently uses SPI_RX_DUAL -> SPI_NOR_DUAL, but does not
check SPI_TX_DUAL (which is required for any of the != 1_1_x
commands). Although this is more an academical concern, as currently
all spi controller drivers seem to support _RX_ and _TX_, never only
one direction.


Jonas
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

end of thread, other threads:[~2015-09-10 10:36 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-09 13:24 [PATCH linux-next v6 0/8] add driver for Atmel QSPI controller Cyrille Pitchen
     [not found] ` <cover.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
2015-09-09 13:24   ` [PATCH linux-next v6 1/8] mtd: spi-nor: read JEDEC ID with multiple I/O protocols Cyrille Pitchen
     [not found]     ` <735e22efa25f6529bf8ed4a709c18a719176714b.1441803609.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
2015-09-09 13:59       ` Marek Vasut
     [not found]         ` <201509091559.40948.marex-ynQEQJNshbs@public.gmane.org>
2015-09-09 15:55           ` Cyrille Pitchen
2015-09-10 10:25       ` Jonas Gorski
2015-09-09 13:24   ` [PATCH linux-next v6 2/8] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Cyrille Pitchen
2015-09-09 13:24   ` [PATCH linux-next v6 3/8] mtd: spi-nor: update the SPI protocol when enabling manufacturer Quad mode Cyrille Pitchen
2015-09-09 13:24   ` [PATCH linux-next v6 4/8] mtd: spi-nor: use optimized commands for read/write/erase operations Cyrille Pitchen
2015-09-10 10:28     ` Jonas Gorski
2015-09-09 13:24   ` [PATCH linux-next v6 5/8] Documentation: mtd: add a DT property to set the number of dummy cycles Cyrille Pitchen
2015-09-09 13:24   ` [PATCH linux-next v6 6/8] mtd: spi-nor: allow to tune " Cyrille Pitchen
2015-09-09 13:24   ` [PATCH linux-next v6 7/8] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen
2015-09-10 10:36   ` [PATCH linux-next v6 0/8] add driver for Atmel QSPI controller Jonas Gorski
2015-09-09 13:24 ` [PATCH linux-next v6 8/8] mtd: atmel-quadspi: " Cyrille Pitchen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).