2014-02-24 18:37:42 +08:00
/*
* Freescale QuadSPI 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/of.h>
# include <linux/of_device.h>
# include <linux/timer.h>
# include <linux/jiffies.h>
# include <linux/completion.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
# include <linux/mtd/spi-nor.h>
2015-05-13 14:40:57 -05:00
# include <linux/mutex.h>
2015-08-04 10:26:04 -05:00
# include <linux/pm_qos.h>
2014-02-24 18:37:42 +08:00
2015-08-04 10:25:29 -05:00
/* Controller needs driver to swap endian */
# define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0)
/* Controller needs 4x internal clock */
# define QUADSPI_QUIRK_4X_INT_CLK (1 << 1)
2015-08-04 10:25:35 -05:00
/*
* TKT253890 , Controller needs driver to fill txfifo till 16 byte to
* trigger data transfer even though extern data will not transferred .
*/
# define QUADSPI_QUIRK_TKT253890 (1 << 2)
2015-08-04 10:26:04 -05:00
/* Controller cannot wake up from wait mode, TKT245618 */
# define QUADSPI_QUIRK_TKT245618 (1 << 3)
2015-08-04 10:25:29 -05:00
2014-02-24 18:37:42 +08:00
/* 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_END_CFG_SHIFT 2
# define QUADSPI_MCR_END_CFG_MASK (3 << QUADSPI_MCR_END_CFG_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
2015-01-14 00:28:56 +08:00
# define QUADSPI_BUF3CR_ALLMST_MASK (1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
# define QUADSPI_BUF3CR_ADATSZ_SHIFT 8
# define QUADSPI_BUF3CR_ADATSZ_MASK (0xFF << QUADSPI_BUF3CR_ADATSZ_SHIFT)
2014-02-24 18:37:42 +08:00
# 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_IP_ACC_SHIFT 1
# define QUADSPI_SR_IP_ACC_MASK (0x1 << QUADSPI_SR_IP_ACC_SHIFT)
# define QUADSPI_SR_AHB_ACC_SHIFT 2
# define QUADSPI_SR_AHB_ACC_MASK (0x1 << QUADSPI_SR_AHB_ACC_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
/*
* 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_WRDI 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_EN4B 10
# define SEQID_BRWR 11
mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read
QSPI may failed to map enough memory (256MB) for AHB read in
previous implementation, especially in 3G/1G memory layout kernel.
Dynamically map memory to avoid such issue.
This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory
for AHB read, it should be enough for common scenarios, and the side
effect (0.6% performance drop) is minor.
Previous implementation
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s
After applied the patch
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s
Signed-off-by: Han Xu <han.xu@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2015-08-04 10:25:22 -05:00
# define QUADSPI_MIN_IOMAP SZ_4M
2014-02-24 18:37:42 +08:00
enum fsl_qspi_devtype {
FSL_QUADSPI_VYBRID ,
FSL_QUADSPI_IMX6SX ,
2015-08-04 10:25:35 -05:00
FSL_QUADSPI_IMX7D ,
2015-08-04 10:25:47 -05:00
FSL_QUADSPI_IMX6UL ,
2014-02-24 18:37:42 +08:00
} ;
struct fsl_qspi_devtype_data {
enum fsl_qspi_devtype devtype ;
int rxfifo ;
int txfifo ;
2015-01-14 00:28:56 +08:00
int ahb_buf_size ;
2015-08-04 10:25:29 -05:00
int driver_data ;
2014-02-24 18:37:42 +08:00
} ;
static struct fsl_qspi_devtype_data vybrid_data = {
. devtype = FSL_QUADSPI_VYBRID ,
. rxfifo = 128 ,
2015-01-14 00:28:56 +08:00
. txfifo = 64 ,
2015-08-04 10:25:29 -05:00
. ahb_buf_size = 1024 ,
. driver_data = QUADSPI_QUIRK_SWAP_ENDIAN ,
2014-02-24 18:37:42 +08:00
} ;
static struct fsl_qspi_devtype_data imx6sx_data = {
. devtype = FSL_QUADSPI_IMX6SX ,
. rxfifo = 128 ,
2015-01-14 00:28:56 +08:00
. txfifo = 512 ,
2015-08-04 10:25:29 -05:00
. ahb_buf_size = 1024 ,
2015-08-04 10:26:04 -05:00
. driver_data = QUADSPI_QUIRK_4X_INT_CLK
| QUADSPI_QUIRK_TKT245618 ,
2014-02-24 18:37:42 +08:00
} ;
2015-08-04 10:25:35 -05:00
static struct fsl_qspi_devtype_data imx7d_data = {
. devtype = FSL_QUADSPI_IMX7D ,
. rxfifo = 512 ,
. txfifo = 512 ,
. ahb_buf_size = 1024 ,
. driver_data = QUADSPI_QUIRK_TKT253890
| QUADSPI_QUIRK_4X_INT_CLK ,
} ;
2015-08-04 10:25:47 -05:00
static struct fsl_qspi_devtype_data imx6ul_data = {
. devtype = FSL_QUADSPI_IMX6UL ,
. rxfifo = 128 ,
. txfifo = 512 ,
. ahb_buf_size = 1024 ,
. driver_data = QUADSPI_QUIRK_TKT253890
| QUADSPI_QUIRK_4X_INT_CLK ,
} ;
2014-02-24 18:37:42 +08:00
# define FSL_QSPI_MAX_CHIP 4
struct fsl_qspi {
struct mtd_info mtd [ FSL_QSPI_MAX_CHIP ] ;
struct spi_nor nor [ FSL_QSPI_MAX_CHIP ] ;
void __iomem * iobase ;
mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read
QSPI may failed to map enough memory (256MB) for AHB read in
previous implementation, especially in 3G/1G memory layout kernel.
Dynamically map memory to avoid such issue.
This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory
for AHB read, it should be enough for common scenarios, and the side
effect (0.6% performance drop) is minor.
Previous implementation
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s
After applied the patch
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s
Signed-off-by: Han Xu <han.xu@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2015-08-04 10:25:22 -05:00
void __iomem * ahb_addr ;
2014-02-24 18:37:42 +08:00
u32 memmap_phy ;
mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read
QSPI may failed to map enough memory (256MB) for AHB read in
previous implementation, especially in 3G/1G memory layout kernel.
Dynamically map memory to avoid such issue.
This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory
for AHB read, it should be enough for common scenarios, and the side
effect (0.6% performance drop) is minor.
Previous implementation
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s
After applied the patch
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s
Signed-off-by: Han Xu <han.xu@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2015-08-04 10:25:22 -05:00
u32 memmap_offs ;
u32 memmap_len ;
2014-02-24 18:37:42 +08:00
struct clk * clk , * clk_en ;
struct device * dev ;
struct completion c ;
struct fsl_qspi_devtype_data * devtype_data ;
u32 nor_size ;
u32 nor_num ;
u32 clk_rate ;
unsigned int chip_base_addr ; /* We may support two chips. */
2015-01-13 20:14:15 -02:00
bool has_second_chip ;
2015-05-13 14:40:57 -05:00
struct mutex lock ;
2015-08-04 10:26:04 -05:00
struct pm_qos_request pm_qos_req ;
2014-02-24 18:37:42 +08:00
} ;
2015-08-04 10:25:29 -05:00
static inline int needs_swap_endian ( struct fsl_qspi * q )
2014-02-24 18:37:42 +08:00
{
2015-08-04 10:25:29 -05:00
return q - > devtype_data - > driver_data & QUADSPI_QUIRK_SWAP_ENDIAN ;
2014-02-24 18:37:42 +08:00
}
2015-08-04 10:25:29 -05:00
static inline int needs_4x_clock ( struct fsl_qspi * q )
2014-02-24 18:37:42 +08:00
{
2015-08-04 10:25:29 -05:00
return q - > devtype_data - > driver_data & QUADSPI_QUIRK_4X_INT_CLK ;
2014-02-24 18:37:42 +08:00
}
2015-08-04 10:25:35 -05:00
static inline int needs_fill_txfifo ( struct fsl_qspi * q )
{
return q - > devtype_data - > driver_data & QUADSPI_QUIRK_TKT253890 ;
}
2015-08-04 10:26:04 -05:00
static inline int needs_wakeup_wait_mode ( struct fsl_qspi * q )
{
return q - > devtype_data - > driver_data & QUADSPI_QUIRK_TKT245618 ;
}
2014-02-24 18:37:42 +08:00
/*
* 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 )
{
2015-08-04 10:25:29 -05:00
return needs_swap_endian ( q ) ? __swab32 ( a ) : a ;
2014-02-24 18:37:42 +08:00
}
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:0x%.8x \n " , q - > chip_base_addr , reg ) ;
return IRQ_HANDLED ;
}
static void fsl_qspi_init_lut ( struct fsl_qspi * q )
{
2014-04-10 15:49:38 -07:00
void __iomem * base = q - > iobase ;
2014-02-24 18:37:42 +08:00
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 ) {
2014-04-08 19:16:49 -07:00
cmd = SPINOR_OP_READ_1_1_4 ;
2014-02-24 18:37:42 +08:00
addrlen = ADDR24BIT ;
dummy = 8 ;
} else {
/* use the 4-byte address */
2014-04-08 19:16:49 -07:00
cmd = SPINOR_OP_READ_1_1_4 ;
2014-02-24 18:37:42 +08:00
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 , PAD4 , rxfifo ) ,
base + QUADSPI_LUT ( lut_base + 1 ) ) ;
/* Write enable */
lut_base = SEQID_WREN * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_WREN ) , base + QUADSPI_LUT ( lut_base ) ) ;
2014-02-24 18:37:42 +08:00
/* Page Program */
lut_base = SEQID_PP * 4 ;
if ( q - > nor_size < = SZ_16M ) {
2014-04-08 18:15:31 -07:00
cmd = SPINOR_OP_PP ;
2014-02-24 18:37:42 +08:00
addrlen = ADDR24BIT ;
} else {
/* use the 4-byte address */
2014-04-08 18:15:31 -07:00
cmd = SPINOR_OP_PP ;
2014-02-24 18:37:42 +08:00
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 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_RDSR ) | LUT1 ( READ , PAD1 , 0x1 ) ,
2014-02-24 18:37:42 +08:00
base + QUADSPI_LUT ( lut_base ) ) ;
/* Erase a sector */
lut_base = SEQID_SE * 4 ;
2015-08-04 10:26:16 -05:00
cmd = q - > nor [ 0 ] . erase_opcode ;
addrlen = q - > nor_size < = SZ_16M ? ADDR24BIT : ADDR32BIT ;
2014-02-24 18:37:42 +08:00
writel ( LUT0 ( CMD , PAD1 , cmd ) | LUT1 ( ADDR , PAD1 , addrlen ) ,
base + QUADSPI_LUT ( lut_base ) ) ;
/* Erase the whole chip */
lut_base = SEQID_CHIP_ERASE * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_CHIP_ERASE ) ,
2014-02-24 18:37:42 +08:00
base + QUADSPI_LUT ( lut_base ) ) ;
/* READ ID */
lut_base = SEQID_RDID * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_RDID ) | LUT1 ( READ , PAD1 , 0x8 ) ,
2014-02-24 18:37:42 +08:00
base + QUADSPI_LUT ( lut_base ) ) ;
/* Write Register */
lut_base = SEQID_WRSR * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_WRSR ) | LUT1 ( WRITE , PAD1 , 0x2 ) ,
2014-02-24 18:37:42 +08:00
base + QUADSPI_LUT ( lut_base ) ) ;
/* Read Configuration Register */
lut_base = SEQID_RDCR * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_RDCR ) | LUT1 ( READ , PAD1 , 0x1 ) ,
2014-02-24 18:37:42 +08:00
base + QUADSPI_LUT ( lut_base ) ) ;
/* Write disable */
lut_base = SEQID_WRDI * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_WRDI ) , base + QUADSPI_LUT ( lut_base ) ) ;
2014-02-24 18:37:42 +08:00
/* Enter 4 Byte Mode (Micron) */
lut_base = SEQID_EN4B * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_EN4B ) , base + QUADSPI_LUT ( lut_base ) ) ;
2014-02-24 18:37:42 +08:00
/* Enter 4 Byte Mode (Spansion) */
lut_base = SEQID_BRWR * 4 ;
2014-04-08 18:15:31 -07:00
writel ( LUT0 ( CMD , PAD1 , SPINOR_OP_BRWR ) , base + QUADSPI_LUT ( lut_base ) ) ;
2014-02-24 18:37:42 +08:00
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 ) {
2014-04-08 19:16:49 -07:00
case SPINOR_OP_READ_1_1_4 :
2014-02-24 18:37:42 +08:00
return SEQID_QUAD_READ ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_WREN :
2014-02-24 18:37:42 +08:00
return SEQID_WREN ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_WRDI :
2014-02-24 18:37:42 +08:00
return SEQID_WRDI ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_RDSR :
2014-02-24 18:37:42 +08:00
return SEQID_RDSR ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_SE :
2014-02-24 18:37:42 +08:00
return SEQID_SE ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_CHIP_ERASE :
2014-02-24 18:37:42 +08:00
return SEQID_CHIP_ERASE ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_PP :
2014-02-24 18:37:42 +08:00
return SEQID_PP ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_RDID :
2014-02-24 18:37:42 +08:00
return SEQID_RDID ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_WRSR :
2014-02-24 18:37:42 +08:00
return SEQID_WRSR ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_RDCR :
2014-02-24 18:37:42 +08:00
return SEQID_RDCR ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_EN4B :
2014-02-24 18:37:42 +08:00
return SEQID_EN4B ;
2014-04-08 18:15:31 -07:00
case SPINOR_OP_BRWR :
2014-02-24 18:37:42 +08:00
return SEQID_BRWR ;
default :
2015-08-04 10:26:16 -05:00
if ( cmd = = q - > nor [ 0 ] . erase_opcode )
return SEQID_SE ;
2014-02-24 18:37:42 +08:00
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 )
{
2014-04-10 15:49:38 -07:00
void __iomem * base = q - > iobase ;
2014-02-24 18:37:42 +08:00
int seqid ;
u32 reg , reg2 ;
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 - > memmap_phy + 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 ) ;
do {
reg2 = readl ( base + QUADSPI_SR ) ;
if ( reg2 & ( QUADSPI_SR_IP_ACC_MASK | QUADSPI_SR_AHB_ACC_MASK ) ) {
udelay ( 1 ) ;
dev_dbg ( q - > dev , " The controller is busy, 0x%x \n " , reg2 ) ;
continue ;
}
break ;
} while ( 1 ) ;
/* 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. */
2015-02-01 06:15:46 -05:00
if ( ! wait_for_completion_timeout ( & q - > c , msecs_to_jiffies ( 1000 ) ) ) {
2014-02-24 18:37:42 +08:00
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 ;
}
/* Read out the data from the QUADSPI_RBDR buffer registers. */
static void fsl_qspi_read_data ( struct fsl_qspi * q , int len , u8 * rxbuf )
{
u32 tmp ;
int i = 0 ;
while ( len > 0 ) {
tmp = readl ( q - > iobase + QUADSPI_RBDR + i * 4 ) ;
tmp = fsl_qspi_endian_xchg ( q , tmp ) ;
dev_dbg ( q - > dev , " chip addr:0x%.8x, rcv:0x%.8x \n " ,
q - > chip_base_addr , tmp ) ;
if ( len > = 4 ) {
* ( ( u32 * ) rxbuf ) = tmp ;
rxbuf + = 4 ;
} else {
memcpy ( rxbuf , & tmp , len ) ;
break ;
}
len - = 4 ;
i + + ;
}
}
/*
* 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 , struct spi_nor * nor ,
u8 opcode , unsigned int to , u32 * txbuf ,
unsigned count , size_t * retlen )
{
int ret , i , j ;
u32 tmp ;
dev_dbg ( q - > dev , " to 0x%.8x:0x%.8x, len : %d \n " ,
q - > chip_base_addr , to , count ) ;
/* clear the TX FIFO. */
tmp = readl ( q - > iobase + QUADSPI_MCR ) ;
2015-07-02 11:37:56 +02:00
writel ( tmp | QUADSPI_MCR_CLR_TXF_MASK , q - > iobase + QUADSPI_MCR ) ;
2014-02-24 18:37:42 +08:00
/* fill the TX data to the FIFO */
for ( j = 0 , i = ( ( count + 3 ) / 4 ) ; j < i ; j + + ) {
tmp = fsl_qspi_endian_xchg ( q , * txbuf ) ;
writel ( tmp , q - > iobase + QUADSPI_TBDR ) ;
txbuf + + ;
}
2015-08-04 10:25:35 -05:00
/* fill the TXFIFO upto 16 bytes for i.MX7d */
if ( needs_fill_txfifo ( q ) )
for ( ; i < 4 ; i + + )
writel ( tmp , q - > iobase + QUADSPI_TBDR ) ;
2014-02-24 18:37:42 +08:00
/* Trigger it */
ret = fsl_qspi_runcmd ( q , opcode , to , count ) ;
if ( ret = = 0 & & retlen )
* retlen + = count ;
return ret ;
}
static void fsl_qspi_set_map_addr ( struct fsl_qspi * q )
{
int nor_size = q - > nor_size ;
void __iomem * base = q - > iobase ;
writel ( nor_size + q - > memmap_phy , base + QUADSPI_SFA1AD ) ;
writel ( nor_size * 2 + q - > memmap_phy , base + QUADSPI_SFA2AD ) ;
writel ( nor_size * 3 + q - > memmap_phy , base + QUADSPI_SFB1AD ) ;
writel ( nor_size * 4 + q - > memmap_phy , base + QUADSPI_SFB2AD ) ;
}
/*
* 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 void fsl_qspi_init_abh_read ( struct fsl_qspi * q )
{
void __iomem * base = q - > iobase ;
int seqid ;
/* 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 ) ;
2015-01-14 00:28:56 +08:00
/*
* Set ADATSZ with the maximum AHB buffer size to improve the
* read performance .
*/
writel ( QUADSPI_BUF3CR_ALLMST_MASK | ( ( q - > devtype_data - > ahb_buf_size / 8 )
< < QUADSPI_BUF3CR_ADATSZ_SHIFT ) , base + QUADSPI_BUF3CR ) ;
2014-02-24 18:37:42 +08:00
/* 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. */
seqid = fsl_qspi_get_seqid ( q , q - > nor [ 0 ] . read_opcode ) ;
writel ( seqid < < QUADSPI_BFGENCR_SEQID_SHIFT ,
q - > iobase + QUADSPI_BFGENCR ) ;
}
2015-08-04 10:25:58 -05:00
/* This function was used to prepare and enable QSPI clock */
static int fsl_qspi_clk_prep_enable ( struct fsl_qspi * q )
{
int ret ;
ret = clk_prepare_enable ( q - > clk_en ) ;
if ( ret )
return ret ;
ret = clk_prepare_enable ( q - > clk ) ;
if ( ret ) {
clk_disable_unprepare ( q - > clk_en ) ;
return ret ;
}
2015-08-04 10:26:04 -05:00
if ( needs_wakeup_wait_mode ( q ) )
pm_qos_add_request ( & q - > pm_qos_req , PM_QOS_CPU_DMA_LATENCY , 0 ) ;
2015-08-04 10:25:58 -05:00
return 0 ;
}
/* This function was used to disable and unprepare QSPI clock */
static void fsl_qspi_clk_disable_unprep ( struct fsl_qspi * q )
{
2015-08-04 10:26:04 -05:00
if ( needs_wakeup_wait_mode ( q ) )
pm_qos_remove_request ( & q - > pm_qos_req ) ;
2015-08-04 10:25:58 -05:00
clk_disable_unprepare ( q - > clk ) ;
clk_disable_unprepare ( q - > clk_en ) ;
}
2014-02-24 18:37:42 +08:00
/* We use this function to do some basic init for spi_nor_scan(). */
static int fsl_qspi_nor_setup ( struct fsl_qspi * q )
{
void __iomem * base = q - > iobase ;
u32 reg ;
int ret ;
2015-08-04 10:25:58 -05:00
/* disable and unprepare clock to avoid glitch pass to controller */
fsl_qspi_clk_disable_unprep ( q ) ;
/* the default frequency, we will change it in the future. */
2014-02-24 18:37:42 +08:00
ret = clk_set_rate ( q - > clk , 66000000 ) ;
if ( ret )
return ret ;
2015-08-04 10:25:58 -05:00
ret = fsl_qspi_clk_prep_enable ( q ) ;
if ( ret )
return ret ;
2015-08-04 10:26:10 -05:00
/* Reset the module */
writel ( QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK ,
base + QUADSPI_MCR ) ;
udelay ( 1 ) ;
2014-02-24 18:37:42 +08:00
/* Init the LUT table. */
fsl_qspi_init_lut ( q ) ;
/* 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 | QUADSPI_MCR_END_CFG_MASK ,
base + QUADSPI_MCR ) ;
2015-08-04 10:26:10 -05:00
/* clear all interrupt status */
writel ( 0xffffffff , q - > iobase + QUADSPI_FR ) ;
2014-02-24 18:37:42 +08:00
/* enable the interrupt */
writel ( QUADSPI_RSER_TFIE , q - > iobase + QUADSPI_RSER ) ;
return 0 ;
}
static int fsl_qspi_nor_setup_last ( struct fsl_qspi * q )
{
unsigned long rate = q - > clk_rate ;
int ret ;
2015-08-04 10:25:29 -05:00
if ( needs_4x_clock ( q ) )
2014-02-24 18:37:42 +08:00
rate * = 4 ;
2015-08-04 10:25:58 -05:00
/* disable and unprepare clock to avoid glitch pass to controller */
fsl_qspi_clk_disable_unprep ( q ) ;
2014-02-24 18:37:42 +08:00
ret = clk_set_rate ( q - > clk , rate ) ;
if ( ret )
return ret ;
2015-08-04 10:25:58 -05:00
ret = fsl_qspi_clk_prep_enable ( q ) ;
if ( ret )
return ret ;
2014-02-24 18:37:42 +08:00
/* Init the LUT table again. */
fsl_qspi_init_lut ( q ) ;
/* Init for AHB read */
fsl_qspi_init_abh_read ( q ) ;
return 0 ;
}
2015-03-16 20:20:28 +01:00
static const struct of_device_id fsl_qspi_dt_ids [ ] = {
2014-02-24 18:37:42 +08:00
{ . compatible = " fsl,vf610-qspi " , . data = ( void * ) & vybrid_data , } ,
{ . compatible = " fsl,imx6sx-qspi " , . data = ( void * ) & imx6sx_data , } ,
2015-08-04 10:25:35 -05:00
{ . compatible = " fsl,imx7d-qspi " , . data = ( void * ) & imx7d_data , } ,
2015-08-04 10:25:47 -05:00
{ . compatible = " fsl,imx6ul-qspi " , . data = ( void * ) & imx6ul_data , } ,
2014-02-24 18:37:42 +08:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , fsl_qspi_dt_ids ) ;
static void fsl_qspi_set_base_addr ( struct fsl_qspi * q , struct spi_nor * nor )
{
q - > chip_base_addr = q - > nor_size * ( nor - q - > nor ) ;
}
static int fsl_qspi_read_reg ( struct spi_nor * nor , u8 opcode , u8 * buf , int len )
{
int ret ;
struct fsl_qspi * q = nor - > priv ;
ret = fsl_qspi_runcmd ( q , opcode , 0 , len ) ;
if ( ret )
return ret ;
fsl_qspi_read_data ( q , len , buf ) ;
return 0 ;
}
static int fsl_qspi_write_reg ( struct spi_nor * nor , u8 opcode , u8 * buf , int len ,
int write_enable )
{
struct fsl_qspi * q = nor - > priv ;
int ret ;
if ( ! buf ) {
ret = fsl_qspi_runcmd ( q , opcode , 0 , 1 ) ;
if ( ret )
return ret ;
2014-04-08 18:15:31 -07:00
if ( opcode = = SPINOR_OP_CHIP_ERASE )
2014-02-24 18:37:42 +08:00
fsl_qspi_invalid ( q ) ;
} else if ( len > 0 ) {
ret = fsl_qspi_nor_write ( q , nor , opcode , 0 ,
( u32 * ) buf , len , NULL ) ;
} else {
dev_err ( q - > dev , " invalid cmd %d \n " , opcode ) ;
ret = - EINVAL ;
}
return ret ;
}
static void fsl_qspi_write ( struct spi_nor * nor , loff_t to ,
size_t len , size_t * retlen , const u_char * buf )
{
struct fsl_qspi * q = nor - > priv ;
fsl_qspi_nor_write ( q , nor , nor - > program_opcode , to ,
( u32 * ) buf , len , retlen ) ;
/* invalid the data in the AHB buffer. */
fsl_qspi_invalid ( q ) ;
}
static int fsl_qspi_read ( struct spi_nor * nor , loff_t from ,
size_t len , size_t * retlen , u_char * buf )
{
struct fsl_qspi * q = nor - > priv ;
u8 cmd = nor - > read_opcode ;
mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read
QSPI may failed to map enough memory (256MB) for AHB read in
previous implementation, especially in 3G/1G memory layout kernel.
Dynamically map memory to avoid such issue.
This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory
for AHB read, it should be enough for common scenarios, and the side
effect (0.6% performance drop) is minor.
Previous implementation
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s
After applied the patch
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s
Signed-off-by: Han Xu <han.xu@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2015-08-04 10:25:22 -05:00
/* if necessary,ioremap buffer before AHB read, */
if ( ! q - > ahb_addr ) {
q - > memmap_offs = q - > chip_base_addr + from ;
q - > memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP ;
q - > ahb_addr = ioremap_nocache (
q - > memmap_phy + q - > memmap_offs ,
q - > memmap_len ) ;
if ( ! q - > ahb_addr ) {
dev_err ( q - > dev , " ioremap failed \n " ) ;
return - ENOMEM ;
}
/* ioremap if the data requested is out of range */
} else if ( q - > chip_base_addr + from < q - > memmap_offs
| | q - > chip_base_addr + from + len >
q - > memmap_offs + q - > memmap_len ) {
iounmap ( q - > ahb_addr ) ;
q - > memmap_offs = q - > chip_base_addr + from ;
q - > memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP ;
q - > ahb_addr = ioremap_nocache (
q - > memmap_phy + q - > memmap_offs ,
q - > memmap_len ) ;
if ( ! q - > ahb_addr ) {
dev_err ( q - > dev , " ioremap failed \n " ) ;
return - ENOMEM ;
}
}
dev_dbg ( q - > dev , " cmd [%x],read from 0x%p, len:%d \n " ,
cmd , q - > ahb_addr + q - > chip_base_addr + from - q - > memmap_offs ,
len ) ;
2014-02-24 18:37:42 +08:00
/* Read out the data directly from the AHB buffer.*/
mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read
QSPI may failed to map enough memory (256MB) for AHB read in
previous implementation, especially in 3G/1G memory layout kernel.
Dynamically map memory to avoid such issue.
This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory
for AHB read, it should be enough for common scenarios, and the side
effect (0.6% performance drop) is minor.
Previous implementation
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s
After applied the patch
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s
Signed-off-by: Han Xu <han.xu@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2015-08-04 10:25:22 -05:00
memcpy ( buf , q - > ahb_addr + q - > chip_base_addr + from - q - > memmap_offs ,
len ) ;
2014-02-24 18:37:42 +08:00
* retlen + = len ;
return 0 ;
}
static int fsl_qspi_erase ( struct spi_nor * nor , loff_t offs )
{
struct fsl_qspi * q = nor - > priv ;
int ret ;
dev_dbg ( nor - > dev , " %dKiB at 0x%08x:0x%08x \n " ,
nor - > mtd - > erasesize / 1024 , q - > chip_base_addr , ( u32 ) offs ) ;
ret = fsl_qspi_runcmd ( q , nor - > erase_opcode , offs , 0 ) ;
if ( ret )
return ret ;
fsl_qspi_invalid ( q ) ;
return 0 ;
}
static int fsl_qspi_prep ( struct spi_nor * nor , enum spi_nor_ops ops )
{
struct fsl_qspi * q = nor - > priv ;
int ret ;
2015-05-13 14:40:57 -05:00
mutex_lock ( & q - > lock ) ;
2014-02-24 18:37:42 +08:00
2015-08-04 10:25:58 -05:00
ret = fsl_qspi_clk_prep_enable ( q ) ;
2015-05-13 14:40:57 -05:00
if ( ret )
2015-08-04 10:25:58 -05:00
goto err_mutex ;
2014-02-24 18:37:42 +08:00
fsl_qspi_set_base_addr ( q , nor ) ;
return 0 ;
2015-05-13 14:40:57 -05:00
err_mutex :
mutex_unlock ( & q - > lock ) ;
return ret ;
2014-02-24 18:37:42 +08:00
}
static void fsl_qspi_unprep ( struct spi_nor * nor , enum spi_nor_ops ops )
{
struct fsl_qspi * q = nor - > priv ;
2015-08-04 10:25:58 -05:00
fsl_qspi_clk_disable_unprep ( q ) ;
2015-05-13 14:40:57 -05:00
mutex_unlock ( & q - > lock ) ;
2014-02-24 18:37:42 +08:00
}
static int fsl_qspi_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct mtd_part_parser_data ppdata ;
struct device * dev = & pdev - > dev ;
struct fsl_qspi * q ;
struct resource * res ;
struct spi_nor * nor ;
struct mtd_info * mtd ;
int ret , i = 0 ;
const struct of_device_id * of_id =
of_match_device ( fsl_qspi_dt_ids , & pdev - > dev ) ;
q = devm_kzalloc ( dev , sizeof ( * q ) , GFP_KERNEL ) ;
if ( ! q )
return - ENOMEM ;
q - > nor_num = of_get_child_count ( dev - > of_node ) ;
if ( ! q - > nor_num | | q - > nor_num > FSL_QSPI_MAX_CHIP )
return - ENODEV ;
2015-08-04 10:26:04 -05:00
q - > dev = dev ;
q - > devtype_data = ( struct fsl_qspi_devtype_data * ) of_id - > data ;
platform_set_drvdata ( pdev , q ) ;
2014-02-24 18:37:42 +08:00
/* find the resources */
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " QuadSPI " ) ;
q - > iobase = devm_ioremap_resource ( dev , res ) ;
2015-01-22 22:43:07 -02:00
if ( IS_ERR ( q - > iobase ) )
return PTR_ERR ( q - > iobase ) ;
2014-02-24 18:37:42 +08:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM ,
" QuadSPI-memory " ) ;
mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read
QSPI may failed to map enough memory (256MB) for AHB read in
previous implementation, especially in 3G/1G memory layout kernel.
Dynamically map memory to avoid such issue.
This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory
for AHB read, it should be enough for common scenarios, and the side
effect (0.6% performance drop) is minor.
Previous implementation
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s
After applied the patch
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s
Signed-off-by: Han Xu <han.xu@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2015-08-04 10:25:22 -05:00
if ( ! devm_request_mem_region ( dev , res - > start , resource_size ( res ) ,
res - > name ) ) {
dev_err ( dev , " can't request region for resource %pR \n " , res ) ;
return - EBUSY ;
}
2015-01-22 22:43:07 -02:00
2014-02-24 18:37:42 +08:00
q - > memmap_phy = res - > start ;
/* find the clocks */
q - > clk_en = devm_clk_get ( dev , " qspi_en " ) ;
2015-01-22 22:43:07 -02:00
if ( IS_ERR ( q - > clk_en ) )
return PTR_ERR ( q - > clk_en ) ;
2014-02-24 18:37:42 +08:00
q - > clk = devm_clk_get ( dev , " qspi " ) ;
2015-01-22 22:43:07 -02:00
if ( IS_ERR ( q - > clk ) )
return PTR_ERR ( q - > clk ) ;
2014-02-24 18:37:42 +08:00
2015-08-04 10:25:58 -05:00
ret = fsl_qspi_clk_prep_enable ( q ) ;
2014-02-24 18:37:42 +08:00
if ( ret ) {
2015-08-04 10:25:58 -05:00
dev_err ( dev , " can not enable the clock \n " ) ;
2014-10-17 17:14:01 -03:00
goto clk_failed ;
2014-02-24 18:37:42 +08:00
}
/* find the irq */
ret = platform_get_irq ( pdev , 0 ) ;
if ( ret < 0 ) {
2015-02-09 10:07:19 -02:00
dev_err ( dev , " failed to get the irq: %d \n " , ret ) ;
2014-02-24 18:37:42 +08:00
goto irq_failed ;
}
ret = devm_request_irq ( dev , ret ,
fsl_qspi_irq_handler , 0 , pdev - > name , q ) ;
if ( ret ) {
2015-02-09 10:07:19 -02:00
dev_err ( dev , " failed to request irq: %d \n " , ret ) ;
2014-02-24 18:37:42 +08:00
goto irq_failed ;
}
ret = fsl_qspi_nor_setup ( q ) ;
if ( ret )
goto irq_failed ;
if ( of_get_property ( np , " fsl,qspi-has-second-chip " , NULL ) )
2015-01-13 20:14:15 -02:00
q - > has_second_chip = true ;
2014-02-24 18:37:42 +08:00
2015-05-13 14:40:57 -05:00
mutex_init ( & q - > lock ) ;
2014-02-24 18:37:42 +08:00
/* iterate the subnodes. */
for_each_available_child_of_node ( dev - > of_node , np ) {
char modalias [ 40 ] ;
/* skip the holes */
2015-01-13 20:14:15 -02:00
if ( ! q - > has_second_chip )
2014-02-24 18:37:42 +08:00
i * = 2 ;
nor = & q - > nor [ i ] ;
mtd = & q - > mtd [ i ] ;
nor - > mtd = mtd ;
nor - > dev = dev ;
nor - > priv = q ;
mtd - > priv = nor ;
/* fill the hooks */
nor - > read_reg = fsl_qspi_read_reg ;
nor - > write_reg = fsl_qspi_write_reg ;
nor - > read = fsl_qspi_read ;
nor - > write = fsl_qspi_write ;
nor - > erase = fsl_qspi_erase ;
nor - > prepare = fsl_qspi_prep ;
nor - > unprepare = fsl_qspi_unprep ;
2014-10-17 15:31:08 -03:00
ret = of_modalias_node ( np , modalias , sizeof ( modalias ) ) ;
if ( ret < 0 )
2015-05-13 14:40:57 -05:00
goto mutex_failed ;
2014-02-24 18:37:42 +08:00
ret = of_property_read_u32 ( np , " spi-max-frequency " ,
& q - > clk_rate ) ;
if ( ret < 0 )
2015-05-13 14:40:57 -05:00
goto mutex_failed ;
2014-02-24 18:37:42 +08:00
/* set the chip address for READID */
fsl_qspi_set_base_addr ( q , nor ) ;
2014-09-29 11:47:54 +02:00
ret = spi_nor_scan ( nor , modalias , SPI_NOR_QUAD ) ;
2014-02-24 18:37:42 +08:00
if ( ret )
2015-05-13 14:40:57 -05:00
goto mutex_failed ;
2014-02-24 18:37:42 +08:00
ppdata . of_node = np ;
ret = mtd_device_parse_register ( mtd , NULL , & ppdata , NULL , 0 ) ;
if ( ret )
2015-05-13 14:40:57 -05:00
goto mutex_failed ;
2014-02-24 18:37:42 +08:00
/* Set the correct NOR size now. */
if ( q - > nor_size = = 0 ) {
q - > nor_size = mtd - > size ;
/* Map the SPI NOR to accessiable address */
fsl_qspi_set_map_addr ( q ) ;
}
/*
* The TX FIFO is 64 bytes in the Vybrid , but the Page Program
* may writes 265 bytes per time . The write is working in the
* unit of the TX FIFO , not in the unit of the SPI NOR ' s page
* size .
*
* So shrink the spi_nor - > page_size if it is larger then the
* TX FIFO .
*/
if ( nor - > page_size > q - > devtype_data - > txfifo )
nor - > page_size = q - > devtype_data - > txfifo ;
i + + ;
}
/* finish the rest init. */
ret = fsl_qspi_nor_setup_last ( q ) ;
if ( ret )
goto last_init_failed ;
2015-08-04 10:25:58 -05:00
fsl_qspi_clk_disable_unprep ( q ) ;
2014-02-24 18:37:42 +08:00
return 0 ;
last_init_failed :
2015-01-13 20:14:15 -02:00
for ( i = 0 ; i < q - > nor_num ; i + + ) {
/* skip the holes */
if ( ! q - > has_second_chip )
i * = 2 ;
2014-02-24 18:37:42 +08:00
mtd_device_unregister ( & q - > mtd [ i ] ) ;
2015-01-13 20:14:15 -02:00
}
2015-05-13 14:40:57 -05:00
mutex_failed :
mutex_destroy ( & q - > lock ) ;
2014-02-24 18:37:42 +08:00
irq_failed :
2015-08-04 10:25:58 -05:00
fsl_qspi_clk_disable_unprep ( q ) ;
2014-10-17 17:14:01 -03:00
clk_failed :
2015-08-04 10:25:58 -05:00
dev_err ( dev , " Freescale QuadSPI probe failed \n " ) ;
2014-02-24 18:37:42 +08:00
return ret ;
}
static int fsl_qspi_remove ( struct platform_device * pdev )
{
struct fsl_qspi * q = platform_get_drvdata ( pdev ) ;
int i ;
2015-01-13 20:14:15 -02:00
for ( i = 0 ; i < q - > nor_num ; i + + ) {
/* skip the holes */
if ( ! q - > has_second_chip )
i * = 2 ;
2014-02-24 18:37:42 +08:00
mtd_device_unregister ( & q - > mtd [ i ] ) ;
2015-01-13 20:14:15 -02:00
}
2014-02-24 18:37:42 +08:00
/* disable the hardware */
writel ( QUADSPI_MCR_MDIS_MASK , q - > iobase + QUADSPI_MCR ) ;
writel ( 0x0 , q - > iobase + QUADSPI_RSER ) ;
2015-05-13 14:40:57 -05:00
mutex_destroy ( & q - > lock ) ;
mtd: spi-nor: fsl-quadspi: dynamically map memory space for AHB read
QSPI may failed to map enough memory (256MB) for AHB read in
previous implementation, especially in 3G/1G memory layout kernel.
Dynamically map memory to avoid such issue.
This implementation generally map QUADSPI_MAX_IOMAP (default 4MB) memory
for AHB read, it should be enough for common scenarios, and the side
effect (0.6% performance drop) is minor.
Previous implementation
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.16006 s, 15.5 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43149 s, 23.4 MB/s
After applied the patch
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=1K count=32K
32768+0 records in
32768+0 records out
33554432 bytes (34 MB) copied, 2.1743 s, 15.4 MB/s
root@imx6qdlsolo:~# dd if=/dev/mtd0 of=/dev/null bs=32M count=1
1+0 records in
1+0 records out
33554432 bytes (34 MB) copied, 1.43158 s, 23.4 MB/s
Signed-off-by: Han Xu <han.xu@freescale.com>
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
2015-08-04 10:25:22 -05:00
if ( q - > ahb_addr )
iounmap ( q - > ahb_addr ) ;
2014-02-24 18:37:42 +08:00
return 0 ;
}
2015-01-13 04:56:40 +08:00
static int fsl_qspi_suspend ( struct platform_device * pdev , pm_message_t state )
{
return 0 ;
}
static int fsl_qspi_resume ( struct platform_device * pdev )
{
2015-08-04 10:25:58 -05:00
int ret ;
2015-01-13 04:56:40 +08:00
struct fsl_qspi * q = platform_get_drvdata ( pdev ) ;
2015-08-04 10:25:58 -05:00
ret = fsl_qspi_clk_prep_enable ( q ) ;
if ( ret )
return ret ;
2015-01-13 04:56:40 +08:00
fsl_qspi_nor_setup ( q ) ;
fsl_qspi_set_map_addr ( q ) ;
fsl_qspi_nor_setup_last ( q ) ;
2015-08-04 10:25:58 -05:00
fsl_qspi_clk_disable_unprep ( q ) ;
2015-01-13 04:56:40 +08:00
return 0 ;
}
2014-02-24 18:37:42 +08:00
static struct platform_driver fsl_qspi_driver = {
. driver = {
. name = " fsl-quadspi " ,
. bus = & platform_bus_type ,
. of_match_table = fsl_qspi_dt_ids ,
} ,
. probe = fsl_qspi_probe ,
. remove = fsl_qspi_remove ,
2015-01-13 04:56:40 +08:00
. suspend = fsl_qspi_suspend ,
. resume = fsl_qspi_resume ,
2014-02-24 18:37:42 +08:00
} ;
module_platform_driver ( fsl_qspi_driver ) ;
MODULE_DESCRIPTION ( " Freescale QuadSPI Controller Driver " ) ;
MODULE_AUTHOR ( " Freescale Semiconductor Inc. " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;