2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2007-02-12 00:52:48 -08:00
/*
* at25 . c - - support most SPI EEPROMs , such as Atmel AT25 models
*
* Copyright ( C ) 2006 David Brownell
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/sched.h>
2016-02-26 20:59:22 +01:00
# include <linux/nvmem-provider.h>
2007-02-12 00:52:48 -08:00
# include <linux/spi/spi.h>
# include <linux/spi/eeprom.h>
2014-10-21 13:33:56 +02:00
# include <linux/property.h>
2007-02-12 00:52:48 -08:00
2007-12-04 23:45:10 -08:00
/*
* NOTE : this is an * EEPROM * driver . The vagaries of product naming
* mean that some AT25 products are EEPROMs , and others are FLASH .
* Handle FLASH chips with the drivers / mtd / devices / m25p80 . c driver ,
* not this one !
2020-11-07 14:33:35 +01:00
*
* EEPROMs that can be used with this driver include , for example :
* AT25M02 , AT25128B
2007-12-04 23:45:10 -08:00
*/
2007-02-12 00:52:48 -08:00
struct at25_data {
struct spi_device * spi ;
struct mutex lock ;
struct spi_eeprom chip ;
unsigned addrlen ;
2016-02-26 20:59:22 +01:00
struct nvmem_config nvmem_config ;
struct nvmem_device * nvmem ;
2007-02-12 00:52:48 -08:00
} ;
# define AT25_WREN 0x06 /* latch the write enable */
# define AT25_WRDI 0x04 /* reset the write enable */
# define AT25_RDSR 0x05 /* read status register */
# define AT25_WRSR 0x01 /* write status register */
# define AT25_READ 0x03 /* read byte(s) */
# define AT25_WRITE 0x02 /* write byte(s)/sector */
# define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */
# define AT25_SR_WEN 0x02 /* write enable (latched) */
# define AT25_SR_BP0 0x04 /* BP for software writeprotect */
# define AT25_SR_BP1 0x08
# define AT25_SR_WPEN 0x80 /* writeprotect enable */
2012-04-18 08:29:34 +02:00
# define AT25_INSTR_BIT3 0x08 /* Additional address bit in instr */
2007-02-12 00:52:48 -08:00
# define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */
/* Specs often allow 5 msec for a page write, sometimes 20 msec;
* it ' s important to recover from write timeouts .
*/
# define EE_TIMEOUT 25
/*-------------------------------------------------------------------------*/
# define io_limit PAGE_SIZE /* bytes */
2016-04-24 20:28:07 +01:00
static int at25_ee_read ( void * priv , unsigned int offset ,
void * val , size_t count )
2007-02-12 00:52:48 -08:00
{
2016-04-24 20:28:07 +01:00
struct at25_data * at25 = priv ;
char * buf = val ;
2007-02-12 00:52:48 -08:00
u8 command [ EE_MAXADDRLEN + 1 ] ;
u8 * cp ;
ssize_t status ;
struct spi_transfer t [ 2 ] ;
struct spi_message m ;
2012-04-18 08:29:34 +02:00
u8 instr ;
2007-02-12 00:52:48 -08:00
2016-02-26 20:59:22 +01:00
if ( unlikely ( offset > = at25 - > chip . byte_len ) )
2016-04-24 20:28:07 +01:00
return - EINVAL ;
2016-02-26 20:59:22 +01:00
if ( ( offset + count ) > at25 - > chip . byte_len )
count = at25 - > chip . byte_len - offset ;
2009-04-02 16:56:58 -07:00
if ( unlikely ( ! count ) )
2016-04-24 20:28:07 +01:00
return - EINVAL ;
2009-04-02 16:56:58 -07:00
2007-02-12 00:52:48 -08:00
cp = command ;
2012-04-18 08:29:34 +02:00
instr = AT25_READ ;
if ( at25 - > chip . flags & EE_INSTR_BIT3_IS_ADDR )
if ( offset > = ( 1U < < ( at25 - > addrlen * 8 ) ) )
instr | = AT25_INSTR_BIT3 ;
* cp + + = instr ;
2007-02-12 00:52:48 -08:00
/* 8/16/24-bit address is written MSB first */
switch ( at25 - > addrlen ) {
default : /* case 3 */
* cp + + = offset > > 16 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2007-02-12 00:52:48 -08:00
case 2 :
* cp + + = offset > > 8 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2007-02-12 00:52:48 -08:00
case 1 :
case 0 : /* can't happen: for better codegen */
* cp + + = offset > > 0 ;
}
spi_message_init ( & m ) ;
2018-03-21 11:04:35 +05:30
memset ( t , 0 , sizeof ( t ) ) ;
2007-02-12 00:52:48 -08:00
t [ 0 ] . tx_buf = command ;
t [ 0 ] . len = at25 - > addrlen + 1 ;
spi_message_add_tail ( & t [ 0 ] , & m ) ;
t [ 1 ] . rx_buf = buf ;
t [ 1 ] . len = count ;
spi_message_add_tail ( & t [ 1 ] , & m ) ;
mutex_lock ( & at25 - > lock ) ;
/* Read it all at once.
*
* REVISIT that ' s potentially a problem with large chips , if
* other devices on the bus need to be accessed regularly or
* this chip is clocked very slowly
*/
status = spi_sync ( at25 - > spi , & m ) ;
2016-09-11 14:58:26 +03:00
dev_dbg ( & at25 - > spi - > dev , " read %zu bytes at %d --> %zd \n " ,
count , offset , status ) ;
2007-02-12 00:52:48 -08:00
mutex_unlock ( & at25 - > lock ) ;
2016-04-24 20:28:07 +01:00
return status ;
2007-02-12 00:52:48 -08:00
}
2016-04-24 20:28:07 +01:00
static int at25_ee_write ( void * priv , unsigned int off , void * val , size_t count )
2007-02-12 00:52:48 -08:00
{
2016-04-24 20:28:07 +01:00
struct at25_data * at25 = priv ;
const char * buf = val ;
int status = 0 ;
2007-02-12 00:52:48 -08:00
unsigned buf_size ;
u8 * bounce ;
2016-02-26 20:59:22 +01:00
if ( unlikely ( off > = at25 - > chip . byte_len ) )
2009-04-02 16:56:58 -07:00
return - EFBIG ;
2016-02-26 20:59:22 +01:00
if ( ( off + count ) > at25 - > chip . byte_len )
count = at25 - > chip . byte_len - off ;
2009-04-02 16:56:58 -07:00
if ( unlikely ( ! count ) )
2016-04-24 20:28:07 +01:00
return - EINVAL ;
2009-04-02 16:56:58 -07:00
2007-02-12 00:52:48 -08:00
/* Temp buffer starts with command and address */
buf_size = at25 - > chip . page_size ;
if ( buf_size > io_limit )
buf_size = io_limit ;
bounce = kmalloc ( buf_size + at25 - > addrlen + 1 , GFP_KERNEL ) ;
if ( ! bounce )
return - ENOMEM ;
/* For write, rollover is within the page ... so we write at
* most one page , then manually roll over to the next page .
*/
mutex_lock ( & at25 - > lock ) ;
do {
unsigned long timeout , retries ;
unsigned segment ;
unsigned offset = ( unsigned ) off ;
2012-04-18 08:29:34 +02:00
u8 * cp = bounce ;
2009-07-29 15:04:05 -07:00
int sr ;
2012-04-18 08:29:34 +02:00
u8 instr ;
2007-02-12 00:52:48 -08:00
* cp = AT25_WREN ;
status = spi_write ( at25 - > spi , cp , 1 ) ;
if ( status < 0 ) {
2016-09-11 14:58:26 +03:00
dev_dbg ( & at25 - > spi - > dev , " WREN --> %d \n " , status ) ;
2007-02-12 00:52:48 -08:00
break ;
}
2012-04-18 08:29:34 +02:00
instr = AT25_WRITE ;
if ( at25 - > chip . flags & EE_INSTR_BIT3_IS_ADDR )
if ( offset > = ( 1U < < ( at25 - > addrlen * 8 ) ) )
instr | = AT25_INSTR_BIT3 ;
* cp + + = instr ;
2007-02-12 00:52:48 -08:00
/* 8/16/24-bit address is written MSB first */
switch ( at25 - > addrlen ) {
default : /* case 3 */
* cp + + = offset > > 16 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2007-02-12 00:52:48 -08:00
case 2 :
* cp + + = offset > > 8 ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2007-02-12 00:52:48 -08:00
case 1 :
case 0 : /* can't happen: for better codegen */
* cp + + = offset > > 0 ;
}
/* Write as much of a page as we can */
segment = buf_size - ( offset % buf_size ) ;
if ( segment > count )
segment = count ;
memcpy ( cp , buf , segment ) ;
status = spi_write ( at25 - > spi , bounce ,
segment + at25 - > addrlen + 1 ) ;
2016-09-11 14:58:26 +03:00
dev_dbg ( & at25 - > spi - > dev , " write %u bytes at %u --> %d \n " ,
segment , offset , status ) ;
2007-02-12 00:52:48 -08:00
if ( status < 0 )
break ;
/* REVISIT this should detect (or prevent) failed writes
* to readonly sections of the EEPROM . . .
*/
/* Wait for non-busy status */
timeout = jiffies + msecs_to_jiffies ( EE_TIMEOUT ) ;
retries = 0 ;
do {
sr = spi_w8r8 ( at25 - > spi , AT25_RDSR ) ;
if ( sr < 0 | | ( sr & AT25_SR_nRDY ) ) {
dev_dbg ( & at25 - > spi - > dev ,
" rdsr --> %d (%02x) \n " , sr , sr ) ;
/* at HZ=100, this is sloooow */
msleep ( 1 ) ;
continue ;
}
if ( ! ( sr & AT25_SR_nRDY ) )
break ;
} while ( retries + + < 3 | | time_before_eq ( jiffies , timeout ) ) ;
2009-07-29 15:04:05 -07:00
if ( ( sr < 0 ) | | ( sr & AT25_SR_nRDY ) ) {
2007-02-12 00:52:48 -08:00
dev_err ( & at25 - > spi - > dev ,
2016-09-11 14:58:26 +03:00
" write %u bytes offset %u, timeout after %u msecs \n " ,
2007-02-12 00:52:48 -08:00
segment , offset ,
jiffies_to_msecs ( jiffies -
( timeout - EE_TIMEOUT ) ) ) ;
status = - ETIMEDOUT ;
break ;
}
off + = segment ;
buf + = segment ;
count - = segment ;
} while ( count > 0 ) ;
mutex_unlock ( & at25 - > lock ) ;
kfree ( bounce ) ;
2016-04-24 20:28:07 +01:00
return status ;
2007-02-12 00:52:48 -08:00
}
/*-------------------------------------------------------------------------*/
2014-10-21 13:33:56 +02:00
static int at25_fw_to_chip ( struct device * dev , struct spi_eeprom * chip )
2012-08-22 12:03:57 -07:00
{
u32 val ;
memset ( chip , 0 , sizeof ( * chip ) ) ;
2014-10-21 13:33:56 +02:00
strncpy ( chip - > name , " at25 " , sizeof ( chip - > name ) ) ;
2012-08-22 12:03:57 -07:00
2014-10-21 13:33:56 +02:00
if ( device_property_read_u32 ( dev , " size " , & val ) = = 0 | |
device_property_read_u32 ( dev , " at25,byte-len " , & val ) = = 0 ) {
2012-08-22 12:03:57 -07:00
chip - > byte_len = val ;
} else {
dev_err ( dev , " Error: missing \" size \" property \n " ) ;
return - ENODEV ;
}
2014-10-21 13:33:56 +02:00
if ( device_property_read_u32 ( dev , " pagesize " , & val ) = = 0 | |
device_property_read_u32 ( dev , " at25,page-size " , & val ) = = 0 ) {
2020-07-27 13:12:18 +02:00
chip - > page_size = val ;
2012-08-22 12:03:57 -07:00
} else {
dev_err ( dev , " Error: missing \" pagesize \" property \n " ) ;
return - ENODEV ;
}
2014-10-21 13:33:56 +02:00
if ( device_property_read_u32 ( dev , " at25,addr-mode " , & val ) = = 0 ) {
2012-08-22 12:03:57 -07:00
chip - > flags = ( u16 ) val ;
} else {
2014-10-21 13:33:56 +02:00
if ( device_property_read_u32 ( dev , " address-width " , & val ) ) {
2012-08-22 12:03:57 -07:00
dev_err ( dev ,
" Error: missing \" address-width \" property \n " ) ;
return - ENODEV ;
}
switch ( val ) {
2017-12-08 14:46:41 +01:00
case 9 :
chip - > flags | = EE_INSTR_BIT3_IS_ADDR ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2012-08-22 12:03:57 -07:00
case 8 :
chip - > flags | = EE_ADDR1 ;
break ;
case 16 :
chip - > flags | = EE_ADDR2 ;
break ;
case 24 :
chip - > flags | = EE_ADDR3 ;
break ;
default :
dev_err ( dev ,
" Error: bad \" address-width \" property: %u \n " ,
val ) ;
return - ENODEV ;
}
2014-10-21 13:33:56 +02:00
if ( device_property_present ( dev , " read-only " ) )
2012-08-22 12:03:57 -07:00
chip - > flags | = EE_READONLY ;
}
return 0 ;
}
2007-02-12 00:52:48 -08:00
static int at25_probe ( struct spi_device * spi )
{
struct at25_data * at25 = NULL ;
2012-06-14 09:59:23 -03:00
struct spi_eeprom chip ;
2007-02-12 00:52:48 -08:00
int err ;
int sr ;
int addrlen ;
/* Chip description */
2012-06-14 09:59:23 -03:00
if ( ! spi - > dev . platform_data ) {
2014-10-21 13:33:56 +02:00
err = at25_fw_to_chip ( & spi - > dev , & chip ) ;
if ( err )
return err ;
2012-06-14 09:59:23 -03:00
} else
chip = * ( struct spi_eeprom * ) spi - > dev . platform_data ;
2007-02-12 00:52:48 -08:00
/* For now we only support 8/16/24 bit addressing */
2012-06-14 09:59:23 -03:00
if ( chip . flags & EE_ADDR1 )
2007-02-12 00:52:48 -08:00
addrlen = 1 ;
2012-06-14 09:59:23 -03:00
else if ( chip . flags & EE_ADDR2 )
2007-02-12 00:52:48 -08:00
addrlen = 2 ;
2012-06-14 09:59:23 -03:00
else if ( chip . flags & EE_ADDR3 )
2007-02-12 00:52:48 -08:00
addrlen = 3 ;
else {
dev_dbg ( & spi - > dev , " unsupported address type \n " ) ;
2013-05-28 13:01:21 -07:00
return - EINVAL ;
2007-02-12 00:52:48 -08:00
}
/* Ping the chip ... the status register is pretty portable,
* unlike probing manufacturer IDs . We do expect that system
* firmware didn ' t write it in the past few milliseconds !
*/
sr = spi_w8r8 ( spi , AT25_RDSR ) ;
if ( sr < 0 | | sr & AT25_SR_nRDY ) {
2007-03-16 13:38:20 -08:00
dev_dbg ( & spi - > dev , " rdsr --> %d (%02x) \n " , sr , sr ) ;
2013-05-28 13:01:21 -07:00
return - ENXIO ;
2007-02-12 00:52:48 -08:00
}
2013-05-28 13:01:21 -07:00
at25 = devm_kzalloc ( & spi - > dev , sizeof ( struct at25_data ) , GFP_KERNEL ) ;
if ( ! at25 )
return - ENOMEM ;
2007-02-12 00:52:48 -08:00
mutex_init ( & at25 - > lock ) ;
2012-06-14 09:59:23 -03:00
at25 - > chip = chip ;
2016-04-20 10:16:35 +01:00
at25 - > spi = spi ;
2013-04-05 10:55:35 +09:00
spi_set_drvdata ( spi , at25 ) ;
2007-02-12 00:52:48 -08:00
at25 - > addrlen = addrlen ;
2020-09-16 20:09:32 +03:00
at25 - > nvmem_config . type = NVMEM_TYPE_EEPROM ;
2016-02-26 20:59:22 +01:00
at25 - > nvmem_config . name = dev_name ( & spi - > dev ) ;
at25 - > nvmem_config . dev = & spi - > dev ;
at25 - > nvmem_config . read_only = chip . flags & EE_READONLY ;
at25 - > nvmem_config . root_only = true ;
at25 - > nvmem_config . owner = THIS_MODULE ;
at25 - > nvmem_config . compat = true ;
at25 - > nvmem_config . base_dev = & spi - > dev ;
2016-04-24 20:28:07 +01:00
at25 - > nvmem_config . reg_read = at25_ee_read ;
at25 - > nvmem_config . reg_write = at25_ee_write ;
at25 - > nvmem_config . priv = at25 ;
2020-07-28 11:29:59 +02:00
at25 - > nvmem_config . stride = 1 ;
2016-04-24 20:28:07 +01:00
at25 - > nvmem_config . word_size = 1 ;
at25 - > nvmem_config . size = chip . byte_len ;
2016-02-26 20:59:22 +01:00
2018-09-21 06:40:02 -07:00
at25 - > nvmem = devm_nvmem_register ( & spi - > dev , & at25 - > nvmem_config ) ;
2016-02-26 20:59:22 +01:00
if ( IS_ERR ( at25 - > nvmem ) )
return PTR_ERR ( at25 - > nvmem ) ;
dev_info ( & spi - > dev , " %d %s %s eeprom%s, pagesize %u \n " ,
2016-09-11 14:58:26 +03:00
( chip . byte_len < 1024 ) ? chip . byte_len : ( chip . byte_len / 1024 ) ,
2016-02-26 20:59:22 +01:00
( chip . byte_len < 1024 ) ? " Byte " : " KByte " ,
2007-02-12 00:52:48 -08:00
at25 - > chip . name ,
2012-06-14 09:59:23 -03:00
( chip . flags & EE_READONLY ) ? " (readonly) " : " " ,
2007-02-12 00:52:48 -08:00
at25 - > chip . page_size ) ;
return 0 ;
}
/*-------------------------------------------------------------------------*/
2013-10-14 17:14:59 +02:00
static const struct of_device_id at25_of_match [ ] = {
{ . compatible = " atmel,at25 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , at25_of_match ) ;
2007-02-12 00:52:48 -08:00
static struct spi_driver at25_driver = {
. driver = {
. name = " at25 " ,
2013-10-14 17:14:59 +02:00
. of_match_table = at25_of_match ,
2007-02-12 00:52:48 -08:00
} ,
. probe = at25_probe ,
} ;
2012-01-22 15:38:22 +08:00
module_spi_driver ( at25_driver ) ;
2007-02-12 00:52:48 -08:00
MODULE_DESCRIPTION ( " Driver for most SPI EEPROMs " ) ;
MODULE_AUTHOR ( " David Brownell " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-09-22 16:46:08 -07:00
MODULE_ALIAS ( " spi:at25 " ) ;