From mboxrd@z Thu Jan 1 00:00:00 1970 From: Sourav Poddar Subject: Re: [PATCH v2 5/8] spi: Add Freescale QuadSpi driver Date: Mon, 26 Aug 2013 11:40:42 +0530 Message-ID: <521AF162.9000308@ti.com> References: <1377492102-23543-1-git-send-email-b32955@freescale.com> <1377492102-23543-6-git-send-email-b32955@freescale.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii"; Format="flowed" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1377492102-23543-6-git-send-email-b32955@freescale.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=m.gmane.org@lists.infradead.org To: Huang Shijie Cc: B20596@freescale.com, shawn.guo@linaro.org, b44548@freescale.com, linux-doc@vger.kernel.org, dedekind1@gmail.com, b18965@freescale.com, linux-spi@vger.kernel.org, devicetree@vger.kernel.org, broonie@kernel.org, linux-mtd@lists.infradead.org, kernel@pengutronix.de, computersforpeace@gmail.com, dwmw2@infradead.org, linux-arm-kernel@lists.infradead.org List-Id: devicetree@vger.kernel.org Hi, Few comments inline.. On Monday 26 August 2013 10:11 AM, Huang Shijie wrote: > (0) What is the Quadspi controller? > > The Quadspi(Quad Serial Peripheral Interface) acts as an interface to > one single or two external serial flash devices, each with up to 4 > bidirectional data lines. > > (1) The Quadspi controller is driven by the LUT(Look-up Table) registers. > The LUT registers are a look-up-table for sequences of instructions. > A valid sequence consists of four LUT registers. > > (2) The definition of the LUT register shows below: > > --------------------------------------------------- > | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | > --------------------------------------------------- > > There are several types of INSTRx, such as: > CMD : the SPI NOR command. > ADDR : the address for the SPI NOR command. > DUMMY : the dummy cycles needed by the SPI NOR command. > .... > > There are several types of PADx, such as: > PAD1 : use a singe I/O line. > PAD2 : use two I/O lines. > PAD4 : use quad I/O lines. > .... > > (3) We connect the NOR the QuadSPI now. I am not sure, but i think the > QuadSPI will be only used for the NOR. We may connect other devices > to it. But, for the reason of (2), we have to parse out the SPI NOR > command for the QuadSPI. > > (4) Test this driver with the JFFS2 and UBIFS: > > For jffs2: > ------------- > #flash_eraseall /dev/mtd0 > #mount -t jffs2 /dev/mtdblock0 tmp > #bonnie++ -d tmp -u 0 -s 10 -r 5 > > For ubifs: > ------------- > #flash_eraseall /dev/mtd0 > #ubiattach /dev/ubi_ctrl -m 0 > #ubimkvol /dev/ubi0 -N test -m > #mount -t ubifs ubi0:test tmp > #bonnie++ -d tmp -u 0 -s 10 -r 5 > > (5) The test result of the DDR QUAD Read (66MHz) performance: > #insmod mtd_speedtest.ko dev=0 > > [ 194.831313] ================================================= > [ 194.825453] mtd_speedtest: MTD device: 0 > [ 194.818670] mtd_speedtest: not NAND flash, assume page size is 512 bytes. > [ 194.811705] mtd_speedtest: MTD device size 16777216, eraseblock size 65536, > page size 512, count of eraseblocks 256, pages per eraseblock 128, OOB size 0 > [ 228.482355] mtd_speedtest: testing eraseblock write speed > [ 213.024166] mtd_speedtest: eraseblock write speed is 203 KiB/s > [ 213.018306] mtd_speedtest: testing eraseblock read speed > [ 212.660856] mtd_speedtest: eraseblock read speed is 46545 KiB/s > [ 181.728267] mtd_speedtest: testing page write speed > [ 231.434842] mtd_speedtest: page write speed is 203 KiB/s > [ 231.429515] mtd_speedtest: testing page read speed > [ 228.957422] mtd_speedtest: page read speed is 6641 KiB/s > [ 197.778872] mtd_speedtest: testing 2 page write speed > [ 247.338069] mtd_speedtest: 2 page write speed is 203 KiB/s > [ 247.332514] mtd_speedtest: testing 2 page read speed > [ 245.925048] mtd_speedtest: 2 page read speed is 11686 KiB/s > [ 245.919460] mtd_speedtest: Testing erase speed > [ 214.612341] mtd_speedtest: erase speed is 523 KiB/s > [ 214.607410] mtd_speedtest: Testing 2x multi-block erase speed > [ 245.545971] mtd_speedtest: 2x multi-block erase speed is 480 KiB/s > [ 245.539744] mtd_speedtest: Testing 4x multi-block erase speed > [ 211.141696] mtd_speedtest: 4x multi-block erase speed is 476 KiB/s > [ 211.135496] mtd_speedtest: Testing 8x multi-block erase speed > [ 241.761502] mtd_speedtest: 8x multi-block erase speed is 475 KiB/s > [ 241.755269] mtd_speedtest: Testing 16x multi-block erase speed > [ 272.307979] mtd_speedtest: 16x multi-block erase speed is 474 KiB/s > [ 272.301660] mtd_speedtest: Testing 32x multi-block erase speed > [ 237.637902] mtd_speedtest: 32x multi-block erase speed is 472 KiB/s > [ 237.631581] mtd_speedtest: Testing 64x multi-block erase speed > [ 267.954341] mtd_speedtest: 64x multi-block erase speed is 471 KiB/s > [ 267.948005] mtd_speedtest: finished > [ 267.944478] ================================================= > > * Conclusion *: > -------------------------------------------------------------------- > We can get the 46.5 MiB/s read speed when the DDR Quad Read is enabled. > (From S25FL128S's spec, the maximum read rate of DDR Quad Read is > 66MiB/s) > -------------------------------------------------------------------- > > Signed-off-by: Huang Shijie > --- > drivers/spi/Kconfig | 7 + > drivers/spi/Makefile | 1 + > drivers/spi/spi-fsl-quadspi.c | 1034 +++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 1042 insertions(+), 0 deletions(-) > create mode 100644 drivers/spi/spi-fsl-quadspi.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 92b2373..dc38063 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -187,6 +187,13 @@ config SPI_FALCON > has only been tested with m25p80 type chips. The hardware has no > support for other types of SPI peripherals. > > +config SPI_FSL_QUADSPI > + tristate "Freescale Quad SPI controller" > + depends on ARCH_MXC > + help > + This enables support for the Quad SPI controller in master mode. > + We only connect the NOR to this controller now. > + > config SPI_GPIO > tristate "GPIO-based bitbanging SPI Master" > depends on GPIOLIB > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index b25f385..7fe505c 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -37,6 +37,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..fa0718f > --- /dev/null > +++ b/drivers/spi/spi-fsl-quadspi.c > @@ -0,0 +1,1034 @@ > +/* > + * Freescale Quad SPI driver. > + * > + * Copyright (C) 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* The registers */ > +#define QUADSPI_MCR 0x00 > +#define QUADSPI_MCR_RESERVED_SHIFT 16 > +#define QUADSPI_MCR_RESERVED_MASK (0xF<< QUADSPI_MCR_RESERVED_SHIFT) > +#define QUADSPI_MCR_MDIS_SHIFT 14 > +#define QUADSPI_MCR_MDIS_MASK (1<< QUADSPI_MCR_MDIS_SHIFT) > +#define QUADSPI_MCR_CLR_TXF_SHIFT 11 > +#define QUADSPI_MCR_CLR_TXF_MASK (1<< QUADSPI_MCR_CLR_TXF_SHIFT) > +#define QUADSPI_MCR_CLR_RXF_SHIFT 10 > +#define QUADSPI_MCR_CLR_RXF_MASK (1<< QUADSPI_MCR_CLR_RXF_SHIFT) > +#define QUADSPI_MCR_DDR_EN_SHIFT 7 > +#define QUADSPI_MCR_DDR_EN_MASK (1<< QUADSPI_MCR_DDR_EN_SHIFT) > +#define QUADSPI_MCR_SWRSTHD_SHIFT 1 > +#define QUADSPI_MCR_SWRSTHD_MASK (1<< QUADSPI_MCR_SWRSTHD_SHIFT) > +#define QUADSPI_MCR_SWRSTSD_SHIFT 0 > +#define QUADSPI_MCR_SWRSTSD_MASK (1<< QUADSPI_MCR_SWRSTSD_SHIFT) > + > +#define QUADSPI_IPCR 0x08 > +#define QUADSPI_IPCR_SEQID_SHIFT 24 > +#define QUADSPI_IPCR_SEQID_MASK (0xF<< QUADSPI_IPCR_SEQID_SHIFT) > + > +#define QUADSPI_BUF0CR 0x10 > +#define QUADSPI_BUF1CR 0x14 > +#define QUADSPI_BUF2CR 0x18 > +#define QUADSPI_BUFXCR_INVALID_MSTRID 0xe > + > +#define QUADSPI_BUF3CR 0x1c > +#define QUADSPI_BUF3CR_ALLMST_SHIFT 31 > +#define QUADSPI_BUF3CR_ALLMST (1<< QUADSPI_BUF3CR_ALLMST_SHIFT) > + > +#define QUADSPI_BFGENCR 0x20 > +#define QUADSPI_BFGENCR_PAR_EN_SHIFT 16 > +#define QUADSPI_BFGENCR_PAR_EN_MASK (1<< (QUADSPI_BFGENCR_PAR_EN_SHIFT)) > +#define QUADSPI_BFGENCR_SEQID_SHIFT 12 > +#define QUADSPI_BFGENCR_SEQID_MASK (0xF<< QUADSPI_BFGENCR_SEQID_SHIFT) > + > +#define QUADSPI_BUF0IND 0x30 > +#define QUADSPI_BUF1IND 0x34 > +#define QUADSPI_BUF2IND 0x38 > +#define QUADSPI_SFAR 0x100 > + > +#define QUADSPI_SMPR 0x108 > +#define QUADSPI_SMPR_DDRSMP_SHIFT 16 > +#define QUADSPI_SMPR_DDRSMP_MASK (7<< QUADSPI_SMPR_DDRSMP_SHIFT) > +#define QUADSPI_SMPR_FSDLY_SHIFT 6 > +#define QUADSPI_SMPR_FSDLY_MASK (1<< QUADSPI_SMPR_FSDLY_SHIFT) > +#define QUADSPI_SMPR_FSPHS_SHIFT 5 > +#define QUADSPI_SMPR_FSPHS_MASK (1<< QUADSPI_SMPR_FSPHS_SHIFT) > +#define QUADSPI_SMPR_HSENA_SHIFT 0 > +#define QUADSPI_SMPR_HSENA_MASK (1<< QUADSPI_SMPR_HSENA_SHIFT) > + > +#define QUADSPI_RBSR 0x10c > +#define QUADSPI_RBSR_RDBFL_SHIFT 8 > +#define QUADSPI_RBSR_RDBFL_MASK (0x3F<< QUADSPI_RBSR_RDBFL_SHIFT) > + > +#define QUADSPI_RBCT 0x110 > +#define QUADSPI_RBCT_WMRK_MASK 0x1F > +#define QUADSPI_RBCT_RXBRD_SHIFT 8 > +#define QUADSPI_RBCT_RXBRD_USEIPS (0x1<< QUADSPI_RBCT_RXBRD_SHIFT) > + > +#define QUADSPI_TBSR 0x150 > +#define QUADSPI_TBDR 0x154 > +#define QUADSPI_SR 0x15c > + > +#define QUADSPI_FR 0x160 > +#define QUADSPI_FR_TFF_MASK 0x1 > + > +#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_LUTKEY_VALUE 0x5AF05AF0 > + > +#define QUADSPI_LCKCR 0x304 > +#define QUADSPI_LCKER_LOCK 0x1 > +#define QUADSPI_LCKER_UNLOCK 0x2 > + > +#define QUADSPI_RSER 0x164 > +#define QUADSPI_RSER_TFIE (0x1<< 0) > + > +#define QUADSPI_LUT_BASE 0x310 > + > +/* > + * The definition of the LUT register shows below: > + * > + * --------------------------------------------------- > + * | INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 | > + * --------------------------------------------------- > + */ > +#define OPRND0_SHIFT 0 > +#define PAD0_SHIFT 8 > +#define INSTR0_SHIFT 10 > +#define OPRND1_SHIFT 16 > + > +/* Instruction set for the LUT register. */ > +#define LUT_STOP 0 > +#define LUT_CMD 1 > +#define LUT_ADDR 2 > +#define LUT_DUMMY 3 > +#define LUT_MODE 4 > +#define LUT_MODE2 5 > +#define LUT_MODE4 6 > +#define LUT_READ 7 > +#define LUT_WRITE 8 > +#define LUT_JMP_ON_CS 9 > +#define LUT_ADDR_DDR 10 > +#define LUT_MODE_DDR 11 > +#define LUT_MODE2_DDR 12 > +#define LUT_MODE4_DDR 13 > +#define LUT_READ_DDR 14 > +#define LUT_WRITE_DDR 15 > +#define LUT_DATA_LEARN 16 > + > +/* > + * The PAD definitions for LUT register. > + * > + * The pad stands for the lines number of IO[0:3]. > + * For example, the Quad read need four IO lines, so you should > + * set LUT_PAD4 which means we use four IO lines. > + */ > +#define LUT_PAD1 0 > +#define LUT_PAD2 1 > +#define LUT_PAD4 2 > + > +/* Oprands for the LUT register. */ > +#define ADDR24BIT 0x18 > +#define ADDR32BIT 0x20 > + > +/* Macros for constructing the LUT register. */ > +#define LUT0(ins, pad, opr) \ > + (((opr)<< OPRND0_SHIFT) | ((LUT_##pad)<< PAD0_SHIFT) | \ > + ((LUT_##ins)<< INSTR0_SHIFT)) > + > +#define LUT1(ins, pad, opr) (LUT0(ins, pad, opr)<< OPRND1_SHIFT) > + > +/* other macros for LUT register. */ > +#define QUADSPI_LUT(x) (QUADSPI_LUT_BASE + (x) * 4) > +#define QUADSPI_LUT_NUM 64 > + > +/* SEQID -- we can have 16 seqids at most. */ > +#define SEQID_QUAD_READ 0 > +#define SEQID_WREN 1 > +#define SEQID_FAST_READ 2 > +#define SEQID_RDSR 3 > +#define SEQID_SE 4 > +#define SEQID_CHIP_ERASE 5 > +#define SEQID_PP 6 > +#define SEQID_RDID 7 > +#define SEQID_WRSR 8 > +#define SEQID_RDCR 9 > +#define SEQID_DDRQUAD_READ 10 > + > +struct fsl_qspi_handler { > + int (*setup)(struct spi_device *); > + int (*do_one_msg)(struct spi_master *, struct spi_message *); > +}; > + > +enum fsl_qspi_devtype { > + FSL_QUADSPI_VYBRID, > + FSL_QUADSPI_IMX6SLX > +}; > + > +struct fsl_qspi_devtype_data { > + enum fsl_qspi_devtype devtype; > + u32 memmap_base; > + int rxfifo; > + int txfifo; > +}; > + > +static struct fsl_qspi_devtype_data vybrid_data = { > + .devtype = FSL_QUADSPI_VYBRID, > + .memmap_base = 0x20000000, > + .rxfifo = 128, > + .txfifo = 64 > +}; > + > +struct fsl_qspi { > + void __iomem *iobase; > + struct clk *clk, *clk_en; > + struct device *dev; > + struct fsl_qspi_handler *h; > + struct completion c; > + struct fsl_qspi_devtype_data *devtype_data; > + void __iomem *ahb_base; /* Used when read from AHB bus */ > + unsigned int chip_base_addr; /* We may support two chips. */ > + unsigned int addr; > + u32 nor_size; /* for mapping */ > + u8 cmd; > + unsigned int quad_read_enabled:1; > + unsigned int has_inited:1; > +}; > + > +static inline int is_vybrid_qspi(struct fsl_qspi *q) > +{ > + return q->devtype_data->devtype == FSL_QUADSPI_VYBRID; > +} > + > +/* > + * An IC bug makes us to re-arrange the 32-bit data. > + * The following chips, such as IMX6SLX, have fixed this bug. > + */ > +static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a) > +{ > + return is_vybrid_qspi(q) ? __swab32(a) : a; > +} > + > +static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q) > +{ > + writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); > + writel(QUADSPI_LCKER_UNLOCK, q->iobase + QUADSPI_LCKCR); > +} > + > +static inline void fsl_qspi_lock_lut(struct fsl_qspi *q) > +{ > + writel(QUADSPI_LUTKEY_VALUE, q->iobase + QUADSPI_LUTKEY); > + writel(QUADSPI_LCKER_LOCK, q->iobase + QUADSPI_LCKCR); > +} > + > +static irqreturn_t fsl_qspi_irq_handler(int irq, void *dev_id) > +{ > + struct fsl_qspi *q = dev_id; > + u32 reg; > + > + /* clear interrupt */ > + reg = readl(q->iobase + QUADSPI_FR); > + writel(reg, q->iobase + QUADSPI_FR); > + > + if (reg& QUADSPI_FR_TFF_MASK) > + complete(&q->c); > + > + dev_dbg(q->dev, "QUADSPI_FR : 0x%.8x\n", reg); > + return IRQ_HANDLED; > +} > + > +/* Init the LUT table. All the parameters are from the S25FL128S. */ > +static void fsl_qspi_init_lut(struct fsl_qspi *q) > +{ > + void *__iomem base = q->iobase; > + int rxfifo = q->devtype_data->rxfifo; > + u32 lut_base; > + u8 cmd, addrlen, dummy; > + int i; > + > + fsl_qspi_unlock_lut(q); > + > + /* Clear all the LUT table */ > + for (i = 0; i< QUADSPI_LUT_NUM; i++) > + writel(0, base + QUADSPI_LUT_BASE + i * 4); > + > + /* Quad Read */ > + lut_base = SEQID_QUAD_READ * 4; > + > + if (q->nor_size<= SZ_16M) { > + cmd = OPCODE_QIOR; > + addrlen = ADDR24BIT; > + dummy = 4; > + } else { > + cmd = OPCODE_4QIOR; > + addrlen = ADDR32BIT; > + dummy = 4; > + } > + > + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD4, addrlen), > + base + QUADSPI_LUT(lut_base)); > + writel(LUT0(MODE, PAD4, 0xff) | LUT1(DUMMY, PAD4, dummy), > + base + QUADSPI_LUT(lut_base + 1)); > + writel(LUT0(READ, PAD4, rxfifo), base + QUADSPI_LUT(lut_base + 2)); > + > + /* Write enable */ > + lut_base = SEQID_WREN * 4; > + writel(LUT0(CMD, PAD1, OPCODE_WREN), base + QUADSPI_LUT(lut_base)); > + > + /* Fast Read */ > + lut_base = SEQID_FAST_READ * 4; > + > + if (q->nor_size<= SZ_16M) { > + cmd = OPCODE_FAST_READ; > + addrlen = ADDR24BIT; > + dummy = 8; > + } else { > + cmd = OPCODE_FAST_READ_4B; > + addrlen = ADDR32BIT; > + dummy = 8; > + } > + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), > + base + QUADSPI_LUT(lut_base)); > + writel(LUT0(DUMMY, PAD1, dummy) | LUT1(READ, PAD1, rxfifo), > + base + QUADSPI_LUT(lut_base + 1)); > + > + /* Page Program */ > + lut_base = SEQID_PP * 4; > + > + if (q->nor_size<= SZ_16M) { > + cmd = OPCODE_PP; > + addrlen = ADDR24BIT; > + } else { > + cmd = OPCODE_PP_4B; > + addrlen = ADDR32BIT; > + } > + > + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), > + base + QUADSPI_LUT(lut_base)); > + writel(LUT0(WRITE, PAD1, 0), base + QUADSPI_LUT(lut_base + 1)); > + > + /* Read Status */ > + lut_base = SEQID_RDSR * 4; > + writel(LUT0(CMD, PAD1, OPCODE_RDSR) | LUT1(READ, PAD1, 0x1), > + base + QUADSPI_LUT(lut_base)); > + > + /* Erase a sector */ > + lut_base = SEQID_SE * 4; > + > + if (q->nor_size<= SZ_16M) { > + cmd = OPCODE_SE; > + addrlen = ADDR24BIT; > + } else { > + cmd = OPCODE_SE_4B; > + addrlen = ADDR32BIT; > + } > + > + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), > + base + QUADSPI_LUT(lut_base)); > + > + /* Erase the whole chip */ > + lut_base = SEQID_CHIP_ERASE * 4; > + writel(LUT0(CMD, PAD1, OPCODE_CHIP_ERASE), > + base + QUADSPI_LUT(lut_base)); > + > + /* READ ID */ > + lut_base = SEQID_RDID * 4; > + writel(LUT0(CMD, PAD1, OPCODE_RDID) | LUT1(READ, PAD1, 0x8), > + base + QUADSPI_LUT(lut_base)); > + > + /* Write Register */ > + lut_base = SEQID_WRSR * 4; > + writel(LUT0(CMD, PAD1, OPCODE_WRSR) | LUT1(WRITE, PAD1, 0x2), > + base + QUADSPI_LUT(lut_base)); > + > + /* Read Configuration Register */ > + lut_base = SEQID_RDCR * 4; > + writel(LUT0(CMD, PAD1, OPCODE_RDCR) | LUT1(READ, PAD1, 0x1), > + base + QUADSPI_LUT(lut_base)); > + > + /* DDR Quad Read */ > + lut_base = SEQID_DDRQUAD_READ * 4; > + > + if (q->nor_size<= SZ_16M) { > + cmd = OPCODE_DDRQIOR; > + addrlen = ADDR24BIT; > + dummy = 6; > + } else { > + cmd = OPCODE_4DDRQIOR; > + addrlen = ADDR32BIT; > + dummy = 6; > + } > + > + writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR_DDR, PAD4, addrlen), > + base + QUADSPI_LUT(lut_base)); > + writel(LUT0(MODE_DDR, PAD4, 0xff) | LUT1(DUMMY, PAD1, dummy), > + base + QUADSPI_LUT(lut_base + 1)); > + writel(LUT0(READ_DDR, PAD4, rxfifo), > + base + QUADSPI_LUT(lut_base + 2)); > + > + fsl_qspi_lock_lut(q); > +} > + > +/* Get the SEQID for the command */ > +static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) > +{ > + switch (cmd) { > + case OPCODE_QIOR: > + case OPCODE_4QIOR: > + return SEQID_QUAD_READ; > + case OPCODE_WREN: > + return SEQID_WREN; > + case OPCODE_RDSR: > + return SEQID_RDSR; > + case OPCODE_SE: > + return SEQID_SE; > + case OPCODE_CHIP_ERASE: > + return SEQID_CHIP_ERASE; > + case OPCODE_PP: > + return SEQID_PP; > + case OPCODE_RDID: > + return SEQID_RDID; > + case OPCODE_WRSR: > + return SEQID_WRSR; > + case OPCODE_RDCR: > + return SEQID_RDCR; > + case OPCODE_DDRQIOR: > + case OPCODE_4DDRQIOR: > + return SEQID_DDRQUAD_READ; > + default: > + dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); > + break; > + } > + return -EINVAL; > +} > + > +static int > +fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) > +{ > + void *__iomem base = q->iobase; > + int seqid; > + u32 reg; > + int err; > + > + init_completion(&q->c); > + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len:%d, cmd:%.2x\n", > + q->chip_base_addr, addr, len, cmd); > + > + /* save the reg */ > + reg = readl(base + QUADSPI_MCR); > + > + writel(q->devtype_data->memmap_base + q->chip_base_addr + addr, > + base + QUADSPI_SFAR); > + writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, > + base + QUADSPI_RBCT); > + writel(reg | QUADSPI_MCR_CLR_RXF_MASK, base + QUADSPI_MCR); > + > + /* trigger the LUT now */ > + seqid = fsl_qspi_get_seqid(q, cmd); > + writel((seqid<< QUADSPI_IPCR_SEQID_SHIFT) | len, base + QUADSPI_IPCR); > + > + /* Wait for the interrupt. */ > + err = wait_for_completion_timeout(&q->c, msecs_to_jiffies(1000)); > + if (!err) { > + dev_err(q->dev, > + "cmd 0x%.2x timeout, addr@%.8x, FR:0x%.8x, SR:0x%.8x\n", > + cmd, addr, readl(base + QUADSPI_FR), > + readl(base + QUADSPI_SR)); > + err = -ETIMEDOUT; > + } else { > + err = 0; > + } > + > + /* restore the MCR */ > + writel(reg, base + QUADSPI_MCR); > + > + return err; > +} > + > +static unsigned int fsl_qspi_get_addr(struct fsl_qspi *q, > + struct spi_transfer *t) > +{ > + unsigned int addr; > + u8 *buf = (u8 *)t->tx_buf; > + > + /* 3-byte address */ > + if (q->nor_size<= SZ_16M) > + addr = (buf[1]<< 16) | (buf[2]<< 8) | buf[3]; > + else /* 4-byte address */ > + addr = (buf[1]<< 24) | (buf[2]<< 16) | (buf[3]<< 8) | buf[4]; > + > + return addr; > +} > + > +/* Read out the data from the QUADSPI_RBDR buffer registers. */ > +static void fsl_qspi_read_data(struct fsl_qspi *q, int len, u32 *rxbuf) > +{ > + u32 tmp; > + int i = 0; > + > + while (len> 0) { > + tmp = readl(q->iobase + QUADSPI_RBDR + i * 4); > + *rxbuf = fsl_qspi_endian_xchg(q, tmp); > + dev_dbg(q->dev, "rcv: 0x%.8x, tmp : 0x%.8x\n", *rxbuf, tmp); > + > + rxbuf++; > + len -= 4; > + i++; > + } > +} > + > +/* Read out the data directly from the AHB buffer.*/ > +static int fsl_qspi_read_data_ahb(struct fsl_qspi *q, struct spi_transfer *t) > +{ > + dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n", > + q->cmd, q->ahb_base, q->chip_base_addr, q->addr, t->len); > + memcpy(t->rx_buf, q->ahb_base + q->chip_base_addr + q->addr, t->len); > + return 0; > +} > + > +static u32 fsl_qspi_read_sr(struct fsl_qspi *q) > +{ > + u32 val = -EINVAL; > + int ret; > + > + ret = fsl_qspi_runcmd(q, OPCODE_RDSR, 0, 1); > + if (!ret) > + fsl_qspi_read_data(q, 1,&val); > + else > + return ret; > + return val; > +} > + > +static int fsl_qspi_wait_till_ready(struct fsl_qspi *q) > +{ > + unsigned long deadline; > + u32 sr; > + > + deadline = jiffies + msecs_to_jiffies(40000); > + > + do { > + cond_resched(); > + > + if ((sr = fsl_qspi_read_sr(q))< 0) > + break; > + else if (!(sr& SR_WIP)) > + return 0; > + } while (!time_after_eq(jiffies, deadline)); > + > + return (sr< 0) ? sr : -ETIMEDOUT; > +} > + > +/* > + * If we have changed the content of the flash by writing or erasing, > + * we need to invalidate the AHB buffer. If we do not do so, we may read out > + * the wrong data. The spec tells us reset the AHB domain and Serial Flash > + * domain at the same time. > + */ > +static inline void fsl_qspi_invalid(struct fsl_qspi *q) > +{ > + u32 reg; > + > + reg = readl(q->iobase + QUADSPI_MCR); > + reg |= QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK; > + writel(reg, q->iobase + QUADSPI_MCR); > + > + /* > + * The minimum delay : 1 AHB + 2 SFCK clocks. > + * Delay 1 us is enough. > + */ > + udelay(1); > + > + reg&= ~(QUADSPI_MCR_SWRSTHD_MASK | QUADSPI_MCR_SWRSTSD_MASK); > + writel(reg, q->iobase + QUADSPI_MCR); > +} > + > +static int fsl_qspi_nor_write(struct fsl_qspi *q, u32 *txbuf, unsigned count) > +{ > + unsigned int addr = q->addr; > + int txfifo_size = q->devtype_data->txfifo; > + int ret = 0; > + int tx_size; > + u32 tmp; > + int i, j; > + u8 cmd = q->cmd; > + > + q->cmd = -1; /* clear the cmd */ > + dev_dbg(q->dev, "to 0x%.8x:0x%.8x, len : %d\n", > + q->chip_base_addr, addr, count); > + > + while (count> 0) { > + tx_size = (count> txfifo_size) ? txfifo_size : count; > + > + /* clear the TX FIFO. */ > + tmp = readl(q->iobase + QUADSPI_MCR); > + writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR); > + > + /* fill the TX data to the FIFO */ > + for (j = 0, i = ((tx_size + 3) / 4); j< i; j++) { > + tmp = fsl_qspi_endian_xchg(q, *txbuf); > + writel(tmp, q->iobase + QUADSPI_TBDR); > + txbuf++; > + } > + > + /* Trigger it */ > + ret = fsl_qspi_runcmd(q, cmd, addr, tx_size); > + > + addr += tx_size; > + count -= tx_size; > + > + /* > + * If the TX FIFO is smaller then the size of Page Program, > + * we have to wait until this Write is finished. > + * For example, the TX FIFO is 64 bytes in the Vybrid, > + * but the Page Program may writes 265 bytes per time. > + * We are lucky that some chip(IMX6SLX) has increase the TX FIFO > + * to 512 bytes. > + * > + * If we can change the @m25p->page_size, we can remove the > + * following code. > + */ > + if (count> 0) { > + ret = fsl_qspi_wait_till_ready(q); > + if (ret) { > + dev_err(q->dev, "Reading SR, err:%d\n", ret); > + break; > + } > + > + /* Write Enable again. */ > + ret = fsl_qspi_runcmd(q, OPCODE_WREN, 0, 0); > + if (ret) { > + dev_err(q->dev, "Write Enable, err:%d\n", ret); > + break; > + } > + } > + } > + return ret; > +} > + > +/* Switch to Quad read or DDR Quad read now. */ > +static inline void fsl_qspi_enable_quad_read(struct fsl_qspi *q, u8 cmd) > +{ > + int seqid; > + u32 reg, reg2; > + > + seqid = fsl_qspi_get_seqid(q, cmd); > + writel(seqid<< QUADSPI_BFGENCR_SEQID_SHIFT, > + q->iobase + QUADSPI_BFGENCR); > + > + /* should we enable the DDR ? */ > + if (seqid == SEQID_DDRQUAD_READ) { > + reg = readl(q->iobase + QUADSPI_MCR); > + > + /* Firstly, disable the module */ > + writel(reg | QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); > + > + /* Set the Sampling Register for DDR */ > + reg2 = readl(q->iobase + QUADSPI_SMPR); > + reg2&= ~QUADSPI_SMPR_DDRSMP_MASK; > + reg2 |= (1<< QUADSPI_SMPR_DDRSMP_SHIFT); > + writel(reg2, q->iobase + QUADSPI_SMPR); > + > + /* Enable the module again (enable the DDR too) */ > + writel(reg | QUADSPI_MCR_DDR_EN_MASK, q->iobase + QUADSPI_MCR); > + } > + > + q->quad_read_enabled = 1; > +} > + > +static int fsl_qspi_nor_tx(struct fsl_qspi *q, struct spi_transfer *t) > +{ > + unsigned int addr = 0; > + bool need_invalid = false; > + int ret = 0; > + u8 cmd; > + > + /* This is the second spi_transfer for Page Program. */ > + if (q->cmd == OPCODE_PP) { > + ret = fsl_qspi_nor_write(q, (u32 *)t->tx_buf, t->len); > + need_invalid = true; > + goto qspi_tx_out; > + } > + > + cmd = *(u8 *)t->tx_buf; > + dev_dbg(q->dev, "NOR cmd is [0x%.2x], len : %d.\n", cmd, t->len); > + > + switch (cmd) { > + case OPCODE_SE: > + addr = fsl_qspi_get_addr(q, t); > + /* fall through */ > + case OPCODE_CHIP_ERASE: > + need_invalid = true; > + /* fall through */ > + case OPCODE_WREN: > + ret = fsl_qspi_runcmd(q, cmd, addr, 0); > + q->cmd = -1; > + break; > + > + case OPCODE_4DDRQIOR: > + case OPCODE_DDRQIOR: > + case OPCODE_4QIOR: > + case OPCODE_QIOR: > + if (!q->quad_read_enabled) > + fsl_qspi_enable_quad_read(q, cmd); > + /* fall through */ > + case OPCODE_FAST_READ: > + case OPCODE_PP: > + q->cmd = cmd; > + q->addr = fsl_qspi_get_addr(q, t); > + break; > + > + case OPCODE_WRSR: > + q->addr = 0; > + q->cmd = cmd; > + ret = fsl_qspi_nor_write(q, > + (u32*)(((u8 *)t->tx_buf) + 1),/* skip the cmd */ > + t->len - 1); > + break; > + > + default: > + q->cmd = cmd; > + break; > + } > + > +qspi_tx_out: > + if (need_invalid) > + fsl_qspi_invalid(q); > + return ret; > +} > + > +static int fsl_qspi_nor_rx(struct fsl_qspi *q, struct spi_transfer *t) > +{ > + int ret = 0; > + > + switch (q->cmd) { > + case OPCODE_RDSR: > + case OPCODE_RDCR: > + case OPCODE_RDID: > + ret = fsl_qspi_runcmd(q, q->cmd, 0, t->len); > + if (!ret) > + fsl_qspi_read_data(q, t->len, t->rx_buf); > + break; > + > + case OPCODE_4DDRQIOR: > + case OPCODE_DDRQIOR: > + case OPCODE_4QIOR: > + case OPCODE_QIOR: > + case OPCODE_FAST_READ: > + ret = fsl_qspi_read_data_ahb(q, t); > + break; > + default: > + dev_err(q->dev, "Unsupported cmd : %x\n", q->cmd); > + return -EINVAL; > + } > + return ret; > +} > + > +static int fsl_qspi_nor_do_one_msg(struct spi_master *master, > + struct spi_message *m) > +{ > + struct fsl_qspi *q = spi_master_get_devdata(master); > + struct spi_transfer *t; > + int ret = 0; > + > + /* The chip address we are working. */ > + q->chip_base_addr = q->nor_size * m->spi->chip_select; > + > + list_for_each_entry(t,&m->transfers, transfer_list) { > + if (t->tx_buf) { > + ret = fsl_qspi_nor_tx(q, t); > + if (!ret) > + m->actual_length += t->len; > + continue; > + } > + > + if (t->rx_buf) { > + ret = fsl_qspi_nor_rx(q, t); > + if (!ret) > + m->actual_length += t->len; > + } > + } > + > + m->status = ret; > + spi_finalize_current_message(master); > + return ret; > +} > + > +/* > + * There are two different ways to read out the data from the flash: > + * the "IP Command Read" and the "AHB Command Read". > + * > + * The IC guy suggests we use the "AHB Command Read" which is faster > + * then the "IP Command Read". (What's more is that there is a bug in > + * the "IP Command Read" in the Vybrid.) > + * > + * After we set up the registers for the "AHB Command Read", we can use > + * the memcpy to read the data directly. A "missed" access to the buffer > + * causes the controller to clear the buffer, and use the sequence pointed > + * by the QUADSPI_BFGENCR[SEQID] to initiate a read from the flash. > + */ > +static int fsl_qspi_init_abh_read(struct fsl_qspi *q, struct spi_device *spi) > +{ > + void __iomem *base = q->iobase; > + u32 memmap_base = q->devtype_data->memmap_base; > + int nor_size = q->nor_size; > + int nor_num = spi->master->num_chipselect; > + > + /* We only can support two NOR flash at the most. */ > + if (nor_num> 2) > + nor_num = 2; > + > + /* Map the SPI NOR to accessiable address */ > + writel(nor_size | memmap_base, base + QUADSPI_SFA1AD); > + writel(nor_size | memmap_base, base + QUADSPI_SFA2AD); > + writel((nor_size * nor_num) | memmap_base, base + QUADSPI_SFB1AD); > + writel((nor_size * nor_num) | memmap_base, base + QUADSPI_SFB2AD); > + > + /* AHB configuration for access buffer 0/1/2 .*/ > + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR); > + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR); > + writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR); > + writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR); > + > + /* We only use the buffer3 */ > + writel(0, base + QUADSPI_BUF0IND); > + writel(0, base + QUADSPI_BUF1IND); > + writel(0, base + QUADSPI_BUF2IND); > + > + /* Set the default lut sequence for AHB Read. */ > + writel(SEQID_FAST_READ<< QUADSPI_BFGENCR_SEQID_SHIFT, > + base + QUADSPI_BFGENCR); > + > + /* Map the AHB address for read. */ > + q->ahb_base = devm_ioremap(q->dev, memmap_base, nor_size * nor_num); > + if (!q->ahb_base) > + return -ENOMEM; > + return 0; > +} > + > +static int fsl_qspi_nor_setup(struct spi_device *spi) > +{ > + struct fsl_qspi *q = spi_master_get_devdata(spi->master); > + void __iomem *base = q->iobase; > + u32 reg; > + int ret; > + > + /* We may support two NOR chips, we only need to init one times. */ > + if (q->has_inited) > + return 0; > + > + /* The DDR Quad read can run at 66MHz for the S25FL128S. */ > + ret = clk_set_rate(q->clk, spi->max_speed_hz); > + if (ret) > + return ret; > + > + fsl_qspi_init_lut(q); > + ret = fsl_qspi_init_abh_read(q, spi); > + if (ret< 0) > + return ret; > + > + /* Disable the module */ > + writel(QUADSPI_MCR_MDIS_MASK | QUADSPI_MCR_RESERVED_MASK, > + base + QUADSPI_MCR); > + > + reg = readl(base + QUADSPI_SMPR); > + writel(reg& ~(QUADSPI_SMPR_FSDLY_MASK > + | QUADSPI_SMPR_FSPHS_MASK > + | QUADSPI_SMPR_HSENA_MASK > + | QUADSPI_SMPR_DDRSMP_MASK), base + QUADSPI_SMPR); > + > + /* Enable the module */ > + writel(QUADSPI_MCR_RESERVED_MASK, base + QUADSPI_MCR); > + > + /* enable the interrupt */ > + writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); > + > + q->has_inited = 1; > + return 0; > +} > + > +/* We only support the NOR now. */ > +static struct fsl_qspi_handler fsl_qspi_nor_handler = { > + .setup = fsl_qspi_nor_setup, > + .do_one_msg = fsl_qspi_nor_do_one_msg, > +}; > + > +static int fsl_qspi_setup(struct spi_device *spi) > +{ > + struct fsl_qspi *q = spi_master_get_devdata(spi->master); > + > + if (q->h&& q->h->setup) > + return q->h->setup(spi); > + return 0; > +} > + > +static int fsl_qspi_do_one_msg(struct spi_master *master, > + struct spi_message *m) > +{ > + struct fsl_qspi *q = spi_master_get_devdata(master); > + > + if (q->h&& q->h->do_one_msg) > + return q->h->do_one_msg(master, m); > + return 0; > +} > + > +static struct of_device_id fsl_qspi_dt_ids[] = { > + { .compatible = "fsl,vf610-qspi", .data = (void*)&vybrid_data, }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); > + > +static int fsl_qspi_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct spi_master *master; > + struct fsl_qspi *q; > + struct resource *res; > + int num_cs, ret; > + const struct of_device_id *of_id = > + of_match_device(fsl_qspi_dt_ids,&pdev->dev); > + > + ret = of_property_read_u32(np, "fsl,spi-num-chipselects",&num_cs); There is a generic chipselect spi binding available. You should use that, rather than defining your own binding. > + if (ret< 0) { > + dev_err(&pdev->dev, "can't get the spi-mum-chipselects\n"); > + return ret; > + } > + > + master = spi_alloc_master(&pdev->dev, sizeof(*q)); > + if (!master) > + return -ENOMEM; > + > + q = spi_master_get_devdata(master); > + > + ret = of_property_read_u32(np, "fsl,nor-size",&q->nor_size); > + if (!ret&& q->nor_size> 0) > + q->h =&fsl_qspi_nor_handler; /* The default is for NOR.*/ > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + q->iobase = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(q->iobase)) { > + dev_err(&pdev->dev, "ioremap failed\n"); You can do away with this error check. > + ret = PTR_ERR(q->iobase); > + goto map_failed; > + } > + > + q->clk_en = devm_clk_get(&pdev->dev, "qspi_en"); > + if (IS_ERR(q->clk_en)) { > + ret = PTR_ERR(q->clk_en); > + goto map_failed; > + } > + > + q->clk = devm_clk_get(&pdev->dev, "qspi"); > + if (IS_ERR(q->clk)) { > + ret = PTR_ERR(q->clk); > + goto map_failed; > + } > + > + ret = clk_prepare_enable(q->clk_en); > + if (ret) { > + dev_err(&pdev->dev, "can not enable the qspi_en clock\n"); > + goto map_failed; > + } > + > + ret = clk_prepare_enable(q->clk); > + if (ret) { > + clk_disable_unprepare(q->clk_en); > + dev_err(&pdev->dev, "can not enable the qspi clock\n"); > + goto map_failed; > + } > + > + ret = platform_get_irq(pdev, 0); > + if (ret< 0) { > + dev_err(&pdev->dev, "failed to get the irq\n"); > + goto irq_failed; > + } > + > + ret = devm_request_irq(&pdev->dev, ret, > + fsl_qspi_irq_handler, 0, pdev->name, q); > + if (ret) { > + dev_err(&pdev->dev, "failed to request irq.\n"); > + goto irq_failed; > + } > + > + q->dev =&pdev->dev; > + q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data; > + > + 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; > + master->flags = SPI_MASTER_HALF_DUPLEX; > + platform_set_drvdata(pdev, master); > + > + ret = spi_register_master(master); > + if (ret) { > + dev_err(&pdev->dev, "failed to register the spi master.\n"); > + goto irq_failed; > + } > + dev_info(&pdev->dev, "QuadSPI bus driver\n"); > + return 0; > + > +irq_failed: > + clk_disable_unprepare(q->clk); > + clk_disable_unprepare(q->clk_en); > +map_failed: > + spi_master_put(master); > + > + dev_err(&pdev->dev, "Freescale QuadSPI probe failed\n"); > + return ret; > +} > + > +static int fsl_qspi_remove(struct platform_device *pdev) > +{ > + struct spi_master *master = platform_get_drvdata(pdev); > + struct fsl_qspi *q = spi_master_get_devdata(master); > + > + /* disable the hardware */ > + writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR); > + writel(0x0, q->iobase + QUADSPI_RSER); > + > + clk_disable_unprepare(q->clk); > + clk_disable_unprepare(q->clk_en); > + spi_master_put(master); > + return 0; > +} > + > +static struct platform_driver fsl_qspi_driver = { > + .driver = { > + .name = "fsl-quadspi", > + .owner = THIS_MODULE, > + .of_match_table = fsl_qspi_dt_ids, > + }, > + .probe = fsl_qspi_probe, > + .remove = fsl_qspi_remove, > +}; > +module_platform_driver(fsl_qspi_driver); > + > +MODULE_DESCRIPTION("Freescale QuadSPI Controller Driver"); > +MODULE_LICENSE("GPL v2");