mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 00/10] spi: spi-mem and fsl-qspi support
@ 2019-05-03  9:33 Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 01/10] mtd: spi-nor: cadence: add cqspi_set_protocol Steffen Trumtrar
                   ` (10 more replies)
  0 siblings, 11 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Hi!

This series imports the spi-mem framework from linux into barebox.

The first patches sync the respective driver with linux to ease
porting of the following patches.
Then drivers/spi/spi.c is updated to use the spi-mem framework.
The Freescale QSPI controller driver, which uses this framework,
is ported from linux; the older driver was removed from mainline
linux and wasn't even working correctly.
Finally drivers/mtd/devices/m25p80.c is simplified by using spi-mem.

The series was tested with the Layerscape LS1046a and SocFPGA Cyclone5
SoCs.

Best regards,
Steffen

Steffen Trumtrar (10):
  mtd: spi-nor: cadence: add cqspi_set_protocol
  spi: Generalize SPI "master" to "controller"
  spi: Import more spi mode defines from Linux
  spi: Extend the core to ease integration of SPI memory controllers
  mtd: spi-nor: remove unused write_enable from write_reg
  mtd: spi-nor: remove unused read_xfer/write_xfer hooks
  spi: add driver for Freescale QSPI controller
  mtd: spi-nor: introduce SPI 1-2-2 and SPI 1-4-4 protocols
  mtd: spi-nor: provide default erase_sector implementation
  mtd: devices: m25p80: use the spi_mem_xx() API

 commands/spi.c                        |  16 +-
 drivers/mtd/devices/Kconfig           |   1 +-
 drivers/mtd/devices/m25p80.c          | 158 ++---
 drivers/mtd/spi-nor/cadence-quadspi.c | 257 ++++----
 drivers/mtd/spi-nor/spi-nor.c         | 669 +++++++++++++++------
 drivers/spi/Kconfig                   |  18 +-
 drivers/spi/Makefile                  |   2 +-
 drivers/spi/spi-fsl-qspi.c            | 869 +++++++++++++++++++++++++++-
 drivers/spi/spi-mem.c                 | 524 ++++++++++++++++-
 drivers/spi/spi.c                     | 110 ++-
 include/linux/mtd/spi-nor.h           | 230 +++++--
 include/linux/spi/spi-mem.h           | 307 ++++++++++-
 include/spi/spi.h                     |  95 ++-
 13 files changed, 2777 insertions(+), 479 deletions(-)
 create mode 100644 drivers/spi/spi-fsl-qspi.c
 create mode 100644 drivers/spi/spi-mem.c
 create mode 100644 include/linux/spi/spi-mem.h

base-commit: 9688b49cd3bc0b61a019e8e1311236c9975a0777
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 01/10] mtd: spi-nor: cadence: add cqspi_set_protocol
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 02/10] spi: Generalize SPI "master" to "controller" Steffen Trumtrar
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Sync up the driver with the original linux v4.7 driver version.

As only Quad-SPI mode is/was supported, the function was not
ported in the initial move to barebox.
To make future synchronizations with the kernel driver easier,
add this function and reorder the functions in the driver
accordingly.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/spi-nor/cadence-quadspi.c | 176 +++++++++++++++++----------
 1 file changed, 113 insertions(+), 63 deletions(-)

diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index ed5377bd4ba0..4142f0de7dcb 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -45,6 +45,9 @@ struct cqspi_flash_pdata {
 	unsigned int tsd2d_ns;
 	unsigned int tchsh_ns;
 	unsigned int tslch_ns;
+	u8	     inst_width;
+	u8	     addr_width;
+	u8	     data_width;
 };
 
 struct cqspi_st {
@@ -287,9 +290,10 @@ static unsigned int cqspi_calc_rdreg(struct spi_nor *nor, u8 opcode)
 
 	f_pdata = &cqspi->f_pdata[cqspi->current_cs];
 
-	if (nor->flash_read == SPI_NOR_QUAD)
-		rdreg |= (CQSPI_INST_TYPE_QUAD
-			  << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB);
+	rdreg |= f_pdata->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB;
+	rdreg |= f_pdata->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB;
+	rdreg |= f_pdata->data_width << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
+
 	return rdreg;
 }
 
@@ -427,6 +431,7 @@ static int cqspi_command_write_addr(struct spi_nor *nor,
 static int cqspi_indirect_read_setup(struct spi_nor *nor,
 				     unsigned int from_addr)
 {
+	struct cqspi_flash_pdata *f_pdata;
 	struct cqspi_st *cqspi = nor->priv;
 	unsigned int ahb_base = (unsigned int) cqspi->ahb_base;
 	void __iomem *reg_base = cqspi->iobase;
@@ -437,6 +442,7 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
 	writel(ahb_base & CQSPI_INDIRECTTRIGGER_ADDR_MASK,
 	       reg_base + CQSPI_REG_INDIRECTTRIGGER);
 	writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
+	f_pdata = &cqspi->f_pdata[cqspi->current_cs];
 
 	reg = nor->read_opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
 	reg |= cqspi_calc_rdreg(nor, nor->read_opcode);
@@ -444,14 +450,10 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
 	/* Setup dummy clock cycles */
 	dummy_bytes = nor->read_dummy / 8;
 
-	if (dummy_bytes) {
-		struct cqspi_flash_pdata *f_pdata;
-
-		f_pdata = &cqspi->f_pdata[cqspi->current_cs];
-
-		if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
-			dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
+	if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
+		dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
 
+	if (dummy_bytes) {
 		reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB);
 		/* Set mode bits high to ensure chip doesn't enter XIP */
 		writel(0xFF, reg_base + CQSPI_REG_MODE_BIT);
@@ -459,7 +461,8 @@ static int cqspi_indirect_read_setup(struct spi_nor *nor,
 		/* Convert to clock cycles. */
 		dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE;
 		/* Need to subtract the mode byte (8 clocks). */
-		dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
+		if (f_pdata->inst_width != CQSPI_INST_TYPE_QUAD)
+			dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
 
 		if (dummy_clk)
 			reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
@@ -676,53 +679,6 @@ failwr:
 	return ret;
 }
 
-static void cqspi_write(struct spi_nor *nor, loff_t to,
-			size_t len, size_t *retlen, const u_char *buf)
-{
-	int ret;
-
-	if (!IS_ENABLED(CONFIG_MTD_WRITE))
-		return;
-
-	ret = cqspi_indirect_write_setup(nor, to);
-	if (ret == 0) {
-		ret = cqspi_indirect_write_execute(nor, buf, len);
-		if (ret == 0)
-			*retlen += len;
-	}
-}
-
-static int cqspi_read(struct spi_nor *nor, loff_t from,
-		      size_t len, size_t *retlen, u_char *buf)
-{
-	int ret;
-
-	ret = cqspi_indirect_read_setup(nor, from);
-	if (ret == 0) {
-		ret = cqspi_indirect_read_execute(nor, buf, len);
-		if (ret == 0)
-			*retlen += len;
-	}
-	return ret;
-}
-
-static int cqspi_erase(struct spi_nor *nor, loff_t offs)
-{
-	int ret;
-
-	/* Send write enable, then erase commands. */
-	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
-	if (ret)
-		return ret;
-
-	/* Set up command buffer. */
-	ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs);
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
 static unsigned int calculate_ticks_for_ns(unsigned int ref_clk_hz,
 					   unsigned int ns_val)
 {
@@ -908,7 +864,7 @@ static void cqspi_switch_cs(struct cqspi_st *cqspi, unsigned int cs)
 	cqspi_controller_enable(cqspi);
 }
 
-static int cqspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
+static int cqspi_configure(struct spi_nor *nor)
 {
 	struct cqspi_st *cqspi = nor->priv;
 	int cs = cqspi_find_chipselect(nor);
@@ -936,13 +892,106 @@ static int cqspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
 	return 0;
 }
 
+static int cqspi_set_protocol(struct spi_nor *nor, const int read)
+{
+	struct cqspi_st *cqspi = nor->priv;
+	struct cqspi_flash_pdata *f_pdata;
+
+	f_pdata = &cqspi->f_pdata[cqspi->current_cs];
+
+	f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE;
+	f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE;
+	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
+
+	if (read) {
+		switch (nor->flash_read) {
+		case SPI_NOR_NORMAL:
+		case SPI_NOR_FAST:
+			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
+			break;
+		case SPI_NOR_DUAL:
+			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
+			break;
+		case SPI_NOR_QUAD:
+			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	cqspi_configure(nor);
+
+	return 0;
+}
+
+static void cqspi_write(struct spi_nor *nor, loff_t to,
+			size_t len, size_t *retlen, const u_char *buf)
+{
+	int ret;
+
+	if (!IS_ENABLED(CONFIG_MTD_WRITE))
+		return;
+
+	ret = cqspi_set_protocol(nor, 0);
+	if (ret)
+		return;
+
+	ret = cqspi_indirect_write_setup(nor, to);
+	if (ret == 0) {
+		ret = cqspi_indirect_write_execute(nor, buf, len);
+		if (ret == 0)
+			*retlen += len;
+	}
+}
+
+static int cqspi_read(struct spi_nor *nor, loff_t from,
+		      size_t len, size_t *retlen, u_char *buf)
+{
+	int ret;
+
+	ret = cqspi_set_protocol(nor, 1);
+	if (ret)
+		return ret;
+
+	ret = cqspi_indirect_read_setup(nor, from);
+	if (ret == 0) {
+		ret = cqspi_indirect_read_execute(nor, buf, len);
+		if (ret == 0)
+			*retlen += len;
+	}
+	return ret;
+}
+
+static int cqspi_erase(struct spi_nor *nor, loff_t offs)
+{
+	int ret;
+
+	ret = cqspi_set_protocol(nor, 1);
+	if (ret)
+		return ret;
+
+	/* Send write enable, then erase commands. */
+	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+	if (ret)
+		return ret;
+
+	/* Set up command buffer. */
+	ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 {
 	int ret;
 
-	cqspi_prep(nor, SPI_NOR_OPS_READ);
+	ret = cqspi_set_protocol(nor, 0);
+	if (!ret)
+		ret = cqspi_command_read(nor, &opcode, 1, buf, len);
 
-	ret = cqspi_command_read(nor, &opcode, 1, buf, len);
 	return ret;
 }
 
@@ -954,9 +1003,10 @@ static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
 	if (!IS_ENABLED(CONFIG_MTD_WRITE))
 		return -ENOTSUPP;
 
-	cqspi_prep(nor, SPI_NOR_OPS_WRITE);
+	ret = cqspi_set_protocol(nor, 0);
+	if (!ret)
+		ret = cqspi_command_write(nor, opcode, buf, len);
 
-	ret = cqspi_command_write(nor, opcode, buf, len);
 	return ret;
 }
 
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 02/10] spi: Generalize SPI "master" to "controller"
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 01/10] mtd: spi-nor: cadence: add cqspi_set_protocol Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-11  7:36   ` Alexander Kurz
  2019-05-03  9:33 ` [PATCH 03/10] spi: Import more spi mode defines from Linux Steffen Trumtrar
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Sync with Linux v5.1-rc1.

This is the barebox adoption of the commit

  commit 8caab75fd2c2a92667cbb1cd315720bede3feaa9
  Author: Geert Uytterhoeven <geert+renesas@glider.be>
  Date:   Tue Jun 13 13:23:52 2017 +0200

      spi: Generalize SPI "master" to "controller"

      Now struct spi_master is used for both SPI master and slave controllers,
      it makes sense to rename it to struct spi_controller, and replace
      "master" by "controller" where appropriate.

      For now this conversion is done for SPI core infrastructure only.
      Wrappers are provided for backwards compatibility, until all SPI drivers
      have been converted.

      Noteworthy details:
	- SPI_MASTER_GPIO_SS is retained, as it only makes sense for SPI
	  master controllers,
	- spi_busnum_to_master() is retained, as it looks up masters only,
	- A new field spi_device.controller is added, but spi_device.master is
	  retained for compatibility (both are always initialized by
	  spi_alloc_device()),
	- spi_flash_read() is used by SPI masters only.

      Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
      Signed-off-by: Mark Brown <broonie@kernel.org>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 commands/spi.c    | 16 +++++------
 drivers/spi/spi.c | 71 ++++++++++++++++++++++++------------------------
 include/spi/spi.h | 30 ++++++++++++--------
 3 files changed, 62 insertions(+), 55 deletions(-)

diff --git a/commands/spi.c b/commands/spi.c
index 7bf193b0bbbb..55a0e255af17 100644
--- a/commands/spi.c
+++ b/commands/spi.c
@@ -62,21 +62,21 @@ static int do_spi(int argc, char *argv[])
 		return COMMAND_ERROR_USAGE;
 
 
-	spi.master = spi_get_master(bus);
-	if (!spi.master) {
+	spi.controller = spi_get_controller(bus);
+	if (!spi.controller) {
 		printf("spi bus %d not found\n", bus);
 		return -ENODEV;
 	}
 
-	if (spi.chip_select >= spi.master->num_chipselect) {
-		printf("spi chip select (%d) >= master num chipselect (%d)\n",
-			spi.chip_select, spi.master->num_chipselect);
+	if (spi.chip_select >= spi.controller->num_chipselect) {
+		printf("spi chip select (%d) >= controller num chipselect (%d)\n",
+			spi.chip_select, spi.controller->num_chipselect);
 		return -EINVAL;
 	}
 
-	ret = spi.master->setup(&spi);
+	ret = spi.controller->setup(&spi);
 	if (ret) {
-		printf("can not setup the master (%d)\n", ret);
+		printf("can not setup the controller (%d)\n", ret);
 		return ret;
 	}
 
@@ -93,7 +93,7 @@ static int do_spi(int argc, char *argv[])
 	byte_per_word = max(spi.bits_per_word / 8, 1);
 	if (verbose) {
 		printf("device config\n");
-		printf("    bus_num       = %d\n", spi.master->bus_num);
+		printf("    bus_num       = %d\n", spi.controller->bus_num);
 		printf("    max_speed_hz  = %d\n", spi.max_speed_hz);
 		printf("    chip_select   = %d\n", spi.chip_select);
 		printf("    mode          = 0x%x\n", spi.mode);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 25bb988794a9..7756304f1981 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -54,22 +54,22 @@ static LIST_HEAD(board_list);
  *
  * Returns the new device, or NULL.
  */
-struct spi_device *spi_new_device(struct spi_master *master,
+struct spi_device *spi_new_device(struct spi_controller *ctrl,
 				  struct spi_board_info *chip)
 {
 	struct spi_device	*proxy;
 	int			status;
 
 	/* Chipselects are numbered 0..max; validate. */
-	if (chip->chip_select >= master->num_chipselect) {
+	if (chip->chip_select >= ctrl->num_chipselect) {
 		debug("cs%d > max %d\n",
 			chip->chip_select,
-			master->num_chipselect);
+			ctrl->num_chipselect);
 		return NULL;
 	}
 
 	proxy = xzalloc(sizeof *proxy);
-	proxy->master = master;
+	proxy->master = ctrl;
 	proxy->chip_select = chip->chip_select;
 	proxy->max_speed_hz = chip->max_speed_hz;
 	proxy->mode = chip->mode;
@@ -81,10 +81,11 @@ struct spi_device *spi_new_device(struct spi_master *master,
 	proxy->dev.id = DEVICE_ID_DYNAMIC;
 	proxy->dev.type_data = proxy;
 	proxy->dev.device_node = chip->device_node;
-	proxy->dev.parent = master->dev;
+	proxy->dev.parent = ctrl->dev;
+	proxy->master = proxy->controller = ctrl;
 
 	/* drivers may modify this initial i/o setup */
-	status = master->setup(proxy);
+	status = ctrl->setup(proxy);
 	if (status < 0) {
 		printf("can't setup %s, status %d\n",
 				proxy->dev.name, status);
@@ -100,12 +101,12 @@ fail:
 }
 EXPORT_SYMBOL(spi_new_device);
 
-static void spi_of_register_slaves(struct spi_master *master)
+static void spi_of_register_slaves(struct spi_controller *ctrl)
 {
 	struct device_node *n;
 	struct spi_board_info chip;
 	struct property *reg;
-	struct device_node *node = master->dev->device_node;
+	struct device_node *node = ctrl->dev->device_node;
 
 	if (!IS_ENABLED(CONFIG_OFDEVICE))
 		return;
@@ -116,7 +117,7 @@ static void spi_of_register_slaves(struct spi_master *master)
 	for_each_available_child_of_node(node, n) {
 		memset(&chip, 0, sizeof(chip));
 		chip.name = xstrdup(n->name);
-		chip.bus_num = master->bus_num;
+		chip.bus_num = ctrl->bus_num;
 		/* Mode (clock phase/polarity/etc.) */
 		if (of_property_read_bool(n, "spi-cpha"))
 			chip.mode |= SPI_CPHA;
@@ -171,7 +172,7 @@ spi_register_board_info(struct spi_board_info const *info, int n)
 	return 0;
 }
 
-static void scan_boardinfo(struct spi_master *master)
+static void scan_boardinfo(struct spi_controller *ctrl)
 {
 	struct boardinfo	*bi;
 
@@ -180,27 +181,27 @@ static void scan_boardinfo(struct spi_master *master)
 		unsigned		n;
 
 		for (n = bi->n_board_info; n > 0; n--, chip++) {
-			debug("%s %d %d\n", __FUNCTION__, chip->bus_num, master->bus_num);
-			if (chip->bus_num != master->bus_num)
+			debug("%s %d %d\n", __FUNCTION__, chip->bus_num, ctrl->bus_num);
+			if (chip->bus_num != ctrl->bus_num)
 				continue;
 			/* NOTE: this relies on spi_new_device to
 			 * issue diagnostics when given bogus inputs
 			 */
-			(void) spi_new_device(master, chip);
+			(void) spi_new_device(ctrl, chip);
 		}
 	}
 }
 
-static LIST_HEAD(spi_master_list);
+static LIST_HEAD(spi_controller_list);
 
 /**
- * spi_register_master - register SPI master controller
- * @master: initialized master, originally from spi_alloc_master()
+ * spi_register_ctrl - register SPI ctrl controller
+ * @ctrl: initialized ctrl, originally from spi_alloc_ctrl()
  * Context: can sleep
  *
- * SPI master controllers connect to their drivers using some non-SPI bus,
+ * SPI controllers connect to their drivers using some non-SPI bus,
  * such as the platform bus.  The final stage of probe() in that code
- * includes calling spi_register_master() to hook up to this SPI bus glue.
+ * includes calling spi_register_ctrl() to hook up to this SPI bus glue.
  *
  * SPI controllers use board specific (often SOC specific) bus numbers,
  * and board-specific addressing for SPI devices combines those numbers
@@ -209,47 +210,47 @@ static LIST_HEAD(spi_master_list);
  * chip is at which address.
  *
  * This must be called from context that can sleep.  It returns zero on
- * success, else a negative error code (dropping the master's refcount).
+ * success, else a negative error code (dropping the ctrl's refcount).
  * After a successful return, the caller is responsible for calling
- * spi_unregister_master().
+ * spi_unregister_ctrl().
  */
-int spi_register_master(struct spi_master *master)
+int spi_register_controller(struct spi_controller *ctrl)
 {
 	static int dyn_bus_id = (1 << 15) - 1;
 	int			status = -ENODEV;
 
-	debug("%s: %s:%d\n", __func__, master->dev->name, master->dev->id);
+	debug("%s: %s:%d\n", __func__, ctrl->dev->name, ctrl->dev->id);
 
 	/* even if it's just one always-selected device, there must
 	 * be at least one chipselect
 	 */
-	if (master->num_chipselect == 0)
+	if (ctrl->num_chipselect == 0)
 		return -EINVAL;
 
-	if ((master->bus_num < 0) && master->dev->device_node)
-		master->bus_num = of_alias_get_id(master->dev->device_node, "spi");
+	if ((ctrl->bus_num < 0) && ctrl->dev->device_node)
+		ctrl->bus_num = of_alias_get_id(ctrl->dev->device_node, "spi");
 
 	/* convention:  dynamically assigned bus IDs count down from the max */
-	if (master->bus_num < 0)
-		master->bus_num = dyn_bus_id--;
+	if (ctrl->bus_num < 0)
+		ctrl->bus_num = dyn_bus_id--;
 
-	list_add_tail(&master->list, &spi_master_list);
+	list_add_tail(&ctrl->list, &spi_controller_list);
 
-	spi_of_register_slaves(master);
+	spi_of_register_slaves(ctrl);
 
 	/* populate children from any spi device tables */
-	scan_boardinfo(master);
+	scan_boardinfo(ctrl);
 	status = 0;
 
 	return status;
 }
-EXPORT_SYMBOL(spi_register_master);
+EXPORT_SYMBOL(spi_register_ctrl);
 
-struct spi_master *spi_get_master(int bus)
+struct spi_controller *spi_get_controller(int bus)
 {
-	struct spi_master* m;
+	struct spi_controller* m;
 
-	list_for_each_entry(m, &spi_master_list, list) {
+	list_for_each_entry(m, &spi_controller_list, list) {
 		if (m->bus_num == bus)
 			return m;
 	}
@@ -259,7 +260,7 @@ struct spi_master *spi_get_master(int bus)
 
 int spi_sync(struct spi_device *spi, struct spi_message *message)
 {
-	return spi->master->transfer(spi, message);
+	return spi->controller->transfer(spi, message);
 }
 
 /**
diff --git a/include/spi/spi.h b/include/spi/spi.h
index 620e5e57b4f8..8c6927da4107 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -20,13 +20,14 @@ struct spi_board_info {
 };
 
 /**
- * struct spi_device - Master side proxy for an SPI slave device
+ * struct spi_device - Controller side proxy for an SPI slave device
  * @dev: Driver model representation of the device.
- * @master: SPI controller used with the device.
+ * @controller: SPI controller used with the device.
+ * @master: Copy of controller, for backwards compatibility
  * @max_speed_hz: Maximum clock rate to be used with this chip
  *	(on this board); may be changed by the device's driver.
  *	The spi_transfer.speed_hz can override this for each transfer.
- * @chip_select: Chipselect, distinguishing chips handled by @master.
+ * @chip_select: Chipselect, distinguishing chips handled by @controller.
  * @mode: The spi mode defines how data is clocked out and in.
  *	This may be changed by the device's driver.
  *	The "active low" default for chipselect mode can be overridden
@@ -59,7 +60,8 @@ struct spi_board_info {
  */
 struct spi_device {
 	struct device_d		dev;
-	struct spi_master	*master;
+	struct spi_controller	*controller;
+	struct spi_controller	*master;	/* compatibility layer */
 	u32			max_speed_hz;
 	u8			chip_select;
 	u8			mode;
@@ -93,7 +95,7 @@ struct spi_device {
 struct spi_message;
 
 /**
- * struct spi_master - interface to SPI master controller
+ * struct spi_controller - interface to SPI master or slave controller
  * @dev: device interface to this driver
  * @bus_num: board-specific (and often SOC-specific) identifier for a
  *	given SPI controller.
@@ -108,8 +110,9 @@ struct spi_message;
  *	the device whose settings are being modified.
  * @transfer: adds a message to the controller's transfer queue.
  * @cleanup: frees controller-specific state
+ * @list: link with the global spi_controller list
  *
- * Each SPI master controller can communicate with one or more @spi_device
+ * Each SPI controller can communicate with one or more @spi_device
  * children.  These make a small bus, sharing MOSI, MISO and SCK signals
  * but not chip select signals.  Each device may be configured to use a
  * different clock rate, since those shared signals are ignored unless
@@ -120,7 +123,7 @@ struct spi_message;
  * an SPI slave device.  For each such message it queues, it calls the
  * message's completion function when the transaction completes.
  */
-struct spi_master {
+struct spi_controller {
 	struct device_d *dev;
 
 	/* other than negative (== assign one dynamically), bus_num is fully
@@ -147,7 +150,7 @@ struct spi_master {
 	 *   any other request management
 	 * + To a given spi_device, message queueing is pure fifo
 	 *
-	 * + The master's main job is to process its message queue,
+	 * + The controller's main job is to process its message queue,
 	 *   selecting a chip then transferring data
 	 * + If there are multiple spi_device children, the i/o queue
 	 *   arbitration algorithm is unspecified (round robin, fifo,
@@ -161,12 +164,15 @@ struct spi_master {
 	int			(*transfer)(struct spi_device *spi,
 						struct spi_message *mesg);
 
-	/* called on release() to free memory provided by spi_master */
+	/* called on release() to free memory provided by spi_controller */
 	void			(*cleanup)(struct spi_device *spi);
 
 	struct list_head list;
 };
 
+#define spi_master  			spi_controller
+#define spi_register_master(_ctrl)	spi_register_controller(_ctrl)
+
 /*---------------------------------------------------------------------------*/
 
 /*
@@ -341,9 +347,9 @@ spi_transfer_del(struct spi_transfer *t)
 
 int spi_sync(struct spi_device *spi, struct spi_message *message);
 
-struct spi_device *spi_new_device(struct spi_master *master,
+struct spi_device *spi_new_device(struct spi_controller *ctrl,
 				  struct spi_board_info *chip);
-int spi_register_master(struct spi_master *master);
+int spi_register_controller(struct spi_controller *ctrl);
 
 #ifdef CONFIG_SPI
 int spi_register_board_info(struct spi_board_info const *info, int num);
@@ -431,7 +437,7 @@ static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
 
 extern struct bus_type spi_bus;
 
-struct spi_master *spi_get_master(int bus);
+struct spi_controller *spi_get_controller(int bus);
 
 static inline int spi_driver_register(struct driver_d *drv)
 {
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 03/10] spi: Import more spi mode defines from Linux
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 01/10] mtd: spi-nor: cadence: add cqspi_set_protocol Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 02/10] spi: Generalize SPI "master" to "controller" Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 04/10] spi: Extend the core to ease integration of SPI memory controllers Steffen Trumtrar
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Sync with Linux v5.1-rc1.

Define more spi mode flags.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 include/spi/spi.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/include/spi/spi.h b/include/spi/spi.h
index 8c6927da4107..6eeaf254c713 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -75,6 +75,16 @@ struct spi_device {
 #define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */
 #define	SPI_3WIRE	0x10			/* SI/SO signals shared */
 #define	SPI_LOOP	0x20			/* loopback mode */
+#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */
+#define	SPI_READY	0x80			/* slave pulls low to pause */
+#define	SPI_TX_DUAL	0x100			/* transmit with 2 wires */
+#define	SPI_TX_QUAD	0x200			/* transmit with 4 wires */
+#define	SPI_RX_DUAL	0x400			/* receive with 2 wires */
+#define	SPI_RX_QUAD	0x800			/* receive with 4 wires */
+#define	SPI_CS_WORD	0x1000			/* toggle cs after each word */
+#define	SPI_TX_OCTAL	0x2000			/* transmit with 8 wires */
+#define	SPI_RX_OCTAL	0x4000			/* receive with 8 wires */
+#define	SPI_3WIRE_HIZ	0x8000			/* high impedance turnaround */
 	u8			bits_per_word;
 	int			irq;
 	void			*controller_state;
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 04/10] spi: Extend the core to ease integration of SPI memory controllers
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (2 preceding siblings ...)
  2019-05-03  9:33 ` [PATCH 03/10] spi: Import more spi mode defines from Linux Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 05/10] mtd: spi-nor: remove unused write_enable from write_reg Steffen Trumtrar
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Sync with Linux v5.1-rc1.

This is the barebox adoption of the commit

  commit c36ff266dc82f4ae797a6f3513c6ffa344f7f1c7
  Author: Boris Brezillon <boris.brezillon@bootlin.com>
  Date:   Thu Apr 26 18:18:14 2018 +0200

      spi: Extend the core to ease integration of SPI memory controllers

      Some controllers are exposing high-level interfaces to access various
      kind of SPI memories. Unfortunately they do not fit in the current
      spi_controller model and usually have drivers placed in
      drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI
      memories in general.

      This is an attempt at defining a SPI memory interface which works for
      all kinds of SPI memories (NORs, NANDs, SRAMs).

      Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
      Reviewed-by: Frieder Schrempf <frieder.schrempf@exceet.de>
      Tested-by: Frieder Schrempf <frieder.schrempf@exceet.de>
      Signed-off-by: Mark Brown <broonie@kernel.org>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/spi-nor/spi-nor.c |  38 +--
 drivers/spi/Kconfig           |   7 +-
 drivers/spi/Makefile          |   1 +-
 drivers/spi/spi-mem.c         | 524 +++++++++++++++++++++++++++++++++++-
 drivers/spi/spi.c             |  39 +++-
 include/linux/spi/spi-mem.h   | 307 +++++++++++++++++++++-
 include/spi/spi.h             |  55 ++++-
 7 files changed, 948 insertions(+), 23 deletions(-)
 create mode 100644 drivers/spi/spi-mem.c
 create mode 100644 include/linux/spi/spi-mem.h

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 85b55c6982ab..851ac1c33e7f 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -784,7 +784,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 	size_t *retlen, const u_char *buf)
 {
 	struct spi_nor *nor = mtd_to_spi_nor(mtd);
-	u32 page_offset, page_size, i;
+	size_t page_offset, page_remain, i;
+	size_t retval;
 	int ret;
 
 	dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
@@ -793,32 +794,23 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
 	if (ret)
 		return ret;
 
-	write_enable(nor);
-
-	page_offset = to & (nor->page_size - 1);
-
-	/* do all the bytes fit onto one page? */
-	if (page_offset + len <= nor->page_size) {
-		nor->write(nor, to, len, retlen, buf);
-	} else {
-		/* the size of data remaining on the first page */
-		page_size = nor->page_size - page_offset;
-		nor->write(nor, to, page_size, retlen, buf);
+	for (i = 0; i < len; ) {
+		ssize_t written;
 
-		/* write everything in nor->page_size chunks */
-		for (i = page_size; i < len; i += page_size) {
-			page_size = len - i;
-			if (page_size > nor->page_size)
-				page_size = nor->page_size;
+		page_offset = (to + i) & (nor->page_size - 1);
+		page_remain = min_t(size_t, nor->page_size - page_offset,
+				    len - i);
 
-			ret = spi_nor_wait_till_ready(nor);
-			if (ret)
-				goto write_err;
+		write_enable(nor);
+		nor->write(nor, to + i, page_remain, &retval, buf + i);
+		written = retval;
 
-			write_enable(nor);
+		ret = spi_nor_wait_till_ready(nor);
+		if (ret)
+			goto write_err;
 
-			nor->write(nor, to + i, page_size, retlen, buf + i);
-		}
+		*retlen += written;
+		i += written;
 	}
 
 	ret = spi_nor_wait_till_ready(nor);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index fed628c58994..650292541f76 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -6,6 +6,13 @@ config SPI
 
 if SPI
 
+config SPI_MEM
+	bool "SPI memory extension"
+	help
+	  Enable this option if you want to enable the SPI memory extension.
+	  This extension is meant to simplify interaction with SPI memories
+	  by providing a high-level interface to send memory-like commands.
+
 config DRIVER_SPI_ALTERA
 	bool "Altera SPI Master driver"
 	depends on NIOS2
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 2329cbfb8da4..841d67d32651 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_SPI) += spi.o
+obj-$(CONFIG_SPI_MEM) += spi-mem.o
 obj-$(CONFIG_DRIVER_SPI_ATH79) += ath79_spi.o
 obj-$(CONFIG_DRIVER_SPI_GPIO) += gpio_spi.o
 obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
new file mode 100644
index 000000000000..b438ed3dcc7a
--- /dev/null
+++ b/drivers/spi/spi-mem.c
@@ -0,0 +1,524 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Exceet Electronics GmbH
+ * Copyright (C) 2018 Bootlin
+ *
+ * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+#include <common.h>
+#include <module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi-mem.h>
+#include <spi/spi.h>
+
+#define SPI_MEM_MAX_BUSWIDTH		8
+
+static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
+{
+	u32 mode = mem->spi->mode;
+
+	switch (buswidth) {
+	case 1:
+		return 0;
+
+	case 2:
+		if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
+		    (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
+			return 0;
+
+		break;
+
+	case 4:
+		if ((tx && (mode & SPI_TX_QUAD)) ||
+		    (!tx && (mode & SPI_RX_QUAD)))
+			return 0;
+
+		break;
+
+	case 8:
+		if ((tx && (mode & SPI_TX_OCTAL)) ||
+		    (!tx && (mode & SPI_RX_OCTAL)))
+			return 0;
+
+		break;
+
+	default:
+		break;
+	}
+
+	return -ENOTSUPP;
+}
+
+static bool spi_mem_default_supports_op(struct spi_mem *mem,
+					const struct spi_mem_op *op)
+{
+	if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
+		return false;
+
+	if (op->addr.nbytes &&
+	    spi_check_buswidth_req(mem, op->addr.buswidth, true))
+		return false;
+
+	if (op->dummy.nbytes &&
+	    spi_check_buswidth_req(mem, op->dummy.buswidth, true))
+		return false;
+
+	if (op->data.dir != SPI_MEM_NO_DATA &&
+	    spi_check_buswidth_req(mem, op->data.buswidth,
+				   op->data.dir == SPI_MEM_DATA_OUT))
+		return false;
+
+	return true;
+}
+EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
+
+static bool spi_mem_buswidth_is_valid(u8 buswidth)
+{
+	if (hweight8(buswidth) > 1 || buswidth > SPI_MEM_MAX_BUSWIDTH)
+		return false;
+
+	return true;
+}
+
+static int spi_mem_check_op(const struct spi_mem_op *op)
+{
+	if (!op->cmd.buswidth)
+		return -EINVAL;
+
+	if ((op->addr.nbytes && !op->addr.buswidth) ||
+	    (op->dummy.nbytes && !op->dummy.buswidth) ||
+	    (op->data.nbytes && !op->data.buswidth))
+		return -EINVAL;
+
+	if (!spi_mem_buswidth_is_valid(op->cmd.buswidth) ||
+	    !spi_mem_buswidth_is_valid(op->addr.buswidth) ||
+	    !spi_mem_buswidth_is_valid(op->dummy.buswidth) ||
+	    !spi_mem_buswidth_is_valid(op->data.buswidth))
+		return -EINVAL;
+
+	return 0;
+}
+
+static bool spi_mem_internal_supports_op(struct spi_mem *mem,
+					 const struct spi_mem_op *op)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+
+	if (ctlr->mem_ops && ctlr->mem_ops->supports_op)
+		return ctlr->mem_ops->supports_op(mem, op);
+
+	return spi_mem_default_supports_op(mem, op);
+}
+
+/**
+ * spi_mem_supports_op() - Check if a memory device and the controller it is
+ *			   connected to support a specific memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to check
+ *
+ * Some controllers are only supporting Single or Dual IOs, others might only
+ * support specific opcodes, or it can even be that the controller and device
+ * both support Quad IOs but the hardware prevents you from using it because
+ * only 2 IO lines are connected.
+ *
+ * This function checks whether a specific operation is supported.
+ *
+ * Return: true if @op is supported, false otherwise.
+ */
+bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	if (spi_mem_check_op(op))
+		return false;
+
+	return spi_mem_internal_supports_op(mem, op);
+}
+EXPORT_SYMBOL_GPL(spi_mem_supports_op);
+
+static int spi_mem_access_start(struct spi_mem *mem)
+{
+	return 0;
+}
+
+static void spi_mem_access_end(struct spi_mem *mem)
+{
+	return;
+}
+
+/**
+ * spi_mem_exec_op() - Execute a memory operation
+ * @mem: the SPI memory
+ * @op: the memory operation to execute
+ *
+ * Executes a memory operation.
+ *
+ * This function first checks that @op is supported and then tries to execute
+ * it.
+ *
+ * Return: 0 in case of success, a negative error code otherwise.
+ */
+int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
+	struct spi_controller *ctlr = mem->spi->controller;
+	struct spi_transfer xfers[4] = { };
+	struct spi_message msg;
+	u8 *tmpbuf;
+	int ret;
+
+	ret = spi_mem_check_op(op);
+	if (ret)
+		return ret;
+
+	if (!spi_mem_internal_supports_op(mem, op))
+		return -ENOTSUPP;
+
+	if (ctlr->mem_ops) {
+		ret = spi_mem_access_start(mem);
+		if (ret)
+			return ret;
+
+		ret = ctlr->mem_ops->exec_op(mem, op);
+
+		spi_mem_access_end(mem);
+
+		/*
+		 * Some controllers only optimize specific paths (typically the
+		 * read path) and expect the core to use the regular SPI
+		 * interface in other cases.
+		 */
+		if (!ret || ret != -ENOTSUPP)
+			return ret;
+	}
+
+	tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
+		     op->dummy.nbytes;
+
+	/*
+	 * Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
+	 * we're guaranteed that this buffer is DMA-able, as required by the
+	 * SPI layer.
+	 */
+	tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL);
+	if (!tmpbuf)
+		return -ENOMEM;
+
+	spi_message_init(&msg);
+
+	tmpbuf[0] = op->cmd.opcode;
+	xfers[xferpos].tx_buf = tmpbuf;
+	xfers[xferpos].len = sizeof(op->cmd.opcode);
+	spi_message_add_tail(&xfers[xferpos], &msg);
+	xferpos++;
+	totalxferlen++;
+
+	if (op->addr.nbytes) {
+		int i;
+
+		for (i = 0; i < op->addr.nbytes; i++)
+			tmpbuf[i + 1] = op->addr.val >>
+					(8 * (op->addr.nbytes - i - 1));
+
+		xfers[xferpos].tx_buf = tmpbuf + 1;
+		xfers[xferpos].len = op->addr.nbytes;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->addr.nbytes;
+	}
+
+	if (op->dummy.nbytes) {
+		memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
+		xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
+		xfers[xferpos].len = op->dummy.nbytes;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->dummy.nbytes;
+	}
+
+	if (op->data.nbytes) {
+		if (op->data.dir == SPI_MEM_DATA_IN)
+			xfers[xferpos].rx_buf = op->data.buf.in;
+		else
+			xfers[xferpos].tx_buf = op->data.buf.out;
+
+		xfers[xferpos].len = op->data.nbytes;
+		spi_message_add_tail(&xfers[xferpos], &msg);
+		xferpos++;
+		totalxferlen += op->data.nbytes;
+	}
+
+	ret = spi_sync(mem->spi, &msg);
+
+	kfree(tmpbuf);
+
+	if (ret)
+		return ret;
+
+	if (msg.actual_length != totalxferlen)
+		return -EIO;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_exec_op);
+
+/**
+ * spi_mem_get_name() - Return the SPI mem device name to be used by the
+ *			upper layer if necessary
+ * @mem: the SPI memory
+ *
+ * This function allows SPI mem users to retrieve the SPI mem device name.
+ * It is useful if the upper layer needs to expose a custom name for
+ * compatibility reasons.
+ *
+ * Return: a string containing the name of the memory device to be used
+ *	   by the SPI mem user
+ */
+const char *spi_mem_get_name(struct spi_mem *mem)
+{
+	return mem->name;
+}
+EXPORT_SYMBOL_GPL(spi_mem_get_name);
+
+/**
+ * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
+ *			      match controller limitations
+ * @mem: the SPI memory
+ * @op: the operation to adjust
+ *
+ * Some controllers have FIFO limitations and must split a data transfer
+ * operation into multiple ones, others require a specific alignment for
+ * optimized accesses. This function allows SPI mem drivers to split a single
+ * operation into multiple sub-operations when required.
+ *
+ * Return: a negative error code if the controller can't properly adjust @op,
+ *	   0 otherwise. Note that @op->data.nbytes will be updated if @op
+ *	   can't be handled in a single step.
+ */
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+	size_t len;
+
+	len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
+
+	if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
+		return ctlr->mem_ops->adjust_op_size(mem, op);
+
+	if (!ctlr->mem_ops || !ctlr->mem_ops->exec_op) {
+		if (len > spi_max_transfer_size(mem->spi))
+			return -EINVAL;
+
+		op->data.nbytes = min3((size_t)op->data.nbytes,
+				      spi_max_transfer_size(mem->spi),
+				      spi_max_message_size(mem->spi) -
+				      len);
+		if (!op->data.nbytes)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
+
+static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
+				      u64 offs, size_t len, void *buf)
+{
+	struct spi_mem_op op = desc->info.op_tmpl;
+	int ret;
+
+	op.addr.val = desc->info.offset + offs;
+	op.data.buf.in = buf;
+	op.data.nbytes = len;
+	ret = spi_mem_adjust_op_size(desc->mem, &op);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(desc->mem, &op);
+	if (ret)
+		return ret;
+
+	return op.data.nbytes;
+}
+
+static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
+				       u64 offs, size_t len, const void *buf)
+{
+	struct spi_mem_op op = desc->info.op_tmpl;
+	int ret;
+
+	op.addr.val = desc->info.offset + offs;
+	op.data.buf.out = buf;
+	op.data.nbytes = len;
+	ret = spi_mem_adjust_op_size(desc->mem, &op);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(desc->mem, &op);
+	if (ret)
+		return ret;
+
+	return op.data.nbytes;
+}
+
+/**
+ * spi_mem_dirmap_create() - Create a direct mapping descriptor
+ * @mem: SPI mem device this direct mapping should be created for
+ * @info: direct mapping information
+ *
+ * This function is creating a direct mapping descriptor which can then be used
+ * to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
+ * If the SPI controller driver does not support direct mapping, this function
+ * fallback to an implementation using spi_mem_exec_op(), so that the caller
+ * doesn't have to bother implementing a fallback on his own.
+ *
+ * Return: a valid pointer in case of success, and ERR_PTR() otherwise.
+ */
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_mem *mem,
+		      const struct spi_mem_dirmap_info *info)
+{
+	struct spi_controller *ctlr = mem->spi->controller;
+	struct spi_mem_dirmap_desc *desc;
+	int ret = -ENOTSUPP;
+
+	/* Make sure the number of address cycles is between 1 and 8 bytes. */
+	if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
+		return ERR_PTR(-EINVAL);
+
+	/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
+	if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
+		return ERR_PTR(-EINVAL);
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	desc->mem = mem;
+	desc->info = *info;
+	if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create)
+		ret = ctlr->mem_ops->dirmap_create(desc);
+
+	if (ret) {
+		desc->nodirmap = true;
+		if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
+			ret = -ENOTSUPP;
+		else
+			ret = 0;
+	}
+
+	if (ret) {
+		kfree(desc);
+		return ERR_PTR(ret);
+	}
+
+	return desc;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
+
+/**
+ * spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
+ * @desc: the direct mapping descriptor to destroy
+ * @info: direct mapping information
+ *
+ * This function destroys a direct mapping descriptor previously created by
+ * spi_mem_dirmap_create().
+ */
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
+{
+	struct spi_controller *ctlr = desc->mem->spi->controller;
+
+	if (!desc->nodirmap && ctlr->mem_ops && ctlr->mem_ops->dirmap_destroy)
+		ctlr->mem_ops->dirmap_destroy(desc);
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
+
+/**
+ * spi_mem_dirmap_dirmap_read() - Read data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start reading from. Note that this is not an absolute
+ *	  offset, but the offset within the direct mapping which already has
+ *	  its own offset
+ * @len: length in bytes
+ * @buf: destination buffer. This buffer must be DMA-able
+ *
+ * This function reads data from a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data read from the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_read() again when that happens.
+ */
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+			    u64 offs, size_t len, void *buf)
+{
+	struct spi_controller *ctlr = desc->mem->spi->controller;
+	ssize_t ret;
+
+	if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
+		return -EINVAL;
+
+	if (!len)
+		return 0;
+
+	if (desc->nodirmap) {
+		ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
+	} else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_read) {
+		ret = spi_mem_access_start(desc->mem);
+		if (ret)
+			return ret;
+
+		ret = ctlr->mem_ops->dirmap_read(desc, offs, len, buf);
+
+		spi_mem_access_end(desc->mem);
+	} else {
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
+
+/**
+ * spi_mem_dirmap_dirmap_write() - Write data through a direct mapping
+ * @desc: direct mapping descriptor
+ * @offs: offset to start writing from. Note that this is not an absolute
+ *	  offset, but the offset within the direct mapping which already has
+ *	  its own offset
+ * @len: length in bytes
+ * @buf: source buffer. This buffer must be DMA-able
+ *
+ * This function writes data to a memory device using a direct mapping
+ * previously instantiated with spi_mem_dirmap_create().
+ *
+ * Return: the amount of data written to the memory device or a negative error
+ * code. Note that the returned size might be smaller than @len, and the caller
+ * is responsible for calling spi_mem_dirmap_write() again when that happens.
+ */
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+			     u64 offs, size_t len, const void *buf)
+{
+	struct spi_controller *ctlr = desc->mem->spi->controller;
+	ssize_t ret;
+
+	if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
+		return -EINVAL;
+
+	if (!len)
+		return 0;
+
+	if (desc->nodirmap) {
+		ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
+	} else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_write) {
+		ret = spi_mem_access_start(desc->mem);
+		if (ret)
+			return ret;
+
+		ret = ctlr->mem_ops->dirmap_write(desc, offs, len, buf);
+
+		spi_mem_access_end(desc->mem);
+	} else {
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 7756304f1981..d9311d4af508 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -19,6 +19,7 @@
  */
 
 #include <common.h>
+#include <linux/spi/spi-mem.h>
 #include <spi/spi.h>
 #include <xfuncs.h>
 #include <malloc.h>
@@ -58,6 +59,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
 				  struct spi_board_info *chip)
 {
 	struct spi_device	*proxy;
+	struct spi_mem		*mem;
 	int			status;
 
 	/* Chipselects are numbered 0..max; validate. */
@@ -84,6 +86,15 @@ struct spi_device *spi_new_device(struct spi_controller *ctrl,
 	proxy->dev.parent = ctrl->dev;
 	proxy->master = proxy->controller = ctrl;
 
+	mem = xzalloc(sizeof *mem);
+	mem->spi = proxy;
+
+	if (ctrl->mem_ops && ctrl->mem_ops->get_name)
+		mem->name = ctrl->mem_ops->get_name(mem);
+	else
+		mem->name = dev_name(&proxy->dev);
+	proxy->mem = mem;
+
 	/* drivers may modify this initial i/o setup */
 	status = ctrl->setup(proxy);
 	if (status < 0) {
@@ -194,6 +205,26 @@ static void scan_boardinfo(struct spi_controller *ctrl)
 
 static LIST_HEAD(spi_controller_list);
 
+static int spi_controller_check_ops(struct spi_controller *ctlr)
+{
+	/*
+	 * The controller may implement only the high-level SPI-memory like
+	 * operations if it does not support regular SPI transfers, and this is
+	 * valid use case.
+	 * If ->mem_ops is NULL, we request that at least one of the
+	 * ->transfer_xxx() method be implemented.
+	 */
+	if (ctlr->mem_ops) {
+		if (!ctlr->mem_ops->exec_op)
+			return -EINVAL;
+	} else if (!ctlr->transfer) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
 /**
  * spi_register_ctrl - register SPI ctrl controller
  * @ctrl: initialized ctrl, originally from spi_alloc_ctrl()
@@ -221,6 +252,14 @@ int spi_register_controller(struct spi_controller *ctrl)
 
 	debug("%s: %s:%d\n", __func__, ctrl->dev->name, ctrl->dev->id);
 
+	/*
+	 * Make sure all necessary hooks are implemented before registering
+	 * the SPI controller.
+	 */
+	status = spi_controller_check_ops(ctrl);
+	if (status)
+		return status;
+
 	/* even if it's just one always-selected device, there must
 	 * be at least one chipselect
 	 */
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
new file mode 100644
index 000000000000..f65104d2d198
--- /dev/null
+++ b/include/linux/spi/spi-mem.h
@@ -0,0 +1,307 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 Exceet Electronics GmbH
+ * Copyright (C) 2018 Bootlin
+ *
+ * Author:
+ *	Peter Pan <peterpandong@micron.com>
+ *	Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#ifndef __LINUX_SPI_MEM_H
+#define __LINUX_SPI_MEM_H
+
+#include <spi/spi.h>
+
+#define SPI_MEM_OP_CMD(__opcode, __buswidth)			\
+	{							\
+		.buswidth = __buswidth,				\
+		.opcode = __opcode,				\
+	}
+
+#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth)		\
+	{							\
+		.nbytes = __nbytes,				\
+		.val = __val,					\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_ADDR	{ }
+
+#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth)			\
+	{							\
+		.nbytes = __nbytes,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_DUMMY	{ }
+
+#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth)		\
+	{							\
+		.dir = SPI_MEM_DATA_IN,				\
+		.nbytes = __nbytes,				\
+		.buf.in = __buf,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth)	\
+	{							\
+		.dir = SPI_MEM_DATA_OUT,			\
+		.nbytes = __nbytes,				\
+		.buf.out = __buf,				\
+		.buswidth = __buswidth,				\
+	}
+
+#define SPI_MEM_OP_NO_DATA	{ }
+
+/**
+ * enum spi_mem_data_dir - describes the direction of a SPI memory data
+ *			   transfer from the controller perspective
+ * @SPI_MEM_NO_DATA: no data transferred
+ * @SPI_MEM_DATA_IN: data coming from the SPI memory
+ * @SPI_MEM_DATA_OUT: data sent to the SPI memory
+ */
+enum spi_mem_data_dir {
+	SPI_MEM_NO_DATA,
+	SPI_MEM_DATA_IN,
+	SPI_MEM_DATA_OUT,
+};
+
+/**
+ * struct spi_mem_op - describes a SPI memory operation
+ * @cmd.buswidth: number of IO lines used to transmit the command
+ * @cmd.opcode: operation opcode
+ * @addr.nbytes: number of address bytes to send. Can be zero if the operation
+ *		 does not need to send an address
+ * @addr.buswidth: number of IO lines used to transmit the address cycles
+ * @addr.val: address value. This value is always sent MSB first on the bus.
+ *	      Note that only @addr.nbytes are taken into account in this
+ *	      address value, so users should make sure the value fits in the
+ *	      assigned number of bytes.
+ * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
+ *		  be zero if the operation does not require dummy bytes
+ * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
+ * @data.buswidth: number of IO lanes used to send/receive the data
+ * @data.dir: direction of the transfer
+ * @data.nbytes: number of data bytes to send/receive. Can be zero if the
+ *		 operation does not involve transferring data
+ * @data.buf.in: input buffer (must be DMA-able)
+ * @data.buf.out: output buffer (must be DMA-able)
+ */
+struct spi_mem_op {
+	struct {
+		u8 buswidth;
+		u8 opcode;
+	} cmd;
+
+	struct {
+		u8 nbytes;
+		u8 buswidth;
+		u64 val;
+	} addr;
+
+	struct {
+		u8 nbytes;
+		u8 buswidth;
+	} dummy;
+
+	struct {
+		u8 buswidth;
+		enum spi_mem_data_dir dir;
+		unsigned int nbytes;
+		union {
+			void *in;
+			const void *out;
+		} buf;
+	} data;
+};
+
+#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)		\
+	{							\
+		.cmd = __cmd,					\
+		.addr = __addr,					\
+		.dummy = __dummy,				\
+		.data = __data,					\
+	}
+
+/**
+ * struct spi_mem_dirmap_info - Direct mapping information
+ * @op_tmpl: operation template that should be used by the direct mapping when
+ *	     the memory device is accessed
+ * @offset: absolute offset this direct mapping is pointing to
+ * @length: length in byte of this direct mapping
+ *
+ * These information are used by the controller specific implementation to know
+ * the portion of memory that is directly mapped and the spi_mem_op that should
+ * be used to access the device.
+ * A direct mapping is only valid for one direction (read or write) and this
+ * direction is directly encoded in the ->op_tmpl.data.dir field.
+ */
+struct spi_mem_dirmap_info {
+	struct spi_mem_op op_tmpl;
+	u64 offset;
+	u64 length;
+};
+
+/**
+ * struct spi_mem_dirmap_desc - Direct mapping descriptor
+ * @mem: the SPI memory device this direct mapping is attached to
+ * @info: information passed at direct mapping creation time
+ * @nodirmap: set to 1 if the SPI controller does not implement
+ *	      ->mem_ops->dirmap_create() or when this function returned an
+ *	      error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
+ *	      calls will use spi_mem_exec_op() to access the memory. This is a
+ *	      degraded mode that allows spi_mem drivers to use the same code
+ *	      no matter whether the controller supports direct mapping or not
+ * @priv: field pointing to controller specific data
+ *
+ * Common part of a direct mapping descriptor. This object is created by
+ * spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
+ * can create/attach direct mapping resources to the descriptor in the ->priv
+ * field.
+ */
+struct spi_mem_dirmap_desc {
+	struct spi_mem *mem;
+	struct spi_mem_dirmap_info info;
+	unsigned int nodirmap;
+	void *priv;
+};
+
+/**
+ * struct spi_mem - describes a SPI memory device
+ * @spi: the underlying SPI device
+ * @drvpriv: spi_mem_driver private data
+ * @name: name of the SPI memory device
+ *
+ * Extra information that describe the SPI memory device and may be needed by
+ * the controller to properly handle this device should be placed here.
+ *
+ * One example would be the device size since some controller expose their SPI
+ * mem devices through a io-mapped region.
+ */
+struct spi_mem {
+	struct spi_device *spi;
+	void *drvpriv;
+	const char *name;
+};
+
+/**
+ * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
+ *				  device
+ * @mem: memory device
+ * @data: data to attach to the memory device
+ */
+static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
+{
+	mem->drvpriv = data;
+}
+
+/**
+ * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
+ *				  device
+ * @mem: memory device
+ *
+ * Return: the data attached to the mem device.
+ */
+static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
+{
+	return mem->drvpriv;
+}
+
+/**
+ * struct spi_controller_mem_ops - SPI memory operations
+ * @adjust_op_size: shrink the data xfer of an operation to match controller's
+ *		    limitations (can be alignment of max RX/TX size
+ *		    limitations)
+ * @supports_op: check if an operation is supported by the controller
+ * @exec_op: execute a SPI memory operation
+ * @get_name: get a custom name for the SPI mem device from the controller.
+ *	      This might be needed if the controller driver has been ported
+ *	      to use the SPI mem layer and a custom name is used to keep
+ *	      mtdparts compatible.
+ *	      Note that if the implementation of this function allocates memory
+ *	      dynamically, then it should do so with devm_xxx(), as we don't
+ *	      have a ->free_name() function.
+ * @dirmap_create: create a direct mapping descriptor that can later be used to
+ *		   access the memory device. This method is optional
+ * @dirmap_destroy: destroy a memory descriptor previous created by
+ *		    ->dirmap_create()
+ * @dirmap_read: read data from the memory device using the direct mapping
+ *		 created by ->dirmap_create(). The function can return less
+ *		 data than requested (for example when the request is crossing
+ *		 the currently mapped area), and the caller of
+ *		 spi_mem_dirmap_read() is responsible for calling it again in
+ *		 this case.
+ * @dirmap_write: write data to the memory device using the direct mapping
+ *		  created by ->dirmap_create(). The function can return less
+ *		  data than requested (for example when the request is crossing
+ *		  the currently mapped area), and the caller of
+ *		  spi_mem_dirmap_write() is responsible for calling it again in
+ *		  this case.
+ *
+ * This interface should be implemented by SPI controllers providing an
+ * high-level interface to execute SPI memory operation, which is usually the
+ * case for QSPI controllers.
+ *
+ * Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
+ * mapping from the CPU because doing that can stall the CPU waiting for the
+ * SPI mem transaction to finish, and this will make real-time maintainers
+ * unhappy and might make your system less reactive. Instead, drivers should
+ * use DMA to access this direct mapping.
+ */
+struct spi_controller_mem_ops {
+	int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
+	bool (*supports_op)(struct spi_mem *mem,
+			    const struct spi_mem_op *op);
+	int (*exec_op)(struct spi_mem *mem,
+		       const struct spi_mem_op *op);
+	const char *(*get_name)(struct spi_mem *mem);
+	int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);
+	void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);
+	ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,
+			       u64 offs, size_t len, void *buf);
+	ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
+				u64 offs, size_t len, const void *buf);
+};
+
+/**
+ * struct spi_mem_driver - SPI memory driver
+ * @spidrv: inherit from a SPI driver
+ * @probe: probe a SPI memory. Usually where detection/initialization takes
+ *	   place
+ * @remove: remove a SPI memory
+ *
+ * This is just a thin wrapper around a spi_driver. The core takes care of
+ * allocating the spi_mem object and forwarding the probe/remove
+ * request to the spi_mem_driver. The reason we use this wrapper is because
+ * we might have to stuff more information into the spi_mem struct to let
+ * SPI controllers know more about the SPI memory they interact with, and
+ * having this intermediate layer allows us to do that without adding more
+ * useless fields to the spi_device object.
+ */
+struct spi_mem_driver {
+	struct driver_d spidrv;
+	int (*probe)(struct spi_mem *mem);
+	int (*remove)(struct spi_mem *mem);
+};
+
+int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
+
+bool spi_mem_supports_op(struct spi_mem *mem,
+			 const struct spi_mem_op *op);
+
+int spi_mem_exec_op(struct spi_mem *mem,
+		    const struct spi_mem_op *op);
+
+const char *spi_mem_get_name(struct spi_mem *mem);
+
+struct spi_mem_dirmap_desc *
+spi_mem_dirmap_create(struct spi_mem *mem,
+		      const struct spi_mem_dirmap_info *info);
+void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc);
+ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
+			    u64 offs, size_t len, void *buf);
+ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
+			     u64 offs, size_t len, const void *buf);
+
+#endif /* __LINUX_SPI_MEM_H */
diff --git a/include/spi/spi.h b/include/spi/spi.h
index 6eeaf254c713..bdcda0bb603c 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -2,8 +2,12 @@
 #define __INCLUDE_SPI_H
 
 #include <driver.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
 #include <linux/string.h>
 
+struct spi_controller_mem_ops;
+
 struct spi_board_info {
 	char	*name;
 	int 	max_speed_hz;
@@ -62,6 +66,7 @@ struct spi_device {
 	struct device_d		dev;
 	struct spi_controller	*controller;
 	struct spi_controller	*master;	/* compatibility layer */
+	struct spi_mem		*mem;
 	u32			max_speed_hz;
 	u8			chip_select;
 	u8			mode;
@@ -109,6 +114,13 @@ struct spi_message;
  * @dev: device interface to this driver
  * @bus_num: board-specific (and often SOC-specific) identifier for a
  *	given SPI controller.
+ * @mem_ops: optimized/dedicated operations for interactions with SPI
+ *      memory. This field is optional and should only be implemented
+ *      if the controller has native support for memory like operations.
+ * @max_transfer_size: function that returns the max transfer size for
+ *	a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
+ * @max_message_size: function that returns the max message size for
+ *	a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
  * @num_chipselect: chipselects are used to distinguish individual
  *	SPI slaves, and are numbered from zero to num_chipselects.
  *	each slave has a chipselect signal, but it's common that not
@@ -144,6 +156,15 @@ struct spi_controller {
 	 */
 	s16			bus_num;
 
+	/* Optimized handlers for SPI memory-like operations */
+	const struct spi_controller_mem_ops *mem_ops;
+	/*
+	 * on some hardware transfer size may be constrained
+	 * the limit may depend on device transfer settings
+	 */
+	size_t (*max_transfer_size)(struct spi_device *spi);
+	size_t (*max_message_size)(struct spi_device *spi);
+
 	/* chipselects will be integral to many controllers; some others
 	 * might use board-specific GPIOs.
 	 */
@@ -180,6 +201,40 @@ struct spi_controller {
 	struct list_head list;
 };
 
+static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
+{
+	if (ctlr->dev->platform_data)
+		return ctlr->dev->platform_data;
+	else
+		return ERR_PTR(-EINVAL);
+}
+
+static inline void spi_controller_set_devdata(struct spi_controller *ctlr,
+					      void *data)
+{
+	ctlr->dev->platform_data = data;
+}
+
+static inline size_t spi_max_message_size(struct spi_device *spi)
+{
+	struct spi_controller *ctrl = spi->controller;
+	if (!ctrl->max_transfer_size)
+		return SIZE_MAX;
+	return ctrl->max_transfer_size(spi);
+}
+
+static inline size_t spi_max_transfer_size(struct spi_device *spi)
+{
+	struct spi_controller *ctrl = spi->controller;
+	size_t tr_max = SIZE_MAX;
+	size_t msg_max = spi_max_message_size(spi);
+
+	if (ctrl->max_transfer_size)
+		tr_max = ctrl->max_transfer_size(spi);
+
+	return min(tr_max, msg_max);
+}
+
 #define spi_master  			spi_controller
 #define spi_register_master(_ctrl)	spi_register_controller(_ctrl)
 
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 05/10] mtd: spi-nor: remove unused write_enable from write_reg
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (3 preceding siblings ...)
  2019-05-03  9:33 ` [PATCH 04/10] spi: Extend the core to ease integration of SPI memory controllers Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 06/10] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Steffen Trumtrar
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Based on the Linux v4.2 commit:

  commit f9f3ce835ddce3c669eee869253105f88819888b
  Author:     Jagan Teki <jteki@openedev.com>
  AuthorDate: Wed Aug 19 15:26:44 2015 +0530
  Commit:     Brian Norris <computersforpeace@gmail.com>
  CommitDate: Fri Sep 11 16:04:55 2015 -0700

      mtd: spi-nor: Zap unneeded write_enable from write_reg

      The 'write_enable' argument is unused and unneeded, so remove it from
      the API.

      Signed-off-by: Jagan Teki <jteki@openedev.com>
      Cc: David Woodhouse <dwmw2@infradead.org>
      Cc: Han Xu <han.xu@freescale.com>
      [Brian: fixed for nxp-spifi.c]
      Signed-off-by: Brian Norris <computersforpeace@gmail.com>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/devices/m25p80.c          |  3 +--
 drivers/mtd/spi-nor/cadence-quadspi.c |  5 ++---
 drivers/mtd/spi-nor/spi-nor.c         | 16 ++++++++--------
 include/linux/mtd/spi-nor.h           |  3 +--
 4 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 8a67792125e3..de1d325cff97 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -65,8 +65,7 @@ static int m25p_cmdsz(struct spi_nor *nor)
 	return 1 + nor->addr_width;
 }
 
-static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
-			    int wr_en)
+static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 {
 	struct m25p *flash = nor->priv;
 	struct spi_device *spi = flash->spi;
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 4142f0de7dcb..781f3b72908e 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -972,7 +972,7 @@ static int cqspi_erase(struct spi_nor *nor, loff_t offs)
 		return ret;
 
 	/* Send write enable, then erase commands. */
-	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+	ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
 	if (ret)
 		return ret;
 
@@ -995,8 +995,7 @@ static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 	return ret;
 }
 
-static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
-			   int write_enable)
+static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 {
 	int ret = 0;
 
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 851ac1c33e7f..ff0dca80f417 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -153,7 +153,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
 static inline int write_sr(struct spi_nor *nor, u8 val)
 {
 	nor->cmd_buf[0] = val;
-	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
+	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
 }
 
 /*
@@ -162,7 +162,7 @@ static inline int write_sr(struct spi_nor *nor, u8 val)
  */
 static inline int write_enable(struct spi_nor *nor)
 {
-	return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
+	return nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0);
 }
 
 /*
@@ -170,7 +170,7 @@ static inline int write_enable(struct spi_nor *nor)
  */
 static inline int write_disable(struct spi_nor *nor)
 {
-	return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0, 0);
+	return nor->write_reg(nor, SPINOR_OP_WRDI, NULL, 0);
 }
 
 static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
@@ -196,7 +196,7 @@ static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
 			write_enable(nor);
 
 		cmd = enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
-		status = nor->write_reg(nor, cmd, NULL, 0, 0);
+		status = nor->write_reg(nor, cmd, NULL, 0);
 		if (need_wren)
 			write_disable(nor);
 
@@ -204,7 +204,7 @@ static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
 	default:
 		/* Spansion style */
 		nor->cmd_buf[0] = enable << 7;
-		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
+		return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
 	}
 }
 static inline int spi_nor_sr_ready(struct spi_nor *nor)
@@ -279,7 +279,7 @@ static int erase_chip(struct spi_nor *nor)
 {
 	dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
 
-	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
+	return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0);
 }
 
 static int spi_nor_lock_and_prep(struct spi_nor *nor, enum spi_nor_ops ops)
@@ -827,7 +827,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
 	write_enable(nor);
 
 	nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
-	nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
+	nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
 
 	if (spi_nor_wait_till_ready(nor))
 		return 1;
@@ -852,7 +852,7 @@ static int write_sr_cr(struct spi_nor *nor, u16 val)
 	nor->cmd_buf[0] = val & 0xff;
 	nor->cmd_buf[1] = (val >> 8);
 
-	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2, 0);
+	return nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 2);
 }
 
 static int spansion_quad_enable(struct spi_nor *nor)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index de9ac08ef173..ec6825471b60 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -175,8 +175,7 @@ struct spi_nor {
 	int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
 			  u8 *buf, size_t len);
 	int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
-	int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
-			int write_enable);
+	int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
 
 	int (*read)(struct spi_nor *nor, loff_t from,
 			size_t len, size_t *retlen, u_char *read_buf);
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 06/10] mtd: spi-nor: remove unused read_xfer/write_xfer hooks
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (4 preceding siblings ...)
  2019-05-03  9:33 ` [PATCH 05/10] mtd: spi-nor: remove unused write_enable from write_reg Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 07/10] spi: add driver for Freescale QSPI controller Steffen Trumtrar
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Based on the Linux v4.3 patch:

  commit 79c452adb159dc9abc507ea13faec8d115a78758
  Author:     Cyrille Pitchen <cyrille.pitchen@atmel.com>
  AuthorDate: Fri Sep 18 17:49:25 2015 +0200
  Commit:     Brian Norris <computersforpeace@gmail.com>
  CommitDate: Mon Sep 21 16:49:52 2015 -0700

      mtd: spi-nor: remove unused read_xfer/write_xfer hooks

      struct spi_nor_xfer_cfg and read_xfer/write_xfer hooks were never used by
      any driver. Do some cleanup by removing them.

      Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
      Reviewed-by: Marek Vasut <marex@denx.de>
      Signed-off-by: Brian Norris <computersforpeace@gmail.com>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 include/linux/mtd/spi-nor.h | 32 --------------------------------
 1 file changed, 32 deletions(-)

diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index ec6825471b60..beac1e7ab0f3 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -82,33 +82,6 @@ enum read_mode {
 	SPI_NOR_QUAD,
 };
 
-/**
- * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
- * @wren:		command for "Write Enable", or 0x00 for not required
- * @cmd:		command for operation
- * @cmd_pins:		number of pins to send @cmd (1, 2, 4)
- * @addr:		address for operation
- * @addr_pins:		number of pins to send @addr (1, 2, 4)
- * @addr_width:		number of address bytes
- *			(3,4, or 0 for address not required)
- * @mode:		mode data
- * @mode_pins:		number of pins to send @mode (1, 2, 4)
- * @mode_cycles:	number of mode cycles (0 for mode not required)
- * @dummy_cycles:	number of dummy cycles (0 for dummy not required)
- */
-struct spi_nor_xfer_cfg {
-	u8		wren;
-	u8		cmd;
-	u8		cmd_pins;
-	u32		addr;
-	u8		addr_pins;
-	u8		addr_width;
-	u8		mode;
-	u8		mode_pins;
-	u8		mode_cycles;
-	u8		dummy_cycles;
-};
-
 #define SPI_NOR_MAX_CMD_SIZE	8
 enum spi_nor_ops {
 	SPI_NOR_OPS_READ = 0,
@@ -165,15 +138,10 @@ struct spi_nor {
 	enum read_mode		flash_read;
 	bool			sst_write_second;
 	u32			flags;
-	struct spi_nor_xfer_cfg	cfg;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
 
 	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
 	void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
-	int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
-			 u8 *buf, size_t len);
-	int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
-			  u8 *buf, size_t len);
 	int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
 	int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
 
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 07/10] spi: add driver for Freescale QSPI controller
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (5 preceding siblings ...)
  2019-05-03  9:33 ` [PATCH 06/10] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 08/10] mtd: spi-nor: introduce SPI 1-2-2 and SPI 1-4-4 protocols Steffen Trumtrar
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

This is based on the Linux v5.1-rc1 driver introduced with the commit:

  commit 84d043185dbe0d1b4f6db575bd91c834d37e2f78
  Refs: v5.0-rc1-1-g84d043185dbe
  Author:     Frieder Schrempf <frieder.schrempf@kontron.de>
  AuthorDate: Mon Jan 7 09:29:47 2019 +0000
  Commit:     Mark Brown <broonie@kernel.org>
  CommitDate: Mon Jan 7 16:56:24 2019 +0000

      spi: Add a driver for the Freescale/NXP QuadSPI controller

      This driver is derived from the SPI NOR driver at
      mtd/spi-nor/fsl-quadspi.c. It uses the new SPI memory interface
      of the SPI framework to issue flash memory operations to up to
      four connected flash chips (2 buses with 2 CS each).

      The controller does not support generic SPI messages.

      This patch also disables the build of the "old" driver and reuses
      its Kconfig option CONFIG_SPI_FSL_QUADSPI to replace it.

      Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
      Acked-by: Han Xu <han.xu@nxp.com>
      Reviewed-by: Yogesh Gaur <yogeshnarayan.gaur@nxp.com>
      Tested-by: Yogesh Gaur <yogeshnarayan.gaur@nxp.com>
      Tested-by: Han Xu <han.xu@nxp.com>
      Reviewed-by: Boris Brezillon <bbrezillon@kernel.org>
      Signed-off-by: Mark Brown <broonie@kernel.org>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/spi/Kconfig        |  11 +-
 drivers/spi/Makefile       |   1 +-
 drivers/spi/spi-fsl-qspi.c | 869 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 881 insertions(+)
 create mode 100644 drivers/spi/spi-fsl-qspi.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 650292541f76..d687105ea488 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -25,6 +25,17 @@ config DRIVER_SPI_ATMEL
 	bool "Atmel (AT91) SPI Master driver"
 	depends on ARCH_AT91
 
+config DRIVER_SPI_FSL_QUADSPI
+	bool "Freescale QSPI controller"
+	depends on ARCH_IMX25 || ARCH_IMX31 || ARCH_IMX35 || ARCH_IMX50 || ARCH_IMX53 || ARCH_LAYERSCAPE
+	depends on SPI_MEM
+	help
+	  This enables support for the Quad SPI controller in master mode.
+          Up to four flash chips can be connected on two buses with two
+          chipselects each.
+          This controller does not support generic SPI messages. It only
+          supports the high-level SPI memory interface.
+
 config DRIVER_SPI_GPIO
 	bool "GPIO SPI Master driver"
 	depends on GPIOLIB
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 841d67d32651..dd8a8cb8b04a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_SPI) += spi.o
 obj-$(CONFIG_SPI_MEM) += spi-mem.o
 obj-$(CONFIG_DRIVER_SPI_ATH79) += ath79_spi.o
 obj-$(CONFIG_DRIVER_SPI_GPIO) += gpio_spi.o
+obj-$(CONFIG_DRIVER_SPI_FSL_QUADSPI) += spi-fsl-qspi.o
 obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
 obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o
 obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o
diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
new file mode 100644
index 000000000000..e22c3099fe72
--- /dev/null
+++ b/drivers/spi/spi-fsl-qspi.c
@@ -0,0 +1,869 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Freescale QuadSPI driver.
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2018 Bootlin
+ * Copyright (C) 2018 exceet electronics GmbH
+ * Copyright (C) 2018 Kontron Electronics GmbH
+ *
+ * Transition to SPI MEM interface:
+ * Authors:
+ *     Boris Brezillon <bbrezillon@kernel.org>
+ *     Frieder Schrempf <frieder.schrempf@kontron.de>
+ *     Yogesh Gaur <yogeshnarayan.gaur@nxp.com>
+ *     Suresh Gupta <suresh.gupta@nxp.com>
+ *
+ * Based on the original fsl-quadspi.c spi-nor driver:
+ * Author: Freescale Semiconductor, Inc.
+ *
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/mutex.h>
+#include <linux/sizes.h>
+#include <of.h>
+#include <of_device.h>
+
+#include <spi/spi.h>
+#include <linux/spi/spi-mem.h>
+
+/*
+ * The driver only uses one single LUT entry, that is updated on
+ * each call of exec_op(). Index 0 is preset at boot with a basic
+ * read operation, so let's use the last entry (15).
+ */
+#define	SEQID_LUT			15
+
+/* Registers used by the driver */
+#define QUADSPI_MCR			0x00
+#define QUADSPI_MCR_RESERVED_MASK	GENMASK(19, 16)
+#define QUADSPI_MCR_MDIS_MASK		BIT(14)
+#define QUADSPI_MCR_CLR_TXF_MASK	BIT(11)
+#define QUADSPI_MCR_CLR_RXF_MASK	BIT(10)
+#define QUADSPI_MCR_DDR_EN_MASK		BIT(7)
+#define QUADSPI_MCR_END_CFG_MASK	GENMASK(3, 2)
+#define QUADSPI_MCR_SWRSTHD_MASK	BIT(1)
+#define QUADSPI_MCR_SWRSTSD_MASK	BIT(0)
+
+#define QUADSPI_IPCR			0x08
+#define QUADSPI_IPCR_SEQID(x)		((x) << 24)
+
+#define QUADSPI_BUF3CR			0x1c
+#define QUADSPI_BUF3CR_ALLMST_MASK	BIT(31)
+#define QUADSPI_BUF3CR_ADATSZ(x)	((x) << 8)
+#define QUADSPI_BUF3CR_ADATSZ_MASK	GENMASK(15, 8)
+
+#define QUADSPI_BFGENCR			0x20
+#define QUADSPI_BFGENCR_SEQID(x)	((x) << 12)
+
+#define QUADSPI_BUF0IND			0x30
+#define QUADSPI_BUF1IND			0x34
+#define QUADSPI_BUF2IND			0x38
+#define QUADSPI_SFAR			0x100
+
+#define QUADSPI_SMPR			0x108
+#define QUADSPI_SMPR_DDRSMP_MASK	GENMASK(18, 16)
+#define QUADSPI_SMPR_FSDLY_MASK		BIT(6)
+#define QUADSPI_SMPR_FSPHS_MASK		BIT(5)
+#define QUADSPI_SMPR_HSENA_MASK		BIT(0)
+
+#define QUADSPI_RBCT			0x110
+#define QUADSPI_RBCT_WMRK_MASK		GENMASK(4, 0)
+#define QUADSPI_RBCT_RXBRD_USEIPS	BIT(8)
+
+#define QUADSPI_TBSR			0x150
+#define QUADSPI_TBDR			0x154
+
+#define QUADSPI_SR			0x15c
+#define QUADSPI_SR_BUSY_MASK		BIT(0)
+#define QUADSPI_SR_IP_ACC_MASK		BIT(1)
+#define QUADSPI_SR_AHB_ACC_MASK		BIT(2)
+
+#define QUADSPI_FR			0x160
+#define QUADSPI_FR_TFF_MASK		BIT(0)
+
+#define QUADSPI_SPTRCLR			0x16c
+#define QUADSPI_SPTRCLR_IPPTRC		BIT(8)
+#define QUADSPI_SPTRCLR_BFPTRC		BIT(0)
+
+#define QUADSPI_SFA1AD			0x180
+#define QUADSPI_SFA2AD			0x184
+#define QUADSPI_SFB1AD			0x188
+#define QUADSPI_SFB2AD			0x18c
+#define QUADSPI_RBDR(x)			(0x200 + ((x) * 4))
+
+#define QUADSPI_LUTKEY			0x300
+#define QUADSPI_LUTKEY_VALUE		0x5AF05AF0
+
+#define QUADSPI_LCKCR			0x304
+#define QUADSPI_LCKER_LOCK		BIT(0)
+#define QUADSPI_LCKER_UNLOCK		BIT(1)
+
+#define QUADSPI_RSER			0x164
+#define QUADSPI_RSER_TFIE		BIT(0)
+
+#define QUADSPI_LUT_BASE		0x310
+#define QUADSPI_LUT_OFFSET		(SEQID_LUT * 4 * 4)
+#define QUADSPI_LUT_REG(idx) \
+	(QUADSPI_LUT_BASE + QUADSPI_LUT_OFFSET + (idx) * 4)
+
+/* Instruction set for the LUT register */
+#define LUT_STOP		0
+#define LUT_CMD			1
+#define LUT_ADDR		2
+#define LUT_DUMMY		3
+#define LUT_MODE		4
+#define LUT_MODE2		5
+#define LUT_MODE4		6
+#define LUT_FSL_READ		7
+#define LUT_FSL_WRITE		8
+#define LUT_JMP_ON_CS		9
+#define LUT_ADDR_DDR		10
+#define LUT_MODE_DDR		11
+#define LUT_MODE2_DDR		12
+#define LUT_MODE4_DDR		13
+#define LUT_FSL_READ_DDR	14
+#define LUT_FSL_WRITE_DDR	15
+#define LUT_DATA_LEARN		16
+
+/*
+ * The PAD definitions for LUT register.
+ *
+ * The pad stands for the number of IO lines [0:3].
+ * For example, the quad read needs four IO lines,
+ * so you should use LUT_PAD(4).
+ */
+#define LUT_PAD(x) (fls(x) - 1)
+
+/*
+ * Macro for constructing the LUT entries with the following
+ * register layout:
+ *
+ *  ---------------------------------------------------
+ *  | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
+ *  ---------------------------------------------------
+ */
+#define LUT_DEF(idx, ins, pad, opr)					\
+	((((ins) << 10) | ((pad) << 8) | (opr)) << (((idx) % 2) * 16))
+
+/* Controller needs driver to swap endianness */
+#define QUADSPI_QUIRK_SWAP_ENDIAN	BIT(0)
+
+/* Controller needs 4x internal clock */
+#define QUADSPI_QUIRK_4X_INT_CLK	BIT(1)
+
+/*
+ * TKT253890, the controller needs the driver to fill the txfifo with
+ * 16 bytes at least to trigger a data transfer, even though the extra
+ * data won't be transferred.
+ */
+#define QUADSPI_QUIRK_TKT253890		BIT(2)
+
+/* TKT245618, the controller cannot wake up from wait mode */
+#define QUADSPI_QUIRK_TKT245618		BIT(3)
+
+/*
+ * Controller adds QSPI_AMBA_BASE (base address of the mapped memory)
+ * internally. No need to add it when setting SFXXAD and SFAR registers
+ */
+#define QUADSPI_QUIRK_BASE_INTERNAL	BIT(4)
+
+struct fsl_qspi_devtype_data {
+	unsigned int rxfifo;
+	unsigned int txfifo;
+	unsigned int ahb_buf_size;
+	unsigned int quirks;
+	bool little_endian;
+};
+
+static const struct fsl_qspi_devtype_data vybrid_data = {
+	.rxfifo = SZ_128,
+	.txfifo = SZ_64,
+	.ahb_buf_size = SZ_1K,
+	.quirks = QUADSPI_QUIRK_SWAP_ENDIAN,
+	.little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data imx6sx_data = {
+	.rxfifo = SZ_128,
+	.txfifo = SZ_512,
+	.ahb_buf_size = SZ_1K,
+	.quirks = QUADSPI_QUIRK_4X_INT_CLK | QUADSPI_QUIRK_TKT245618,
+	.little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data imx7d_data = {
+	.rxfifo = SZ_512,
+	.txfifo = SZ_512,
+	.ahb_buf_size = SZ_1K,
+	.quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_4X_INT_CLK,
+	.little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data imx6ul_data = {
+	.rxfifo = SZ_128,
+	.txfifo = SZ_512,
+	.ahb_buf_size = SZ_1K,
+	.quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_4X_INT_CLK,
+	.little_endian = true,
+};
+
+static const struct fsl_qspi_devtype_data ls1021a_data = {
+	.rxfifo = SZ_128,
+	.txfifo = SZ_64,
+	.ahb_buf_size = SZ_1K,
+	.quirks = 0,
+	.little_endian = false,
+};
+
+static const struct fsl_qspi_devtype_data ls2080a_data = {
+	.rxfifo = SZ_128,
+	.txfifo = SZ_64,
+	.ahb_buf_size = SZ_1K,
+	.quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_BASE_INTERNAL,
+	.little_endian = true,
+};
+
+struct fsl_qspi {
+	void __iomem *iobase;
+	void __iomem *ahb_addr;
+	u32 memmap_phy;
+	struct clk *clk, *clk_en;
+	struct device_d *dev;
+	struct spi_controller ctlr;
+	const struct fsl_qspi_devtype_data *devtype_data;
+	struct mutex lock;
+	int selected;
+};
+
+static inline int needs_swap_endian(struct fsl_qspi *q)
+{
+	return q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN;
+}
+
+static inline int needs_4x_clock(struct fsl_qspi *q)
+{
+	return q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK;
+}
+
+static inline int needs_fill_txfifo(struct fsl_qspi *q)
+{
+	return q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890;
+}
+
+static inline int needs_amba_base_offset(struct fsl_qspi *q)
+{
+	return !(q->devtype_data->quirks & QUADSPI_QUIRK_BASE_INTERNAL);
+}
+
+/*
+ * An IC bug makes it necessary to rearrange the 32-bit data.
+ * Later chips, such as IMX6SLX, have fixed this bug.
+ */
+static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
+{
+	return needs_swap_endian(q) ? __swab32(a) : a;
+}
+
+/*
+ * R/W functions for big- or little-endian registers:
+ * The QSPI controller's endianness is independent of
+ * the CPU core's endianness. So far, although the CPU
+ * core is little-endian the QSPI controller can use
+ * big-endian or little-endian.
+ */
+static void qspi_writel(struct fsl_qspi *q, u32 val, void __iomem *addr)
+{
+	if (q->devtype_data->little_endian)
+		iowrite32(val, addr);
+	else
+		iowrite32be(val, addr);
+}
+
+static u32 qspi_readl(struct fsl_qspi *q, void __iomem *addr)
+{
+	if (q->devtype_data->little_endian)
+		return ioread32(addr);
+
+	return ioread32be(addr);
+}
+
+static int fsl_qspi_check_buswidth(struct fsl_qspi *q, u8 width)
+{
+	switch (width) {
+	case 1:
+	case 2:
+	case 4:
+		return 0;
+	}
+
+	return -ENOTSUPP;
+}
+
+static bool fsl_qspi_supports_op(struct spi_mem *mem,
+				 const struct spi_mem_op *op)
+{
+	struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+	int ret;
+
+	ret = fsl_qspi_check_buswidth(q, op->cmd.buswidth);
+
+	if (op->addr.nbytes)
+		ret |= fsl_qspi_check_buswidth(q, op->addr.buswidth);
+
+	if (op->dummy.nbytes)
+		ret |= fsl_qspi_check_buswidth(q, op->dummy.buswidth);
+
+	if (op->data.nbytes)
+		ret |= fsl_qspi_check_buswidth(q, op->data.buswidth);
+
+	if (ret)
+		return false;
+
+	/*
+	 * The number of instructions needed for the op, needs
+	 * to fit into a single LUT entry.
+	 */
+	if (op->addr.nbytes +
+	   (op->dummy.nbytes ? 1:0) +
+	   (op->data.nbytes ? 1:0) > 6)
+		return false;
+
+	/* Max 64 dummy clock cycles supported */
+	if (op->dummy.nbytes &&
+	    (op->dummy.nbytes * 8 / op->dummy.buswidth > 64))
+		return false;
+
+	/* Max data length, check controller limits and alignment */
+	if (op->data.dir == SPI_MEM_DATA_IN &&
+	    (op->data.nbytes > q->devtype_data->ahb_buf_size ||
+	     (op->data.nbytes > q->devtype_data->rxfifo - 4 &&
+	      !IS_ALIGNED(op->data.nbytes, 8))))
+		return false;
+
+	if (op->data.dir == SPI_MEM_DATA_OUT &&
+	    op->data.nbytes > q->devtype_data->txfifo)
+		return false;
+
+	return true;
+}
+
+static void fsl_qspi_prepare_lut(struct fsl_qspi *q,
+				 const struct spi_mem_op *op)
+{
+	void __iomem *base = q->iobase;
+	u32 lutval[4] = {};
+	int lutidx = 1, i;
+
+	lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
+			     op->cmd.opcode);
+
+	/*
+	 * For some unknown reason, using LUT_ADDR doesn't work in some
+	 * cases (at least with only one byte long addresses), so
+	 * let's use LUT_MODE to write the address bytes one by one
+	 */
+	for (i = 0; i < op->addr.nbytes; i++) {
+		u8 addrbyte = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
+
+		lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_MODE,
+					      LUT_PAD(op->addr.buswidth),
+					      addrbyte);
+		lutidx++;
+	}
+
+	if (op->dummy.nbytes) {
+		lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_DUMMY,
+					      LUT_PAD(op->dummy.buswidth),
+					      op->dummy.nbytes * 8 /
+					      op->dummy.buswidth);
+		lutidx++;
+	}
+
+	if (op->data.nbytes) {
+		lutval[lutidx / 2] |= LUT_DEF(lutidx,
+					      op->data.dir == SPI_MEM_DATA_IN ?
+					      LUT_FSL_READ : LUT_FSL_WRITE,
+					      LUT_PAD(op->data.buswidth),
+					      0);
+		lutidx++;
+	}
+
+	lutval[lutidx / 2] |= LUT_DEF(lutidx, LUT_STOP, 0, 0);
+
+	/* unlock LUT */
+	qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+	qspi_writel(q, QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR);
+
+	/* fill LUT */
+	for (i = 0; i < ARRAY_SIZE(lutval); i++)
+		qspi_writel(q, lutval[i], base + QUADSPI_LUT_REG(i));
+
+	/* lock LUT */
+	qspi_writel(q, QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY);
+	qspi_writel(q, QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR);
+}
+
+static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q)
+{
+	int ret;
+
+	ret = clk_enable(q->clk_en);
+	if (ret)
+		return ret;
+
+	ret = clk_enable(q->clk);
+	if (ret) {
+		clk_disable(q->clk_en);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q)
+{
+	clk_disable(q->clk);
+	clk_disable(q->clk_en);
+}
+
+/*
+ * If we have changed the content of the flash by writing or erasing, or if we
+ * read from flash with a different offset into the page buffer, we need to
+ * invalidate the AHB buffer. If we do not do so, we may read out the wrong
+ * data. The spec tells us reset the AHB domain and Serial Flash domain at
+ * the same time.
+ */
+static void fsl_qspi_invalidate(struct fsl_qspi *q)
+{
+	u32 reg;
+
+	reg = qspi_readl(q, q->iobase + QUADSPI_MCR);
+	reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK;
+	qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+
+	/*
+	 * The minimum delay : 1 AHB + 2 SFCK clocks.
+	 * Delay 1 us is enough.
+	 */
+	udelay(1);
+
+	reg &= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK);
+	qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+}
+
+static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi)
+{
+	unsigned long rate = spi->max_speed_hz;
+	int ret;
+
+	if (q->selected == spi->chip_select)
+		return;
+
+	if (needs_4x_clock(q))
+		rate *= 4;
+
+	fsl_qspi_clk_disable_unprep(q);
+
+	ret = clk_set_rate(q->clk, rate);
+	if (ret)
+		return;
+
+	ret = fsl_qspi_clk_prep_enable(q);
+	if (ret)
+		return;
+
+	q->selected = spi->chip_select;
+
+	fsl_qspi_invalidate(q);
+}
+
+static void fsl_qspi_read_ahb(struct fsl_qspi *q, const struct spi_mem_op *op)
+{
+	memcpy(op->data.buf.in,
+	       q->ahb_addr + q->selected * q->devtype_data->ahb_buf_size,
+	       op->data.nbytes);
+}
+
+static void fsl_qspi_fill_txfifo(struct fsl_qspi *q,
+				 const struct spi_mem_op *op)
+{
+	void __iomem *base = q->iobase;
+	int i;
+	u32 val;
+
+	for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 4); i += 4) {
+		memcpy(&val, op->data.buf.out + i, 4);
+		val = fsl_qspi_endian_xchg(q, val);
+		qspi_writel(q, val, base + QUADSPI_TBDR);
+	}
+
+	if (i < op->data.nbytes) {
+		memcpy(&val, op->data.buf.out + i, op->data.nbytes - i);
+		val = fsl_qspi_endian_xchg(q, val);
+		qspi_writel(q, val, base + QUADSPI_TBDR);
+	}
+
+	if (needs_fill_txfifo(q)) {
+		for (i = op->data.nbytes; i < 16; i += 4)
+			qspi_writel(q, 0, base + QUADSPI_TBDR);
+	}
+}
+
+static void fsl_qspi_read_rxfifo(struct fsl_qspi *q,
+			  const struct spi_mem_op *op)
+{
+	void __iomem *base = q->iobase;
+	int i;
+	u8 *buf = op->data.buf.in;
+	u32 val;
+
+	for (i = 0; i < ALIGN_DOWN(op->data.nbytes, 4); i += 4) {
+		val = qspi_readl(q, base + QUADSPI_RBDR(i / 4));
+		val = fsl_qspi_endian_xchg(q, val);
+		memcpy(buf + i, &val, 4);
+	}
+
+	if (i < op->data.nbytes) {
+		val = qspi_readl(q, base + QUADSPI_RBDR(i / 4));
+		val = fsl_qspi_endian_xchg(q, val);
+		memcpy(buf + i, &val, op->data.nbytes - i);
+	}
+}
+
+static int fsl_qspi_do_op(struct fsl_qspi *q, const struct spi_mem_op *op)
+{
+	void __iomem *base = q->iobase;
+	uint64_t timeout = 1000;
+	uint64_t start;
+	u32 reg;
+
+	/*
+	 * Always start the sequence at the same index since we update
+	 * the LUT at each exec_op() call. And also specify the DATA
+	 * length, since it's has not been specified in the LUT.
+	 */
+	qspi_writel(q, op->data.nbytes | QUADSPI_IPCR_SEQID(SEQID_LUT),
+		    base + QUADSPI_IPCR);
+
+	start = get_time_ns();
+	do {
+		reg = qspi_readl(q, q->iobase + QUADSPI_FR);
+		if (reg & QUADSPI_FR_TFF_MASK) {
+			/* clear interrupt */
+			qspi_writel(q, reg, q->iobase + QUADSPI_FR);
+			if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
+				fsl_qspi_read_rxfifo(q, op);
+			return 0;
+		}
+
+	} while (!is_timeout(start, timeout * MSECOND));
+
+	return -ETIMEDOUT;
+}
+
+static int fsl_qspi_readl_poll_tout(struct fsl_qspi *q, void __iomem *base,
+				    u32 mask, u32 delay_us, u32 timeout_us)
+{
+	uint64_t timeout = MSEC_PER_SEC * timeout_us;
+	u32 reg;
+
+	if (!q->devtype_data->little_endian)
+		mask = (u32)cpu_to_be32(mask);
+
+	return readl_poll_timeout(base, reg, !(reg & mask), timeout);
+}
+
+static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+	struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+	void __iomem *base;
+	u32 addr_offset = 0;
+	int err = 0;
+
+	base = q->iobase;
+
+	mutex_lock(&q->lock);
+
+	/* wait for the controller being ready */
+	fsl_qspi_readl_poll_tout(q, base + QUADSPI_SR, (QUADSPI_SR_IP_ACC_MASK |
+				 QUADSPI_SR_AHB_ACC_MASK), 10, 1000);
+
+	fsl_qspi_select_mem(q, mem->spi);
+
+	if (needs_amba_base_offset(q))
+		addr_offset = q->memmap_phy;
+
+	qspi_writel(q,
+		    q->selected * q->devtype_data->ahb_buf_size + addr_offset,
+		    base + QUADSPI_SFAR);
+
+	qspi_writel(q, qspi_readl(q, base + QUADSPI_MCR) |
+		    QUADSPI_MCR_CLR_RXF_MASK | QUADSPI_MCR_CLR_TXF_MASK,
+		    base + QUADSPI_MCR);
+
+	qspi_writel(q, QUADSPI_SPTRCLR_BFPTRC | QUADSPI_SPTRCLR_IPPTRC,
+		    base + QUADSPI_SPTRCLR);
+
+	fsl_qspi_prepare_lut(q, op);
+
+	/*
+	 * If we have large chunks of data, we read them through the AHB bus
+	 * by accessing the mapped memory. In all other cases we use
+	 * IP commands to access the flash.
+	 */
+	if (op->data.nbytes > (q->devtype_data->rxfifo - 4) &&
+	    op->data.dir == SPI_MEM_DATA_IN) {
+		fsl_qspi_read_ahb(q, op);
+	} else {
+		qspi_writel(q, QUADSPI_RBCT_WMRK_MASK |
+			    QUADSPI_RBCT_RXBRD_USEIPS, base + QUADSPI_RBCT);
+
+		if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
+			fsl_qspi_fill_txfifo(q, op);
+
+		err = fsl_qspi_do_op(q, op);
+	}
+
+	/* Invalidate the data in the AHB buffer. */
+	fsl_qspi_invalidate(q);
+
+	mutex_unlock(&q->lock);
+
+	return err;
+}
+
+static int fsl_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+	struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+
+	if (op->data.dir == SPI_MEM_DATA_OUT) {
+		if (op->data.nbytes > q->devtype_data->txfifo)
+			op->data.nbytes = q->devtype_data->txfifo;
+	} else {
+		if (op->data.nbytes > q->devtype_data->ahb_buf_size)
+			op->data.nbytes = q->devtype_data->ahb_buf_size;
+		else if (op->data.nbytes > (q->devtype_data->rxfifo - 4))
+			op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8);
+	}
+
+	return 0;
+}
+
+static int fsl_qspi_setup(struct spi_device *spi)
+{
+	struct fsl_qspi *q = container_of(spi->controller, struct fsl_qspi, ctlr);
+	void __iomem *base = q->iobase;
+	u32 reg, addr_offset = 0;
+	int ret;
+
+	/* disable and unprepare clock to avoid glitch pass to controller */
+	fsl_qspi_clk_disable_unprep(q);
+
+	/* the default frequency, we will change it later if necessary. */
+	ret = clk_set_rate(q->clk, 66000000);
+	if (ret)
+		return ret;
+
+	ret = fsl_qspi_clk_prep_enable(q);
+	if (ret)
+		return ret;
+
+	/* Reset the module */
+	qspi_writel(q, QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
+		    base + QUADSPI_MCR);
+	udelay(1);
+
+	/* Disable the module */
+	qspi_writel(q, QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK,
+		    base + QUADSPI_MCR);
+
+	reg = qspi_readl(q, base + QUADSPI_SMPR);
+	qspi_writel(q, reg & ~(QUADSPI_SMPR_FSDLY_MASK
+			| QUADSPI_SMPR_FSPHS_MASK
+			| QUADSPI_SMPR_HSENA_MASK
+			| QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR);
+
+	/* We only use the buffer3 for AHB read */
+	qspi_writel(q, 0, base + QUADSPI_BUF0IND);
+	qspi_writel(q, 0, base + QUADSPI_BUF1IND);
+	qspi_writel(q, 0, base + QUADSPI_BUF2IND);
+
+	qspi_writel(q, QUADSPI_BFGENCR_SEQID(SEQID_LUT),
+		    q->iobase + QUADSPI_BFGENCR);
+	qspi_writel(q, QUADSPI_RBCT_WMRK_MASK, base + QUADSPI_RBCT);
+	qspi_writel(q, QUADSPI_BUF3CR_ALLMST_MASK |
+		    QUADSPI_BUF3CR_ADATSZ(q->devtype_data->ahb_buf_size / 8),
+		    base + QUADSPI_BUF3CR);
+
+	if (needs_amba_base_offset(q))
+		addr_offset = q->memmap_phy;
+
+	/*
+	 * In HW there can be a maximum of four chips on two buses with
+	 * two chip selects on each bus. We use four chip selects in SW
+	 * to differentiate between the four chips.
+	 * We use ahb_buf_size for each chip and set SFA1AD, SFA2AD, SFB1AD,
+	 * SFB2AD accordingly.
+	 */
+	qspi_writel(q, q->devtype_data->ahb_buf_size + addr_offset,
+		    base + QUADSPI_SFA1AD);
+	qspi_writel(q, q->devtype_data->ahb_buf_size * 2 + addr_offset,
+		    base + QUADSPI_SFA2AD);
+	qspi_writel(q, q->devtype_data->ahb_buf_size * 3 + addr_offset,
+		    base + QUADSPI_SFB1AD);
+	qspi_writel(q, q->devtype_data->ahb_buf_size * 4 + addr_offset,
+		    base + QUADSPI_SFB2AD);
+
+	q->selected = -1;
+
+	/* Enable the module */
+	qspi_writel(q, QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
+		    base + QUADSPI_MCR);
+
+	/* clear all interrupt status */
+	qspi_writel(q, 0xffffffff, q->iobase + QUADSPI_FR);
+
+	/* enable the interrupt */
+	qspi_writel(q, QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
+
+	return 0;
+}
+
+static const char *fsl_qspi_get_name(struct spi_mem *mem)
+{
+	struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
+	struct device_d *dev = &mem->spi->dev;
+	const char *name;
+
+	/*
+	 * In order to keep mtdparts compatible with the old MTD driver at
+	 * mtd/spi-nor/fsl-quadspi.c, we set a custom name derived from the
+	 * platform_device of the controller.
+	 */
+	if (of_get_available_child_count(q->dev->device_node) == 1)
+		return dev_name(q->dev);
+
+	name = basprintf("%s-%d", dev_name(q->dev), mem->spi->chip_select);
+	if (!name) {
+		dev_err(dev, "failed to get memory for custom flash name\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return name;
+}
+
+static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
+	.adjust_op_size = fsl_qspi_adjust_op_size,
+	.supports_op = fsl_qspi_supports_op,
+	.exec_op = fsl_qspi_exec_op,
+	.get_name = fsl_qspi_get_name,
+};
+
+static int fsl_qspi_probe(struct device_d *dev)
+{
+	struct spi_controller *ctlr;
+	struct resource *res;
+	struct fsl_qspi *q;
+	int ret;
+
+	q = xzalloc(sizeof(*q));
+
+	ctlr = &q->ctlr;
+
+	/* /\* ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | *\/ */
+	/* /\* 		  SPI_TX_DUAL | SPI_TX_QUAD; *\/ */
+
+	q->dev = dev;
+	q->devtype_data = of_device_get_match_data(dev);
+	if (!q->devtype_data) {
+		ret = -ENODEV;
+		goto err_put_ctrl;
+	}
+
+	ctlr->dev = dev;
+	ctlr->bus_num = dev->id;
+	ctlr->setup = fsl_qspi_setup;
+	ctlr->num_chipselect = 4;
+	ctlr->mem_ops = &fsl_qspi_mem_ops;
+
+	spi_controller_set_devdata(ctlr, q);
+
+	/* find the resources */
+	res = dev_request_mem_resource(dev, 0);
+	q->iobase = IOMEM(res->start);
+	if (IS_ERR(q->iobase)) {
+		ret = PTR_ERR(q->iobase);
+		goto err_put_ctrl;
+	}
+
+	res = dev_request_mem_resource(dev, 1);
+	q->ahb_addr = IOMEM(res->start);
+	if (IS_ERR(q->ahb_addr)) {
+		ret = PTR_ERR(q->ahb_addr);
+		goto err_put_ctrl;
+	}
+
+	q->memmap_phy = res->start;
+
+	/* find the clocks */
+	q->clk_en = clk_get(dev, "qspi_en");
+	if (IS_ERR(q->clk_en)) {
+		ret = PTR_ERR(q->clk_en);
+		goto err_put_ctrl;
+	}
+
+	q->clk = clk_get(dev, "qspi");
+	if (IS_ERR(q->clk)) {
+		ret = PTR_ERR(q->clk);
+		goto err_put_ctrl;
+	}
+
+	ret = fsl_qspi_clk_prep_enable(q);
+	if (ret) {
+		dev_err(dev, "can not enable the clock\n");
+		goto err_put_ctrl;
+	}
+
+	mutex_init(&q->lock);
+
+	ret = spi_register_controller(ctlr);
+	if (ret)
+		goto err_disable_clk;
+
+	return 0;
+
+err_disable_clk:
+	fsl_qspi_clk_disable_unprep(q);
+
+err_put_ctrl:
+	dev_err(dev, "Freescale QuadSPI probe failed\n");
+	return ret;
+}
+
+static const struct of_device_id fsl_qspi_dt_ids[] = {
+	{ .compatible = "fsl,vf610-qspi", .data = &vybrid_data, },
+	{ .compatible = "fsl,imx6sx-qspi", .data = &imx6sx_data, },
+	{ .compatible = "fsl,imx7d-qspi", .data = &imx7d_data, },
+	{ .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, },
+	{ .compatible = "fsl,ls1021a-qspi", .data = &ls1021a_data, },
+	{ .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
+	{ /* sentinel */ }
+};
+
+static struct driver_d fsl_qspi_driver = {
+	.name		= "fsl-quadspi",
+	.probe          = fsl_qspi_probe,
+	.of_compatible	= DRV_OF_COMPAT(fsl_qspi_dt_ids),
+};
+device_platform_driver(fsl_qspi_driver);
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 08/10] mtd: spi-nor: introduce SPI 1-2-2 and SPI 1-4-4 protocols
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (6 preceding siblings ...)
  2019-05-03  9:33 ` [PATCH 07/10] spi: add driver for Freescale QSPI controller Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:33 ` [PATCH 09/10] mtd: spi-nor: provide default erase_sector implementation Steffen Trumtrar
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Sync the driver with Linux v4.12 and apply the patch

  commit cfc5604c488ccd17936b69008af0c9ae050f4a08
  Author:     Cyrille Pitchen <cyrille.pitchen@atmel.com>
  AuthorDate: Tue Apr 25 22:08:46 2017 +0200
  Commit:     Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
  CommitDate: Mon May 15 21:56:17 2017 +0200

      mtd: spi-nor: introduce SPI 1-2-2 and SPI 1-4-4 protocols

      This patch changes the prototype of spi_nor_scan(): its 3rd parameter
      is replaced by a 'struct spi_nor_hwcaps' pointer, which tells the spi-nor
      framework about the actual hardware capabilities supported by the SPI
      controller and its driver.

      Besides, this patch also introduces a new 'struct spi_nor_flash_parameter'
      telling the spi-nor framework about the hardware capabilities supported by
      the SPI flash memory and the associated settings required to use those
      hardware caps.

      Then, to improve the readability of spi_nor_scan(), the discovery of the
      memory settings and the memory initialization are now split into two
      dedicated functions.

      1 - spi_nor_init_params()

      The spi_nor_init_params() function is responsible for initializing the
      'struct spi_nor_flash_parameter'. Currently this structure is filled with
      legacy values but further patches will allow to override some parameter
      values dynamically, for instance by reading the JESD216 Serial Flash
      Discoverable Parameter (SFDP) tables from the SPI memory.
      The spi_nor_init_params() function only deals with the hardware
      capabilities of the SPI flash memory: especially it doesn't care about
      the hardware capabilities supported by the SPI controller.

      2 - spi_nor_setup()

      The second function is called once the 'struct spi_nor_flash_parameter'
      has been initialized by spi_nor_init_params().
      With both 'struct spi_nor_flash_parameter' and 'struct spi_nor_hwcaps',
      the new argument of spi_nor_scan(), spi_nor_setup() computes the best
      match between hardware caps supported by both the (Q)SPI memory and
      controller hence selecting the relevant settings for (Fast) Read and Page
      Program operations.

      Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
      Reviewed-by: Marek Vasut <marek.vasut@gmail.com>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/devices/m25p80.c          |  13 +-
 drivers/mtd/spi-nor/cadence-quadspi.c | 118 ++---
 drivers/mtd/spi-nor/spi-nor.c         | 584 ++++++++++++++++++++-------
 include/linux/mtd/spi-nor.h           | 194 +++++++--
 4 files changed, 679 insertions(+), 230 deletions(-)

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index de1d325cff97..8c4659ce11e0 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -231,7 +231,11 @@ static int m25p_probe(struct device_d *dev)
 	struct flash_platform_data	*data;
 	struct m25p			*flash;
 	struct spi_nor			*nor;
-	enum read_mode mode =		SPI_NOR_NORMAL;
+	struct spi_nor_hwcaps hwcaps = {
+		.mask = SNOR_HWCAPS_READ |
+			SNOR_HWCAPS_READ_FAST |
+			SNOR_HWCAPS_PP,
+	};
 	const char			*flash_name = NULL;
 	int				device_id;
 	bool				use_large_blocks;
@@ -258,6 +262,11 @@ static int m25p_probe(struct device_d *dev)
 	flash->mtd.parent = &spi->dev;
 	flash->spi = spi;
 
+	if (spi->mode & SPI_RX_QUAD)
+		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+	else if (spi->mode & SPI_RX_DUAL)
+		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+
 	dev->priv = (void *)flash;
 
 	if (data && data->name)
@@ -275,7 +284,7 @@ static int m25p_probe(struct device_d *dev)
 	use_large_blocks = of_property_read_bool(dev->device_node,
 			"use-large-blocks");
 
-	ret = spi_nor_scan(nor, flash_name, mode, use_large_blocks);
+	ret = spi_nor_scan(nor, flash_name, &hwcaps, use_large_blocks);
 	if (ret)
 		return ret;
 
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index 781f3b72908e..11e4d236dd4c 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -679,6 +679,55 @@ failwr:
 	return ret;
 }
 
+static void cqspi_controller_enable(struct cqspi_st *cqspi)
+{
+	void __iomem *reg_base = cqspi->iobase;
+	unsigned int reg;
+
+	reg = readl(reg_base + CQSPI_REG_CONFIG);
+	reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
+	writel(reg, reg_base + CQSPI_REG_CONFIG);
+}
+
+static void cqspi_controller_disable(struct cqspi_st *cqspi)
+{
+	void __iomem *reg_base = cqspi->iobase;
+	unsigned int reg;
+
+	reg = readl(reg_base + CQSPI_REG_CONFIG);
+	reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
+	writel(reg, reg_base + CQSPI_REG_CONFIG);
+}
+
+static void cqspi_chipselect(struct cqspi_st *cqspi,
+			     unsigned int chip_select,
+			     unsigned int decoder_enable)
+{
+	void __iomem *reg_base = cqspi->iobase;
+	unsigned int reg;
+
+	reg = readl(reg_base + CQSPI_REG_CONFIG);
+	if (decoder_enable) {
+		reg |= CQSPI_REG_CONFIG_DECODE_MASK;
+	} else {
+		reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
+
+		/* Convert CS if without decoder.
+		 * CS0 to 4b'1110
+		 * CS1 to 4b'1101
+		 * CS2 to 4b'1011
+		 * CS3 to 4b'0111
+		 */
+		chip_select = 0xF & ~(1 << chip_select);
+	}
+
+	reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
+		 << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
+	reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
+		<< CQSPI_REG_CONFIG_CHIPSELECT_LSB;
+	writel(reg, reg_base + CQSPI_REG_CONFIG);
+}
+
 static unsigned int calculate_ticks_for_ns(unsigned int ref_clk_hz,
 					   unsigned int ns_val)
 {
@@ -790,55 +839,6 @@ static void cqspi_readdata_capture(struct cqspi_st *cqspi,
 	writel(reg, reg_base + CQSPI_REG_READCAPTURE);
 }
 
-static void cqspi_chipselect(struct cqspi_st *cqspi,
-			     unsigned int chip_select,
-			     unsigned int decoder_enable)
-{
-	void __iomem *reg_base = cqspi->iobase;
-	unsigned int reg;
-
-	reg = readl(reg_base + CQSPI_REG_CONFIG);
-	if (decoder_enable) {
-		reg |= CQSPI_REG_CONFIG_DECODE_MASK;
-	} else {
-		reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
-
-		/* Convert CS if without decoder.
-		 * CS0 to 4b'1110
-		 * CS1 to 4b'1101
-		 * CS2 to 4b'1011
-		 * CS3 to 4b'0111
-		 */
-		chip_select = 0xF & ~(1 << chip_select);
-	}
-
-	reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
-		 << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
-	reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
-	    << CQSPI_REG_CONFIG_CHIPSELECT_LSB;
-	writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
-static void cqspi_controller_enable(struct cqspi_st *cqspi)
-{
-	void __iomem *reg_base = cqspi->iobase;
-	unsigned int reg;
-
-	reg = readl(reg_base + CQSPI_REG_CONFIG);
-	reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
-	writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
-static void cqspi_controller_disable(struct cqspi_st *cqspi)
-{
-	void __iomem *reg_base = cqspi->iobase;
-	unsigned int reg;
-
-	reg = readl(reg_base + CQSPI_REG_CONFIG);
-	reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
-	writel(reg, reg_base + CQSPI_REG_CONFIG);
-}
-
 static void cqspi_switch_cs(struct cqspi_st *cqspi, unsigned int cs)
 {
 	unsigned int reg;
@@ -904,15 +904,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
 	f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
 
 	if (read) {
-		switch (nor->flash_read) {
-		case SPI_NOR_NORMAL:
-		case SPI_NOR_FAST:
+		switch (nor->read_proto) {
+		case SNOR_PROTO_1_1_1:
 			f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
 			break;
-		case SPI_NOR_DUAL:
+		case SNOR_PROTO_1_1_2:
 			f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
 			break;
-		case SPI_NOR_QUAD:
+		case SNOR_PROTO_1_1_4:
 			f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
 			break;
 		default:
@@ -967,7 +966,7 @@ static int cqspi_erase(struct spi_nor *nor, loff_t offs)
 {
 	int ret;
 
-	ret = cqspi_set_protocol(nor, 1);
+	ret = cqspi_set_protocol(nor, 0);
 	if (ret)
 		return ret;
 
@@ -1083,6 +1082,13 @@ static int cqspi_setup_flash(struct device_d *dev,
 			     struct cqspi_flash_pdata *f_pdata,
 			     struct device_node *np)
 {
+	const struct spi_nor_hwcaps hwcaps = {
+		.mask = SNOR_HWCAPS_READ |
+			SNOR_HWCAPS_READ_FAST |
+			SNOR_HWCAPS_READ_1_1_2 |
+			SNOR_HWCAPS_READ_1_1_4 |
+			SNOR_HWCAPS_PP,
+	};
 	struct cqspi_st *cqspi = dev->priv;
 	struct mtd_info *mtd;
 	struct spi_nor *nor;
@@ -1124,7 +1130,7 @@ static int cqspi_setup_flash(struct device_d *dev,
 	nor->write = cqspi_write;
 	nor->erase = cqspi_erase;
 
-	ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD, false);
+	ret = spi_nor_scan(nor, NULL, &hwcaps, false);
 	if (ret)
 		goto probe_failed;
 
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index ff0dca80f417..e086d868e2e3 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -57,14 +57,85 @@ struct flash_info {
 	u16		addr_width;
 
 	u16		flags;
-#define	SECT_4K			0x01	/* SPINOR_OP_BE_4K works uniformly */
-#define	SPI_NOR_NO_ERASE	0x02	/* No erase command needed */
-#define	SST_WRITE		0x04	/* use SST byte programming */
-#define	SPI_NOR_NO_FR		0x08	/* Can't do fastread */
-#define	SECT_4K_PMC		0x10	/* SPINOR_OP_BE_4K_PMC works uniformly */
-#define	SPI_NOR_DUAL_READ	0x20    /* Flash supports Dual Read */
-#define	SPI_NOR_QUAD_READ	0x40    /* Flash supports Quad Read */
-#define	USE_FSR			0x80	/* use flag status register */
+#define SECT_4K			BIT(0)	/* SPINOR_OP_BE_4K works uniformly */
+#define SPI_NOR_NO_ERASE	BIT(1)	/* No erase command needed */
+#define SST_WRITE		BIT(2)	/* use SST byte programming */
+#define SPI_NOR_NO_FR		BIT(3)	/* Can't do fastread */
+#define SECT_4K_PMC		BIT(4)	/* SPINOR_OP_BE_4K_PMC works uniformly */
+#define SPI_NOR_DUAL_READ	BIT(5)	/* Flash supports Dual Read */
+#define SPI_NOR_QUAD_READ	BIT(6)	/* Flash supports Quad Read */
+#define USE_FSR			BIT(7)	/* use flag status register */
+#define SPI_NOR_HAS_LOCK	BIT(8)	/* Flash supports lock/unlock via SR */
+#define SPI_NOR_HAS_TB		BIT(9)	/*
+					 * Flash SR has Top/Bottom (TB) protect
+					 * bit. Must be used with
+					 * SPI_NOR_HAS_LOCK.
+					 */
+#define	SPI_S3AN		BIT(10)	/*
+					 * Xilinx Spartan 3AN In-System Flash
+					 * (MFR cannot be used for probing
+					 * because it has the same value as
+					 * ATMEL flashes)
+					 */
+#define SPI_NOR_4B_OPCODES	BIT(11)	/*
+					 * Use dedicated 4byte address op codes
+					 * to support memory size above 128Mib.
+					 */
+#define NO_CHIP_ERASE		BIT(12) /* Chip does not support chip erase */
+#define SPI_NOR_SKIP_SFDP	BIT(13)	/* Skip parsing of SFDP tables */
+#define USE_CLSR		BIT(14)	/* use CLSR command */
+#define SPI_NOR_OCTAL_READ	BIT(15)	/* Flash supports Octal Read */
+};
+
+enum spi_nor_read_command_index {
+	SNOR_CMD_READ,
+	SNOR_CMD_READ_FAST,
+
+	/* Dual SPI */
+	SNOR_CMD_READ_1_1_2,
+	SNOR_CMD_READ_1_2_2,
+	SNOR_CMD_READ_2_2_2,
+
+	/* Quad SPI */
+	SNOR_CMD_READ_1_1_4,
+	SNOR_CMD_READ_1_4_4,
+	SNOR_CMD_READ_4_4_4,
+
+	SNOR_CMD_READ_MAX
+};
+
+struct spi_nor_read_command {
+	u8			num_mode_clocks;
+	u8			num_wait_states;
+	u8			opcode;
+	enum spi_nor_protocol	proto;
+};
+
+struct spi_nor_pp_command {
+	u8			opcode;
+	enum spi_nor_protocol	proto;
+};
+
+enum spi_nor_pp_command_index {
+	SNOR_CMD_PP,
+
+	/* Quad SPI */
+	SNOR_CMD_PP_1_1_4,
+	SNOR_CMD_PP_1_4_4,
+	SNOR_CMD_PP_4_4_4,
+
+	SNOR_CMD_PP_MAX
+};
+
+struct spi_nor_flash_parameter {
+	u64				size;
+	u32				page_size;
+
+	struct spi_nor_hwcaps		hwcaps;
+	struct spi_nor_read_command	reads[SNOR_CMD_READ_MAX];
+	struct spi_nor_pp_command	page_programs[SNOR_CMD_PP_MAX];
+
+	int (*quad_enable)(struct spi_nor *nor);
 };
 
 #define JEDEC_MFR(info)	((info)->id[0])
@@ -129,24 +200,6 @@ static int read_cr(struct spi_nor *nor)
 }
 
 /*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
-	switch (nor->flash_read) {
-	case SPI_NOR_FAST:
-	case SPI_NOR_DUAL:
-	case SPI_NOR_QUAD:
-		return 8;
-	case SPI_NOR_NORMAL:
-		return 0;
-	}
-	return 0;
-}
-
-/*
  * Write status register 1 byte
  * Returns negative if error occurred.
  */
@@ -178,6 +231,81 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
 	return mtd->priv;
 }
 
+static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
+{
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		if (table[i][0] == opcode)
+			return table[i][1];
+
+	/* No conversion found, keep input op code. */
+	return opcode;
+}
+
+static u8 spi_nor_convert_3to4_read(u8 opcode)
+{
+	static const u8 spi_nor_3to4_read[][2] = {
+		{ SPINOR_OP_READ,	SPINOR_OP_READ_4B },
+		{ SPINOR_OP_READ_FAST,	SPINOR_OP_READ_FAST_4B },
+		{ SPINOR_OP_READ_1_1_2,	SPINOR_OP_READ_1_1_2_4B },
+		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
+		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
+		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
+
+		{ SPINOR_OP_READ_1_1_1_DTR,	SPINOR_OP_READ_1_1_1_DTR_4B },
+		{ SPINOR_OP_READ_1_2_2_DTR,	SPINOR_OP_READ_1_2_2_DTR_4B },
+		{ SPINOR_OP_READ_1_4_4_DTR,	SPINOR_OP_READ_1_4_4_DTR_4B },
+	};
+
+	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
+				      ARRAY_SIZE(spi_nor_3to4_read));
+}
+
+static u8 spi_nor_convert_3to4_program(u8 opcode)
+{
+	static const u8 spi_nor_3to4_program[][2] = {
+		{ SPINOR_OP_PP,		SPINOR_OP_PP_4B },
+		{ SPINOR_OP_PP_1_1_4,	SPINOR_OP_PP_1_1_4_4B },
+		{ SPINOR_OP_PP_1_4_4,	SPINOR_OP_PP_1_4_4_4B },
+	};
+
+	return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
+				      ARRAY_SIZE(spi_nor_3to4_program));
+}
+
+static u8 spi_nor_convert_3to4_erase(u8 opcode)
+{
+	static const u8 spi_nor_3to4_erase[][2] = {
+		{ SPINOR_OP_BE_4K,	SPINOR_OP_BE_4K_4B },
+		{ SPINOR_OP_BE_32K,	SPINOR_OP_BE_32K_4B },
+		{ SPINOR_OP_SE,		SPINOR_OP_SE_4B },
+	};
+
+	return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
+				      ARRAY_SIZE(spi_nor_3to4_erase));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
+{
+	/* Do some manufacturer fixups first */
+	switch (JEDEC_MFR(nor->info)) {
+	case SNOR_MFR_SPANSION:
+		/* No small sector erase for 4-byte command set */
+		nor->erase_opcode = SPINOR_OP_SE;
+		nor->mtd->erasesize = nor->info->sector_size;
+		break;
+
+	default:
+		break;
+	}
+
+	nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+	nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
+	nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+}
+
+
 /* Enable/disable 4-byte addressing mode. */
 static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
 			    int enable)
@@ -819,28 +947,6 @@ write_err:
 	return ret;
 }
 
-static int macronix_quad_enable(struct spi_nor *nor)
-{
-	int ret, val;
-
-	val = read_sr(nor);
-	write_enable(nor);
-
-	nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
-	nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1);
-
-	if (spi_nor_wait_till_ready(nor))
-		return 1;
-
-	ret = read_sr(nor);
-	if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
-		dev_err(nor->dev, "Macronix Quad bit not set\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
 /*
  * Write status Register and configuration register with 2 bytes
  * The first byte will be written to the status register, while the
@@ -879,42 +985,272 @@ static int spansion_quad_enable(struct spi_nor *nor)
 	return 0;
 }
 
-static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
+static int spi_nor_check(struct spi_nor *nor)
 {
-	int status;
+	if (!nor->dev || !nor->read || !nor->write ||
+		!nor->read_reg || !nor->write_reg || !nor->erase) {
+		pr_err("spi-nor: please fill all the necessary fields!\n");
+		return -EINVAL;
+	}
 
-	switch (JEDEC_MFR(info)) {
-	case CFI_MFR_MACRONIX:
-		status = macronix_quad_enable(nor);
-		if (status) {
-			dev_err(nor->dev, "Macronix quad-read not enabled\n");
-			return -EINVAL;
-		}
-		return status;
-	default:
-		status = spansion_quad_enable(nor);
-		if (status) {
-			dev_err(nor->dev, "Spansion quad-read not enabled\n");
-			return -EINVAL;
-		}
-		return status;
+	return 0;
+}
+
+static void
+spi_nor_set_read_settings(struct spi_nor_read_command *read,
+			  u8 num_mode_clocks,
+			  u8 num_wait_states,
+			  u8 opcode,
+			  enum spi_nor_protocol proto)
+{
+	read->num_mode_clocks = num_mode_clocks;
+	read->num_wait_states = num_wait_states;
+	read->opcode = opcode;
+	read->proto = proto;
+}
+
+static void
+spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
+			u8 opcode,
+			enum spi_nor_protocol proto)
+{
+	pp->opcode = opcode;
+	pp->proto = proto;
+}
+
+static int spi_nor_init_params(struct spi_nor *nor,
+			       const struct flash_info *info,
+			       struct spi_nor_flash_parameter *params)
+{
+	/* Set legacy flash parameters as default. */
+	memset(params, 0, sizeof(*params));
+
+	/* Set SPI NOR sizes. */
+	params->size = info->sector_size * info->n_sectors;
+	params->page_size = info->page_size;
+
+	/* (Fast) Read settings. */
+	params->hwcaps.mask |= SNOR_HWCAPS_READ;
+	spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ],
+				  0, 0, SPINOR_OP_READ,
+				  SNOR_PROTO_1_1_1);
+
+	if (!(info->flags & SPI_NOR_NO_FR)) {
+		params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
+		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_FAST],
+					  0, 8, SPINOR_OP_READ_FAST,
+					  SNOR_PROTO_1_1_1);
 	}
+
+	if (info->flags & SPI_NOR_DUAL_READ) {
+		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
+		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_2],
+					  0, 8, SPINOR_OP_READ_1_1_2,
+					  SNOR_PROTO_1_1_2);
+	}
+
+	if (info->flags & SPI_NOR_QUAD_READ) {
+		params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
+		spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_4],
+					  0, 8, SPINOR_OP_READ_1_1_4,
+					  SNOR_PROTO_1_1_4);
+	}
+
+	/* Page Program settings. */
+	params->hwcaps.mask |= SNOR_HWCAPS_PP;
+	spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP],
+				SPINOR_OP_PP, SNOR_PROTO_1_1_1);
+
+	/* Select the procedure to set the Quad Enable bit. */
+	if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
+				   SNOR_HWCAPS_PP_QUAD))
+		params->quad_enable = spansion_quad_enable;
+
+	return 0;
 }
 
-static int spi_nor_check(struct spi_nor *nor)
+static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
 {
-	if (!nor->dev || !nor->read || !nor->write ||
-		!nor->read_reg || !nor->write_reg || !nor->erase) {
-		pr_err("spi-nor: please fill all the necessary fields!\n");
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		if (table[i][0] == (int)hwcaps)
+			return table[i][1];
+
+	return -EINVAL;
+}
+
+static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
+{
+	static const int hwcaps_read2cmd[][2] = {
+		{ SNOR_HWCAPS_READ,		SNOR_CMD_READ },
+		{ SNOR_HWCAPS_READ_FAST,	SNOR_CMD_READ_FAST },
+		{ SNOR_HWCAPS_READ_1_1_2,	SNOR_CMD_READ_1_1_2 },
+		{ SNOR_HWCAPS_READ_1_2_2,	SNOR_CMD_READ_1_2_2 },
+		{ SNOR_HWCAPS_READ_2_2_2,	SNOR_CMD_READ_2_2_2 },
+		{ SNOR_HWCAPS_READ_1_1_4,	SNOR_CMD_READ_1_1_4 },
+		{ SNOR_HWCAPS_READ_1_4_4,	SNOR_CMD_READ_1_4_4 },
+		{ SNOR_HWCAPS_READ_4_4_4,	SNOR_CMD_READ_4_4_4 },
+	};
+
+	return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
+				  ARRAY_SIZE(hwcaps_read2cmd));
+}
+
+static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
+{
+	static const int hwcaps_pp2cmd[][2] = {
+		{ SNOR_HWCAPS_PP,		SNOR_CMD_PP },
+		{ SNOR_HWCAPS_PP_1_1_4,		SNOR_CMD_PP_1_1_4 },
+		{ SNOR_HWCAPS_PP_1_4_4,		SNOR_CMD_PP_1_4_4 },
+		{ SNOR_HWCAPS_PP_4_4_4,		SNOR_CMD_PP_4_4_4 },
+	};
+
+	return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
+				  ARRAY_SIZE(hwcaps_pp2cmd));
+}
+
+static int spi_nor_select_read(struct spi_nor *nor,
+			       const struct spi_nor_flash_parameter *params,
+			       u32 shared_hwcaps)
+{
+	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
+	const struct spi_nor_read_command *read;
+
+	if (best_match < 0)
+		return -EINVAL;
+
+	cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));
+	if (cmd < 0)
 		return -EINVAL;
+
+	read = &params->reads[cmd];
+	nor->read_opcode = read->opcode;
+	nor->read_proto = read->proto;
+
+	/*
+	 * In the spi-nor framework, we don't need to make the difference
+	 * between mode clock cycles and wait state clock cycles.
+	 * Indeed, the value of the mode clock cycles is used by a QSPI
+	 * flash memory to know whether it should enter or leave its 0-4-4
+	 * (Continuous Read / XIP) mode.
+	 * eXecution In Place is out of the scope of the mtd sub-system.
+	 * Hence we choose to merge both mode and wait state clock cycles
+	 * into the so called dummy clock cycles.
+	 */
+	nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+	return 0;
+}
+
+static int spi_nor_select_pp(struct spi_nor *nor,
+			     const struct spi_nor_flash_parameter *params,
+			     u32 shared_hwcaps)
+{
+	int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
+	const struct spi_nor_pp_command *pp;
+
+	if (best_match < 0)
+		return -EINVAL;
+
+	cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));
+	if (cmd < 0)
+		return -EINVAL;
+
+	pp = &params->page_programs[cmd];
+	nor->program_opcode = pp->opcode;
+	nor->write_proto = pp->proto;
+	return 0;
+}
+
+static int spi_nor_select_erase(struct spi_nor *nor,
+				const struct flash_info *info)
+{
+	struct mtd_info *mtd = nor->mtd;
+
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+	/* prefer "small sector" erase if possible */
+	if (info->flags & SECT_4K) {
+		nor->erase_opcode = SPINOR_OP_BE_4K;
+		mtd->erasesize = 4096;
+	} else if (info->flags & SECT_4K_PMC) {
+		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
+		mtd->erasesize = 4096;
+	} else
+#endif
+	{
+		nor->erase_opcode = SPINOR_OP_SE;
+		mtd->erasesize = info->sector_size;
+	}
+	return 0;
+}
+
+static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
+			 const struct spi_nor_flash_parameter *params,
+			 const struct spi_nor_hwcaps *hwcaps)
+{
+	u32 ignored_mask, shared_mask;
+	bool enable_quad_io;
+	int err;
+
+	/*
+	 * Keep only the hardware capabilities supported by both the SPI
+	 * controller and the SPI flash memory.
+	 */
+	shared_mask = hwcaps->mask & params->hwcaps.mask;
+
+	/* SPI n-n-n protocols are not supported yet. */
+	ignored_mask = (SNOR_HWCAPS_READ_2_2_2 |
+			SNOR_HWCAPS_READ_4_4_4 |
+			SNOR_HWCAPS_PP_4_4_4);
+	if (shared_mask & ignored_mask) {
+		dev_dbg(nor->dev,
+			"SPI n-n-n protocols are not supported yet.\n");
+		shared_mask &= ~ignored_mask;
+	}
+
+	/* Select the (Fast) Read command. */
+	err = spi_nor_select_read(nor, params, shared_mask);
+	if (err) {
+		dev_err(nor->dev,
+			"can't select read settings supported by both the SPI controller and memory.\n");
+		return err;
+	}
+
+	/* Select the Page Program command. */
+	err = spi_nor_select_pp(nor, params, shared_mask);
+	if (err) {
+		dev_err(nor->dev,
+			"can't select write settings supported by both the SPI controller and memory.\n");
+		return err;
+	}
+
+	/* Select the Sector Erase command. */
+	err = spi_nor_select_erase(nor, info);
+	if (err) {
+		dev_err(nor->dev,
+			"can't select erase settings supported by both the SPI controller and memory.\n");
+		return err;
+	}
+
+	/* Enable Quad I/O if needed. */
+	enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
+			  spi_nor_get_protocol_width(nor->write_proto) == 4);
+	if (enable_quad_io && params->quad_enable) {
+		err = params->quad_enable(nor);
+		if (err) {
+			dev_err(nor->dev, "quad mode not supported\n");
+			return err;
+		}
 	}
 
 	return 0;
 }
 
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+		 const struct spi_nor_hwcaps *hwcaps,
 		 bool use_large_blocks)
 {
+	struct spi_nor_flash_parameter params;
 	const struct spi_device_id	*id = NULL;
 	struct flash_info		*info;
 	struct device_d *dev = nor->dev;
@@ -927,6 +1263,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
 	if (ret)
 		return ret;
 
+	/* Reset SPI protocol for all commands. */
+	nor->reg_proto = SNOR_PROTO_1_1_1;
+	nor->read_proto = SNOR_PROTO_1_1_1;
+	nor->write_proto = SNOR_PROTO_1_1_1;
+
 	/* Try to auto-detect if chip name wasn't specified */
 	if (!name)
 		id = spi_nor_read_id(nor);
@@ -962,6 +1303,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
 		}
 	}
 
+	nor->info = info;
+
 	mutex_init(&nor->lock);
 
 	/*
@@ -976,12 +1319,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
 		write_sr(nor, 0);
 	}
 
+	/* Parse the Serial Flash Discoverable Parameters table. */
+	ret = spi_nor_init_params(nor, info, &params);
+	if (ret)
+		return ret;
+
 	if (!mtd->name)
 		mtd->name = (char *) dev_name(dev);
 	mtd->type = MTD_NORFLASH;
 	mtd->writesize = 1;
 	mtd->flags = MTD_CAP_NORFLASH;
-	mtd->size = info->sector_size * info->n_sectors;
+	mtd->size = params.size;
 	mtd->erase = spi_nor_erase;
 	mtd->read = spi_nor_read;
 
@@ -1000,108 +1348,52 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
 	if (info->flags & USE_FSR)
 		nor->flags |= SNOR_F_USE_FSR;
 
-#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
-	/* prefer "small sector" erase if possible */
-	if (info->flags & SECT_4K && !use_large_blocks) {
-		nor->erase_opcode = SPINOR_OP_BE_4K;
-		mtd->erasesize = 4096;
-	} else if (info->flags & SECT_4K_PMC && !use_large_blocks) {
-		nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
-		mtd->erasesize = 4096;
-	} else
-#endif
-	{
-		nor->erase_opcode = SPINOR_OP_SE;
-		mtd->erasesize = info->sector_size;
-	}
-
 	if (info->flags & SPI_NOR_NO_ERASE)
 		mtd->flags |= MTD_NO_ERASE;
 
-	nor->page_size = info->page_size;
+	nor->page_size = params.page_size;
 	mtd->writebufsize = nor->page_size;
 
 	if (np) {
 		/* If we were instantiated by DT, use it */
 		if (of_property_read_bool(np, "m25p,fast-read"))
-			nor->flash_read = SPI_NOR_FAST;
+			params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
 		else
-			nor->flash_read = SPI_NOR_NORMAL;
+			params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
 	} else {
 		/* If we weren't instantiated by DT, default to fast-read */
-		nor->flash_read = SPI_NOR_FAST;
+		params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
 	}
 
 	/* Some devices cannot do fast-read, no matter what DT tells us */
 	if (info->flags & SPI_NOR_NO_FR)
-		nor->flash_read = SPI_NOR_NORMAL;
-
-	/* Quad/Dual-read mode takes precedence over fast/normal */
-	if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
-		ret = set_quad_mode(nor, info);
-		if (ret) {
-			dev_err(dev, "quad mode not supported\n");
-			return ret;
-		}
-		nor->flash_read = SPI_NOR_QUAD;
-	} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
-		nor->flash_read = SPI_NOR_DUAL;
-	}
-
-	/* Default commands */
-	switch (nor->flash_read) {
-	case SPI_NOR_QUAD:
-		nor->read_opcode = SPINOR_OP_READ_1_1_4;
-		break;
-	case SPI_NOR_DUAL:
-		nor->read_opcode = SPINOR_OP_READ_1_1_2;
-		break;
-	case SPI_NOR_FAST:
-		nor->read_opcode = SPINOR_OP_READ_FAST;
-		break;
-	case SPI_NOR_NORMAL:
-		nor->read_opcode = SPINOR_OP_READ;
-		break;
-	default:
-		dev_err(dev, "No Read opcode defined\n");
-		return -EINVAL;
-	}
+		params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
 
-	nor->program_opcode = SPINOR_OP_PP;
+	/*
+	 * Configure the SPI memory:
+	 * - select op codes for (Fast) Read, Page Program and Sector Erase.
+	 * - set the number of dummy cycles (mode cycles + wait states).
+	 * - set the SPI protocols for register and memory accesses.
+	 * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
+	 */
+	ret = spi_nor_setup(nor, info, &params, hwcaps);
+	if (ret)
+		return ret;
 
 	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) == CFI_MFR_AMD) {
-			/* Dedicated 4-byte command set */
-			switch (nor->flash_read) {
-			case SPI_NOR_QUAD:
-				nor->read_opcode = SPINOR_OP_READ4_1_1_4;
-				break;
-			case SPI_NOR_DUAL:
-				nor->read_opcode = SPINOR_OP_READ4_1_1_2;
-				break;
-			case SPI_NOR_FAST:
-				nor->read_opcode = SPINOR_OP_READ4_FAST;
-				break;
-			case SPI_NOR_NORMAL:
-				nor->read_opcode = SPINOR_OP_READ4;
-				break;
-			}
-			nor->program_opcode = SPINOR_OP_PP_4B;
-			/* No small sector erase for 4-byte command set */
-			nor->erase_opcode = SPINOR_OP_SE_4B;
-			mtd->erasesize = info->sector_size;
-		} else
+		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+	 	    info->flags & SPI_NOR_4B_OPCODES)
+			spi_nor_set_4byte_opcodes(nor);
+		else
 			set_4byte(nor, info, 1);
 	} else {
 		nor->addr_width = 3;
 	}
 
-	nor->read_dummy = spi_nor_read_dummy_cycles(nor);
-
 	dev_info(dev, "%s (%lld Kbytes)\n", id->name,
 			(long long)mtd->size >> 10);
 
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index beac1e7ab0f3..eec5a890ebef 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -10,9 +10,26 @@
 #ifndef __LINUX_MTD_SPI_NOR_H
 #define __LINUX_MTD_SPI_NOR_H
 
+#include <linux/bitops.h>
 #include <linux/mutex.h>
 
 /*
+ * Manufacturer IDs
+ *
+ * The first byte returned from the flash after sending opcode SPINOR_OP_RDID.
+ * Sometimes these are the same as CFI IDs, but sometimes they aren't.
+ */
+#define SNOR_MFR_ATMEL		CFI_MFR_ATMEL
+#define SNOR_MFR_GIGADEVICE	0xc8
+#define SNOR_MFR_INTEL		CFI_MFR_INTEL
+#define SNOR_MFR_ST		CFI_MFR_ST	/* ST Micro */
+#define SNOR_MFR_MICRON		CFI_MFR_MICRON	/* Micron */
+#define SNOR_MFR_MACRONIX	CFI_MFR_MACRONIX
+#define SNOR_MFR_SPANSION	CFI_MFR_AMD
+#define SNOR_MFR_SST		CFI_MFR_SST
+#define SNOR_MFR_WINBOND	0xef /* Also used by some Spansion */
+
+/*
  * Note on opcode nomenclature: some opcodes have a format like
  * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
  * of I/O lines used for the opcode, address, and data (respectively). The
@@ -24,28 +41,53 @@
 #define SPINOR_OP_WREN		0x06	/* Write enable */
 #define SPINOR_OP_RDSR		0x05	/* Read status register */
 #define SPINOR_OP_WRSR		0x01	/* Write status register 1 byte */
+#define SPINOR_OP_RDSR2		0x3f	/* Read status register 2 */
+#define SPINOR_OP_WRSR2		0x3e	/* Write status register 2 */
 #define SPINOR_OP_READ		0x03	/* Read data bytes (low frequency) */
 #define SPINOR_OP_READ_FAST	0x0b	/* Read data bytes (high frequency) */
-#define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2	0xbb	/* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4	0xeb	/* Read data bytes (Quad I/O SPI) */
 #define SPINOR_OP_PP		0x02	/* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4	0x32	/* Quad page program */
+#define SPINOR_OP_PP_1_4_4	0x38	/* Quad page program */
 #define SPINOR_OP_BE_4K		0x20	/* Erase 4KiB block */
 #define SPINOR_OP_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
 #define SPINOR_OP_BE_32K	0x52	/* Erase 32KiB block */
 #define SPINOR_OP_CHIP_ERASE	0xc7	/* Erase whole flash chip */
 #define SPINOR_OP_SE		0xd8	/* Sector erase (usually 64KiB) */
 #define SPINOR_OP_RDID		0x9f	/* Read JEDEC ID */
+#define SPINOR_OP_RDSFDP	0x5a	/* Read SFDP */
 #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_RDEAR		0xc8	/* Read Extended Address Register */
+#define SPINOR_OP_WREAR		0xc5	/* Write Extended Address Register */
 
 /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define SPINOR_OP_READ4		0x13	/* Read data bytes (low frequency) */
-#define SPINOR_OP_READ4_FAST	0x0c	/* Read data bytes (high frequency) */
-#define SPINOR_OP_READ4_1_1_2	0x3c	/* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ4_1_1_4	0x6c	/* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_4B	0x13	/* Read data bytes (low frequency) */
+#define SPINOR_OP_READ_FAST_4B	0x0c	/* Read data bytes (high frequency) */
+#define SPINOR_OP_READ_1_1_2_4B	0x3c	/* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2_4B	0xbc	/* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4_4B	0x6c	/* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4_4B	0xec	/* Read data bytes (Quad I/O SPI) */
 #define SPINOR_OP_PP_4B		0x12	/* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4_4B	0x34	/* Quad page program */
+#define SPINOR_OP_PP_1_4_4_4B	0x3e	/* Quad page program */
+#define SPINOR_OP_BE_4K_4B	0x21	/* Erase 4KiB block */
+#define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
 #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
 
+/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
+#define SPINOR_OP_READ_1_1_1_DTR	0x0d
+#define SPINOR_OP_READ_1_2_2_DTR	0xbd
+#define SPINOR_OP_READ_1_4_4_DTR	0xed
+
+#define SPINOR_OP_READ_1_1_1_DTR_4B	0x0e
+#define SPINOR_OP_READ_1_2_2_DTR_4B	0xbe
+#define SPINOR_OP_READ_1_4_4_DTR_4B	0xee
+
 /* Used for SST flashes only. */
 #define SPINOR_OP_BP		0x02	/* Byte program */
 #define SPINOR_OP_WRDI		0x04	/* Write disable */
@@ -59,29 +101,79 @@
 #define SPINOR_OP_BRWR		0x17	/* Bank register write */
 
 /* Status Register bits. */
-#define SR_WIP			1	/* Write in progress */
-#define SR_WEL			2	/* Write enable latch */
+#define SR_WIP			BIT(0)	/* Write in progress */
+#define SR_WEL			BIT(1)	/* Write enable latch */
 /* meaning of other SR_* bits may differ between vendors */
-#define SR_BP0			4	/* Block protect 0 */
-#define SR_BP1			8	/* Block protect 1 */
-#define SR_BP2			0x10	/* Block protect 2 */
-#define SR_SRWD			0x80	/* SR write protect */
+#define SR_BP0			BIT(2)	/* Block protect 0 */
+#define SR_BP1			BIT(3)	/* Block protect 1 */
+#define SR_BP2			BIT(4)	/* Block protect 2 */
+#define SR_SRWD			BIT(7)	/* SR write protect */
 
-#define SR_QUAD_EN_MX		0x40	/* Macronix Quad I/O */
+#define SR_QUAD_EN_MX		BIT(6)	/* Macronix Quad I/O */
 
 /* Flag Status Register bits */
-#define FSR_READY		0x80
+#define FSR_READY		BIT(7)
 
 /* Configuration Register bits. */
-#define CR_QUAD_EN_SPAN		0x2	/* Spansion Quad I/O */
+#define CR_QUAD_EN_SPAN		BIT(2)	/* Spansion Quad I/O */
+
+/* Supported SPI protocols */
+#define SNOR_PROTO_INST_MASK   GENMASK(23, 16)
+#define SNOR_PROTO_INST_SHIFT  16
+#define SNOR_PROTO_INST(_nbits)        \
+       ((((unsigned long)(_nbits)) << SNOR_PROTO_INST_SHIFT) & \
+        SNOR_PROTO_INST_MASK)
 
-enum read_mode {
-	SPI_NOR_NORMAL = 0,
-	SPI_NOR_FAST,
-	SPI_NOR_DUAL,
-	SPI_NOR_QUAD,
+#define SNOR_PROTO_ADDR_MASK   GENMASK(15, 8)
+#define SNOR_PROTO_ADDR_SHIFT  8
+#define SNOR_PROTO_ADDR(_nbits)        \
+       ((((unsigned long)(_nbits)) << SNOR_PROTO_ADDR_SHIFT) & \
+        SNOR_PROTO_ADDR_MASK)
+
+#define SNOR_PROTO_DATA_MASK   GENMASK(7, 0)
+#define SNOR_PROTO_DATA_SHIFT  0
+#define SNOR_PROTO_DATA(_nbits)        \
+       ((((unsigned long)(_nbits)) << SNOR_PROTO_DATA_SHIFT) & \
+        SNOR_PROTO_DATA_MASK)
+
+#define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits)  \
+       (SNOR_PROTO_INST(_inst_nbits) |                         \
+        SNOR_PROTO_ADDR(_addr_nbits) |                         \
+        SNOR_PROTO_DATA(_data_nbits))
+
+enum spi_nor_protocol {
+       SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(1, 1, 1),
+       SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(1, 1, 2),
+       SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(1, 1, 4),
+       SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(1, 2, 2),
+       SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(1, 4, 4),
+       SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(2, 2, 2),
+       SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(4, 4, 4),
 };
 
+static inline u8 spi_nor_get_protocol_inst_nbits(enum spi_nor_protocol proto)
+{
+       return ((unsigned long)(proto & SNOR_PROTO_INST_MASK)) >>
+               SNOR_PROTO_INST_SHIFT;
+}
+
+static inline u8 spi_nor_get_protocol_addr_nbits(enum spi_nor_protocol proto)
+{
+       return ((unsigned long)(proto & SNOR_PROTO_ADDR_MASK)) >>
+               SNOR_PROTO_ADDR_SHIFT;
+}
+
+static inline u8 spi_nor_get_protocol_data_nbits(enum spi_nor_protocol proto)
+{
+       return ((unsigned long)(proto & SNOR_PROTO_DATA_MASK)) >>
+               SNOR_PROTO_DATA_SHIFT;
+}
+
+static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
+{
+       return spi_nor_get_protocol_data_nbits(proto);
+}
+
 #define SPI_NOR_MAX_CMD_SIZE	8
 enum spi_nor_ops {
 	SPI_NOR_OPS_READ = 0,
@@ -106,17 +198,17 @@ enum spi_nor_option_flags {
  * @read_opcode:	the read opcode
  * @read_dummy:		the dummy needed by the read operation
  * @program_opcode:	the program opcode
- * @flash_read:		the mode of the read
  * @sst_write_second:	used by the SST write operation
  * @flags:		flag options for the current SPI-NOR (SNOR_F_*)
+ * @read_proto:		the SPI protocol for read operations
+ * @write_proto:	the SPI protocol for write operations
+ * @reg_proto		the SPI protocol for read_reg/write_reg/erase operations
  * @cfg:		used by the read_xfer/write_xfer
  * @cmd_buf:		used by the write_reg
  * @prepare:		[OPTIONAL] do some preparations for the
  *			read/write/erase/lock/unlock operations
  * @unprepare:		[OPTIONAL] do some post work after the
  *			read/write/erase/lock/unlock operations
- * @read_xfer:		[OPTIONAL] the read fundamental primitive
- * @write_xfer:		[OPTIONAL] the writefundamental primitive
  * @read_reg:		[DRIVER-SPECIFIC] read out the register
  * @write_reg:		[DRIVER-SPECIFIC] write data to the register
  * @read:		[DRIVER-SPECIFIC] read data from the SPI NOR
@@ -129,13 +221,16 @@ struct spi_nor {
 	struct mtd_info		*mtd;
 	struct mutex		lock;
 	struct device_d		*dev;
+	const struct flash_info	*info;
 	u32			page_size;
 	u8			addr_width;
 	u8			erase_opcode;
 	u8			read_opcode;
 	u8			read_dummy;
 	u8			program_opcode;
-	enum read_mode		flash_read;
+	enum spi_nor_protocol	read_proto;
+	enum spi_nor_protocol	write_proto;
+	enum spi_nor_protocol	reg_proto;
 	bool			sst_write_second;
 	u32			flags;
 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
@@ -155,10 +250,56 @@ struct spi_nor {
 };
 
 /**
+ * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
+ * supported by the SPI controller (bus master).
+ * @mask:              the bitmask listing all the supported hw capabilies
+ */
+struct spi_nor_hwcaps {
+       u32     mask;
+};
+
+/*
+ *(Fast) Read capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * As a matter of performances, it is relevant to use Quad SPI protocols first,
+ * then Dual SPI protocols before Fast Read and lastly (Slow) Read.
+ */
+#define SNOR_HWCAPS_READ_MASK          GENMASK(7, 0)
+#define SNOR_HWCAPS_READ               BIT(0)
+#define SNOR_HWCAPS_READ_FAST          BIT(1)
+
+#define SNOR_HWCAPS_READ_DUAL          GENMASK(4, 2)
+#define SNOR_HWCAPS_READ_1_1_2         BIT(2)
+#define SNOR_HWCAPS_READ_1_2_2         BIT(3)
+#define SNOR_HWCAPS_READ_2_2_2         BIT(4)
+
+#define SNOR_HWCAPS_READ_QUAD          GENMASK(7, 5)
+#define SNOR_HWCAPS_READ_1_1_4         BIT(5)
+#define SNOR_HWCAPS_READ_1_4_4         BIT(6)
+#define SNOR_HWCAPS_READ_4_4_4         BIT(7)
+
+/*
+ * Page Program capabilities.
+ * MUST be ordered by priority: the higher bit position, the higher priority.
+ * Like (Fast) Read capabilities, Quad SPI protocols are preferred to the
+ * legacy SPI 1-1-1 protocol.
+ * Note that Dual Page Programs are not supported because there is no existing
+ * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
+ * implements such commands.
+ */
+#define SNOR_HWCAPS_PP_MASK    GENMASK(19, 16)
+#define SNOR_HWCAPS_PP         BIT(16)
+
+#define SNOR_HWCAPS_PP_QUAD    GENMASK(19, 17)
+#define SNOR_HWCAPS_PP_1_1_4   BIT(17)
+#define SNOR_HWCAPS_PP_1_4_4   BIT(18)
+#define SNOR_HWCAPS_PP_4_4_4   BIT(19)
+
+/**
  * spi_nor_scan() - scan the SPI NOR
  * @nor:	the spi_nor structure
  * @name:	the chip type name
- * @mode:	the read mode supported by the driver
+ * @hwcaps:	the hardware capabilities supported by the controller driver
  * @use_large_blocks: prefer large blocks even if 4k blocks are supported
  *
  * The drivers can use this fuction to scan the SPI NOR.
@@ -169,7 +310,8 @@ struct spi_nor {
  *
  * Return: 0 for success, others for failure.
  */
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode,
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+		 const struct spi_nor_hwcaps *hwcaps,
 		 bool use_large_blocks);
 
 #endif
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 09/10] mtd: spi-nor: provide default erase_sector implementation
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (7 preceding siblings ...)
  2019-05-03  9:33 ` [PATCH 08/10] mtd: spi-nor: introduce SPI 1-2-2 and SPI 1-4-4 protocols Steffen Trumtrar
@ 2019-05-03  9:33 ` Steffen Trumtrar
  2019-05-03  9:34 ` [PATCH 10/10] mtd: devices: m25p80: use the spi_mem_xx() API Steffen Trumtrar
  2019-05-08  8:54 ` [PATCH 00/10] spi: spi-mem and fsl-qspi support Sascha Hauer
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:33 UTC (permalink / raw)
  To: Barebox List

Based on the Linux v4.4 patch

commit c67cbb839da9cc2757eabfa128556db6a2baf160
Author:     Brian Norris <computersforpeace@gmail.com>
AuthorDate: Tue Nov 10 12:15:27 2015 -0800
Commit:     Brian Norris <computersforpeace@gmail.com>
CommitDate: Thu Nov 19 13:34:44 2015 -0800

    mtd: spi-nor: provide default erase_sector implementation

    Some spi-nor drivers perform sector erase by duplicating their
    write_reg() command. Let's not require that the driver fill this out,
    and provide a default instead.

    Tested on m25p80.c and Medatek's MT8173 SPI NOR flash driver.

    Signed-off-by: Brian Norris <computersforpeace@gmail.com>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/spi-nor/spi-nor.c | 37 ++++++++++++++++++++++++++++++++----
 include/linux/mtd/spi-nor.h   |  3 ++-
 2 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index e086d868e2e3..9f72df39f015 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -25,6 +25,7 @@
 #include <spi/flash.h>
 
 #define SPI_NOR_MAX_ID_LEN	6
+#define SPI_NOR_MAX_ADDR_WIDTH	4
 
 /*
  * For everything but full-chip erase; probably could be much smaller, but kept
@@ -435,6 +436,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
 }
 
 /*
+ * Initiate the erasure of a single sector
+ */
+static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
+{
+	u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+	int i;
+
+	if (nor->erase)
+		return nor->erase(nor, addr);
+
+	/*
+	 * Default implementation, if driver doesn't have a specialized HW
+	 * control
+	 */
+	for (i = nor->addr_width - 1; i >= 0; i--) {
+		buf[i] = addr & 0xff;
+		addr >>= 8;
+	}
+
+	return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+}
+
+/*
  * Erase an address range on the nor chip.  The address range may extend
  * one or more erase sectors.  Return an error is there is a problem erasing.
  */
@@ -498,10 +522,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 		while (len) {
 			write_enable(nor);
 
-			if (nor->erase(nor, addr)) {
-				ret = -EIO;
+			ret = spi_nor_erase_sector(nor, addr);
+			if (ret)
 				goto erase_err;
-			}
 
 			addr += mtd->erasesize;
 			len -= mtd->erasesize;
@@ -988,7 +1011,7 @@ static int spansion_quad_enable(struct spi_nor *nor)
 static int spi_nor_check(struct spi_nor *nor)
 {
 	if (!nor->dev || !nor->read || !nor->write ||
-		!nor->read_reg || !nor->write_reg || !nor->erase) {
+		!nor->read_reg || !nor->write_reg) {
 		pr_err("spi-nor: please fill all the necessary fields!\n");
 		return -EINVAL;
 	}
@@ -1394,6 +1417,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 		nor->addr_width = 3;
 	}
 
+	if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) {
+		dev_err(dev, "address width is too large: %u\n",
+			nor->addr_width);
+		return -EINVAL;
+	}
+
 	dev_info(dev, "%s (%lld Kbytes)\n", id->name,
 			(long long)mtd->size >> 10);
 
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index eec5a890ebef..33413ff955bb 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -214,7 +214,8 @@ enum spi_nor_option_flags {
  * @read:		[DRIVER-SPECIFIC] read data from the SPI NOR
  * @write:		[DRIVER-SPECIFIC] write data to the SPI NOR
  * @erase:		[DRIVER-SPECIFIC] erase a sector of the SPI NOR
- *			at the offset @offs
+ *			at the offset @offs; if not provided by the driver,
+ *			spi-nor will send the erase opcode via write_reg()
  * @priv:		the private data
  */
 struct spi_nor {
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 10/10] mtd: devices: m25p80: use the spi_mem_xx() API
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (8 preceding siblings ...)
  2019-05-03  9:33 ` [PATCH 09/10] mtd: spi-nor: provide default erase_sector implementation Steffen Trumtrar
@ 2019-05-03  9:34 ` Steffen Trumtrar
  2019-05-08  8:54 ` [PATCH 00/10] spi: spi-mem and fsl-qspi support Sascha Hauer
  10 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-03  9:34 UTC (permalink / raw)
  To: Barebox List

This is the barebox adoption of the linux v4.16 patch

  4120f8d158ef904fb305b27e4a4524649faf3096
  Author:     Boris Brezillon <bbrezillon@kernel.org>
  AuthorDate: Thu Apr 26 18:18:19 2018 +0200
  Commit:     Mark Brown <broonie@kernel.org>
  CommitDate: Fri May 11 11:33:51 2018 +0900

  mtd: spi-nor: Use the spi_mem_xx() API

  The spi_mem_xxx() API has been introduced to replace the
  spi_flash_read() one. Make use of it so we can get rid of
  spi_flash_read().

  Note that using spi_mem_xx() also simplifies the code because this API
  takes care of using the regular spi_sync() interface when the optimized
  ->mem_ops interface is not implemented by the controller.

  Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
  Reviewed-by: Frieder Schrempf <frieder.schrempf@exceet.de>
  Tested-by: Frieder Schrempf <frieder.schrempf@exceet.de>
  Signed-off-by: Mark Brown <broonie@kernel.org>

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/mtd/devices/Kconfig  |   1 +-
 drivers/mtd/devices/m25p80.c | 142 +++++++++++++++---------------------
 2 files changed, 62 insertions(+), 81 deletions(-)

diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 9c3925bde135..25db10a9b2cb 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -23,6 +23,7 @@ config MTD_M25P80
 	tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
 	depends on SPI
 	select MTD_SPI_NOR
+	select SPI_MEM
 	help
 	  This enables access to most modern SPI flash chips, used for
 	  program and data storage. Series supported include Atmel AT26DF,
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 8c4659ce11e0..a6098b9c5856 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -27,12 +27,13 @@
 #include <clock.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/spi-nor.h>
+#include <linux/spi/spi-mem.h>
 #include <linux/mod_devicetable.h>
 
 #define MAX_CMD_SIZE		6
 
 struct m25p {
-	struct spi_device	*spi;
+	struct spi_mem		*spimem;
 	struct spi_nor		spi_nor;
 	struct mtd_info		mtd;
 	u8			command[MAX_CMD_SIZE];
@@ -41,70 +42,60 @@ struct m25p {
 static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
+					  SPI_MEM_OP_NO_ADDR,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_IN(len, val, 1));
 	int ret;
 
-	ret = spi_write_then_read(spi, &code, 1, val, len);
-	if (ret < 0)
-		dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+	ret = spi_mem_exec_op(flash->spimem, &op);
+ 	if (ret < 0)
+		dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
+			code);
 
 	return ret;
 }
 
-static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
-{
-	/* opcode is in cmd[0] */
-	cmd[1] = addr >> (nor->addr_width * 8 -  8);
-	cmd[2] = addr >> (nor->addr_width * 8 - 16);
-	cmd[3] = addr >> (nor->addr_width * 8 - 24);
-	cmd[4] = addr >> (nor->addr_width * 8 - 32);
-}
-
-static int m25p_cmdsz(struct spi_nor *nor)
-{
-	return 1 + nor->addr_width;
-}
-
 static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
+	struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
+					  SPI_MEM_OP_NO_ADDR,
+					  SPI_MEM_OP_NO_DUMMY,
+					  SPI_MEM_OP_DATA_OUT(len, buf, 1));
 
-	flash->command[0] = opcode;
-	if (buf)
-		memcpy(&flash->command[1], buf, len);
-
-	return spi_write(spi, flash->command, len + 1);
+	return spi_mem_exec_op(flash->spimem, &op);
 }
 
 static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
 			 size_t *retlen, const u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	struct spi_transfer t[2] = {};
-	struct spi_message m;
-	int cmd_sz = m25p_cmdsz(nor);
+	struct spi_mem_op op =
+		SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
+			   SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
+			   SPI_MEM_OP_NO_DUMMY,
+			   SPI_MEM_OP_DATA_OUT(len, buf, 1));
+	int ret;
 
-	spi_message_init(&m);
+	op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
+	op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
+	op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
 
 	if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
-		cmd_sz = 1;
-
-	flash->command[0] = nor->program_opcode;
-	m25p_addr2cmd(nor, to, flash->command);
+		op.addr.nbytes = 0;
 
-	t[0].tx_buf = flash->command;
-	t[0].len = cmd_sz;
-	spi_message_add_tail(&t[0], &m);
+	ret = spi_mem_adjust_op_size(flash->spimem, &op);
+	if (ret)
+		return;
 
-	t[1].tx_buf = buf;
-	t[1].len = len;
-	spi_message_add_tail(&t[1], &m);
+	op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
 
-	spi_sync(spi, &m);
+	ret = spi_mem_exec_op(flash->spimem, &op);
+	if (ret)
+		return;
 
-	*retlen += m.actual_length - cmd_sz;
+	*retlen = op.data.nbytes;
 }
 
 /*
@@ -115,46 +106,35 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 		       size_t *retlen, u_char *buf)
 {
 	struct m25p *flash = nor->priv;
-	struct spi_device *spi = flash->spi;
-	struct spi_transfer t[2];
-	struct spi_message m;
-	unsigned int dummy = nor->read_dummy;
-
-	/* convert the dummy cycles to the number of bytes */
-	dummy /= 8;
-
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
-
-	flash->command[0] = nor->read_opcode;
-	m25p_addr2cmd(nor, from, flash->command);
-
-	t[0].tx_buf = flash->command;
-	t[0].len = m25p_cmdsz(nor) + dummy;
-	spi_message_add_tail(&t[0], &m);
-
-	t[1].rx_buf = buf;
-	t[1].len = len;
-	spi_message_add_tail(&t[1], &m);
-
-	spi_sync(spi, &m);
-
-	*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
-	return 0;
-}
+	struct spi_mem_op op =
+		SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
+			   SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
+			   SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
+			   SPI_MEM_OP_DATA_IN(len, buf, 1));
+	size_t remaining = len;
+	int ret;
 
-static int m25p80_erase(struct spi_nor *nor, loff_t offset)
-{
-	struct m25p *flash = nor->priv;
+	op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
+	op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
+	op.dummy.buswidth = op.addr.buswidth;
+	op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
 
-	dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
-			flash->mtd.erasesize / 1024, (u32)offset);
+	op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
 
-	/* Set up command buffer. */
-	flash->command[0] = nor->erase_opcode;
-	m25p_addr2cmd(nor, offset, flash->command);
+	while (remaining) {
+		op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
+		ret = spi_mem_adjust_op_size(flash->spimem, &op);
+		if (ret)
+ 			return ret;
+		ret = spi_mem_exec_op(flash->spimem, &op);
+		if (ret)
+			return ret;
+		op.addr.val += op.data.nbytes;
+		remaining -= op.data.nbytes;
+		op.data.buf.in += op.data.nbytes;
+ 	}
 
-	spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
+	*retlen = len;
 
 	return 0;
 }
@@ -228,6 +208,7 @@ static const struct platform_device_id m25p_ids[] = {
 static int m25p_probe(struct device_d *dev)
 {
 	struct spi_device *spi = (struct spi_device *)dev->type_data;
+	struct spi_mem *spimem = spi->mem;
 	struct flash_platform_data	*data;
 	struct m25p			*flash;
 	struct spi_nor			*nor;
@@ -250,17 +231,16 @@ static int m25p_probe(struct device_d *dev)
 	/* install the hooks */
 	nor->read = m25p80_read;
 	nor->write = m25p80_write;
-	nor->erase = m25p80_erase;
 	nor->write_reg = m25p80_write_reg;
 	nor->read_reg = m25p80_read_reg;
 
-	nor->dev = dev;
+	nor->dev = &spimem->spi->dev;
 	nor->mtd = &flash->mtd;
 	nor->priv = flash;
 
 	flash->mtd.priv = nor;
 	flash->mtd.parent = &spi->dev;
-	flash->spi = spi;
+	flash->spimem = spimem;
 
 	if (spi->mode & SPI_RX_QUAD)
 		hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
-- 
git-series 0.9.1

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 00/10] spi: spi-mem and fsl-qspi support
  2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
                   ` (9 preceding siblings ...)
  2019-05-03  9:34 ` [PATCH 10/10] mtd: devices: m25p80: use the spi_mem_xx() API Steffen Trumtrar
@ 2019-05-08  8:54 ` Sascha Hauer
  10 siblings, 0 replies; 14+ messages in thread
From: Sascha Hauer @ 2019-05-08  8:54 UTC (permalink / raw)
  To: Steffen Trumtrar; +Cc: Barebox List

On Fri, May 03, 2019 at 11:33:50AM +0200, Steffen Trumtrar wrote:
> Hi!
> 
> This series imports the spi-mem framework from linux into barebox.
> 
> The first patches sync the respective driver with linux to ease
> porting of the following patches.
> Then drivers/spi/spi.c is updated to use the spi-mem framework.
> The Freescale QSPI controller driver, which uses this framework,
> is ported from linux; the older driver was removed from mainline
> linux and wasn't even working correctly.
> Finally drivers/mtd/devices/m25p80.c is simplified by using spi-mem.
> 
> The series was tested with the Layerscape LS1046a and SocFPGA Cyclone5
> SoCs.
> 
> Best regards,
> Steffen

Applied, thanks

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 02/10] spi: Generalize SPI "master" to "controller"
  2019-05-03  9:33 ` [PATCH 02/10] spi: Generalize SPI "master" to "controller" Steffen Trumtrar
@ 2019-05-11  7:36   ` Alexander Kurz
  2019-05-16  9:29     ` Steffen Trumtrar
  0 siblings, 1 reply; 14+ messages in thread
From: Alexander Kurz @ 2019-05-11  7:36 UTC (permalink / raw)
  To: Steffen Trumtrar; +Cc: Barebox List

Hi Steffen,
this commit seems to be the origin of a crash when running the spi 
command, e.g.

barebox@Amazon Kindle D01100:/ spi -b 2 -f 100000 -w 32 -m 4 -r 4  0 0 0 
0xe            
unable to handle NULL pointer dereference at address 0x00000030
pc : [<7fe11a6c>]    lr : [<7fe1bc83>]
sp : 7ffefd78  ip : 00000030  fp : 7fe457e0
r10: 7fe33a28  r9 : 00000000  r8 : 00000002
r7 : 780c6ba4  r6 : 00000004  r5 : 00000004  r4 : 7fe56d6c
r3 : 00000000  r2 : 00000001  r1 : 00000000  r0 : 7ffefd90
Flags: nZcv  IRQs off  FIQs off  Mode SVC_32
WARNING: [<7fe11a6c>] (cspi_0_7_init+0x0/0xe) from [<7fe1bc83>] 
(do_spi+0x11b/0x240)
WARNING: [<7fe1bc83>] (do_spi+0x11b/0x240) from [<780c6b30>] (0x780c6b30)

WARNING: [<7fe297c1>] (unwind_backtrace+0x1/0x68) from [<7fe011ad>] 
(panic+0x1d/0x2c)
WARNING: [<7fe011ad>] (panic+0x1d/0x2c) from [<7fe27b5d>] 
(do_exception+0xd/0x10)
WARNING: [<7fe27b5d>] (do_exception+0xd/0x10) from [<7fe27bbd>] 
(do_data_abort+0x21/0x2c)
WARNING: [<7fe27bbd>] (do_data_abort+0x21/0x2c) from [<7fe277d4>] 
(do_abort_6+0x48/0x54)

The inquired device is the mc13892 PMIC
barebox@Amazon Kindle D01100:/ dmesg 
detected i.MX50 revision 1.1
i.MX reset reason unknown (SRSR: 0x00000010)
barebox 2019.04.0-00247-g58763bed5 #12 Sat May 11 09:22:59 CEST 2019
Board: Amazon Kindle D01100
mc13xxx-spi mc13892@00: Found MC13892 ID: 0x0045d4 [Rev: 2.4]
m25p80 m25p80@00: w25q20bw (256 Kbytes)
imx-esdhc 50020000.esdhc@50020000.of: registered as mmc2

barebox@Amazon Kindle D01100:/ devinfo 
`-- global
`-- nv
`-- platform
   `-- mem0
      `-- 0x00000000-0x0fffffff (   256 MiB): /dev/ram0
   `-- fffc000.tz-interrupt-controller@fffc000.of
   `-- soc.of
...
      `-- 60000000.aips@60000000.of
...
         `-- 63fc0000.spi@63fc0000.of
            `-- mc13892@00
               `-- 0x00000000-0x000000ff ( 256 Bytes): /dev/mc13892@00

Regarding SPI for the i.MX50: DT aliases are currently missing and barebox
will fail to probe SPI devices without alias. A linux DT patch is 
already accepted and will hopefully released with 5.2, e.g.
diff --git a/dts/src/arm/imx50.dtsi b/dts/src/arm/imx50.dtsi
index ee1e3e8bf..82e7acd7f 100644
--- a/dts/src/arm/imx50.dtsi
+++ b/dts/src/arm/imx50.dtsi
@@ -26,11 +26,20 @@
                gpio3 = &gpio4;
                gpio4 = &gpio5;
                gpio5 = &gpio6;
+               i2c0 = &i2c1;
+               i2c1 = &i2c2;
+               mmc0 = &esdhc1;
+               mmc1 = &esdhc2;
+               mmc2 = &esdhc3;
+               mmc3 = &esdhc4;
                serial0 = &uart1;
                serial1 = &uart2;
                serial2 = &uart3;
                serial3 = &uart4;
                serial4 = &uart5;
+               spi0 = &ecspi1;
+               spi1 = &ecspi2;
+               spi2 = &cspi;
        };
 
        cpus {

Regards, Alexander

On Fri, 3 May 2019, Steffen Trumtrar wrote:

> Sync with Linux v5.1-rc1.
> 
> This is the barebox adoption of the commit
> 
>   commit 8caab75fd2c2a92667cbb1cd315720bede3feaa9
>   Author: Geert Uytterhoeven <geert+renesas@glider.be>
>   Date:   Tue Jun 13 13:23:52 2017 +0200
> 
>       spi: Generalize SPI "master" to "controller"
> 
>       Now struct spi_master is used for both SPI master and slave controllers,
>       it makes sense to rename it to struct spi_controller, and replace
>       "master" by "controller" where appropriate.
> 
>       For now this conversion is done for SPI core infrastructure only.
>       Wrappers are provided for backwards compatibility, until all SPI drivers
>       have been converted.
> 
>       Noteworthy details:
> 	- SPI_MASTER_GPIO_SS is retained, as it only makes sense for SPI
> 	  master controllers,
> 	- spi_busnum_to_master() is retained, as it looks up masters only,
> 	- A new field spi_device.controller is added, but spi_device.master is
> 	  retained for compatibility (both are always initialized by
> 	  spi_alloc_device()),
> 	- spi_flash_read() is used by SPI masters only.
> 
>       Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
>       Signed-off-by: Mark Brown <broonie@kernel.org>
> 
> Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> ---
>  commands/spi.c    | 16 +++++------
>  drivers/spi/spi.c | 71 ++++++++++++++++++++++++------------------------
>  include/spi/spi.h | 30 ++++++++++++--------
>  3 files changed, 62 insertions(+), 55 deletions(-)
> 
> diff --git a/commands/spi.c b/commands/spi.c
> index 7bf193b0bbbb..55a0e255af17 100644
> --- a/commands/spi.c
> +++ b/commands/spi.c
> @@ -62,21 +62,21 @@ static int do_spi(int argc, char *argv[])
>  		return COMMAND_ERROR_USAGE;
>  
>  
> -	spi.master = spi_get_master(bus);
> -	if (!spi.master) {
> +	spi.controller = spi_get_controller(bus);
> +	if (!spi.controller) {
>  		printf("spi bus %d not found\n", bus);
>  		return -ENODEV;
>  	}
>  
> -	if (spi.chip_select >= spi.master->num_chipselect) {
> -		printf("spi chip select (%d) >= master num chipselect (%d)\n",
> -			spi.chip_select, spi.master->num_chipselect);
> +	if (spi.chip_select >= spi.controller->num_chipselect) {
> +		printf("spi chip select (%d) >= controller num chipselect (%d)\n",
> +			spi.chip_select, spi.controller->num_chipselect);
>  		return -EINVAL;
>  	}
>  
> -	ret = spi.master->setup(&spi);
> +	ret = spi.controller->setup(&spi);
>  	if (ret) {
> -		printf("can not setup the master (%d)\n", ret);
> +		printf("can not setup the controller (%d)\n", ret);
>  		return ret;
>  	}
>  
> @@ -93,7 +93,7 @@ static int do_spi(int argc, char *argv[])
>  	byte_per_word = max(spi.bits_per_word / 8, 1);
>  	if (verbose) {
>  		printf("device config\n");
> -		printf("    bus_num       = %d\n", spi.master->bus_num);
> +		printf("    bus_num       = %d\n", spi.controller->bus_num);
>  		printf("    max_speed_hz  = %d\n", spi.max_speed_hz);
>  		printf("    chip_select   = %d\n", spi.chip_select);
>  		printf("    mode          = 0x%x\n", spi.mode);
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index 25bb988794a9..7756304f1981 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -54,22 +54,22 @@ static LIST_HEAD(board_list);
>   *
>   * Returns the new device, or NULL.
>   */
> -struct spi_device *spi_new_device(struct spi_master *master,
> +struct spi_device *spi_new_device(struct spi_controller *ctrl,
>  				  struct spi_board_info *chip)
>  {
>  	struct spi_device	*proxy;
>  	int			status;
>  
>  	/* Chipselects are numbered 0..max; validate. */
> -	if (chip->chip_select >= master->num_chipselect) {
> +	if (chip->chip_select >= ctrl->num_chipselect) {
>  		debug("cs%d > max %d\n",
>  			chip->chip_select,
> -			master->num_chipselect);
> +			ctrl->num_chipselect);
>  		return NULL;
>  	}
>  
>  	proxy = xzalloc(sizeof *proxy);
> -	proxy->master = master;
> +	proxy->master = ctrl;
>  	proxy->chip_select = chip->chip_select;
>  	proxy->max_speed_hz = chip->max_speed_hz;
>  	proxy->mode = chip->mode;
> @@ -81,10 +81,11 @@ struct spi_device *spi_new_device(struct spi_master *master,
>  	proxy->dev.id = DEVICE_ID_DYNAMIC;
>  	proxy->dev.type_data = proxy;
>  	proxy->dev.device_node = chip->device_node;
> -	proxy->dev.parent = master->dev;
> +	proxy->dev.parent = ctrl->dev;
> +	proxy->master = proxy->controller = ctrl;
>  
>  	/* drivers may modify this initial i/o setup */
> -	status = master->setup(proxy);
> +	status = ctrl->setup(proxy);
>  	if (status < 0) {
>  		printf("can't setup %s, status %d\n",
>  				proxy->dev.name, status);
> @@ -100,12 +101,12 @@ fail:
>  }
>  EXPORT_SYMBOL(spi_new_device);
>  
> -static void spi_of_register_slaves(struct spi_master *master)
> +static void spi_of_register_slaves(struct spi_controller *ctrl)
>  {
>  	struct device_node *n;
>  	struct spi_board_info chip;
>  	struct property *reg;
> -	struct device_node *node = master->dev->device_node;
> +	struct device_node *node = ctrl->dev->device_node;
>  
>  	if (!IS_ENABLED(CONFIG_OFDEVICE))
>  		return;
> @@ -116,7 +117,7 @@ static void spi_of_register_slaves(struct spi_master *master)
>  	for_each_available_child_of_node(node, n) {
>  		memset(&chip, 0, sizeof(chip));
>  		chip.name = xstrdup(n->name);
> -		chip.bus_num = master->bus_num;
> +		chip.bus_num = ctrl->bus_num;
>  		/* Mode (clock phase/polarity/etc.) */
>  		if (of_property_read_bool(n, "spi-cpha"))
>  			chip.mode |= SPI_CPHA;
> @@ -171,7 +172,7 @@ spi_register_board_info(struct spi_board_info const *info, int n)
>  	return 0;
>  }
>  
> -static void scan_boardinfo(struct spi_master *master)
> +static void scan_boardinfo(struct spi_controller *ctrl)
>  {
>  	struct boardinfo	*bi;
>  
> @@ -180,27 +181,27 @@ static void scan_boardinfo(struct spi_master *master)
>  		unsigned		n;
>  
>  		for (n = bi->n_board_info; n > 0; n--, chip++) {
> -			debug("%s %d %d\n", __FUNCTION__, chip->bus_num, master->bus_num);
> -			if (chip->bus_num != master->bus_num)
> +			debug("%s %d %d\n", __FUNCTION__, chip->bus_num, ctrl->bus_num);
> +			if (chip->bus_num != ctrl->bus_num)
>  				continue;
>  			/* NOTE: this relies on spi_new_device to
>  			 * issue diagnostics when given bogus inputs
>  			 */
> -			(void) spi_new_device(master, chip);
> +			(void) spi_new_device(ctrl, chip);
>  		}
>  	}
>  }
>  
> -static LIST_HEAD(spi_master_list);
> +static LIST_HEAD(spi_controller_list);
>  
>  /**
> - * spi_register_master - register SPI master controller
> - * @master: initialized master, originally from spi_alloc_master()
> + * spi_register_ctrl - register SPI ctrl controller
> + * @ctrl: initialized ctrl, originally from spi_alloc_ctrl()
>   * Context: can sleep
>   *
> - * SPI master controllers connect to their drivers using some non-SPI bus,
> + * SPI controllers connect to their drivers using some non-SPI bus,
>   * such as the platform bus.  The final stage of probe() in that code
> - * includes calling spi_register_master() to hook up to this SPI bus glue.
> + * includes calling spi_register_ctrl() to hook up to this SPI bus glue.
>   *
>   * SPI controllers use board specific (often SOC specific) bus numbers,
>   * and board-specific addressing for SPI devices combines those numbers
> @@ -209,47 +210,47 @@ static LIST_HEAD(spi_master_list);
>   * chip is at which address.
>   *
>   * This must be called from context that can sleep.  It returns zero on
> - * success, else a negative error code (dropping the master's refcount).
> + * success, else a negative error code (dropping the ctrl's refcount).
>   * After a successful return, the caller is responsible for calling
> - * spi_unregister_master().
> + * spi_unregister_ctrl().
>   */
> -int spi_register_master(struct spi_master *master)
> +int spi_register_controller(struct spi_controller *ctrl)
>  {
>  	static int dyn_bus_id = (1 << 15) - 1;
>  	int			status = -ENODEV;
>  
> -	debug("%s: %s:%d\n", __func__, master->dev->name, master->dev->id);
> +	debug("%s: %s:%d\n", __func__, ctrl->dev->name, ctrl->dev->id);
>  
>  	/* even if it's just one always-selected device, there must
>  	 * be at least one chipselect
>  	 */
> -	if (master->num_chipselect == 0)
> +	if (ctrl->num_chipselect == 0)
>  		return -EINVAL;
>  
> -	if ((master->bus_num < 0) && master->dev->device_node)
> -		master->bus_num = of_alias_get_id(master->dev->device_node, "spi");
> +	if ((ctrl->bus_num < 0) && ctrl->dev->device_node)
> +		ctrl->bus_num = of_alias_get_id(ctrl->dev->device_node, "spi");
>  
>  	/* convention:  dynamically assigned bus IDs count down from the max */
> -	if (master->bus_num < 0)
> -		master->bus_num = dyn_bus_id--;
> +	if (ctrl->bus_num < 0)
> +		ctrl->bus_num = dyn_bus_id--;
>  
> -	list_add_tail(&master->list, &spi_master_list);
> +	list_add_tail(&ctrl->list, &spi_controller_list);
>  
> -	spi_of_register_slaves(master);
> +	spi_of_register_slaves(ctrl);
>  
>  	/* populate children from any spi device tables */
> -	scan_boardinfo(master);
> +	scan_boardinfo(ctrl);
>  	status = 0;
>  
>  	return status;
>  }
> -EXPORT_SYMBOL(spi_register_master);
> +EXPORT_SYMBOL(spi_register_ctrl);
>  
> -struct spi_master *spi_get_master(int bus)
> +struct spi_controller *spi_get_controller(int bus)
>  {
> -	struct spi_master* m;
> +	struct spi_controller* m;
>  
> -	list_for_each_entry(m, &spi_master_list, list) {
> +	list_for_each_entry(m, &spi_controller_list, list) {
>  		if (m->bus_num == bus)
>  			return m;
>  	}
> @@ -259,7 +260,7 @@ struct spi_master *spi_get_master(int bus)
>  
>  int spi_sync(struct spi_device *spi, struct spi_message *message)
>  {
> -	return spi->master->transfer(spi, message);
> +	return spi->controller->transfer(spi, message);
>  }
>  
>  /**
> diff --git a/include/spi/spi.h b/include/spi/spi.h
> index 620e5e57b4f8..8c6927da4107 100644
> --- a/include/spi/spi.h
> +++ b/include/spi/spi.h
> @@ -20,13 +20,14 @@ struct spi_board_info {
>  };
>  
>  /**
> - * struct spi_device - Master side proxy for an SPI slave device
> + * struct spi_device - Controller side proxy for an SPI slave device
>   * @dev: Driver model representation of the device.
> - * @master: SPI controller used with the device.
> + * @controller: SPI controller used with the device.
> + * @master: Copy of controller, for backwards compatibility
>   * @max_speed_hz: Maximum clock rate to be used with this chip
>   *	(on this board); may be changed by the device's driver.
>   *	The spi_transfer.speed_hz can override this for each transfer.
> - * @chip_select: Chipselect, distinguishing chips handled by @master.
> + * @chip_select: Chipselect, distinguishing chips handled by @controller.
>   * @mode: The spi mode defines how data is clocked out and in.
>   *	This may be changed by the device's driver.
>   *	The "active low" default for chipselect mode can be overridden
> @@ -59,7 +60,8 @@ struct spi_board_info {
>   */
>  struct spi_device {
>  	struct device_d		dev;
> -	struct spi_master	*master;
> +	struct spi_controller	*controller;
> +	struct spi_controller	*master;	/* compatibility layer */
>  	u32			max_speed_hz;
>  	u8			chip_select;
>  	u8			mode;
> @@ -93,7 +95,7 @@ struct spi_device {
>  struct spi_message;
>  
>  /**
> - * struct spi_master - interface to SPI master controller
> + * struct spi_controller - interface to SPI master or slave controller
>   * @dev: device interface to this driver
>   * @bus_num: board-specific (and often SOC-specific) identifier for a
>   *	given SPI controller.
> @@ -108,8 +110,9 @@ struct spi_message;
>   *	the device whose settings are being modified.
>   * @transfer: adds a message to the controller's transfer queue.
>   * @cleanup: frees controller-specific state
> + * @list: link with the global spi_controller list
>   *
> - * Each SPI master controller can communicate with one or more @spi_device
> + * Each SPI controller can communicate with one or more @spi_device
>   * children.  These make a small bus, sharing MOSI, MISO and SCK signals
>   * but not chip select signals.  Each device may be configured to use a
>   * different clock rate, since those shared signals are ignored unless
> @@ -120,7 +123,7 @@ struct spi_message;
>   * an SPI slave device.  For each such message it queues, it calls the
>   * message's completion function when the transaction completes.
>   */
> -struct spi_master {
> +struct spi_controller {
>  	struct device_d *dev;
>  
>  	/* other than negative (== assign one dynamically), bus_num is fully
> @@ -147,7 +150,7 @@ struct spi_master {
>  	 *   any other request management
>  	 * + To a given spi_device, message queueing is pure fifo
>  	 *
> -	 * + The master's main job is to process its message queue,
> +	 * + The controller's main job is to process its message queue,
>  	 *   selecting a chip then transferring data
>  	 * + If there are multiple spi_device children, the i/o queue
>  	 *   arbitration algorithm is unspecified (round robin, fifo,
> @@ -161,12 +164,15 @@ struct spi_master {
>  	int			(*transfer)(struct spi_device *spi,
>  						struct spi_message *mesg);
>  
> -	/* called on release() to free memory provided by spi_master */
> +	/* called on release() to free memory provided by spi_controller */
>  	void			(*cleanup)(struct spi_device *spi);
>  
>  	struct list_head list;
>  };
>  
> +#define spi_master  			spi_controller
> +#define spi_register_master(_ctrl)	spi_register_controller(_ctrl)
> +
>  /*---------------------------------------------------------------------------*/
>  
>  /*
> @@ -341,9 +347,9 @@ spi_transfer_del(struct spi_transfer *t)
>  
>  int spi_sync(struct spi_device *spi, struct spi_message *message);
>  
> -struct spi_device *spi_new_device(struct spi_master *master,
> +struct spi_device *spi_new_device(struct spi_controller *ctrl,
>  				  struct spi_board_info *chip);
> -int spi_register_master(struct spi_master *master);
> +int spi_register_controller(struct spi_controller *ctrl);
>  
>  #ifdef CONFIG_SPI
>  int spi_register_board_info(struct spi_board_info const *info, int num);
> @@ -431,7 +437,7 @@ static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
>  
>  extern struct bus_type spi_bus;
>  
> -struct spi_master *spi_get_master(int bus);
> +struct spi_controller *spi_get_controller(int bus);
>  
>  static inline int spi_driver_register(struct driver_d *drv)
>  {
> -- 
> git-series 0.9.1
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
> 

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 02/10] spi: Generalize SPI "master" to "controller"
  2019-05-11  7:36   ` Alexander Kurz
@ 2019-05-16  9:29     ` Steffen Trumtrar
  0 siblings, 0 replies; 14+ messages in thread
From: Steffen Trumtrar @ 2019-05-16  9:29 UTC (permalink / raw)
  To: Alexander Kurz; +Cc: Barebox List


Hi Alexander,

Alexander Kurz <akurz@blala.de> writes:

> Hi Steffen,
> this commit seems to be the origin of a crash when running the 
> spi 
> command, e.g.
>

I just sent a patch that should fix this problem:

    [PATCH] commands: spi: fix NULL pointer dereference


Thanks for testing,
Steffen

-- 
Pengutronix e.K.                          | Steffen Trumtrar 
|
Industrial Linux Solutions                | 
http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany| Phone: 
+49-5121-206917-0   |
Amtsgericht Hildesheim, HRA 2686          | Fax: 
+49-5121-206917-5555|

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

end of thread, other threads:[~2019-05-16  9:29 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-03  9:33 [PATCH 00/10] spi: spi-mem and fsl-qspi support Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 01/10] mtd: spi-nor: cadence: add cqspi_set_protocol Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 02/10] spi: Generalize SPI "master" to "controller" Steffen Trumtrar
2019-05-11  7:36   ` Alexander Kurz
2019-05-16  9:29     ` Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 03/10] spi: Import more spi mode defines from Linux Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 04/10] spi: Extend the core to ease integration of SPI memory controllers Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 05/10] mtd: spi-nor: remove unused write_enable from write_reg Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 06/10] mtd: spi-nor: remove unused read_xfer/write_xfer hooks Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 07/10] spi: add driver for Freescale QSPI controller Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 08/10] mtd: spi-nor: introduce SPI 1-2-2 and SPI 1-4-4 protocols Steffen Trumtrar
2019-05-03  9:33 ` [PATCH 09/10] mtd: spi-nor: provide default erase_sector implementation Steffen Trumtrar
2019-05-03  9:34 ` [PATCH 10/10] mtd: devices: m25p80: use the spi_mem_xx() API Steffen Trumtrar
2019-05-08  8:54 ` [PATCH 00/10] spi: spi-mem and fsl-qspi support Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox