[PATCH 2/2] spi: Add Freescale QuadSpi driver

Xiaochun Li b41219 at freescale.com
Fri Jun 21 06:13:10 EDT 2013


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.

Signed-off-by: Alison Wang <b18965 at freescale.com>
Signed-off-by: Xiaochun Li <b41219 at freescale.com>
---
 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

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 2015897..3c8b72c 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -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
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 33f9c09..c56aa2e 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -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
diff --git a/drivers/spi/spi-fsl-quadspi.c b/drivers/spi/spi-fsl-quadspi.c
new file mode 100644
index 0000000..ceb46ab
--- /dev/null
+++ b/drivers/spi/spi-fsl-quadspi.c
@@ -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>
+#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"
+
+static inline unsigned int fsl_qspi_check_status(struct fsl_qspi *fsl_qspi)
+{
+	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;
+}
+
+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 */
+	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;
+	/* 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);
+	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);
+
+	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);
+		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;
+}
+
+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",
+				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;
+	}
+
+	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;
+	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;
+	}
+
+	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;
+	}
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*fsl_qspi));
+	if (master == NULL) {
+		dev_dbg(&pdev->dev, "spi_alloc_master failed\n");
+		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;
+	}
+
+	fsl_qspi->iobase = devm_ioremap_resource(&pdev->dev, res);
+	if (!fsl_qspi->iobase) {
+		dev_dbg(&pdev->dev, "ioremap failed\n");
+		status = -ENOMEM;
+		goto fail0;
+	}
+
+	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);
+		goto fail0;
+	}
+	clk_prepare_enable(gate);
+
+	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);
+	if (fsl_qspi->irq < 0) {
+		dev_dbg(&pdev->dev, "platform_get_irq failed\n");
+		status = -ENXIO;
+		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;
+
+	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");
+
+	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");
+
+	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);
+	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, },
+	{ /* 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);
diff --git a/drivers/spi/spi-fsl-quadspi.h b/drivers/spi/spi-fsl-quadspi.h
new file mode 100644
index 0000000..2128128
--- /dev/null
+++ b/drivers/spi/spi-fsl-quadspi.h
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#ifndef __SPI_FSL_QSPI_H__
+#define __SPI_FSL_QSPI_H__
+
+#define	DRIVER_NAME "fsl-qspi"
+
+/* Quad SPI Controller*/
+#define QUADSPI_MCR             (0x00)
+#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)
+#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))
+
+#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
-- 
1.8.0





More information about the linux-arm-kernel mailing list