[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