On 07/29/2017 12:07 AM, Alexandru Gagniuc wrote:Hi Marek,
Add support for the QSPI controller found in Adaptrum Anarion SOCs.
This controller is designed specifically to handle SPI NOR chips, and
the driver is modeled as such.
Because the system is emulated on an FPGA, we don't have a way to test
all the hardware adjustemts, so only basic features are implemented at
this time.
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/anarion-quadspi.txt
@@ -0,0 +1,22 @@
+* Adaptrum Anarion Quad SPI controller
+
+Required properties:
+- compatible : Should be "adaptrum,anarion-qspi".
+- reg : Contains two entries, each of which is a tuple consisting of a
+ physical address and length. The first entry is the address and
+ length of the controller register set. The second entry is the
+ address and length of the memory-mapped flash. This second region is
+ the region where the controller responds to XIP requests, and may be
+ larger than the size of the attached flash.
You want to split the bindings into separate patch and CC Rob to review
them.
diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 293c8a4..98dc012 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -48,6 +48,13 @@ config SPI_ATMEL_QUADSPI
This driver does not support generic SPI. The implementation only
supports SPI NOR.
+config SPI_ANARION_QUADSPI
+ tristate "Adaptrum Anarion Quad SPI Controller"
+ depends on OF && HAS_IOMEM
+ help
+ Enable support for the Adaptrum Anarion Quad SPI controller.
+ This driver does not support generic SPI. It only supports SPI NOR.
Keep the list sorted.
config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller"
depends on OF && (ARM || COMPILE_TEST)
diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile
index 285aab8..53635f6 100644
--- a/drivers/mtd/spi-nor/Makefile
+++ b/drivers/mtd/spi-nor/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
+obj-$(CONFIG_SPI_ANARION_QUADSPI) += anarion-quadspi.o
DTTO, N is before S and T .
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
diff --git a/drivers/mtd/spi-nor/anarion-quadspi.c b/drivers/mtd/spi-nor/anarion-quadspi.c
new file mode 100644
index 0000000..d981356
--- /dev/null
+++ b/drivers/mtd/spi-nor/anarion-quadspi.c
@@ -0,0 +1,490 @@
+/*
+ * Adaptrum Anarion Quad SPI controller driver
+ *
+ * Copyright (C) 2017, Adaptrum, Inc.
+ * (Written by Alexandru Gagniuc <alex.g at adaptrum.com> for Adaptrum, Inc.)
+ * Licensed under the GPLv2 or (at your option) any later version.
The GPL boilerplate should be here.
+#define ASPI_CLK_SW_RESET (1 << 0)
BIT(0) , fix globally
+#define ASPI_CLK_RESET_BUF (1 << 1)
+#define ASPI_CLK_RESET_ALL (ASPI_CLK_SW_RESET | ASPI_CLK_RESET_BUF)
+#define ASPI_CLK_SPI_MODE3 (1 << 2)
+#define ASPI_CLOCK_DIV_MASK (0xff << 8)
+#define ASPI_CLOCK_DIV(d) (((d) << 8) & ASPI_CLOCK_DIV_MASK)
+
+#define ASPI_TIMEOUT_US 100000
+
+#define ASPI_DATA_LEN_MASK 0x3fff
+#define ASPI_MAX_XFER_LEN (size_t)(ASPI_DATA_LEN_MASK + 1)
+
+#define MODE_IO_X1 (0 << 16)
+#define MODE_IO_X2 (1 << 16)
+#define MODE_IO_X4 (2 << 16)
+#define MODE_IO_SDR_POS_SKEW (0 << 20)
+#define MODE_IO_SDR_NEG_SKEW (1 << 20)
+#define MODE_IO_DDR_34_SKEW (2 << 20)
+#define MODE_IO_DDR_PN_SKEW (3 << 20)
+#define MODE_IO_DDR_DQS (5 << 20)
+
+#define ASPI_STATUS_BUSY (1 << 2)
+
+/*
+ * This mask does not match reality. Get over it:
What is this about ?
+ * DATA2: 0x3fff
+ * CMD2: 0x0003
+ * ADDR2: 0x0007
+ * PERF2: 0x0000
+ * HI_Z: 0x003f
+ * BCNT: 0x0007
+ */
+#define CHAIN_LEN(x) ((x - 1) & ASPI_DATA_LEN_MASK)
+
+struct anarion_qspi {
+ struct spi_nor nor;
+ struct device *dev;
+ uintptr_t regbase;
Should be void __iomem * I guess ?
+ uintptr_t xipbase;
+ uint32_t xfer_mode_cmd;
u32 etc, fix globally, this is not userspace.
+static void aspi_drain_fifo(struct anarion_qspi *aspi, uint8_t *buf, size_t len)
+{
+ uint32_t data;
Is this stuff below something like ioread32_rep() ?
+ aspi_write_reg(aspi, ASPI_REG_BYTE_COUNT, sizeof(uint32_t));
+ while (len >= 4) {
+ data = aspi_read_reg(aspi, ASPI_REG_DATA1);
+ memcpy(buf, &data, sizeof(data));
+ buf += 4;
+ len -= 4;
+ }
+ if (len) {
+ aspi_write_reg(aspi, ASPI_REG_BYTE_COUNT, len);
+ data = aspi_read_reg(aspi, ASPI_REG_DATA1);
+ memcpy(buf, &data, len);
+ }
+}
+
+static void aspi_seed_fifo(struct anarion_qspi *spi,
+ const uint8_t *buf, size_t len)
+{
+ uint32_t data;
+
+ aspi_write_reg(spi, ASPI_REG_BYTE_COUNT, sizeof(uint32_t));
+ while (len >= 4) {
+ memcpy(&data, buf, sizeof(data));
+ aspi_write_reg(spi, ASPI_REG_DATA1, data);
iowrite32_rep ?
+/* While we could send read commands manually to the flash chip, we'd have to
+ * get data back through the DATA2 register. That is on the AHB bus, whereas
+ * XIP reads go over AXI. Hence, we use the memory-mapped flash space for read.
+ * TODO: Look at using DMA instead of memcpy().
+ */
Multiline comment looks like this,
/*
* foo
* bar
*/
OOPS! That wasn't supposed to be there.+static ssize_t anarion_spi_nor_write(struct spi_nor *nor, loff_t to,
+ size_t len, const uint8_t *src)
+{
+ int ret;
+ struct anarion_qspi *aspi = nor->priv;
+
+ dev_err(aspi->dev, "%s, @0x%llx + %zu\n", __func__, to, len);
Drop this.
+ switch (nor->flash_read) {
+ default: /* Fall through */
This will break once we add OSPI support ...
+ case SPI_NOR_NORMAL:
+ aspi->num_hi_z_clocks = nor->read_dummy;
+ aspi->xfer_mode_cmd = MODE_IO_X1;
+ aspi->xfer_mode_addr = MODE_IO_X1;
+ aspi->xfer_mode_data = MODE_IO_X1;
+ break;
+ case SPI_NOR_FAST:
+ aspi->num_hi_z_clocks = nor->read_dummy;
+ aspi->xfer_mode_cmd = MODE_IO_X1;
+ aspi->xfer_mode_addr = MODE_IO_X1;
+ aspi->xfer_mode_data = MODE_IO_X1;
+ break;
+ case SPI_NOR_DUAL:
+ aspi->num_hi_z_clocks = nor->read_dummy;
+ aspi->xfer_mode_cmd = MODE_IO_X1;
+ aspi->xfer_mode_addr = MODE_IO_X1;
+ aspi->xfer_mode_data = MODE_IO_X2;
+ break;
+ case SPI_NOR_QUAD:
+ aspi->num_hi_z_clocks = nor->read_dummy;
+ aspi->xfer_mode_cmd = MODE_IO_X1;
+ aspi->xfer_mode_addr = MODE_IO_X1;
+ aspi->xfer_mode_data = MODE_IO_X4;
+ break;
+ }
+
+ aspi_setup_xip_read_chain(aspi, nor);
+
+ mtd_device_register(&aspi->nor.mtd, NULL, 0);
+
+ return 0;
+}