2012-01-12 17:38:57 +04:00
/*
* SMI ( Serial Memory Controller ) device driver for Serial NOR Flash on
* SPEAr platform
2015-05-22 20:42:01 +03:00
* The serial nor interface is largely based on m25p80 . c , however the SPI
* interface has been replaced by SMI .
2012-01-12 17:38:57 +04:00
*
* Copyright © 2010 STMicroelectronics .
* Ashish Priyadarshi
2014-04-19 02:07:16 +04:00
* Shiraz Hashim < shiraz . linux . kernel @ gmail . com >
2012-01-12 17:38:57 +04:00
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/jiffies.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/param.h>
# include <linux/platform_device.h>
2012-07-02 09:58:45 +04:00
# include <linux/pm.h>
2012-01-12 17:38:57 +04:00
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
# include <linux/mtd/spear_smi.h>
# include <linux/mutex.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/wait.h>
2012-03-16 14:42:11 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
2012-01-12 17:38:57 +04:00
/* SMI clock rate */
# define SMI_MAX_CLOCK_FREQ 50000000 /* 50 MHz */
/* MAX time out to safely come out of a erase or write busy conditions */
# define SMI_PROBE_TIMEOUT (HZ / 10)
# define SMI_MAX_TIME_OUT (3 * HZ)
/* timeout for command completion */
# define SMI_CMD_TIMEOUT (HZ / 10)
/* registers of smi */
# define SMI_CR1 0x0 /* SMI control register 1 */
# define SMI_CR2 0x4 /* SMI control register 2 */
# define SMI_SR 0x8 /* SMI status register */
# define SMI_TR 0xC /* SMI transmit register */
# define SMI_RR 0x10 /* SMI receive register */
/* defines for control_reg 1 */
# define BANK_EN (0xF << 0) /* enables all banks */
# define DSEL_TIME (0x6 << 4) /* Deselect time 6 + 1 SMI_CK periods */
# define SW_MODE (0x1 << 28) /* enables SW Mode */
# define WB_MODE (0x1 << 29) /* Write Burst Mode */
# define FAST_MODE (0x1 << 15) /* Fast Mode */
# define HOLD1 (0x1 << 16) /* Clock Hold period selection */
/* defines for control_reg 2 */
# define SEND (0x1 << 7) /* Send data */
# define TFIE (0x1 << 8) /* Transmission Flag Interrupt Enable */
# define WCIE (0x1 << 9) /* Write Complete Interrupt Enable */
# define RD_STATUS_REG (0x1 << 10) /* reads status reg */
# define WE (0x1 << 11) /* Write Enable */
# define TX_LEN_SHIFT 0
# define RX_LEN_SHIFT 4
# define BANK_SHIFT 12
/* defines for status register */
# define SR_WIP 0x1 /* Write in progress */
# define SR_WEL 0x2 /* Write enable latch */
# define SR_BP0 0x4 /* Block protect 0 */
# define SR_BP1 0x8 /* Block protect 1 */
# define SR_BP2 0x10 /* Block protect 2 */
# define SR_SRWD 0x80 /* SR write protect */
# define TFF 0x100 /* Transfer Finished Flag */
# define WCF 0x200 /* Transfer Finished Flag */
# define ERF1 0x400 /* Forbidden Write Request */
# define ERF2 0x800 /* Forbidden Access */
# define WM_SHIFT 12
/* flash opcodes */
# define OPCODE_RDID 0x9f /* Read JEDEC ID */
/* Flash Device Ids maintenance section */
/* data structure to maintain flash ids from different vendors */
struct flash_device {
char * name ;
u8 erase_cmd ;
u32 device_id ;
u32 pagesize ;
unsigned long sectorsize ;
unsigned long size_in_bytes ;
} ;
# define FLASH_ID(n, es, id, psize, ssize, size) \
{ \
. name = n , \
. erase_cmd = es , \
. device_id = id , \
. pagesize = psize , \
. sectorsize = ssize , \
. size_in_bytes = size \
}
static struct flash_device flash_devices [ ] = {
FLASH_ID ( " st m25p16 " , 0xd8 , 0x00152020 , 0x100 , 0x10000 , 0x200000 ) ,
FLASH_ID ( " st m25p32 " , 0xd8 , 0x00162020 , 0x100 , 0x10000 , 0x400000 ) ,
FLASH_ID ( " st m25p64 " , 0xd8 , 0x00172020 , 0x100 , 0x10000 , 0x800000 ) ,
FLASH_ID ( " st m25p128 " , 0xd8 , 0x00182020 , 0x100 , 0x40000 , 0x1000000 ) ,
FLASH_ID ( " st m25p05 " , 0xd8 , 0x00102020 , 0x80 , 0x8000 , 0x10000 ) ,
FLASH_ID ( " st m25p10 " , 0xd8 , 0x00112020 , 0x80 , 0x8000 , 0x20000 ) ,
FLASH_ID ( " st m25p20 " , 0xd8 , 0x00122020 , 0x100 , 0x10000 , 0x40000 ) ,
FLASH_ID ( " st m25p40 " , 0xd8 , 0x00132020 , 0x100 , 0x10000 , 0x80000 ) ,
FLASH_ID ( " st m25p80 " , 0xd8 , 0x00142020 , 0x100 , 0x10000 , 0x100000 ) ,
FLASH_ID ( " st m45pe10 " , 0xd8 , 0x00114020 , 0x100 , 0x10000 , 0x20000 ) ,
FLASH_ID ( " st m45pe20 " , 0xd8 , 0x00124020 , 0x100 , 0x10000 , 0x40000 ) ,
FLASH_ID ( " st m45pe40 " , 0xd8 , 0x00134020 , 0x100 , 0x10000 , 0x80000 ) ,
FLASH_ID ( " st m45pe80 " , 0xd8 , 0x00144020 , 0x100 , 0x10000 , 0x100000 ) ,
FLASH_ID ( " sp s25fl004 " , 0xd8 , 0x00120201 , 0x100 , 0x10000 , 0x80000 ) ,
FLASH_ID ( " sp s25fl008 " , 0xd8 , 0x00130201 , 0x100 , 0x10000 , 0x100000 ) ,
FLASH_ID ( " sp s25fl016 " , 0xd8 , 0x00140201 , 0x100 , 0x10000 , 0x200000 ) ,
FLASH_ID ( " sp s25fl032 " , 0xd8 , 0x00150201 , 0x100 , 0x10000 , 0x400000 ) ,
FLASH_ID ( " sp s25fl064 " , 0xd8 , 0x00160201 , 0x100 , 0x10000 , 0x800000 ) ,
FLASH_ID ( " atmel 25f512 " , 0x52 , 0x0065001F , 0x80 , 0x8000 , 0x10000 ) ,
FLASH_ID ( " atmel 25f1024 " , 0x52 , 0x0060001F , 0x100 , 0x8000 , 0x20000 ) ,
FLASH_ID ( " atmel 25f2048 " , 0x52 , 0x0063001F , 0x100 , 0x10000 , 0x40000 ) ,
FLASH_ID ( " atmel 25f4096 " , 0x52 , 0x0064001F , 0x100 , 0x10000 , 0x80000 ) ,
FLASH_ID ( " atmel 25fs040 " , 0xd7 , 0x0004661F , 0x100 , 0x10000 , 0x80000 ) ,
FLASH_ID ( " mac 25l512 " , 0xd8 , 0x001020C2 , 0x010 , 0x10000 , 0x10000 ) ,
FLASH_ID ( " mac 25l1005 " , 0xd8 , 0x001120C2 , 0x010 , 0x10000 , 0x20000 ) ,
FLASH_ID ( " mac 25l2005 " , 0xd8 , 0x001220C2 , 0x010 , 0x10000 , 0x40000 ) ,
FLASH_ID ( " mac 25l4005 " , 0xd8 , 0x001320C2 , 0x010 , 0x10000 , 0x80000 ) ,
FLASH_ID ( " mac 25l4005a " , 0xd8 , 0x001320C2 , 0x010 , 0x10000 , 0x80000 ) ,
FLASH_ID ( " mac 25l8005 " , 0xd8 , 0x001420C2 , 0x010 , 0x10000 , 0x100000 ) ,
FLASH_ID ( " mac 25l1605 " , 0xd8 , 0x001520C2 , 0x100 , 0x10000 , 0x200000 ) ,
FLASH_ID ( " mac 25l1605a " , 0xd8 , 0x001520C2 , 0x010 , 0x10000 , 0x200000 ) ,
FLASH_ID ( " mac 25l3205 " , 0xd8 , 0x001620C2 , 0x100 , 0x10000 , 0x400000 ) ,
FLASH_ID ( " mac 25l3205a " , 0xd8 , 0x001620C2 , 0x100 , 0x10000 , 0x400000 ) ,
FLASH_ID ( " mac 25l6405 " , 0xd8 , 0x001720C2 , 0x100 , 0x10000 , 0x800000 ) ,
} ;
/* Define spear specific structures */
struct spear_snor_flash ;
/**
* struct spear_smi - Structure for SMI Device
*
* @ clk : functional clock
* @ status : current status register of SMI .
* @ clk_rate : functional clock rate of SMI ( default : SMI_MAX_CLOCK_FREQ )
* @ lock : lock to prevent parallel access of SMI .
* @ io_base : base address for registers of SMI .
* @ pdev : platform device
* @ cmd_complete : queue to wait for command completion of NOR - flash .
* @ num_flashes : number of flashes actually present on board .
* @ flash : separate structure for each Serial NOR - flash attached to SMI .
*/
struct spear_smi {
struct clk * clk ;
u32 status ;
unsigned long clk_rate ;
struct mutex lock ;
void __iomem * io_base ;
struct platform_device * pdev ;
wait_queue_head_t cmd_complete ;
u32 num_flashes ;
struct spear_snor_flash * flash [ MAX_NUM_FLASH_CHIP ] ;
} ;
/**
* struct spear_snor_flash - Structure for Serial NOR Flash
*
* @ bank : Bank number ( 0 , 1 , 2 , 3 ) for each NOR - flash .
* @ dev_id : Device ID of NOR - flash .
* @ lock : lock to manage flash read , write and erase operations
* @ mtd : MTD info for each NOR - flash .
* @ num_parts : Total number of partition in each bank of NOR - flash .
* @ parts : Partition info for each bank of NOR - flash .
* @ page_size : Page size of NOR - flash .
* @ base_addr : Base address of NOR - flash .
* @ erase_cmd : erase command may vary on different flash types
* @ fast_mode : flash supports read in fast mode
*/
struct spear_snor_flash {
u32 bank ;
u32 dev_id ;
struct mutex lock ;
struct mtd_info mtd ;
u32 num_parts ;
struct mtd_partition * parts ;
u32 page_size ;
void __iomem * base_addr ;
u8 erase_cmd ;
u8 fast_mode ;
} ;
static inline struct spear_snor_flash * get_flash_data ( struct mtd_info * mtd )
{
return container_of ( mtd , struct spear_snor_flash , mtd ) ;
}
/**
* spear_smi_read_sr - Read status register of flash through SMI
* @ dev : structure of SMI information .
* @ bank : bank to which flash is connected
*
* This routine will return the status register of the flash chip present at the
* given bank .
*/
static int spear_smi_read_sr ( struct spear_smi * dev , u32 bank )
{
int ret ;
u32 ctrlreg1 ;
mutex_lock ( & dev - > lock ) ;
dev - > status = 0 ; /* Will be set in interrupt handler */
ctrlreg1 = readl ( dev - > io_base + SMI_CR1 ) ;
/* program smi in hw mode */
writel ( ctrlreg1 & ~ ( SW_MODE | WB_MODE ) , dev - > io_base + SMI_CR1 ) ;
/* performing a rsr instruction in hw mode */
writel ( ( bank < < BANK_SHIFT ) | RD_STATUS_REG | TFIE ,
dev - > io_base + SMI_CR2 ) ;
/* wait for tff */
ret = wait_event_interruptible_timeout ( dev - > cmd_complete ,
dev - > status & TFF , SMI_CMD_TIMEOUT ) ;
/* copy dev->status (lower 16 bits) in order to release lock */
if ( ret > 0 )
ret = dev - > status & 0xffff ;
2012-07-02 09:58:47 +04:00
else if ( ret = = 0 )
ret = - ETIMEDOUT ;
2012-01-12 17:38:57 +04:00
/* restore the ctrl regs state */
writel ( ctrlreg1 , dev - > io_base + SMI_CR1 ) ;
writel ( 0 , dev - > io_base + SMI_CR2 ) ;
mutex_unlock ( & dev - > lock ) ;
return ret ;
}
/**
* spear_smi_wait_till_ready - wait till flash is ready
* @ dev : structure of SMI information .
* @ bank : flash corresponding to this bank
* @ timeout : timeout for busy wait condition
*
* This routine checks for WIP ( write in progress ) bit in Status register
* If successful the routine returns 0 else - EBUSY
*/
static int spear_smi_wait_till_ready ( struct spear_smi * dev , u32 bank ,
unsigned long timeout )
{
unsigned long finish ;
int status ;
finish = jiffies + timeout ;
do {
status = spear_smi_read_sr ( dev , bank ) ;
2012-07-02 09:58:47 +04:00
if ( status < 0 ) {
if ( status = = - ETIMEDOUT )
continue ; /* try till finish */
return status ;
} else if ( ! ( status & SR_WIP ) ) {
2012-01-12 17:38:57 +04:00
return 0 ;
2012-07-02 09:58:47 +04:00
}
2012-01-12 17:38:57 +04:00
cond_resched ( ) ;
} while ( ! time_after_eq ( jiffies , finish ) ) ;
dev_err ( & dev - > pdev - > dev , " smi controller is busy, timeout \n " ) ;
2012-07-02 09:58:47 +04:00
return - EBUSY ;
2012-01-12 17:38:57 +04:00
}
/**
* spear_smi_int_handler - SMI Interrupt Handler .
* @ irq : irq number
* @ dev_id : structure of SMI device , embedded in dev_id .
*
* The handler clears all interrupt conditions and records the status in
* dev - > status which is used by the driver later .
*/
static irqreturn_t spear_smi_int_handler ( int irq , void * dev_id )
{
u32 status = 0 ;
struct spear_smi * dev = dev_id ;
status = readl ( dev - > io_base + SMI_SR ) ;
if ( unlikely ( ! status ) )
return IRQ_NONE ;
/* clear all interrupt conditions */
writel ( 0 , dev - > io_base + SMI_SR ) ;
/* copy the status register in dev->status */
dev - > status | = status ;
/* send the completion */
wake_up_interruptible ( & dev - > cmd_complete ) ;
return IRQ_HANDLED ;
}
/**
* spear_smi_hw_init - initializes the smi controller .
* @ dev : structure of smi device
*
* this routine initializes the smi controller wit the default values
*/
static void spear_smi_hw_init ( struct spear_smi * dev )
{
unsigned long rate = 0 ;
u32 prescale = 0 ;
u32 val ;
rate = clk_get_rate ( dev - > clk ) ;
/* functional clock of smi */
prescale = DIV_ROUND_UP ( rate , dev - > clk_rate ) ;
/*
* setting the standard values , fast mode , prescaler for
* SMI_MAX_CLOCK_FREQ ( 50 MHz ) operation and bank enable
*/
val = HOLD1 | BANK_EN | DSEL_TIME | ( prescale < < 8 ) ;
mutex_lock ( & dev - > lock ) ;
2012-07-02 09:58:46 +04:00
/* clear all interrupt conditions */
writel ( 0 , dev - > io_base + SMI_SR ) ;
2012-01-12 17:38:57 +04:00
writel ( val , dev - > io_base + SMI_CR1 ) ;
mutex_unlock ( & dev - > lock ) ;
}
/**
* get_flash_index - match chip id from a flash list .
* @ flash_id : a valid nor flash chip id obtained from board .
*
* try to validate the chip id by matching from a list , if not found then simply
* returns negative . In case of success returns index in to the flash devices
* array .
*/
static int get_flash_index ( u32 flash_id )
{
int index ;
/* Matches chip-id to entire list of 'serial-nor flash' ids */
for ( index = 0 ; index < ARRAY_SIZE ( flash_devices ) ; index + + ) {
if ( flash_devices [ index ] . device_id = = flash_id )
return index ;
}
/* Memory chip is not listed and not supported */
return - ENODEV ;
}
/**
* spear_smi_write_enable - Enable the flash to do write operation
* @ dev : structure of SMI device
* @ bank : enable write for flash connected to this bank
*
* Set write enable latch with Write Enable command .
* Returns 0 on success .
*/
static int spear_smi_write_enable ( struct spear_smi * dev , u32 bank )
{
int ret ;
u32 ctrlreg1 ;
mutex_lock ( & dev - > lock ) ;
dev - > status = 0 ; /* Will be set in interrupt handler */
ctrlreg1 = readl ( dev - > io_base + SMI_CR1 ) ;
/* program smi in h/w mode */
writel ( ctrlreg1 & ~ SW_MODE , dev - > io_base + SMI_CR1 ) ;
/* give the flash, write enable command */
writel ( ( bank < < BANK_SHIFT ) | WE | TFIE , dev - > io_base + SMI_CR2 ) ;
ret = wait_event_interruptible_timeout ( dev - > cmd_complete ,
dev - > status & TFF , SMI_CMD_TIMEOUT ) ;
/* restore the ctrl regs state */
writel ( ctrlreg1 , dev - > io_base + SMI_CR1 ) ;
writel ( 0 , dev - > io_base + SMI_CR2 ) ;
2012-07-02 09:58:47 +04:00
if ( ret = = 0 ) {
2012-01-12 17:38:57 +04:00
ret = - EIO ;
dev_err ( & dev - > pdev - > dev ,
" smi controller failed on write enable \n " ) ;
2012-07-02 09:58:47 +04:00
} else if ( ret > 0 ) {
2012-01-12 17:38:57 +04:00
/* check whether write mode status is set for required bank */
if ( dev - > status & ( 1 < < ( bank + WM_SHIFT ) ) )
ret = 0 ;
else {
dev_err ( & dev - > pdev - > dev , " couldn't enable write \n " ) ;
ret = - EIO ;
}
}
mutex_unlock ( & dev - > lock ) ;
return ret ;
}
static inline u32
get_sector_erase_cmd ( struct spear_snor_flash * flash , u32 offset )
{
u32 cmd ;
u8 * x = ( u8 * ) & cmd ;
x [ 0 ] = flash - > erase_cmd ;
x [ 1 ] = offset > > 16 ;
x [ 2 ] = offset > > 8 ;
x [ 3 ] = offset ;
return cmd ;
}
/**
* spear_smi_erase_sector - erase one sector of flash
* @ dev : structure of SMI information
* @ command : erase command to be send
* @ bank : bank to which this command needs to be send
* @ bytes : size of command
*
* Erase one sector of flash memory at offset ` ` offset ' ' which is any
* address within the sector which should be erased .
* Returns 0 if successful , non - zero otherwise .
*/
static int spear_smi_erase_sector ( struct spear_smi * dev ,
u32 bank , u32 command , u32 bytes )
{
u32 ctrlreg1 = 0 ;
int ret ;
ret = spear_smi_wait_till_ready ( dev , bank , SMI_MAX_TIME_OUT ) ;
if ( ret )
return ret ;
ret = spear_smi_write_enable ( dev , bank ) ;
if ( ret )
return ret ;
mutex_lock ( & dev - > lock ) ;
ctrlreg1 = readl ( dev - > io_base + SMI_CR1 ) ;
writel ( ( ctrlreg1 | SW_MODE ) & ~ WB_MODE , dev - > io_base + SMI_CR1 ) ;
/* send command in sw mode */
writel ( command , dev - > io_base + SMI_TR ) ;
writel ( ( bank < < BANK_SHIFT ) | SEND | TFIE | ( bytes < < TX_LEN_SHIFT ) ,
dev - > io_base + SMI_CR2 ) ;
ret = wait_event_interruptible_timeout ( dev - > cmd_complete ,
dev - > status & TFF , SMI_CMD_TIMEOUT ) ;
2012-07-02 09:58:47 +04:00
if ( ret = = 0 ) {
2012-01-12 17:38:57 +04:00
ret = - EIO ;
dev_err ( & dev - > pdev - > dev , " sector erase failed \n " ) ;
2012-07-02 09:58:47 +04:00
} else if ( ret > 0 )
2012-01-12 17:38:57 +04:00
ret = 0 ; /* success */
/* restore ctrl regs */
writel ( ctrlreg1 , dev - > io_base + SMI_CR1 ) ;
writel ( 0 , dev - > io_base + SMI_CR2 ) ;
mutex_unlock ( & dev - > lock ) ;
return ret ;
}
/**
* spear_mtd_erase - perform flash erase operation as requested by user
* @ mtd : Provides the memory characteristics
* @ e_info : Provides the erase information
*
* Erase an address range on the flash chip . The address range may extend
* one or more erase sectors . Return an error is there is a problem erasing .
*/
static int spear_mtd_erase ( struct mtd_info * mtd , struct erase_info * e_info )
{
struct spear_snor_flash * flash = get_flash_data ( mtd ) ;
struct spear_smi * dev = mtd - > priv ;
u32 addr , command , bank ;
int len , ret ;
if ( ! flash | | ! dev )
return - ENODEV ;
bank = flash - > bank ;
if ( bank > dev - > num_flashes - 1 ) {
dev_err ( & dev - > pdev - > dev , " Invalid Bank Num " ) ;
return - EINVAL ;
}
addr = e_info - > addr ;
len = e_info - > len ;
mutex_lock ( & flash - > lock ) ;
/* now erase sectors in loop */
while ( len ) {
command = get_sector_erase_cmd ( flash , addr ) ;
/* preparing the command for flash */
ret = spear_smi_erase_sector ( dev , bank , command , 4 ) ;
if ( ret ) {
e_info - > state = MTD_ERASE_FAILED ;
mutex_unlock ( & flash - > lock ) ;
return ret ;
}
addr + = mtd - > erasesize ;
len - = mtd - > erasesize ;
}
mutex_unlock ( & flash - > lock ) ;
e_info - > state = MTD_ERASE_DONE ;
mtd_erase_callback ( e_info ) ;
return 0 ;
}
/**
* spear_mtd_read - performs flash read operation as requested by the user
* @ mtd : MTD information of the memory bank
* @ from : Address from which to start read
* @ len : Number of bytes to be read
* @ retlen : Fills the Number of bytes actually read
* @ buf : Fills this after reading
*
* Read an address range from the flash chip . The address range
* may be any size provided it is within the physical boundaries .
* Returns 0 on success , non zero otherwise
*/
static int spear_mtd_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , u8 * buf )
{
struct spear_snor_flash * flash = get_flash_data ( mtd ) ;
struct spear_smi * dev = mtd - > priv ;
2013-08-07 11:16:36 +04:00
void __iomem * src ;
2012-01-12 17:38:57 +04:00
u32 ctrlreg1 , val ;
int ret ;
if ( ! flash | | ! dev )
return - ENODEV ;
if ( flash - > bank > dev - > num_flashes - 1 ) {
dev_err ( & dev - > pdev - > dev , " Invalid Bank Num " ) ;
return - EINVAL ;
}
/* select address as per bank number */
src = flash - > base_addr + from ;
mutex_lock ( & flash - > lock ) ;
/* wait till previous write/erase is done. */
ret = spear_smi_wait_till_ready ( dev , flash - > bank , SMI_MAX_TIME_OUT ) ;
if ( ret ) {
mutex_unlock ( & flash - > lock ) ;
return ret ;
}
mutex_lock ( & dev - > lock ) ;
/* put smi in hw mode not wbt mode */
ctrlreg1 = val = readl ( dev - > io_base + SMI_CR1 ) ;
val & = ~ ( SW_MODE | WB_MODE ) ;
if ( flash - > fast_mode )
val | = FAST_MODE ;
writel ( val , dev - > io_base + SMI_CR1 ) ;
2013-08-07 11:16:36 +04:00
memcpy_fromio ( buf , src , len ) ;
2012-01-12 17:38:57 +04:00
/* restore ctrl reg1 */
writel ( ctrlreg1 , dev - > io_base + SMI_CR1 ) ;
mutex_unlock ( & dev - > lock ) ;
* retlen = len ;
mutex_unlock ( & flash - > lock ) ;
return 0 ;
}
static inline int spear_smi_cpy_toio ( struct spear_smi * dev , u32 bank ,
2013-08-07 11:16:36 +04:00
void __iomem * dest , const void * src , size_t len )
2012-01-12 17:38:57 +04:00
{
int ret ;
u32 ctrlreg1 ;
/* wait until finished previous write command. */
ret = spear_smi_wait_till_ready ( dev , bank , SMI_MAX_TIME_OUT ) ;
if ( ret )
return ret ;
/* put smi in write enable */
ret = spear_smi_write_enable ( dev , bank ) ;
if ( ret )
return ret ;
/* put smi in hw, write burst mode */
mutex_lock ( & dev - > lock ) ;
ctrlreg1 = readl ( dev - > io_base + SMI_CR1 ) ;
writel ( ( ctrlreg1 | WB_MODE ) & ~ SW_MODE , dev - > io_base + SMI_CR1 ) ;
memcpy_toio ( dest , src , len ) ;
writel ( ctrlreg1 , dev - > io_base + SMI_CR1 ) ;
mutex_unlock ( & dev - > lock ) ;
return 0 ;
}
/**
* spear_mtd_write - performs write operation as requested by the user .
* @ mtd : MTD information of the memory bank .
* @ to : Address to write .
* @ len : Number of bytes to be written .
* @ retlen : Number of bytes actually wrote .
* @ buf : Buffer from which the data to be taken .
*
* Write an address range to the flash chip . Data must be written in
* flash_page_size chunks . The address range may be any size provided
* it is within the physical boundaries .
* Returns 0 on success , non zero otherwise
*/
static int spear_mtd_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u8 * buf )
{
struct spear_snor_flash * flash = get_flash_data ( mtd ) ;
struct spear_smi * dev = mtd - > priv ;
2013-08-07 11:16:36 +04:00
void __iomem * dest ;
2012-01-12 17:38:57 +04:00
u32 page_offset , page_size ;
int ret ;
if ( ! flash | | ! dev )
return - ENODEV ;
if ( flash - > bank > dev - > num_flashes - 1 ) {
dev_err ( & dev - > pdev - > dev , " Invalid Bank Num " ) ;
return - EINVAL ;
}
/* select address as per bank number */
dest = flash - > base_addr + to ;
mutex_lock ( & flash - > lock ) ;
page_offset = ( u32 ) to % flash - > page_size ;
/* do if all the bytes fit onto one page */
if ( page_offset + len < = flash - > page_size ) {
ret = spear_smi_cpy_toio ( dev , flash - > bank , dest , buf , len ) ;
if ( ! ret )
* retlen + = len ;
} else {
u32 i ;
/* the size of data remaining on the first page */
page_size = flash - > page_size - page_offset ;
ret = spear_smi_cpy_toio ( dev , flash - > bank , dest , buf ,
page_size ) ;
if ( ret )
goto err_write ;
else
* retlen + = page_size ;
/* write everything in pagesize chunks */
for ( i = page_size ; i < len ; i + = page_size ) {
page_size = len - i ;
if ( page_size > flash - > page_size )
page_size = flash - > page_size ;
ret = spear_smi_cpy_toio ( dev , flash - > bank , dest + i ,
buf + i , page_size ) ;
if ( ret )
break ;
else
* retlen + = page_size ;
}
}
err_write :
mutex_unlock ( & flash - > lock ) ;
return ret ;
}
/**
* spear_smi_probe_flash - Detects the NOR Flash chip .
* @ dev : structure of SMI information .
* @ bank : bank on which flash must be probed
*
* This routine will check whether there exists a flash chip on a given memory
* bank ID .
* Return index of the probed flash in flash devices structure
*/
static int spear_smi_probe_flash ( struct spear_smi * dev , u32 bank )
{
int ret ;
u32 val = 0 ;
ret = spear_smi_wait_till_ready ( dev , bank , SMI_PROBE_TIMEOUT ) ;
if ( ret )
return ret ;
mutex_lock ( & dev - > lock ) ;
dev - > status = 0 ; /* Will be set in interrupt handler */
/* put smi in sw mode */
val = readl ( dev - > io_base + SMI_CR1 ) ;
writel ( val | SW_MODE , dev - > io_base + SMI_CR1 ) ;
/* send readid command in sw mode */
writel ( OPCODE_RDID , dev - > io_base + SMI_TR ) ;
val = ( bank < < BANK_SHIFT ) | SEND | ( 1 < < TX_LEN_SHIFT ) |
( 3 < < RX_LEN_SHIFT ) | TFIE ;
writel ( val , dev - > io_base + SMI_CR2 ) ;
/* wait for TFF */
ret = wait_event_interruptible_timeout ( dev - > cmd_complete ,
dev - > status & TFF , SMI_CMD_TIMEOUT ) ;
if ( ret < = 0 ) {
ret = - ENODEV ;
goto err_probe ;
}
/* get memory chip id */
val = readl ( dev - > io_base + SMI_RR ) ;
val & = 0x00ffffff ;
ret = get_flash_index ( val ) ;
err_probe :
/* clear sw mode */
val = readl ( dev - > io_base + SMI_CR1 ) ;
writel ( val & ~ SW_MODE , dev - > io_base + SMI_CR1 ) ;
mutex_unlock ( & dev - > lock ) ;
return ret ;
}
2012-03-16 14:42:11 +04:00
# ifdef CONFIG_OF
2012-11-19 22:23:07 +04:00
static int spear_smi_probe_config_dt ( struct platform_device * pdev ,
2012-12-22 01:19:05 +04:00
struct device_node * np )
2012-03-16 14:42:11 +04:00
{
struct spear_smi_plat_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
struct device_node * pp = NULL ;
const __be32 * addr ;
u32 val ;
int len ;
int i = 0 ;
if ( ! np )
return - ENODEV ;
of_property_read_u32 ( np , " clock-rate " , & val ) ;
pdata - > clk_rate = val ;
pdata - > board_flash_info = devm_kzalloc ( & pdev - > dev ,
sizeof ( * pdata - > board_flash_info ) ,
GFP_KERNEL ) ;
/* Fill structs for each subnode (flash device) */
while ( ( pp = of_get_next_child ( np , pp ) ) ) {
struct spear_smi_flash_info * flash_info ;
flash_info = & pdata - > board_flash_info [ i ] ;
pdata - > np [ i ] = pp ;
/* Read base-addr and size from DT */
addr = of_get_property ( pp , " reg " , & len ) ;
pdata - > board_flash_info - > mem_base = be32_to_cpup ( & addr [ 0 ] ) ;
pdata - > board_flash_info - > size = be32_to_cpup ( & addr [ 1 ] ) ;
if ( of_get_property ( pp , " st,smi-fast-mode " , NULL ) )
pdata - > board_flash_info - > fast_mode = 1 ;
i + + ;
}
pdata - > num_flashes = i ;
return 0 ;
}
# else
2012-11-19 22:23:07 +04:00
static int spear_smi_probe_config_dt ( struct platform_device * pdev ,
2012-12-22 01:19:05 +04:00
struct device_node * np )
2012-03-16 14:42:11 +04:00
{
return - ENOSYS ;
}
# endif
static int spear_smi_setup_banks ( struct platform_device * pdev ,
u32 bank , struct device_node * np )
2012-01-12 17:38:57 +04:00
{
struct spear_smi * dev = platform_get_drvdata ( pdev ) ;
struct spear_smi_flash_info * flash_info ;
struct spear_smi_plat_data * pdata ;
struct spear_snor_flash * flash ;
2012-03-16 14:41:40 +04:00
struct mtd_partition * parts = NULL ;
int count = 0 ;
2012-01-12 17:38:57 +04:00
int flash_index ;
int ret = 0 ;
pdata = dev_get_platdata ( & pdev - > dev ) ;
if ( bank > pdata - > num_flashes - 1 )
return - EINVAL ;
flash_info = & pdata - > board_flash_info [ bank ] ;
if ( ! flash_info )
return - ENODEV ;
2012-08-05 00:36:38 +04:00
flash = devm_kzalloc ( & pdev - > dev , sizeof ( * flash ) , GFP_ATOMIC ) ;
2012-01-12 17:38:57 +04:00
if ( ! flash )
return - ENOMEM ;
flash - > bank = bank ;
flash - > fast_mode = flash_info - > fast_mode ? 1 : 0 ;
mutex_init ( & flash - > lock ) ;
/* verify whether nor flash is really present on board */
flash_index = spear_smi_probe_flash ( dev , bank ) ;
if ( flash_index < 0 ) {
dev_info ( & dev - > pdev - > dev , " smi-nor%d not found \n " , bank ) ;
2012-08-05 00:36:38 +04:00
return flash_index ;
2012-01-12 17:38:57 +04:00
}
/* map the memory for nor flash chip */
2012-08-05 00:36:38 +04:00
flash - > base_addr = devm_ioremap ( & pdev - > dev , flash_info - > mem_base ,
flash_info - > size ) ;
if ( ! flash - > base_addr )
return - EIO ;
2012-01-12 17:38:57 +04:00
dev - > flash [ bank ] = flash ;
flash - > mtd . priv = dev ;
if ( flash_info - > name )
flash - > mtd . name = flash_info - > name ;
else
flash - > mtd . name = flash_devices [ flash_index ] . name ;
2015-06-10 23:38:21 +03:00
flash - > mtd . dev . parent = & pdev - > dev ;
2015-10-31 06:33:28 +03:00
mtd_set_of_node ( & flash - > mtd , np ) ;
2012-01-12 17:38:57 +04:00
flash - > mtd . type = MTD_NORFLASH ;
flash - > mtd . writesize = 1 ;
flash - > mtd . flags = MTD_CAP_NORFLASH ;
flash - > mtd . size = flash_info - > size ;
flash - > mtd . erasesize = flash_devices [ flash_index ] . sectorsize ;
flash - > page_size = flash_devices [ flash_index ] . pagesize ;
2012-02-03 12:14:12 +04:00
flash - > mtd . writebufsize = flash - > page_size ;
2012-01-12 17:38:57 +04:00
flash - > erase_cmd = flash_devices [ flash_index ] . erase_cmd ;
2012-01-30 16:58:32 +04:00
flash - > mtd . _erase = spear_mtd_erase ;
flash - > mtd . _read = spear_mtd_read ;
flash - > mtd . _write = spear_mtd_write ;
2012-01-12 17:38:57 +04:00
flash - > dev_id = flash_devices [ flash_index ] . device_id ;
dev_info ( & dev - > pdev - > dev , " mtd .name=%s .size=%llx(%lluM) \n " ,
flash - > mtd . name , flash - > mtd . size ,
flash - > mtd . size / ( 1024 * 1024 ) ) ;
dev_info ( & dev - > pdev - > dev , " .erasesize = 0x%x(%uK) \n " ,
flash - > mtd . erasesize , flash - > mtd . erasesize / 1024 ) ;
2012-03-16 14:42:11 +04:00
# ifndef CONFIG_OF
2012-01-12 17:38:57 +04:00
if ( flash_info - > partitions ) {
parts = flash_info - > partitions ;
count = flash_info - > nr_partitions ;
}
2012-03-16 14:42:11 +04:00
# endif
2015-10-31 06:33:28 +03:00
ret = mtd_device_register ( & flash - > mtd , parts , count ) ;
2012-03-16 14:41:40 +04:00
if ( ret ) {
2012-01-12 17:38:57 +04:00
dev_err ( & dev - > pdev - > dev , " Err MTD partition=%d \n " , ret ) ;
2012-08-05 00:36:38 +04:00
return ret ;
2012-03-16 14:41:40 +04:00
}
2012-01-12 17:38:57 +04:00
2012-03-16 14:41:40 +04:00
return 0 ;
2012-01-12 17:38:57 +04:00
}
/**
* spear_smi_probe - Entry routine
* @ pdev : platform device structure
*
* This is the first routine which gets invoked during booting and does all
* initialization / allocation work . The routine looks for available memory banks ,
* and do proper init for any found one .
* Returns 0 on success , non zero otherwise
*/
2012-11-19 22:23:07 +04:00
static int spear_smi_probe ( struct platform_device * pdev )
2012-01-12 17:38:57 +04:00
{
2012-03-16 14:42:11 +04:00
struct device_node * np = pdev - > dev . of_node ;
struct spear_smi_plat_data * pdata = NULL ;
2012-01-12 17:38:57 +04:00
struct spear_smi * dev ;
struct resource * smi_base ;
int irq , ret = 0 ;
int i ;
2012-03-16 14:42:11 +04:00
if ( np ) {
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata ) {
ret = - ENOMEM ;
goto err ;
}
pdev - > dev . platform_data = pdata ;
ret = spear_smi_probe_config_dt ( pdev , np ) ;
if ( ret ) {
ret = - ENODEV ;
dev_err ( & pdev - > dev , " no platform data \n " ) ;
goto err ;
}
} else {
pdata = dev_get_platdata ( & pdev - > dev ) ;
2012-07-11 12:58:38 +04:00
if ( ! pdata ) {
2012-03-16 14:42:11 +04:00
ret = - ENODEV ;
dev_err ( & pdev - > dev , " no platform data \n " ) ;
goto err ;
}
2012-01-12 17:38:57 +04:00
}
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
ret = - ENODEV ;
dev_err ( & pdev - > dev , " invalid smi irq \n " ) ;
goto err ;
}
2012-08-05 00:36:38 +04:00
dev = devm_kzalloc ( & pdev - > dev , sizeof ( * dev ) , GFP_ATOMIC ) ;
2012-01-12 17:38:57 +04:00
if ( ! dev ) {
ret = - ENOMEM ;
goto err ;
}
2012-08-05 00:36:38 +04:00
smi_base = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2012-01-12 17:38:57 +04:00
2013-01-21 14:09:12 +04:00
dev - > io_base = devm_ioremap_resource ( & pdev - > dev , smi_base ) ;
if ( IS_ERR ( dev - > io_base ) ) {
ret = PTR_ERR ( dev - > io_base ) ;
2012-08-05 00:36:38 +04:00
goto err ;
2012-01-12 17:38:57 +04:00
}
dev - > pdev = pdev ;
dev - > clk_rate = pdata - > clk_rate ;
2012-08-17 14:05:41 +04:00
if ( dev - > clk_rate > SMI_MAX_CLOCK_FREQ )
2012-01-12 17:38:57 +04:00
dev - > clk_rate = SMI_MAX_CLOCK_FREQ ;
dev - > num_flashes = pdata - > num_flashes ;
if ( dev - > num_flashes > MAX_NUM_FLASH_CHIP ) {
dev_err ( & pdev - > dev , " exceeding max number of flashes \n " ) ;
dev - > num_flashes = MAX_NUM_FLASH_CHIP ;
}
2012-08-05 00:36:38 +04:00
dev - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2012-01-12 17:38:57 +04:00
if ( IS_ERR ( dev - > clk ) ) {
ret = PTR_ERR ( dev - > clk ) ;
2012-08-05 00:36:38 +04:00
goto err ;
2012-01-12 17:38:57 +04:00
}
2012-04-17 15:37:56 +04:00
ret = clk_prepare_enable ( dev - > clk ) ;
2012-01-12 17:38:57 +04:00
if ( ret )
2012-08-05 00:36:38 +04:00
goto err ;
2012-01-12 17:38:57 +04:00
2012-08-05 00:36:38 +04:00
ret = devm_request_irq ( & pdev - > dev , irq , spear_smi_int_handler , 0 ,
pdev - > name , dev ) ;
2012-01-12 17:38:57 +04:00
if ( ret ) {
dev_err ( & dev - > pdev - > dev , " SMI IRQ allocation failed \n " ) ;
goto err_irq ;
}
mutex_init ( & dev - > lock ) ;
init_waitqueue_head ( & dev - > cmd_complete ) ;
spear_smi_hw_init ( dev ) ;
platform_set_drvdata ( pdev , dev ) ;
/* loop for each serial nor-flash which is connected to smi */
for ( i = 0 ; i < dev - > num_flashes ; i + + ) {
2012-03-16 14:42:11 +04:00
ret = spear_smi_setup_banks ( pdev , i , pdata - > np [ i ] ) ;
2012-01-12 17:38:57 +04:00
if ( ret ) {
dev_err ( & dev - > pdev - > dev , " bank setup failed \n " ) ;
2013-05-07 10:17:36 +04:00
goto err_irq ;
2012-01-12 17:38:57 +04:00
}
}
return 0 ;
err_irq :
2012-04-17 15:37:56 +04:00
clk_disable_unprepare ( dev - > clk ) ;
2012-01-12 17:38:57 +04:00
err :
return ret ;
}
/**
* spear_smi_remove - Exit routine
* @ pdev : platform device structure
*
* free all allocations and delete the partitions .
*/
2012-11-19 22:26:04 +04:00
static int spear_smi_remove ( struct platform_device * pdev )
2012-01-12 17:38:57 +04:00
{
struct spear_smi * dev ;
struct spear_snor_flash * flash ;
2012-08-05 00:36:38 +04:00
int ret , i ;
2012-01-12 17:38:57 +04:00
dev = platform_get_drvdata ( pdev ) ;
if ( ! dev ) {
dev_err ( & pdev - > dev , " dev is null \n " ) ;
return - ENODEV ;
}
/* clean up for all nor flash */
for ( i = 0 ; i < dev - > num_flashes ; i + + ) {
flash = dev - > flash [ i ] ;
if ( ! flash )
continue ;
/* clean up mtd stuff */
ret = mtd_device_unregister ( & flash - > mtd ) ;
if ( ret )
dev_err ( & pdev - > dev , " error removing mtd \n " ) ;
}
2012-04-17 15:37:56 +04:00
clk_disable_unprepare ( dev - > clk ) ;
2012-01-12 17:38:57 +04:00
return 0 ;
}
2013-03-26 10:54:22 +04:00
# ifdef CONFIG_PM_SLEEP
2012-07-02 09:58:45 +04:00
static int spear_smi_suspend ( struct device * dev )
2012-01-12 17:38:57 +04:00
{
2012-07-02 09:58:45 +04:00
struct spear_smi * sdev = dev_get_drvdata ( dev ) ;
2012-01-12 17:38:57 +04:00
2012-07-02 09:58:45 +04:00
if ( sdev & & sdev - > clk )
clk_disable_unprepare ( sdev - > clk ) ;
2012-01-12 17:38:57 +04:00
return 0 ;
}
2012-07-02 09:58:45 +04:00
static int spear_smi_resume ( struct device * dev )
2012-01-12 17:38:57 +04:00
{
2012-07-02 09:58:45 +04:00
struct spear_smi * sdev = dev_get_drvdata ( dev ) ;
2012-01-12 17:38:57 +04:00
int ret = - EPERM ;
2012-07-02 09:58:45 +04:00
if ( sdev & & sdev - > clk )
ret = clk_prepare_enable ( sdev - > clk ) ;
2012-01-12 17:38:57 +04:00
if ( ! ret )
2012-07-02 09:58:45 +04:00
spear_smi_hw_init ( sdev ) ;
2012-01-12 17:38:57 +04:00
return ret ;
}
2013-03-26 10:54:22 +04:00
# endif
2012-01-12 17:38:57 +04:00
2012-07-02 09:58:45 +04:00
static SIMPLE_DEV_PM_OPS ( spear_smi_pm_ops , spear_smi_suspend , spear_smi_resume ) ;
2012-03-16 14:42:11 +04:00
# ifdef CONFIG_OF
static const struct of_device_id spear_smi_id_table [ ] = {
{ . compatible = " st,spear600-smi " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , spear_smi_id_table ) ;
# endif
2012-01-12 17:38:57 +04:00
static struct platform_driver spear_smi_driver = {
. driver = {
. name = " smi " ,
. bus = & platform_bus_type ,
2012-03-16 14:42:11 +04:00
. of_match_table = of_match_ptr ( spear_smi_id_table ) ,
2012-07-02 09:58:45 +04:00
. pm = & spear_smi_pm_ops ,
2012-01-12 17:38:57 +04:00
} ,
. probe = spear_smi_probe ,
2012-11-19 22:21:24 +04:00
. remove = spear_smi_remove ,
2012-01-12 17:38:57 +04:00
} ;
2012-10-10 22:39:59 +04:00
module_platform_driver ( spear_smi_driver ) ;
2012-01-12 17:38:57 +04:00
MODULE_LICENSE ( " GPL " ) ;
2014-04-19 02:07:16 +04:00
MODULE_AUTHOR ( " Ashish Priyadarshi, Shiraz Hashim <shiraz.linux.kernel@gmail.com> " ) ;
2012-01-12 17:38:57 +04:00
MODULE_DESCRIPTION ( " MTD SMI driver for serial nor flash chips " ) ;