2012-09-17 13:50:49 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
2013-03-25 00:51:31 +04:00
# include <linux/delay.h>
2012-09-17 13:50:49 +04:00
# include <linux/mtd/mtd.h>
# include <linux/platform_device.h>
# include <linux/bcma/bcma.h>
2013-01-07 00:28:50 +04:00
# include "bcm47xxsflash.h"
2012-09-17 13:50:49 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Serial flash driver for BCMA bus " ) ;
2013-03-12 12:50:07 +04:00
static const char * const probes [ ] = { " bcm47xxpart " , NULL } ;
2012-09-17 13:50:49 +04:00
2013-03-25 00:51:31 +04:00
/**************************************************
* Various helpers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void bcm47xxsflash_cmd ( struct bcm47xxsflash * b47s , u32 opcode )
{
int i ;
b47s - > cc_write ( b47s , BCMA_CC_FLASHCTL , BCMA_CC_FLASHCTL_START | opcode ) ;
for ( i = 0 ; i < 1000 ; i + + ) {
if ( ! ( b47s - > cc_read ( b47s , BCMA_CC_FLASHCTL ) &
BCMA_CC_FLASHCTL_BUSY ) )
return ;
cpu_relax ( ) ;
}
pr_err ( " Control command failed (timeout)! \n " ) ;
}
static int bcm47xxsflash_poll ( struct bcm47xxsflash * b47s , int timeout )
{
unsigned long deadline = jiffies + timeout ;
do {
switch ( b47s - > type ) {
case BCM47XXSFLASH_TYPE_ST :
bcm47xxsflash_cmd ( b47s , OPCODE_ST_RDSR ) ;
if ( ! ( b47s - > cc_read ( b47s , BCMA_CC_FLASHDATA ) &
SR_ST_WIP ) )
return 0 ;
break ;
case BCM47XXSFLASH_TYPE_ATMEL :
bcm47xxsflash_cmd ( b47s , OPCODE_AT_STATUS ) ;
if ( b47s - > cc_read ( b47s , BCMA_CC_FLASHDATA ) &
SR_AT_READY )
return 0 ;
break ;
}
cpu_relax ( ) ;
udelay ( 1 ) ;
} while ( ! time_after_eq ( jiffies , deadline ) ) ;
pr_err ( " Timeout waiting for flash to be ready! \n " ) ;
return - EBUSY ;
}
/**************************************************
* MTD ops
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-09-17 13:50:49 +04:00
static int bcm47xxsflash_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , u_char * buf )
{
2013-01-07 00:28:50 +04:00
struct bcm47xxsflash * b47s = mtd - > priv ;
2012-09-17 13:50:49 +04:00
/* Check address range */
if ( ( from + len ) > mtd - > size )
return - EINVAL ;
2013-01-07 00:28:50 +04:00
memcpy_fromio ( buf , ( void __iomem * ) KSEG0ADDR ( b47s - > window + from ) ,
2012-09-17 13:50:49 +04:00
len ) ;
2013-01-24 20:39:54 +04:00
* retlen = len ;
2012-09-17 13:50:49 +04:00
return len ;
}
2013-01-07 00:28:50 +04:00
static void bcm47xxsflash_fill_mtd ( struct bcm47xxsflash * b47s )
2012-09-17 13:50:49 +04:00
{
2013-01-07 00:28:50 +04:00
struct mtd_info * mtd = & b47s - > mtd ;
mtd - > priv = b47s ;
2012-09-17 13:50:49 +04:00
mtd - > name = " bcm47xxsflash " ;
mtd - > owner = THIS_MODULE ;
mtd - > type = MTD_ROM ;
2013-01-07 00:28:50 +04:00
mtd - > size = b47s - > size ;
2012-09-17 13:50:49 +04:00
mtd - > _read = bcm47xxsflash_read ;
/* TODO: implement writing support and verify/change following code */
mtd - > flags = MTD_CAP_ROM ;
mtd - > writebufsize = mtd - > writesize = 1 ;
}
2013-02-04 11:23:08 +04:00
/**************************************************
* BCMA
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-03-25 00:53:24 +04:00
static int bcm47xxsflash_bcma_cc_read ( struct bcm47xxsflash * b47s , u16 offset )
{
return bcma_cc_read32 ( b47s - > bcma_cc , offset ) ;
}
static void bcm47xxsflash_bcma_cc_write ( struct bcm47xxsflash * b47s , u16 offset ,
u32 value )
{
bcma_cc_write32 ( b47s - > bcma_cc , offset , value ) ;
}
2013-02-04 11:23:08 +04:00
static int bcm47xxsflash_bcma_probe ( struct platform_device * pdev )
2012-09-17 13:50:49 +04:00
{
struct bcma_sflash * sflash = dev_get_platdata ( & pdev - > dev ) ;
2013-01-07 00:28:50 +04:00
struct bcm47xxsflash * b47s ;
2012-09-17 13:50:49 +04:00
int err ;
2013-01-07 00:28:50 +04:00
b47s = kzalloc ( sizeof ( * b47s ) , GFP_KERNEL ) ;
if ( ! b47s ) {
2012-09-17 13:50:49 +04:00
err = - ENOMEM ;
goto out ;
}
2013-01-07 00:28:50 +04:00
sflash - > priv = b47s ;
2013-03-06 15:33:17 +04:00
b47s - > bcma_cc = container_of ( sflash , struct bcma_drv_cc , sflash ) ;
2013-03-25 00:53:24 +04:00
b47s - > cc_read = bcm47xxsflash_bcma_cc_read ;
b47s - > cc_write = bcm47xxsflash_bcma_cc_write ;
2013-03-06 15:33:17 +04:00
2013-03-06 15:34:19 +04:00
switch ( b47s - > bcma_cc - > capabilities & BCMA_CC_CAP_FLASHT ) {
case BCMA_CC_FLASHT_STSER :
b47s - > type = BCM47XXSFLASH_TYPE_ST ;
break ;
case BCMA_CC_FLASHT_ATSER :
b47s - > type = BCM47XXSFLASH_TYPE_ATMEL ;
break ;
}
2013-01-07 00:28:50 +04:00
b47s - > window = sflash - > window ;
b47s - > blocksize = sflash - > blocksize ;
b47s - > numblocks = sflash - > numblocks ;
b47s - > size = sflash - > size ;
bcm47xxsflash_fill_mtd ( b47s ) ;
2012-09-17 13:50:49 +04:00
2013-01-07 00:28:50 +04:00
err = mtd_device_parse_register ( & b47s - > mtd , probes , NULL , NULL , 0 ) ;
2012-09-17 13:50:49 +04:00
if ( err ) {
pr_err ( " Failed to register MTD device: %d \n " , err ) ;
goto err_dev_reg ;
}
2013-03-25 00:51:31 +04:00
if ( bcm47xxsflash_poll ( b47s , HZ / 10 ) )
pr_warn ( " Serial flash busy \n " ) ;
2012-09-17 13:50:49 +04:00
return 0 ;
err_dev_reg :
2013-01-07 00:28:50 +04:00
kfree ( & b47s - > mtd ) ;
2012-09-17 13:50:49 +04:00
out :
return err ;
}
2013-02-04 11:23:08 +04:00
static int bcm47xxsflash_bcma_remove ( struct platform_device * pdev )
2012-09-17 13:50:49 +04:00
{
struct bcma_sflash * sflash = dev_get_platdata ( & pdev - > dev ) ;
2013-01-07 00:28:50 +04:00
struct bcm47xxsflash * b47s = sflash - > priv ;
2012-09-17 13:50:49 +04:00
2013-01-07 00:28:50 +04:00
mtd_device_unregister ( & b47s - > mtd ) ;
kfree ( b47s ) ;
2012-09-17 13:50:49 +04:00
return 0 ;
}
static struct platform_driver bcma_sflash_driver = {
2013-02-04 11:23:08 +04:00
. probe = bcm47xxsflash_bcma_probe ,
. remove = bcm47xxsflash_bcma_remove ,
2012-09-17 13:50:49 +04:00
. driver = {
. name = " bcma_sflash " ,
. owner = THIS_MODULE ,
} ,
} ;
2013-02-04 11:23:08 +04:00
/**************************************************
* Init
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-09-17 13:50:49 +04:00
static int __init bcm47xxsflash_init ( void )
{
int err ;
2013-01-24 20:39:55 +04:00
err = platform_driver_register ( & bcma_sflash_driver ) ;
2012-09-17 13:50:49 +04:00
if ( err )
pr_err ( " Failed to register BCMA serial flash driver: %d \n " ,
err ) ;
return err ;
}
static void __exit bcm47xxsflash_exit ( void )
{
platform_driver_unregister ( & bcma_sflash_driver ) ;
}
module_init ( bcm47xxsflash_init ) ;
module_exit ( bcm47xxsflash_exit ) ;