Skip to contentSkip to conversation

spi: Add Freescale QuadSpi driver

PATCH New Xiaochun Li 3 participants 3 messages Jun 21, 2013 – Jun 24, 2013 SPI

lore
Share & tools
lore
#0 lore raw

PATCH 2/2 spi: Add Freescale QuadSpi driver

Add QuadSpi driver for Freescale's Quad Spi controller. This controller is designed for serial flash access.
This driver has been tested on vf610-twr board with m25p80 type chips. The hardware has no support for other types of spi peripherals. Chip select control is handled via Serial Flash Address Register.

---
 drivers/spi/Kconfig           |   5 +
 drivers/spi/Makefile          |   1 +
 drivers/spi/spi-fsl-quadspi.c | 595 ++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-fsl-quadspi.h | 167 ++++++++++++
 4 files changed, 768 insertions(+)
 create mode 100644 drivers/spi/spi-fsl-quadspi.c
 create mode 100644 drivers/spi/spi-fsl-quadspi.h
drivers/spi/Kconfig +5 -0
index 2015897..3c8b72c 100644
@@ -198,6 +198,11 @@ config SPI_IMX
 	  This enables using the Freescale i.MX SPI controllers in master
 	  mode.
 
+config SPI_FSL_QUADSPI
+        tristate "Freescale Quad SPI controller"
+	help
+	  This enables support for the Quad SPI controller in master mode.
+
 config SPI_LM70_LLP
 	tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
 	depends on PARPORT
drivers/spi/Makefile +1 -0
index 33f9c09..c56aa2e 100644
@@ -35,6 +35,7 @@ obj-$(CONFIG_SPI_FSL_ESPI)		+= spi-fsl-espi.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi-fsl-spi.o
 obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
+obj-$(CONFIG_SPI_FSL_QUADSPI)           += spi-fsl-quadspi.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
drivers/spi/spi-fsl-quadspi.c new file +595 -0

595 lines not on this page · raw message

drivers/spi/spi-fsl-quadspi.h new file +167 -0

167 lines not on this page · raw message

Signature · 1 line
1.8.0
#1 lore raw

PATCH 2/2 spi: Add Freescale QuadSpi driver

Quoted 8 lines · Xiaochun Li

Add QuadSpi driver for Freescale's Quad Spi controller. This controller is designed for serial flash access.
This driver has been tested on vf610-twr board with m25p80 type chips. The hardware has no support for other types of spi peripherals. Chip select control is handled via Serial Flash Address Register.

Quoted 8 lines
---
 drivers/spi/Kconfig           |   5 +
 drivers/spi/Makefile          |   1 +
 drivers/spi/spi-fsl-quadspi.c | 595 ++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-fsl-quadspi.h | 167 ++++++++++++
 4 files changed, 768 insertions(+)
 create mode 100644 drivers/spi/spi-fsl-quadspi.c
 create mode 100644 drivers/spi/spi-fsl-quadspi.h

There should be a binding doc along with the patch. Check Documentation/devicetree/bindings/spi/ for examples.

drivers/spi/Kconfig config SPI_IMX · line 202

Quoted 11 lines
drivers/spi/Kconfig +2 -0
index 2015897..3c8b72c 100644
@@ -198,6 +198,11 @@ config SPI_IMX
 	  This enables using the Freescale i.MX SPI controllers in master
 	  mode.
 
+config SPI_FSL_QUADSPI
+        tristate "Freescale Quad SPI controller"

I see indentation problem here, probably because you're using spaces here.

Quoted 42 lines
drivers/spi/Kconfig +3 -0
+	help
+	  This enables support for the Quad SPI controller in master mode.
+
 config SPI_LM70_LLP
 	tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
 	depends on PARPORT
drivers/spi/Makefile +1 -0
index 33f9c09..c56aa2e 100644
@@ -35,6 +35,7 @@ obj-$(CONFIG_SPI_FSL_ESPI)		+= spi-fsl-espi.o
 obj-$(CONFIG_SPI_FSL_SPI)		+= spi-fsl-spi.o
 obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
+obj-$(CONFIG_SPI_FSL_QUADSPI)           += spi-fsl-quadspi.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
drivers/spi/spi-fsl-quadspi.c new file +18 -0
new file mode 100644
index 0000000..ceb46ab
@@ -0,0 +1,595 @@
+/*
+ * Freescale Quad SPI driver.
+ *
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>

drivers/spi/spi-fsl-quadspi.c line 28

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+
+#include "spi-fsl-quadspi.h"

If the header is only used by this file, we do not have to have the header. It's always easier to search definitions in a single file than in two.

drivers/spi/spi-fsl-quadspi.c line 30

Quoted 2 lines
drivers/spi/spi-fsl-quadspi.c new file +2 -0
+
+static inline unsigned int fsl_qspi_check_status(struct fsl_qspi *fsl_qspi)

fsl_qspi_check_busy() could be a better name.

drivers/spi/spi-fsl-quadspi.c line 43

Quoted 13 lines
drivers/spi/spi-fsl-quadspi.c new file +13 -0
+{
+	return readl(fsl_qspi->iobase + QUADSPI_SR) & QSPI_SR_BUSY_MASK;
+}
+
+static inline unsigned int fsl_qspi_check_txfull(struct fsl_qspi *fsl_qspi)
+{
+	return readl(fsl_qspi->iobase + QUADSPI_SR) & QSPI_SR_TXFULL_MASK;
+}
+
+static void wait_do_timer(unsigned long arg)
+{
+	struct fsl_qspi *fsl_qspi = (struct fsl_qspi *)(arg);
+	fsl_qspi->timeout = 1;

I expect "true" to be used here, since timeout is defined as bool and "false" is used in probe() for initialization.

Quoted 8 lines
drivers/spi/spi-fsl-quadspi.c new file +8 -0
+}
+
+static irqreturn_t fsl_qspi_irq_handler(int this_irq, void *dev_id)
+{
+	struct fsl_qspi *fsl_qspi = dev_id;
+	u32 reg_fr = readl(fsl_qspi->iobase + QUADSPI_FR);
+
+	/* clear interrupt */

drivers/spi/spi-fsl-quadspi.c line 61

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+	writel(reg_fr, fsl_qspi->iobase + QUADSPI_FR);
+	if (reg_fr & QSPI_FR_TFF_MASK)
+		wake_up(&fsl_qspi->waitq);
+
+	return IRQ_HANDLED;
+}
+
+static void set_lut(struct fsl_qspi *fsl_qspi)
+{
+	u32 lut_base;

Have a blank line here.

Quoted 23 lines
drivers/spi/spi-fsl-quadspi.c new file +23 -0
+	/* Unlock the LUT */
+	writel(KEY_VALUE, fsl_qspi->iobase + QUADSPI_LUTKEY);
+	writel(QSPI_LCKCR_UNLOCK, fsl_qspi->iobase + QUADSPI_LCKCR);
+
+	/* SEQID 1 - Write enable */
+	lut_base = WEN_SEQID * 4;
+	writel(OPRND0(0x6) | PAD0(0x0) | INSTR0(CMD),
+			fsl_qspi->iobase + QUADSPI_LUT(lut_base));
+	writel(0x0, fsl_qspi->iobase + QUADSPI_LUT(lut_base + 1));
+	writel(0x0, fsl_qspi->iobase + QUADSPI_LUT(lut_base + 2));
+	writel(0x0, fsl_qspi->iobase + QUADSPI_LUT(lut_base + 3));
+
+	/* SEQID 3 - Read Status */
+	lut_base = READSTATU_SEQID * 4;
+	writel(OPRND0(0x05) | PAD0(0x0) | INSTR0(CMD) |
+			OPRND1(0x1) | PAD1(0x0) | INSTR1(READ),
+			fsl_qspi->iobase + QUADSPI_LUT(lut_base));
+	writel(0x0, fsl_qspi->iobase + QUADSPI_LUT(lut_base + 1));
+	writel(0x0, fsl_qspi->iobase + QUADSPI_LUT(lut_base + 2));
+	writel(0x0, fsl_qspi->iobase + QUADSPI_LUT(lut_base + 3));
+
+	/* Lock the LUT */
+	writel(KEY_VALUE, fsl_qspi->iobase + QUADSPI_LUTKEY);

drivers/spi/spi-fsl-quadspi.c line 94

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+	writel(QSPI_LCKCR_LOCK, fsl_qspi->iobase + QUADSPI_LCKCR);
+}
+
+static void waiting_for_end(struct fsl_qspi *fsl_qspi,
+		u32 current_bit, u32 bitvalue, u32 timeout)
+{
+	u32 reg, status_reg;
+
+	fsl_qspi->wait_timer.expires = jiffies + timeout;
+	add_timer(&fsl_qspi->wait_timer);

Shouldn't we always initialize timeout to be false when adding the timer? Otherwise, once we get one timeout event, we will timeout all the time?

drivers/spi/spi-fsl-quadspi.c line 102

Quoted 8 lines
drivers/spi/spi-fsl-quadspi.c new file +8 -0
+
+	status_reg = !bitvalue << current_bit;
+	while ((status_reg & (0x1 << current_bit)) != bitvalue << current_bit) {
+		if (fsl_qspi->timeout) {
+			dev_err(fsl_qspi->dev, "tired waiting for end status\n");
+			break;
+		}
+		writel((READSTATU_SEQID << QSPI_IPCR_SEQID_SHIFT) | 1,

There are so many bit filed definitions, and no one for this magic "1"?

Quoted 290 lines
drivers/spi/spi-fsl-quadspi.c new file +290 -0
+				fsl_qspi->iobase + QUADSPI_IPCR);
+		wait_event(fsl_qspi->waitq, !fsl_qspi_check_status(fsl_qspi));
+
+		reg = readl(fsl_qspi->iobase + QUADSPI_RBSR);
+		if (reg & QSPI_RBSR_RDBFL_MASK)
+			status_reg = readl(fsl_qspi->iobase +
+					QUADSPI_RBDR);
+
+		writel(readl(fsl_qspi->iobase + QUADSPI_MCR) |
+				QSPI_MCR_CLR_RXF_MASK,
+				fsl_qspi->iobase + QUADSPI_MCR);
+	}
+	del_timer(&fsl_qspi->wait_timer);
+}
+
+static void set_send_lut(struct fsl_qspi *fsl_qspi, u8 opr,
+		u32 num_of_trans, u32 len)
+{
+	u32 lut[4], lut_base;
+
+	writel(KEY_VALUE, fsl_qspi->iobase + QUADSPI_LUTKEY);
+	writel(QSPI_LCKCR_UNLOCK, fsl_qspi->iobase + QUADSPI_LCKCR);
+
+	lut[0] = OPRND0(opr) | PAD0(0x0) | INSTR0(CMD);
+	lut[1] = 0;
+	lut[2] = 0;
+	lut[3] = 0;
+
+	if (len > 1)
+		lut[0] = lut[0] | OPRND1(ADDR24BIT) | PAD1(0x0) | INSTR1(ADDR);
+	if (num_of_trans > 1)
+		lut[1] = OPRND0(TX_BUFFER_SIZE) | PAD0(0x0) | INSTR0(WRITE);
+	lut_base = TX_SEQID * 4;
+	writel(lut[0], fsl_qspi->iobase + QUADSPI_LUT(lut_base));
+	writel(lut[1], fsl_qspi->iobase + QUADSPI_LUT(lut_base + 1));
+	writel(lut[2], fsl_qspi->iobase + QUADSPI_LUT(lut_base + 2));
+	writel(lut[3], fsl_qspi->iobase + QUADSPI_LUT(lut_base + 3));
+
+	writel(KEY_VALUE, fsl_qspi->iobase + QUADSPI_LUTKEY);
+	writel(QSPI_LCKCR_LOCK, fsl_qspi->iobase + QUADSPI_LCKCR);
+}
+
+static void set_receive_lut(struct fsl_qspi *fsl_qspi, u8 opr,
+		u32 num_of_tran, u32 len, u32 tx_len)
+{
+	u32 lut[4], lut_base;
+
+	writel(KEY_VALUE, fsl_qspi->iobase + QUADSPI_LUTKEY);
+	writel(QSPI_LCKCR_UNLOCK, fsl_qspi->iobase + QUADSPI_LCKCR);
+
+	lut[0] = OPRND0(opr) | PAD0(0x0) | INSTR0(CMD);
+	lut[1] = OPRND0(ADDR24BIT) | PAD0(0x0) | INSTR0(ADDR);
+	lut[2] = 0;
+	lut[3] = 0;
+
+	if (tx_len > 1) {
+		len = RX_BUFFER_SIZE;
+		lut[0] = lut[0] | OPRND1(ADDR24BIT) | PAD1(0x0) | INSTR1(ADDR);
+		lut[1] = OPRND0(len) | PAD0(0x0) | INSTR0(READ);
+	} else {
+		lut[0] = lut[0] | OPRND1(len) | PAD1(0x0) | INSTR1(READ);
+	}
+	lut_base = RX_SEQID * 4;
+	writel(lut[0], fsl_qspi->iobase + QUADSPI_LUT(lut_base));
+	writel(lut[1], fsl_qspi->iobase + QUADSPI_LUT(lut_base + 1));
+	writel(lut[2], fsl_qspi->iobase + QUADSPI_LUT(lut_base + 2));
+	writel(lut[3], fsl_qspi->iobase + QUADSPI_LUT(lut_base + 3));
+
+	writel(KEY_VALUE, fsl_qspi->iobase + QUADSPI_LUTKEY);
+	writel(QSPI_LCKCR_LOCK, fsl_qspi->iobase + QUADSPI_LCKCR);
+}
+
+static void fsl_qspi_do_rx(struct fsl_qspi *fsl_qspi, u32 position,
+		u32 count, u32 *rxbuf)
+{
+	u32 tmp, i, size;
+
+	position += PSP_QSPI0_MEMMAP_BASE;
+
+	while (count > 0) {
+		writel(position, fsl_qspi->iobase + QUADSPI_SFAR);
+		size = (count > RX_BUFFER_SIZE) ?
+			RX_BUFFER_SIZE : count;
+
+		writel(RX_SEQID << QSPI_IPCR_SEQID_SHIFT | size,
+				fsl_qspi->iobase + QUADSPI_IPCR);
+		wait_event(fsl_qspi->waitq, !fsl_qspi_check_status(fsl_qspi));
+
+		position += size;
+		count -= size;
+
+		i = 0;
+		while ((RX_BUFFER_SIZE >= size) && (size > 0)) {
+			tmp = readl(fsl_qspi->iobase + QUADSPI_RBDR +
+					i * 4);
+			*rxbuf = endian_change_32bit(tmp);
+			rxbuf++;
+			size -= 4;
+			i++;
+		}
+
+		writel(readl(fsl_qspi->iobase + QUADSPI_MCR) |
+				QSPI_MCR_CLR_RXF_MASK,
+				fsl_qspi->iobase + QUADSPI_MCR);
+	}
+}
+
+static void fsl_qspi_do_tx(struct fsl_qspi *fsl_qspi,
+		u32 position, u32 count, const u32 *txbuf)
+{
+	u32 tmp, i, j, size, tx_size;
+
+	position += PSP_QSPI0_MEMMAP_BASE;
+
+	while (count > 0) {
+		writel(position, fsl_qspi->iobase + QUADSPI_SFAR);
+
+		writel((WEN_SEQID << QSPI_IPCR_SEQID_SHIFT) | 0,
+				fsl_qspi->iobase + QUADSPI_IPCR);
+		wait_event(fsl_qspi->waitq, !fsl_qspi_check_status(fsl_qspi));
+
+		waiting_for_end(fsl_qspi, WRITE_ENABLE_BIT,
+				VALID_WRITE_ENABLE_BIT, WRITE_ENABLE_TIMEOUT);
+
+		writel(position, fsl_qspi->iobase + QUADSPI_SFAR);
+		tx_size = (count > TX_BUFFER_SIZE) ?
+			TX_BUFFER_SIZE : count;
+
+		position += tx_size;
+		count -= tx_size;
+
+		size = (tx_size + 3) / 4;
+		i = (size > 16) ? 16 : size;
+		for (j = 0; j < i; j++) {
+			tmp = endian_change_32bit(*txbuf);
+			writel(tmp, fsl_qspi->iobase + QUADSPI_TBDR);
+			txbuf++;
+		}
+
+		writel(TX_SEQID << QSPI_IPCR_SEQID_SHIFT | tx_size,
+				fsl_qspi->iobase + QUADSPI_IPCR);
+
+		for (j = i; j < size; j++) {
+			wait_event(fsl_qspi->waitq,
+					!fsl_qspi_check_txfull(fsl_qspi));
+			tmp = endian_change_32bit(*txbuf);
+			writel(tmp, fsl_qspi->iobase + QUADSPI_TBDR);
+			txbuf++;
+		}
+
+		wait_event(fsl_qspi->waitq, !fsl_qspi_check_status(fsl_qspi));
+
+		waiting_for_end(fsl_qspi, STATUS_BIT,
+				VALID_STATUS_BIT, READ_STATUS_TIMEOUT);
+	}
+}
+
+static void fsl_qspi_do_tx_cmd(struct fsl_qspi *fsl_qspi,
+		u32 position)
+{
+	position += PSP_QSPI0_MEMMAP_BASE;
+	writel(position, fsl_qspi->iobase + QUADSPI_SFAR);
+
+	writel((TX_SEQID << QSPI_IPCR_SEQID_SHIFT) | 0,
+			fsl_qspi->iobase + QUADSPI_IPCR);
+
+	wait_event(fsl_qspi->waitq, !fsl_qspi_check_status(fsl_qspi));
+
+	waiting_for_end(fsl_qspi, STATUS_BIT,
+			VALID_STATUS_BIT, READ_STATUS_TIMEOUT);
+}
+
+static void fsl_qspi_write_trans(struct spi_master *master,
+		struct spi_message *msg, u32 num)
+{
+	struct fsl_qspi *fsl_qspi = spi_master_get_devdata(master);
+	struct spi_transfer *xfer;
+	u8 opr = 0;
+	loff_t pos = 0;
+	int status = 0;
+	u32 mcr_reg, trans_counter = 0;
+
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+		if (xfer->tx_buf && trans_counter == 0) {
+			opr = *(u8 *)xfer->tx_buf;
+			if (xfer->len > 1)
+				pos = *(u32 *)xfer->tx_buf & POS_LOCATION_MASK;
+
+			msg->actual_length += xfer->len;
+			set_send_lut(fsl_qspi, opr, num, xfer->len);
+		}
+
+		mcr_reg = readl(fsl_qspi->iobase + QUADSPI_MCR);
+		writel(QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | QSPI_MCR_RESERVED_MASK,
+				fsl_qspi->iobase + QUADSPI_MCR);
+		writel(QSPI_RBCT_WMRK_MASK | QSPI_RBCT_RXBRD_USEIPS,
+				fsl_qspi->iobase + QUADSPI_RBCT);
+
+		if (xfer->tx_buf && num > 1 && trans_counter > 0) {
+			fsl_qspi_do_tx(fsl_qspi, endian_change_32bit(pos),
+					xfer->len, xfer->tx_buf);
+			msg->actual_length += xfer->len;
+		} else if (xfer->tx_buf && (num == 1) && (trans_counter == 0)) {
+			fsl_qspi_do_tx_cmd(fsl_qspi, endian_change_32bit(pos));
+		}
+		writel(mcr_reg, fsl_qspi->iobase + QUADSPI_MCR);
+
+		if (xfer->delay_usecs)
+			udelay(xfer->delay_usecs);
+
+		trans_counter++;
+	}
+
+	msg->status = status;
+	spi_finalize_current_message(master);
+}
+
+static void fsl_qspi_read_trans(struct spi_master *master,
+		struct spi_message *msg, u32 num)
+{
+	struct fsl_qspi *fsl_qspi = spi_master_get_devdata(master);
+	struct spi_transfer *xfer;
+	u32 tx_len = 0, trans_counter = 0, mcr_reg;
+	u8 opr = 0;
+	loff_t pos = 0;
+	int status = 0;
+
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+
+		writel(QSPI_RBCT_WMRK_MASK | QSPI_RBCT_RXBRD_USEIPS,
+				fsl_qspi->iobase + QUADSPI_RBCT);
+
+		if (xfer->tx_buf && trans_counter == 0) {
+			opr = *(u8 *)xfer->tx_buf;
+			if (xfer->len > 1)
+				pos = *(u32 *)xfer->tx_buf & POS_LOCATION_MASK;
+			else
+				pos = 0;
+			tx_len = xfer->len;
+		}
+
+		if (xfer->rx_buf && trans_counter > 0) {
+			set_receive_lut(fsl_qspi, opr, num, xfer->len, tx_len);
+
+			mcr_reg = readl(fsl_qspi->iobase + QUADSPI_MCR);
+			writel(QSPI_MCR_CLR_RXF_MASK |
+					QSPI_MCR_CLR_TXF_MASK | QSPI_MCR_RESERVED_MASK,
+					fsl_qspi->iobase + QUADSPI_MCR);
+
+			fsl_qspi_do_rx(fsl_qspi, endian_change_32bit(pos),
+					xfer->len, xfer->rx_buf);
+
+			writel(mcr_reg, fsl_qspi->iobase + QUADSPI_MCR);
+		}
+
+		if (xfer->delay_usecs)
+			udelay(xfer->delay_usecs);
+		msg->actual_length += xfer->len;
+
+		trans_counter++;
+	}
+
+	msg->status = status;
+	spi_finalize_current_message(master);
+}
+
+static int fsl_qspi_do_one_msg(struct spi_master *master,
+		struct spi_message *m)
+{
+	struct spi_transfer *t;
+	u8 *rx_buf = NULL;
+	unsigned int xfer_num = 0;
+	struct fsl_qspi *fsl_qspi = spi_master_get_devdata(master);
+
+	writel(INT_TFIE, fsl_qspi->iobase + QUADSPI_RSER);
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->rx_buf)
+			rx_buf = t->rx_buf;
+		xfer_num++;
+	}
+
+	if (!rx_buf)
+		fsl_qspi_write_trans(master, m, xfer_num);
+	else
+		fsl_qspi_read_trans(master, m, xfer_num);
+
+	return m->status;
+}

drivers/spi/spi-fsl-quadspi.c line 402

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+
+static int fsl_qspi_setup(struct spi_device *spi)
+{
+	u32 reg_val, smpr_val, seq_id;
+	struct fsl_qspi *fsl_qspi;
+
+	fsl_qspi = spi_master_get_devdata(spi->master);
+
+	if ((spi->bits_per_word < 8) || (spi->bits_per_word > 16)) {
+		dev_dbg(&spi->dev, "%d bits per word is not supported\n",

dev_err

drivers/spi/spi-fsl-quadspi.c line 408

Quoted 6 lines
drivers/spi/spi-fsl-quadspi.c new file +6 -0
+				spi->bits_per_word);
+		return -EINVAL;
+	}
+
+	if (spi->chip_select >= spi->master->num_chipselect) {
+		dev_dbg(&spi->dev, "%d chip select is out of range\n",

Ditto

Quoted 48 lines
drivers/spi/spi-fsl-quadspi.c new file +48 -0
+				spi->chip_select);
+		return -EINVAL;
+	}
+
+	writel(QSPI_MCR_RESERVED_MASK | QSPI_MCR_MDIS_MASK,
+			fsl_qspi->iobase + QUADSPI_MCR);
+
+	reg_val = readl(fsl_qspi->iobase + QUADSPI_SMPR);
+
+	writel(reg_val & ~(QSPI_SMPR_FSDLY_MASK
+				| QSPI_SMPR_FSPHS_MASK
+				| QSPI_SMPR_HSENA_MASK),
+			fsl_qspi->iobase + QUADSPI_SMPR);
+
+	writel(QSPI_MCR_RESERVED_MASK, fsl_qspi->iobase + QUADSPI_MCR);
+
+	writel(TPADA | PSP_QSPI0_MEMMAP_BASE,
+			fsl_qspi->iobase + QUADSPI_SFA1AD);
+	writel(TPADA | PSP_QSPI0_MEMMAP_BASE,
+			fsl_qspi->iobase + QUADSPI_SFA2AD);
+	writel(TPADB | PSP_QSPI0_MEMMAP_BASE,
+			fsl_qspi->iobase + QUADSPI_SFB1AD);
+	writel(TPADB | PSP_QSPI0_MEMMAP_BASE,
+			fsl_qspi->iobase + QUADSPI_SFB2AD);
+
+	set_lut(fsl_qspi);
+
+	reg_val = 0;
+	reg_val |= QSPI_MCR_RESERVED_MASK;
+	smpr_val = readl(fsl_qspi->iobase + QUADSPI_SMPR);
+	smpr_val &= ~QSPI_SMPR_DDRSMP_MASK;
+	writel(smpr_val, fsl_qspi->iobase + QUADSPI_SMPR);
+	reg_val &= ~QSPI_MCR_DDR_EN_MASK;
+	writel(reg_val, fsl_qspi->iobase + QUADSPI_MCR);
+
+	seq_id = 0;
+	reg_val = readl(fsl_qspi->iobase + QUADSPI_BFGENCR);
+	reg_val &= ~QSPI_BFGENCR_SEQID_MASK;
+	reg_val |= (seq_id << QSPI_BFGENCR_SEQID_SHIFT);
+	reg_val &= ~QSPI_BFGENCR_PAR_EN_MASK;
+	writel(reg_val, fsl_qspi->iobase + QUADSPI_BFGENCR);
+
+	return 0;
+}
+
+static int fsl_qspi_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;

drivers/spi/spi-fsl-quadspi.c line 466

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+	struct spi_master *master;
+	struct fsl_qspi *fsl_qspi;
+	struct resource *res;
+	int ret, num_cs, status = 0;
+	struct clk *gate;
+
+	if (!np) {
+		dev_err(&pdev->dev, "can't get the platform data\n");
+		return -EINVAL;
+	}

The driver supports DT probe only. We can not get here at all if the np is NULL. The check is meaningless.

drivers/spi/spi-fsl-quadspi.c line 472

Quoted 6 lines
drivers/spi/spi-fsl-quadspi.c new file +6 -0
+
+	ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "can't get the spi-mum-chipselects\n");
+		return ret;
+	}

If you move the block somewhere after spi_alloc_master() call, you can use master->num_chipselect directly, and save one local variable and one assignment.

drivers/spi/spi-fsl-quadspi.c line 476

Quoted 4 lines
drivers/spi/spi-fsl-quadspi.c new file +4 -0
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*fsl_qspi));
+	if (master == NULL) {
+		dev_dbg(&pdev->dev, "spi_alloc_master failed\n");

It hits an error condition. We should use dev_err().

drivers/spi/spi-fsl-quadspi.c line 487

Quoted 11 lines
drivers/spi/spi-fsl-quadspi.c new file +11 -0
+		return -ENOMEM;
+	}
+
+	fsl_qspi = spi_master_get_devdata(master);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_dbg(&pdev->dev, "platform_get_resource failed\n");
+		status = -ENXIO;
+		goto fail0;
+	}

devm_ioremap_resource() covers the error checking, so we can save it. Check kerenldoc of function devm_ioremap_resource() for usage example.

drivers/spi/spi-fsl-quadspi.c line 492

Quoted 5 lines
drivers/spi/spi-fsl-quadspi.c new file +5 -0
+
+	fsl_qspi->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (!fsl_qspi->iobase) {
+		dev_dbg(&pdev->dev, "ioremap failed\n");
+		status = -ENOMEM;

We should return the error code returned by devm_ioremap_resource(). Again, check usage example in devm_ioremap_resource kerenldoc.

Nit, we generally name the variable used for return as "ret".

drivers/spi/spi-fsl-quadspi.c line 493

Quoted 1 line
drivers/spi/spi-fsl-quadspi.c new file +1 -0
+		goto fail0;

Can we name the label in a meaningful way, like out_put_master or something?

drivers/spi/spi-fsl-quadspi.c line 499

Quoted 6 lines
drivers/spi/spi-fsl-quadspi.c new file +6 -0
+	}
+
+	gate = devm_clk_get(&pdev->dev, "qspi_en");
+	if (IS_ERR(gate)) {
+		dev_dbg(&pdev->dev, "clk_get failed\n");
+		status = PTR_ERR(fsl_qspi->clk);

Shouldn't be PTR_ERR(gate)? Also why it can be a local variable while the "qspi" clk below is a member variable of fsl_qspi.

drivers/spi/spi-fsl-quadspi.c line 502

Quoted 3 lines
drivers/spi/spi-fsl-quadspi.c new file +3 -0
+		goto fail0;
+	}
+	clk_prepare_enable(gate);

The function call can fail. Error check?

drivers/spi/spi-fsl-quadspi.c line 512

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+
+	fsl_qspi->clk = devm_clk_get(&pdev->dev, "qspi");
+	if (IS_ERR(fsl_qspi->clk)) {
+		dev_dbg(&pdev->dev, "clk_get failed\n");
+		status = PTR_ERR(fsl_qspi->clk);
+		goto fail1;
+	}
+	clk_prepare_enable(fsl_qspi->clk);
+
+	fsl_qspi->irq = platform_get_irq(pdev, 0);

It seems to me that the "irq" can simply be a local variable?

drivers/spi/spi-fsl-quadspi.c line 515

Quoted 3 lines
drivers/spi/spi-fsl-quadspi.c new file +3 -0
+	if (fsl_qspi->irq < 0) {
+		dev_dbg(&pdev->dev, "platform_get_irq failed\n");
+		status = -ENXIO;

fsl_qspi->irq is just the error code we should return here.

Quoted 22 lines
drivers/spi/spi-fsl-quadspi.c new file +22 -0
+		goto fail2;
+	}
+
+	status = devm_request_irq(&pdev->dev, fsl_qspi->irq,
+			fsl_qspi_irq_handler, 0, pdev->name, fsl_qspi);
+	if (status) {
+		dev_dbg(&pdev->dev, "request_irq failed\n");
+		goto fail2;
+	}
+
+	init_timer(&fsl_qspi->wait_timer);
+	fsl_qspi->wait_timer.function = &wait_do_timer;
+	fsl_qspi->wait_timer.data = (unsigned long)fsl_qspi;
+	fsl_qspi->timeout = false;
+
+	init_waitqueue_head(&fsl_qspi->waitq);
+	fsl_qspi->dev = &pdev->dev;
+
+	master->bus_num = pdev->id;
+	master->num_chipselect = num_cs;
+	master->dev.of_node = pdev->dev.of_node;
+

drivers/spi/spi-fsl-quadspi.c line 547

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+	master->setup = fsl_qspi_setup;
+	master->transfer_one_message = fsl_qspi_do_one_msg;
+	platform_set_drvdata(pdev, master);
+
+	status = spi_register_master(master);
+	if (status) {
+		dev_dbg(&pdev->dev, "spi_register_master failed\n");
+		goto fail2;
+	}
+	dev_info(&pdev->dev, "QuadSpi bus driver\n");

Nit: I'm more comfortable with spelling it "QuadSPI", which is how Reference Manual spells.

drivers/spi/spi-fsl-quadspi.c line 558

Quoted 11 lines
drivers/spi/spi-fsl-quadspi.c new file +11 -0
+
+	return 0;
+
+fail2:
+	clk_disable_unprepare(fsl_qspi->clk);
+fail1:
+	clk_disable_unprepare(gate);
+fail0:
+	spi_master_put(master);
+
+	dev_dbg(&pdev->dev, "Fsl QuadSpi probe failed\n");

Nit: Either "FSL" or "Freescale" looks better than "Fsl" to me.

Quoted 11 lines
drivers/spi/spi-fsl-quadspi.c new file +11 -0
+
+	return status;
+}
+
+static int fsl_qspi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct fsl_qspi *fsl_qspi = spi_master_get_devdata(master);
+
+	/* disable the hardware */
+	writel(0x0, fsl_qspi->iobase + QUADSPI_MCR);

drivers/spi/spi-fsl-quadspi.c line 579

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+	writel(0x0, fsl_qspi->iobase + QUADSPI_RSER);
+
+	clk_disable_unprepare(fsl_qspi->clk);
+	spi_master_put(master);
+
+	return 0;
+}
+
+static struct of_device_id fsl_qspi_dt_ids[] = {
+	{ .compatible = "fsl,vf610-qspi", .data = NULL, },

.data has been initialized to NULL.

Quoted 27 lines
drivers/spi/spi-fsl-quadspi.c new file +16 -0
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
+
+static struct platform_driver fsl_qspi_driver = {
+	.driver.name    = DRIVER_NAME,
+	.driver.of_match_table = fsl_qspi_dt_ids,
+	.driver.owner   = THIS_MODULE,
+	.probe          = fsl_qspi_probe,
+	.remove		= fsl_qspi_remove,
+};
+module_platform_driver(fsl_qspi_driver);
+
+MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
drivers/spi/spi-fsl-quadspi.h new file +5 -0
new file mode 100644
index 0000000..2128128
@@ -0,0 +1,167 @@
+/*
+ * Freescale Quad SPI driver.
+ *
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *

drivers/spi/spi-fsl-quadspi.h line 15

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.h new file +10 -0
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __SPI_FSL_QSPI_H__
+#define __SPI_FSL_QSPI_H__
+
+#define	DRIVER_NAME "fsl-qspi"

I'm not strong on this. But I think it would be better if we have the driver name matches file name. Considering the abbr qspi is already used in the compatible string, we may name all the files and definitions in "qspi" (you already did this expect file names and register names below.) It makes some sense to me since even hardware spec uses this abbr somewhere.

Quad SPI can still be used as the formal name of the block in Kconfig help text, binding doc etc.

drivers/spi/spi-fsl-quadspi.h line 18

Quoted 3 lines
drivers/spi/spi-fsl-quadspi.h new file +3 -0
+
+/* Quad SPI Controller*/
+#define QUADSPI_MCR             (0x00)

No need of parentheses for such constant.

Quoted 113 lines
drivers/spi/spi-fsl-quadspi.h new file +113 -0
+#define QUADSPI_IPCR            (0x08)
+#define QUADSPI_BFGENCR         (0x20)
+#define QUADSPI_SFAR            (0x100)
+#define QUADSPI_SMPR            (0x108)
+#define QUADSPI_RBSR            (0x10c)
+#define QUADSPI_RBCT            (0x110)
+#define QUADSPI_TBSR            (0x150)
+#define QUADSPI_TBDR            (0x154)
+#define QUADSPI_SR              (0x15c)
+#define QUADSPI_FR              (0x160)
+#define QUADSPI_SFA1AD          (0x180)
+#define QUADSPI_SFA2AD          (0x184)
+#define QUADSPI_SFB1AD          (0x188)
+#define QUADSPI_SFB2AD          (0x18c)
+#define QUADSPI_RBDR            (0x200)
+#define QUADSPI_LUTKEY          (0x300)
+#define QUADSPI_LCKCR           (0x304)
+#define QUADSPI_RSER            (0x164)
+#define KEY_VALUE               (0x5AF05AF0)
+#define QUADSPI_LUT(x)          (0x310 + (x) * 4)
+
+#define INT_TFIE                (0x1 << 0)
+#define TPADA                   (0x1 << 24)
+#define TPADB                   (0x1 << 25)
+
+/* Field definitions for LCKCR */
+#define QSPI_LCKCR_LOCK		(0x1)		
+#define QSPI_LCKCR_UNLOCK	(0x2)
+
+/* Field definitions for RBSR */
+#define QSPI_RBSR_RDBFL_SHIFT	(8)
+#define QSPI_RBSR_RDBFL_MASK	(0x3F << QSPI_RBSR_RDBFL_SHIFT)
+
+/* Field definitions for RBCT */
+#define QSPI_RBCT_WMRK_MASK	(0x1F)
+#define QSPI_RBCT_RXBRD_SHIFT	(8)
+#define QSPI_RBCT_RXBRD_USEIPS	(0x1 << QSPI_RBCT_RXBRD_SHIFT)
+
+/* Field definitions for MCR */
+#define QSPI_MCR_MDIS_SHIFT	(14)
+#define QSPI_MCR_MDIS_MASK	(1 << QSPI_MCR_MDIS_SHIFT)
+#define QSPI_MCR_CLR_TXF_SHIFT	(11)
+#define QSPI_MCR_CLR_TXF_MASK	(1 << QSPI_MCR_CLR_TXF_SHIFT)
+#define QSPI_MCR_CLR_RXF_SHIFT	(10)
+#define QSPI_MCR_CLR_RXF_MASK	(1 << QSPI_MCR_CLR_RXF_SHIFT)
+#define QSPI_MCR_DDR_EN_SHIFT	(7)
+#define QSPI_MCR_DDR_EN_MASK	(1 << QSPI_MCR_DDR_EN_SHIFT)
+#define QSPI_MCR_RESERVED_SHIFT	(16)
+#define QSPI_MCR_RESERVED_MASK  (0xF << QSPI_MCR_RESERVED_SHIFT)
+
+/* Field definitions for SMPR */
+#define QSPI_SMPR_DDRSMP_SHIFT		(16)
+#define QSPI_SMPR_DDRSMP_MASK		(7 << QSPI_SMPR_DDRSMP_SHIFT)
+#define QSPI_SMPR_FSDLY_SHIFT		(6)
+#define QSPI_SMPR_FSDLY_MASK		(1 << QSPI_SMPR_FSDLY_SHIFT)
+#define QSPI_SMPR_FSPHS_SHIFT		(5)
+#define QSPI_SMPR_FSPHS_MASK		(1 << QSPI_SMPR_FSPHS_SHIFT)
+#define QSPI_SMPR_HSENA_SHIFT		(0)
+#define QSPI_SMPR_HSENA_MASK		(1 << QSPI_SMPR_HSENA_SHIFT)
+
+/* Field definitions for BFGENCR */
+#define QSPI_BFGENCR_PAR_EN_SHIFT	(16)
+#define QSPI_BFGENCR_PAR_EN_MASK	(1 << (QSPI_BFGENCR_PAR_EN_SHIFT))
+#define QSPI_BFGENCR_SEQID_SHIFT	(12)
+#define QSPI_BFGENCR_SEQID_MASK		(0xF << QSPI_BFGENCR_SEQID_SHIFT)
+
+/* Field definitions for SR */
+#define QSPI_SR_TXFULL_SHIFT		(27)
+#define QSPI_SR_TXFULL_MASK		(1 << QSPI_SR_TXFULL_SHIFT)
+#define QSPI_SR_AHBTRN_SHIFT		(6)
+#define QSPI_SR_AHBTRN_MASK		(1 << QSPI_SR_AHBTRN_SHIFT)
+#define QSPI_SR_AHB_ACC_SHIFT		(2)
+#define QSPI_SR_AHB_ACC_MASK		(1 << QSPI_SR_AHB_ACC_SHIFT)
+#define QSPI_SR_IP_ACC_SHIFT		(1)
+#define QSPI_SR_IP_ACC_MASK		(1 << QSPI_SR_IP_ACC_SHIFT)
+#define QSPI_SR_BUSY_SHIFT		(0)
+#define QSPI_SR_BUSY_MASK		(1 << QSPI_SR_BUSY_SHIFT)
+
+/* Field definitions for FR */
+#define QSPI_FR_TFF_MASK		(0x1)
+
+/* Seqid */
+#define WEN_SEQID			(1)	
+#define READSTATU_SEQID			(3)	
+#define TX_SEQID			(8)
+#define RX_SEQID			(9)
+
+/* Field definitions for IPCR */
+#define QSPI_IPCR_SEQID_SHIFT	(24)
+#define QSPI_IPCR_SEQID_MASK	(0xF << QSPI_IPCR_SEQID_SHIFT)
+#define OPRND0_SHIFT		(0)
+#define OPRND0(x)		((x) << (OPRND0_SHIFT))
+#define PAD0_SHIFT		(8)
+#define PAD0(x)			((x) << (PAD0_SHIFT))
+#define INSTR0_SHIFT		(10)
+#define INSTR0(x)		((x) << (INSTR0_SHIFT))
+#define OPRND1_SHIFT		(16)
+#define OPRND1(x)		((x) << (OPRND1_SHIFT))
+#define PAD1_SHIFT		(24)
+#define PAD1(x)			((x) << (PAD1_SHIFT))
+#define INSTR1_SHIFT		(26)
+#define INSTR1(x)		((x) << (INSTR1_SHIFT))
+
+/* instruction set */
+#define CMD			(1)
+#define ADDR			(2)
+#define ADDR24BIT		(0x18)
+#define DUMMY			(3)
+#define MODE			(4)
+#define MODE2			(5)
+#define MODE4			(6)
+#define READ			(7)
+#define WRITE			(8)

drivers/spi/spi-fsl-quadspi.h line 141

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.h new file +10 -0
+#define JMP_ON_CS		(9)
+#define ADDR_DDR		(10)
+#define MODE_DDR		(11)
+#define MODE2_DDR		(12)
+#define MODE4_DDR		(13)
+
+#define endian_change_32bit(A)	((((u32)(A) & 0xFF000000) >> 24) | \
+		(((u32)(A) & 0x00FF0000) >> 8) | \
+		(((u32)(A) & 0x0000FF00) << 8) | \
+		(((u32)(A) & 0x000000FF) << 24))

The macros defined in include/linux/byteorder/generic.h can help, I think.

Shawn

Quoted 30 lines
drivers/spi/spi-fsl-quadspi.h new file +26 -0
+
+#define PSP_QSPI0_MEMMAP_BASE	(0x20000000)
+#define RX_BUFFER_SIZE		(0x80)
+#define TX_BUFFER_SIZE		(0x40)
+#define POS_LOCATION_MASK	(0xFFFFFF00)
+#define STATUS_BIT		(24)
+#define VALID_STATUS_BIT	(0)
+#define READ_STATUS_TIMEOUT	(5000)
+#define WRITE_ENABLE_BIT	(25)
+#define VALID_WRITE_ENABLE_BIT	(1)
+#define WRITE_ENABLE_TIMEOUT	(10)
+
+struct fsl_qspi {
+	void __iomem *iobase;
+	int irq;
+	struct clk *clk;
+	u32 quad_mode;
+	u32 select_cs;
+	struct device *dev;
+
+	wait_queue_head_t waitq;
+	struct timer_list wait_timer;
+	bool timeout;
+};
+
+#endif
Signature · 1 line
1.8.0
#2 lore raw

PATCH 2/2 spi: Add Freescale QuadSpi driver

In reply to Xiaochun Li
Quoted 5 lines · Xiaochun Li

Add QuadSpi driver for Freescale's Quad Spi controller. This controller is designed for serial flash access.
This driver has been tested on vf610-twr board with m25p80 type chips. The hardware has no support for other types of spi peripherals. Chip select control is handled via Serial Flash Address Register.

This really needs serious rework. If possible find someone in your company who can help you with this driver.

drivers/spi/spi-fsl-quadspi.c line 33

Quoted 4 lines
drivers/spi/spi-fsl-quadspi.c new file +4 -0
+static inline unsigned int fsl_qspi_check_status(struct fsl_qspi *fsl_qspi)
+{
+	return readl(fsl_qspi->iobase + QUADSPI_SR) & QSPI_SR_BUSY_MASK;
+}

What does a function calles check_status return? Does it return "Yes, I'm busy" or does it return "Yes, I'm idle". Use fsl_qspi_is_busy or fsl_qspi_is_idle instead.

Quoted 5 lines
drivers/spi/spi-fsl-quadspi.c new file +5 -0
+static irqreturn_t fsl_qspi_irq_handler(int this_irq, void *dev_id)
+{
+	struct fsl_qspi *fsl_qspi = dev_id;
+	u32 reg_fr = readl(fsl_qspi->iobase + QUADSPI_FR);
+

drivers/spi/spi-fsl-quadspi.c line 60

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+	/* clear interrupt */
+	writel(reg_fr, fsl_qspi->iobase + QUADSPI_FR);
+	if (reg_fr & QSPI_FR_TFF_MASK)
+		wake_up(&fsl_qspi->waitq);
+
+	return IRQ_HANDLED;
+}
+
+static void set_lut(struct fsl_qspi *fsl_qspi)
+{

Consistently add a fsl_qspi_ namespace to all functions in this driver.

Quoted 19 lines
drivers/spi/spi-fsl-quadspi.c new file +19 -0
+static void waiting_for_end(struct fsl_qspi *fsl_qspi,
+		u32 current_bit, u32 bitvalue, u32 timeout)
+{
+	u32 reg, status_reg;
+
+	fsl_qspi->wait_timer.expires = jiffies + timeout;
+	add_timer(&fsl_qspi->wait_timer);
+
+	status_reg = !bitvalue << current_bit;
+	while ((status_reg & (0x1 << current_bit)) != bitvalue << current_bit) {
+		if (fsl_qspi->timeout) {
+			dev_err(fsl_qspi->dev, "tired waiting for end status\n");
+			break;
+		}
+		writel((READSTATU_SEQID << QSPI_IPCR_SEQID_SHIFT) | 1,
+				fsl_qspi->iobase + QUADSPI_IPCR);
+		wait_event(fsl_qspi->waitq, !fsl_qspi_check_status(fsl_qspi));
+
+		reg = readl(fsl_qspi->iobase + QUADSPI_RBSR);

drivers/spi/spi-fsl-quadspi.c line 116

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+		if (reg & QSPI_RBSR_RDBFL_MASK)
+			status_reg = readl(fsl_qspi->iobase +
+					QUADSPI_RBDR);
+
+		writel(readl(fsl_qspi->iobase + QUADSPI_MCR) |
+				QSPI_MCR_CLR_RXF_MASK,
+				fsl_qspi->iobase + QUADSPI_MCR);
+	}
+	del_timer(&fsl_qspi->wait_timer);
+}

What the hell are you doing here??

You have interrupts. Use them! Interrupts are for triggering actions, not for polling until they happen. The driver logic is completely wrong.

Each wait_event possibly deadlocks the driver. Don't do this. Also each wait_event is either unnecessary in this context or means that there is an interrupt. I lost track how many wait_events you have in your execution path, but get rid of them all.

drivers/spi/spi-fsl-quadspi.c line 332

Quoted 12 lines
drivers/spi/spi-fsl-quadspi.c new file +12 -0
+static void fsl_qspi_read_trans(struct spi_master *master,
+		struct spi_message *msg, u32 num)
+{
+	struct fsl_qspi *fsl_qspi = spi_master_get_devdata(master);
+	struct spi_transfer *xfer;
+	u32 tx_len = 0, trans_counter = 0, mcr_reg;
+	u8 opr = 0;
+	loff_t pos = 0;
+	int status = 0;
+
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+

Why do you have two function which both iterate over all transfers of a message?
To clean this up start with a function which handles one transfer. Then add one function which iterates over all transfers and calls the transfer function.

Then, since your hardware doesn't seem to be able to handle spi messages directly you should probably use the bitbanging helpers instead of implementing a master directly.

drivers/spi/spi-fsl-quadspi.c line 346

Quoted 3 lines
drivers/spi/spi-fsl-quadspi.c new file +3 -0
+
+		if (xfer->rx_buf && trans_counter > 0) {
+			set_receive_lut(fsl_qspi, opr, num, xfer->len, tx_len);

set_receive_lut is not at all interested in the number of transfers. What it wants to know is whether this transfer is the last one of a message...

drivers/spi/spi-fsl-quadspi.c line 383

Quoted 11 lines
drivers/spi/spi-fsl-quadspi.c new file +11 -0
+	struct spi_transfer *t;
+	u8 *rx_buf = NULL;
+	unsigned int xfer_num = 0;
+	struct fsl_qspi *fsl_qspi = spi_master_get_devdata(master);
+
+	writel(INT_TFIE, fsl_qspi->iobase + QUADSPI_RSER);
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->rx_buf)
+			rx_buf = t->rx_buf;
+		xfer_num++;

...which means that you no longer have to count the number of transfers here.

drivers/spi/spi-fsl-quadspi.c line 389

Quoted 6 lines
drivers/spi/spi-fsl-quadspi.c new file +6 -0
+	}
+
+	if (!rx_buf)
+		fsl_qspi_write_trans(master, m, xfer_num);
+	else
+		fsl_qspi_read_trans(master, m, xfer_num);

There shouldn't be any need to distinguish between messages with no read transfers and messages with at least one read transfer. Get rid of this.

Quoted 12 lines
drivers/spi/spi-fsl-quadspi.c new file +12 -0
+
+	return m->status;
+}
+
+static int fsl_qspi_setup(struct spi_device *spi)
+{
+	u32 reg_val, smpr_val, seq_id;
+	struct fsl_qspi *fsl_qspi;
+
+	fsl_qspi = spi_master_get_devdata(spi->master);
+
+	if ((spi->bits_per_word < 8) || (spi->bits_per_word > 16)) {

drivers/spi/spi-fsl-quadspi.c line 411

Quoted 10 lines
drivers/spi/spi-fsl-quadspi.c new file +10 -0
+		dev_dbg(&spi->dev, "%d bits per word is not supported\n",
+				spi->bits_per_word);
+		return -EINVAL;
+	}
+
+	if (spi->chip_select >= spi->master->num_chipselect) {
+		dev_dbg(&spi->dev, "%d chip select is out of range\n",
+				spi->chip_select);
+		return -EINVAL;
+	}

This will never happen. The spi core checks this in spi_add_device.

Sascha

Signature · 4 lines
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 |