2006-01-08 13:34:27 -08:00
/*
2007-06-24 15:12:35 -07:00
* MTD SPI driver for ST M25Pxx ( and similar ) serial flash chips
2006-01-08 13:34:27 -08: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 .
*
*/
2010-06-22 20:57:42 +04:00
# include <linux/err.h>
# include <linux/errno.h>
2006-01-08 13:34:27 -08:00
# include <linux/module.h>
# include <linux/device.h>
2007-06-24 15:09:13 -07:00
2006-01-08 13:34:27 -08:00
# include <linux/mtd/mtd.h>
# include <linux/mtd/partitions.h>
2007-06-24 15:09:13 -07:00
2006-01-08 13:34:27 -08:00
# include <linux/spi/spi.h>
# include <linux/spi/flash.h>
2014-03-20 05:00:12 -07:00
# include <linux/mtd/spi-nor.h>
2006-01-08 13:34:27 -08:00
2013-07-24 18:32:07 -07:00
# define MAX_CMD_SIZE 6
2006-01-08 13:34:27 -08:00
struct m25p {
struct spi_device * spi ;
2014-03-20 05:00:12 -07:00
struct spi_nor spi_nor ;
2006-01-08 13:34:27 -08:00
struct mtd_info mtd ;
2014-03-20 05:00:12 -07:00
u8 command [ MAX_CMD_SIZE ] ;
2006-01-08 13:34:27 -08:00
} ;
2014-03-20 05:00:12 -07:00
static int m25p80_read_reg ( struct spi_nor * nor , u8 code , u8 * val , int len )
2013-11-06 20:05:35 +05:30
{
2014-03-20 05:00:12 -07:00
struct m25p * flash = nor - > priv ;
struct spi_device * spi = flash - > spi ;
2013-11-06 20:05:35 +05:30
int ret ;
2014-03-20 05:00:12 -07:00
ret = spi_write_then_read ( spi , & code , 1 , val , len ) ;
if ( ret < 0 )
dev_err ( & spi - > dev , " error %d reading %x \n " , ret , code ) ;
2008-08-11 16:59:13 +08:00
2014-03-20 05:00:12 -07:00
return ret ;
2008-08-11 16:59:13 +08:00
}
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
static void m25p_addr2cmd ( struct spi_nor * nor , unsigned int addr , u8 * cmd )
mtd: m25p80: Add support for CAT25xxx serial EEPROMs
CAT25 chips (as manufactured by On Semiconductor, previously Catalyst
Semiconductor) are similar to the original M25Px0 chips, except:
- Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P
chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz()
calls, and place address width information into flash_info struct;
- Page size can vary, therefore we shouldn't hardcode it, so get rid
of FLASH_PAGESIZE definition, and place the page size information
into flash_info struct;
- CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and
propagate it to the mtd subsystem.
[dwmw2: Fix up for conflicts with DMA safety patch]
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-10-12 20:24:40 +04:00
{
/* opcode is in cmd[0] */
2014-03-20 05:00:12 -07:00
cmd [ 1 ] = addr > > ( nor - > addr_width * 8 - 8 ) ;
cmd [ 2 ] = addr > > ( nor - > addr_width * 8 - 16 ) ;
cmd [ 3 ] = addr > > ( nor - > addr_width * 8 - 24 ) ;
cmd [ 4 ] = addr > > ( nor - > addr_width * 8 - 32 ) ;
mtd: m25p80: Add support for CAT25xxx serial EEPROMs
CAT25 chips (as manufactured by On Semiconductor, previously Catalyst
Semiconductor) are similar to the original M25Px0 chips, except:
- Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P
chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz()
calls, and place address width information into flash_info struct;
- Page size can vary, therefore we shouldn't hardcode it, so get rid
of FLASH_PAGESIZE definition, and place the page size information
into flash_info struct;
- CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and
propagate it to the mtd subsystem.
[dwmw2: Fix up for conflicts with DMA safety patch]
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-10-12 20:24:40 +04:00
}
2014-03-20 05:00:12 -07:00
static int m25p_cmdsz ( struct spi_nor * nor )
mtd: m25p80: Add support for CAT25xxx serial EEPROMs
CAT25 chips (as manufactured by On Semiconductor, previously Catalyst
Semiconductor) are similar to the original M25Px0 chips, except:
- Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P
chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz()
calls, and place address width information into flash_info struct;
- Page size can vary, therefore we shouldn't hardcode it, so get rid
of FLASH_PAGESIZE definition, and place the page size information
into flash_info struct;
- CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and
propagate it to the mtd subsystem.
[dwmw2: Fix up for conflicts with DMA safety patch]
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-10-12 20:24:40 +04:00
{
2014-03-20 05:00:12 -07:00
return 1 + nor - > addr_width ;
mtd: m25p80: Add support for CAT25xxx serial EEPROMs
CAT25 chips (as manufactured by On Semiconductor, previously Catalyst
Semiconductor) are similar to the original M25Px0 chips, except:
- Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P
chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz()
calls, and place address width information into flash_info struct;
- Page size can vary, therefore we shouldn't hardcode it, so get rid
of FLASH_PAGESIZE definition, and place the page size information
into flash_info struct;
- CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and
propagate it to the mtd subsystem.
[dwmw2: Fix up for conflicts with DMA safety patch]
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-10-12 20:24:40 +04:00
}
2014-03-20 05:00:12 -07:00
static int m25p80_write_reg ( struct spi_nor * nor , u8 opcode , u8 * buf , int len ,
int wr_en )
2006-01-08 13:34:27 -08:00
{
2014-03-20 05:00:12 -07:00
struct m25p * flash = nor - > priv ;
struct spi_device * spi = flash - > spi ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
flash - > command [ 0 ] = opcode ;
if ( buf )
memcpy ( & flash - > command [ 1 ] , buf , len ) ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
return spi_write ( spi , flash - > command , len + 1 ) ;
2006-01-08 13:34:27 -08:00
}
2014-03-20 05:00:12 -07:00
static void m25p80_write ( struct spi_nor * nor , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
2006-01-08 13:34:27 -08:00
{
2014-03-20 05:00:12 -07:00
struct m25p * flash = nor - > priv ;
struct spi_device * spi = flash - > spi ;
struct spi_transfer t [ 2 ] = { } ;
struct spi_message m ;
int cmd_sz = m25p_cmdsz ( nor ) ;
2008-11-26 10:23:57 +00:00
2014-03-20 05:00:12 -07:00
spi_message_init ( & m ) ;
2008-11-26 10:23:57 +00:00
2014-04-08 18:15:31 -07:00
if ( nor - > program_opcode = = SPINOR_OP_AAI_WP & & nor - > sst_write_second )
2014-03-20 05:00:12 -07:00
cmd_sz = 1 ;
2008-08-11 16:59:13 +08:00
2014-03-20 05:00:12 -07:00
flash - > command [ 0 ] = nor - > program_opcode ;
m25p_addr2cmd ( nor , to , flash - > command ) ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
t [ 0 ] . tx_buf = flash - > command ;
t [ 0 ] . len = cmd_sz ;
spi_message_add_tail ( & t [ 0 ] , & m ) ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
t [ 1 ] . tx_buf = buf ;
t [ 1 ] . len = len ;
spi_message_add_tail ( & t [ 1 ] , & m ) ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
spi_sync ( spi , & m ) ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
* retlen + = m . actual_length - cmd_sz ;
2013-11-06 20:05:34 +05:30
}
2014-03-20 05:00:12 -07:00
static inline unsigned int m25p80_rx_nbits ( struct spi_nor * nor )
2014-01-21 13:59:17 +01:00
{
2014-03-20 05:00:12 -07:00
switch ( nor - > flash_read ) {
case SPI_NOR_DUAL :
2014-01-21 13:59:18 +01:00
return 2 ;
2014-03-20 05:00:12 -07:00
case SPI_NOR_QUAD :
2014-01-21 13:59:17 +01:00
return 4 ;
default :
return 0 ;
}
}
2006-01-08 13:34:27 -08:00
/*
2014-03-20 05:00:12 -07:00
* Read an address range from the nor chip . The address range
2006-01-08 13:34:27 -08:00
* may be any size provided it is within the physical boundaries .
*/
2014-03-20 05:00:12 -07:00
static int m25p80_read ( struct spi_nor * nor , loff_t from , size_t len ,
size_t * retlen , u_char * buf )
2006-01-08 13:34:27 -08:00
{
2014-03-20 05:00:12 -07:00
struct m25p * flash = nor - > priv ;
struct spi_device * spi = flash - > spi ;
2006-01-08 13:34:27 -08:00
struct spi_transfer t [ 2 ] ;
struct spi_message m ;
2014-04-28 11:53:38 +08:00
unsigned int dummy = nor - > read_dummy ;
2006-01-08 13:34:27 -08:00
2014-04-28 11:53:38 +08:00
/* convert the dummy cycles to the number of bytes */
dummy / = 8 ;
2006-01-08 13:34:28 -08:00
spi_message_init ( & m ) ;
memset ( t , 0 , ( sizeof t ) ) ;
2014-03-20 05:00:12 -07:00
flash - > command [ 0 ] = nor - > read_opcode ;
m25p_addr2cmd ( nor , from , flash - > command ) ;
2013-11-06 20:05:34 +05:30
2006-01-08 13:34:28 -08:00
t [ 0 ] . tx_buf = flash - > command ;
2014-03-20 05:00:12 -07:00
t [ 0 ] . len = m25p_cmdsz ( nor ) + dummy ;
2006-01-08 13:34:28 -08:00
spi_message_add_tail ( & t [ 0 ] , & m ) ;
t [ 1 ] . rx_buf = buf ;
2014-03-20 05:00:12 -07:00
t [ 1 ] . rx_nbits = m25p80_rx_nbits ( nor ) ;
2006-01-08 13:34:28 -08:00
t [ 1 ] . len = len ;
spi_message_add_tail ( & t [ 1 ] , & m ) ;
2014-03-20 05:00:12 -07:00
spi_sync ( spi , & m ) ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
* retlen = m . actual_length - m25p_cmdsz ( nor ) - dummy ;
2006-01-08 13:34:27 -08:00
return 0 ;
}
2014-03-20 05:00:12 -07:00
static int m25p80_erase ( struct spi_nor * nor , loff_t offset )
2006-01-08 13:34:27 -08:00
{
2014-03-20 05:00:12 -07:00
struct m25p * flash = nor - > priv ;
2009-06-15 08:23:41 +00:00
2014-03-20 05:00:12 -07:00
dev_dbg ( nor - > dev , " %dKiB at 0x%08x \n " ,
flash - > mtd . erasesize / 1024 , ( u32 ) offset ) ;
2009-06-15 08:23:41 +00:00
2014-03-20 05:00:12 -07:00
/* Set up command buffer. */
flash - > command [ 0 ] = nor - > erase_opcode ;
m25p_addr2cmd ( nor , offset , flash - > command ) ;
2007-06-24 15:12:35 -07:00
2014-03-20 05:00:12 -07:00
spi_write ( flash - > spi , flash - > command , m25p_cmdsz ( nor ) ) ;
2008-08-11 16:59:15 +08:00
2014-03-20 05:00:12 -07:00
return 0 ;
2007-06-24 15:12:35 -07:00
}
2006-01-08 13:34:27 -08: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 ) .
*/
2012-11-19 13:23:07 -05:00
static int m25p_probe ( struct spi_device * spi )
2006-01-08 13:34:27 -08:00
{
2011-05-30 01:02:20 +04:00
struct mtd_part_parser_data ppdata ;
2014-03-20 05:00:12 -07:00
struct flash_platform_data * data ;
struct m25p * flash ;
struct spi_nor * nor ;
enum read_mode mode = SPI_NOR_NORMAL ;
2014-09-29 11:47:53 +02:00
char * flash_name = NULL ;
2013-11-06 20:05:35 +05:30
int ret ;
2011-10-14 15:49:00 +08:00
2014-09-28 22:36:54 +02:00
data = dev_get_platdata ( & spi - > dev ) ;
2013-07-24 18:32:07 -07:00
flash = devm_kzalloc ( & spi - > dev , sizeof ( * flash ) , GFP_KERNEL ) ;
2006-01-08 13:34:27 -08:00
if ( ! flash )
return - ENOMEM ;
2013-07-24 18:32:07 -07:00
2014-03-20 05:00:12 -07:00
nor = & flash - > spi_nor ;
2008-07-03 23:54:42 -07:00
2014-03-20 05:00:12 -07:00
/* install the hooks */
nor - > read = m25p80_read ;
nor - > write = m25p80_write ;
nor - > erase = m25p80_erase ;
nor - > write_reg = m25p80_write_reg ;
nor - > read_reg = m25p80_read_reg ;
2013-01-04 13:02:28 +13:00
2014-03-20 05:00:12 -07:00
nor - > dev = & spi - > dev ;
nor - > mtd = & flash - > mtd ;
nor - > priv = flash ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
spi_set_drvdata ( spi , flash ) ;
flash - > mtd . priv = nor ;
flash - > spi = spi ;
2007-06-24 15:12:35 -07:00
2014-03-20 05:00:12 -07:00
if ( spi - > mode & SPI_RX_QUAD )
mode = SPI_NOR_QUAD ;
2014-04-22 14:45:31 +02:00
else if ( spi - > mode & SPI_RX_DUAL )
mode = SPI_NOR_DUAL ;
2014-09-28 22:36:54 +02:00
if ( data & & data - > name )
flash - > mtd . name = data - > name ;
/* For some (historical?) reason many platforms provide two different
* names in flash_platform_data : " name " and " type " . Quite often name is
* set to " m25p80 " and then " type " provides a real chip name .
* If that ' s the case , respect " type " and ignore a " name " .
*/
if ( data & & data - > type )
2014-09-29 11:47:53 +02:00
flash_name = data - > type ;
2015-03-27 10:29:50 -07:00
else if ( ! strcmp ( spi - > modalias , " nor-jedec " ) )
flash_name = NULL ; /* auto-detect */
2014-09-29 11:47:53 +02:00
else
flash_name = spi - > modalias ;
2014-09-28 22:36:54 +02:00
2014-09-29 11:47:54 +02:00
ret = spi_nor_scan ( nor , flash_name , mode ) ;
2014-03-20 05:00:12 -07:00
if ( ret )
return ret ;
mtd: m25p80: Add support for CAT25xxx serial EEPROMs
CAT25 chips (as manufactured by On Semiconductor, previously Catalyst
Semiconductor) are similar to the original M25Px0 chips, except:
- Address width can vary (1-2 bytes, in contrast to 3 bytes in M25P
chips). So, implement convenient m25p_addr2cmd() and m25p_cmdsz()
calls, and place address width information into flash_info struct;
- Page size can vary, therefore we shouldn't hardcode it, so get rid
of FLASH_PAGESIZE definition, and place the page size information
into flash_info struct;
- CAT25 EEPROMs don't need to be erased, so add NO_ERASE flag, and
propagate it to the mtd subsystem.
[dwmw2: Fix up for conflicts with DMA safety patch]
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
2009-10-12 20:24:40 +04:00
2011-05-30 01:02:20 +04:00
ppdata . of_node = spi - > dev . of_node ;
2013-11-06 20:05:35 +05:30
2011-06-02 17:59:16 +04:00
return mtd_device_parse_register ( & flash - > mtd , NULL , & ppdata ,
data ? data - > parts : NULL ,
data ? data - > nr_parts : 0 ) ;
2006-01-08 13:34:27 -08:00
}
2012-11-19 13:26:04 -05:00
static int m25p_remove ( struct spi_device * spi )
2006-01-08 13:34:27 -08:00
{
2013-04-06 15:41:32 +09:00
struct m25p * flash = spi_get_drvdata ( spi ) ;
2006-01-08 13:34:27 -08:00
/* Clean up MTD stuff. */
2013-10-27 15:42:12 -07:00
return mtd_device_unregister ( & flash - > mtd ) ;
2006-01-08 13:34:27 -08:00
}
2014-09-30 03:14:55 +01:00
/*
2015-03-27 10:29:50 -07:00
* Do NOT add to this array without reading the following :
*
* Historically , many flash devices are bound to this driver by their name . But
* since most of these flash are compatible to some extent , and their
* differences can often be differentiated by the JEDEC read - ID command , we
* encourage new users to add support to the spi - nor library , and simply bind
* against a generic string here ( e . g . , " nor-jedec " ) .
*
* Many flash names are kept here in this list ( as well as in spi - nor . c ) to
* keep them available as module aliases for existing platforms .
2014-09-30 03:14:55 +01:00
*/
static const struct spi_device_id m25p_ids [ ] = {
{ " at25fs010 " } , { " at25fs040 " } , { " at25df041a " } , { " at25df321a " } ,
{ " at25df641 " } , { " at26f004 " } , { " at26df081a " } , { " at26df161a " } ,
{ " at26df321 " } , { " at45db081d " } ,
{ " en25f32 " } , { " en25p32 " } , { " en25q32b " } , { " en25p64 " } ,
{ " en25q64 " } , { " en25qh128 " } , { " en25qh256 " } ,
{ " f25l32pa " } ,
{ " mr25h256 " } , { " mr25h10 " } ,
{ " gd25q32 " } , { " gd25q64 " } ,
{ " 160s33b " } , { " 320s33b " } , { " 640s33b " } ,
{ " mx25l2005a " } , { " mx25l4005a " } , { " mx25l8005 " } , { " mx25l1606e " } ,
{ " mx25l3205d " } , { " mx25l3255e " } , { " mx25l6405d " } , { " mx25l12805d " } ,
{ " mx25l12855e " } , { " mx25l25635e " } , { " mx25l25655e " } , { " mx66l51235l " } ,
{ " mx66l1g55g " } ,
{ " n25q064 " } , { " n25q128a11 " } , { " n25q128a13 " } , { " n25q256a " } ,
{ " n25q512a " } , { " n25q512ax3 " } , { " n25q00 " } ,
{ " pm25lv512 " } , { " pm25lv010 " } , { " pm25lq032 " } ,
{ " s25sl032p " } , { " s25sl064p " } , { " s25fl256s0 " } , { " s25fl256s1 " } ,
{ " s25fl512s " } , { " s70fl01gs " } , { " s25sl12800 " } , { " s25sl12801 " } ,
{ " s25fl129p0 " } , { " s25fl129p1 " } , { " s25sl004a " } , { " s25sl008a " } ,
{ " s25sl016a " } , { " s25sl032a " } , { " s25sl064a " } , { " s25fl008k " } ,
2014-11-10 16:54:53 +01:00
{ " s25fl016k " } , { " s25fl064k " } , { " s25fl132k " } ,
2014-09-30 03:14:55 +01:00
{ " sst25vf040b " } , { " sst25vf080b " } , { " sst25vf016b " } , { " sst25vf032b " } ,
{ " sst25vf064c " } , { " sst25wf512 " } , { " sst25wf010 " } , { " sst25wf020 " } ,
{ " sst25wf040 " } ,
{ " m25p05 " } , { " m25p10 " } , { " m25p20 " } , { " m25p40 " } ,
{ " m25p80 " } , { " m25p16 " } , { " m25p32 " } , { " m25p64 " } ,
{ " m25p128 " } , { " n25q032 " } ,
{ " m25p05-nonjedec " } , { " m25p10-nonjedec " } , { " m25p20-nonjedec " } ,
{ " m25p40-nonjedec " } , { " m25p80-nonjedec " } , { " m25p16-nonjedec " } ,
{ " m25p32-nonjedec " } , { " m25p64-nonjedec " } , { " m25p128-nonjedec " } ,
{ " m45pe10 " } , { " m45pe80 " } , { " m45pe16 " } ,
{ " m25pe20 " } , { " m25pe80 " } , { " m25pe16 " } ,
{ " m25px16 " } , { " m25px32 " } , { " m25px32-s0 " } , { " m25px32-s1 " } ,
2014-11-10 16:54:54 +01:00
{ " m25px64 " } , { " m25px80 " } ,
2014-09-30 03:14:55 +01:00
{ " w25x10 " } , { " w25x20 " } , { " w25x40 " } , { " w25x80 " } ,
{ " w25x16 " } , { " w25x32 " } , { " w25q32 " } , { " w25q32dw " } ,
2014-11-10 16:54:54 +01:00
{ " w25x64 " } , { " w25q64 " } , { " w25q80 " } , { " w25q80bl " } ,
{ " w25q128 " } , { " w25q256 " } , { " cat25c11 " } ,
2014-09-30 03:14:55 +01:00
{ " cat25c03 " } , { " cat25c09 " } , { " cat25c17 " } , { " cat25128 " } ,
2015-03-27 10:29:50 -07:00
/*
* Generic support for SPI NOR that can be identified by the JEDEC READ
* ID opcode ( 0x9F ) . Use this , if possible .
*/
{ " nor-jedec " } ,
2014-09-30 03:14:55 +01:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( spi , m25p_ids ) ;
2006-01-08 13:34:27 -08:00
static struct spi_driver m25p80_driver = {
. driver = {
. name = " m25p80 " ,
. owner = THIS_MODULE ,
} ,
2014-09-30 03:14:55 +01:00
. id_table = m25p_ids ,
2006-01-08 13:34:27 -08:00
. probe = m25p_probe ,
2012-11-19 13:21:24 -05:00
. remove = m25p_remove ,
2007-06-24 15:12:35 -07: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-08 13:34:27 -08:00
} ;
2012-01-27 15:45:20 +08:00
module_spi_driver ( m25p80_driver ) ;
2006-01-08 13:34:27 -08:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Mike Lavender " ) ;
MODULE_DESCRIPTION ( " MTD SPI driver for ST M25Pxx flash chips " ) ;