[parent not found: <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>]
* [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 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 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
[not found] ` <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
@ 2015-09-15 15:28 ` Cyrille Pitchen
2015-09-15 17:53 ` Jagan Teki
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
` (2 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
* 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
* [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
[not found] ` <cover.1442330503.git.cyrille.pitchen-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
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 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