2005-06-07 17:55:38 +04:00
/*
pca9539 . c - 16 - bit I / O port with interrupt and reset
Copyright ( C ) 2005 Ben Gardner < bgardner @ wabtec . com >
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/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/hwmon-sysfs.h>
/* Addresses to scan */
static unsigned short normal_i2c [ ] = { 0x74 , 0x75 , 0x76 , 0x77 , I2C_CLIENT_END } ;
/* Insmod parameters */
2005-07-31 23:49:03 +04:00
I2C_CLIENT_INSMOD_1 ( pca9539 ) ;
2005-06-07 17:55:38 +04:00
enum pca9539_cmd
{
PCA9539_INPUT_0 = 0 ,
PCA9539_INPUT_1 = 1 ,
PCA9539_OUTPUT_0 = 2 ,
PCA9539_OUTPUT_1 = 3 ,
PCA9539_INVERT_0 = 4 ,
PCA9539_INVERT_1 = 5 ,
PCA9539_DIRECTION_0 = 6 ,
PCA9539_DIRECTION_1 = 7 ,
} ;
static int pca9539_attach_adapter ( struct i2c_adapter * adapter ) ;
static int pca9539_detect ( struct i2c_adapter * adapter , int address , int kind ) ;
static int pca9539_detach_client ( struct i2c_client * client ) ;
/* This is the driver that will be inserted */
static struct i2c_driver pca9539_driver = {
2005-11-26 22:36:00 +03:00
. driver = {
. name = " pca9539 " ,
} ,
2005-06-07 17:55:38 +04:00
. attach_adapter = pca9539_attach_adapter ,
. detach_client = pca9539_detach_client ,
} ;
struct pca9539_data {
struct i2c_client client ;
} ;
/* following are the sysfs callback functions */
static ssize_t pca9539_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct sensor_device_attribute * psa = to_sensor_dev_attr ( attr ) ;
struct i2c_client * client = to_i2c_client ( dev ) ;
return sprintf ( buf , " %d \n " , i2c_smbus_read_byte_data ( client ,
psa - > index ) ) ;
}
static ssize_t pca9539_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct sensor_device_attribute * psa = to_sensor_dev_attr ( attr ) ;
struct i2c_client * client = to_i2c_client ( dev ) ;
unsigned long val = simple_strtoul ( buf , NULL , 0 ) ;
if ( val > 0xff )
return - EINVAL ;
i2c_smbus_write_byte_data ( client , psa - > index , val ) ;
return count ;
}
/* Define the device attributes */
# define PCA9539_ENTRY_RO(name, cmd_idx) \
static SENSOR_DEVICE_ATTR ( name , S_IRUGO , pca9539_show , NULL , cmd_idx )
# define PCA9539_ENTRY_RW(name, cmd_idx) \
static SENSOR_DEVICE_ATTR ( name , S_IRUGO | S_IWUSR , pca9539_show , \
pca9539_store , cmd_idx )
PCA9539_ENTRY_RO ( input0 , PCA9539_INPUT_0 ) ;
PCA9539_ENTRY_RO ( input1 , PCA9539_INPUT_1 ) ;
PCA9539_ENTRY_RW ( output0 , PCA9539_OUTPUT_0 ) ;
PCA9539_ENTRY_RW ( output1 , PCA9539_OUTPUT_1 ) ;
PCA9539_ENTRY_RW ( invert0 , PCA9539_INVERT_0 ) ;
PCA9539_ENTRY_RW ( invert1 , PCA9539_INVERT_1 ) ;
PCA9539_ENTRY_RW ( direction0 , PCA9539_DIRECTION_0 ) ;
PCA9539_ENTRY_RW ( direction1 , PCA9539_DIRECTION_1 ) ;
static struct attribute * pca9539_attributes [ ] = {
& sensor_dev_attr_input0 . dev_attr . attr ,
& sensor_dev_attr_input1 . dev_attr . attr ,
& sensor_dev_attr_output0 . dev_attr . attr ,
& sensor_dev_attr_output1 . dev_attr . attr ,
& sensor_dev_attr_invert0 . dev_attr . attr ,
& sensor_dev_attr_invert1 . dev_attr . attr ,
& sensor_dev_attr_direction0 . dev_attr . attr ,
& sensor_dev_attr_direction1 . dev_attr . attr ,
NULL
} ;
static struct attribute_group pca9539_defattr_group = {
. attrs = pca9539_attributes ,
} ;
static int pca9539_attach_adapter ( struct i2c_adapter * adapter )
{
2005-07-31 23:42:02 +04:00
return i2c_probe ( adapter , & addr_data , pca9539_detect ) ;
2005-06-07 17:55:38 +04:00
}
2005-07-31 23:42:02 +04:00
/* This function is called by i2c_probe */
2005-06-07 17:55:38 +04:00
static int pca9539_detect ( struct i2c_adapter * adapter , int address , int kind )
{
struct i2c_client * new_client ;
struct pca9539_data * data ;
int err = 0 ;
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
goto exit ;
/* OK. For now, we presume we have a valid client. We now create the
client structure , even though we cannot fill it completely yet . */
2005-10-18 01:09:43 +04:00
if ( ! ( data = kzalloc ( sizeof ( struct pca9539_data ) , GFP_KERNEL ) ) ) {
2005-06-07 17:55:38 +04:00
err = - ENOMEM ;
goto exit ;
}
new_client = & data - > client ;
i2c_set_clientdata ( new_client , data ) ;
new_client - > addr = address ;
new_client - > adapter = adapter ;
new_client - > driver = & pca9539_driver ;
new_client - > flags = 0 ;
2006-07-01 19:22:18 +04:00
if ( kind < 0 ) {
/* Detection: the pca9539 only has 8 registers (0-7).
A read of 7 should succeed , but a read of 8 should fail . */
if ( ( i2c_smbus_read_byte_data ( new_client , 7 ) < 0 ) | |
( i2c_smbus_read_byte_data ( new_client , 8 ) > = 0 ) )
goto exit_kfree ;
}
2005-06-07 17:55:38 +04:00
strlcpy ( new_client - > name , " pca9539 " , I2C_NAME_SIZE ) ;
/* Tell the I2C layer a new client has arrived */
if ( ( err = i2c_attach_client ( new_client ) ) )
goto exit_kfree ;
2006-09-04 00:20:24 +04:00
/* Register sysfs hooks */
err = sysfs_create_group ( & new_client - > dev . kobj ,
& pca9539_defattr_group ) ;
if ( err )
goto exit_detach ;
2005-06-07 17:55:38 +04:00
return 0 ;
2006-09-04 00:20:24 +04:00
exit_detach :
i2c_detach_client ( new_client ) ;
2005-06-07 17:55:38 +04:00
exit_kfree :
kfree ( data ) ;
exit :
return err ;
}
static int pca9539_detach_client ( struct i2c_client * client )
{
int err ;
2006-09-04 00:20:24 +04:00
sysfs_remove_group ( & client - > dev . kobj , & pca9539_defattr_group ) ;
2005-07-28 00:14:49 +04:00
if ( ( err = i2c_detach_client ( client ) ) )
2005-06-07 17:55:38 +04:00
return err ;
kfree ( i2c_get_clientdata ( client ) ) ;
return 0 ;
}
static int __init pca9539_init ( void )
{
return i2c_add_driver ( & pca9539_driver ) ;
}
static void __exit pca9539_exit ( void )
{
i2c_del_driver ( & pca9539_driver ) ;
}
MODULE_AUTHOR ( " Ben Gardner <bgardner@wabtec.com> " ) ;
MODULE_DESCRIPTION ( " PCA9539 driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( pca9539_init ) ;
module_exit ( pca9539_exit ) ;