linux-mtd.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/5] RFC for Zynq QSPI
@ 2018-03-23 12:21 Naga Sureshkumar Relli
  2018-03-23 12:21 ` [RFC PATCH 1/5] spi: Add support for Zynq qspi controller Naga Sureshkumar Relli
                   ` (6 more replies)
  0 siblings, 7 replies; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-03-23 12:21 UTC (permalink / raw)
  To: cyrille.pitchen, marek.vasut, dwmw2, computersforpeace, boris.brezillon
  Cc: linux-mtd, Naga Sureshkumar Relli

Xilinx Zynq uses a QSPI controller that is based on the Cadence SPI IP.
This controller implements all the functionality required to support
Quad SPI NOR flash devices.
This driver along with the MTD layer is used to support flash devices.

This series is for the following purposes:
- RFC of the Quad SPI driver.
- We currently use a custom MTD layer and would like to get inputs on
  dual stacked/dual parallel handling (described below).

The flash device(s) can be connected in the three configurations:
1. Single - One flash device with 1 CS, 1 Clock and 4 IO lines.
2. Dual Parallel - Two flash devices connected with common CS and
   separate IO lines (resulting in 8 IO lines).
   In this configuration, the controller
   a) Duplicates commands, address etc. sent on both sets of 4 IO lines.
   b) Stripes data both transmitted and received i.e.
      4 bits of data is sent to the first flash and the other 4 bits
      to the second flash. Similarly read data is also consolidated.
   Due to this, TX and RX data handling in the driver need special
   handling for parallel mode.
3. Dual Stacked - Two flash devices connected with separate CS and
   4 common IO lines. This is largely similar to single, except for
   the slave selection logic.
The above configuration is conveyed to the QSPI driver through a
devicetree property.

The QSPI driver differs from the existing Cadence SPI driver in
the following respects majorly:
1. TX and RX handling: Different TX registers are used to write into
   the TX FIFO. TXD0, TXD1, TXD2 and TXD3 are used write 4, 1, 2 and 3
   bytes respectively. Depending on the TXD register used, the received
   bytes also need to be handled separately.
2. Depending on the configuration in which flash devices are connected
   (single, parallel or stacked), QSPI controller configuration registers
   need to be modified.
3. There is no support for extended slave select in QSPI, as opposed to
   SPI. In case of stacked configuration, the slave select field remains
   the same and a different configuration bit is used to select between
   the two flash devices.
4. Handling of dual parallel configuration.

MTD layer:
The Xilinx Zynq MTD layer by far makes use of the mainline version with
some differences. The primary flash families supported are
Spansion, Winbond and Micron.
- Probe:
  - In dual configurations, both flash devices are recognized as one
    continuous memory. (ID is read only from one flash and it is a 
    pre-stated assumption that both flash devices have the same flash
    make and size.)
- Addressing:
  a) In dual stacked mode, the address passed to the MTD layer can be
     between 0x0 to 2*(one flash size). Hence the MTD layer has to recognize
     whether the address belongs to the first flash or the second flash
     subtract the offset and indicate the same to the QSPI driver.
  b) In dual stacked mode too the address can range between
     0 to 2*(one flash size). But, when an 8 bit word is written,
     4 bits are written to the first and 4 bits are written to the
     second flash. Hence the address sent is always halved and checks
     are in place for even address and even length.
- 4 byte addressing is not supported and hence bank selection logic is used
  along with the addressing system described above.
- Flash register read/writes, for example, lock/unlock, quad enable etc.
  are handled differently in dual stacked and parallel modes.

This is tested with current master branch of Linux.
To make Zynq QSPI work, we need to tweak spi-nor.c.
I agree that changing the spi-nor layer apis, doesn't look good but
i didn't find a way to do the same.
So please suggest a way to handle Zynq QSPI controller in dual parallel and 
stacked modes.
we can move the spi/spi-zynq-qspi.c to spi-nor/ but even in that case
also changes are needed in spi-nor.c.
What ever the changes we do to make Zynq QSPI work, will applicable for 
Xilinx ZynqMP SOC QSPI controller(spi-zynqmp-qspi.c) as well.
I am happy to do the changes suggested, so that we will have a solution
for Xilinx Zynq and ZynqMP SOC QSPI controllers.

Naga Sureshkumar Relli (5):
  spi: Add support for Zynq qspi controller
  mtd: spi-nor: Add support for Zynq QSPI controller
  mtd: spi-nor: Add Dual Parallel and Stacked support for Zynq QSPI
  spi: Add PM Support for Zynq QSPI controller
  devicetree: Add devicetree bindings documentation for Zynq QSPI

 .../devicetree/bindings/spi/spi-zynq-qspi.txt      |  26 +
 drivers/mtd/spi-nor/spi-nor.c                      | 425 ++++++++++-
 drivers/spi/Kconfig                                |  15 +
 drivers/spi/Makefile                               |   1 +
 drivers/spi/spi-zynq-qspi.c                        | 827 +++++++++++++++++++++
 include/linux/mtd/spi-nor.h                        |  15 +-
 include/linux/spi/spi.h                            |   5 +-
 7 files changed, 1276 insertions(+), 38 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt
 create mode 100644 drivers/spi/spi-zynq-qspi.c

-- 
2.7.4

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

* [RFC PATCH 1/5] spi: Add support for Zynq qspi controller
  2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
@ 2018-03-23 12:21 ` Naga Sureshkumar Relli
  2018-03-23 12:21 ` [RFC PATCH 2/5] mtd: spi-nor: Add support for Zynq QSPI controller Naga Sureshkumar Relli
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-03-23 12:21 UTC (permalink / raw)
  To: cyrille.pitchen, marek.vasut, dwmw2, computersforpeace, boris.brezillon
  Cc: linux-mtd, Naga Sureshkumar Relli

This patch adds support for QSPI controller driver used by
Xilinx Zynq SOC.

Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com>
---
 drivers/spi/Kconfig         |  15 +
 drivers/spi/Makefile        |   1 +
 drivers/spi/spi-zynq-qspi.c | 769 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 785 insertions(+)
 create mode 100644 drivers/spi/spi-zynq-qspi.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6037839..75b8f13 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -762,6 +762,21 @@ config SPI_ZYNQMP_GQSPI
 	help
 	  Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
 
+config SPI_ZYNQ_QSPI
+	tristate "Xilinx Zynq QSPI controller"
+	depends on ARCH_ZYNQ
+	depends on SPI_MASTER
+	help
+	 This selects the Xilinx ZYNQ Quad SPI controller master driver.
+
+config SPI_ZYNQ_QSPI_DUAL_STACKED
+	bool "Xilinx Zynq QSPI Dual stacked configuration"
+	depends on SPI_ZYNQ_QSPI
+	help
+	 This selects the Xilinx ZYNQ Quad SPI controller in dual stacked mode.
+	 Enable this option if your hw design is using dual stacked
+	 configuration.
+
 #
 # Add new SPI master controllers in alphabetical order above this line
 #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 34c5f28..b49cff6 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_SPI_XILINX)		+= spi-xilinx.o
 obj-$(CONFIG_SPI_XLP)			+= spi-xlp.o
 obj-$(CONFIG_SPI_XTENSA_XTFPGA)		+= spi-xtensa-xtfpga.o
 obj-$(CONFIG_SPI_ZYNQMP_GQSPI)		+= spi-zynqmp-gqspi.o
+obj-$(CONFIG_SPI_ZYNQ_QSPI)		+= spi-zynq-qspi.o
 
 # SPI slave protocol handlers
 obj-$(CONFIG_SPI_SLAVE_TIME)		+= spi-slave-time.o
diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c
new file mode 100644
index 0000000..7a772ca
--- /dev/null
+++ b/drivers/spi/spi-zynq-qspi.c
@@ -0,0 +1,769 @@
+
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx Zynq Quad-SPI (QSPI) controller driver (master mode only)
+ *
+ * Copyright (C) 2017 Xilinx, Inc.
+ * Author: harini katakam <harinik@xilinx.com>
+ * Author: nagasuresh <nagasure@xilinx.com>
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+
+/* Name of this driver */
+#define DRIVER_NAME			"zynq-qspi"
+
+/* Register offset definitions */
+#define ZYNQ_QSPI_CONFIG_OFFSET		0x00 /* Configuration  Register, RW */
+#define ZYNQ_QSPI_STATUS_OFFSET		0x04 /* Interrupt Status Register, RO */
+#define ZYNQ_QSPI_IEN_OFFSET		0x08 /* Interrupt Enable Register, WO */
+#define ZYNQ_QSPI_IDIS_OFFSET		0x0C /* Interrupt Disable Reg, WO */
+#define ZYNQ_QSPI_IMASK_OFFSET		0x10 /* Interrupt Enabled Mask Reg,RO */
+#define ZYNQ_QSPI_ENABLE_OFFSET		0x14 /* Enable/Disable Register, RW */
+#define ZYNQ_QSPI_DELAY_OFFSET		0x18 /* Delay Register, RW */
+#define ZYNQ_QSPI_TXD_00_00_OFFSET	0x1C /* Transmit 4-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_01_OFFSET	0x80 /* Transmit 1-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_10_OFFSET	0x84 /* Transmit 2-byte inst, WO */
+#define ZYNQ_QSPI_TXD_00_11_OFFSET	0x88 /* Transmit 3-byte inst, WO */
+#define ZYNQ_QSPI_RXD_OFFSET		0x20 /* Data Receive Register, RO */
+#define ZYNQ_QSPI_SIC_OFFSET		0x24 /* Slave Idle Count Register, RW */
+#define ZYNQ_QSPI_TX_THRESH_OFFSET	0x28 /* TX FIFO Watermark Reg, RW */
+#define ZYNQ_QSPI_RX_THRESH_OFFSET	0x2C /* RX FIFO Watermark Reg, RW */
+#define ZYNQ_QSPI_GPIO_OFFSET		0x30 /* GPIO Register, RW */
+#define ZYNQ_QSPI_LINEAR_CFG_OFFSET	0xA0 /* Linear Adapter Config Ref, RW */
+#define ZYNQ_QSPI_MOD_ID_OFFSET		0xFC /* Module ID Register, RO */
+
+/*
+ * QSPI Configuration Register bit Masks
+ *
+ * This register contains various control bits that effect the operation
+ * of the QSPI controller
+ */
+#define ZYNQ_QSPI_CONFIG_IFMODE_MASK	0x80000000 /* Flash Memory Interface */
+#define ZYNQ_QSPI_CONFIG_MANSRT_MASK	0x00010000 /* Manual TX Start */
+#define ZYNQ_QSPI_CONFIG_MANSRTEN_MASK	0x00008000 /* Enable Manual TX Mode */
+#define ZYNQ_QSPI_CONFIG_SSFORCE_MASK	0x00004000 /* Manual Chip Select */
+#define ZYNQ_QSPI_CONFIG_BDRATE_MASK	0x00000038 /* Baud Rate Divisor Mask */
+#define ZYNQ_QSPI_CONFIG_CPHA_MASK	0x00000004 /* Clock Phase Control */
+#define ZYNQ_QSPI_CONFIG_CPOL_MASK	0x00000002 /* Clock Polarity Control */
+#define ZYNQ_QSPI_CONFIG_SSCTRL_MASK	0x00003C00 /* Slave Select Mask */
+#define ZYNQ_QSPI_CONFIG_FWIDTH_MASK	0x000000C0 /* FIFO width */
+#define ZYNQ_QSPI_CONFIG_MSTREN_MASK	0x00000001 /* Master Mode */
+
+/*
+ * QSPI Configuration Register - Baud rate and slave select
+ *
+ * These are the values used in the calculation of baud rate divisor and
+ * setting the slave select.
+ */
+#define ZYNQ_QSPI_BAUD_DIV_MAX		7 /* Baud rate divisor maximum */
+#define ZYNQ_QSPI_BAUD_DIV_SHIFT	3 /* Baud rate divisor shift in CR */
+#define ZYNQ_QSPI_SS_SHIFT		10 /* Slave Select field shift in CR */
+
+/*
+ * QSPI Interrupt Registers bit Masks
+ *
+ * All the four interrupt registers (Status/Mask/Enable/Disable) have the same
+ * bit definitions.
+ */
+#define ZYNQ_QSPI_IXR_TXNFULL_MASK	0x00000004 /* QSPI TX FIFO Overflow */
+#define ZYNQ_QSPI_IXR_TXFULL_MASK	0x00000008 /* QSPI TX FIFO is full */
+#define ZYNQ_QSPI_IXR_RXNEMTY_MASK	0x00000010 /* QSPI RX FIFO Not Empty */
+#define ZYNQ_QSPI_IXR_ALL_MASK		(ZYNQ_QSPI_IXR_TXNFULL_MASK | \
+					ZYNQ_QSPI_IXR_RXNEMTY_MASK)
+
+/*
+ * QSPI Enable Register bit Masks
+ *
+ * This register is used to enable or disable the QSPI controller
+ */
+#define ZYNQ_QSPI_ENABLE_ENABLE_MASK	0x00000001 /* QSPI Enable Bit Mask */
+
+/*
+ * QSPI Linear Configuration Register
+ *
+ * It is named Linear Configuration but it controls other modes when not in
+ * linear mode also.
+ */
+#define ZYNQ_QSPI_LCFG_TWO_MEM_MASK	0x40000000 /* LQSPI Two memories Mask */
+#define ZYNQ_QSPI_LCFG_SEP_BUS_MASK	0x20000000 /* LQSPI Separate bus Mask */
+#define ZYNQ_QSPI_LCFG_U_PAGE_MASK	0x10000000 /* LQSPI Upper Page Mask */
+
+#define ZYNQ_QSPI_LCFG_DUMMY_SHIFT	8
+
+#define ZYNQ_QSPI_FAST_READ_QOUT_CODE	0x6B /* read instruction code */
+#define ZYNQ_QSPI_FIFO_DEPTH		63 /* FIFO depth in words */
+#define ZYNQ_QSPI_RX_THRESHOLD		32 /* Rx FIFO threshold level */
+#define ZYNQ_QSPI_TX_THRESHOLD		1 /* Tx FIFO threshold level */
+
+/*
+ * The modebits configurable by the driver to make the SPI support different
+ * data formats
+ */
+#define MODEBITS			(SPI_CPOL | SPI_CPHA)
+
+/* Default number of chip selects */
+#define ZYNQ_QSPI_DEFAULT_NUM_CS	1
+
+/**
+ * struct zynq_qspi - Defines qspi driver instance
+ * @regs:		Virtual address of the QSPI controller registers
+ * @refclk:		Pointer to the peripheral clock
+ * @pclk:		Pointer to the APB clock
+ * @irq:		IRQ number
+ * @txbuf:		Pointer to the TX buffer
+ * @rxbuf:		Pointer to the RX buffer
+ * @bytes_to_transfer:	Number of bytes left to transfer
+ * @bytes_to_receive:	Number of bytes left to receive
+ * @is_dual:		Flag to indicate whether dual flash memories are used
+ * @is_instr:		Flag to indicate if transfer contains an instruction
+ *			(Used in dual parallel configuration)
+ */
+struct zynq_qspi {
+	void __iomem *regs;
+	struct clk *refclk;
+	struct clk *pclk;
+	int irq;
+	const void *txbuf;
+	void *rxbuf;
+	int bytes_to_transfer;
+	int bytes_to_receive;
+	u32 is_dual;
+	u8 is_instr;
+};
+
+/*
+ * Inline functions for the QSPI controller read/write
+ */
+static inline u32 zynq_qspi_read(struct zynq_qspi *xqspi, u32 offset)
+{
+	return readl_relaxed(xqspi->regs + offset);
+}
+
+static inline void zynq_qspi_write(struct zynq_qspi *xqspi, u32 offset,
+				   u32 val)
+{
+	writel_relaxed(val, xqspi->regs + offset);
+}
+
+/**
+ * zynq_qspi_init_hw - Initialize the hardware
+ * @xqspi:	Pointer to the zynq_qspi structure
+ *
+ * The default settings of the QSPI controller's configurable parameters on
+ * reset are
+ *	- Master mode
+ *	- Baud rate divisor is set to 2
+ *	- Tx threshold set to 1l Rx threshold set to 32
+ *	- Flash memory interface mode enabled
+ *	- Size of the word to be transferred as 8 bit
+ * This function performs the following actions
+ *	- Disable and clear all the interrupts
+ *	- Enable manual slave select
+ *	- Enable manual start
+ *	- Deselect all the chip select lines
+ *	- Set the size of the word to be transferred as 32 bit
+ *	- Set the little endian mode of TX FIFO and
+ *	- Enable the QSPI controller
+ */
+static void zynq_qspi_init_hw(struct zynq_qspi *xqspi)
+{
+	u32 config_reg;
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0);
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_IDIS_OFFSET, 0x7F);
+
+	/* Disable linear mode as the boot loader may have used it */
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET, 0);
+
+	/* Clear the RX FIFO */
+	while (zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET) &
+			      ZYNQ_QSPI_IXR_RXNEMTY_MASK)
+		zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, 0x7F);
+	config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+	config_reg &= ~(ZYNQ_QSPI_CONFIG_MSTREN_MASK |
+			ZYNQ_QSPI_CONFIG_CPOL_MASK |
+			ZYNQ_QSPI_CONFIG_CPHA_MASK |
+			ZYNQ_QSPI_CONFIG_BDRATE_MASK |
+			ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
+			ZYNQ_QSPI_CONFIG_MANSRTEN_MASK |
+			ZYNQ_QSPI_CONFIG_MANSRT_MASK);
+	config_reg |= (ZYNQ_QSPI_CONFIG_MSTREN_MASK |
+		       ZYNQ_QSPI_CONFIG_SSFORCE_MASK |
+		       ZYNQ_QSPI_CONFIG_FWIDTH_MASK |
+		       ZYNQ_QSPI_CONFIG_IFMODE_MASK);
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_RX_THRESH_OFFSET,
+			ZYNQ_QSPI_RX_THRESHOLD);
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_TX_THRESH_OFFSET,
+			ZYNQ_QSPI_TX_THRESHOLD);
+
+	if (xqspi->is_dual)
+		/* Enable two memories on separate buses */
+		zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET,
+				(ZYNQ_QSPI_LCFG_TWO_MEM_MASK |
+				ZYNQ_QSPI_LCFG_SEP_BUS_MASK |
+				(1 << ZYNQ_QSPI_LCFG_DUMMY_SHIFT) |
+				ZYNQ_QSPI_FAST_READ_QOUT_CODE));
+#ifdef CONFIG_SPI_ZYNQ_QSPI_DUAL_STACKED
+	/* Enable two memories on shared bus */
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET,
+			(ZYNQ_QSPI_LCFG_TWO_MEM_MASK |
+			(1 << ZYNQ_QSPI_LCFG_DUMMY_SHIFT) |
+			ZYNQ_QSPI_FAST_READ_QOUT_CODE));
+#endif
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET,
+			ZYNQ_QSPI_ENABLE_ENABLE_MASK);
+}
+
+/**
+ * zynq_qspi_read_rx_fifo - Read 1..4 bytes from RxFIFO to RX buffer
+ * @xqspi:	Pointer to the zynq_qspi structure
+ * @size:	Number of bytes to be read (1..4)
+ *
+ * Note: In case of dual parallel connection, even number of bytes are read
+ * when odd bytes are requested to avoid transfer of a nibble to each flash.
+ * The receive buffer though, is populated with the number of bytes requested.
+ */
+static void zynq_qspi_read_rx_fifo(struct zynq_qspi *xqspi, unsigned int size)
+{
+	unsigned int xsize;
+	u32 data;
+
+	data = zynq_qspi_read(xqspi, ZYNQ_QSPI_RXD_OFFSET);
+
+	if (xqspi->rxbuf) {
+		xsize = size;
+		if (xqspi->is_dual && !xqspi->is_instr && (size % 2))
+			xsize++;
+		memcpy(xqspi->rxbuf, ((u8 *)&data) + 4 - xsize, size);
+		xqspi->rxbuf += size;
+	}
+
+	xqspi->bytes_to_receive -= size;
+	if (xqspi->bytes_to_receive < 0)
+		xqspi->bytes_to_receive = 0;
+}
+
+/**
+ * zynq_qspi_write_tx_fifo - Write 1..4 bytes from TX buffer to TxFIFO
+ * @xqspi:	Pointer to the zynq_qspi structure
+ * @size:	Number of bytes to be written (1..4)
+ *
+ * In dual parallel configuration, when read/write data operations
+ * are performed, odd data bytes have to be converted to even to
+ * avoid a nibble (of data when programming / dummy when reading)
+ * going to individual flash devices, where a byte is expected.
+ * This check is only for data and will not apply for commands.
+ */
+static void zynq_qspi_write_tx_fifo(struct zynq_qspi *xqspi, unsigned int size)
+{
+	static const unsigned int offset[4] = {
+		ZYNQ_QSPI_TXD_00_01_OFFSET, ZYNQ_QSPI_TXD_00_10_OFFSET,
+		ZYNQ_QSPI_TXD_00_11_OFFSET, ZYNQ_QSPI_TXD_00_00_OFFSET };
+	unsigned int xsize;
+	u32 data;
+
+	if (xqspi->txbuf) {
+		data = 0xffffffff;
+		memcpy(&data, xqspi->txbuf, size);
+		xqspi->txbuf += size;
+	} else {
+		data = 0;
+	}
+
+	xqspi->bytes_to_transfer -= size;
+
+	xsize = size;
+	if (xqspi->is_dual && !xqspi->is_instr && (size % 2))
+		xsize++;
+	zynq_qspi_write(xqspi, offset[xsize - 1], data);
+}
+
+/**
+ * zynq_prepare_transfer_hardware - Prepares hardware for transfer.
+ * @master:	Pointer to the spi_master structure which provides
+ *		information about the controller.
+ *
+ * This function enables SPI master controller.
+ *
+ * Return:	Always 0
+ */
+static int zynq_prepare_transfer_hardware(struct spi_master *master)
+{
+	struct zynq_qspi *xqspi = spi_master_get_devdata(master);
+
+	clk_enable(xqspi->refclk);
+	clk_enable(xqspi->pclk);
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET,
+			ZYNQ_QSPI_ENABLE_ENABLE_MASK);
+
+	return 0;
+}
+
+/**
+ * zynq_unprepare_transfer_hardware - Relaxes hardware after transfer
+ * @master:	Pointer to the spi_master structure which provides
+ *		information about the controller.
+ *
+ * This function disables the SPI master controller.
+ *
+ * Return:	Always 0
+ */
+static int zynq_unprepare_transfer_hardware(struct spi_master *master)
+{
+	struct zynq_qspi *xqspi = spi_master_get_devdata(master);
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0);
+	clk_disable(xqspi->refclk);
+	clk_disable(xqspi->pclk);
+
+	return 0;
+}
+
+/**
+ * zynq_qspi_chipselect - Select or deselect the chip select line
+ * @qspi:	Pointer to the spi_device structure
+ * @is_high:	Select(0) or deselect (1) the chip select line
+ */
+static void zynq_qspi_chipselect(struct spi_device *qspi, bool is_high)
+{
+	struct zynq_qspi *xqspi = spi_master_get_devdata(qspi->master);
+	u32 config_reg;
+#ifdef CONFIG_SPI_ZYNQ_QSPI_DUAL_STACKED
+	u32 lqspi_cfg_reg;
+#endif
+
+	config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+
+	/* Select upper/lower page before asserting CS */
+#ifdef CONFIG_SPI_ZYNQ_QSPI_DUAL_STACKED
+		lqspi_cfg_reg = zynq_qspi_read(xqspi,
+					       ZYNQ_QSPI_LINEAR_CFG_OFFSET);
+		if (qspi->master->flags & SPI_MASTER_U_PAGE)
+			lqspi_cfg_reg |= ZYNQ_QSPI_LCFG_U_PAGE_MASK;
+		else
+			lqspi_cfg_reg &= ~ZYNQ_QSPI_LCFG_U_PAGE_MASK;
+		zynq_qspi_write(xqspi, ZYNQ_QSPI_LINEAR_CFG_OFFSET,
+				lqspi_cfg_reg);
+#endif
+
+	if (is_high) {
+		/* Deselect the slave */
+		config_reg |= ZYNQ_QSPI_CONFIG_SSCTRL_MASK;
+	} else {
+		/* Select the slave */
+		config_reg &= ~ZYNQ_QSPI_CONFIG_SSCTRL_MASK;
+		config_reg |= (((~(BIT(qspi->chip_select))) <<
+				 ZYNQ_QSPI_SS_SHIFT) &
+				 ZYNQ_QSPI_CONFIG_SSCTRL_MASK);
+		xqspi->is_instr = 1;
+	}
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+}
+
+/**
+ * zynq_qspi_setup_transfer - Configure QSPI controller for specified transfer
+ * @qspi:	Pointer to the spi_device structure
+ * @transfer:	Pointer to the spi_transfer structure which provides information
+ *		about next transfer setup parameters
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer and
+ * sets the requested clock frequency.
+ *
+ * Return:	0 on success and -EINVAL on invalid input parameter
+ *
+ * Note: If the requested frequency is not an exact match with what can be
+ * obtained using the prescalar value, the driver sets the clock frequency which
+ * is lower than the requested frequency (maximum lower) for the transfer. If
+ * the requested frequency is higher or lower than that is supported by the QSPI
+ * controller the driver will set the highest or lowest frequency supported by
+ * controller.
+ */
+static int zynq_qspi_setup_transfer(struct spi_device *qspi,
+				    struct spi_transfer *transfer)
+{
+	struct zynq_qspi *xqspi = spi_master_get_devdata(qspi->master);
+	u32 config_reg, req_hz, baud_rate_val = 0;
+
+	if (transfer)
+		req_hz = transfer->speed_hz;
+	else
+		req_hz = qspi->max_speed_hz;
+
+	/* Set the clock frequency */
+	/* If req_hz == 0, default to lowest speed */
+	while ((baud_rate_val < ZYNQ_QSPI_BAUD_DIV_MAX)  &&
+	       (clk_get_rate(xqspi->refclk) / (2 << baud_rate_val)) > req_hz)
+		baud_rate_val++;
+
+	config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
+
+	/* Set the QSPI clock phase and clock polarity */
+	config_reg &= (~ZYNQ_QSPI_CONFIG_CPHA_MASK) &
+		      (~ZYNQ_QSPI_CONFIG_CPOL_MASK);
+	if (qspi->mode & SPI_CPHA)
+		config_reg |= ZYNQ_QSPI_CONFIG_CPHA_MASK;
+	if (qspi->mode & SPI_CPOL)
+		config_reg |= ZYNQ_QSPI_CONFIG_CPOL_MASK;
+
+	config_reg &= ~ZYNQ_QSPI_CONFIG_BDRATE_MASK;
+	config_reg |= (baud_rate_val << ZYNQ_QSPI_BAUD_DIV_SHIFT);
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_CONFIG_OFFSET, config_reg);
+
+	return 0;
+}
+
+/**
+ * zynq_qspi_setup - Configure the QSPI controller
+ * @qspi:	Pointer to the spi_device structure
+ *
+ * Sets the operational mode of QSPI controller for the next QSPI transfer, baud
+ * rate and divisor value to setup the requested qspi clock.
+ *
+ * Return:	0 on success and error value on failure
+ */
+static int zynq_qspi_setup(struct spi_device *qspi)
+{
+	if (qspi->master->busy)
+		return -EBUSY;
+
+	return zynq_qspi_setup_transfer(qspi, NULL);
+}
+
+/**
+ * zynq_qspi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible
+ * @xqspi:	Pointer to the zynq_qspi structure
+ * @txcount:	Maximum number of words to write
+ * @txempty:	Indicates that TxFIFO is empty
+ */
+static void zynq_qspi_fill_tx_fifo(struct zynq_qspi *xqspi, int txcount,
+				   bool txempty)
+{
+	int count, len, k;
+
+	len = xqspi->bytes_to_transfer;
+	if (len && len < 4) {
+		/*
+		 * We must empty the TxFIFO between accesses to TXD0,
+		 * TXD1, TXD2, TXD3.
+		 */
+		if (txempty)
+			zynq_qspi_write_tx_fifo(xqspi, len);
+		return;
+	}
+
+	count = len / 4;
+	if (count > txcount)
+		count = txcount;
+
+	if (xqspi->txbuf) {
+		writesl(xqspi->regs + ZYNQ_QSPI_TXD_00_00_OFFSET,
+			xqspi->txbuf, count);
+		xqspi->txbuf += count * 4;
+	} else {
+		for (k = 0; k < count; k++)
+			writel_relaxed(0, xqspi->regs +
+					  ZYNQ_QSPI_TXD_00_00_OFFSET);
+	}
+	xqspi->bytes_to_transfer -= count * 4;
+}
+
+/**
+ * zynq_qspi_drain_rx_fifo - Drains the RX FIFO by as many bytes as possible
+ * @xqspi:	Pointer to the zynq_qspi structure
+ * @rxcount:	Maximum number of words to read
+ */
+static void zynq_qspi_drain_rx_fifo(struct zynq_qspi *xqspi, int rxcount)
+{
+	int count, len, k;
+
+	len = xqspi->bytes_to_receive - xqspi->bytes_to_transfer;
+	count = len / 4;
+	if (count > rxcount)
+		count = rxcount;
+
+	if (xqspi->rxbuf) {
+		readsl(xqspi->regs + ZYNQ_QSPI_RXD_OFFSET,
+		       xqspi->rxbuf, count);
+		xqspi->rxbuf += count * 4;
+	} else {
+		for (k = 0; k < count; k++)
+			readl_relaxed(xqspi->regs + ZYNQ_QSPI_RXD_OFFSET);
+	}
+	xqspi->bytes_to_receive -= count * 4;
+	len -= count * 4;
+
+	if (len && len < 4 && count < rxcount)
+		zynq_qspi_read_rx_fifo(xqspi, len);
+}
+
+/**
+ * zynq_qspi_irq - Interrupt service routine of the QSPI controller
+ * @irq:	IRQ number
+ * @dev_id:	Pointer to the xqspi structure
+ *
+ * This function handles TX empty only.
+ * On TX empty interrupt this function reads the received data from RX FIFO and
+ * fills the TX FIFO if there is any data remaining to be transferred.
+ *
+ * Return:	IRQ_HANDLED when interrupt is handled; IRQ_NONE otherwise.
+ */
+static irqreturn_t zynq_qspi_irq(int irq, void *dev_id)
+{
+	struct spi_master *master = dev_id;
+	struct zynq_qspi *xqspi = spi_master_get_devdata(master);
+	u32 intr_status;
+	bool txempty;
+
+	intr_status = zynq_qspi_read(xqspi, ZYNQ_QSPI_STATUS_OFFSET);
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_STATUS_OFFSET, intr_status);
+
+	if ((intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK) ||
+	    (intr_status & ZYNQ_QSPI_IXR_RXNEMTY_MASK)) {
+		/*
+		 * This bit is set when Tx FIFO has < THRESHOLD entries.
+		 * We have the THRESHOLD value set to 1,
+		 * so this bit indicates Tx FIFO is empty.
+		 */
+		txempty = !!(intr_status & ZYNQ_QSPI_IXR_TXNFULL_MASK);
+
+		/* Read out the data from the RX FIFO */
+		zynq_qspi_drain_rx_fifo(xqspi, ZYNQ_QSPI_RX_THRESHOLD);
+
+		if (xqspi->bytes_to_transfer) {
+			/* There is more data to send */
+			zynq_qspi_fill_tx_fifo(xqspi, ZYNQ_QSPI_RX_THRESHOLD,
+					       txempty);
+		} else {
+			/*
+			 * If transfer and receive is completed then only send
+			 * complete signal.
+			 */
+			if (!xqspi->bytes_to_receive) {
+				zynq_qspi_write(xqspi,
+						ZYNQ_QSPI_IDIS_OFFSET,
+						ZYNQ_QSPI_IXR_ALL_MASK);
+				spi_finalize_current_transfer(master);
+				xqspi->is_instr = 0;
+			}
+		}
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+/**
+ * zynq_qspi_start_transfer - Initiates the QSPI transfer
+ * @master:	Pointer to the spi_master structure which provides
+ *		information about the controller.
+ * @qspi:	Pointer to the spi_device structure
+ * @transfer:	Pointer to the spi_transfer structure which provide information
+ *		about next transfer parameters
+ *
+ * This function fills the TX FIFO, starts the QSPI transfer, and waits for the
+ * transfer to be completed.
+ *
+ * Return:	Number of bytes transferred in the last transfer
+ */
+static int zynq_qspi_start_transfer(struct spi_master *master,
+				    struct spi_device *qspi,
+				    struct spi_transfer *transfer)
+{
+	struct zynq_qspi *xqspi = spi_master_get_devdata(master);
+
+	xqspi->txbuf = transfer->tx_buf;
+	xqspi->rxbuf = transfer->rx_buf;
+	xqspi->bytes_to_transfer = transfer->len;
+	xqspi->bytes_to_receive = transfer->len;
+
+	zynq_qspi_setup_transfer(qspi, transfer);
+
+	zynq_qspi_fill_tx_fifo(xqspi, ZYNQ_QSPI_FIFO_DEPTH, true);
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_IEN_OFFSET,
+			ZYNQ_QSPI_IXR_ALL_MASK);
+
+	return transfer->len;
+}
+
+/**
+ * zynq_qspi_probe - Probe method for the QSPI driver
+ * @pdev:	Pointer to the platform_device structure
+ *
+ * This function initializes the driver data structures and the hardware.
+ *
+ * Return:	0 on success and error value on failure
+ */
+static int zynq_qspi_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct spi_master *master;
+	struct zynq_qspi *xqspi;
+	struct resource *res;
+	u32 num_cs;
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
+	if (!master)
+		return -ENOMEM;
+
+	xqspi = spi_master_get_devdata(master);
+	master->dev.of_node = pdev->dev.of_node;
+	platform_set_drvdata(pdev, master);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	xqspi->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(xqspi->regs)) {
+		ret = PTR_ERR(xqspi->regs);
+		goto remove_master;
+	}
+
+	if (of_property_read_u32(pdev->dev.of_node, "is-dual",
+				 &xqspi->is_dual)) {
+		dev_warn(&pdev->dev, "couldn't determine configuration info");
+		dev_warn(&pdev->dev, "about dual memories. defaulting to single memory\n");
+	}
+
+	xqspi->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(xqspi->pclk)) {
+		dev_err(&pdev->dev, "pclk clock not found.\n");
+		ret = PTR_ERR(xqspi->pclk);
+		goto remove_master;
+	}
+
+	xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk");
+	if (IS_ERR(xqspi->refclk)) {
+		dev_err(&pdev->dev, "ref_clk clock not found.\n");
+		ret = PTR_ERR(xqspi->refclk);
+		goto remove_master;
+	}
+
+	ret = clk_prepare_enable(xqspi->pclk);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to enable APB clock.\n");
+		goto remove_master;
+	}
+
+	ret = clk_prepare_enable(xqspi->refclk);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to enable device clock.\n");
+		goto clk_dis_pclk;
+	}
+
+	/* QSPI controller initializations */
+	zynq_qspi_init_hw(xqspi);
+
+	xqspi->irq = platform_get_irq(pdev, 0);
+	if (xqspi->irq <= 0) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "irq resource not found\n");
+		goto remove_master;
+	}
+	ret = devm_request_irq(&pdev->dev, xqspi->irq, zynq_qspi_irq,
+			       0, pdev->name, master);
+	if (ret != 0) {
+		ret = -ENXIO;
+		dev_err(&pdev->dev, "request_irq failed\n");
+		goto remove_master;
+	}
+
+	ret = of_property_read_u32(pdev->dev.of_node, "num-cs",
+				   &num_cs);
+	if (ret < 0)
+		master->num_chipselect = ZYNQ_QSPI_DEFAULT_NUM_CS;
+	else
+		master->num_chipselect = num_cs;
+
+	master->setup = zynq_qspi_setup;
+	master->set_cs = zynq_qspi_chipselect;
+	master->transfer_one = zynq_qspi_start_transfer;
+	master->prepare_transfer_hardware = zynq_prepare_transfer_hardware;
+	master->unprepare_transfer_hardware = zynq_unprepare_transfer_hardware;
+	master->flags = SPI_MASTER_QUAD_MODE;
+
+	master->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
+	master->bits_per_word_mask = SPI_BPW_MASK(8);
+	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
+			    SPI_TX_DUAL | SPI_TX_QUAD;
+
+	ret = spi_register_master(master);
+	if (ret) {
+		dev_err(&pdev->dev, "spi_register_master failed\n");
+		goto clk_dis_all;
+	}
+
+	return ret;
+
+clk_dis_all:
+	clk_disable_unprepare(xqspi->refclk);
+clk_dis_pclk:
+	clk_disable_unprepare(xqspi->pclk);
+remove_master:
+	spi_master_put(master);
+	return ret;
+}
+
+/**
+ * zynq_qspi_remove - Remove method for the QSPI driver
+ * @pdev:	Pointer to the platform_device structure
+ *
+ * This function is called if a device is physically removed from the system or
+ * if the driver module is being unloaded. It frees all resources allocated to
+ * the device.
+ *
+ * Return:	0 on success and error value on failure
+ */
+static int zynq_qspi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct zynq_qspi *xqspi = spi_master_get_devdata(master);
+
+	zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0);
+
+	clk_disable_unprepare(xqspi->refclk);
+	clk_disable_unprepare(xqspi->pclk);
+
+	spi_unregister_master(master);
+
+	return 0;
+}
+
+static const struct of_device_id zynq_qspi_of_match[] = {
+	{ .compatible = "xlnx,zynq-qspi-1.0", },
+	{ /* end of table */ }
+};
+MODULE_DEVICE_TABLE(of, zynq_qspi_of_match);
+
+/*
+ * zynq_qspi_driver - This structure defines the QSPI platform driver
+ */
+static struct platform_driver zynq_qspi_driver = {
+	.probe = zynq_qspi_probe,
+	.remove = zynq_qspi_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = zynq_qspi_of_match,
+	},
+};
+
+module_platform_driver(zynq_qspi_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_DESCRIPTION("Xilinx Zynq QSPI driver");
+MODULE_LICENSE("GPL");
-- 
2.7.4

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

* [RFC PATCH 2/5] mtd: spi-nor: Add support for Zynq QSPI controller
  2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
  2018-03-23 12:21 ` [RFC PATCH 1/5] spi: Add support for Zynq qspi controller Naga Sureshkumar Relli
@ 2018-03-23 12:21 ` Naga Sureshkumar Relli
  2018-03-23 12:22 ` [RFC PATCH 3/5] mtd: spi-nor: Add Dual Parallel and Stacked support for Zynq QSPI Naga Sureshkumar Relli
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-03-23 12:21 UTC (permalink / raw)
  To: cyrille.pitchen, marek.vasut, dwmw2, computersforpeace, boris.brezillon
  Cc: linux-mtd, Naga Sureshkumar Relli

This patch adds necessary changes required to support Zynq
QSPI Controller driver.

Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 194 ++++++++++++++++++++++++++++++++++++++----
 include/linux/mtd/spi-nor.h   |  13 ++-
 include/linux/spi/spi.h       |   5 +-
 3 files changed, 192 insertions(+), 20 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index d445a4d..7698a92 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -23,6 +23,7 @@
 #include <linux/of_platform.h>
 #include <linux/spi/flash.h>
 #include <linux/mtd/spi-nor.h>
+#include <linux/spi/spi.h>
 
 /* Define max times to check status register before we give up. */
 
@@ -292,6 +293,35 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
 	}
 }
 
+/**
+ * read_ear - Get the extended/bank address register value
+ * @nor:	Pointer to the flash control structure
+ *
+ * This routine reads the Extended/bank address register value
+ *
+ * Return:	Negative if error occurred.
+ */
+static int read_ear(struct spi_nor *nor, struct flash_info *info)
+{
+	int ret;
+	u8 val;
+	u8 code;
+
+	/* This is actually Spansion */
+	if (JEDEC_MFR(info) == CFI_MFR_AMD)
+		code = SPINOR_OP_BRRD;
+	/* This is actually Micron */
+	else if (JEDEC_MFR(info) == CFI_MFR_ST)
+		code = SPINOR_OP_RDEAR;
+	else
+		return -EINVAL;
+
+	ret = nor->read_reg(nor, code, &val, 1);
+	if (ret < 0)
+		return ret;
+
+	return val;
+}
 static int s3an_sr_ready(struct spi_nor *nor)
 {
 	int ret;
@@ -401,15 +431,60 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)
 }
 
 /*
+ * Update Extended Address/bank selection Register.
+ * Call with flash->lock locked.
+ */
+static int write_ear(struct spi_nor *nor, u32 addr)
+{
+	u8 code;
+	u8 ear;
+	int ret;
+	struct mtd_info *mtd = &nor->mtd;
+
+	/* Wait until finished previous write command. */
+	if (spi_nor_wait_till_ready(nor))
+		return 1;
+
+	if (mtd->size <= (0x1000000) << nor->shift)
+		return 0;
+
+	addr = addr % (u32) mtd->size;
+	ear = addr >> 24;
+
+	if (ear == nor->curbank)
+		return 0;
+
+	if (nor->jedec_id == CFI_MFR_AMD)
+		code = SPINOR_OP_BRWR;
+	if (nor->jedec_id == CFI_MFR_ST) {
+		write_enable(nor);
+		code = SPINOR_OP_WREAR;
+	}
+	nor->cmd_buf[0] = ear;
+
+	ret = nor->write_reg(nor, code, nor->cmd_buf, 1);
+	if (ret < 0)
+		return ret;
+
+	nor->curbank = ear;
+
+	return 0;
+}
+/*
  * Erase the whole flash memory
  *
  * Returns 0 if successful, non-zero otherwise.
  */
 static int erase_chip(struct spi_nor *nor)
 {
+	u32 ret;
 	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));
 
-	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
+	ret = nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
+	if (ret)
+		return ret;
+
+	return ret;
 }
 
 static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
@@ -490,7 +565,7 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
 static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
-	u32 addr, len;
+	u32 addr, len, offset;
 	uint32_t rem;
 	int ret;
 
@@ -540,9 +615,23 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 	/* "sector"-at-a-time erase */
 	} else {
 		while (len) {
+
+			write_enable(nor);
+			offset = addr;
+
+			if (nor->addr_width == 3) {
+				/* Update Extended Address Register */
+				ret = write_ear(nor, offset);
+				if (ret)
+					goto erase_err;
+			}
+			ret = spi_nor_wait_till_ready(nor);
+			if (ret)
+				goto erase_err;
+
 			write_enable(nor);
 
-			ret = spi_nor_erase_sector(nor, addr);
+			ret = spi_nor_erase_sector(nor, offset);
 			if (ret)
 				goto erase_err;
 
@@ -1265,6 +1354,13 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 {
 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
 	int ret;
+	u32 offset = from;
+	u32 read_len = 0;
+	u32 rem_bank_len = 0;
+	u8 bank;
+	loff_t addr = 0;
+
+#define OFFSET_16_MB 0x1000000
 
 	dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
 
@@ -1273,7 +1369,25 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 		return ret;
 
 	while (len) {
-		loff_t addr = from;
+		if (nor->addr_width == 3) {
+			bank = (u32)from / (OFFSET_16_MB << nor->shift);
+			rem_bank_len = ((OFFSET_16_MB << nor->shift) *
+							(bank + 1)) - from;
+		}
+		offset = from;
+
+		/* Die cross over issue is not handled */
+		if (nor->addr_width == 3)
+			write_ear(nor, offset);
+		if (len < rem_bank_len)
+			read_len = len;
+		else
+			read_len = rem_bank_len;
+
+		/* Wait till previous write/erase is done. */
+		ret = spi_nor_wait_till_ready(nor);
+		if (ret)
+			goto read_err;
 
 		if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
 			addr = spi_nor_s3an_addr_convert(nor, addr);
@@ -1390,6 +1504,9 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
 	size_t page_offset, page_remain, i;
 	ssize_t ret;
+	u8 bank = 0;
+	u32 rem_bank_len = 0;
+#define OFFSET_16_MB 0x1000000
 
 	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
 
@@ -1401,6 +1518,14 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 		ssize_t written;
 		loff_t addr = to + i;
 
+		if (nor->addr_width == 3) {
+			bank = (u32)to / (OFFSET_16_MB << nor->shift);
+			rem_bank_len = ((OFFSET_16_MB << nor->shift) *
+							(bank + 1)) - to;
+		}
+
+		page_offset = ((to + i)) & (nor->page_size - 1);
+
 		/*
 		 * If page_size is a power of two, the offset can be quickly
 		 * calculated with an AND operation. On the other cases we
@@ -1416,9 +1541,14 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 			page_offset = do_div(aux, nor->page_size);
 		}
-		/* the size of data remaining on the first page */
-		page_remain = min_t(size_t,
+		/* Die cross over issue is not handled */
+		if (nor->addr_width == 3)
+			write_ear(nor, addr);
+		else {
+			/* the size of data remaining on the first page */
+			page_remain = min_t(size_t,
 				    nor->page_size - page_offset, len - i);
+		}
 
 		if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
 			addr = spi_nor_s3an_addr_convert(nor, addr);
@@ -2760,10 +2890,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 		 const struct spi_nor_hwcaps *hwcaps)
 {
 	struct spi_nor_flash_parameter params;
-	const struct flash_info *info = NULL;
+	struct flash_info *info = NULL;
 	struct device *dev = nor->dev;
 	struct mtd_info *mtd = &nor->mtd;
 	struct device_node *np = spi_nor_get_flash_node(nor);
+	struct device_node *np_spi;
 	int ret;
 	int i;
 
@@ -2777,10 +2908,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	nor->write_proto = SNOR_PROTO_1_1_1;
 
 	if (name)
-		info = spi_nor_match_id(name);
+		info = (struct flash_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 = (struct flash_info *)spi_nor_read_id(nor);
 	if (IS_ERR_OR_NULL(info))
 		return -ENOENT;
 
@@ -2804,7 +2935,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 			 */
 			dev_warn(dev, "found %s, expected %s\n",
 				 jinfo->name, info->name);
-			info = jinfo;
+			info = (struct flash_info *)jinfo;
 		}
 	}
 
@@ -2863,9 +2994,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	if (info->flags & USE_CLSR)
 		nor->flags |= SNOR_F_USE_CLSR;
 
-	if (info->flags & SPI_NOR_NO_ERASE)
-		mtd->flags |= MTD_NO_ERASE;
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+	/* prefer "small sector" erase if possible */
+	if (info->flags & SECT_4K ||
+			info->flags & SECT_4K_PMC) {
+		mtd->erasesize = 4096 << nor->shift;
+	} else
+#endif
+		if (info->flags & SPI_NOR_NO_ERASE)
+			mtd->flags |= MTD_NO_ERASE;
 
+	nor->jedec_id = info->id[0];
 	mtd->dev.parent = dev;
 	nor->page_size = params.page_size;
 	mtd->writebufsize = nor->page_size;
@@ -2901,11 +3040,32 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	} else if (info->addr_width) {
 		nor->addr_width = info->addr_width;
 	} else if (mtd->size > 0x1000000) {
-		/* enable 4-byte addressing if the device exceeds 16MiB */
-		nor->addr_width = 4;
-		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
-		    info->flags & SPI_NOR_4B_OPCODES)
-			spi_nor_set_4byte_opcodes(nor, info);
+#ifdef CONFIG_OF
+		np_spi = of_get_next_parent(np);
+		if (of_property_match_string(np_spi, "compatible",
+					     "xlnx,zynq-qspi-1.0") >= 0) {
+			int status;
+
+			nor->addr_width = 3;
+			set_4byte(nor, info, 0);
+			status = read_ear(nor, info);
+			if (status < 0)
+				dev_warn(dev, "failed to read ear reg\n");
+			else
+				nor->curbank = status & EAR_SEGMENT_MASK;
+		} else {
+#endif
+			/*
+			 * enable 4-byte addressing
+			 * if the device exceeds 16MiB
+			 */
+			nor->addr_width = 4;
+			if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+			    info->flags & SPI_NOR_4B_OPCODES)
+				spi_nor_set_4byte_opcodes(nor, info);
+#ifdef CONFIG_OF
+		}
+#endif
 	} else {
 		nor->addr_width = 3;
 	}
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index de36969..64232b1 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -62,6 +62,9 @@
 #define SPINOR_OP_RDCR		0x35	/* Read configuration register */
 #define SPINOR_OP_RDFSR		0x70	/* Read flag status register */
 #define SPINOR_OP_CLFSR		0x50	/* Clear flag status register */
+#define SPINOR_OP_WREAR		0xc5	/* Write Extended Address Register */
+#define SPINOR_OP_RDEAR		0xc8	/* Read Extended Address Register */
+
 
 /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
 #define SPINOR_OP_READ_4B	0x13	/* Read data bytes (low frequency) */
@@ -106,6 +109,7 @@
 
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
+#define	SPINOR_OP_BRRD		0x16	/* Bank register read */
 #define SPINOR_OP_CLSR		0x30	/* Clear status register 1 */
 
 /* Used for Micron flashes only. */
@@ -139,6 +143,7 @@
 /* Configuration Register bits. */
 #define CR_QUAD_EN_SPAN		BIT(1)	/* Spansion Quad I/O */
 
+#define EAR_SEGMENT_MASK	0x7 /* 128 Mb segment mask */
 /* Status Register 2 bits. */
 #define SR2_QUAD_EN_BIT7	BIT(7)
 
@@ -281,6 +286,7 @@ struct spi_nor {
 	struct mtd_info		mtd;
 	struct mutex		lock;
 	struct device		*dev;
+	struct spi_device       *spi;
 	const struct flash_info	*info;
 	u32			page_size;
 	u8			addr_width;
@@ -288,11 +294,16 @@ struct spi_nor {
 	u8			read_opcode;
 	u8			read_dummy;
 	u8			program_opcode;
+	u32			jedec_id;
+	u16			curbank;
+	u16			n_sectors;
+	u32			sector_size;
 	enum spi_nor_protocol	read_proto;
 	enum spi_nor_protocol	write_proto;
 	enum spi_nor_protocol	reg_proto;
 	bool			sst_write_second;
-	u32			flags;
+	bool			shift;
+	u8			flags;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
 
 	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index bc6bb32..576dea1 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -451,7 +451,8 @@ struct spi_controller {
 #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* requires tx */
 
 #define SPI_MASTER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */
-
+#define SPI_MASTER_QUAD_MODE		BIT(6)	/* support quad mode */
+#define SPI_MASTER_U_PAGE		BIT(9)	/* select upper flash */
 	/* flag indicating this is an SPI slave controller */
 	bool			slave;
 
@@ -797,7 +798,7 @@ struct spi_transfer {
 	u8		bits_per_word;
 	u16		delay_usecs;
 	u32		speed_hz;
-
+	u32		dummy;
 	struct list_head transfer_list;
 };
 
-- 
2.7.4

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

* [RFC PATCH 3/5] mtd: spi-nor: Add Dual Parallel and Stacked support for Zynq QSPI
  2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
  2018-03-23 12:21 ` [RFC PATCH 1/5] spi: Add support for Zynq qspi controller Naga Sureshkumar Relli
  2018-03-23 12:21 ` [RFC PATCH 2/5] mtd: spi-nor: Add support for Zynq QSPI controller Naga Sureshkumar Relli
@ 2018-03-23 12:22 ` Naga Sureshkumar Relli
  2018-03-23 12:22 ` [RFC PATCH 4/5] spi: Add PM Support for Zynq QSPI controller Naga Sureshkumar Relli
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-03-23 12:22 UTC (permalink / raw)
  To: cyrille.pitchen, marek.vasut, dwmw2, computersforpeace, boris.brezillon
  Cc: linux-mtd, Naga Sureshkumar Relli

This patch adds Xilinx Zynq QSPI dual parallel and stacked support.

Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com>
---
 drivers/mtd/spi-nor/spi-nor.c | 241 ++++++++++++++++++++++++++++++++++++++----
 include/linux/mtd/spi-nor.h   |   2 +
 2 files changed, 220 insertions(+), 23 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 7698a92..03aa8c9 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -106,15 +106,24 @@ static const struct flash_info *spi_nor_match_id(const char *name);
 static int read_sr(struct spi_nor *nor)
 {
 	int ret;
-	u8 val;
+	u8 val[2];
 
-	ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val, 1);
-	if (ret < 0) {
-		pr_err("error %d reading SR\n", (int) ret);
-		return ret;
+	if (nor->isparallel) {
+		ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val[0], 2);
+		if (ret < 0) {
+			pr_err("error %d reading SR\n", (int) ret);
+			return ret;
+		}
+		val[0] |= val[1];
+	} else {
+		ret = nor->read_reg(nor, SPINOR_OP_RDSR, &val[0], 1);
+		if (ret < 0) {
+			pr_err("error %d reading SR\n", (int) ret);
+			return ret;
+		}
 	}
 
-	return val;
+	return val[0];
 }
 
 /*
@@ -125,15 +134,24 @@ static int read_sr(struct spi_nor *nor)
 static int read_fsr(struct spi_nor *nor)
 {
 	int ret;
-	u8 val;
+	u8 val[2];
 
-	ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1);
-	if (ret < 0) {
-		pr_err("error %d reading FSR\n", ret);
-		return ret;
+	if (nor->isparallel) {
+		ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val[0], 2);
+		if (ret < 0) {
+			pr_err("error %d reading FSR\n", ret);
+			return ret;
+		}
+		val[0] &= val[1];
+	} else {
+		ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val[0], 1);
+		if (ret < 0) {
+			pr_err("error %d reading FSR\n", ret);
+			return ret;
+		}
 	}
 
-	return val;
+	return val[0];
 }
 
 /*
@@ -451,7 +469,10 @@ static int write_ear(struct spi_nor *nor, u32 addr)
 	addr = addr % (u32) mtd->size;
 	ear = addr >> 24;
 
-	if (ear == nor->curbank)
+	if ((!nor->isstacked) && (ear == nor->curbank))
+		return 0;
+
+	if (nor->isstacked && (mtd->size <= 0x2000000))
 		return 0;
 
 	if (nor->jedec_id == CFI_MFR_AMD)
@@ -480,9 +501,21 @@ static int erase_chip(struct spi_nor *nor)
 	u32 ret;
 	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10));
 
+	if (nor->isstacked)
+		nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
 	ret = nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
 	if (ret)
 		return ret;
+	if (nor->isstacked) {
+		/* Wait until previous write command finished */
+		ret = spi_nor_wait_till_ready(nor);
+		if (ret)
+			return ret;
+
+		nor->spi->master->flags |= SPI_MASTER_U_PAGE;
+
+		ret = nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
+	}
 
 	return ret;
 }
@@ -619,6 +652,20 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 			write_enable(nor);
 			offset = addr;
 
+			if (nor->isparallel == 1)
+				offset /= 2;
+
+			if (nor->isstacked == 1) {
+				if (offset >= (mtd->size / 2)) {
+					offset = offset - (mtd->size / 2);
+					nor->spi->master->flags |=
+						SPI_MASTER_U_PAGE;
+				} else {
+					nor->spi->master->flags &=
+						~SPI_MASTER_U_PAGE;
+				}
+			}
+
 			if (nor->addr_width == 3) {
 				/* Update Extended Address Register */
 				ret = write_ear(nor, offset);
@@ -949,6 +996,16 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
 	if (ret)
 		return ret;
+	if (nor->isparallel == 1)
+		ofs = ofs >> nor->shift;
+
+	if (nor->isstacked == 1) {
+		if (ofs >= (mtd->size / 2)) {
+			ofs = ofs - (mtd->size / 2);
+			nor->spi->master->flags |= SPI_MASTER_U_PAGE;
+		} else
+			nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
+	}
 
 	ret = nor->flash_lock(nor, ofs, len);
 
@@ -964,6 +1021,16 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 	ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
 	if (ret)
 		return ret;
+	if (nor->isparallel == 1)
+		ofs = ofs >> nor->shift;
+
+	if (nor->isstacked == 1) {
+		if (ofs >= (mtd->size / 2)) {
+			ofs = ofs - (mtd->size / 2);
+			nor->spi->master->flags |= SPI_MASTER_U_PAGE;
+		} else
+			nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
+	}
 
 	ret = nor->flash_unlock(nor, ofs, len);
 
@@ -1355,6 +1422,7 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
 	int ret;
 	u32 offset = from;
+	u32 stack_shift = 0;
 	u32 read_len = 0;
 	u32 rem_bank_len = 0;
 	u8 bank;
@@ -1375,8 +1443,25 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 							(bank + 1)) - from;
 		}
 		offset = from;
+		if (nor->isparallel == 1)
+			offset /= 2;
+
+		if (nor->isstacked == 1) {
+			stack_shift = 1;
+			if (offset >= (mtd->size / 2)) {
+				offset = offset - (mtd->size / 2);
+				nor->spi->master->flags |= SPI_MASTER_U_PAGE;
+			} else {
+				nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
+			}
+		}
 
 		/* Die cross over issue is not handled */
+		if (nor->addr_width == 4) {
+			rem_bank_len = (mtd->size >> stack_shift) -
+					(offset << nor->shift);
+		}
+
 		if (nor->addr_width == 3)
 			write_ear(nor, offset);
 		if (len < rem_bank_len)
@@ -1390,9 +1475,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
 			goto read_err;
 
 		if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
-			addr = spi_nor_s3an_addr_convert(nor, addr);
+			addr = spi_nor_s3an_addr_convert(nor, offset);
 
-		ret = nor->read(nor, addr, len, buf);
+		ret = nor->read(nor, offset, len, buf);
 		if (ret == 0) {
 			/* We shouldn't see 0-length reads */
 			ret = -EIO;
@@ -1504,6 +1589,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
 	size_t page_offset, page_remain, i;
 	ssize_t ret;
+	u32 offset, stack_shift = 0;
 	u8 bank = 0;
 	u32 rem_bank_len = 0;
 #define OFFSET_16_MB 0x1000000
@@ -1517,7 +1603,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 	for (i = 0; i < len; ) {
 		ssize_t written;
 		loff_t addr = to + i;
-
+		offset = (to + i);
 		if (nor->addr_width == 3) {
 			bank = (u32)to / (OFFSET_16_MB << nor->shift);
 			rem_bank_len = ((OFFSET_16_MB << nor->shift) *
@@ -1535,26 +1621,53 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 		 * modulus (do_div()) or not.
 		 */
 		if (hweight32(nor->page_size) == 1) {
-			page_offset = addr & (nor->page_size - 1);
+			page_offset = offset & (nor->page_size - 1);
 		} else {
-			uint64_t aux = addr;
+			uint64_t aux = offset;
 
 			page_offset = do_div(aux, nor->page_size);
 		}
+		if (nor->isparallel == 1)
+			offset /= 2;
+
+		if (nor->isstacked == 1) {
+			stack_shift = 1;
+			if (offset >= (mtd->size / 2)) {
+				offset = offset - (mtd->size / 2);
+				nor->spi->master->flags |= SPI_MASTER_U_PAGE;
+			} else {
+				nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
+			}
+		}
 		/* Die cross over issue is not handled */
+		if (nor->addr_width == 4)
+			rem_bank_len = (mtd->size >> stack_shift) - offset;
 		if (nor->addr_width == 3)
-			write_ear(nor, addr);
-		else {
-			/* the size of data remaining on the first page */
+			write_ear(nor, offset);
+		if (nor->isstacked == 1) {
+			if (len <= rem_bank_len) {
+				page_remain = min_t(size_t,
+					 nor->page_size - page_offset, len - i);
+			} else {
+				/*
+				 * the size of data remaining
+				 * on the first page
+				 */
+				page_remain = rem_bank_len;
+			}
+		} else {
 			page_remain = min_t(size_t,
-				    nor->page_size - page_offset, len - i);
+					 nor->page_size - page_offset, len - i);
 		}
+		ret = spi_nor_wait_till_ready(nor);
+		if (ret)
+			goto write_err;
 
 		if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
 			addr = spi_nor_s3an_addr_convert(nor, addr);
 
 		write_enable(nor);
-		ret = nor->write(nor, addr, page_remain, buf + i);
+		ret = nor->write(nor, offset, page_remain, buf + i);
 		if (ret < 0)
 			goto write_err;
 		written = ret;
@@ -2897,6 +3010,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	struct device_node *np_spi;
 	int ret;
 	int i;
+	u32 is_dual;
 
 	ret = spi_nor_check(nor);
 	if (ret)
@@ -2965,6 +3079,69 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 	mtd->_read = spi_nor_read;
 	mtd->_resume = spi_nor_resume;
 
+#ifdef CONFIG_OF
+	np_spi = of_get_next_parent(np);
+	if ((of_property_match_string(np_spi, "compatible",
+		    "xlnx,zynq-qspi-1.0") >= 0) ||
+			(of_property_match_string(np_spi, "compatible",
+					"xlnx,zynqmp-qspi-1.0") >= 0)) {
+		if (of_property_read_u32(np_spi, "is-dual",
+					 &is_dual) < 0) {
+			/* Default to single if prop not defined */
+			nor->shift = 0;
+			nor->isstacked = 0;
+			nor->isparallel = 0;
+		} else {
+			if (is_dual == 1) {
+				/* dual parallel */
+				nor->shift = 1;
+				info->sector_size <<= nor->shift;
+				info->page_size <<= nor->shift;
+				mtd->size <<= nor->shift;
+				nor->isparallel = 1;
+				nor->isstacked = 0;
+			} else {
+#ifdef CONFIG_SPI_ZYNQ_QSPI_DUAL_STACKED
+				/* dual stacked */
+				nor->shift = 0;
+				mtd->size <<= 1;
+				info->n_sectors <<= 1;
+				nor->isstacked = 1;
+				nor->isparallel = 0;
+#else
+				u32 is_stacked;
+
+				if (of_property_read_u32(np_spi,
+						"is-stacked",
+						&is_stacked) < 0) {
+					is_stacked = 0;
+				}
+				if (is_stacked) {
+					/* dual stacked */
+					nor->shift = 0;
+					mtd->size <<= 1;
+					info->n_sectors <<= 1;
+					nor->isstacked = 1;
+					nor->isparallel = 0;
+				} else {
+					/* single */
+					nor->shift = 0;
+					nor->isstacked = 0;
+					nor->isparallel = 0;
+				}
+#endif
+			}
+		}
+	}
+
+#else
+	/* Default to single */
+	nor->shift = 0;
+	nor->isstacked = 0;
+	nor->isparallel = 0;
+#endif
+
+
 	/* NOR protection support for STmicro/Micron chips and similar */
 	if (JEDEC_MFR(info) == SNOR_MFR_MICRON ||
 			info->flags & SPI_NOR_HAS_LOCK) {
@@ -3063,6 +3240,24 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 			if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
 			    info->flags & SPI_NOR_4B_OPCODES)
 				spi_nor_set_4byte_opcodes(nor, info);
+			else {
+				np_spi = of_get_next_parent(np);
+				if (of_property_match_string(np_spi,
+						"compatible",
+						"xlnx,xps-spi-2.00.a") >= 0) {
+					nor->addr_width = 3;
+					set_4byte(nor, info, 0);
+				} else {
+					set_4byte(nor, info, 1);
+					if (nor->isstacked) {
+						nor->spi->master->flags |=
+							SPI_MASTER_U_PAGE;
+						set_4byte(nor, info, 1);
+						nor->spi->master->flags &=
+							~SPI_MASTER_U_PAGE;
+					}
+				}
+			}
 #ifdef CONFIG_OF
 		}
 #endif
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 64232b1..bdf6433 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -303,6 +303,8 @@ struct spi_nor {
 	enum spi_nor_protocol	reg_proto;
 	bool			sst_write_second;
 	bool			shift;
+	bool			isparallel;
+	bool			isstacked;
 	u8			flags;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
 
-- 
2.7.4

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

* [RFC PATCH 4/5] spi: Add PM Support for Zynq QSPI controller
  2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
                   ` (2 preceding siblings ...)
  2018-03-23 12:22 ` [RFC PATCH 3/5] mtd: spi-nor: Add Dual Parallel and Stacked support for Zynq QSPI Naga Sureshkumar Relli
@ 2018-03-23 12:22 ` Naga Sureshkumar Relli
  2018-03-23 12:22 ` [RFC PATCH 5/5] devicetree: Add devicetree bindings documentation for Naga Sureshkumar Relli
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-03-23 12:22 UTC (permalink / raw)
  To: cyrille.pitchen, marek.vasut, dwmw2, computersforpeace, boris.brezillon
  Cc: linux-mtd, Naga Sureshkumar Relli

This patch adds PM Support for Xilinx Zynq QSPI Controller
driver.

Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com>
---
 drivers/spi/spi-zynq-qspi.c | 58 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c
index 7a772ca..9f87ee1 100644
--- a/drivers/spi/spi-zynq-qspi.c
+++ b/drivers/spi/spi-zynq-qspi.c
@@ -604,6 +604,64 @@ static int zynq_qspi_start_transfer(struct spi_master *master,
 }
 
 /**
+ * zynq_qspi_suspend - Suspend method for the QSPI driver
+ * @_dev:	Address of the platform_device structure
+ *
+ * This function stops the QSPI driver queue and disables the QSPI controller
+ *
+ * Return:	Always 0
+ */
+static int __maybe_unused zynq_qspi_suspend(struct device *_dev)
+{
+	struct platform_device *pdev = container_of(_dev,
+			struct platform_device, dev);
+	struct spi_master *master = platform_get_drvdata(pdev);
+
+	spi_master_suspend(master);
+
+	zynq_unprepare_transfer_hardware(master);
+
+	return 0;
+}
+
+/**
+ * zynq_qspi_resume - Resume method for the QSPI driver
+ * @dev:	Address of the platform_device structure
+ *
+ * The function starts the QSPI driver queue and initializes the QSPI controller
+ *
+ * Return:	0 on success and error value on error
+ */
+static int __maybe_unused zynq_qspi_resume(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev,
+			struct platform_device, dev);
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct zynq_qspi *xqspi = spi_master_get_devdata(master);
+	int ret = 0;
+
+	ret = clk_enable(xqspi->pclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable APB clock.\n");
+		return ret;
+	}
+
+	ret = clk_enable(xqspi->refclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable device clock.\n");
+		clk_disable(xqspi->pclk);
+		return ret;
+	}
+
+	spi_master_resume(master);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(zynq_qspi_dev_pm_ops, zynq_qspi_suspend,
+zynq_qspi_resume);
+
+/**
  * zynq_qspi_probe - Probe method for the QSPI driver
  * @pdev:	Pointer to the platform_device structure
  *
-- 
2.7.4

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

* [RFC PATCH 5/5] devicetree: Add devicetree bindings documentation for
  2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
                   ` (3 preceding siblings ...)
  2018-03-23 12:22 ` [RFC PATCH 4/5] spi: Add PM Support for Zynq QSPI controller Naga Sureshkumar Relli
@ 2018-03-23 12:22 ` Naga Sureshkumar Relli
  2018-03-23 14:47   ` Miquel Raynal
  2018-03-23 12:52 ` [RFC PATCH 0/5] RFC for Zynq QSPI Marek Vasut
  2018-04-16  7:25 ` Naga Sureshkumar Relli
  6 siblings, 1 reply; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-03-23 12:22 UTC (permalink / raw)
  To: cyrille.pitchen, marek.vasut, dwmw2, computersforpeace, boris.brezillon
  Cc: linux-mtd, Naga Sureshkumar Relli

Add bindings documentation for Zynq QSPI driver.

Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com>
---
 .../devicetree/bindings/spi/spi-zynq-qspi.txt      | 28 ++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt

diff --git a/Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt b/Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt
new file mode 100644
index 0000000..fe3b8e9
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt
@@ -0,0 +1,28 @@
+Xilinx Zynq QSPI controller Device Tree Bindings
+-------------------------------------------------
+
+Required properties:
+- compatible		: Should be "xlnx,zynq-qspi-1.0".
+- reg			: Physical base address and size of QSPI registers map.
+- interrupts		: Property with a value describing the interrupt
+			  number.
+- interrupt-parent	: Must be core interrupt controller
+- clock-names		: List of input clock names - "ref_clk", "pclk"
+			  (See clock bindings for details).
+- clocks		: Clock phandles (see clock bindings for details).
+
+Optional properties:
+- num-cs		: Number of chip selects used.
+- is-dual		: QSPI operating in Dual parallel.
+- is-satcked		: QSPI operating in Stacked mode.
+
+Example:
+	qspi@e000d000 {
+		compatible = "xlnx,zynq-qspi-1.0";
+		clock-names = "ref_clk", "pclk";
+		clocks = <&clkc 10>, <&clkc 43>;
+		interrupt-parent = <&intc>;
+		interrupts = <0 19 4>;
+		num-cs = <1>;
+		reg = <0xe000d000 0x1000>;
+	} ;
-- 
2.7.4

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

* Re: [RFC PATCH 0/5] RFC for Zynq QSPI
  2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
                   ` (4 preceding siblings ...)
  2018-03-23 12:22 ` [RFC PATCH 5/5] devicetree: Add devicetree bindings documentation for Naga Sureshkumar Relli
@ 2018-03-23 12:52 ` Marek Vasut
  2018-03-23 13:39   ` Naga Sureshkumar Relli
  2018-04-16  7:25 ` Naga Sureshkumar Relli
  6 siblings, 1 reply; 15+ messages in thread
From: Marek Vasut @ 2018-03-23 12:52 UTC (permalink / raw)
  To: Naga Sureshkumar Relli, cyrille.pitchen, dwmw2,
	computersforpeace, boris.brezillon
  Cc: linux-mtd, Naga Sureshkumar Relli

On 03/23/2018 01:21 PM, Naga Sureshkumar Relli wrote:
> Xilinx Zynq uses a QSPI controller that is based on the Cadence SPI IP.

Don't we have a CQSPI driver already ? Why is this adding a new one ?

-- 
Best regards,
Marek Vasut

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

* RE: [RFC PATCH 0/5] RFC for Zynq QSPI
  2018-03-23 12:52 ` [RFC PATCH 0/5] RFC for Zynq QSPI Marek Vasut
@ 2018-03-23 13:39   ` Naga Sureshkumar Relli
  0 siblings, 0 replies; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-03-23 13:39 UTC (permalink / raw)
  To: Marek Vasut, cyrille.pitchen, dwmw2, computersforpeace, boris.brezillon
  Cc: linux-mtd

Hi Marek,

Thanks for reviewing the patch.

> -----Original Message-----
> From: Marek Vasut [mailto:marek.vasut@gmail.com]
> Sent: Friday, March 23, 2018 6:22 PM
> To: Naga Sureshkumar Relli <nagasure@xilinx.com>;
> cyrille.pitchen@wedev4u.fr; dwmw2@infradead.org;
> computersforpeace@gmail.com; boris.brezillon@free-electrons.com
> Cc: linux-mtd@lists.infradead.org; Naga Sureshkumar Relli
> <nagasure@xilinx.com>
> Subject: Re: [RFC PATCH 0/5] RFC for Zynq QSPI
> 
> On 03/23/2018 01:21 PM, Naga Sureshkumar Relli wrote:
> > Xilinx Zynq uses a QSPI controller that is based on the Cadence SPI IP.
> 
> Don't we have a CQSPI driver already ? Why is this adding a new one ?
Zynq QSPI Controller is designed by using Cadence SPI IP core in order to add multi bit IO capability.
The modified core is Zynq QSPI Controller core. Both are different.
> 
> --
> Best regards,
> Marek Vasut

Thanks,
Naga Sureshkumar Relli.

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

* Re: [RFC PATCH 5/5] devicetree: Add devicetree bindings documentation for
  2018-03-23 12:22 ` [RFC PATCH 5/5] devicetree: Add devicetree bindings documentation for Naga Sureshkumar Relli
@ 2018-03-23 14:47   ` Miquel Raynal
  0 siblings, 0 replies; 15+ messages in thread
From: Miquel Raynal @ 2018-03-23 14:47 UTC (permalink / raw)
  To: Naga Sureshkumar Relli
  Cc: cyrille.pitchen, marek.vasut, dwmw2, computersforpeace,
	boris.brezillon, Naga Sureshkumar Relli, linux-mtd

Hi Naga,

On Fri, 23 Mar 2018 17:52:02 +0530, Naga Sureshkumar Relli
<naga.sureshkumar.relli@xilinx.com> wrote:

> Add bindings documentation for Zynq QSPI driver.
> 
> Signed-off-by: Naga Sureshkumar Relli <nagasure@xilinx.com>
> ---
>  .../devicetree/bindings/spi/spi-zynq-qspi.txt      | 28 ++++++++++++++++++++++
>  1 file changed, 28 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt
> 
> diff --git a/Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt b/Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt
> new file mode 100644
> index 0000000..fe3b8e9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt
> @@ -0,0 +1,28 @@
> +Xilinx Zynq QSPI controller Device Tree Bindings
> +-------------------------------------------------
> +
> +Required properties:
> +- compatible		: Should be "xlnx,zynq-qspi-1.0".

What is this trailing "-1.0" for?

> +- reg			: Physical base address and size of QSPI registers map.
> +- interrupts		: Property with a value describing the interrupt
> +			  number.
> +- interrupt-parent	: Must be core interrupt controller
> +- clock-names		: List of input clock names - "ref_clk", "pclk"
> +			  (See clock bindings for details).
> +- clocks		: Clock phandles (see clock bindings for details).
> +
> +Optional properties:
> +- num-cs		: Number of chip selects used.
> +- is-dual		: QSPI operating in Dual parallel.
> +- is-satcked		: QSPI operating in Stacked mode.

        ^ stacked

BTW, if you have both is-dual and is-stacked properties in the DT, do
you really need a configuration option for that?

> +
> +Example:
> +	qspi@e000d000 {
> +		compatible = "xlnx,zynq-qspi-1.0";
> +		clock-names = "ref_clk", "pclk";
> +		clocks = <&clkc 10>, <&clkc 43>;
> +		interrupt-parent = <&intc>;
> +		interrupts = <0 19 4>;
> +		num-cs = <1>;
> +		reg = <0xe000d000 0x1000>;
> +	} ;

Thanks,
Miquèl

-- 
Miquel Raynal, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

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

* RE: [RFC PATCH 0/5] RFC for Zynq QSPI
  2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
                   ` (5 preceding siblings ...)
  2018-03-23 12:52 ` [RFC PATCH 0/5] RFC for Zynq QSPI Marek Vasut
@ 2018-04-16  7:25 ` Naga Sureshkumar Relli
       [not found]   ` <CALgLF9K=rZxVx_QXvV_LH3OhidTcguZ=K=p=Lf9AfFbk4H+8Zw@mail.gmail.com>
  6 siblings, 1 reply; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-04-16  7:25 UTC (permalink / raw)
  To: Naga Sureshkumar Relli, cyrille.pitchen, marek.vasut, dwmw2,
	computersforpeace, boris.brezillon
  Cc: linux-mtd

Hi all,

Can some body propose an alternative to get the things done(Adding Zynq Dual parallel and stacked support)? Because to handle this we have changed the core functions. See my patches 
"[RFC PATCH 3/5] mtd: spi-nor: Add Dual Parallel and Stacked support for Zynq QSPI"
"[RFC PATCH 1/5] spi: Add support for Zynq qspi controller"

I will provide required inputs, if any.

Thanks,
Naga Sureshkumar Relli.

> -----Original Message-----
> From: Naga Sureshkumar Relli [mailto:naga.sureshkumar.relli@xilinx.com]
> Sent: Friday, March 23, 2018 5:52 PM
> To: cyrille.pitchen@wedev4u.fr; marek.vasut@gmail.com; dwmw2@infradead.org;
> computersforpeace@gmail.com; boris.brezillon@free-electrons.com
> Cc: linux-mtd@lists.infradead.org; Naga Sureshkumar Relli <nagasure@xilinx.com>
> Subject: [RFC PATCH 0/5] RFC for Zynq QSPI
> 
> Xilinx Zynq uses a QSPI controller that is based on the Cadence SPI IP.
> This controller implements all the functionality required to support Quad SPI NOR flash
> devices.
> This driver along with the MTD layer is used to support flash devices.
> 
> This series is for the following purposes:
> - RFC of the Quad SPI driver.
> - We currently use a custom MTD layer and would like to get inputs on
>   dual stacked/dual parallel handling (described below).
> 
> The flash device(s) can be connected in the three configurations:
> 1. Single - One flash device with 1 CS, 1 Clock and 4 IO lines.
> 2. Dual Parallel - Two flash devices connected with common CS and
>    separate IO lines (resulting in 8 IO lines).
>    In this configuration, the controller
>    a) Duplicates commands, address etc. sent on both sets of 4 IO lines.
>    b) Stripes data both transmitted and received i.e.
>       4 bits of data is sent to the first flash and the other 4 bits
>       to the second flash. Similarly read data is also consolidated.
>    Due to this, TX and RX data handling in the driver need special
>    handling for parallel mode.
> 3. Dual Stacked - Two flash devices connected with separate CS and
>    4 common IO lines. This is largely similar to single, except for
>    the slave selection logic.
> The above configuration is conveyed to the QSPI driver through a devicetree property.
> 
> The QSPI driver differs from the existing Cadence SPI driver in the following respects majorly:
> 1. TX and RX handling: Different TX registers are used to write into
>    the TX FIFO. TXD0, TXD1, TXD2 and TXD3 are used write 4, 1, 2 and 3
>    bytes respectively. Depending on the TXD register used, the received
>    bytes also need to be handled separately.
> 2. Depending on the configuration in which flash devices are connected
>    (single, parallel or stacked), QSPI controller configuration registers
>    need to be modified.
> 3. There is no support for extended slave select in QSPI, as opposed to
>    SPI. In case of stacked configuration, the slave select field remains
>    the same and a different configuration bit is used to select between
>    the two flash devices.
> 4. Handling of dual parallel configuration.
> 
> MTD layer:
> The Xilinx Zynq MTD layer by far makes use of the mainline version with some differences.
> The primary flash families supported are Spansion, Winbond and Micron.
> - Probe:
>   - In dual configurations, both flash devices are recognized as one
>     continuous memory. (ID is read only from one flash and it is a
>     pre-stated assumption that both flash devices have the same flash
>     make and size.)
> - Addressing:
>   a) In dual stacked mode, the address passed to the MTD layer can be
>      between 0x0 to 2*(one flash size). Hence the MTD layer has to recognize
>      whether the address belongs to the first flash or the second flash
>      subtract the offset and indicate the same to the QSPI driver.
>   b) In dual stacked mode too the address can range between
>      0 to 2*(one flash size). But, when an 8 bit word is written,
>      4 bits are written to the first and 4 bits are written to the
>      second flash. Hence the address sent is always halved and checks
>      are in place for even address and even length.
> - 4 byte addressing is not supported and hence bank selection logic is used
>   along with the addressing system described above.
> - Flash register read/writes, for example, lock/unlock, quad enable etc.
>   are handled differently in dual stacked and parallel modes.
> 
> This is tested with current master branch of Linux.
> To make Zynq QSPI work, we need to tweak spi-nor.c.
> I agree that changing the spi-nor layer apis, doesn't look good but i didn't find a way to do the
> same.
> So please suggest a way to handle Zynq QSPI controller in dual parallel and stacked modes.
> we can move the spi/spi-zynq-qspi.c to spi-nor/ but even in that case also changes are needed in
> spi-nor.c.
> What ever the changes we do to make Zynq QSPI work, will applicable for Xilinx ZynqMP
> SOC QSPI controller(spi-zynqmp-qspi.c) as well.
> I am happy to do the changes suggested, so that we will have a solution for Xilinx Zynq and
> ZynqMP SOC QSPI controllers.
> 
> Naga Sureshkumar Relli (5):
>   spi: Add support for Zynq qspi controller
>   mtd: spi-nor: Add support for Zynq QSPI controller
>   mtd: spi-nor: Add Dual Parallel and Stacked support for Zynq QSPI
>   spi: Add PM Support for Zynq QSPI controller
>   devicetree: Add devicetree bindings documentation for Zynq QSPI
> 
>  .../devicetree/bindings/spi/spi-zynq-qspi.txt      |  26 +
>  drivers/mtd/spi-nor/spi-nor.c                      | 425 ++++++++++-
>  drivers/spi/Kconfig                                |  15 +
>  drivers/spi/Makefile                               |   1 +
>  drivers/spi/spi-zynq-qspi.c                        | 827 +++++++++++++++++++++
>  include/linux/mtd/spi-nor.h                        |  15 +-
>  include/linux/spi/spi.h                            |   5 +-
>  7 files changed, 1276 insertions(+), 38 deletions(-)  create mode 100644
> Documentation/devicetree/bindings/spi/spi-zynq-qspi.txt
>  create mode 100644 drivers/spi/spi-zynq-qspi.c
> 
> --
> 2.7.4

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

* Re: [RFC PATCH 0/5] RFC for Zynq QSPI
       [not found]   ` <CALgLF9K=rZxVx_QXvV_LH3OhidTcguZ=K=p=Lf9AfFbk4H+8Zw@mail.gmail.com>
@ 2018-09-17 15:05     ` Boris Brezillon
  2018-09-17 15:07       ` Boris Brezillon
       [not found]       ` <CALgLF9JVweEcEm6L33HaHp4HtXhmsZL7J5FDe8+O11i0Q1nukg@mail.gmail.com>
  0 siblings, 2 replies; 15+ messages in thread
From: Boris Brezillon @ 2018-09-17 15:05 UTC (permalink / raw)
  To: naga suresh kumar
  Cc: nagasure, cyrille.pitchen, marek.vasut, dwmw2, computersforpeace,
	boris.brezillon, linux-mtd

Hi Naga,

On Mon, 17 Sep 2018 16:08:26 +0530
naga suresh kumar <nagasureshkumarrelli@gmail.com> wrote:

> Hi all,
> 
> Can i write the Zynq QSPI driver under the new spi/mem framework?

Yes, you should.

> As i said, it needs tweaking the mtd->size and spi read/write addresses.

Can you elaborate a bit on why you think this is needed? Did you look
at [1]? Maybe it will prevent us from exposing the flash size at the
spi-mem level.

> Can somebody share your thoughts on this(Adding Zynq QSPI Dual parallel and
> stacked)?

I always have a hard time with this naming. I guess stacked is when you
have 2 chips sharing the same I/O bus, and parallel is when you have 2
chips with one taking all of the I/O and the other taking the other
half. Is that correct?

If it is, then stacked mode should be easy to support (it's just a
matter of patching the spi layer to allow passing several CS lines to a
single device (in case of DT parsing, it means reg = <0 1> implies CS 0
and 1 are assigned to the same device. For the parallel, I don't have a
solution yet.

Anyway, I'd suggest that you start with something simple (a spi-mem
driver supporting ->exec_op() in Single/Dual/Quad mode), and build on
top of that for the advanced features you're mentioning here.

Regards,

Boris

[1]http://lists.infradead.org/pipermail/linux-mtd/2018-June/081460.html

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

* Re: [RFC PATCH 0/5] RFC for Zynq QSPI
  2018-09-17 15:05     ` Boris Brezillon
@ 2018-09-17 15:07       ` Boris Brezillon
       [not found]       ` <CALgLF9JVweEcEm6L33HaHp4HtXhmsZL7J5FDe8+O11i0Q1nukg@mail.gmail.com>
  1 sibling, 0 replies; 15+ messages in thread
From: Boris Brezillon @ 2018-09-17 15:07 UTC (permalink / raw)
  To: naga suresh kumar
  Cc: nagasure, cyrille.pitchen, marek.vasut, dwmw2, computersforpeace,
	boris.brezillon, linux-mtd

On Mon, 17 Sep 2018 17:05:22 +0200
Boris Brezillon <boris.brezillon@bootlin.com> wrote:

> Hi Naga,
> 
> On Mon, 17 Sep 2018 16:08:26 +0530
> naga suresh kumar <nagasureshkumarrelli@gmail.com> wrote:
> 
> > Hi all,
> > 
> > Can i write the Zynq QSPI driver under the new spi/mem framework?  
> 
> Yes, you should.
> 
> > As i said, it needs tweaking the mtd->size and spi read/write addresses.  
> 
> Can you elaborate a bit on why you think this is needed? Did you look
> at [1]? Maybe it will prevent us from exposing the flash size at the
> spi-mem level.
> 
> > Can somebody share your thoughts on this(Adding Zynq QSPI Dual parallel and
> > stacked)?  
> 
> I always have a hard time with this naming. I guess stacked is when you
> have 2 chips sharing the same I/O bus, and parallel is when you have 2
> chips with one taking all of the I/O and the other taking the other

			^half of the I/O bus and the other one

> half. Is that correct?
> 
> If it is, then stacked mode should be easy to support (it's just a
> matter of patching the spi layer to allow passing several CS lines to a
> single device (in case of DT parsing, it means reg = <0 1> implies CS 0
> and 1 are assigned to the same device. For the parallel, I don't have a
> solution yet.
> 
> Anyway, I'd suggest that you start with something simple (a spi-mem
> driver supporting ->exec_op() in Single/Dual/Quad mode), and build on
> top of that for the advanced features you're mentioning here.
> 
> Regards,
> 
> Boris
> 
> [1]http://lists.infradead.org/pipermail/linux-mtd/2018-June/081460.html

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

* Re: [RFC PATCH 0/5] RFC for Zynq QSPI
       [not found]       ` <CALgLF9JVweEcEm6L33HaHp4HtXhmsZL7J5FDe8+O11i0Q1nukg@mail.gmail.com>
@ 2018-09-18  7:17         ` Boris Brezillon
  2018-09-18  8:58           ` Naga Sureshkumar Relli
  2019-06-26  9:53           ` naga suresh kumar
  0 siblings, 2 replies; 15+ messages in thread
From: Boris Brezillon @ 2018-09-18  7:17 UTC (permalink / raw)
  To: naga suresh kumar
  Cc: nagasure, cyrille.pitchen, Marek Vašut, David Woodhouse,
	Brian Norris, boris.brezillon, linux-mtd

Hi Naga,

On Tue, 18 Sep 2018 11:26:05 +0530
naga suresh kumar <nagasureshkumarrelli@gmail.com> wrote:

> Hi,
> 
> Please see my reply inline by prefacing my name, currently i am facing
> issues with my outlook. the reply looks not good but please don't mind.

I do mind. Please find a way to fix that, 'cause I'm pretty sure others
will soon complain about that too.


> > > As i said, it needs tweaking the mtd->size and spi read/write addresses.  
> >
> > Can you elaborate a bit on why you think this is needed? Did you look
> > at [1]? Maybe it will prevent us from exposing the flash size at the
> > spi-mem level.
> > [naga]: some example snippet below
> >
> > spi_nor_read () {
> >
> >    if (nor->isparallel == 1)
> >
> >    {
> >
> >       offset /= 2; //byte stripping will happen here
> >
> >        nor->spi->master->flags |= SPI_DATA_STRIPE;
> >
> >        //By setting this flag even bits of data word located in lower
> > memory and odd are in upper memory
> >
> >    }
> >
> >    if (nor->isstacked == 1) {
> >
> >      stack_shift = 1;
> >
> >      if (offset >= (mtd->size / 2)) {
> >
> >      offset = offset - (mtd->size / 2);
> >
> >      nor->spi->master->flags |= SPI_MASTER_U_PAGE;
> >
> >      //We can access Upper memory or lower memory, by setting this flag in
> > controller.
> >
> >   } else {
> >
> >         nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
> >
> >   }
> >
> >  }
> >
> > same for spi_nor_write() also.

Let's put the stacked and parallel support on the side for now and
focus on single die/chip support. Will find a clean way to handle that
afterwards.

> >
> > Also in both dual parallel and stacked mode mtd->size should be sum of
> > both flash device sizes

Yes, but this information should stay at the spi-nor/mtd level.

> >
> > also status register read will change, it needs status from both devices.
> > like that some changes
> >
> > will be needed in core.  

Which is why this needs to be handled in spi-nor.c. The spi-nor core
needs to know about the parallel/stack setup and handle it differently
(read both status bytes, read both IDs, ...).

> > > Can somebody share your thoughts on this(Adding Zynq QSPI Dual parallel  
> > and  
> > > stacked)?  
> >
> > I always have a hard time with this naming. I guess stacked is when you
> > have 2 chips sharing the same I/O bus, and parallel is when you have 2
> > chips with one taking all of the I/O and the other taking the other
> > half. Is that correct?
> > [naga]: Yes, you are correct, i have attached the diagrams for these mode.
> >
> > In parallel mode, with the help of controller STRIPE feature, even bits of
> >
> > data words are located in lower memory and odd bits are located in upper
> > memory.
> >
> > Data management will be taken care by controller in both modes

Ok. I need to think about it a bit more. In the meantime, I recommend
that you submit a version of the driver that does not support parallel
and stacked modes.

Regards,

Boris

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

* RE: [RFC PATCH 0/5] RFC for Zynq QSPI
  2018-09-18  7:17         ` Boris Brezillon
@ 2018-09-18  8:58           ` Naga Sureshkumar Relli
  2019-06-26  9:53           ` naga suresh kumar
  1 sibling, 0 replies; 15+ messages in thread
From: Naga Sureshkumar Relli @ 2018-09-18  8:58 UTC (permalink / raw)
  To: Boris Brezillon, naga suresh kumar
  Cc: cyrille.pitchen, Marek Vašut, David Woodhouse, Brian Norris,
	boris.brezillon, linux-mtd

Hi Boris,

> -----Original Message-----
> From: Boris Brezillon [mailto:boris.brezillon@bootlin.com]
> Sent: Tuesday, September 18, 2018 12:47 PM
> To: naga suresh kumar <nagasureshkumarrelli@gmail.com>
> Cc: Naga Sureshkumar Relli <nagasure@xilinx.com>; cyrille.pitchen@wedev4u.fr; Marek
> Vašut <marek.vasut@gmail.com>; David Woodhouse <dwmw2@infradead.org>; Brian
> Norris <computersforpeace@gmail.com>; boris.brezillon@free-electrons.com; linux-
> mtd@lists.infradead.org
> Subject: Re: [RFC PATCH 0/5] RFC for Zynq QSPI
> 
> Hi Naga,
> 
> On Tue, 18 Sep 2018 11:26:05 +0530
> naga suresh kumar <nagasureshkumarrelli@gmail.com> wrote:
> 
> > Hi,
> >
> > Please see my reply inline by prefacing my name, currently i am facing
> > issues with my outlook. the reply looks not good but please don't mind.
> 
> I do mind. Please find a way to fix that, 'cause I'm pretty sure others will soon complain about
> that too.
Its working now.
> 
> 
> > > > As i said, it needs tweaking the mtd->size and spi read/write addresses.
> > >
> > > Can you elaborate a bit on why you think this is needed? Did you
> > > look at [1]? Maybe it will prevent us from exposing the flash size
> > > at the spi-mem level.
> > > [naga]: some example snippet below
> > >
> > > spi_nor_read () {
> > >
> > >    if (nor->isparallel == 1)
> > >
> > >    {
> > >
> > >       offset /= 2; //byte stripping will happen here
> > >
> > >        nor->spi->master->flags |= SPI_DATA_STRIPE;
> > >
> > >        //By setting this flag even bits of data word located in
> > > lower memory and odd are in upper memory
> > >
> > >    }
> > >
> > >    if (nor->isstacked == 1) {
> > >
> > >      stack_shift = 1;
> > >
> > >      if (offset >= (mtd->size / 2)) {
> > >
> > >      offset = offset - (mtd->size / 2);
> > >
> > >      nor->spi->master->flags |= SPI_MASTER_U_PAGE;
> > >
> > >      //We can access Upper memory or lower memory, by setting this
> > > flag in controller.
> > >
> > >   } else {
> > >
> > >         nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
> > >
> > >   }
> > >
> > >  }
> > >
> > > same for spi_nor_write() also.
> 
> Let's put the stacked and parallel support on the side for now and focus on single die/chip
> support. Will find a clean way to handle that afterwards.
Ok
> 
> > >
> > > Also in both dual parallel and stacked mode mtd->size should be sum
> > > of both flash device sizes
> 
> Yes, but this information should stay at the spi-nor/mtd level.
> 
> > >
> > > also status register read will change, it needs status from both devices.
> > > like that some changes
> > >
> > > will be needed in core.
> 
> Which is why this needs to be handled in spi-nor.c. The spi-nor core needs to know about the
> parallel/stack setup and handle it differently (read both status bytes, read both IDs, ...).
Ok
> 
> > > > Can somebody share your thoughts on this(Adding Zynq QSPI Dual
> > > > parallel
> > > and
> > > > stacked)?
> > >
> > > I always have a hard time with this naming. I guess stacked is when
> > > you have 2 chips sharing the same I/O bus, and parallel is when you
> > > have 2 chips with one taking all of the I/O and the other taking the
> > > other half. Is that correct?
> > > [naga]: Yes, you are correct, i have attached the diagrams for these mode.
> > >
> > > In parallel mode, with the help of controller STRIPE feature, even
> > > bits of
> > >
> > > data words are located in lower memory and odd bits are located in
> > > upper memory.
> > >
> > > Data management will be taken care by controller in both modes
> 
> Ok. I need to think about it a bit more. In the meantime, I recommend that you submit a
> version of the driver that does not support parallel and stacked modes.
Ok sure.

Thanks,
Naga Sureshkumar Relli.
> 
> Regards,
> 
> Boris

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

* Re: [RFC PATCH 0/5] RFC for Zynq QSPI
  2018-09-18  7:17         ` Boris Brezillon
  2018-09-18  8:58           ` Naga Sureshkumar Relli
@ 2019-06-26  9:53           ` naga suresh kumar
  1 sibling, 0 replies; 15+ messages in thread
From: naga suresh kumar @ 2019-06-26  9:53 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: vigneshr, linux-spi, Marek Vašut, Mark Brown,
	Naga Sureshkumar Relli, linux-mtd, Brian Norris, David Woodhouse

Hi Boris,

On Tue, Sep 18, 2018 at 12:47 PM Boris Brezillon
<boris.brezillon@bootlin.com> wrote:
>
> Hi Naga,
>
> On Tue, 18 Sep 2018 11:26:05 +0530
> naga suresh kumar <nagasureshkumarrelli@gmail.com> wrote:
>
> > Hi,
> >
> > Please see my reply inline by prefacing my name, currently i am facing
> > issues with my outlook. the reply looks not good but please don't mind.
>
> I do mind. Please find a way to fix that, 'cause I'm pretty sure others
> will soon complain about that too.
>
>
> > > > As i said, it needs tweaking the mtd->size and spi read/write addresses.
> > >
> > > Can you elaborate a bit on why you think this is needed? Did you look
> > > at [1]? Maybe it will prevent us from exposing the flash size at the
> > > spi-mem level.
> > > [naga]: some example snippet below
> > >
> > > spi_nor_read () {
> > >
> > >    if (nor->isparallel == 1)
> > >
> > >    {
> > >
> > >       offset /= 2; //byte stripping will happen here
> > >
> > >        nor->spi->master->flags |= SPI_DATA_STRIPE;
> > >
> > >        //By setting this flag even bits of data word located in lower
> > > memory and odd are in upper memory
> > >
> > >    }
> > >
> > >    if (nor->isstacked == 1) {
> > >
> > >      stack_shift = 1;
> > >
> > >      if (offset >= (mtd->size / 2)) {
> > >
> > >      offset = offset - (mtd->size / 2);
> > >
> > >      nor->spi->master->flags |= SPI_MASTER_U_PAGE;
> > >
> > >      //We can access Upper memory or lower memory, by setting this flag in
> > > controller.
> > >
> > >   } else {
> > >
> > >         nor->spi->master->flags &= ~SPI_MASTER_U_PAGE;
> > >
> > >   }
> > >
> > >  }
> > >
> > > same for spi_nor_write() also.
>
> Let's put the stacked and parallel support on the side for now and
> focus on single die/chip support. Will find a clean way to handle that
> afterwards.
>
> > >
> > > Also in both dual parallel and stacked mode mtd->size should be sum of
> > > both flash device sizes
>
> Yes, but this information should stay at the spi-nor/mtd level.
>
> > >
> > > also status register read will change, it needs status from both devices.
> > > like that some changes
> > >
> > > will be needed in core.
>
> Which is why this needs to be handled in spi-nor.c. The spi-nor core
> needs to know about the parallel/stack setup and handle it differently
> (read both status bytes, read both IDs, ...).
>
> > > > Can somebody share your thoughts on this(Adding Zynq QSPI Dual parallel
> > > and
> > > > stacked)?
> > >
> > > I always have a hard time with this naming. I guess stacked is when you
> > > have 2 chips sharing the same I/O bus, and parallel is when you have 2
> > > chips with one taking all of the I/O and the other taking the other
> > > half. Is that correct?
> > > [naga]: Yes, you are correct, i have attached the diagrams for these mode.
> > >
> > > In parallel mode, with the help of controller STRIPE feature, even bits of
> > >
> > > data words are located in lower memory and odd bits are located in upper
> > > memory.
> > >
> > > Data management will be taken care by controller in both modes
>
> Ok. I need to think about it a bit more. In the meantime, I recommend
> that you submit a version of the driver that does not support parallel
> and stacked modes.

As you suggested previously, i have added the support for Zynq QSPI
controller to support single chip.
Currently i am planning to add support for Parallel and Stacked for the same.
Could you please provide your implementation thoughts on this Parallel
and Stacked mode?

From my side, i am thinking like below.
Will take two config entries CONFIG_SPI_XLNX_PARALLEL and
CONFIG_SPI_XLNX_STACKED
and
#ifdef CONFIG_SPI_XILINX_PARALLEL
   #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
   ------
  .sector_size = (_sector_size) << 1, \
  .n_sectors = (_n_sectors), \
  .page_size = (256 << 1), \
  .flags = (_flags | SPI_NOR_XLNX_PARALLEL),
#elif CONFIG_SPI_XILINX_STACKED
    #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
    -------
  .sector_size = (_sector_size), \
  .n_sectors = (_n_sectors << 1), \
  .page_size = 256, \
  .flags = (_flags | SPI_NOR_XLNX_STACKED),
#else
  #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
   ------
 .sector_size = (_sector_size), \
  .n_sectors = (_n_sectors), \
  .page_size = 256, \
 .flags = (_flags),
 #endif
>

And in the spi_nor_scan()
if (info->flags & (SPI_NOR_XLNX_PARALLEL || SPI_NOR_XLNX_STACKED)) {
        mtd->size = (params.size << 1);
        if (info->flags & SPI_NOR_XLNX_PARALLEL) {
            nor->spi->controller->flags |= (SPI_CONTROLLER_BOTH_CS |
SPI_CONTROLLER_DATA_STRIPE);
            nor->flags |= SNOR_F_XLNX_PARALLEL;
        } else if (info->flags & SPI_NOR_XLNX_STACKED) {
            nor->flags |= SNOR_F_XLNX_STACKED;
       }
}
Based on these flags, will alter the address to write/read, in
spi_nor_write() and spi_nor_read().
i will send an RFC patch with this update just to give show case this feature.

So could you please provide a way to implement the same?
i have also seen, that NXP flex controller is also supporting Parallel
flash mode, but not sure how
they are handling the chip select and mtd->size, page_size etc...
Sorry to bother you, but your suggestions helps a lot.

Thanks,
Naga Sureshkumar Relli

> Regards,
>
> Boris

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

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

end of thread, other threads:[~2019-06-26  9:54 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-23 12:21 [RFC PATCH 0/5] RFC for Zynq QSPI Naga Sureshkumar Relli
2018-03-23 12:21 ` [RFC PATCH 1/5] spi: Add support for Zynq qspi controller Naga Sureshkumar Relli
2018-03-23 12:21 ` [RFC PATCH 2/5] mtd: spi-nor: Add support for Zynq QSPI controller Naga Sureshkumar Relli
2018-03-23 12:22 ` [RFC PATCH 3/5] mtd: spi-nor: Add Dual Parallel and Stacked support for Zynq QSPI Naga Sureshkumar Relli
2018-03-23 12:22 ` [RFC PATCH 4/5] spi: Add PM Support for Zynq QSPI controller Naga Sureshkumar Relli
2018-03-23 12:22 ` [RFC PATCH 5/5] devicetree: Add devicetree bindings documentation for Naga Sureshkumar Relli
2018-03-23 14:47   ` Miquel Raynal
2018-03-23 12:52 ` [RFC PATCH 0/5] RFC for Zynq QSPI Marek Vasut
2018-03-23 13:39   ` Naga Sureshkumar Relli
2018-04-16  7:25 ` Naga Sureshkumar Relli
     [not found]   ` <CALgLF9K=rZxVx_QXvV_LH3OhidTcguZ=K=p=Lf9AfFbk4H+8Zw@mail.gmail.com>
2018-09-17 15:05     ` Boris Brezillon
2018-09-17 15:07       ` Boris Brezillon
     [not found]       ` <CALgLF9JVweEcEm6L33HaHp4HtXhmsZL7J5FDe8+O11i0Q1nukg@mail.gmail.com>
2018-09-18  7:17         ` Boris Brezillon
2018-09-18  8:58           ` Naga Sureshkumar Relli
2019-06-26  9:53           ` naga suresh kumar

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