summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlison Wang <b18965@freescale.com>2012-09-28 13:21:42 +0800
committerAndy Voltz <andy.voltz@timesys.com>2012-10-17 14:37:24 -0400
commitb2fcdda7a78d05ac0b75d52ffc991f1c62d87178 (patch)
tree1eb943269e892818680fb32529481e3969b317c2
parentb7354b3953a041d7b0a09f9fbb1d99448c01b3c5 (diff)
ENGR00181390-2: qspi: Add Quad SPI driver support for Vybrid
Add Quad SPI driver support for Vybrid. Signed-off-by: Alison Wang <b18965@freescale.com> Xiaochun Li <b41219@freescale.com>
-rw-r--r--drivers/mtd/devices/m25p80.c1
-rw-r--r--drivers/spi/Kconfig10
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi_mvf_qspi.c867
4 files changed, 879 insertions, 0 deletions
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 35180e475c4c..2e416b6ec0d0 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -698,6 +698,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SECT_4K) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
+ { "s25fl128s", INFO(0x012018, 0x4d00, 64 * 1024, 256, 0) },
{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
{ "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, 0) },
{ "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, 0) },
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 78e8e1d53a68..cfc6cd5945e6 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -133,6 +133,16 @@ config SPI_COLDFIRE_QSPI
This driver can also be built as a module. If so, the module
will be called coldfire_qspi.
+config SPI_MVF_QSPI
+ tristate "Freescale Faraday QSPI controller"
+ depends on ARCH_MVF
+ help
+ This enables support for the Faraday QSPI controller in master
+ mode.
+
+ This driver can also be built as a module. If so, the module
+ will be called spi_mvf_qspi.
+
config SPI_DAVINCI
tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller"
depends on SPI_MASTER && ARCH_DAVINCI
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index a798d0bca956..31292e36ca1b 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += coldfire_qspi.o
+obj-$(CONFIG_SPI_MVF_QSPI) += spi_mvf_qspi.o
obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o
obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o
obj-$(CONFIG_SPI_DW_PCI) += dw_spi_midpci.o
diff --git a/drivers/spi/spi_mvf_qspi.c b/drivers/spi/spi_mvf_qspi.c
new file mode 100644
index 000000000000..a15425827a27
--- /dev/null
+++ b/drivers/spi/spi_mvf_qspi.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * Freescale Faraday Quad SPI driver
+ *
+ * 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/workqueue.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <mach/spi-mvf.h>
+
+#define DRIVER_NAME "mvf-qspi"
+
+/* Flash opcodes. */
+#define OPCODE_WREN 0x06 /* Write enable */
+#define OPCODE_RDSR 0x05 /* Read status register */
+#define OPCODE_WRSR 0x01 /* Write status register 1 byte */
+#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
+#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
+#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
+#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
+#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
+#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */
+#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
+#define OPCODE_RDID 0x9f /* Read JEDEC ID */
+
+/* Used for Spansion flashes only. */
+#define OPCODE_BRWR 0x17 /* Bank register write */
+
+#define endian_change_32bit(A) ((((unsigned int)(A) & 0xff000000) >> 24) | \
+ (((unsigned int)(A) & 0x00ff0000) >> 8) | \
+ (((unsigned int)(A) & 0x0000ff00) << 8) | \
+ (((unsigned int)(A) & 0x000000ff) << 24))
+
+#define PSP_QSPI0_MEMMAP_BASE (0x20000000)
+#define PSP_QSPI1_MEMMAP_BASE (0x50000000)
+#define RX_BUFFER_SIZE (32 << 2)
+#define TX_BUFFER_SIZE (16 << 2)
+
+/* Field definitions for RBSR */
+#define QSPI_RBSR_RDCTR_SHIFT (16)
+#define QSPI_RBSR_RDCTR_MASK ((0x0000FFFF) << (QSPI_RBSR_RDCTR_SHIFT))
+
+#define QSPI_RBSR_RDBFL_SHIFT (8)
+#define QSPI_RBSR_RDBFL_MASK ((0x0000003F) << (QSPI_RBSR_RDBFL_SHIFT))
+
+/* Field definitions for IPCR */
+#define QSPI_IPCR_SEQID_SHIFT (24)
+#define QSPI_IPCR_SEQID_MASK ((0x0000000F) << (QSPI_IPCR_SEQID_SHIFT))
+
+#define QSPI_IPCR_PAR_EN_SHIFT (16)
+#define QSPI_IPCR_PAR_EN_MASK ((1) << (QSPI_IPCR_PAR_EN_SHIFT))
+
+#define QSPI_IPCR_IDATSZ_SHIFT (0)
+#define QSPI_IPCR_IDATSZ_MASK ((0x0000FFFF) << (QSPI_IPCR_IDATSZ_SHIFT))
+
+/* Field definitions for MCR */
+#define QSPI_MCR_DOZE_SHIFT (15)
+#define QSPI_MCR_DOZE_MASK ((1) << (QSPI_MCR_DOZE_SHIFT))
+
+#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_DQS_EN_SHIFT (6)
+#define QSPI_MCR_DQS_EN_MASK ((1) << (QSPI_MCR_DQS_EN_SHIFT))
+
+#define QSPI_MCR_SWRSTHD_SHIFT (1)
+#define QSPI_MCR_SWRSTHD_MASK ((1) << (QSPI_MCR_SWRSTHD_SHIFT))
+
+#define QSPI_MCR_SWRSTSD_SHIFT (0)
+#define QSPI_MCR_SWRSTSD_MASK ((1) << (QSPI_MCR_SWRSTSD_SHIFT))
+
+/* Field definitions for SMPR */
+#define QSPI_SMPR_DDRSMP_SHIFT (16)
+#define QSPI_SMPR_DDRSMP_MASK ((0x00000007) << \
+ (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_HSDLY_SHIFT (2)
+#define QSPI_SMPR_HSDLY_MASK ((1) << (QSPI_SMPR_HSDLY_SHIFT))
+
+#define QSPI_SMPR_HSPHS_SHIFT (1)
+#define QSPI_SMPR_HSPHS_MASK ((1) << (QSPI_SMPR_HSPHS_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_DLPSMP_SHIFT (29)
+#define QSPI_SR_DLPSMP_MASK ((0x7) << (QSPI_SR_DLPSMP_SHIFT))
+
+#define QSPI_SR_TXFULL_SHIFT (27)
+#define QSPI_SR_TXFULL_MASK ((1) << (QSPI_SR_TXFULL_SHIFT))
+
+#define QSPI_SR_TXNE_SHIFT (24)
+#define QSPI_SR_TXNE_MASK ((1) << (QSPI_SR_TXNE_SHIFT))
+
+#define QSPI_SR_RXDMA_SHIFT (23)
+#define QSPI_SR_RXDMA_MASK ((1) << (QSPI_SR_RXDMA_SHIFT))
+
+#define QSPI_SR_RXFULL_SHIFT (19)
+#define QSPI_SR_RXFULL_MASK ((1) << (QSPI_SR_RXFULL_SHIFT))
+
+#define QSPI_SR_RXWE_SHIFT (16)
+#define QSPI_SR_RXWE_MASK ((1) << (QSPI_SR_RXWE_SHIFT))
+
+#define QSPI_SR_AHB3FULL_SHIFT (14)
+#define QSPI_SR_AHB3FULL_MASK ((1) << (QSPI_SR_AHB3FULL_SHIFT))
+
+#define QSPI_SR_AHB2FULL_SHIFT (13)
+#define QSPI_SR_AHB2FULL_MASK ((1) << (QSPI_SR_AHB2FULL_SHIFT))
+
+#define QSPI_SR_AHB1FULL_SHIFT (12)
+#define QSPI_SR_AHB1FULL_MASK ((1) << (QSPI_SR_AHB1FULL_SHIFT))
+
+#define QSPI_SR_AHB0FULL_SHIFT (11)
+#define QSPI_SR_AHB0FULL_MASK ((1) << (QSPI_SR_AHB0FULL_SHIFT))
+
+#define QSPI_SR_AHB3NE_SHIFT (10)
+#define QSPI_SR_AHB3NE_MASK ((1) << (QSPI_SR_AHB3NE_SHIFT))
+
+#define QSPI_SR_AHB2NE_SHIFT (9)
+#define QSPI_SR_AHB2NE_MASK ((1) << (QSPI_SR_AHB2NE_SHIFT))
+
+#define QSPI_SR_AHB1NE_SHIFT (8)
+#define QSPI_SR_AHB1NE_MASK ((1) << (QSPI_SR_AHB1NE_SHIFT))
+
+#define QSPI_SR_AHB0NE_SHIFT (7)
+#define QSPI_SR_AHB0NE_MASK ((1) << (QSPI_SR_AHB0NE_SHIFT))
+
+#define QSPI_SR_AHBTRN_SHIFT (6)
+#define QSPI_SR_AHBTRN_MASK ((1) << (QSPI_SR_AHBTRN_SHIFT))
+
+#define QSPI_SR_AHBGNT_SHIFT (5)
+#define QSPI_SR_AHBGNT_MASK ((1) << (QSPI_SR_AHBGNT_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))
+
+struct mvfqspi {
+ void __iomem *iobase;
+ int irq;
+ struct clk *clk;
+
+ wait_queue_head_t waitq;
+
+ struct work_struct work;
+ struct workqueue_struct *workq;
+ spinlock_t lock;
+ struct list_head msgq;
+};
+
+static void set_lut(struct mvfqspi *mvfqspi)
+{
+ /* Unlock the LUT */
+ writel(0x5af05af0, mvfqspi->iobase + QUADSPI_LUTKEY);
+ writel(0x2, mvfqspi->iobase + QUADSPI_LCKCR);
+ do {} while (!(readl(mvfqspi->iobase + QUADSPI_LCKCR) >> 1));
+
+ /* program required commands */
+ /* SEQID 0 - leave as single read default */
+#ifdef QUADSPI_ENABLE_32BITS_ADDR
+ writel(0x08200413, mvfqspi->iobase + QUADSPI_LUT(0));
+#else
+ writel(0x08180403, mvfqspi->iobase + QUADSPI_LUT(0));
+#endif
+ writel(0x1c80, mvfqspi->iobase + QUADSPI_LUT(1));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(2));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(3));
+
+ /* SEQID 1 - Write enable */
+ writel(0x406, mvfqspi->iobase + QUADSPI_LUT(4));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(5));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(6));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(7));
+
+ /* SEQID 2 - Bulk Erase */
+ writel(0x460, mvfqspi->iobase + QUADSPI_LUT(8));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(9));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(10));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(11));
+
+ /* SEQID 3 - Read Status */
+ writel(0x1c010405, mvfqspi->iobase + QUADSPI_LUT(12));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(13));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(14));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(15));
+
+ /* SEQID 4 - Page Program */
+#ifdef QUADSPI_ENABLE_32BITS_ADDR
+ writel(0x08200412, mvfqspi->iobase + QUADSPI_LUT(16));
+#else
+ writel(0x08180402, mvfqspi->iobase + QUADSPI_LUT(16));
+#endif
+ writel(0x2040, mvfqspi->iobase + QUADSPI_LUT(17));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(18));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(19));
+
+ /* SEQID 5 - Write Config/Status */
+ writel(0x20020401, mvfqspi->iobase + QUADSPI_LUT(20));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(21));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(22));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(23));
+
+ /* SEQID 6 - Read Config */
+ writel(0x1c010435, mvfqspi->iobase + QUADSPI_LUT(24));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(25));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(26));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(27));
+
+
+ /* SEQID 7 - Sector Erase */
+#ifdef QUADSPI_ENABLE_32BITS_ADDR
+ writel(0x082004dc, mvfqspi->iobase + QUADSPI_LUT(28));
+#else
+ writel(0x081804d8, mvfqspi->iobase + QUADSPI_LUT(28));
+#endif
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(29));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(30));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(31));
+
+
+ /* SEQID 8 - Read ID */
+ writel(0x1c04049f, mvfqspi->iobase + QUADSPI_LUT(32));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(33));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(34));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(35));
+
+ /* SEQID 9 - Read ID */
+ writel(0x08180490, mvfqspi->iobase + QUADSPI_LUT(36));
+ writel(0x00001c02, mvfqspi->iobase + QUADSPI_LUT(37));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(38));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(39));
+
+ /* SEQID10 - DDR SINGLE I/O READ */
+#ifdef QUADSPI_ENABLE_32BITS_ADDR
+ writel(0x2820040e, mvfqspi->iobase + QUADSPI_LUT(40));
+#else
+ writel(0x2818040d, mvfqspi->iobase + QUADSPI_LUT(40));
+#endif
+ writel(0x0c022cff, mvfqspi->iobase + QUADSPI_LUT(41));
+ writel(0x3880, mvfqspi->iobase + QUADSPI_LUT(42));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(43));
+
+ /* SEQID 11 - SDR QUAD I/O read */
+#ifdef QUADSPI_ENABLE_32BITS_ADDR
+ writel(0x0a2004ec, mvfqspi->iobase + QUADSPI_LUT(44));
+#else
+ writel(0x0a1804eb, mvfqspi->iobase + QUADSPI_LUT(44));
+#endif
+ writel(0x0e0412ff, mvfqspi->iobase + QUADSPI_LUT(45));
+ writel(0x1e80, mvfqspi->iobase + QUADSPI_LUT(46));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(47));
+
+ /* SEQID 12 - DDR QUAD I/O read */
+#ifdef QUADSPI_ENABLE_32BITS_ADDR
+ writel(0x2a2004ee, mvfqspi->iobase + QUADSPI_LUT(48));
+#else
+ writel(0x2a1804ed, mvfqspi->iobase + QUADSPI_LUT(48));
+#endif
+ writel(0x0c062eff, mvfqspi->iobase + QUADSPI_LUT(49));
+ writel(0x3a80, mvfqspi->iobase + QUADSPI_LUT(50));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(51));
+
+ /* SEQID 13 - QUAD Page Program */
+#ifdef QUADSPI_ENABLE_32BITS_ADDR
+ writel(0x08200434, mvfqspi->iobase + QUADSPI_LUT(52));
+#else
+ writel(0x08180432, mvfqspi->iobase + QUADSPI_LUT(52));
+#endif
+ writel(0x2240, mvfqspi->iobase + QUADSPI_LUT(53));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(54));
+ writel(0x0, mvfqspi->iobase + QUADSPI_LUT(55));
+
+ /* Lock the LUT */
+ writel(0x5af05af0, mvfqspi->iobase + QUADSPI_LUTKEY);
+ writel(0x1, mvfqspi->iobase + QUADSPI_LCKCR);
+ do {} while (!(readl(mvfqspi->iobase + QUADSPI_LCKCR) & 0x1));
+}
+
+static void mvfqspi_transfer_msg(unsigned int opr, unsigned int to_or_from,
+ struct mvfqspi *mvfqspi, unsigned count, const u32 *txbuf, u32 *rxbuf)
+{
+ unsigned int reg, status_reg, tmp, mcr_reg;
+ unsigned int i, j, size, tx_size;
+
+ mcr_reg = readl(mvfqspi->iobase + QUADSPI_MCR);
+ writel(QSPI_MCR_CLR_RXF_MASK | QSPI_MCR_CLR_TXF_MASK | 0xf0000,
+ mvfqspi->iobase + QUADSPI_MCR);
+ writel(0x100, mvfqspi->iobase + QUADSPI_RBCT);
+
+ switch (opr) {
+ case OPCODE_PP:
+ to_or_from += PSP_QSPI0_MEMMAP_BASE;
+
+ while (count > 0) {
+ writel(to_or_from, mvfqspi->iobase + QUADSPI_SFAR);
+
+ status_reg = 0;
+ while ((status_reg & 0x02000000) != 0x02000000) {
+ writel((1 << QSPI_IPCR_SEQID_SHIFT) | 0,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase +
+ QUADSPI_SR) & QSPI_SR_BUSY_MASK);
+
+ writel((0x3 << QSPI_IPCR_SEQID_SHIFT) | 1,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase +
+ QUADSPI_SR) & QSPI_SR_BUSY_MASK);
+
+ reg = readl(mvfqspi->iobase + QUADSPI_RBSR);
+ if (reg & QSPI_RBSR_RDBFL_MASK)
+ status_reg =
+ readl(mvfqspi->iobase + QUADSPI_RBDR);
+ writel(readl(mvfqspi->iobase + QUADSPI_MCR) |
+ QSPI_MCR_CLR_RXF_MASK,
+ mvfqspi->iobase + QUADSPI_MCR);
+ }
+
+ writel(to_or_from, mvfqspi->iobase + QUADSPI_SFAR);
+ tx_size = (count > TX_BUFFER_SIZE) ?
+ TX_BUFFER_SIZE : count;
+
+ to_or_from += 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, mvfqspi->iobase + QUADSPI_TBDR);
+ txbuf++;
+ }
+
+ writel(4 << QSPI_IPCR_SEQID_SHIFT | tx_size,
+ mvfqspi->iobase + QUADSPI_IPCR);
+
+ for (j = i; j < size; j++) {
+ do {} while (readl(mvfqspi->iobase +
+ QUADSPI_SR) & QSPI_SR_TXFULL_MASK);
+ tmp = endian_change_32bit(*txbuf);
+ writel(tmp, mvfqspi->iobase + QUADSPI_TBDR);
+ txbuf++;
+ }
+
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ status_reg = 0x01000000;
+ while ((status_reg & 0x01000000) == 0x01000000) {
+ writel((0x3 << QSPI_IPCR_SEQID_SHIFT) | 1,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase +
+ QUADSPI_SR) & QSPI_SR_BUSY_MASK);
+
+ reg = readl(mvfqspi->iobase + QUADSPI_RBSR);
+ if (reg & QSPI_RBSR_RDBFL_MASK)
+ status_reg =
+ readl(mvfqspi->iobase + QUADSPI_RBDR);
+ writel(readl(mvfqspi->iobase + QUADSPI_MCR) |
+ QSPI_MCR_CLR_RXF_MASK,
+ mvfqspi->iobase + QUADSPI_MCR);
+ }
+ }
+ break;
+ case OPCODE_NORM_READ:
+ to_or_from += PSP_QSPI0_MEMMAP_BASE;
+
+ while (count > 0) {
+ writel(to_or_from, mvfqspi->iobase + QUADSPI_SFAR);
+ size = (count > RX_BUFFER_SIZE) ?
+ RX_BUFFER_SIZE : count;
+ writel(0 << QSPI_IPCR_SEQID_SHIFT | size,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ to_or_from += size;
+ count -= size;
+
+ i = 0;
+ while ((RX_BUFFER_SIZE >= size) && (size > 0)) {
+ tmp = readl(mvfqspi->iobase + QUADSPI_RBDR +
+ i * 4);
+ *rxbuf = endian_change_32bit(tmp);
+ rxbuf++;
+ size -= 4;
+ i++;
+ }
+ writel(readl(mvfqspi->iobase + QUADSPI_MCR) |
+ QSPI_MCR_CLR_RXF_MASK,
+ mvfqspi->iobase + QUADSPI_MCR);
+ }
+ break;
+ case OPCODE_SE:
+ to_or_from += PSP_QSPI0_MEMMAP_BASE;
+ writel(to_or_from, mvfqspi->iobase + QUADSPI_SFAR);
+
+ writel((7 << QSPI_IPCR_SEQID_SHIFT) | 0,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ status_reg = 0x01000000;
+ while ((status_reg & 0x01000000) == 0x01000000) {
+ writel((0x3 << QSPI_IPCR_SEQID_SHIFT) | 1,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ reg = readl(mvfqspi->iobase + QUADSPI_RBSR);
+ if (reg & QSPI_RBSR_RDBFL_MASK)
+ status_reg = readl(mvfqspi->iobase +
+ QUADSPI_RBDR);
+ writel(readl(mvfqspi->iobase + QUADSPI_MCR) |
+ QSPI_MCR_CLR_RXF_MASK,
+ mvfqspi->iobase + QUADSPI_MCR);
+ }
+ break;
+ case OPCODE_CHIP_ERASE:
+ writel(PSP_QSPI0_MEMMAP_BASE, mvfqspi->iobase + QUADSPI_SFAR);
+
+ writel((0x2 << QSPI_IPCR_SEQID_SHIFT) | 0,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ status_reg = 0x01000000;
+ while ((status_reg & 0x01000000) == 0x01000000) {
+ writel((0x3 << QSPI_IPCR_SEQID_SHIFT) | 1,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ reg = readl(mvfqspi->iobase + QUADSPI_RBSR);
+ if (reg & QSPI_RBSR_RDBFL_MASK)
+ status_reg = readl(mvfqspi->iobase +
+ QUADSPI_RBDR);
+ writel(readl(mvfqspi->iobase + QUADSPI_MCR) |
+ QSPI_MCR_CLR_RXF_MASK,
+ mvfqspi->iobase + QUADSPI_MCR);
+ }
+ break;
+ case OPCODE_WREN:
+ to_or_from += PSP_QSPI0_MEMMAP_BASE;
+ writel(to_or_from, mvfqspi->iobase + QUADSPI_SFAR);
+
+ writel((0x1 << QSPI_IPCR_SEQID_SHIFT) | 0,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+ break;
+ case OPCODE_RDSR:
+ writel(PSP_QSPI0_MEMMAP_BASE, mvfqspi->iobase + QUADSPI_SFAR);
+
+ writel(0x3 << QSPI_IPCR_SEQID_SHIFT | 1,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ reg = readl(mvfqspi->iobase + QUADSPI_RBSR);
+ if (reg & QSPI_RBSR_RDBFL_MASK)
+ *rxbuf = readl(mvfqspi->iobase + QUADSPI_RBDR);
+
+ writel(readl(mvfqspi->iobase + QUADSPI_MCR) |
+ QSPI_MCR_CLR_RXF_MASK, mvfqspi->iobase + QUADSPI_MCR);
+ break;
+ case OPCODE_RDID:
+ writel(PSP_QSPI0_MEMMAP_BASE, mvfqspi->iobase + QUADSPI_SFAR);
+
+ writel((0x8 << QSPI_IPCR_SEQID_SHIFT) | count,
+ mvfqspi->iobase + QUADSPI_IPCR);
+ do {} while (readl(mvfqspi->iobase + QUADSPI_SR) &
+ QSPI_SR_BUSY_MASK);
+
+ reg = readl(mvfqspi->iobase + QUADSPI_RBSR);
+ if (reg & QSPI_RBSR_RDBFL_MASK) {
+ tmp = readl(mvfqspi->iobase + QUADSPI_RBDR);
+ *rxbuf = endian_change_32bit(tmp);
+ rxbuf += 4;
+ count -= 4;
+ }
+ break;
+ default:
+ printk(KERN_ERR "cannot support this opcode\n");
+ break;
+ }
+
+ writel(mcr_reg, mvfqspi->iobase + QUADSPI_MCR);
+}
+
+
+static void mvfqspi_work(struct work_struct *work)
+{
+ struct mvfqspi *mvfqspi = container_of(work, struct mvfqspi, work);
+ unsigned long flags;
+
+ spin_lock_irqsave(&mvfqspi->lock, flags);
+ while (!list_empty(&mvfqspi->msgq)) {
+ struct spi_message *msg;
+ struct spi_device *spi;
+ struct spi_transfer *xfer;
+ int status = 0;
+ unsigned int opr = 0, to_or_from = 0;
+
+ msg = container_of(mvfqspi->msgq.next,
+ struct spi_message, queue);
+
+ list_del_init(&msg->queue);
+ spin_unlock_irqrestore(&mvfqspi->lock, flags);
+
+ spi = msg->spi;
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (xfer->tx_buf && opr == 0) {
+ unsigned char temp_tx_buf =
+ *(unsigned char *)xfer->tx_buf;
+ unsigned int tx_buf =
+ *(unsigned int *)xfer->tx_buf;
+
+ switch (temp_tx_buf) {
+ case OPCODE_NORM_READ:
+ opr = OPCODE_NORM_READ;
+ to_or_from = tx_buf & 0xffffff00;
+ msg->actual_length += xfer->len;
+ continue;
+ case OPCODE_PP:
+ opr = OPCODE_PP;
+ to_or_from = tx_buf & 0xffffff00;
+ msg->actual_length += xfer->len;
+ continue;
+ case OPCODE_RDID:
+ opr = OPCODE_RDID;
+ msg->actual_length += xfer->len;
+ continue;
+ case OPCODE_SE:
+ opr = OPCODE_SE;
+ to_or_from = tx_buf & 0xffffff00;
+ break;
+ case OPCODE_WREN:
+ opr = OPCODE_WREN;
+ to_or_from = tx_buf & 0xffffff00;
+ break;
+ case OPCODE_RDSR:
+ opr = OPCODE_RDSR;
+ msg->actual_length += xfer->len;
+ continue;
+ case OPCODE_CHIP_ERASE:
+ opr = OPCODE_CHIP_ERASE;
+ msg->actual_length += xfer->len;
+ break;
+ default:
+ printk(KERN_ERR "cannot support"
+ " this opcode\n");
+ break;
+ }
+ }
+
+ if (opr != 0) {
+ mvfqspi_transfer_msg(opr,
+ endian_change_32bit(to_or_from),
+ mvfqspi, xfer->len,
+ xfer->tx_buf,
+ xfer->rx_buf);
+ opr = 0;
+ }
+
+ if (xfer->delay_usecs)
+ udelay(xfer->delay_usecs);
+ msg->actual_length += xfer->len;
+ }
+ msg->status = status;
+ msg->complete(msg->context);
+
+ spin_lock_irqsave(&mvfqspi->lock, flags);
+ }
+ spin_unlock_irqrestore(&mvfqspi->lock, flags);
+}
+
+static int mvfqspi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct mvfqspi *mvfqspi;
+ struct spi_transfer *xfer;
+ unsigned long flags;
+ mvfqspi = spi_master_get_devdata(spi->master);
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if (xfer->bits_per_word && ((xfer->bits_per_word < 8)
+ || (xfer->bits_per_word > 16))) {
+ dev_dbg(&spi->dev, "%d bits per word is not "
+ "supported\n", xfer->bits_per_word);
+ goto fail;
+ }
+ }
+ msg->status = -EINPROGRESS;
+ msg->actual_length = 0;
+
+ spin_lock_irqsave(&mvfqspi->lock, flags);
+ list_add_tail(&msg->queue, &mvfqspi->msgq);
+ queue_work(mvfqspi->workq, &mvfqspi->work);
+ spin_unlock_irqrestore(&mvfqspi->lock, flags);
+
+ return 0;
+fail:
+ msg->status = -EINVAL;
+ return -EINVAL;
+}
+
+static int mvfqspi_setup(struct spi_device *spi)
+{
+ unsigned int reg_val, smpr_val, seq_id;
+ struct mvfqspi *mvfqspi;
+
+ mvfqspi = 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(0xf0000 | QSPI_MCR_MDIS_MASK, mvfqspi->iobase + QUADSPI_MCR);
+
+ reg_val = readl(mvfqspi->iobase + QUADSPI_SMPR);
+ writel(reg_val & ~(QSPI_SMPR_FSDLY_MASK | QSPI_SMPR_FSPHS_MASK |
+ QSPI_SMPR_HSENA_MASK), mvfqspi->iobase + QUADSPI_SMPR);
+ writel(0xf0000, mvfqspi->iobase + QUADSPI_MCR);
+
+ writel(0x1 << 24 | PSP_QSPI0_MEMMAP_BASE,
+ mvfqspi->iobase + QUADSPI_SFA1AD);
+ writel(0x1 << 24 | PSP_QSPI0_MEMMAP_BASE,
+ mvfqspi->iobase + QUADSPI_SFA2AD);
+ writel(0x1 << 25 | PSP_QSPI0_MEMMAP_BASE,
+ mvfqspi->iobase + QUADSPI_SFB1AD);
+ writel(0x1 << 25 | PSP_QSPI0_MEMMAP_BASE,
+ mvfqspi->iobase + QUADSPI_SFB2AD);
+
+ set_lut(mvfqspi);
+
+ reg_val = 0;
+ reg_val |= 0xf0000;
+ smpr_val = readl(mvfqspi->iobase + QUADSPI_SMPR);
+ smpr_val &= ~QSPI_SMPR_DDRSMP_MASK;
+ writel(smpr_val, mvfqspi->iobase + QUADSPI_SMPR);
+ reg_val &= ~QSPI_MCR_DDR_EN_MASK;
+ writel(reg_val, mvfqspi->iobase + QUADSPI_MCR);
+
+ seq_id = 0;
+ reg_val = readl(mvfqspi->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, mvfqspi->iobase + QUADSPI_BFGENCR);
+
+ return 0;
+}
+
+static int __devinit mvfqspi_probe(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct mvfqspi *mvfqspi;
+ struct resource *res;
+ struct spi_mvf_master *pdata;
+ int status = 0;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*mvfqspi));
+ if (master == NULL) {
+ dev_dbg(&pdev->dev, "spi_alloc_master failed\n");
+ return -ENOMEM;
+ }
+
+ mvfqspi = 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;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ status = -EBUSY;
+ goto fail0;
+ }
+
+ mvfqspi->iobase = ioremap(res->start, resource_size(res));
+ if (!mvfqspi->iobase) {
+ dev_dbg(&pdev->dev, "ioremap failed\n");
+ status = -ENOMEM;
+ goto fail1;
+ }
+
+ mvfqspi->clk = clk_get(&pdev->dev, "mvf-qspi.0");
+ if (IS_ERR(mvfqspi->clk)) {
+ dev_dbg(&pdev->dev, "clk_get failed\n");
+ status = PTR_ERR(mvfqspi->clk);
+ goto fail3;
+ }
+ clk_enable(mvfqspi->clk);
+
+ mvfqspi->workq =
+ create_singlethread_workqueue(dev_name(master->dev.parent));
+ if (!mvfqspi->workq) {
+ dev_dbg(&pdev->dev, "create_workqueue failed\n");
+ status = -ENOMEM;
+ goto fail4;
+ }
+
+ INIT_WORK(&mvfqspi->work, mvfqspi_work);
+ spin_lock_init(&mvfqspi->lock);
+ INIT_LIST_HEAD(&mvfqspi->msgq);
+ init_waitqueue_head(&mvfqspi->waitq);
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_dbg(&pdev->dev, "platform data is missing\n");
+ goto fail5;
+ }
+ master->bus_num = pdata->bus_num;
+ master->num_chipselect = pdata->num_chipselect;
+
+ master->setup = mvfqspi_setup;
+ master->transfer = mvfqspi_transfer;
+
+ platform_set_drvdata(pdev, master);
+
+ status = spi_register_master(master);
+ if (status) {
+ dev_dbg(&pdev->dev, "spi_register_master failed\n");
+ goto fail6;
+ }
+ dev_info(&pdev->dev, "QSPI bus driver\n");
+
+ return 0;
+
+fail6:
+fail5:
+ destroy_workqueue(mvfqspi->workq);
+fail4:
+ clk_disable(mvfqspi->clk);
+ clk_put(mvfqspi->clk);
+fail3:
+ free_irq(mvfqspi->irq, mvfqspi);
+fail1:
+ release_mem_region(res->start, resource_size(res));
+fail0:
+ spi_master_put(master);
+ dev_dbg(&pdev->dev, "QSPI probe failed\n");
+ return status;
+}
+
+static int mvfqspi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct mvfqspi *mvfqspi = spi_master_get_devdata(master);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* disable the hardware*/
+ writel(0x0, mvfqspi->iobase + QUADSPI_MCR);
+
+ platform_set_drvdata(pdev, NULL);
+ destroy_workqueue(mvfqspi->workq);
+ clk_disable(mvfqspi->clk);
+ clk_put(mvfqspi->clk);
+ free_irq(mvfqspi->irq, mvfqspi);
+ iounmap(mvfqspi->iobase);
+ release_mem_region(res->start, resource_size(res));
+ spi_unregister_master(master);
+ spi_master_put(master);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mvfqspi_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct mvfqspi *mvfqspi = platform_get_drvdata(pdev);
+
+ clk_disable(mvfqspi->clk);
+ return 0;
+}
+
+static int mvfqspi_resume(struct platform_device *pdev)
+{
+ struct mvfqspi *mvfqspi = platform_get_drvdata(pdev);
+
+ clk_enable(mvfqspi->clk);
+ return 0;
+}
+#else
+#define mvfqspi_suspend NULL
+#define mvfqspi_resume NULL
+#endif
+
+static struct platform_driver mvfqspi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .remove = mvfqspi_remove,
+ .suspend = mvfqspi_suspend,
+ .resume = mvfqspi_resume,
+};
+
+static int __init mvfqspi_init(void)
+{
+ return platform_driver_probe(&mvfqspi_driver, mvfqspi_probe);
+}
+module_init(mvfqspi_init);
+
+static void __exit mvfqspi_exit(void)
+{
+ platform_driver_unregister(&mvfqspi_driver);
+}
+module_exit(mvfqspi_exit);
+
+MODULE_DESCRIPTION("QSPI Controller Driver");
+MODULE_LICENSE("GPL");