2006-01-09 00:34:27 +03:00
/*
2007-06-25 02:12:35 +04:00
* MTD SPI driver for ST M25Pxx ( and similar ) serial flash chips
2006-01-09 00:34:27 +03:00
*
* Author : Mike Lavender , mike @ steroidmicros . com
*
* Copyright ( c ) 2005 , Intec Automation Inc .
*
* Some parts are based on lart . c by Abraham Van Der Merwe
*
* Cleaned up and generalized based on mtd_dataflash . c
*
* This code 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 .
*
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/interrupt.h>
2007-06-25 02:09:13 +04:00
# include <linux/mutex.h>
2006-01-09 00:34:27 +03:00
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2007-06-25 02:09:13 +04:00
2006-01-09 00:34:27 +03:00
# include <linux/spi/spi.h>
# include <linux/spi/flash.h>
# define FLASH_PAGESIZE 256
/* Flash opcodes. */
2007-06-25 02:12:35 +04:00
# define OPCODE_WREN 0x06 /* Write enable */
# define OPCODE_RDSR 0x05 /* Read status register */
# define OPCODE_READ 0x03 /* Read data bytes (low frequency) */
# define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
# define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
2007-06-29 01:38:38 +04:00
# define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
# define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
# define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
2006-01-09 00:34:27 +03:00
# define OPCODE_RDID 0x9f /* Read JEDEC ID */
/* Status Register bits. */
# define SR_WIP 1 /* Write in progress */
# define SR_WEL 2 /* Write enable latch */
2007-06-25 02:12:35 +04:00
/* meaning of other SR_* bits may differ between vendors */
2006-01-09 00:34:27 +03:00
# define SR_BP0 4 /* Block protect 0 */
# define SR_BP1 8 /* Block protect 1 */
# define SR_BP2 0x10 /* Block protect 2 */
# define SR_SRWD 0x80 /* SR write protect */
/* Define max times to check status register before we give up. */
# define MAX_READY_WAIT_COUNT 100000
# ifdef CONFIG_MTD_PARTITIONS
# define mtd_has_partitions() (1)
# else
# define mtd_has_partitions() (0)
# endif
/****************************************************************************/
struct m25p {
struct spi_device * spi ;
2007-06-25 02:09:13 +04:00
struct mutex lock ;
2006-01-09 00:34:27 +03:00
struct mtd_info mtd ;
2007-06-25 02:12:35 +04:00
unsigned partitioned : 1 ;
u8 erase_opcode ;
2006-01-09 00:34:27 +03:00
u8 command [ 4 ] ;
} ;
static inline struct m25p * mtd_to_m25p ( struct mtd_info * mtd )
{
return container_of ( mtd , struct m25p , mtd ) ;
}
/****************************************************************************/
/*
* Internal helper functions
*/
/*
* Read the status register , returning its value in the location
* Return the status register value .
* Returns negative if error occurred .
*/
static int read_sr ( struct m25p * flash )
{
ssize_t retval ;
u8 code = OPCODE_RDSR ;
u8 val ;
retval = spi_write_then_read ( flash - > spi , & code , 1 , & val , 1 ) ;
if ( retval < 0 ) {
dev_err ( & flash - > spi - > dev , " error %d reading SR \n " ,
( int ) retval ) ;
return retval ;
}
return val ;
}
/*
* Set write enable latch with Write Enable command .
* Returns negative if error occurred .
*/
static inline int write_enable ( struct m25p * flash )
{
u8 code = OPCODE_WREN ;
return spi_write_then_read ( flash - > spi , & code , 1 , NULL , 0 ) ;
}
/*
* Service routine to read status register until ready , or timeout occurs .
* Returns non - zero if error .
*/
static int wait_till_ready ( struct m25p * flash )
{
int count ;
int sr ;
/* one chip guarantees max 5 msec wait here after page writes,
* but potentially three seconds ( ! ) after page erase .
*/
for ( count = 0 ; count < MAX_READY_WAIT_COUNT ; count + + ) {
if ( ( sr = read_sr ( flash ) ) < 0 )
break ;
else if ( ! ( sr & SR_WIP ) )
return 0 ;
/* REVISIT sometimes sleeping would be best */
}
return 1 ;
}
/*
* 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 erase_sector ( struct m25p * flash , u32 offset )
{
2007-06-29 01:38:38 +04:00
DEBUG ( MTD_DEBUG_LEVEL3 , " %s: %s %dKiB at 0x%08x \n " ,
2007-06-25 02:12:35 +04:00
flash - > spi - > dev . bus_id , __FUNCTION__ ,
flash - > mtd . erasesize / 1024 , offset ) ;
2006-01-09 00:34:27 +03:00
/* Wait until finished previous write command. */
if ( wait_till_ready ( flash ) )
return 1 ;
/* Send write enable, then erase commands. */
write_enable ( flash ) ;
/* Set up command buffer. */
2007-06-25 02:12:35 +04:00
flash - > command [ 0 ] = flash - > erase_opcode ;
2006-01-09 00:34:27 +03:00
flash - > command [ 1 ] = offset > > 16 ;
flash - > command [ 2 ] = offset > > 8 ;
flash - > command [ 3 ] = offset ;
spi_write ( flash - > spi , flash - > command , sizeof ( flash - > command ) ) ;
return 0 ;
}
/****************************************************************************/
/*
* MTD implementation
*/
/*
* 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 m25p80_erase ( struct mtd_info * mtd , struct erase_info * instr )
{
struct m25p * flash = mtd_to_m25p ( mtd ) ;
u32 addr , len ;
2006-05-29 14:33:33 +04:00
DEBUG ( MTD_DEBUG_LEVEL2 , " %s: %s %s 0x%08x, len %d \n " ,
2006-01-09 00:34:27 +03:00
flash - > spi - > dev . bus_id , __FUNCTION__ , " at " ,
( u32 ) instr - > addr , instr - > len ) ;
/* sanity checks */
if ( instr - > addr + instr - > len > flash - > mtd . size )
return - EINVAL ;
if ( ( instr - > addr % mtd - > erasesize ) ! = 0
| | ( instr - > len % mtd - > erasesize ) ! = 0 ) {
return - EINVAL ;
}
addr = instr - > addr ;
len = instr - > len ;
2007-06-25 02:09:13 +04:00
mutex_lock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
2007-06-25 02:12:35 +04:00
/* REVISIT in some cases we could speed up erasing large regions
* by using OPCODE_SE instead of OPCODE_BE_4K
*/
2006-01-09 00:34:27 +03:00
/* now erase those sectors */
while ( len ) {
if ( erase_sector ( flash , addr ) ) {
instr - > state = MTD_ERASE_FAILED ;
2007-06-25 02:09:13 +04:00
mutex_unlock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
return - EIO ;
}
addr + = mtd - > erasesize ;
len - = mtd - > erasesize ;
}
2007-06-25 02:09:13 +04:00
mutex_unlock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
instr - > state = MTD_ERASE_DONE ;
mtd_erase_callback ( instr ) ;
return 0 ;
}
/*
* Read an address range from the flash chip . The address range
* may be any size provided it is within the physical boundaries .
*/
static int m25p80_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , u_char * buf )
{
struct m25p * flash = mtd_to_m25p ( mtd ) ;
struct spi_transfer t [ 2 ] ;
struct spi_message m ;
DEBUG ( MTD_DEBUG_LEVEL2 , " %s: %s %s 0x%08x, len %zd \n " ,
flash - > spi - > dev . bus_id , __FUNCTION__ , " from " ,
( u32 ) from , len ) ;
/* sanity checks */
if ( ! len )
return 0 ;
if ( from + len > flash - > mtd . size )
return - EINVAL ;
2006-01-09 00:34:28 +03:00
spi_message_init ( & m ) ;
memset ( t , 0 , ( sizeof t ) ) ;
t [ 0 ] . tx_buf = flash - > command ;
t [ 0 ] . len = sizeof ( flash - > command ) ;
spi_message_add_tail ( & t [ 0 ] , & m ) ;
t [ 1 ] . rx_buf = buf ;
t [ 1 ] . len = len ;
spi_message_add_tail ( & t [ 1 ] , & m ) ;
/* Byte count starts at zero. */
if ( retlen )
* retlen = 0 ;
2007-06-25 02:09:13 +04:00
mutex_lock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
/* Wait till previous write/erase is done. */
if ( wait_till_ready ( flash ) ) {
/* REVISIT status return?? */
2007-06-25 02:09:13 +04:00
mutex_unlock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
return 1 ;
}
2007-06-25 02:12:35 +04:00
/* FIXME switch to OPCODE_FAST_READ. It's required for higher
* clocks ; and at this writing , every chip this driver handles
* supports that opcode .
*/
2006-01-09 00:34:27 +03:00
/* Set up the write data buffer. */
flash - > command [ 0 ] = OPCODE_READ ;
flash - > command [ 1 ] = from > > 16 ;
flash - > command [ 2 ] = from > > 8 ;
flash - > command [ 3 ] = from ;
spi_sync ( flash - > spi , & m ) ;
* retlen = m . actual_length - sizeof ( flash - > command ) ;
2007-06-25 02:09:13 +04:00
mutex_unlock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
return 0 ;
}
/*
* Write an address range to the flash chip . Data must be written in
* FLASH_PAGESIZE chunks . The address range may be any size provided
* it is within the physical boundaries .
*/
static int m25p80_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
{
struct m25p * flash = mtd_to_m25p ( mtd ) ;
u32 page_offset , page_size ;
struct spi_transfer t [ 2 ] ;
struct spi_message m ;
DEBUG ( MTD_DEBUG_LEVEL2 , " %s: %s %s 0x%08x, len %zd \n " ,
flash - > spi - > dev . bus_id , __FUNCTION__ , " to " ,
( u32 ) to , len ) ;
if ( retlen )
* retlen = 0 ;
/* sanity checks */
if ( ! len )
return ( 0 ) ;
if ( to + len > flash - > mtd . size )
return - EINVAL ;
2006-01-09 00:34:28 +03:00
spi_message_init ( & m ) ;
memset ( t , 0 , ( sizeof t ) ) ;
t [ 0 ] . tx_buf = flash - > command ;
t [ 0 ] . len = sizeof ( flash - > command ) ;
spi_message_add_tail ( & t [ 0 ] , & m ) ;
t [ 1 ] . tx_buf = buf ;
spi_message_add_tail ( & t [ 1 ] , & m ) ;
2007-06-25 02:09:13 +04:00
mutex_lock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
/* Wait until finished previous write command. */
if ( wait_till_ready ( flash ) )
return 1 ;
write_enable ( flash ) ;
/* Set up the opcode in the write buffer. */
flash - > command [ 0 ] = OPCODE_PP ;
flash - > command [ 1 ] = to > > 16 ;
flash - > command [ 2 ] = to > > 8 ;
flash - > command [ 3 ] = to ;
/* what page do we start with? */
page_offset = to % FLASH_PAGESIZE ;
/* do all the bytes fit onto one page? */
if ( page_offset + len < = FLASH_PAGESIZE ) {
t [ 1 ] . len = len ;
spi_sync ( flash - > spi , & m ) ;
* retlen = m . actual_length - sizeof ( flash - > command ) ;
} else {
u32 i ;
/* the size of data remaining on the first page */
page_size = FLASH_PAGESIZE - page_offset ;
t [ 1 ] . len = page_size ;
spi_sync ( flash - > spi , & m ) ;
* retlen = m . actual_length - sizeof ( flash - > command ) ;
/* write everything in PAGESIZE chunks */
for ( i = page_size ; i < len ; i + = page_size ) {
page_size = len - i ;
if ( page_size > FLASH_PAGESIZE )
page_size = FLASH_PAGESIZE ;
/* write the next page to flash */
flash - > command [ 1 ] = ( to + i ) > > 16 ;
flash - > command [ 2 ] = ( to + i ) > > 8 ;
flash - > command [ 3 ] = ( to + i ) ;
t [ 1 ] . tx_buf = buf + i ;
t [ 1 ] . len = page_size ;
wait_till_ready ( flash ) ;
write_enable ( flash ) ;
spi_sync ( flash - > spi , & m ) ;
2006-01-09 00:34:29 +03:00
if ( retlen )
* retlen + = m . actual_length
- sizeof ( flash - > command ) ;
2007-06-25 02:09:13 +04:00
}
}
2006-01-09 00:34:27 +03:00
2007-06-25 02:09:13 +04:00
mutex_unlock ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
return 0 ;
}
/****************************************************************************/
/*
* SPI device driver setup and teardown
*/
struct flash_info {
char * name ;
2007-06-25 02:12:35 +04:00
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
* a high byte of zero plus three data bytes : the manufacturer id ,
* then a two byte device id .
*/
u32 jedec_id ;
/* The size listed here is what works with OPCODE_SE, which isn't
* necessarily called a " sector " by the vendor .
*/
2006-01-09 00:34:27 +03:00
unsigned sector_size ;
2007-06-25 02:12:35 +04:00
u16 n_sectors ;
u16 flags ;
# define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
2006-01-09 00:34:27 +03:00
} ;
2007-06-25 02:12:35 +04:00
/* NOTE: double check command sets and memory organization when you add
* more flash chips . This current list focusses on newer chips , which
* have been converging on command sets which including JEDEC ID .
*/
2006-01-09 00:34:27 +03:00
static struct flash_info __devinitdata m25p_data [ ] = {
2007-06-25 02:12:35 +04:00
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
{ " at25fs010 " , 0x1f6601 , 32 * 1024 , 4 , SECT_4K , } ,
{ " at25fs040 " , 0x1f6604 , 64 * 1024 , 8 , SECT_4K , } ,
{ " at25df041a " , 0x1f4401 , 64 * 1024 , 8 , SECT_4K , } ,
{ " at26f004 " , 0x1f0400 , 64 * 1024 , 8 , SECT_4K , } ,
{ " at26df081a " , 0x1f4501 , 64 * 1024 , 16 , SECT_4K , } ,
{ " at26df161a " , 0x1f4601 , 64 * 1024 , 32 , SECT_4K , } ,
{ " at26df321 " , 0x1f4701 , 64 * 1024 , 64 , SECT_4K , } ,
/* Spansion -- single (large) sector size only, at least
* for the chips listed here ( without boot sectors ) .
*/
{ " s25sl004a " , 0x010212 , 64 * 1024 , 8 , } ,
{ " s25sl008a " , 0x010213 , 64 * 1024 , 16 , } ,
{ " s25sl016a " , 0x010214 , 64 * 1024 , 32 , } ,
{ " s25sl032a " , 0x010215 , 64 * 1024 , 64 , } ,
{ " s25sl064a " , 0x010216 , 64 * 1024 , 128 , } ,
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ " sst25vf040b " , 0xbf258d , 64 * 1024 , 8 , SECT_4K , } ,
{ " sst25vf080b " , 0xbf258e , 64 * 1024 , 16 , SECT_4K , } ,
{ " sst25vf016b " , 0xbf2541 , 64 * 1024 , 32 , SECT_4K , } ,
{ " sst25vf032b " , 0xbf254a , 64 * 1024 , 64 , SECT_4K , } ,
/* ST Microelectronics -- newer production may have feature updates */
{ " m25p05 " , 0x202010 , 32 * 1024 , 2 , } ,
{ " m25p10 " , 0x202011 , 32 * 1024 , 4 , } ,
{ " m25p20 " , 0x202012 , 64 * 1024 , 4 , } ,
{ " m25p40 " , 0x202013 , 64 * 1024 , 8 , } ,
{ " m25p80 " , 0 , 64 * 1024 , 16 , } ,
{ " m25p16 " , 0x202015 , 64 * 1024 , 32 , } ,
{ " m25p32 " , 0x202016 , 64 * 1024 , 64 , } ,
{ " m25p64 " , 0x202017 , 64 * 1024 , 128 , } ,
{ " m25p128 " , 0x202018 , 256 * 1024 , 64 , } ,
{ " m45pe80 " , 0x204014 , 64 * 1024 , 16 , } ,
{ " m45pe16 " , 0x204015 , 64 * 1024 , 32 , } ,
{ " m25pe80 " , 0x208014 , 64 * 1024 , 16 , } ,
{ " m25pe16 " , 0x208015 , 64 * 1024 , 32 , SECT_4K , } ,
2007-06-29 01:38:38 +04:00
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
2007-06-25 02:12:35 +04:00
{ " w25x10 " , 0xef3011 , 64 * 1024 , 2 , SECT_4K , } ,
{ " w25x20 " , 0xef3012 , 64 * 1024 , 4 , SECT_4K , } ,
{ " w25x40 " , 0xef3013 , 64 * 1024 , 8 , SECT_4K , } ,
{ " w25x80 " , 0xef3014 , 64 * 1024 , 16 , SECT_4K , } ,
{ " w25x16 " , 0xef3015 , 64 * 1024 , 32 , SECT_4K , } ,
{ " w25x32 " , 0xef3016 , 64 * 1024 , 64 , SECT_4K , } ,
{ " w25x64 " , 0xef3017 , 64 * 1024 , 128 , SECT_4K , } ,
2006-01-09 00:34:27 +03:00
} ;
2007-06-25 02:12:35 +04:00
static struct flash_info * __devinit jedec_probe ( struct spi_device * spi )
{
int tmp ;
u8 code = OPCODE_RDID ;
u8 id [ 3 ] ;
u32 jedec ;
struct flash_info * info ;
/* JEDEC also defines an optional "extended device information"
* string for after vendor - specific data , after the three bytes
* we use here . Supporting some chips might require using it .
*/
tmp = spi_write_then_read ( spi , & code , 1 , id , 3 ) ;
if ( tmp < 0 ) {
DEBUG ( MTD_DEBUG_LEVEL0 , " %s: error %d reading JEDEC ID \n " ,
spi - > dev . bus_id , tmp ) ;
return NULL ;
}
jedec = id [ 0 ] ;
jedec = jedec < < 8 ;
jedec | = id [ 1 ] ;
jedec = jedec < < 8 ;
jedec | = id [ 2 ] ;
for ( tmp = 0 , info = m25p_data ;
tmp < ARRAY_SIZE ( m25p_data ) ;
tmp + + , info + + ) {
if ( info - > jedec_id = = jedec )
return info ;
}
dev_err ( & spi - > dev , " unrecognized JEDEC id %06x \n " , jedec ) ;
return NULL ;
}
2006-01-09 00:34:27 +03:00
/*
* board specific setup should have ensured the SPI clock used here
* matches what the READ command supports , at least until this driver
* understands FAST_READ ( for clocks over 25 MHz ) .
*/
static int __devinit m25p_probe ( struct spi_device * spi )
{
struct flash_platform_data * data ;
struct m25p * flash ;
struct flash_info * info ;
unsigned i ;
/* Platform data helps sort out which chip type we have, as
2007-06-25 02:12:35 +04:00
* well as how this board partitions it . If we don ' t have
* a chip ID , try the JEDEC id commands ; they ' ll work for most
* newer chips , even if we don ' t recognize the particular chip .
2006-01-09 00:34:27 +03:00
*/
data = spi - > dev . platform_data ;
2007-06-25 02:12:35 +04:00
if ( data & & data - > type ) {
for ( i = 0 , info = m25p_data ;
i < ARRAY_SIZE ( m25p_data ) ;
i + + , info + + ) {
if ( strcmp ( data - > type , info - > name ) = = 0 )
break ;
}
2006-01-09 00:34:27 +03:00
2007-06-25 02:12:35 +04:00
/* unrecognized chip? */
if ( i = = ARRAY_SIZE ( m25p_data ) ) {
DEBUG ( MTD_DEBUG_LEVEL0 , " %s: unrecognized id %s \n " ,
spi - > dev . bus_id , data - > type ) ;
info = NULL ;
/* recognized; is that chip really what's there? */
} else if ( info - > jedec_id ) {
struct flash_info * chip = jedec_probe ( spi ) ;
if ( ! chip | | chip ! = info ) {
dev_warn ( & spi - > dev , " found %s, expected %s \n " ,
chip ? chip - > name : " UNKNOWN " ,
info - > name ) ;
info = NULL ;
}
}
} else
info = jedec_probe ( spi ) ;
if ( ! info )
2006-01-09 00:34:27 +03:00
return - ENODEV ;
2006-12-07 07:33:17 +03:00
flash = kzalloc ( sizeof * flash , GFP_KERNEL ) ;
2006-01-09 00:34:27 +03:00
if ( ! flash )
return - ENOMEM ;
flash - > spi = spi ;
2007-06-25 02:09:13 +04:00
mutex_init ( & flash - > lock ) ;
2006-01-09 00:34:27 +03:00
dev_set_drvdata ( & spi - > dev , flash ) ;
2007-06-25 02:12:35 +04:00
if ( data & & data - > name )
2006-01-09 00:34:27 +03:00
flash - > mtd . name = data - > name ;
else
flash - > mtd . name = spi - > dev . bus_id ;
flash - > mtd . type = MTD_NORFLASH ;
2006-06-14 19:53:44 +04:00
flash - > mtd . writesize = 1 ;
2006-01-09 00:34:27 +03:00
flash - > mtd . flags = MTD_CAP_NORFLASH ;
flash - > mtd . size = info - > sector_size * info - > n_sectors ;
flash - > mtd . erase = m25p80_erase ;
flash - > mtd . read = m25p80_read ;
flash - > mtd . write = m25p80_write ;
2007-06-25 02:12:35 +04:00
/* prefer "small sector" erase if possible */
if ( info - > flags & SECT_4K ) {
flash - > erase_opcode = OPCODE_BE_4K ;
flash - > mtd . erasesize = 4096 ;
} else {
flash - > erase_opcode = OPCODE_SE ;
flash - > mtd . erasesize = info - > sector_size ;
}
2006-01-09 00:34:27 +03:00
dev_info ( & spi - > dev , " %s (%d Kbytes) \n " , info - > name ,
flash - > mtd . size / 1024 ) ;
DEBUG ( MTD_DEBUG_LEVEL2 ,
2007-06-29 01:38:38 +04:00
" mtd .name = %s, .size = 0x%.8x (%uMiB) "
" .erasesize = 0x%.8x (%uKiB) .numeraseregions = %d \n " ,
2006-01-09 00:34:27 +03:00
flash - > mtd . name ,
flash - > mtd . size , flash - > mtd . size / ( 1024 * 1024 ) ,
flash - > mtd . erasesize , flash - > mtd . erasesize / 1024 ,
flash - > mtd . numeraseregions ) ;
if ( flash - > mtd . numeraseregions )
for ( i = 0 ; i < flash - > mtd . numeraseregions ; i + + )
DEBUG ( MTD_DEBUG_LEVEL2 ,
" mtd.eraseregions[%d] = { .offset = 0x%.8x, "
2007-06-29 01:38:38 +04:00
" .erasesize = 0x%.8x (%uKiB), "
2006-01-09 00:34:27 +03:00
" .numblocks = %d } \n " ,
i , flash - > mtd . eraseregions [ i ] . offset ,
flash - > mtd . eraseregions [ i ] . erasesize ,
flash - > mtd . eraseregions [ i ] . erasesize / 1024 ,
flash - > mtd . eraseregions [ i ] . numblocks ) ;
/* partitions should match sector boundaries; and it may be good to
* use readonly partitions for writeprotected sectors ( BP2 . . BP0 ) .
*/
if ( mtd_has_partitions ( ) ) {
struct mtd_partition * parts = NULL ;
int nr_parts = 0 ;
# ifdef CONFIG_MTD_CMDLINE_PARTS
static const char * part_probes [ ] = { " cmdlinepart " , NULL , } ;
nr_parts = parse_mtd_partitions ( & flash - > mtd ,
part_probes , & parts , 0 ) ;
# endif
if ( nr_parts < = 0 & & data & & data - > parts ) {
parts = data - > parts ;
nr_parts = data - > nr_parts ;
}
if ( nr_parts > 0 ) {
2007-06-25 02:12:35 +04:00
for ( i = 0 ; i < nr_parts ; i + + ) {
2006-01-09 00:34:27 +03:00
DEBUG ( MTD_DEBUG_LEVEL2 , " partitions[%d] = "
" {.name = %s, .offset = 0x%.8x, "
2007-06-29 01:38:38 +04:00
" .size = 0x%.8x (%uKiB) } \n " ,
2007-06-25 02:12:35 +04:00
i , parts [ i ] . name ,
parts [ i ] . offset ,
parts [ i ] . size ,
parts [ i ] . size / 1024 ) ;
2006-01-09 00:34:27 +03:00
}
flash - > partitioned = 1 ;
return add_mtd_partitions ( & flash - > mtd , parts , nr_parts ) ;
}
} else if ( data - > nr_parts )
dev_warn ( & spi - > dev , " ignoring %d default partitions on %s \n " ,
data - > nr_parts , data - > name ) ;
return add_mtd_device ( & flash - > mtd ) = = 1 ? - ENODEV : 0 ;
}
static int __devexit m25p_remove ( struct spi_device * spi )
{
struct m25p * flash = dev_get_drvdata ( & spi - > dev ) ;
int status ;
/* Clean up MTD stuff. */
if ( mtd_has_partitions ( ) & & flash - > partitioned )
status = del_mtd_partitions ( & flash - > mtd ) ;
else
status = del_mtd_device ( & flash - > mtd ) ;
if ( status = = 0 )
kfree ( flash ) ;
return 0 ;
}
static struct spi_driver m25p80_driver = {
. driver = {
. name = " m25p80 " ,
. bus = & spi_bus_type ,
. owner = THIS_MODULE ,
} ,
. probe = m25p_probe ,
. remove = __devexit_p ( m25p_remove ) ,
2007-06-25 02:12:35 +04:00
/* REVISIT: many of these chips have deep power-down modes, which
* should clearly be entered on suspend ( ) to minimize power use .
* And also when they ' re otherwise idle . . .
*/
2006-01-09 00:34:27 +03:00
} ;
static int m25p80_init ( void )
{
return spi_register_driver ( & m25p80_driver ) ;
}
static void m25p80_exit ( void )
{
spi_unregister_driver ( & m25p80_driver ) ;
}
module_init ( m25p80_init ) ;
module_exit ( m25p80_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Mike Lavender " ) ;
MODULE_DESCRIPTION ( " MTD SPI driver for ST M25Pxx flash chips " ) ;