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-03-20 05:00:12 -07:00
int dummy = nor - > read_dummy ;
int ret ;
2006-01-08 13:34:27 -08:00
2014-03-20 05:00:12 -07:00
/* Wait till previous write/erase is done. */
ret = nor - > wait_till_ready ( nor ) ;
if ( ret )
return ret ;
2006-01-08 13:34:27 -08:00
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 ;
int ret ;
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
/* Wait until finished previous write command. */
2014-03-20 05:00:12 -07:00
ret = nor - > wait_till_ready ( nor ) ;
2009-06-15 08:23:41 +00:00
if ( ret )
2014-03-20 05:00:12 -07:00
return ret ;
2009-06-15 08:23:41 +00:00
2014-03-20 05:00:12 -07:00
/* Send write enable, then erase commands. */
2014-04-08 18:15:31 -07:00
ret = nor - > write_reg ( nor , SPINOR_OP_WREN , NULL , 0 , 0 ) ;
2009-06-15 08:23:41 +00:00
if ( ret )
2014-03-20 05:00:12 -07:00
return ret ;
2007-06-24 15:12:35 -07: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 ;
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
}
static struct spi_driver m25p80_driver = {
. driver = {
. name = " m25p80 " ,
. owner = THIS_MODULE ,
} ,
2014-03-20 05:00:12 -07:00
. id_table = spi_nor_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 " ) ;