* [linux-next RFC v7 0/6] mtd: spi-nor: improve support of QSPI nor
@ 2015-09-15 15:27 Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 2/6] mtd: spi-nor: read JEDEC ID with multiple I/O protocols Cyrille Pitchen
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-15 15:27 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
Hi all,
this series is still a work in progress though it is almost finished.
The support of Macronix dummy code to configure the number of dummy cycles
is still missing...
However I would like some feedbacks to synchronize this work with other
mtd / spi-nor improvements as I see lots of patches on the mailing list.
Best Regards,
Cyrille
Cyrille Pitchen (6):
mtd: spi-nor: remove unused read_xfer/write_xfer hooks
mtd: spi-nor: read JEDEC ID with multiple I/O protocols
mtd: spi-nor: set the read op code and protocol based on the
manufacturer
mtd: m25p80: add support of dual and quad spi protocols to all
commands
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 +
drivers/mtd/devices/m25p80.c | 262 ++++--
drivers/mtd/spi-nor/Kconfig | 7 +
drivers/mtd/spi-nor/Makefile | 1 +
drivers/mtd/spi-nor/atmel-quadspi.c | 880 +++++++++++++++++++++
drivers/mtd/spi-nor/fsl-quadspi.c | 2 +-
drivers/mtd/spi-nor/nxp-spifi.c | 13 +-
drivers/mtd/spi-nor/spi-nor.c | 603 ++++++++++++--
include/linux/mtd/spi-nor.h | 68 +-
9 files changed, 1689 insertions(+), 176 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] 9+ messages in thread
* [linux-next RFC v7 1/6] mtd: spi-nor: remove unused read_xfer/write_xfer hooks
[not found] ` <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
@ 2015-09-15 15:27 ` Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 3/6] mtd: spi-nor: set the read op code and protocol based on the manufacturer Cyrille Pitchen
1 sibling, 0 replies; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-15 15:27 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 e9c912d73141..672595a381c5 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -87,33 +87,6 @@ enum read_mode {
SPI_NOR_QUAD,
};
-/**
- * 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,
@@ -144,14 +117,11 @@ struct mtd_info;
* @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
* @cmd_buf: used by the write_reg
* @prepare: [OPTIONAL] do some preparations for the
* 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
@@ -176,15 +146,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);
--
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] 9+ messages in thread
* [linux-next RFC v7 2/6] mtd: spi-nor: read JEDEC ID with multiple I/O protocols
2015-09-15 15:27 [linux-next RFC v7 0/6] mtd: spi-nor: improve support of QSPI nor Cyrille Pitchen
@ 2015-09-15 15:28 ` Cyrille Pitchen
2015-09-15 17:53 ` Jagan Teki
[not found] ` <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-15 15:28 UTC (permalink / raw)
To: nicolas.ferre, broonie, linux-spi, dwmw2, computersforpeace,
zajec5, beanhuo, juhosg, marex, ben
Cc: linux-kernel, linux-arm-kernel, devicetree, robh+dt, pawel.moll,
mark.rutland, ijc+devicetree, galak, linux-mtd, 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@atmel.com>
---
drivers/mtd/devices/m25p80.c | 8 +-----
drivers/mtd/spi-nor/fsl-quadspi.c | 2 +-
drivers/mtd/spi-nor/nxp-spifi.c | 13 +++------
drivers/mtd/spi-nor/spi-nor.c | 59 ++++++++++++++++++++++++++++++++++-----
include/linux/mtd/spi-nor.h | 27 +++++++++++++++---
5 files changed, 81 insertions(+), 28 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 4b5d7a4655fd..1457866a4930 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -179,7 +179,6 @@ static int m25p_probe(struct spi_device *spi)
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
- enum read_mode mode = SPI_NOR_NORMAL;
char *flash_name = NULL;
int ret;
@@ -205,11 +204,6 @@ static int m25p_probe(struct spi_device *spi)
spi_set_drvdata(spi, flash);
flash->spi = spi;
- if (spi->mode & SPI_RX_QUAD)
- mode = SPI_NOR_QUAD;
- else if (spi->mode & SPI_RX_DUAL)
- mode = SPI_NOR_DUAL;
-
if (data && data->name)
nor->mtd.name = data->name;
@@ -223,7 +217,7 @@ static int m25p_probe(struct spi_device *spi)
else
flash_name = spi->modalias;
- ret = spi_nor_scan(nor, flash_name, mode);
+ ret = spi_nor_scan(nor, flash_name, spi->mode);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 2954f89fc8be..1ded5dbe2240 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -1033,7 +1033,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
/* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor);
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, SPI_RX_QUAD);
if (ret)
goto mutex_failed;
diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
index 9e82098ae644..c499f3258245 100644
--- a/drivers/mtd/spi-nor/nxp-spifi.c
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
@@ -272,7 +272,6 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
struct device_node *np)
{
struct mtd_part_parser_data ppdata;
- enum read_mode flash_read;
u32 ctrl, property;
u16 mode = 0;
int ret;
@@ -304,16 +303,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
SPIFI_CTRL_CSHIGH(15) |
SPIFI_CTRL_FBCLK;
- if (mode & SPI_RX_DUAL) {
+ if (mode & SPI_RX_DUAL)
ctrl |= SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_DUAL;
- } else if (mode & SPI_RX_QUAD) {
+ else if (mode & SPI_RX_QUAD)
ctrl &= ~SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_QUAD;
- } else {
+ else
ctrl |= SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_NORMAL;
- }
switch (mode & (SPI_CPHA | SPI_CPOL)) {
case SPI_MODE_0:
@@ -349,7 +344,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
*/
nxp_spifi_dummy_id_read(&spifi->nor);
- ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
+ ret = spi_nor_scan(&spifi->nor, NULL, mode);
if (ret) {
dev_err(spifi->dev, "device scan failed\n");
return ret;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 8818d4325d20..1908038c8f2e 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -20,6 +20,7 @@
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/of_platform.h>
+#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
@@ -61,6 +62,11 @@ struct flash_info {
#define JEDEC_MFR(info) ((info)->id[0])
+struct read_id_proto {
+ enum spi_protocol proto; /* SPI protocol to read the JEDEC ID */
+ u16 mode; /* SPI controller required caps */
+};
+
static const struct flash_info *spi_nor_match_id(const char *name);
/*
@@ -701,11 +707,15 @@ static const struct flash_info spi_nor_ids[] = {
{ },
};
-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *spi_nor_read_id(struct spi_nor *nor, u16 mode)
{
- int tmp;
+ int i, tmp;
u8 id[SPI_NOR_MAX_ID_LEN];
const struct flash_info *info;
+ static const struct read_id_proto proto[] = {
+ { SPI_PROTO_4_4_4, SPI_RX_QUAD | SPI_TX_QUAD },
+ { SPI_PROTO_2_2_2, SPI_RX_DUAL | SPI_TX_DUAL }
+ };
tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
if (tmp < 0) {
@@ -713,6 +723,35 @@ 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;
+
+ /* Check whether the SPI controller supports this protocol. */
+ if ((mode & proto[i].mode) != proto[i].mode)
+ continue;
+
+ nor->erase_proto = proto[i].proto;
+ nor->read_proto = proto[i].proto;
+ nor->write_proto = proto[i].proto;
+ nor->reg_proto = proto[i].proto;
+
+ /*
+ * Multiple I/O Read ID only returns the Manufacturer ID
+ * (1 byte) and the Device ID (2 bytes). So we reset the
+ * remaining bytes.
+ */
+ memset(id, 0, sizeof(id));
+ tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID, id, 3);
+ 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) {
@@ -999,7 +1038,7 @@ static int spi_nor_check(struct spi_nor *nor)
return 0;
}
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode)
{
const struct flash_info *info = NULL;
struct device *dev = nor->dev;
@@ -1012,11 +1051,17 @@ 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 */
if (!info)
- info = spi_nor_read_id(nor);
+ info = spi_nor_read_id(nor, mode);
if (IS_ERR_OR_NULL(info))
return -ENOENT;
@@ -1027,7 +1072,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (name && info->id_len) {
const struct flash_info *jinfo;
- jinfo = spi_nor_read_id(nor);
+ jinfo = spi_nor_read_id(nor, mode);
if (IS_ERR(jinfo)) {
return PTR_ERR(jinfo);
} else if (jinfo != info) {
@@ -1126,14 +1171,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
nor->flash_read = SPI_NOR_NORMAL;
/* Quad/Dual-read mode takes precedence over fast/normal */
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
+ if (mode & SPI_RX_QUAD && info->flags & SPI_NOR_QUAD_READ) {
ret = set_quad_mode(nor, info);
if (ret) {
dev_err(dev, "quad mode not supported\n");
return ret;
}
nor->flash_read = SPI_NOR_QUAD;
- } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
+ } else if (mode & SPI_RX_DUAL && info->flags & SPI_NOR_DUAL_READ) {
nor->flash_read = SPI_NOR_DUAL;
}
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 672595a381c5..32339e82cb9d 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 */
+};
+
#define SPI_NOR_MAX_CMD_SIZE 8
enum spi_nor_ops {
SPI_NOR_OPS_READ = 0,
@@ -117,6 +128,10 @@ struct mtd_info;
* @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_*)
+ * @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
@@ -143,6 +158,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;
@@ -169,7 +188,7 @@ struct spi_nor {
* spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure
* @name: the chip type name
- * @mode: the read mode supported by the driver
+ * @mode: the bitmask or TX/RX modes supported by the driver
*
* The drivers can use this fuction to scan the SPI NOR.
* In the scanning, it will try to get all the necessary information to
@@ -179,6 +198,6 @@ struct spi_nor {
*
* Return: 0 for success, others for failure.
*/
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
+int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode);
#endif
--
1.8.2.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [linux-next RFC v7 3/6] mtd: spi-nor: set the read op code and protocol based on the manufacturer
[not found] ` <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
2015-09-15 15:27 ` [linux-next RFC v7 1/6] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Cyrille Pitchen
@ 2015-09-15 15:28 ` Cyrille Pitchen
1 sibling, 0 replies; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-15 15:28 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.
If the QPI mode is disabled, the Fast Read Dual Output (0x3b) command uses
the SPI 1-1-2 protocol whereas other commands use the SPI 1-1-1 protocol.
Spansion:
When Quad I/O operations are enabled, the Fast Read Quad Output (0x6b / 0x6c)
commands use the SPI 1-1-4 protocol.
Also when using the Fast Read Dual Output (0x3b / 0x3c) commands, the
SPI 1-1-2 protocol must be used. 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 | 544 ++++++++++++++++++++++++++++++++++++------
include/linux/mtd/spi-nor.h | 10 +-
2 files changed, 481 insertions(+), 73 deletions(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 1908038c8f2e..7fbcccb3d02e 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -900,27 +900,110 @@ write_err:
return ret;
}
-static int macronix_quad_enable(struct spi_nor *nor)
+static int macronix_set_qpi_mode(struct spi_nor *nor, bool enable)
{
- int ret, val;
+ int sr, mask, qpi_bit;
+
+ mask = SR_QUAD_EN_MX;
+ qpi_bit = (enable) ? SR_QUAD_EN_MX : 0;
+
+ sr = read_sr(nor);
+ if ((sr & mask) == qpi_bit)
+ return 0;
- val = read_sr(nor);
write_enable(nor);
+ write_sr(nor, (sr & ~mask) | qpi_bit);
- write_sr(nor, val | SR_QUAD_EN_MX);
+ /* Set the reg protocol now before accessing any other register. */
+ nor->reg_proto = (enable) ? SPI_PROTO_4_4_4 : SPI_PROTO_1_1_1;
- if (spi_nor_wait_till_ready(nor))
+ if (spi_nor_wait_till_ready(nor)) {
+ dev_err(nor->dev, "Failed to %s the QPI mode.\n",
+ enable ? "enable" : "disable");
return 1;
+ }
- ret = read_sr(nor);
- if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
- dev_err(nor->dev, "Macronix Quad bit not set\n");
+ sr = read_sr(nor);
+ if (!(sr > 0 && ((sr & mask) != qpi_bit))) {
+ dev_err(nor->dev, "Macronix Quad bit was not %s.\n",
+ enable ? "set" : "cleared");
return -EINVAL;
}
return 0;
}
+static int macronix_set_quad_io(struct spi_nor *nor)
+{
+ /* Enable the QPI mode if not done yet. */
+ if (macronix_set_qpi_mode(nor, true))
+ return -EINVAL;
+
+ /* Use SPI 4-4-4 protocol for all commands. */
+ nor->read_proto = SPI_PROTO_4_4_4;
+ nor->write_proto = SPI_PROTO_4_4_4;
+ nor->erase_proto = SPI_PROTO_4_4_4;
+
+ /*
+ * The Fast Read Quad Output 1-1-4 command (0x6b) command is not
+ * supported in QPI mode, use the Fast Read Quad I/O 1-4-4 (0xeb)
+ * instead.
+ */
+ nor->read_opcode = SPINOR_OP_READ_1_4_4;
+
+ return 0;
+}
+
+static int macronix_set_quad_output(struct spi_nor *nor)
+{
+ /* Disable the QPI mode if not done yet. */
+ if (macronix_set_qpi_mode(nor, false))
+ return -EINVAL;
+
+ /* Use the Fast Read Quad Output 1-1-4 command. */
+ nor->read_proto = SPI_PROTO_1_1_4;
+ nor->read_opcode = SPINOR_OP_READ_1_1_4;
+
+ return 0;
+}
+
+static int macronix_set_dual_io(struct spi_nor *nor)
+{
+ /* Disable the QPI mode if not done yet. */
+ if (macronix_set_qpi_mode(nor, false))
+ return -EINVAL;
+
+ /* Use the Fast Read Dual I/O 1-2-2 command. */
+ nor->read_proto = SPI_PROTO_1_2_2;
+ nor->read_opcode = SPINOR_OP_READ_1_2_2;
+
+ return 0;
+}
+
+static int macronix_set_dual_output(struct spi_nor *nor)
+{
+ /* Disable the QPI mode if not done yet. */
+ if (macronix_set_qpi_mode(nor, false))
+ return -EINVAL;
+
+ /* Use the Fast Read Dual Output 1-1-2 command. */
+ nor->read_proto = SPI_PROTO_1_1_2;
+ nor->read_opcode = SPINOR_OP_READ_1_1_2;
+
+ return 0;
+}
+
+static int macronix_set_single(struct spi_nor *nor)
+{
+ /* Disable the QPI mode if not done yet. */
+ if (macronix_set_qpi_mode(nor, false))
+ return -EINVAL;
+
+ nor->read_proto = SPI_PROTO_1_1_1;
+
+ return 0;
+}
+
/*
* Write status Register and configuration register with 2 bytes
* The first byte will be written to the status register, while the
@@ -935,96 +1018,416 @@ static int write_sr_cr(struct spi_nor *nor, u16 val)
return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2);
}
-static int spansion_quad_enable(struct spi_nor *nor)
+static int spansion_write_cr(struct spi_nor *nor, bool quad_mode, u8 lc)
{
- int ret;
- int quad_en = CR_QUAD_EN_SPAN << 8;
+ int cr, mask, value;
- write_enable(nor);
+ mask = CR_QUAD_EN_SPAN | CR_LC_MASK;
+ value = (lc << 6) & CR_LC_MASK;
+ if (quad_mode)
+ value |= CR_QUAD_EN_SPAN;
- ret = write_sr_cr(nor, quad_en);
- if (ret < 0) {
+ /* Read the control register */
+ cr = read_cr(nor);
+ if (cr < 0) {
+ dev_err(nor->dev,
+ "error while reading configuration register\n");
+ return -EINVAL;
+ }
+
+ /* Check whether the Quad bit and the Latency Code need to be updated */
+ if ((cr & mask) == value)
+ return 0;
+
+ /* Update the configuration register. */
+ cr = (cr & ~mask) | value;
+ write_enable(nor);
+ if (write_sr_cr(nor, cr << 8) < 0) {
dev_err(nor->dev,
"error while writing configuration register\n");
return -EINVAL;
}
/* read back and check it */
- ret = read_cr(nor);
- if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
- dev_err(nor->dev, "Spansion Quad bit not set\n");
+ cr = read_cr(nor);
+ if (!(cr > 0 && ((cr & mask) == value))) {
+ dev_err(nor->dev,
+ "error while updating configuration register\n");
return -EINVAL;
}
return 0;
}
-static int micron_quad_enable(struct spi_nor *nor)
+static int spansion_set_quad_output(struct spi_nor *nor)
+{
+ int ret;
+
+ /*
+ * Set the Latency Code to 0 so the memory expects 8 dummy cycles when
+ * processing a Fast Read Quad Output 1-1-4 command.
+ */
+ ret = spansion_write_cr(nor, true, 0);
+ if (ret)
+ return ret;
+
+ /* Use the Fast Read Quad Output 1-1-4 command. */
+ nor->read_proto = SPI_PROTO_1_1_4;
+ nor->read_opcode = SPINOR_OP_READ_1_1_4;
+
+ return 0;
+}
+
+static int spansion_set_dual_output(struct spi_nor *nor)
+{
+ int ret;
+
+ /*
+ * Set the Latency Code to 0 so the memory expects 8 dummy cycles when
+ * processing a Fast Read Dual Output 1-1-2 command.
+ */
+ ret = spansion_write_cr(nor, false, 0);
+ if (ret)
+ return ret;
+
+ /* Use the Fast Read Dual Output 1-1-2 command. */
+ nor->read_proto = SPI_PROTO_1_1_2;
+ nor->read_opcode = SPINOR_OP_READ_1_1_2;
+
+ return 0;
+}
+
+static int spansion_set_single(struct spi_nor *nor)
{
int ret;
- u8 val;
- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
+ /*
+ * Set the Latency Code to 0 so the memory expects 8 dummy cycles when
+ * processing a Fast Read 1-1-1 command. The Latency Code is not
+ * relevant for Read command since no dummy cycle is expected.
+ */
+ ret = spansion_write_cr(nor, false, 0);
+ if (ret)
+ return ret;
+
+ nor->read_proto = SPI_PROTO_1_1_1;
+
+ return 0;
+}
+
+static int micron_set_dummy_cycles(struct spi_nor *nor, u8 num_dummy_cycles)
+{
+ u8 vcr, val, mask;
+ int ret;
+
+ mask = GENMASK(7, 4);
+ val = (num_dummy_cycles << 4) & mask;
+
+ /* Read the Volatile Configuration Register (VCR). */
+ ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &vcr, 1);
if (ret < 0) {
- dev_err(nor->dev, "error %d reading EVCR\n", ret);
+ dev_err(nor->dev, "error while reading VCR register\n");
return ret;
}
+ /* Check whether we need to update the number of dummy cycles. */
+ if ((vcr & mask) == val)
+ return 0;
+
+ /* Update the number of dummy into the VCR. */
write_enable(nor);
+ vcr = (vcr & ~mask) | val;
+ ret = nor->write_reg(nor, SPINOR_OP_WR_VCR, &vcr, 1);
+ 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;
- /* set EVCR, enable quad I/O */
- nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
- ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1);
+ /* Read VCR and check it. */
+ ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &vcr, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while reading VCR\n");
+ return ret;
+ }
+ if ((vcr & mask) != val) {
+ dev_err(nor->dev, "Micron VCR dummy cycles not updated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int micron_set_protocol(struct spi_nor *nor, u8 val,
+ enum spi_protocol proto)
+{
+ u8 evcr, mask;
+ int ret;
+
+ mask = EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON;
+
+ /* Read the Exhanced Volatile Configuration Register (EVCR). */
+ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while reading EVCR register\n");
+ return ret;
+ }
+
+ /* Check whether we need to update the protocol bits. */
+ if ((evcr & mask) == val)
+ return 0;
+
+ /* Set EVCR protocol bits. */
+ write_enable(nor);
+ evcr = (evcr & ~mask) | val;
+ ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, &evcr, 1);
if (ret < 0) {
dev_err(nor->dev, "error while writing EVCR register\n");
return ret;
}
+ /* Switch reg protocol now before accessing any other registers. */
+ nor->reg_proto = proto;
+
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
- /* read EVCR and check it */
- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
+ /* Read EVCR and check it. */
+ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
if (ret < 0) {
- dev_err(nor->dev, "error %d reading EVCR\n", ret);
+ dev_err(nor->dev, "error while reading EVCR\n");
return ret;
}
- if (val & EVCR_QUAD_EN_MICRON) {
- dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
+ if ((evcr & mask) != val) {
+ dev_err(nor->dev, "Micron EVCR protocol bits not updated\n");
return -EINVAL;
}
return 0;
}
-static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
+static inline int micron_set_extended_spi_protocol(struct spi_nor *nor)
{
- int status;
+ int ret;
+ /* Set both the Quad and Dual bits to select the Extended SPI mode */
+ ret = micron_set_protocol(nor,
+ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+ SPI_PROTO_1_1_1);
+ if (ret) {
+ dev_err(nor->dev, "Failed to set Micron Extended SPI mode\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int micron_set_quad_io(struct spi_nor *nor)
+{
+ int ret;
+
+ /* Clear at least the Quad bit to enable quad mode */
+ ret = micron_set_protocol(nor,
+ EVCR_DUAL_EN_MICRON,
+ SPI_PROTO_4_4_4);
+ if (ret) {
+ dev_err(nor->dev, "Failed to set Micron Quad mode\n");
+ return ret;
+ }
+
+ /* Force the number of dummy cycles to 8 */
+ ret = micron_set_dummy_cycles(nor, 8);
+ if (ret)
+ return ret;
+
+ /* Use SPI 4-4-4 protocol for all commands. */
+ nor->read_proto = SPI_PROTO_4_4_4;
+ nor->write_proto = SPI_PROTO_4_4_4;
+ nor->erase_proto = SPI_PROTO_4_4_4;
+
+ /*
+ * The Fast Read Quad Output 1-1-4 command (0x6b) is processed with
+ * SPI 4-4-4 protocol.
+ */
+ nor->read_opcode = SPINOR_OP_READ_1_1_4;
+
+ return 0;
+}
+
+static int micron_set_quad_output(struct spi_nor *nor)
+{
+ int ret;
+
+ ret = micron_set_extended_spi_protocol(nor);
+ if (ret)
+ return ret;
+
+ /* Force the number of dummy cycles to 8 */
+ ret = micron_set_dummy_cycles(nor, 8);
+ if (ret)
+ return ret;
+
+ /* Use the Fast Read Quad Output 1-1-4 command. */
+ nor->read_proto = SPI_PROTO_1_1_4;
+ nor->read_opcode = SPINOR_OP_READ_1_1_4;
+
+ return 0;
+}
+
+static int micron_set_dual_io(struct spi_nor *nor)
+{
+ int ret;
+
+ /* Clear Dual bit but keep Quad bit set to enable dual mode */
+ ret = micron_set_protocol(nor,
+ EVCR_QUAD_EN_MICRON,
+ SPI_PROTO_2_2_2);
+ if (ret) {
+ dev_err(nor->dev, "Failed to set Micron Dual mode\n");
+ return ret;
+ }
+
+ /* Force the number of dummy cycles to 8 */
+ ret = micron_set_dummy_cycles(nor, 8);
+ if (ret)
+ return ret;
+
+ /* Use SPI 2-2-2 protocol for all commands. */
+ nor->read_proto = SPI_PROTO_2_2_2;
+ nor->write_proto = SPI_PROTO_2_2_2;
+ nor->erase_proto = SPI_PROTO_2_2_2;
+
+ /*
+ * The Fast Read Dual Output 1-1-2 command (0x3b) is processed with
+ * SPI 2-2-2 protocol.
+ */
+ nor->read_opcode = SPINOR_OP_READ_1_1_2;
+
+ return 0;
+}
+
+static int micron_set_dual_output(struct spi_nor *nor)
+{
+ int ret;
+
+ ret = micron_set_extended_spi_protocol(nor);
+ if (ret)
+ return ret;
+
+ /* Force the number of dummy cycles to 8 */
+ ret = micron_set_dummy_cycles(nor, 8);
+ if (ret)
+ return ret;
+
+ /* Use the Fast Read Dual Output 1-1-4 command. */
+ nor->read_proto = SPI_PROTO_1_1_2;
+ nor->read_opcode = SPINOR_OP_READ_1_1_2;
+
+ return 0;
+}
+
+static int micron_set_single(struct spi_nor *nor)
+{
+ int ret;
+
+ ret = micron_set_extended_spi_protocol(nor);
+ if (ret)
+ return ret;
+
+ /* Force the number of dummy cycles to 8 (Fast Read only) */
+ ret = micron_set_dummy_cycles(nor, 8);
+ if (ret)
+ return ret;
+
+ nor->read_proto = SPI_PROTO_1_1_1;
+
+ return 0;
+}
+
+static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info,
+ u16 mode)
+{
switch (JEDEC_MFR(info)) {
case CFI_MFR_MACRONIX:
- status = macronix_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Macronix quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
+ if (mode & SPI_TX_QUAD)
+ return macronix_set_quad_io(nor);
+ return macronix_set_quad_output(nor);
+
case CFI_MFR_ST:
- status = micron_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Micron quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
+ if (mode & SPI_TX_QUAD)
+ return micron_set_quad_io(nor);
+ return micron_set_quad_output(nor);
+
+ case CFI_MFR_AMD:
+ /*
+ * Don't use the Fast Read Quad I/O (0xeb / 0xec) commands as
+ * their number of dummy cycles is not a multiple of 8. Some
+ * SPI controllers, especially those relying on the m25p80
+ * driver, expect the number of dummy cycles to be a multiple
+ * of 8.
+ */
+ return spansion_set_quad_output(nor);
+
default:
- status = spansion_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Spansion quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info,
+ u16 mode)
+{
+ switch (JEDEC_MFR(info)) {
+ case CFI_MFR_MACRONIX:
+ if (mode & SPI_TX_DUAL)
+ return macronix_set_dual_io(nor);
+ return macronix_set_dual_output(nor);
+
+ case CFI_MFR_ST:
+ if (mode & SPI_TX_DUAL)
+ return micron_set_dual_io(nor);
+ return micron_set_dual_output(nor);
+
+ case CFI_MFR_AMD:
+ /*
+ * Don't use the Fast Read Dual I/O (0xbb / 0xbc) commands as
+ * their number of dummy cycles is not a multiple of 8. Some
+ * SPI controllers, especially those relying on the m25p80
+ * driver, expect the number of dummy cycles to be a multiple
+ * of 8.
+ */
+ return spansion_set_dual_output(nor);
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int set_single_mode(struct spi_nor *nor, const struct flash_info *info,
+ u16 mode)
+{
+ switch (JEDEC_MFR(info)) {
+ case CFI_MFR_MACRONIX:
+ return macronix_set_single(nor);
+
+ case CFI_MFR_ST:
+ return micron_set_single(nor);
+
+ case CFI_MFR_AMD:
+ return spansion_set_single(nor);
+
+ default:
+ break;
}
+
+ return -EINVAL;
}
static int spi_nor_check(struct spi_nor *nor)
@@ -1170,39 +1573,38 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode)
if (info->flags & SPI_NOR_NO_FR)
nor->flash_read = SPI_NOR_NORMAL;
+ /* Default commands */
+ nor->program_opcode = SPINOR_OP_PP;
+ if (nor->flash_read == SPI_NOR_NORMAL)
+ nor->read_opcode = SPINOR_OP_READ;
+ else
+ nor->read_opcode = SPINOR_OP_READ_FAST;
+
/* Quad/Dual-read mode takes precedence over fast/normal */
if (mode & SPI_RX_QUAD && info->flags & SPI_NOR_QUAD_READ) {
- ret = set_quad_mode(nor, info);
+ /* At least SPI 1-1-4 should be supported */
+ ret = set_quad_mode(nor, info, mode);
if (ret) {
dev_err(dev, "quad mode not supported\n");
return ret;
}
nor->flash_read = SPI_NOR_QUAD;
} else if (mode & SPI_RX_DUAL && info->flags & SPI_NOR_DUAL_READ) {
+ /* At lest SPI 1-1-2 should be supported */
+ ret = set_dual_mode(nor, info, mode);
+ if (ret) {
+ dev_err(dev, "dual mode not supported\n");
+ return ret;
+ }
nor->flash_read = SPI_NOR_DUAL;
+ } else if (info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) {
+ ret = set_single_mode(nor, info, mode);
+ if (ret) {
+ dev_err(dev, "failed to switch back to single mode\n");
+ return ret;
+ }
}
- /* Default commands */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ;
- break;
- default:
- dev_err(dev, "No Read opcode defined\n");
- return -EINVAL;
- }
-
- nor->program_opcode = SPINOR_OP_PP;
-
if (info->addr_width)
nor->addr_width = info->addr_width;
else if (mtd->size > 0x1000000) {
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 32339e82cb9d..1cdc905a3077 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -24,8 +24,10 @@
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
#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_1_4 0x6b /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Out SPI) */
+#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
@@ -58,6 +60,8 @@
/* Used for Micron flashes only. */
#define SPINOR_OP_MIO_RDID 0xaf /* Multiple I/O Read JEDEC ID */
+#define SPINOR_OP_RD_VCR 0x85 /* Read VCR register */
+#define SPINOR_OP_WR_VCR 0x81 /* Write VCR register */
#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
@@ -74,12 +78,14 @@
/* 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
/* Configuration Register bits. */
#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
+#define CR_LC_MASK 0xc0 /* Spansion Latency Code */
enum read_mode {
SPI_NOR_NORMAL = 0,
--
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] 9+ messages in thread
* [linux-next RFC v7 4/6] mtd: m25p80: add support of dual and quad spi protocols to all commands
2015-09-15 15:27 [linux-next RFC v7 0/6] mtd: spi-nor: improve support of QSPI nor Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 2/6] mtd: spi-nor: read JEDEC ID with multiple I/O protocols Cyrille Pitchen
[not found] ` <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
@ 2015-09-15 15:28 ` Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 5/6] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 6/6] mtd: atmel-quadspi: add driver for Atmel QSPI controller Cyrille Pitchen
4 siblings, 0 replies; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-15 15:28 UTC (permalink / raw)
To: nicolas.ferre, broonie, linux-spi, dwmw2, computersforpeace,
zajec5, beanhuo, juhosg, marex, ben
Cc: linux-kernel, linux-arm-kernel, devicetree, robh+dt, pawel.moll,
mark.rutland, ijc+devicetree, galak, linux-mtd, Cyrille Pitchen
Before this patch, m25p80_read() supported few SPI protocols:
- regular SPI 1-1-1
- SPI Dual Output 1-1-2
- SPI Quad Output 1-1-4
On the other hand, all other m25p80_*() hooks only supported SPI 1-1-1.
However once their Quad mode enabled, Micron and Macronix spi-nor memories
expect all commands to use the SPI 4-4-4 protocol.
Also, once their Dual mode enabled, Micron spi-nor memories expect all
commands to use the SPI-2-2-2 protocol.
So this patch adds support to all currently existing SPI protocols to
cover as many protocols as possible.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/devices/m25p80.c | 254 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 212 insertions(+), 42 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 1457866a4930..96072b33b29f 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -27,22 +27,110 @@
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
-#define MAX_CMD_SIZE 6
+#define MAX_CMD_SIZE 8
struct m25p {
struct spi_device *spi;
struct spi_nor spi_nor;
u8 command[MAX_CMD_SIZE];
};
+static inline int m25p80_proto2nbits(enum spi_protocol proto,
+ unsigned *code_nbits,
+ unsigned *addr_nbits,
+ unsigned *data_nbits)
+{
+ unsigned code, addr, data;
+
+ switch (proto) {
+ case SPI_PROTO_1_1_1:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_SINGLE;
+ data = SPI_NBITS_SINGLE;
+ break;
+
+ case SPI_PROTO_1_1_2:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_SINGLE;
+ data = SPI_NBITS_DUAL;
+ break;
+
+ case SPI_PROTO_1_1_4:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_SINGLE;
+ data = SPI_NBITS_QUAD;
+ break;
+
+ case SPI_PROTO_1_2_2:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_DUAL;
+ data = SPI_NBITS_DUAL;
+ break;
+
+ case SPI_PROTO_1_4_4:
+ code = SPI_NBITS_SINGLE;
+ addr = SPI_NBITS_QUAD;
+ data = SPI_NBITS_QUAD;
+ break;
+
+ case SPI_PROTO_2_2_2:
+ code = SPI_NBITS_DUAL;
+ addr = SPI_NBITS_DUAL;
+ data = SPI_NBITS_DUAL;
+ break;
+
+ case SPI_PROTO_4_4_4:
+ code = SPI_NBITS_QUAD;
+ addr = SPI_NBITS_QUAD;
+ data = SPI_NBITS_QUAD;
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+
+ if (code_nbits)
+ *code_nbits = code;
+ if (addr_nbits)
+ *addr_nbits = addr;
+ if (data_nbits)
+ *data_nbits = data;
+
+ return 0;
+}
+
static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
+ unsigned code_nbits, data_nbits;
+ struct spi_transfer xfers[2];
int ret;
- ret = spi_write_then_read(spi, &code, 1, val, len);
+ /* Get transfer protocols (addr_nbits is not relevant here). */
+ ret = m25p80_proto2nbits(nor->reg_proto,
+ &code_nbits, NULL, &data_nbits);
+ if (ret < 0)
+ return ret;
+
+ /* Set up transfers. */
+ memset(xfers, 0, sizeof(xfers));
+
+ flash->command[0] = code;
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
+
+ xfers[1].len = len;
+ xfers[1].rx_buf = &flash->command[1];
+ xfers[1].rx_nbits = data_nbits;
+
+ /* Process command. */
+ ret = spi_sync_transfer(spi, xfers, 2);
if (ret < 0)
dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+ else
+ memcpy(val, &flash->command[1], len);
return ret;
}
@@ -65,12 +153,38 @@ static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
+ unsigned code_nbits, data_nbits, num_xfers = 1;
+ struct spi_transfer xfers[2];
+ int ret;
+
+ /* Get transfer protocols (addr_nbits is not relevant here). */
+ ret = m25p80_proto2nbits(nor->reg_proto,
+ &code_nbits, NULL, &data_nbits);
+ if (ret < 0)
+ return ret;
+
+ /* Set up transfer(s). */
+ memset(xfers, 0, sizeof(xfers));
flash->command[0] = opcode;
- if (buf)
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
+
+ if (buf) {
memcpy(&flash->command[1], buf, len);
+ if (data_nbits == code_nbits) {
+ xfers[0].len += len;
+ } else {
+ xfers[1].len = len;
+ xfers[1].tx_buf = &flash->command[1];
+ xfers[1].tx_nbits = data_nbits;
+ num_xfers++;
+ }
+ }
- return spi_write(spi, flash->command, len + 1);
+ /* Process command. */
+ return spi_sync_transfer(spi, xfers, num_xfers);
}
static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -78,43 +192,54 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
- struct spi_transfer t[2] = {};
+ unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1;
+ struct spi_transfer xfers[3];
struct spi_message m;
- int cmd_sz = m25p_cmdsz(nor);
-
- spi_message_init(&m);
+ int ret, cmd_sz = m25p_cmdsz(nor);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
cmd_sz = 1;
- flash->command[0] = nor->program_opcode;
- m25p_addr2cmd(nor, to, flash->command);
+ /* Get transfer protocols. */
+ ret = m25p80_proto2nbits(nor->write_proto,
+ &code_nbits, &addr_nbits, &data_nbits);
+ if (ret < 0) {
+ *retlen = 0;
+ return;
+ }
+
+ /* Set up transfers. */
+ memset(xfers, 0, sizeof(xfers));
- t[0].tx_buf = flash->command;
- t[0].len = cmd_sz;
- spi_message_add_tail(&t[0], &m);
+ flash->command[0] = nor->program_opcode;
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
+
+ if (cmd_sz > 1) {
+ m25p_addr2cmd(nor, to, flash->command);
+ if (addr_nbits == code_nbits) {
+ xfers[0].len += nor->addr_width;
+ } else {
+ xfers[1].len = nor->addr_width;
+ xfers[1].tx_buf = &flash->command[1];
+ xfers[1].tx_nbits = addr_nbits;
+ num_xfers++;
+ }
+ }
- t[1].tx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ xfers[num_xfers].len = len;
+ xfers[num_xfers].tx_buf = buf;
+ xfers[num_xfers].tx_nbits = data_nbits;
+ num_xfers++;
+ /* Process command. */
+ spi_message_init_with_transfers(&m, xfers, num_xfers);
spi_sync(spi, &m);
*retlen += m.actual_length - cmd_sz;
}
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_DUAL:
- return 2;
- case SPI_NOR_QUAD:
- return 4;
- default:
- return 0;
- }
-}
-
/*
* Read an address range from the nor chip. The address range
* may be any size provided it is within the physical boundaries.
@@ -124,28 +249,48 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
- struct spi_transfer t[2];
- struct spi_message m;
+ unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1;
unsigned int dummy = nor->read_dummy;
+ struct spi_transfer xfers[3];
+ struct spi_message m;
+ int ret;
+
+ /* Get transfer protocols. */
+ ret = m25p80_proto2nbits(nor->read_proto,
+ &code_nbits, &addr_nbits, &data_nbits);
+ if (ret < 0) {
+ *retlen = 0;
+ return ret;
+ }
/* convert the dummy cycles to the number of bytes */
dummy /= 8;
- spi_message_init(&m);
- memset(t, 0, (sizeof t));
+ /* Set up transfers. */
+ memset(xfers, 0, sizeof(xfers));
flash->command[0] = nor->read_opcode;
- m25p_addr2cmd(nor, from, flash->command);
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
- t[0].tx_buf = flash->command;
- t[0].len = m25p_cmdsz(nor) + dummy;
- spi_message_add_tail(&t[0], &m);
+ m25p_addr2cmd(nor, from, flash->command);
+ if (addr_nbits == code_nbits) {
+ xfers[0].len += nor->addr_width + dummy;
+ } else {
+ xfers[1].len = nor->addr_width + dummy;
+ xfers[1].tx_buf = &flash->command[1];
+ xfers[1].tx_nbits = addr_nbits;
+ num_xfers++;
+ }
- t[1].rx_buf = buf;
- t[1].rx_nbits = m25p80_rx_nbits(nor);
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ xfers[num_xfers].len = len;
+ xfers[num_xfers].rx_buf = buf;
+ xfers[num_xfers].rx_nbits = data_nbits;
+ num_xfers++;
+ /* Process command. */
+ spi_message_init_with_transfers(&m, xfers, num_xfers);
spi_sync(spi, &m);
*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
@@ -155,15 +300,40 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
static int m25p80_erase(struct spi_nor *nor, loff_t offset)
{
struct m25p *flash = nor->priv;
+ struct spi_device *spi = flash->spi;
+ unsigned code_nbits, addr_nbits, num_xfers = 1;
+ struct spi_transfer xfers[2];
+ int ret;
dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
flash->spi_nor.mtd.erasesize / 1024, (u32)offset);
- /* Set up command buffer. */
+ /* Get transfer protocols (data_nbits is not relevant here). */
+ ret = m25p80_proto2nbits(nor->erase_proto,
+ &code_nbits, &addr_nbits, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Set up transfers. */
+ memset(xfers, 0, sizeof(xfers));
+
flash->command[0] = nor->erase_opcode;
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
+
m25p_addr2cmd(nor, offset, flash->command);
+ if (code_nbits == addr_nbits) {
+ xfers[0].len += nor->addr_width;
+ } else {
+ xfers[1].len = nor->addr_width;
+ xfers[1].tx_buf = &flash->command[1];
+ xfers[1].tx_nbits = addr_nbits;
+ num_xfers++;
+ }
- spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
+ /* Process command. */
+ spi_sync_transfer(spi, xfers, num_xfers);
return 0;
}
--
1.8.2.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [linux-next RFC v7 5/6] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver
2015-09-15 15:27 [linux-next RFC v7 0/6] mtd: spi-nor: improve support of QSPI nor Cyrille Pitchen
` (2 preceding siblings ...)
2015-09-15 15:28 ` [linux-next RFC v7 4/6] mtd: m25p80: add support of dual and quad spi protocols to all commands Cyrille Pitchen
@ 2015-09-15 15:28 ` Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 6/6] mtd: atmel-quadspi: add driver for Atmel QSPI controller Cyrille Pitchen
4 siblings, 0 replies; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-15 15:28 UTC (permalink / raw)
To: nicolas.ferre, broonie, linux-spi, dwmw2, computersforpeace,
zajec5, beanhuo, juhosg, marex, ben
Cc: linux-kernel, linux-arm-kernel, devicetree, robh+dt, pawel.moll,
mark.rutland, ijc+devicetree, galak, linux-mtd, 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@atmel.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Marek Vasut <marex@denx.de>
Acked-by: Rob Herring <robh@kernel.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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [linux-next RFC v7 6/6] mtd: atmel-quadspi: add driver for Atmel QSPI controller
2015-09-15 15:27 [linux-next RFC v7 0/6] mtd: spi-nor: improve support of QSPI nor Cyrille Pitchen
` (3 preceding siblings ...)
2015-09-15 15:28 ` [linux-next RFC v7 5/6] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen
@ 2015-09-15 15:28 ` Cyrille Pitchen
4 siblings, 0 replies; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-15 15:28 UTC (permalink / raw)
To: nicolas.ferre, broonie, linux-spi, dwmw2, computersforpeace,
zajec5, beanhuo, juhosg, marex, ben
Cc: linux-kernel, linux-arm-kernel, devicetree, robh+dt, pawel.moll,
mark.rutland, ijc+devicetree, galak, linux-mtd, Cyrille Pitchen
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 | 880 ++++++++++++++++++++++++++++++++++++
3 files changed, 888 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..e938152c2c89
--- /dev/null
+++ b/drivers/mtd/spi-nor/atmel-quadspi.c
@@ -0,0 +1,880 @@
+/*
+ * 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 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",
+ nor->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;
+ 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 = &nor->mtd;
+
+ nor->dev = &pdev->dev;
+ nor->flash_node = child;
+ 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;
+
+ 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;
+
+ err = spi_nor_scan(nor, NULL, SPI_RX_QUAD);
+ 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->nor.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] 9+ messages in thread
* Re: [linux-next RFC v7 2/6] mtd: spi-nor: read JEDEC ID with multiple I/O protocols
2015-09-15 15:28 ` [linux-next RFC v7 2/6] mtd: spi-nor: read JEDEC ID with multiple I/O protocols Cyrille Pitchen
@ 2015-09-15 17:53 ` Jagan Teki
[not found] ` <CAD6G_RQV--nRk-pf347ThnEbQ=+tozXq8PKZL818cEK7nAi+gA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
0 siblings, 1 reply; 9+ messages in thread
From: Jagan Teki @ 2015-09-15 17:53 UTC (permalink / raw)
To: Cyrille Pitchen
Cc: nicolas.ferre, Mark Brown, linux-spi, David Woodhouse,
Brian Norris, zajec5, beanhuo, juhosg, Marek Vašut, ben,
Mark Rutland, devicetree, Pawel Moll, Ian Campbell,
linux-kernel@vger.kernel.org, Rob Herring,
linux-mtd@lists.infradead.org, Kumar Gala,
linux-arm-kernel@lists.infradead.org
On 15 September 2015 at 20:58, Cyrille Pitchen
<cyrille.pitchen@atmel.com> 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@atmel.com>
> ---
> drivers/mtd/devices/m25p80.c | 8 +-----
> drivers/mtd/spi-nor/fsl-quadspi.c | 2 +-
> drivers/mtd/spi-nor/nxp-spifi.c | 13 +++------
> drivers/mtd/spi-nor/spi-nor.c | 59 ++++++++++++++++++++++++++++++++++-----
> include/linux/mtd/spi-nor.h | 27 +++++++++++++++---
> 5 files changed, 81 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 4b5d7a4655fd..1457866a4930 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -179,7 +179,6 @@ static int m25p_probe(struct spi_device *spi)
> struct flash_platform_data *data;
> struct m25p *flash;
> struct spi_nor *nor;
> - enum read_mode mode = SPI_NOR_NORMAL;
> char *flash_name = NULL;
> int ret;
>
> @@ -205,11 +204,6 @@ static int m25p_probe(struct spi_device *spi)
> spi_set_drvdata(spi, flash);
> flash->spi = spi;
>
> - if (spi->mode & SPI_RX_QUAD)
> - mode = SPI_NOR_QUAD;
> - else if (spi->mode & SPI_RX_DUAL)
> - mode = SPI_NOR_DUAL;
> -
> if (data && data->name)
> nor->mtd.name = data->name;
>
> @@ -223,7 +217,7 @@ static int m25p_probe(struct spi_device *spi)
> else
> flash_name = spi->modalias;
>
> - ret = spi_nor_scan(nor, flash_name, mode);
> + ret = spi_nor_scan(nor, flash_name, spi->mode);
IMHO, this is certainly incorrect because spi-nor never know anything
about spi (Linux) that is why this framework got into picture.
> if (ret)
> return ret;
>
> diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
> index 2954f89fc8be..1ded5dbe2240 100644
> --- a/drivers/mtd/spi-nor/fsl-quadspi.c
> +++ b/drivers/mtd/spi-nor/fsl-quadspi.c
> @@ -1033,7 +1033,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
> /* set the chip address for READID */
> fsl_qspi_set_base_addr(q, nor);
>
> - ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
> + ret = spi_nor_scan(nor, NULL, SPI_RX_QUAD);
> if (ret)
> goto mutex_failed;
>
> diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
> index 9e82098ae644..c499f3258245 100644
> --- a/drivers/mtd/spi-nor/nxp-spifi.c
> +++ b/drivers/mtd/spi-nor/nxp-spifi.c
> @@ -272,7 +272,6 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
> struct device_node *np)
> {
> struct mtd_part_parser_data ppdata;
> - enum read_mode flash_read;
> u32 ctrl, property;
> u16 mode = 0;
> int ret;
> @@ -304,16 +303,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
> SPIFI_CTRL_CSHIGH(15) |
> SPIFI_CTRL_FBCLK;
>
> - if (mode & SPI_RX_DUAL) {
> + if (mode & SPI_RX_DUAL)
> ctrl |= SPIFI_CTRL_DUAL;
> - flash_read = SPI_NOR_DUAL;
> - } else if (mode & SPI_RX_QUAD) {
> + else if (mode & SPI_RX_QUAD)
> ctrl &= ~SPIFI_CTRL_DUAL;
> - flash_read = SPI_NOR_QUAD;
> - } else {
> + else
> ctrl |= SPIFI_CTRL_DUAL;
> - flash_read = SPI_NOR_NORMAL;
> - }
>
> switch (mode & (SPI_CPHA | SPI_CPOL)) {
> case SPI_MODE_0:
> @@ -349,7 +344,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
> */
> nxp_spifi_dummy_id_read(&spifi->nor);
>
> - ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
> + ret = spi_nor_scan(&spifi->nor, NULL, mode);
> if (ret) {
> dev_err(spifi->dev, "device scan failed\n");
> return ret;
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index 8818d4325d20..1908038c8f2e 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -20,6 +20,7 @@
> #include <linux/mtd/cfi.h>
> #include <linux/mtd/mtd.h>
> #include <linux/of_platform.h>
> +#include <linux/spi/spi.h>
Same comment as above - don't use spi on spi-nor.
> #include <linux/spi/flash.h>
> #include <linux/mtd/spi-nor.h>
>
> @@ -61,6 +62,11 @@ struct flash_info {
>
> #define JEDEC_MFR(info) ((info)->id[0])
>
> +struct read_id_proto {
> + enum spi_protocol proto; /* SPI protocol to read the JEDEC ID */
> + u16 mode; /* SPI controller required caps */
> +};
> +
> static const struct flash_info *spi_nor_match_id(const char *name);
>
> /*
> @@ -701,11 +707,15 @@ static const struct flash_info spi_nor_ids[] = {
> { },
> };
>
> -static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
> +static const struct flash_info *spi_nor_read_id(struct spi_nor *nor, u16 mode)
> {
> - int tmp;
> + int i, tmp;
> u8 id[SPI_NOR_MAX_ID_LEN];
> const struct flash_info *info;
> + static const struct read_id_proto proto[] = {
> + { SPI_PROTO_4_4_4, SPI_RX_QUAD | SPI_TX_QUAD },
> + { SPI_PROTO_2_2_2, SPI_RX_DUAL | SPI_TX_DUAL }
> + };
>
> tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
> if (tmp < 0) {
> @@ -713,6 +723,35 @@ 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;
> +
> + /* Check whether the SPI controller supports this protocol. */
> + if ((mode & proto[i].mode) != proto[i].mode)
> + continue;
> +
> + nor->erase_proto = proto[i].proto;
> + nor->read_proto = proto[i].proto;
> + nor->write_proto = proto[i].proto;
> + nor->reg_proto = proto[i].proto;
> +
> + /*
> + * Multiple I/O Read ID only returns the Manufacturer ID
> + * (1 byte) and the Device ID (2 bytes). So we reset the
> + * remaining bytes.
> + */
> + memset(id, 0, sizeof(id));
> + tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID, id, 3);
> + 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) {
> @@ -999,7 +1038,7 @@ static int spi_nor_check(struct spi_nor *nor)
> return 0;
> }
>
> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
> +int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode)
> {
> const struct flash_info *info = NULL;
> struct device *dev = nor->dev;
> @@ -1012,11 +1051,17 @@ 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 */
> if (!info)
> - info = spi_nor_read_id(nor);
> + info = spi_nor_read_id(nor, mode);
> if (IS_ERR_OR_NULL(info))
> return -ENOENT;
>
> @@ -1027,7 +1072,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
> if (name && info->id_len) {
> const struct flash_info *jinfo;
>
> - jinfo = spi_nor_read_id(nor);
> + jinfo = spi_nor_read_id(nor, mode);
> if (IS_ERR(jinfo)) {
> return PTR_ERR(jinfo);
> } else if (jinfo != info) {
> @@ -1126,14 +1171,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
> nor->flash_read = SPI_NOR_NORMAL;
>
> /* Quad/Dual-read mode takes precedence over fast/normal */
> - if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
> + if (mode & SPI_RX_QUAD && info->flags & SPI_NOR_QUAD_READ) {
> ret = set_quad_mode(nor, info);
> if (ret) {
> dev_err(dev, "quad mode not supported\n");
> return ret;
> }
> nor->flash_read = SPI_NOR_QUAD;
> - } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
> + } else if (mode & SPI_RX_DUAL && info->flags & SPI_NOR_DUAL_READ) {
> nor->flash_read = SPI_NOR_DUAL;
> }
>
> diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
> index 672595a381c5..32339e82cb9d 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 */
> +};
> +
> #define SPI_NOR_MAX_CMD_SIZE 8
> enum spi_nor_ops {
> SPI_NOR_OPS_READ = 0,
> @@ -117,6 +128,10 @@ struct mtd_info;
> * @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_*)
> + * @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
> @@ -143,6 +158,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;
> @@ -169,7 +188,7 @@ struct spi_nor {
> * spi_nor_scan() - scan the SPI NOR
> * @nor: the spi_nor structure
> * @name: the chip type name
> - * @mode: the read mode supported by the driver
> + * @mode: the bitmask or TX/RX modes supported by the driver
> *
> * The drivers can use this fuction to scan the SPI NOR.
> * In the scanning, it will try to get all the necessary information to
> @@ -179,6 +198,6 @@ struct spi_nor {
> *
> * Return: 0 for success, others for failure.
> */
> -int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
> +int spi_nor_scan(struct spi_nor *nor, const char *name, u16 mode);
>
> #endif
> --
> 1.8.2.2
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
thanks!
--
Jagan | openedev.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [linux-next RFC v7 2/6] mtd: spi-nor: read JEDEC ID with multiple I/O protocols
[not found] ` <CAD6G_RQV--nRk-pf347ThnEbQ=+tozXq8PKZL818cEK7nAi+gA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2015-09-16 8:34 ` Cyrille Pitchen
0 siblings, 0 replies; 9+ messages in thread
From: Cyrille Pitchen @ 2015-09-16 8:34 UTC (permalink / raw)
To: Jagan Teki
Cc: nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w, Mark Brown,
linux-spi-u79uwXL29TY76Z2rM5mHXA, David Woodhouse, Brian Norris,
zajec5-Re5JQEeQqe8AvxtiuMwx3w, beanhuo-AL4WhLSQfzjQT0dZR+AlfA,
juhosg-p3rKhJxN3npAfugRpC6u6w, Marek Vašut,
ben-/+tVBieCtBitmTQ+vhA3Yw, Mark Rutland,
devicetree-u79uwXL29TY76Z2rM5mHXA, Pawel Moll, Ian Campbell,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Kumar Gala,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
Jonas Gorski
Hi Jagan,
Le 15/09/2015 19:53, Jagan Teki a écrit :
> On 15 September 2015 at 20:58, 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/devices/m25p80.c | 8 +-----
>> drivers/mtd/spi-nor/fsl-quadspi.c | 2 +-
>> drivers/mtd/spi-nor/nxp-spifi.c | 13 +++------
>> drivers/mtd/spi-nor/spi-nor.c | 59 ++++++++++++++++++++++++++++++++++-----
>> include/linux/mtd/spi-nor.h | 27 +++++++++++++++---
>> 5 files changed, 81 insertions(+), 28 deletions(-)
>>
>> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
>> index 4b5d7a4655fd..1457866a4930 100644
>> --- a/drivers/mtd/devices/m25p80.c
>> +++ b/drivers/mtd/devices/m25p80.c
>> @@ -179,7 +179,6 @@ static int m25p_probe(struct spi_device *spi)
>> struct flash_platform_data *data;
>> struct m25p *flash;
>> struct spi_nor *nor;
>> - enum read_mode mode = SPI_NOR_NORMAL;
>> char *flash_name = NULL;
>> int ret;
>>
>> @@ -205,11 +204,6 @@ static int m25p_probe(struct spi_device *spi)
>> spi_set_drvdata(spi, flash);
>> flash->spi = spi;
>>
>> - if (spi->mode & SPI_RX_QUAD)
>> - mode = SPI_NOR_QUAD;
>> - else if (spi->mode & SPI_RX_DUAL)
>> - mode = SPI_NOR_DUAL;
>> -
>> if (data && data->name)
>> nor->mtd.name = data->name;
>>
>> @@ -223,7 +217,7 @@ static int m25p_probe(struct spi_device *spi)
>> else
>> flash_name = spi->modalias;
>>
>> - ret = spi_nor_scan(nor, flash_name, mode);
>> + ret = spi_nor_scan(nor, flash_name, spi->mode);
>
> IMHO, this is certainly incorrect because spi-nor never know anything
> about spi (Linux) that is why this framework got into picture.
>
OK but what to use instead? Because we need to know the SPI controller
capabilities as compared to what the SPI NOR memory expects.
SPI_NOR_QUAD is just not enough as it doesn't allow us to make the difference
between SPI 1-1-4, 1-4-4 or 4-4-4 protocols.
Some manufacturer specific commands like Multiple I/O Read ID (0xaf) only work
with the SPI 4-4-4 protocol.
Also patch 3 (mtd: spi-nor: set the read op code and protocol based on the
manufacturer) makes use of the SPI controller capabilities to select the right
Fast Read op code and SPI protocol to match the SPI NOR memory interface.
Currently the framework always uses the Fast Read Quad Output 1-1-4 (0x6b).
Then for Macronix QSPI memories, the framework also enables their QPI mode.
However the datasheet of Macronix MX66L1G claims this command (0x6b) is only
supported in in SPI mode but not in QPI mode.
Also for Micron QSPI memories, the framework turns their Quad mode on but once
enabled the spi-nor memories expected ALL commands to use the SPI 4-4-4
protocol. SPI controllers have no mean to be notified about that protocol
change.
Besides, the m25p80 driver only checks the SPI_RX_QUAD flag before asking the
spi-nor framework to use some Quad SPI mode but in many cases it's not enough;
the SPI_TX_QUAD flag is also needed.
Of course there are other ways to provide the spi-nor framework with the SPI
controller. Maybe we can still use a bitmask for its simplicity of use by
changing a little bit the definition of the spi_protocol constants:
#define SPI_PROTO_1_1_1 0x0001
#define SPI_PROTO_1_1_2 0x0002
#define SPI_PROTO_1_1_4 0x0004
#define SPI_PROTO_1_2_2 0x0008
#define SPI_PROTO_1_4_4 0x0010
#define SPI_PROTO_2_2_2 0x0020
#define SPI_PROTO_4_4_4 0x0040
Then for instance in m25p80.c, we do something like:
unsigned int supported_protocols = SPI_PROTO_1_1_1;
if (spi->mode & SPI_RX_QUAD) {
supported_protocols |= SPI_PROTO_1_1_4;
if (spi->mode & SPI_TX_QUAD)
supported_protocols |= (SPI_PROTO_1_4_4 | SPI_PROTO_4_4_4);
}
if (spi->mode & SPI_RX_DUAL) {
supported_protocols |= SPI_PROTO_1_1_2;
if (spi->mode & SPI_TX_DUAL)
supported_protocols |= (SPI_PROTO_1_2_2 | SPI_PROTO_2_2_2);
}
ret = spi_nor_scan(nor, flash_name, supported_protocols);
Does it sound better to you so we don't use the flags defined in
<linux/spi/spi.h> in the spi-nor framework ?
[...]
>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>> index 8818d4325d20..1908038c8f2e 100644
>> --- a/drivers/mtd/spi-nor/spi-nor.c
>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>> @@ -20,6 +20,7 @@
>> #include <linux/mtd/cfi.h>
>> #include <linux/mtd/mtd.h>
>> #include <linux/of_platform.h>
>> +#include <linux/spi/spi.h>
>
> Same comment as above - don't use spi on spi-nor.
>
>> #include <linux/spi/flash.h>
>> #include <linux/mtd/spi-nor.h>
>>
>> @@ -61,6 +62,11 @@ struct flash_info {
>>
>> #define JEDEC_MFR(info) ((info)->id[0])
>>
>> +struct read_id_proto {
>> + enum spi_protocol proto; /* SPI protocol to read the JEDEC ID */
>> + u16 mode; /* SPI controller required caps */
>> +};
>> +
>> static const struct flash_info *spi_nor_match_id(const char *name);
>>
>> /*
>> @@ -701,11 +707,15 @@ static const struct flash_info spi_nor_ids[] = {
>> { },
>> };
>>
>> -static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
>> +static const struct flash_info *spi_nor_read_id(struct spi_nor *nor, u16 mode)
>> {
>> - int tmp;
>> + int i, tmp;
>> u8 id[SPI_NOR_MAX_ID_LEN];
>> const struct flash_info *info;
>> + static const struct read_id_proto proto[] = {
>> + { SPI_PROTO_4_4_4, SPI_RX_QUAD | SPI_TX_QUAD },
>> + { SPI_PROTO_2_2_2, SPI_RX_DUAL | SPI_TX_DUAL }
>> + };
>>
>> tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
>> if (tmp < 0) {
>> @@ -713,6 +723,35 @@ 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;
>> +
>> + /* Check whether the SPI controller supports this protocol. */
>> + if ((mode & proto[i].mode) != proto[i].mode)
>> + continue;
>> +
>> + nor->erase_proto = proto[i].proto;
>> + nor->read_proto = proto[i].proto;
>> + nor->write_proto = proto[i].proto;
>> + nor->reg_proto = proto[i].proto;
>> +
>> + /*
>> + * Multiple I/O Read ID only returns the Manufacturer ID
>> + * (1 byte) and the Device ID (2 bytes). So we reset the
>> + * remaining bytes.
>> + */
>> + memset(id, 0, sizeof(id));
>> + tmp = nor->read_reg(nor, SPINOR_OP_MIO_RDID, id, 3);
>> + 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) {
[...]
>
>
> thanks!
>
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] 9+ messages in thread
end of thread, other threads:[~2015-09-16 8:34 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-15 15:27 [linux-next RFC v7 0/6] mtd: spi-nor: improve support of QSPI nor Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 2/6] mtd: spi-nor: read JEDEC ID with multiple I/O protocols Cyrille Pitchen
2015-09-15 17:53 ` Jagan Teki
[not found] ` <CAD6G_RQV--nRk-pf347ThnEbQ=+tozXq8PKZL818cEK7nAi+gA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-09-16 8:34 ` Cyrille Pitchen
[not found] ` <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
2015-09-15 15:27 ` [linux-next RFC v7 1/6] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 3/6] mtd: spi-nor: set the read op code and protocol based on the manufacturer Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 4/6] mtd: m25p80: add support of dual and quad spi protocols to all commands Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 5/6] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen
2015-09-15 15:28 ` [linux-next RFC v7 6/6] mtd: atmel-quadspi: add driver for Atmel QSPI controller 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).