[PATCH v2 5/8] spi: Add Freescale QuadSpi driver

Sourav Poddar sourav.poddar at ti.com
Mon Aug 26 02:10:42 EDT 2013


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<b32955 at freescale.com>
> ---
>   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<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_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");




More information about the linux-mtd mailing list