2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2018-10-07 19:05:00 +02:00
/*
* ee1004 - driver for DDR4 SPD EEPROMs
*
2019-05-06 15:16:56 +02:00
* Copyright ( C ) 2017 - 2019 Jean Delvare
2018-10-07 19:05:00 +02:00
*
* Based on the at24 driver :
* Copyright ( C ) 2005 - 2007 David Brownell
* Copyright ( C ) 2008 Wolfram Sang , Pengutronix
*/
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/mod_devicetable.h>
# include <linux/module.h>
# include <linux/mutex.h>
/*
* DDR4 memory modules use special EEPROMs following the Jedec EE1004
* specification . These are 512 - byte EEPROMs using a single I2C address
* in the 0x50 - 0x57 range for data . One of two 256 - byte page is selected
* by writing a command to I2C address 0x36 or 0x37 on the same I2C bus .
*
* Therefore we need to request these 2 additional addresses , and serialize
* access to all such EEPROMs with a single mutex .
*
* We assume it is safe to read up to 32 bytes at once from these EEPROMs .
* We use SMBus access even if I2C is available , these EEPROMs are small
* enough , and reading from them infrequent enough , that we favor simplicity
* over performance .
*/
# define EE1004_ADDR_SET_PAGE 0x36
# define EE1004_EEPROM_SIZE 512
# define EE1004_PAGE_SIZE 256
# define EE1004_PAGE_SHIFT 8
/*
* Mutex protects ee1004_set_page and ee1004_dev_count , and must be held
* from page selection to end of read .
*/
static DEFINE_MUTEX ( ee1004_bus_lock ) ;
static struct i2c_client * ee1004_set_page [ 2 ] ;
static unsigned int ee1004_dev_count ;
static int ee1004_current_page ;
static const struct i2c_device_id ee1004_ids [ ] = {
{ " ee1004 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ee1004_ids ) ;
/*-------------------------------------------------------------------------*/
2019-05-06 15:15:39 +02:00
static int ee1004_get_current_page ( void )
{
int err ;
err = i2c_smbus_read_byte ( ee1004_set_page [ 0 ] ) ;
if ( err = = - ENXIO ) {
/* Nack means page 1 is selected */
return 1 ;
}
if ( err < 0 ) {
/* Anything else is a real error, bail out */
return err ;
}
/* Ack means page 0 is selected, returned value meaningless */
return 0 ;
}
2018-10-07 19:05:00 +02:00
static ssize_t ee1004_eeprom_read ( struct i2c_client * client , char * buf ,
unsigned int offset , size_t count )
{
int status ;
if ( count > I2C_SMBUS_BLOCK_MAX )
count = I2C_SMBUS_BLOCK_MAX ;
/* Can't cross page boundaries */
if ( unlikely ( offset + count > EE1004_PAGE_SIZE ) )
count = EE1004_PAGE_SIZE - offset ;
status = i2c_smbus_read_i2c_block_data_or_emulated ( client , offset ,
count , buf ) ;
dev_dbg ( & client - > dev , " read %zu@%d --> %d \n " , count , offset , status ) ;
return status ;
}
static ssize_t ee1004_read ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
{
struct device * dev = kobj_to_dev ( kobj ) ;
struct i2c_client * client = to_i2c_client ( dev ) ;
size_t requested = count ;
int page ;
if ( unlikely ( ! count ) )
return count ;
page = off > > EE1004_PAGE_SHIFT ;
if ( unlikely ( page > 1 ) )
return 0 ;
off & = ( 1 < < EE1004_PAGE_SHIFT ) - 1 ;
/*
* Read data from chip , protecting against concurrent access to
* other EE1004 SPD EEPROMs on the same adapter .
*/
mutex_lock ( & ee1004_bus_lock ) ;
while ( count ) {
int status ;
/* Select page */
if ( page ! = ee1004_current_page ) {
/* Data is ignored */
status = i2c_smbus_write_byte ( ee1004_set_page [ page ] ,
0x00 ) ;
2019-05-06 15:16:56 +02:00
if ( status = = - ENXIO ) {
/*
* Don ' t give up just yet . Some memory
* modules will select the page but not
* ack the command . Check which page is
* selected now .
*/
if ( ee1004_get_current_page ( ) = = page )
status = 0 ;
}
2018-10-07 19:05:00 +02:00
if ( status < 0 ) {
dev_err ( dev , " Failed to select page %d (%d) \n " ,
page , status ) ;
mutex_unlock ( & ee1004_bus_lock ) ;
return status ;
}
dev_dbg ( dev , " Selected page %d \n " , page ) ;
ee1004_current_page = page ;
}
status = ee1004_eeprom_read ( client , buf , off , count ) ;
if ( status < 0 ) {
mutex_unlock ( & ee1004_bus_lock ) ;
return status ;
}
buf + = status ;
off + = status ;
count - = status ;
if ( off = = EE1004_PAGE_SIZE ) {
page + + ;
off = 0 ;
}
}
mutex_unlock ( & ee1004_bus_lock ) ;
return requested ;
}
static const struct bin_attribute eeprom_attr = {
. attr = {
. name = " eeprom " ,
. mode = 0444 ,
} ,
. size = EE1004_EEPROM_SIZE ,
. read = ee1004_read ,
} ;
static int ee1004_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
int err , cnr = 0 ;
const char * slow = NULL ;
/* Make sure we can operate on this adapter */
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_BYTE |
I2C_FUNC_SMBUS_READ_I2C_BLOCK ) ) {
if ( i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_BYTE |
I2C_FUNC_SMBUS_READ_WORD_DATA ) )
slow = " word " ;
else if ( i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_BYTE |
I2C_FUNC_SMBUS_READ_BYTE_DATA ) )
slow = " byte " ;
else
return - EPFNOSUPPORT ;
}
/* Use 2 dummy devices for page select command */
mutex_lock ( & ee1004_bus_lock ) ;
if ( + + ee1004_dev_count = = 1 ) {
for ( cnr = 0 ; cnr < 2 ; cnr + + ) {
2019-07-22 19:26:15 +02:00
ee1004_set_page [ cnr ] = i2c_new_dummy_device ( client - > adapter ,
2018-10-07 19:05:00 +02:00
EE1004_ADDR_SET_PAGE + cnr ) ;
2019-07-22 19:26:15 +02:00
if ( IS_ERR ( ee1004_set_page [ cnr ] ) ) {
2018-10-07 19:05:00 +02:00
dev_err ( & client - > dev ,
" address 0x%02x unavailable \n " ,
EE1004_ADDR_SET_PAGE + cnr ) ;
2019-07-22 19:26:15 +02:00
err = PTR_ERR ( ee1004_set_page [ cnr ] ) ;
2018-10-07 19:05:00 +02:00
goto err_clients ;
}
}
} else if ( i2c_adapter_id ( client - > adapter ) ! =
i2c_adapter_id ( ee1004_set_page [ 0 ] - > adapter ) ) {
dev_err ( & client - > dev ,
" Driver only supports devices on a single I2C bus \n " ) ;
err = - EOPNOTSUPP ;
goto err_clients ;
}
/* Remember current page to avoid unneeded page select */
2019-05-06 15:15:39 +02:00
err = ee1004_get_current_page ( ) ;
if ( err < 0 )
2018-10-07 19:05:00 +02:00
goto err_clients ;
2019-05-06 15:15:39 +02:00
ee1004_current_page = err ;
2018-10-07 19:05:00 +02:00
dev_dbg ( & client - > dev , " Currently selected page: %d \n " ,
ee1004_current_page ) ;
mutex_unlock ( & ee1004_bus_lock ) ;
/* Create the sysfs eeprom file */
err = sysfs_create_bin_file ( & client - > dev . kobj , & eeprom_attr ) ;
if ( err )
goto err_clients_lock ;
dev_info ( & client - > dev ,
" %u byte EE1004-compliant SPD EEPROM, read-only \n " ,
EE1004_EEPROM_SIZE ) ;
if ( slow )
dev_notice ( & client - > dev ,
" Falling back to %s reads, performance will suffer \n " ,
slow ) ;
return 0 ;
err_clients_lock :
mutex_lock ( & ee1004_bus_lock ) ;
err_clients :
if ( - - ee1004_dev_count = = 0 ) {
for ( cnr - - ; cnr > = 0 ; cnr - - ) {
i2c_unregister_device ( ee1004_set_page [ cnr ] ) ;
ee1004_set_page [ cnr ] = NULL ;
}
}
mutex_unlock ( & ee1004_bus_lock ) ;
return err ;
}
static int ee1004_remove ( struct i2c_client * client )
{
int i ;
sysfs_remove_bin_file ( & client - > dev . kobj , & eeprom_attr ) ;
/* Remove page select clients if this is the last device */
mutex_lock ( & ee1004_bus_lock ) ;
if ( - - ee1004_dev_count = = 0 ) {
for ( i = 0 ; i < 2 ; i + + ) {
i2c_unregister_device ( ee1004_set_page [ i ] ) ;
ee1004_set_page [ i ] = NULL ;
}
}
mutex_unlock ( & ee1004_bus_lock ) ;
return 0 ;
}
/*-------------------------------------------------------------------------*/
static struct i2c_driver ee1004_driver = {
. driver = {
. name = " ee1004 " ,
} ,
. probe = ee1004_probe ,
. remove = ee1004_remove ,
. id_table = ee1004_ids ,
} ;
static int __init ee1004_init ( void )
{
return i2c_add_driver ( & ee1004_driver ) ;
}
module_init ( ee1004_init ) ;
static void __exit ee1004_exit ( void )
{
i2c_del_driver ( & ee1004_driver ) ;
}
module_exit ( ee1004_exit ) ;
MODULE_DESCRIPTION ( " Driver for EE1004-compliant DDR4 SPD EEPROMs " ) ;
MODULE_AUTHOR ( " Jean Delvare " ) ;
MODULE_LICENSE ( " GPL " ) ;