2015-08-26 12:30:20

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next v5 0/5] add driver for Atmel QSPI controller

ChangeLog

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

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

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

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

v1:

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

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

Cyrille Pitchen (5):
mtd: spi-nor: notify (Q)SPI controller about protocol change
Documentation: mtd: add a DT property to set the number of dummy
cycles
mtd: spi-nor: allow to tune the number of dummy cycles
Documentation: atmel-quadspi: add binding file for Atmel QSPI driver
mtd: atmel-quadspi: add driver for Atmel QSPI controller

.../devicetree/bindings/mtd/atmel-quadspi.txt | 29 +
.../devicetree/bindings/mtd/jedec,spi-nor.txt | 6 +
drivers/mtd/spi-nor/Kconfig | 7 +
drivers/mtd/spi-nor/Makefile | 1 +
drivers/mtd/spi-nor/atmel-quadspi.c | 859 +++++++++++++++++++++
drivers/mtd/spi-nor/spi-nor.c | 123 ++-
include/linux/mtd/spi-nor.h | 15 +
7 files changed, 1021 insertions(+), 19 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


2015-08-26 12:30:44

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next v5 1/5] mtd: spi-nor: notify (Q)SPI controller about protocol change

Once the Quad SPI mode has been enabled on a Micron flash memory, this
device expects ALL the following commands to use the SPI 4-4-4 protocol.
The (Q)SPI controller needs to be notified about the protocol change so it
can adapt and keep on dialoging with the Micron memory.

Signed-off-by: Cyrille Pitchen <[email protected]>
Acked-by: Marek Vasut <[email protected]>
Acked-by: Bean Huo <[email protected]>
---
drivers/mtd/spi-nor/spi-nor.c | 21 +++++++++++++++++++++
include/linux/mtd/spi-nor.h | 13 +++++++++++++
2 files changed, 34 insertions(+)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index c27d427fead4..c8810313a752 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -165,6 +165,22 @@ static inline int write_disable(struct spi_nor *nor)
return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
}

+/*
+ * Let the spi-nor framework notify lower layers, especially the driver of the
+ * (Q)SPI controller, about the new protocol to be used. Indeed, once the
+ * spi-nor framework has sent manufacturer specific commands to a memory to
+ * enable its Quad SPI mode, it should immediately after tell the QSPI
+ * controller to use the very same Quad SPI protocol as expected by the memory.
+ */
+static inline int spi_nor_set_protocol(struct spi_nor *nor,
+ enum spi_protocol proto)
+{
+ if (nor->set_protocol)
+ return nor->set_protocol(nor, proto);
+
+ return 0;
+}
+
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
{
return mtd->priv;
@@ -940,6 +956,11 @@ static int micron_quad_enable(struct spi_nor *nor)
return ret;
}

+ /* switch protocol to Quad CMD 4-4-4 */
+ ret = spi_nor_set_protocol(nor, SPI_PROTO_4_4_4);
+ if (ret)
+ return ret;
+
ret = spi_nor_wait_till_ready(nor);
if (ret)
return ret;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index e5409524bb0a..1bf6f11310ef 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -87,6 +87,16 @@ enum read_mode {
SPI_NOR_QUAD,
};

+enum spi_protocol {
+ SPI_PROTO_1_1_1, /* SPI */
+ SPI_PROTO_1_1_2, /* Dual Output */
+ SPI_PROTO_1_1_4, /* Quad Output */
+ SPI_PROTO_1_2_2, /* Dual IO */
+ SPI_PROTO_1_4_4, /* Quad IO */
+ SPI_PROTO_2_2_2, /* Dual Command */
+ SPI_PROTO_4_4_4, /* Quad Command */
+};
+
/**
* struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
* @wren: command for "Write Enable", or 0x00 for not required
@@ -149,6 +159,7 @@ enum spi_nor_option_flags {
* read/write/erase/lock/unlock operations
* @read_xfer: [OPTIONAL] the read fundamental primitive
* @write_xfer: [OPTIONAL] the writefundamental primitive
+ * @set_protocol: [OPTIONAL] notify about protocol change
* @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
@@ -185,6 +196,8 @@ struct spi_nor {
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
int write_enable);

+ int (*set_protocol)(struct spi_nor *nor, enum spi_protocol proto);
+
int (*read)(struct spi_nor *nor, loff_t from,
size_t len, size_t *retlen, u_char *read_buf);
void (*write)(struct spi_nor *nor, loff_t to,
--
1.8.2.2

2015-08-26 12:31:09

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next v5 2/5] Documentation: mtd: add a DT property to set the number of dummy cycles

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

Signed-off-by: Cyrille Pitchen <[email protected]>
---
Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
index 2bee68103b01..4387567d8024 100644
--- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
+++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
@@ -19,6 +19,11 @@ Optional properties:
all chips and support for it can not be detected at runtime.
Refer to your chips' datasheet to check if this is supported
by your chip.
+- m25p,num-dummy-cycles : Set the number of dummy cycles for Fast Read commands.
+ Depending on the manufacturer additional dedicated
+ commands are sent to the flash memory so the
+ controller and the memory can agree on the number of
+ dummy cycles to use.

Example:

@@ -29,4 +34,5 @@ Example:
reg = <0>;
spi-max-frequency = <40000000>;
m25p,fast-read;
+ m25p,num-dummy-cycles = <8>;
};
--
1.8.2.2

2015-08-26 12:31:36

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next v5 3/5] mtd: spi-nor: allow to tune the number of dummy cycles

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

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

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

Signed-off-by: Cyrille Pitchen <[email protected]>
---
drivers/mtd/spi-nor/spi-nor.c | 102 ++++++++++++++++++++++++++++++++++--------
include/linux/mtd/spi-nor.h | 2 +
2 files changed, 85 insertions(+), 19 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index c8810313a752..46d6e02f7560 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -121,24 +121,6 @@ static int read_cr(struct spi_nor *nor)
}

/*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_FAST:
- case SPI_NOR_DUAL:
- case SPI_NOR_QUAD:
- return 8;
- case SPI_NOR_NORMAL:
- return 0;
- }
- return 0;
-}
-
-/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
@@ -1008,6 +990,86 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
}
}

+static int micron_set_dummy_cycles(struct spi_nor *nor)
+{
+ int ret;
+ u8 val, mask;
+
+ /* Read the Volatile Configuration Register (VCR). */
+ ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &val, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error %d reading VCR\n", ret);
+ return ret;
+ }
+
+ write_enable(nor);
+
+ /* Update the number of dummy into the VCR. */
+ mask = GENMASK(7, 4);
+ val &= ~mask;
+ val |= (nor->read_dummy << 4) & mask;
+ ret = nor->write_reg(nor, SPINOR_OP_WR_VCR, &val, 1, 0);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while writing VCR register\n");
+ return ret;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Dummy Cycle calculation for different type of read.
+ * It can be used to support more commands with
+ * different dummy cycle requirements.
+ */
+static int spi_nor_read_dummy_cycles(struct spi_nor *nor,
+ const struct flash_info *info)
+{
+ struct device_node *np = nor->dev->of_node;
+ struct device *dev = nor->dev;
+ u32 num_dummy_cycles;
+
+ if (np && !of_property_read_u32(np, "m25p,num-dummy-cycles",
+ &num_dummy_cycles)) {
+ nor->read_dummy = num_dummy_cycles;
+
+ /*
+ * This switch block might be moved at the end of the function,
+ * once nor->read_dummy has been set, but it was not tested with
+ * all Micron memories.
+ * Now the "m25p,num-dummy-cycles" property needs to be
+ * explicitly set in the device tree so the switch statement is
+ * executed. This should avoid unwanted side effects and keep
+ * backward compatibility.
+ */
+ switch (JEDEC_MFR(info)) {
+ case CFI_MFR_ST:
+ return micron_set_dummy_cycles(nor);
+ default:
+ dev_warn(dev,
+ "Tuning of the number of dummy cycles is not implemented for spi-nor of this manufacturer,\n"
+ "ignoring the DT property value and falling back to the framework default settings.\n");
+ break;
+ }
+ }
+
+ /* Fallback to legacy code. */
+ switch (nor->flash_read) {
+ case SPI_NOR_FAST:
+ case SPI_NOR_DUAL:
+ case SPI_NOR_QUAD:
+ nor->read_dummy = 8;
+ case SPI_NOR_NORMAL:
+ nor->read_dummy = 0;
+ }
+
+ return 0;
+}
+
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
@@ -1208,7 +1270,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
nor->addr_width = 3;
}

- nor->read_dummy = spi_nor_read_dummy_cycles(nor);
+ ret = spi_nor_read_dummy_cycles(nor, info);
+ if (ret)
+ return ret;

dev_info(dev, "%s (%lld Kbytes)\n", info->name,
(long long)mtd->size >> 10);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 1bf6f11310ef..e03a4c4053d3 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -59,6 +59,8 @@
/* 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_RD_VCR 0x85 /* Read VCR register */
+#define SPINOR_OP_WR_VCR 0x81 /* Write VCR register */

/* Status Register bits. */
#define SR_WIP 1 /* Write in progress */
--
1.8.2.2

2015-08-26 12:32:03

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next v5 4/5] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver

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

Signed-off-by: Cyrille Pitchen <[email protected]>
Acked-by: Nicolas Ferre <[email protected]>
Acked-by: Marek Vasut <[email protected]>
---
.../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

2015-08-26 12:32:28

by Cyrille Pitchen

[permalink] [raw]
Subject: [PATCH linux-next v5 5/5] mtd: atmel-quadspi: add driver for Atmel QSPI controller

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 <[email protected]>
Acked-by: Nicolas Ferre <[email protected]>
---
drivers/mtd/spi-nor/Kconfig | 7 +
drivers/mtd/spi-nor/Makefile | 1 +
drivers/mtd/spi-nor/atmel-quadspi.c | 859 ++++++++++++++++++++++++++++++++++++
3 files changed, 867 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..003cdf00e8b1
--- /dev/null
+++ b/drivers/mtd/spi-nor/atmel-quadspi.c
@@ -0,0 +1,859 @@
+/*
+ * Driver for Atmel QSPI Controller
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Cyrille Pitchen <[email protected]>
+ *
+ * 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 ifr_width;
+ u32 pending;
+
+ struct mtd_info mtd;
+ struct spi_nor nor;
+ u32 clk_rate;
+ struct completion cmd_completion;
+ struct completion dma_completion;
+
+#ifdef DEBUG
+ u8 last_instruction;
+#endif
+};
+
+struct atmel_qspi_command {
+ u32 ifr_tfrtyp;
+ 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 (aq->ifr_width) {
+ 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 = aq->ifr_width | cmd->ifr_tfrtyp;
+
+ /* 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_read_reg(struct spi_nor *nor, u8 opcode,
+ u8 *buf, int len)
+{
+ struct atmel_qspi *aq = nor->priv;
+ struct atmel_qspi_command cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.ifr_tfrtyp = QSPI_IFR_TFRTYP_TRSFR_READ;
+ cmd.enable.bits.instruction = 1;
+ cmd.enable.bits.data = 1;
+ cmd.instruction = opcode;
+ cmd.rx_buf = buf;
+ cmd.buf_len = len;
+ 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;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.ifr_tfrtyp = QSPI_IFR_TFRTYP_TRSFR_WRITE;
+ cmd.enable.bits.instruction = 1;
+ cmd.enable.bits.data = (buf != NULL && len > 0);
+ cmd.instruction = opcode;
+ cmd.tx_buf = buf;
+ cmd.buf_len = len;
+ 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.ifr_tfrtyp = QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM;
+ 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_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;
+
+ dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
+ aq->mtd.erasesize / 1024, (u32)offs);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.ifr_tfrtyp = QSPI_IFR_TFRTYP_TRSFR_WRITE;
+ cmd.enable.bits.instruction = 1;
+ cmd.enable.bits.address = nor->addr_width;
+ cmd.instruction = nor->erase_opcode;
+ cmd.address = (u32)offs;
+ 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 err;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.ifr_tfrtyp = QSPI_IFR_TFRTYP_TRSFR_READ_MEM;
+ 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;
+ err = atmel_qspi_run_command(aq, &cmd);
+ if (err)
+ return err;
+
+ *retlen += len;
+ return 0;
+}
+
+static int atmel_qspi_set_protocol(struct spi_nor *nor, enum spi_protocol proto)
+{
+ struct atmel_qspi *aq = nor->priv;
+
+ switch (proto) {
+ case SPI_PROTO_1_1_1:
+ aq->ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
+ break;
+ case SPI_PROTO_1_1_2:
+ aq->ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
+ break;
+ case SPI_PROTO_1_1_4:
+ aq->ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
+ break;
+ case SPI_PROTO_1_2_2:
+ aq->ifr_width = QSPI_IFR_WIDTH_DUAL_IO;
+ break;
+ case SPI_PROTO_1_4_4:
+ aq->ifr_width = QSPI_IFR_WIDTH_QUAD_IO;
+ break;
+ case SPI_PROTO_2_2_2:
+ aq->ifr_width = QSPI_IFR_WIDTH_DUAL_CMD;
+ break;
+ case SPI_PROTO_4_4_4:
+ aq->ifr_width = QSPI_IFR_WIDTH_QUAD_CMD;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int atmel_qspi_init(struct atmel_qspi *aq)
+{
+ unsigned long src_rate;
+ u32 mr, scr, scbr;
+
+ /* Reset the QSPI controller */
+ qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST);
+
+ /* Set the QSPI controller in Serial Memory Mode */
+ mr = QSPI_MR_SSM | QSPI_MR_NBBITS(8);
+ qspi_writel(aq, QSPI_MR, mr);
+
+ src_rate = clk_get_rate(aq->clk);
+ if (!src_rate)
+ return -EINVAL;
+
+ /* Compute the QSPI baudrate */
+ scbr = DIV_ROUND_UP(src_rate, aq->clk_rate);
+ if (scbr > 0)
+ scbr--;
+ scr = QSPI_SCR_SCBR(scbr);
+ qspi_writel(aq, QSPI_SCR, scr);
+
+ /* Enable the QSPI controller */
+ qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN);
+
+ return 0;
+}
+
+static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
+{
+ struct atmel_qspi *aq = (struct atmel_qspi *)dev_id;
+ u32 status, mask, pending;
+
+ status = qspi_readl(aq, QSPI_SR);
+ mask = qspi_readl(aq, QSPI_IMR);
+ pending = status & mask;
+
+ if (!pending)
+ return IRQ_NONE;
+
+ aq->pending |= pending;
+ if (pending & QSPI_SR_INSTRE)
+ complete(&aq->cmd_completion);
+
+ return IRQ_HANDLED;
+}
+
+static int atmel_qspi_probe(struct platform_device *pdev)
+{
+ struct device_node *child, *np = pdev->dev.of_node;
+ struct mtd_part_parser_data ppdata;
+ struct atmel_qspi *aq;
+ struct resource *res;
+ dma_cap_mask_t mask;
+ struct spi_nor *nor;
+ struct mtd_info *mtd;
+ char modalias[40];
+ int irq, err = 0;
+
+ if (of_get_child_count(np) != 1)
+ return -ENODEV;
+ child = of_get_next_child(np, NULL);
+
+ aq = devm_kzalloc(&pdev->dev, sizeof(*aq), GFP_KERNEL);
+ if (!aq) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ platform_set_drvdata(pdev, aq);
+ init_completion(&aq->cmd_completion);
+ init_completion(&aq->dma_completion);
+ aq->pdev = pdev;
+ /* Start in Extended SPI (1-1-1) */
+ aq->ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
+
+ /* Map the registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ aq->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(aq->regs)) {
+ dev_err(&pdev->dev, "missing registers\n");
+ err = PTR_ERR(aq->regs);
+ goto exit;
+ }
+
+ /* Map the AHB memory */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ aq->mem = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(aq->mem)) {
+ dev_err(&pdev->dev, "missing AHB memory\n");
+ err = PTR_ERR(aq->regs);
+ goto exit;
+ }
+ aq->phys_addr = (dma_addr_t)res->start;
+
+ /* Get the peripheral clock */
+ aq->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(aq->clk)) {
+ dev_err(&pdev->dev, "missing peripheral clock\n");
+ err = PTR_ERR(aq->clk);
+ goto exit;
+ }
+
+ /* Enable the peripheral clock */
+ err = clk_prepare_enable(aq->clk);
+ if (err) {
+ dev_err(&pdev->dev, "failed to enable the peripheral clock\n");
+ goto exit;
+ }
+
+ /* Request the IRQ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "missing IRQ\n");
+ err = irq;
+ goto disable_clk;
+ }
+ err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
+ 0, dev_name(&pdev->dev), aq);
+ if (err)
+ goto disable_clk;
+
+ /* Try to get a DMA channel for memcpy() operation */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ aq->chan = dma_request_channel(mask, NULL, NULL);
+ if (!aq->chan)
+ dev_warn(&pdev->dev, "no available DMA channel\n");
+
+ /* Setup the spi-nor */
+ nor = &aq->nor;
+ mtd = &aq->mtd;
+
+ nor->mtd = mtd;
+ nor->dev = &pdev->dev;
+ nor->priv = aq;
+ mtd->priv = nor;
+
+ nor->read_reg = atmel_qspi_read_reg;
+ nor->write_reg = atmel_qspi_write_reg;
+ nor->read = atmel_qspi_read;
+ nor->write = atmel_qspi_write;
+ nor->erase = atmel_qspi_erase;
+ nor->set_protocol = atmel_qspi_set_protocol;
+
+ if (of_modalias_node(child, modalias, sizeof(modalias)) < 0) {
+ err = -ENODEV;
+ goto release_channel;
+ }
+
+ err = of_property_read_u32(child, "spi-max-frequency", &aq->clk_rate);
+ if (err < 0)
+ goto release_channel;
+
+ err = atmel_qspi_init(aq);
+ if (err)
+ goto release_channel;
+
+ nor->dev->of_node = child;
+ err = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
+ nor->dev->of_node = np;
+ if (err)
+ goto release_channel;
+
+ ppdata.of_node = child;
+ err = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ if (err)
+ goto release_channel;
+
+ of_node_put(child);
+
+ return 0;
+
+release_channel:
+ if (aq->chan)
+ dma_release_channel(aq->chan);
+disable_clk:
+ clk_disable_unprepare(aq->clk);
+exit:
+ of_node_put(child);
+
+ return err;
+}
+
+static int atmel_qspi_remove(struct platform_device *pdev)
+{
+ struct atmel_qspi *aq = platform_get_drvdata(pdev);
+
+ mtd_device_unregister(&aq->mtd);
+ qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS);
+ if (aq->chan)
+ dma_release_channel(aq->chan);
+ clk_disable_unprepare(aq->clk);
+ return 0;
+}
+
+
+static const struct of_device_id atmel_qspi_dt_ids[] = {
+ { .compatible = "atmel,sama5d2-qspi" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_qspi_dt_ids);
+
+static struct platform_driver atmel_qspi_driver = {
+ .driver = {
+ .name = "atmel_qspi",
+ .of_match_table = atmel_qspi_dt_ids,
+ },
+ .probe = atmel_qspi_probe,
+ .remove = atmel_qspi_remove,
+};
+module_platform_driver(atmel_qspi_driver);
+
+MODULE_AUTHOR("Cyrille Pitchen <[email protected]>");
+MODULE_DESCRIPTION("Atmel QSPI Controller driver");
+MODULE_LICENSE("GPL v2");
--
1.8.2.2

2015-08-26 14:03:22

by Jonas Gorski

[permalink] [raw]
Subject: Re: [PATCH linux-next v5 1/5] mtd: spi-nor: notify (Q)SPI controller about protocol change

On Wed, Aug 26, 2015 at 2:30 PM, Cyrille Pitchen
<[email protected]> wrote:
> Once the Quad SPI mode has been enabled on a Micron flash memory, this
> device expects ALL the following commands to use the SPI 4-4-4 protocol.
> The (Q)SPI controller needs to be notified about the protocol change so it
> can adapt and keep on dialoging with the Micron memory.

Doesn't that mean you need to disable quad mode on removal? Else the
following will break/fail:

insmod atmel-quadspi.ko ~> spi-nor attaches -> sees micron -> enables quad mode
rmmod atmel-quadspi.ko ~> spi-nor detaches
insmod atmel-quadspi.ko ~> spi-nor attaches -> fails to read the id
because flash is still in quad mode.

> Signed-off-by: Cyrille Pitchen <[email protected]>
> Acked-by: Marek Vasut <[email protected]>
> Acked-by: Bean Huo <[email protected]>
> ---
> drivers/mtd/spi-nor/spi-nor.c | 21 +++++++++++++++++++++
> include/linux/mtd/spi-nor.h | 13 +++++++++++++
> 2 files changed, 34 insertions(+)
>
> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
> index c27d427fead4..c8810313a752 100644
> --- a/drivers/mtd/spi-nor/spi-nor.c
> +++ b/drivers/mtd/spi-nor/spi-nor.c
> @@ -165,6 +165,22 @@ static inline int write_disable(struct spi_nor *nor)
> return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
> }
>
> +/*
> + * Let the spi-nor framework notify lower layers, especially the driver of the
> + * (Q)SPI controller, about the new protocol to be used. Indeed, once the
> + * spi-nor framework has sent manufacturer specific commands to a memory to
> + * enable its Quad SPI mode, it should immediately after tell the QSPI
> + * controller to use the very same Quad SPI protocol as expected by the memory.
> + */
> +static inline int spi_nor_set_protocol(struct spi_nor *nor,
> + enum spi_protocol proto)
> +{
> + if (nor->set_protocol)
> + return nor->set_protocol(nor, proto);
> +
> + return 0;

Shouldn't the default assumption be that it won't support it? Also it
might make sense to first check if it's supported before enabling it
in the chip, so that we don't enable something just to then find out
we can't back out of it.

I also wonder if we need an extra flag for that as at least SPI has
RX_{DUAL,QUAD} and TX_{DUAL,QUAD} separated, so in theory there could
be a controller that supports quad read, but not quad write, so we
shouldn't be using the quad mode in that case. m25p80 currently sets
SPI_NOR_{DUAL,QUAD} only based on SPI_RX_{DUAL,QUAD}, so that would
then fail.

At least the n25q32 seems to support the "boring" 1_1_2 and 1_1_4
commands, so these should work in case the spi controller does not
support quad tx, but quad rx.

So maybe an additional flag for the "full" dual/quad modes would be in order.

Last but not least, the protocol probably should be stored somewhere
in the nor struct, so that users don't have to store it themselves (I
assume they will need to check it for each command again to know if
the cmd/address should be send in serial or quad mode).


Jonas

2015-08-26 14:39:13

by Marek Vasut

[permalink] [raw]
Subject: Re: [PATCH linux-next v5 3/5] mtd: spi-nor: allow to tune the number of dummy cycles

On Wednesday, August 26, 2015 at 02:30:25 PM, Cyrille Pitchen wrote:
> The number of dummy cycles used during Fast Read commands can be reduced
> to improve transfer performances. Each manufacturer has a dedicated set of
> registers to provide the memory with the exact number of dummy cycles it
> should expect. Both the memory and the (Q)SPI controller must agree on
> this number of dummy cycles.
>
> The number of dummy cycles can be found into the memory datasheet and
> mostly depends on the SPI clock frequency, the Fast Read op code and the
> Single/Dual Data Rate mode.
>
> Probing JEDEC Serial Flash Discoverable Parameters (SFDP) tables would
> only provide the driver with a high enough number of dummy cycles for each
> Fast Read command to be used for all clock frequencies: this solution
> would not be optimized.
>
> Signed-off-by: Cyrille Pitchen <[email protected]>

Acked-by: Marek Vasut <[email protected]>

Best regards,
Marek Vasut

2015-08-26 16:26:57

by Marek Vasut

[permalink] [raw]
Subject: Re: [PATCH linux-next v5 5/5] mtd: atmel-quadspi: add driver for Atmel QSPI controller

On Wednesday, August 26, 2015 at 02:30:27 PM, Cyrille Pitchen wrote:
> 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 <[email protected]>
> Acked-by: Nicolas Ferre <[email protected]>

Acked-by: Marek Vasut <[email protected]>

Best regards,
Marek Vasut

2015-08-27 09:52:08

by Cyrille Pitchen

[permalink] [raw]
Subject: Re: [PATCH linux-next v5 1/5] mtd: spi-nor: notify (Q)SPI controller about protocol change

Hi Jonas,

Le 26/08/2015 16:02, Jonas Gorski a écrit :
> On Wed, Aug 26, 2015 at 2:30 PM, Cyrille Pitchen
> <[email protected]> wrote:
>> Once the Quad SPI mode has been enabled on a Micron flash memory, this
>> device expects ALL the following commands to use the SPI 4-4-4 protocol.
>> The (Q)SPI controller needs to be notified about the protocol change so it
>> can adapt and keep on dialoging with the Micron memory.
>
> Doesn't that mean you need to disable quad mode on removal? Else the
> following will break/fail:
>
> insmod atmel-quadspi.ko ~> spi-nor attaches -> sees micron -> enables quad mode
> rmmod atmel-quadspi.ko ~> spi-nor detaches
> insmod atmel-quadspi.ko ~> spi-nor attaches -> fails to read the id
> because flash is still in quad mode.
>

Indeed you're right such an issue does exist. So as you said one solution
could be create a new function to "clean" what have been done by spi_nor_scan()
then call it from remove() function of each driver calling spi_nor_scan() from
its probe().
However we could also enhance the probing of the memory. It is true that Micron
spi nor memories only accept SPI 4-4-4 commands once switched in quad mode but
actually they also provide a new command for this purpose:
"Multiple I/O Read ID" (0xAF).
Hence we could first try to probe using the regular Read ID (0x9F) command then
change the protocol, for instance to SPI 4-4-4, and try the 0xAF command.
I don't think all combinations for command/protocol need to be tested: for
Micron memories, their datasheets claim the 0x9F command is only supported in
"extended spi mode" so for the SPI 1-1-1 protocol whereas the 0xAF command
only works in dual or quad modes.
On the other hand Spansion memories still reply to the regular 0x9F command in
SPI 1-1-1 protocol even after their quad mode had been enabled.
For other manufacturers, well... I don't know!

Some of the advantages of the probe solution as compared to the remove one are:
- we don't need to patch all drivers using spi_nor_scan() to call a new
function from their remove().
- it doesn't rely on the assumption that the spi-nor memory starts in
SPI 1-1-1 protocol. As a matter of fact the remove() won't be called for
built-in modules or in many (all ?) cases of reset. Moreover some bootloaders
may have already enabled the quad mode before starting the Linux kernel. This
is what the sama5d2 romcode does when it is configured to boot from a QSPI
memory.

Anyway you're right and the issue need to be addressed but maybe in another
dedicated patch ?

>> Signed-off-by: Cyrille Pitchen <[email protected]>
>> Acked-by: Marek Vasut <[email protected]>
>> Acked-by: Bean Huo <[email protected]>
>> ---
>> drivers/mtd/spi-nor/spi-nor.c | 21 +++++++++++++++++++++
>> include/linux/mtd/spi-nor.h | 13 +++++++++++++
>> 2 files changed, 34 insertions(+)
>>
>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>> index c27d427fead4..c8810313a752 100644
>> --- a/drivers/mtd/spi-nor/spi-nor.c
>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>> @@ -165,6 +165,22 @@ static inline int write_disable(struct spi_nor *nor)
>> return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
>> }
>>
>> +/*
>> + * Let the spi-nor framework notify lower layers, especially the driver of the
>> + * (Q)SPI controller, about the new protocol to be used. Indeed, once the
>> + * spi-nor framework has sent manufacturer specific commands to a memory to
>> + * enable its Quad SPI mode, it should immediately after tell the QSPI
>> + * controller to use the very same Quad SPI protocol as expected by the memory.
>> + */
>> +static inline int spi_nor_set_protocol(struct spi_nor *nor,
>> + enum spi_protocol proto)
>> +{
>> + if (nor->set_protocol)
>> + return nor->set_protocol(nor, proto);
>> +
>> + return 0;
>
> Shouldn't the default assumption be that it won't support it? Also it
> might make sense to first check if it's supported before enabling it
> in the chip, so that we don't enable something just to then find out
> we can't back out of it.
>
> I also wonder if we need an extra flag for that as at least SPI has
> RX_{DUAL,QUAD} and TX_{DUAL,QUAD} separated, so in theory there could
> be a controller that supports quad read, but not quad write, so we
> shouldn't be using the quad mode in that case. m25p80 currently sets
> SPI_NOR_{DUAL,QUAD} only based on SPI_RX_{DUAL,QUAD}, so that would
> then fail.
>
> At least the n25q32 seems to support the "boring" 1_1_2 and 1_1_4
> commands, so these should work in case the spi controller does not
> support quad tx, but quad rx.
>
> So maybe an additional flag for the "full" dual/quad modes would be in order.
>

My first thought was to return -ENOSYS when the set_protocol() callback is not
implemented but logically none of the already existing drivers implements it.
So I made this new callback optional. This way, micron_quad_enable() works the
exact same way as before for all the existing drivers so no regression or side-
effect should be introduced by this patch.

Besides, the purpose of this callback is to notify the SPI controller that the
protocol change has been done at the memory side but not to decide whether such
a change is possible. Indeed, the capabilities of both the controller and the
memory are checked before calling set_quad_mode() so the decision has already
been taken when micron_quad_enable() is eventually called. Once the Quad Mode
bit set in the Enhanced Volatile Configuration Register of Micron memory, it's
too late: the memory now expects commands in SPI 4-4-4 protocol whether or not
the controller supports this protocol.

So more accurate checks should be done before calling set_quad_mode(). Maybe
the range of values for the mode parameter of spi_nor_scan() is too small.
SPI_NOR_QUAD doesn't allow to make the difference between SPI protocols 1-1-4,
1-4-4 or 4-4-4. Knowing the exact set of protocols supported by both the
controller and the memory could help making the right decision and choosing the
best suited Fast Read command.
The RX_{DUAL_QUAD} and TX_{DUAL,QUAD} flags from the spi->mode in the m25p80
driver provide more information than the limited "mode" argument of
spi_nor_scan().

> Last but not least, the protocol probably should be stored somewhere
> in the nor struct, so that users don't have to store it themselves (I
> assume they will need to check it for each command again to know if
> the cmd/address should be send in serial or quad mode).
>
>
I agree with you, this could be an improvement for some spi controller drivers :)

> Jonas
>

thanks for your review!

Best regards,

Cyrille

2015-08-31 19:22:47

by Jonas Gorski

[permalink] [raw]
Subject: Re: [PATCH linux-next v5 1/5] mtd: spi-nor: notify (Q)SPI controller about protocol change

Hi,

On Thu, Aug 27, 2015 at 11:51 AM, Cyrille Pitchen
<[email protected]> wrote:
> Hi Jonas,
>
> Le 26/08/2015 16:02, Jonas Gorski a écrit :
>> On Wed, Aug 26, 2015 at 2:30 PM, Cyrille Pitchen
>> <[email protected]> wrote:
>>> Once the Quad SPI mode has been enabled on a Micron flash memory, this
>>> device expects ALL the following commands to use the SPI 4-4-4 protocol.
>>> The (Q)SPI controller needs to be notified about the protocol change so it
>>> can adapt and keep on dialoging with the Micron memory.
>>
>> Doesn't that mean you need to disable quad mode on removal? Else the
>> following will break/fail:
>>
>> insmod atmel-quadspi.ko ~> spi-nor attaches -> sees micron -> enables quad mode
>> rmmod atmel-quadspi.ko ~> spi-nor detaches
>> insmod atmel-quadspi.ko ~> spi-nor attaches -> fails to read the id
>> because flash is still in quad mode.
>>
>
> Indeed you're right such an issue does exist. So as you said one solution
> could be create a new function to "clean" what have been done by spi_nor_scan()
> then call it from remove() function of each driver calling spi_nor_scan() from
> its probe().
> However we could also enhance the probing of the memory. It is true that Micron
> spi nor memories only accept SPI 4-4-4 commands once switched in quad mode but
> actually they also provide a new command for this purpose:
> "Multiple I/O Read ID" (0xAF).
> Hence we could first try to probe using the regular Read ID (0x9F) command then
> change the protocol, for instance to SPI 4-4-4, and try the 0xAF command.
> I don't think all combinations for command/protocol need to be tested: for
> Micron memories, their datasheets claim the 0x9F command is only supported in
> "extended spi mode" so for the SPI 1-1-1 protocol whereas the 0xAF command
> only works in dual or quad modes.
> On the other hand Spansion memories still reply to the regular 0x9F command in
> SPI 1-1-1 protocol even after their quad mode had been enabled.
> For other manufacturers, well... I don't know!

At least from the two spansion datasheets I looked at, there is no 2_
or 4_ mode for spansion flashes, and the quad mode only enables the
1_x_4 commands, where as on micron the 1_x_4 commands are always
available, and you only need to switch to DIO or QIO if you want to
use 2_2_2 or 4_4_4.

The question is how to detect if the READID command failed. Hopefully
it will result in an invalid command, and we get 0xff... (or 0x00...)
for the the id. And from there on we can first test 4_4_4 MIORDID (so
it won't a a full command on a DIO-enabled flash), and if that fails,
2_2_2. We need to tell the spi-nor controller to send these
accordingly though. I wonder if it might not be better to pass the
c_a_d as an additional parameter to the read_reg/write_reg etc call
backs, because they might change depending on the command used. E.g.
when using SPI_NOR_QUAD, we might want to make use of the quad page
program command (which is 1_1_4), but there is no 1_1_2 version, so we
cannot rely on a global "protocol" member for that, unless we want to
do a set_protocol before every command.

>
> Some of the advantages of the probe solution as compared to the remove one are:
> - we don't need to patch all drivers using spi_nor_scan() to call a new
> function from their remove().
> - it doesn't rely on the assumption that the spi-nor memory starts in
> SPI 1-1-1 protocol. As a matter of fact the remove() won't be called for
> built-in modules or in many (all ?) cases of reset. Moreover some bootloaders
> may have already enabled the quad mode before starting the Linux kernel. This
> is what the sama5d2 romcode does when it is configured to boot from a QSPI
> memory.
>
> Anyway you're right and the issue need to be addressed but maybe in another
> dedicated patch ?

Yes, probably as a prerequisite to the quad mode on micron flashes. I
think we should also add a separate flag for the DIO/QIO modes for
micron flashes, as these are something separate from just the DOR/QOR
commands usually implied by SPI_NOR_DUAL/QUAD (well they are the same,
but work differently)

>>> Signed-off-by: Cyrille Pitchen <[email protected]>
>>> Acked-by: Marek Vasut <[email protected]>
>>> Acked-by: Bean Huo <[email protected]>
>>> ---
>>> drivers/mtd/spi-nor/spi-nor.c | 21 +++++++++++++++++++++
>>> include/linux/mtd/spi-nor.h | 13 +++++++++++++
>>> 2 files changed, 34 insertions(+)
>>>
>>> diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
>>> index c27d427fead4..c8810313a752 100644
>>> --- a/drivers/mtd/spi-nor/spi-nor.c
>>> +++ b/drivers/mtd/spi-nor/spi-nor.c
>>> @@ -165,6 +165,22 @@ static inline int write_disable(struct spi_nor *nor)
>>> return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
>>> }
>>>
>>> +/*
>>> + * Let the spi-nor framework notify lower layers, especially the driver of the
>>> + * (Q)SPI controller, about the new protocol to be used. Indeed, once the
>>> + * spi-nor framework has sent manufacturer specific commands to a memory to
>>> + * enable its Quad SPI mode, it should immediately after tell the QSPI
>>> + * controller to use the very same Quad SPI protocol as expected by the memory.
>>> + */
>>> +static inline int spi_nor_set_protocol(struct spi_nor *nor,
>>> + enum spi_protocol proto)
>>> +{
>>> + if (nor->set_protocol)
>>> + return nor->set_protocol(nor, proto);
>>> +
>>> + return 0;
>>
>> Shouldn't the default assumption be that it won't support it? Also it
>> might make sense to first check if it's supported before enabling it
>> in the chip, so that we don't enable something just to then find out
>> we can't back out of it.
>>
>> I also wonder if we need an extra flag for that as at least SPI has
>> RX_{DUAL,QUAD} and TX_{DUAL,QUAD} separated, so in theory there could
>> be a controller that supports quad read, but not quad write, so we
>> shouldn't be using the quad mode in that case. m25p80 currently sets
>> SPI_NOR_{DUAL,QUAD} only based on SPI_RX_{DUAL,QUAD}, so that would
>> then fail.
>>
>> At least the n25q32 seems to support the "boring" 1_1_2 and 1_1_4
>> commands, so these should work in case the spi controller does not
>> support quad tx, but quad rx.
>>
>> So maybe an additional flag for the "full" dual/quad modes would be in order.
>>
>
> My first thought was to return -ENOSYS when the set_protocol() callback is not
> implemented but logically none of the already existing drivers implements it.
> So I made this new callback optional. This way, micron_quad_enable() works the
> exact same way as before for all the existing drivers so no regression or side-
> effect should be introduced by this patch.

If they don't implement the callback, then they won't know that they
are supposed to use a different protocol, so change will definitely
have broken them. Therefore an error return value and or a dev_warn
would be IMHO better.

> So more accurate checks should be done before calling set_quad_mode(). Maybe
> the range of values for the mode parameter of spi_nor_scan() is too small.
> SPI_NOR_QUAD doesn't allow to make the difference between SPI protocols 1-1-4,
> 1-4-4 or 4-4-4. Knowing the exact set of protocols supported by both the
> controller and the memory could help making the right decision and choosing the
> best suited Fast Read command.
> The RX_{DUAL_QUAD} and TX_{DUAL,QUAD} flags from the spi->mode in the m25p80
> driver provide more information than the limited "mode" argument of
> spi_nor_scan().

so from the controller side we need to know whether it can do
RX_{DUAL,QUAD} and TX_{DUAL,QUAD}; but for the flash supported
commands we have a bit more variety (the x_y_z you mentioned).

Funny enough, once we are on QIO on spansion, there is no difference
anymore between FAST (0x0b), QOR (0x6b) and QIOR (0xeb), they all work
in the 4_4_4 mode.

>> Last but not least, the protocol probably should be stored somewhere
>> in the nor struct, so that users don't have to store it themselves (I
>> assume they will need to check it for each command again to know if
>> the cmd/address should be send in serial or quad mode).
>>
>>
> I agree with you, this could be an improvement for some spi controller drivers :)
>
>> Jonas
>>
>
> thanks for your review!
>
> Best regards,
>
> Cyrille


Regards
Jonas