2005-06-03 21:03:27 +04:00
/*
max6875 . c - driver for MAX6874 / MAX6875
Copyright ( C ) 2005 Ben Gardner < bgardner @ wabtec . com >
Based on i2c / chips / eeprom . c
2005-07-12 22:21:11 +04:00
The MAX6875 has a bank of registers and two banks of EEPROM .
Address ranges are defined as follows :
* 0x0000 - 0x0046 = configuration registers
* 0x8000 - 0x8046 = configuration EEPROM
* 0x8100 - 0x82FF = user EEPROM
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
This driver makes the user EEPROM available for read .
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
The registers & config EEPROM should be accessed via i2c - dev .
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
The MAX6875 ignores the lowest address bit , so each chip responds to
two addresses - 0x50 / 0x51 and 0x52 / 0x53 .
Note that the MAX6875 uses i2c_smbus_write_byte_data ( ) to set the read
address , so this driver is destructive if loaded for the wrong EEPROM chip .
2005-06-03 21:03:27 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/i2c.h>
2006-01-19 01:16:04 +03:00
# include <linux/mutex.h>
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
/* Do not scan - the MAX6875 access method will write to some EEPROM chips */
2008-01-27 20:14:47 +03:00
static const unsigned short normal_i2c [ ] = { I2C_CLIENT_END } ;
2005-06-03 21:03:27 +04:00
/* Insmod parameters */
2005-07-31 23:49:03 +04:00
I2C_CLIENT_INSMOD_1 ( max6875 ) ;
2005-06-03 21:03:27 +04:00
/* The MAX6875 can only read/write 16 bytes at a time */
# define SLICE_SIZE 16
# define SLICE_BITS 4
/* USER EEPROM is at addresses 0x8100 - 0x82FF */
# define USER_EEPROM_BASE 0x8100
# define USER_EEPROM_SIZE 0x0200
# define USER_EEPROM_SLICES 32
/* MAX6875 commands */
2005-07-12 22:21:11 +04:00
# define MAX6875_CMD_BLK_READ 0x84
2005-06-03 21:03:27 +04:00
/* Each client has this additional data */
struct max6875_data {
struct i2c_client client ;
2006-01-19 01:16:04 +03:00
struct mutex update_lock ;
2005-07-12 22:21:11 +04:00
u32 valid ;
u8 data [ USER_EEPROM_SIZE ] ;
unsigned long last_updated [ USER_EEPROM_SLICES ] ;
2005-06-03 21:03:27 +04:00
} ;
static int max6875_attach_adapter ( struct i2c_adapter * adapter ) ;
static int max6875_detect ( struct i2c_adapter * adapter , int address , int kind ) ;
static int max6875_detach_client ( struct i2c_client * client ) ;
/* This is the driver that will be inserted */
static struct i2c_driver max6875_driver = {
2005-11-26 22:36:00 +03:00
. driver = {
. name = " max6875 " ,
} ,
2005-06-03 21:03:27 +04:00
. attach_adapter = max6875_attach_adapter ,
. detach_client = max6875_detach_client ,
} ;
2005-07-12 22:21:11 +04:00
static void max6875_update_slice ( struct i2c_client * client , int slice )
2005-06-03 21:03:27 +04:00
{
struct max6875_data * data = i2c_get_clientdata ( client ) ;
2005-07-12 22:21:11 +04:00
int i , j , addr ;
u8 * buf ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
if ( slice > = USER_EEPROM_SLICES )
return ;
2005-06-03 21:03:27 +04:00
2006-01-19 01:16:04 +03:00
mutex_lock ( & data - > update_lock ) ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
buf = & data - > data [ slice < < SLICE_BITS ] ;
if ( ! ( data - > valid & ( 1 < < slice ) ) | |
time_after ( jiffies , data - > last_updated [ slice ] ) ) {
2005-07-27 21:43:03 +04:00
dev_dbg ( & client - > dev , " Starting update of slice %u \n " , slice ) ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
data - > valid & = ~ ( 1 < < slice ) ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
addr = USER_EEPROM_BASE + ( slice < < SLICE_BITS ) ;
/* select the eeprom address */
if ( i2c_smbus_write_byte_data ( client , addr > > 8 , addr & 0xFF ) ) {
dev_err ( & client - > dev , " address set failed \n " ) ;
goto exit_up ;
2005-06-03 21:03:27 +04:00
}
2005-07-12 22:21:11 +04:00
if ( i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_I2C_BLOCK ) ) {
if ( i2c_smbus_read_i2c_block_data ( client ,
MAX6875_CMD_BLK_READ ,
i2c: Fix the i2c_smbus_read_i2c_block_data() prototype
Let the drivers specify how many bytes they want to read with
i2c_smbus_read_i2c_block_data(). So far, the block count was
hard-coded to I2C_SMBUS_BLOCK_MAX (32), which did not make much sense.
Many driver authors complained about this before, and I believe it's
about time to fix it. Right now, authors have to do technically stupid
things, such as individual byte reads or full-fledged I2C messaging,
to work around the problem. We do not want to encourage that.
I even found that some bus drivers (e.g. i2c-amd8111) already
implemented I2C block read the "right" way, that is, they didn't
follow the old, broken standard. The fact that it was never noticed
before just shows how little i2c_smbus_read_i2c_block_data() was used,
which isn't that surprising given how broken its prototype was so far.
There are some obvious compatiblity considerations:
* This changes the i2c_smbus_read_i2c_block_data() prototype. Users
outside the kernel tree will notice at compilation time, and will
have to update their code.
* User-space has access to i2c_smbus_xfer() directly using i2c-dev, so
the changed expectations would affect tools such as i2cdump. In order
to preserve binary compatibility, we give I2C_SMBUS_I2C_BLOCK_DATA
a new numeric value, and define I2C_SMBUS_I2C_BLOCK_BROKEN with the
old numeric value. When i2c-dev receives a transaction with the
old value, it can convert it to the new format on the fly.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
2007-07-12 16:12:29 +04:00
SLICE_SIZE ,
2005-07-12 22:21:11 +04:00
buf ) ! = SLICE_SIZE ) {
goto exit_up ;
2005-06-03 21:03:27 +04:00
}
} else {
2005-07-12 22:21:11 +04:00
for ( i = 0 ; i < SLICE_SIZE ; i + + ) {
2005-06-03 21:03:27 +04:00
j = i2c_smbus_read_byte ( client ) ;
2005-07-12 22:21:11 +04:00
if ( j < 0 ) {
goto exit_up ;
2005-06-03 21:03:27 +04:00
}
2005-07-12 22:21:11 +04:00
buf [ i ] = j ;
2005-06-03 21:03:27 +04:00
}
}
2005-07-12 22:21:11 +04:00
data - > last_updated [ slice ] = jiffies ;
data - > valid | = ( 1 < < slice ) ;
2005-06-03 21:03:27 +04:00
}
2005-07-12 22:21:11 +04:00
exit_up :
2006-01-19 01:16:04 +03:00
mutex_unlock ( & data - > update_lock ) ;
2005-06-03 21:03:27 +04:00
}
2007-06-09 09:57:22 +04:00
static ssize_t max6875_read ( struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
2005-06-03 21:03:27 +04:00
{
2005-07-12 22:21:11 +04:00
struct i2c_client * client = kobj_to_i2c_client ( kobj ) ;
2005-06-03 21:03:27 +04:00
struct max6875_data * data = i2c_get_clientdata ( client ) ;
2005-07-12 22:21:11 +04:00
int slice , max_slice ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
if ( off > USER_EEPROM_SIZE )
2005-06-03 21:03:27 +04:00
return 0 ;
2005-07-12 22:21:11 +04:00
if ( off + count > USER_EEPROM_SIZE )
count = USER_EEPROM_SIZE - off ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
/* refresh slices which contain requested bytes */
max_slice = ( off + count - 1 ) > > SLICE_BITS ;
for ( slice = ( off > > SLICE_BITS ) ; slice < = max_slice ; slice + + )
max6875_update_slice ( client , slice ) ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
memcpy ( buf , & data - > data [ off ] , count ) ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
return count ;
2005-06-03 21:03:27 +04:00
}
static struct bin_attribute user_eeprom_attr = {
. attr = {
2005-07-12 22:21:11 +04:00
. name = " eeprom " ,
. mode = S_IRUGO ,
2005-06-03 21:03:27 +04:00
} ,
2005-07-12 22:21:11 +04:00
. size = USER_EEPROM_SIZE ,
. read = max6875_read ,
2005-06-03 21:03:27 +04:00
} ;
static int max6875_attach_adapter ( struct i2c_adapter * adapter )
{
2005-07-31 23:42:02 +04:00
return i2c_probe ( adapter , & addr_data , max6875_detect ) ;
2005-06-03 21:03:27 +04:00
}
2005-07-31 23:42:02 +04:00
/* This function is called by i2c_probe */
2005-06-03 21:03:27 +04:00
static int max6875_detect ( struct i2c_adapter * adapter , int address , int kind )
{
2005-07-12 22:21:11 +04:00
struct i2c_client * real_client ;
struct i2c_client * fake_client ;
2005-06-03 21:03:27 +04:00
struct max6875_data * data ;
2008-07-15 00:38:36 +04:00
int err ;
2005-06-03 21:03:27 +04:00
2005-07-27 21:43:14 +04:00
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_WRITE_BYTE_DATA
2005-07-12 22:21:11 +04:00
| I2C_FUNC_SMBUS_READ_BYTE ) )
return 0 ;
/* Only check even addresses */
if ( address & 1 )
return 0 ;
2005-10-18 01:09:43 +04:00
if ( ! ( data = kzalloc ( sizeof ( struct max6875_data ) , GFP_KERNEL ) ) )
2005-07-12 22:21:11 +04:00
return - ENOMEM ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
/* A fake client is created on the odd address */
2005-10-18 01:09:43 +04:00
if ( ! ( fake_client = kzalloc ( sizeof ( struct i2c_client ) , GFP_KERNEL ) ) ) {
2005-07-12 22:21:11 +04:00
err = - ENOMEM ;
goto exit_kfree1 ;
}
/* Init real i2c_client */
real_client = & data - > client ;
i2c_set_clientdata ( real_client , data ) ;
real_client - > addr = address ;
real_client - > adapter = adapter ;
real_client - > driver = & max6875_driver ;
strlcpy ( real_client - > name , " max6875 " , I2C_NAME_SIZE ) ;
2006-01-19 01:16:04 +03:00
mutex_init ( & data - > update_lock ) ;
2005-06-03 21:03:27 +04:00
2005-07-12 22:21:11 +04:00
/* Init fake client data */
2006-09-04 00:20:24 +04:00
i2c_set_clientdata ( fake_client , NULL ) ;
2005-07-12 22:21:11 +04:00
fake_client - > addr = address | 1 ;
fake_client - > adapter = adapter ;
fake_client - > driver = & max6875_driver ;
2005-07-27 21:43:14 +04:00
strlcpy ( fake_client - > name , " max6875 subclient " , I2C_NAME_SIZE ) ;
2005-07-12 22:21:11 +04:00
if ( ( err = i2c_attach_client ( real_client ) ) ! = 0 )
goto exit_kfree2 ;
if ( ( err = i2c_attach_client ( fake_client ) ) ! = 0 )
2006-09-04 00:20:24 +04:00
goto exit_detach1 ;
2005-07-12 22:21:11 +04:00
2006-09-04 00:20:24 +04:00
err = sysfs_create_bin_file ( & real_client - > dev . kobj , & user_eeprom_attr ) ;
if ( err )
goto exit_detach2 ;
2005-06-03 21:03:27 +04:00
return 0 ;
2006-09-04 00:20:24 +04:00
exit_detach2 :
i2c_detach_client ( fake_client ) ;
exit_detach1 :
2005-07-12 22:21:11 +04:00
i2c_detach_client ( real_client ) ;
exit_kfree2 :
kfree ( fake_client ) ;
exit_kfree1 :
2005-06-03 21:03:27 +04:00
kfree ( data ) ;
return err ;
}
2006-09-04 00:20:24 +04:00
/* Will be called for both the real client and the fake client */
2005-06-03 21:03:27 +04:00
static int max6875_detach_client ( struct i2c_client * client )
{
int err ;
2006-09-04 00:20:24 +04:00
struct max6875_data * data = i2c_get_clientdata ( client ) ;
/* data is NULL for the fake client */
if ( data )
sysfs_remove_bin_file ( & client - > dev . kobj , & user_eeprom_attr ) ;
2005-06-03 21:03:27 +04:00
err = i2c_detach_client ( client ) ;
2005-07-28 00:14:49 +04:00
if ( err )
2005-06-03 21:03:27 +04:00
return err ;
2006-09-04 00:20:24 +04:00
if ( data ) /* real client */
kfree ( data ) ;
else /* fake client */
kfree ( client ) ;
2005-06-03 21:03:27 +04:00
return 0 ;
}
static int __init max6875_init ( void )
{
return i2c_add_driver ( & max6875_driver ) ;
}
static void __exit max6875_exit ( void )
{
i2c_del_driver ( & max6875_driver ) ;
}
MODULE_AUTHOR ( " Ben Gardner <bgardner@wabtec.com> " ) ;
MODULE_DESCRIPTION ( " MAX6875 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( max6875_init ) ;
module_exit ( max6875_exit ) ;