2015-11-18 06:30:02 +03:00
/*
* Copyright ( c ) 2015 MediaTek Inc .
* Author : Bayi Cheng < bayi . cheng @ mediatek . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/ioport.h>
# include <linux/math64.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
# include <linux/mtd/spi-nor.h>
# define MTK_NOR_CMD_REG 0x00
# define MTK_NOR_CNT_REG 0x04
# define MTK_NOR_RDSR_REG 0x08
# define MTK_NOR_RDATA_REG 0x0c
# define MTK_NOR_RADR0_REG 0x10
# define MTK_NOR_RADR1_REG 0x14
# define MTK_NOR_RADR2_REG 0x18
# define MTK_NOR_WDATA_REG 0x1c
# define MTK_NOR_PRGDATA0_REG 0x20
# define MTK_NOR_PRGDATA1_REG 0x24
# define MTK_NOR_PRGDATA2_REG 0x28
# define MTK_NOR_PRGDATA3_REG 0x2c
# define MTK_NOR_PRGDATA4_REG 0x30
# define MTK_NOR_PRGDATA5_REG 0x34
# define MTK_NOR_SHREG0_REG 0x38
# define MTK_NOR_SHREG1_REG 0x3c
# define MTK_NOR_SHREG2_REG 0x40
# define MTK_NOR_SHREG3_REG 0x44
# define MTK_NOR_SHREG4_REG 0x48
# define MTK_NOR_SHREG5_REG 0x4c
# define MTK_NOR_SHREG6_REG 0x50
# define MTK_NOR_SHREG7_REG 0x54
# define MTK_NOR_SHREG8_REG 0x58
# define MTK_NOR_SHREG9_REG 0x5c
# define MTK_NOR_CFG1_REG 0x60
# define MTK_NOR_CFG2_REG 0x64
# define MTK_NOR_CFG3_REG 0x68
# define MTK_NOR_STATUS0_REG 0x70
# define MTK_NOR_STATUS1_REG 0x74
# define MTK_NOR_STATUS2_REG 0x78
# define MTK_NOR_STATUS3_REG 0x7c
# define MTK_NOR_FLHCFG_REG 0x84
# define MTK_NOR_TIME_REG 0x94
# define MTK_NOR_PP_DATA_REG 0x98
# define MTK_NOR_PREBUF_STUS_REG 0x9c
# define MTK_NOR_DELSEL0_REG 0xa0
# define MTK_NOR_DELSEL1_REG 0xa4
# define MTK_NOR_INTRSTUS_REG 0xa8
# define MTK_NOR_INTREN_REG 0xac
# define MTK_NOR_CHKSUM_CTL_REG 0xb8
# define MTK_NOR_CHKSUM_REG 0xbc
# define MTK_NOR_CMD2_REG 0xc0
# define MTK_NOR_WRPROT_REG 0xc4
# define MTK_NOR_RADR3_REG 0xc8
# define MTK_NOR_DUAL_REG 0xcc
# define MTK_NOR_DELSEL2_REG 0xd0
# define MTK_NOR_DELSEL3_REG 0xd4
# define MTK_NOR_DELSEL4_REG 0xd8
/* commands for mtk nor controller */
# define MTK_NOR_READ_CMD 0x0
# define MTK_NOR_RDSR_CMD 0x2
# define MTK_NOR_PRG_CMD 0x4
# define MTK_NOR_WR_CMD 0x10
# define MTK_NOR_PIO_WR_CMD 0x90
# define MTK_NOR_WRSR_CMD 0x20
# define MTK_NOR_PIO_READ_CMD 0x81
# define MTK_NOR_WR_BUF_ENABLE 0x1
# define MTK_NOR_WR_BUF_DISABLE 0x0
# define MTK_NOR_ENABLE_SF_CMD 0x30
# define MTK_NOR_DUAD_ADDR_EN 0x8
# define MTK_NOR_QUAD_READ_EN 0x4
# define MTK_NOR_DUAL_ADDR_EN 0x2
# define MTK_NOR_DUAL_READ_EN 0x1
# define MTK_NOR_DUAL_DISABLE 0x0
# define MTK_NOR_FAST_READ 0x1
# define SFLASH_WRBUF_SIZE 128
/* Can shift up to 48 bits (6 bytes) of TX/RX */
# define MTK_NOR_MAX_RX_TX_SHIFT 6
/* can shift up to 56 bits (7 bytes) transfer by MTK_NOR_PRG_CMD */
# define MTK_NOR_MAX_SHIFT 7
2017-04-05 11:37:42 +03:00
/* nor controller 4-byte address mode enable bit */
# define MTK_NOR_4B_ADDR_EN BIT(4)
2015-11-18 06:30:02 +03:00
/* Helpers for accessing the program data / shift data registers */
# define MTK_NOR_PRG_REG(n) (MTK_NOR_PRGDATA0_REG + 4 * (n))
# define MTK_NOR_SHREG(n) (MTK_NOR_SHREG0_REG + 4 * (n))
2017-12-18 04:47:35 +03:00
struct mtk_nor {
2015-11-18 06:30:02 +03:00
struct spi_nor nor ;
struct device * dev ;
void __iomem * base ; /* nor flash base address */
struct clk * spi_clk ;
struct clk * nor_clk ;
} ;
2017-12-18 04:47:35 +03:00
static void mtk_nor_set_read_mode ( struct mtk_nor * mtk_nor )
2015-11-18 06:30:02 +03:00
{
2017-12-18 04:47:35 +03:00
struct spi_nor * nor = & mtk_nor - > nor ;
2015-11-18 06:30:02 +03:00
2017-04-25 23:08:46 +03:00
switch ( nor - > read_proto ) {
case SNOR_PROTO_1_1_1 :
2017-12-18 04:47:35 +03:00
writeb ( nor - > read_opcode , mtk_nor - > base +
2015-11-18 06:30:02 +03:00
MTK_NOR_PRGDATA3_REG ) ;
2017-12-18 04:47:35 +03:00
writeb ( MTK_NOR_FAST_READ , mtk_nor - > base +
2015-11-18 06:30:02 +03:00
MTK_NOR_CFG1_REG ) ;
break ;
2017-04-25 23:08:46 +03:00
case SNOR_PROTO_1_1_2 :
2017-12-18 04:47:35 +03:00
writeb ( nor - > read_opcode , mtk_nor - > base +
2015-11-18 06:30:02 +03:00
MTK_NOR_PRGDATA3_REG ) ;
2017-12-18 04:47:35 +03:00
writeb ( MTK_NOR_DUAL_READ_EN , mtk_nor - > base +
2015-11-18 06:30:02 +03:00
MTK_NOR_DUAL_REG ) ;
break ;
2017-04-25 23:08:46 +03:00
case SNOR_PROTO_1_1_4 :
2017-12-18 04:47:35 +03:00
writeb ( nor - > read_opcode , mtk_nor - > base +
2015-11-18 06:30:02 +03:00
MTK_NOR_PRGDATA4_REG ) ;
2017-12-18 04:47:35 +03:00
writeb ( MTK_NOR_QUAD_READ_EN , mtk_nor - > base +
2015-11-18 06:30:02 +03:00
MTK_NOR_DUAL_REG ) ;
break ;
default :
2017-12-18 04:47:35 +03:00
writeb ( MTK_NOR_DUAL_DISABLE , mtk_nor - > base +
2015-11-18 06:30:02 +03:00
MTK_NOR_DUAL_REG ) ;
break ;
}
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_execute_cmd ( struct mtk_nor * mtk_nor , u8 cmdval )
2015-11-18 06:30:02 +03:00
{
int reg ;
u8 val = cmdval & 0x1f ;
2017-12-18 04:47:35 +03:00
writeb ( cmdval , mtk_nor - > base + MTK_NOR_CMD_REG ) ;
return readl_poll_timeout ( mtk_nor - > base + MTK_NOR_CMD_REG , reg ,
2015-11-18 06:30:02 +03:00
! ( reg & val ) , 100 , 10000 ) ;
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_do_tx_rx ( struct mtk_nor * mtk_nor , u8 op ,
u8 * tx , int txlen , u8 * rx , int rxlen )
2015-11-18 06:30:02 +03:00
{
int len = 1 + txlen + rxlen ;
int i , ret , idx ;
if ( len > MTK_NOR_MAX_SHIFT )
return - EINVAL ;
2017-12-18 04:47:35 +03:00
writeb ( len * 8 , mtk_nor - > base + MTK_NOR_CNT_REG ) ;
2015-11-18 06:30:02 +03:00
/* start at PRGDATA5, go down to PRGDATA0 */
idx = MTK_NOR_MAX_RX_TX_SHIFT - 1 ;
/* opcode */
2017-12-18 04:47:35 +03:00
writeb ( op , mtk_nor - > base + MTK_NOR_PRG_REG ( idx ) ) ;
2015-11-18 06:30:02 +03:00
idx - - ;
/* program TX data */
for ( i = 0 ; i < txlen ; i + + , idx - - )
2017-12-18 04:47:35 +03:00
writeb ( tx [ i ] , mtk_nor - > base + MTK_NOR_PRG_REG ( idx ) ) ;
2015-11-18 06:30:02 +03:00
/* clear out rest of TX registers */
while ( idx > = 0 ) {
2017-12-18 04:47:35 +03:00
writeb ( 0 , mtk_nor - > base + MTK_NOR_PRG_REG ( idx ) ) ;
2015-11-18 06:30:02 +03:00
idx - - ;
}
2017-12-18 04:47:35 +03:00
ret = mtk_nor_execute_cmd ( mtk_nor , MTK_NOR_PRG_CMD ) ;
2015-11-18 06:30:02 +03:00
if ( ret )
return ret ;
/* restart at first RX byte */
idx = rxlen - 1 ;
/* read out RX data */
for ( i = 0 ; i < rxlen ; i + + , idx - - )
2017-12-18 04:47:35 +03:00
rx [ i ] = readb ( mtk_nor - > base + MTK_NOR_SHREG ( idx ) ) ;
2015-11-18 06:30:02 +03:00
return 0 ;
}
/* Do a WRSR (Write Status Register) command */
2017-12-18 04:47:35 +03:00
static int mtk_nor_wr_sr ( struct mtk_nor * mtk_nor , u8 sr )
2015-11-18 06:30:02 +03:00
{
2017-12-18 04:47:35 +03:00
writeb ( sr , mtk_nor - > base + MTK_NOR_PRGDATA5_REG ) ;
writeb ( 8 , mtk_nor - > base + MTK_NOR_CNT_REG ) ;
return mtk_nor_execute_cmd ( mtk_nor , MTK_NOR_WRSR_CMD ) ;
2015-11-18 06:30:02 +03:00
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_write_buffer_enable ( struct mtk_nor * mtk_nor )
2015-11-18 06:30:02 +03:00
{
u8 reg ;
/* the bit0 of MTK_NOR_CFG2_REG is pre-fetch buffer
* 0 : pre - fetch buffer use for read
* 1 : pre - fetch buffer use for page program
*/
2017-12-18 04:47:35 +03:00
writel ( MTK_NOR_WR_BUF_ENABLE , mtk_nor - > base + MTK_NOR_CFG2_REG ) ;
return readb_poll_timeout ( mtk_nor - > base + MTK_NOR_CFG2_REG , reg ,
2015-11-18 06:30:02 +03:00
0x01 = = ( reg & 0x01 ) , 100 , 10000 ) ;
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_write_buffer_disable ( struct mtk_nor * mtk_nor )
2015-11-18 06:30:02 +03:00
{
u8 reg ;
2017-12-18 04:47:35 +03:00
writel ( MTK_NOR_WR_BUF_DISABLE , mtk_nor - > base + MTK_NOR_CFG2_REG ) ;
return readb_poll_timeout ( mtk_nor - > base + MTK_NOR_CFG2_REG , reg ,
2015-11-18 06:30:02 +03:00
MTK_NOR_WR_BUF_DISABLE = = ( reg & 0x1 ) , 100 ,
10000 ) ;
}
2017-12-18 04:47:35 +03:00
static void mtk_nor_set_addr_width ( struct mtk_nor * mtk_nor )
2017-04-05 11:37:42 +03:00
{
u8 val ;
2017-12-18 04:47:35 +03:00
struct spi_nor * nor = & mtk_nor - > nor ;
2017-04-05 11:37:42 +03:00
2017-12-18 04:47:35 +03:00
val = readb ( mtk_nor - > base + MTK_NOR_DUAL_REG ) ;
2017-04-05 11:37:42 +03:00
switch ( nor - > addr_width ) {
case 3 :
val & = ~ MTK_NOR_4B_ADDR_EN ;
break ;
case 4 :
val | = MTK_NOR_4B_ADDR_EN ;
break ;
default :
2017-12-18 04:47:35 +03:00
dev_warn ( mtk_nor - > dev , " Unexpected address width %u. \n " ,
2017-04-05 11:37:42 +03:00
nor - > addr_width ) ;
break ;
}
2017-12-18 04:47:35 +03:00
writeb ( val , mtk_nor - > base + MTK_NOR_DUAL_REG ) ;
2017-04-05 11:37:42 +03:00
}
2017-12-18 04:47:35 +03:00
static void mtk_nor_set_addr ( struct mtk_nor * mtk_nor , u32 addr )
2015-11-18 06:30:02 +03:00
{
int i ;
2017-12-18 04:47:35 +03:00
mtk_nor_set_addr_width ( mtk_nor ) ;
2017-04-05 11:37:42 +03:00
2015-11-18 06:30:02 +03:00
for ( i = 0 ; i < 3 ; i + + ) {
2017-12-18 04:47:35 +03:00
writeb ( addr & 0xff , mtk_nor - > base + MTK_NOR_RADR0_REG + i * 4 ) ;
2015-11-18 06:30:02 +03:00
addr > > = 8 ;
}
/* Last register is non-contiguous */
2017-12-18 04:47:35 +03:00
writeb ( addr & 0xff , mtk_nor - > base + MTK_NOR_RADR3_REG ) ;
2015-11-18 06:30:02 +03:00
}
2017-12-18 04:47:35 +03:00
static ssize_t mtk_nor_read ( struct spi_nor * nor , loff_t from , size_t length ,
u_char * buffer )
2015-11-18 06:30:02 +03:00
{
int i , ret ;
int addr = ( int ) from ;
u8 * buf = ( u8 * ) buffer ;
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor = nor - > priv ;
2015-11-18 06:30:02 +03:00
/* set mode for fast read mode ,dual mode or quad mode */
2017-12-18 04:47:35 +03:00
mtk_nor_set_read_mode ( mtk_nor ) ;
mtk_nor_set_addr ( mtk_nor , addr ) ;
2015-11-18 06:30:02 +03:00
2016-05-06 03:31:53 +03:00
for ( i = 0 ; i < length ; i + + ) {
2017-12-18 04:47:35 +03:00
ret = mtk_nor_execute_cmd ( mtk_nor , MTK_NOR_PIO_READ_CMD ) ;
2015-11-18 06:30:02 +03:00
if ( ret < 0 )
return ret ;
2017-12-18 04:47:35 +03:00
buf [ i ] = readb ( mtk_nor - > base + MTK_NOR_RDATA_REG ) ;
2015-11-18 06:30:02 +03:00
}
2016-05-06 03:31:50 +03:00
return length ;
2015-11-18 06:30:02 +03:00
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_write_single_byte ( struct mtk_nor * mtk_nor ,
int addr , int length , u8 * data )
2015-11-18 06:30:02 +03:00
{
int i , ret ;
2017-12-18 04:47:35 +03:00
mtk_nor_set_addr ( mtk_nor , addr ) ;
2015-11-18 06:30:02 +03:00
for ( i = 0 ; i < length ; i + + ) {
2017-12-18 04:47:35 +03:00
writeb ( * data + + , mtk_nor - > base + MTK_NOR_WDATA_REG ) ;
ret = mtk_nor_execute_cmd ( mtk_nor , MTK_NOR_PIO_WR_CMD ) ;
2015-11-18 06:30:02 +03:00
if ( ret < 0 )
return ret ;
}
return 0 ;
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_write_buffer ( struct mtk_nor * mtk_nor , int addr ,
const u8 * buf )
2015-11-18 06:30:02 +03:00
{
int i , bufidx , data ;
2017-12-18 04:47:35 +03:00
mtk_nor_set_addr ( mtk_nor , addr ) ;
2015-11-18 06:30:02 +03:00
bufidx = 0 ;
for ( i = 0 ; i < SFLASH_WRBUF_SIZE ; i + = 4 ) {
data = buf [ bufidx + 3 ] < < 24 | buf [ bufidx + 2 ] < < 16 |
buf [ bufidx + 1 ] < < 8 | buf [ bufidx ] ;
bufidx + = 4 ;
2017-12-18 04:47:35 +03:00
writel ( data , mtk_nor - > base + MTK_NOR_PP_DATA_REG ) ;
2015-11-18 06:30:02 +03:00
}
2017-12-18 04:47:35 +03:00
return mtk_nor_execute_cmd ( mtk_nor , MTK_NOR_WR_CMD ) ;
2015-11-18 06:30:02 +03:00
}
2017-12-18 04:47:35 +03:00
static ssize_t mtk_nor_write ( struct spi_nor * nor , loff_t to , size_t len ,
const u_char * buf )
2015-11-18 06:30:02 +03:00
{
int ret ;
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor = nor - > priv ;
2016-05-06 03:31:50 +03:00
size_t i ;
2015-11-18 06:30:02 +03:00
2017-12-18 04:47:35 +03:00
ret = mtk_nor_write_buffer_enable ( mtk_nor ) ;
2016-05-06 03:31:47 +03:00
if ( ret < 0 ) {
2017-12-18 04:47:35 +03:00
dev_warn ( mtk_nor - > dev , " write buffer enable failed! \n " ) ;
2016-05-06 03:31:47 +03:00
return ret ;
}
2015-11-18 06:30:02 +03:00
2016-05-06 03:31:50 +03:00
for ( i = 0 ; i + SFLASH_WRBUF_SIZE < = len ; i + = SFLASH_WRBUF_SIZE ) {
2017-12-18 04:47:35 +03:00
ret = mtk_nor_write_buffer ( mtk_nor , to , buf ) ;
2016-05-06 03:31:47 +03:00
if ( ret < 0 ) {
2017-12-18 04:47:35 +03:00
dev_err ( mtk_nor - > dev , " write buffer failed! \n " ) ;
2016-05-06 03:31:47 +03:00
return ret ;
}
2015-11-18 06:30:02 +03:00
to + = SFLASH_WRBUF_SIZE ;
buf + = SFLASH_WRBUF_SIZE ;
}
2017-12-18 04:47:35 +03:00
ret = mtk_nor_write_buffer_disable ( mtk_nor ) ;
2016-05-06 03:31:47 +03:00
if ( ret < 0 ) {
2017-12-18 04:47:35 +03:00
dev_warn ( mtk_nor - > dev , " write buffer disable failed! \n " ) ;
2016-05-06 03:31:47 +03:00
return ret ;
}
2015-11-18 06:30:02 +03:00
2016-05-06 03:31:50 +03:00
if ( i < len ) {
2017-12-18 04:47:35 +03:00
ret = mtk_nor_write_single_byte ( mtk_nor , to ,
( int ) ( len - i ) , ( u8 * ) buf ) ;
2016-05-06 03:31:47 +03:00
if ( ret < 0 ) {
2017-12-18 04:47:35 +03:00
dev_err ( mtk_nor - > dev , " write single byte failed! \n " ) ;
2016-05-06 03:31:47 +03:00
return ret ;
}
2015-11-18 06:30:02 +03:00
}
2016-05-06 03:31:47 +03:00
2016-05-06 03:31:50 +03:00
return len ;
2015-11-18 06:30:02 +03:00
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_read_reg ( struct spi_nor * nor , u8 opcode , u8 * buf , int len )
2015-11-18 06:30:02 +03:00
{
int ret ;
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor = nor - > priv ;
2015-11-18 06:30:02 +03:00
switch ( opcode ) {
case SPINOR_OP_RDSR :
2017-12-18 04:47:35 +03:00
ret = mtk_nor_execute_cmd ( mtk_nor , MTK_NOR_RDSR_CMD ) ;
2015-11-18 06:30:02 +03:00
if ( ret < 0 )
return ret ;
if ( len = = 1 )
2017-12-18 04:47:35 +03:00
* buf = readb ( mtk_nor - > base + MTK_NOR_RDSR_REG ) ;
2015-11-18 06:30:02 +03:00
else
2017-12-18 04:47:35 +03:00
dev_err ( mtk_nor - > dev , " len should be 1 for read status! \n " ) ;
2015-11-18 06:30:02 +03:00
break ;
default :
2017-12-18 04:47:35 +03:00
ret = mtk_nor_do_tx_rx ( mtk_nor , opcode , NULL , 0 , buf , len ) ;
2015-11-18 06:30:02 +03:00
break ;
}
return ret ;
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_write_reg ( struct spi_nor * nor , u8 opcode , u8 * buf ,
int len )
2015-11-18 06:30:02 +03:00
{
int ret ;
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor = nor - > priv ;
2015-11-18 06:30:02 +03:00
switch ( opcode ) {
case SPINOR_OP_WRSR :
/* We only handle 1 byte */
2017-12-18 04:47:35 +03:00
ret = mtk_nor_wr_sr ( mtk_nor , * buf ) ;
2015-11-18 06:30:02 +03:00
break ;
default :
2017-12-18 04:47:35 +03:00
ret = mtk_nor_do_tx_rx ( mtk_nor , opcode , buf , len , NULL , 0 ) ;
2015-11-18 06:30:02 +03:00
if ( ret )
2017-12-18 04:47:35 +03:00
dev_warn ( mtk_nor - > dev , " write reg failure! \n " ) ;
2015-11-18 06:30:02 +03:00
break ;
}
return ret ;
}
2017-12-18 04:47:35 +03:00
static void mtk_nor_disable_clk ( struct mtk_nor * mtk_nor )
2017-09-21 15:45:06 +03:00
{
2017-12-18 04:47:35 +03:00
clk_disable_unprepare ( mtk_nor - > spi_clk ) ;
clk_disable_unprepare ( mtk_nor - > nor_clk ) ;
2017-09-21 15:45:06 +03:00
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_enable_clk ( struct mtk_nor * mtk_nor )
2017-09-21 15:45:06 +03:00
{
int ret ;
2017-12-18 04:47:35 +03:00
ret = clk_prepare_enable ( mtk_nor - > spi_clk ) ;
2017-09-21 15:45:06 +03:00
if ( ret )
return ret ;
2017-12-18 04:47:35 +03:00
ret = clk_prepare_enable ( mtk_nor - > nor_clk ) ;
2017-09-21 15:45:06 +03:00
if ( ret ) {
2017-12-18 04:47:35 +03:00
clk_disable_unprepare ( mtk_nor - > spi_clk ) ;
2017-09-21 15:45:06 +03:00
return ret ;
}
return 0 ;
}
2017-12-18 04:47:35 +03:00
static int mtk_nor_init ( struct mtk_nor * mtk_nor ,
2016-01-15 16:46:29 +03:00
struct device_node * flash_node )
2015-11-18 06:30:02 +03:00
{
2017-04-25 23:08:46 +03:00
const struct spi_nor_hwcaps hwcaps = {
. mask = SNOR_HWCAPS_READ_FAST |
SNOR_HWCAPS_READ_1_1_2 |
SNOR_HWCAPS_PP ,
} ;
2015-11-18 06:30:02 +03:00
int ret ;
struct spi_nor * nor ;
/* initialize controller to accept commands */
2017-12-18 04:47:35 +03:00
writel ( MTK_NOR_ENABLE_SF_CMD , mtk_nor - > base + MTK_NOR_WRPROT_REG ) ;
2015-11-18 06:30:02 +03:00
2017-12-18 04:47:35 +03:00
nor = & mtk_nor - > nor ;
nor - > dev = mtk_nor - > dev ;
nor - > priv = mtk_nor ;
2015-11-18 06:30:02 +03:00
spi_nor_set_flash_node ( nor , flash_node ) ;
/* fill the hooks to spi nor */
2017-12-18 04:47:35 +03:00
nor - > read = mtk_nor_read ;
nor - > read_reg = mtk_nor_read_reg ;
nor - > write = mtk_nor_write ;
nor - > write_reg = mtk_nor_write_reg ;
2015-11-18 06:30:02 +03:00
nor - > mtd . name = " mtk_nor " ;
/* initialized with NULL */
2017-04-25 23:08:46 +03:00
ret = spi_nor_scan ( nor , NULL , & hwcaps ) ;
2015-11-18 06:30:02 +03:00
if ( ret )
return ret ;
return mtd_device_register ( & nor - > mtd , NULL , 0 ) ;
}
static int mtk_nor_drv_probe ( struct platform_device * pdev )
{
struct device_node * flash_np ;
struct resource * res ;
int ret ;
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor ;
2015-11-18 06:30:02 +03:00
if ( ! pdev - > dev . of_node ) {
dev_err ( & pdev - > dev , " No DT found \n " ) ;
return - EINVAL ;
}
2017-12-18 04:47:35 +03:00
mtk_nor = devm_kzalloc ( & pdev - > dev , sizeof ( * mtk_nor ) , GFP_KERNEL ) ;
if ( ! mtk_nor )
2015-11-18 06:30:02 +03:00
return - ENOMEM ;
2017-12-18 04:47:35 +03:00
platform_set_drvdata ( pdev , mtk_nor ) ;
2015-11-18 06:30:02 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2017-12-18 04:47:35 +03:00
mtk_nor - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( mtk_nor - > base ) )
return PTR_ERR ( mtk_nor - > base ) ;
2015-11-18 06:30:02 +03:00
2017-12-18 04:47:35 +03:00
mtk_nor - > spi_clk = devm_clk_get ( & pdev - > dev , " spi " ) ;
if ( IS_ERR ( mtk_nor - > spi_clk ) )
return PTR_ERR ( mtk_nor - > spi_clk ) ;
2015-11-18 06:30:02 +03:00
2017-12-18 04:47:35 +03:00
mtk_nor - > nor_clk = devm_clk_get ( & pdev - > dev , " sf " ) ;
if ( IS_ERR ( mtk_nor - > nor_clk ) )
return PTR_ERR ( mtk_nor - > nor_clk ) ;
2015-11-18 06:30:02 +03:00
2017-12-18 04:47:35 +03:00
mtk_nor - > dev = & pdev - > dev ;
2017-09-21 15:45:06 +03:00
2017-12-18 04:47:35 +03:00
ret = mtk_nor_enable_clk ( mtk_nor ) ;
2015-11-18 06:30:02 +03:00
if ( ret )
return ret ;
/* only support one attached flash */
flash_np = of_get_next_available_child ( pdev - > dev . of_node , NULL ) ;
if ( ! flash_np ) {
dev_err ( & pdev - > dev , " no SPI flash device to configure \n " ) ;
ret = - ENODEV ;
goto nor_free ;
}
2017-12-18 04:47:35 +03:00
ret = mtk_nor_init ( mtk_nor , flash_np ) ;
2015-11-18 06:30:02 +03:00
nor_free :
2017-09-21 15:45:06 +03:00
if ( ret )
2017-12-18 04:47:35 +03:00
mtk_nor_disable_clk ( mtk_nor ) ;
2017-09-21 15:45:06 +03:00
2015-11-18 06:30:02 +03:00
return ret ;
}
static int mtk_nor_drv_remove ( struct platform_device * pdev )
{
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor = platform_get_drvdata ( pdev ) ;
2015-11-18 06:30:02 +03:00
2017-12-18 04:47:35 +03:00
mtk_nor_disable_clk ( mtk_nor ) ;
2017-09-21 15:45:06 +03:00
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int mtk_nor_suspend ( struct device * dev )
{
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor = dev_get_drvdata ( dev ) ;
2017-09-21 15:45:06 +03:00
2017-12-18 04:47:35 +03:00
mtk_nor_disable_clk ( mtk_nor ) ;
2017-09-21 15:45:06 +03:00
2015-11-18 06:30:02 +03:00
return 0 ;
}
2017-09-21 15:45:06 +03:00
static int mtk_nor_resume ( struct device * dev )
{
2017-12-18 04:47:35 +03:00
struct mtk_nor * mtk_nor = dev_get_drvdata ( dev ) ;
2017-09-21 15:45:06 +03:00
2017-12-18 04:47:35 +03:00
return mtk_nor_enable_clk ( mtk_nor ) ;
2017-09-21 15:45:06 +03:00
}
static const struct dev_pm_ops mtk_nor_dev_pm_ops = {
. suspend = mtk_nor_suspend ,
. resume = mtk_nor_resume ,
} ;
# define MTK_NOR_DEV_PM_OPS (&mtk_nor_dev_pm_ops)
# else
# define MTK_NOR_DEV_PM_OPS NULL
# endif
2015-11-18 06:30:02 +03:00
static const struct of_device_id mtk_nor_of_ids [ ] = {
{ . compatible = " mediatek,mt8173-nor " } ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , mtk_nor_of_ids ) ;
static struct platform_driver mtk_nor_driver = {
. probe = mtk_nor_drv_probe ,
. remove = mtk_nor_drv_remove ,
. driver = {
. name = " mtk-nor " ,
2017-09-21 15:45:06 +03:00
. pm = MTK_NOR_DEV_PM_OPS ,
2015-11-18 06:30:02 +03:00
. of_match_table = mtk_nor_of_ids ,
} ,
} ;
module_platform_driver ( mtk_nor_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " MediaTek SPI NOR Flash Driver " ) ;