Adding devicetree@xxxxxxxxxxxxxxx You might want to split out the devicetree binding documentation as a separate patch from the driver submission, so that DT binding reviewers will have an easier time. On Mon, Aug 19, 2013 at 12:10:02PM +0800, 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. > .... > > (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 > > Signed-off-by: Huang Shijie <b32955@xxxxxxxxxxxxx> > --- > .../devicetree/bindings/spi/fsl-quadspi.txt | 27 + > drivers/spi/Kconfig | 7 + > drivers/spi/Makefile | 1 + > drivers/spi/spi-fsl-quadspi.c | 930 ++++++++++++++++++++ > 4 files changed, 965 insertions(+), 0 deletions(-) > create mode 100644 Documentation/devicetree/bindings/spi/fsl-quadspi.txt > create mode 100644 drivers/spi/spi-fsl-quadspi.c > > diff --git a/Documentation/devicetree/bindings/spi/fsl-quadspi.txt b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt > new file mode 100644 > index 0000000..e5bfa82 > --- /dev/null > +++ b/Documentation/devicetree/bindings/spi/fsl-quadspi.txt > @@ -0,0 +1,27 @@ > +* Freescale Quad Serial Peripheral Interface(QuadSPI) > + > +Required properties: > +- compatible : Should be "fsl,vf610-qspi" > +- reg : Offset and length of the register set for the device > +- interrupts : Should contain the interrupt for the device > +- fsl,spi-num-chipselects : Contains the number of the chipselect Can this controller support more than one chip? If so, then the nor-size property makes even less sense. See below. > +- clocks : The clocks needed by the QuadSPI controller > +- clock-names : the name of the clocks > + > +Optional properties: > +- fsl,nor-size : The NOR size used by the QuadSPI mapping. Why does the size of the NOR flash need to be in the controller's device node? Shouldn't this be detected at run-time if possible? Or at least included as a property of m25p80, if absolutely required? > + > +Example: > + > +qspi0: quadspi@40044000 { > + #address-cells = <1>; > + #size-cells = <0>; > + compatible = "fsl,vf610-qspi"; > + reg = <0x40044000 0x1000>; > + interrupts = <0 24 0x04>; > + clocks = <&clks VF610_CLK_QSPI0_EN>, > + <&clks VF610_CLK_QSPI0>; > + clock-names = "qspi_en", "qspi"; > + fsl,nor-size = <0x1000000>; > + status = "disabled"; > +}; > 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..de71a4e > --- /dev/null > +++ b/drivers/spi/spi-fsl-quadspi.c > @@ -0,0 +1,930 @@ > +/* > + * 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 <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 <linux/completion.h> > +#include <linux/mtd/spi-nor.h> > + > +/* The registers */ > +#define QUADSPI_MCR 0x00 > +#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_RESERVED_SHIFT 16 > +#define QUADSPI_MCR_RESERVED_MASK (0xF << QUADSPI_MCR_RESERVED_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_SR_TXFULL_SHIFT 27 > +#define QUADSPI_SR_TXFULL_MASK (1 << QUADSPI_SR_TXFULL_SHIFT) > +#define QUADSPI_SR_AHBTRN_SHIFT 6 > +#define QUADSPI_SR_AHBTRN_MASK (1 << QUADSPI_SR_AHBTRN_SHIFT) > +#define QUADSPI_SR_AHB_ACC_SHIFT 2 > +#define QUADSPI_SR_AHB_ACC_MASK (1 << QUADSPI_SR_AHB_ACC_SHIFT) > +#define QUADSPI_SR_IP_ACC_SHIFT 1 > +#define QUADSPI_SR_IP_ACC_MASK (1 << QUADSPI_SR_IP_ACC_SHIFT) > +#define QUADSPI_SR_BUSY_SHIFT 0 > +#define QUADSPI_SR_BUSY_MASK (1 << QUADSPI_SR_BUSY_SHIFT) > + > +#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 > + > +/* Field definitions for LUT register. */ > +#define OPRND0_SHIFT 0 > +#define PAD0_SHIFT 8 > +#define INSTR0_SHIFT 10 > +#define OPRND1_SHIFT 16 > + > +/* Instruction set for the LUT register. */ > +#define CMD 1 > +#define ADDR 2 > +#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 > + > +/* > + * 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 PAD4 which means we use four IO lines. > + */ > +#define PAD1 0 > +#define PAD2 1 > +#define PAD4 2 > + > +/* Oprands for the LUT register. */ > +#define ADDR24BIT 0x18 > + > +/* Macros for constructing the LUT register. */ > +#define QUADSPI_LUT0(ins, pad, opr) \ > + (((opr) << OPRND0_SHIFT) | ((pad) << PAD0_SHIFT) | \ > + ((ins) << INSTR0_SHIFT)) > + > +#define QUADSPI_LUT1(ins, pad, opr) \ > + (QUADSPI_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 */ > +#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 > + > +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 addr; > + u32 nor_size; /* for mapping */ > + u8 cmd; > + unsigned int quad_read_enabled: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 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 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. */ > +static void fsl_qspi_init_lut(struct fsl_qspi *q) > +{ > + void *__iomem base = q->iobase; > + int rxfifo = q->devtype_data->rxfifo; > + u32 lut_base; > + int i; > + > + 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; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_QIOR) > + | QUADSPI_LUT1(ADDR, PAD4, ADDR24BIT), > + base + QUADSPI_LUT(lut_base)); > + writel(QUADSPI_LUT0(MODE, PAD4, 0xff) | QUADSPI_LUT1(DUMMY, PAD4, 4), > + base + QUADSPI_LUT(lut_base + 1)); > + writel(QUADSPI_LUT0(READ, PAD4, rxfifo), > + base + QUADSPI_LUT(lut_base + 2)); > + > + /* Write enable */ > + lut_base = SEQID_WREN * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WREN), > + base + QUADSPI_LUT(lut_base)); > + > + /* Fast Read */ > + lut_base = SEQID_FAST_READ * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_FAST_READ) > + | QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT), > + base + QUADSPI_LUT(lut_base)); > + writel(QUADSPI_LUT0(DUMMY, PAD1, 8) | QUADSPI_LUT1(READ, PAD1, rxfifo), > + base + QUADSPI_LUT(lut_base + 1)); > + > + /* Page Program */ > + lut_base = SEQID_PP * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_PP) > + | QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT), > + base + QUADSPI_LUT(lut_base)); > + writel(QUADSPI_LUT0(WRITE, PAD1, 0), > + base + QUADSPI_LUT(lut_base + 1)); > + > + /* Read Status */ > + lut_base = SEQID_RDSR * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDSR) > + | QUADSPI_LUT1(READ, PAD1, 0x1), > + base + QUADSPI_LUT(lut_base)); > + > + /* Erase a sector */ > + lut_base = SEQID_SE * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_SE) > + | QUADSPI_LUT1(ADDR, PAD1, ADDR24BIT), > + base + QUADSPI_LUT(lut_base)); > + > + /* Erase the whole chip */ > + lut_base = SEQID_CHIP_ERASE * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_CHIP_ERASE), > + base + QUADSPI_LUT(lut_base)); > + > + /* READ ID */ > + lut_base = SEQID_RDID * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDID) > + | QUADSPI_LUT1(READ, PAD1, 0x8), > + base + QUADSPI_LUT(lut_base)); > + > + /* Write Register */ > + lut_base = SEQID_WRSR * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_WRSR) > + | QUADSPI_LUT1(WRITE, PAD1, 0x2), > + base + QUADSPI_LUT(lut_base)); > + > + /* Read Configuration Register */ > + lut_base = SEQID_RDCR * 4; > + writel(QUADSPI_LUT0(CMD, PAD1, OPCODE_RDCR) > + | QUADSPI_LUT1(READ, PAD1, 0x1), > + base + QUADSPI_LUT(lut_base)); > + 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_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; > + default: > + dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd); > + break; > + } > + return -1; > +} > + > +static int > +fsl_qspi_runcmd(struct fsl_qspi *q, u8 cmd, unsigned int addr, int len) > +{ > + int seqid; > + u32 reg; > + int err; > + > + init_completion(&q->c); > + dev_dbg(q->dev, "to @%.8x, len:%d, cmd:%.2x\n", addr, len, cmd); > + > + /* save the reg */ > + reg = readl(q->iobase + QUADSPI_MCR); > + > + writel(q->devtype_data->memmap_base + addr, q->iobase + QUADSPI_SFAR); > + writel(QUADSPI_RBCT_WMRK_MASK | QUADSPI_RBCT_RXBRD_USEIPS, > + q->iobase + QUADSPI_RBCT); > + writel(QUADSPI_MCR_CLR_RXF_MASK | QUADSPI_MCR_RESERVED_MASK, > + q->iobase + QUADSPI_MCR); > + > + /* trigger the LUT now */ > + seqid = fsl_qspi_get_seqid(q, cmd); > + writel((seqid << QUADSPI_IPCR_SEQID_SHIFT) | len, > + q->iobase + 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(q->iobase + QUADSPI_FR), > + readl(q->iobase + QUADSPI_SR)); > + err = -ETIMEDOUT; > + } else { > + err = 0; > + } > + > + /* restore the MCR */ > + writel(reg, q->iobase + QUADSPI_MCR); > + > + return err; > +} > + > +/* Get the address from the tx buffer. */ > +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]; > + return addr; > +} > + > +/* Read out the data from the 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%.8x,len:%d\n", > + q->cmd, q->addr, t->len); > + memcpy(t->rx_buf, q->ahb_base + 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); > + 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 { > + if ((sr = fsl_qspi_read_sr(q)) < 0) > + break; > + else if (!(sr & SR_WIP)) > + return 0; > + > + cond_resched(); > + > + } while (!time_after_eq(jiffies, deadline)); > + > + return 1; > +} > + > +/* > + * 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 @%.8x, len : %d\n", 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 now. */ > +static inline void fsl_qspi_enable_quad_read(struct fsl_qspi *q) > +{ > + writel(SEQID_QUAD_READ << QUADSPI_BFGENCR_SEQID_SHIFT, > + q->iobase + QUADSPI_BFGENCR); > + 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; > + u32 val; > + 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; > + case OPCODE_WREN: > + ret = fsl_qspi_runcmd(q, cmd, addr, 0); > + q->cmd = -1; > + break; > + > + case OPCODE_QIOR: > + if (!q->quad_read_enabled) > + fsl_qspi_enable_quad_read(q); > + /* 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; > + /* skip the cmd */ > + memcpy((void *) &val, ((u8 *)t->tx_buf) + 1, t->len -1); > + ret = fsl_qspi_nor_write(q, &val, 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_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; > + > + list_for_each_entry(t, &m->transfers, transfer_list) { > + if (t->rx_buf && t->tx_buf) { > + dev_err(q->dev, > + "Can't send and receive simultaneously\n"); > + ret = -EINVAL; > + break; > + } > + > + 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) > +{ > + void __iomem *base = q->iobase; > + u32 memmap_base = q->devtype_data->memmap_base; > + int nor_size = q->nor_size; > + > + /* 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 * 2) | memmap_base, base + QUADSPI_SFB1AD); > + writel((nor_size * 2) | 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 = ioremap(memmap_base, nor_size); > + 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_val, smpr_val; > + int ret; > + > + writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_MDIS_MASK, > + base + QUADSPI_MCR); > + > + reg_val = readl(base + QUADSPI_SMPR); > + writel(reg_val & ~(QUADSPI_SMPR_FSDLY_MASK > + | QUADSPI_SMPR_FSPHS_MASK > + | QUADSPI_SMPR_HSENA_MASK), base + QUADSPI_SMPR); > + > + writel(QUADSPI_MCR_RESERVED_MASK, base + QUADSPI_MCR); > + > + fsl_qspi_init_lut(q); > + ret = fsl_qspi_init_abh_read(q); > + if (ret < 0) > + return ret; > + > + reg_val = 0; > + reg_val |= QUADSPI_MCR_RESERVED_MASK; > + smpr_val = readl(base + QUADSPI_SMPR); > + smpr_val &= ~QUADSPI_SMPR_DDRSMP_MASK; > + writel(smpr_val, base + QUADSPI_SMPR); > + reg_val &= ~QUADSPI_MCR_DDR_EN_MASK; > + writel(reg_val, base + QUADSPI_MCR); > + > + /* enable the interrupt */ > + writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER); > + 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); > + > + return q->h->setup(spi); > +} > + > +static int fsl_qspi_do_one_msg(struct spi_master *master, > + struct spi_message *m) > +{ > + struct fsl_qspi *q = spi_master_get_devdata(master); > + > + return q->h->do_one_msg(master, m); > +} > + > +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 = 0; > + 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); > + 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 < 0) > + dev_dbg(&pdev->dev, "can't get the nor size.\n"); > + > + 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"); > + ret = PTR_ERR(q->iobase); > + goto map_failed; > + } > + > + q->clk_en = devm_clk_get(&pdev->dev, "qspi_en"); > + q->clk = devm_clk_get(&pdev->dev, "qspi"); > + if (IS_ERR(q->clk_en) || IS_ERR(q->clk)) { > + dev_err(&pdev->dev, "failed to get clocks\n"); > + ret = -ENOENT; > + 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; > + > + /* The default handler is for NOR. */ > + q->h = &fsl_qspi_nor_handler; > + > + 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); > + > + 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(0x0, 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"); > -- > 1.7.1 > > Brian -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html