2008-02-05 09:28:26 +03:00
/*
2008-02-06 12:39:04 +03:00
* pca953x . c - 4 / 8 / 16 bit I / O ports
2008-02-05 09:28:26 +03:00
*
* Copyright ( C ) 2005 Ben Gardner < bgardner @ wabtec . com >
* Copyright ( C ) 2007 Marvell International Ltd .
*
* Derived from drivers / i2c / chips / pca9539 . c
*
* 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/i2c.h>
2008-02-06 12:39:02 +03:00
# include <linux/i2c/pca953x.h>
2008-02-05 09:28:26 +03:00
# include <asm/gpio.h>
2008-02-06 12:39:04 +03:00
# define PCA953X_INPUT 0
# define PCA953X_OUTPUT 1
# define PCA953X_INVERT 2
# define PCA953X_DIRECTION 3
2008-02-05 09:28:26 +03:00
2008-02-06 12:39:04 +03:00
/* This is temporary - in 2.6.26 i2c_driver_data should replace it. */
struct pca953x_desc {
char name [ I2C_NAME_SIZE ] ;
unsigned long driver_data ;
} ;
2008-02-05 09:28:26 +03:00
2008-02-06 12:39:04 +03:00
static const struct pca953x_desc pca953x_descs [ ] = {
{ " pca9534 " , 8 , } ,
{ " pca9535 " , 16 , } ,
{ " pca9536 " , 4 , } ,
{ " pca9537 " , 4 , } ,
{ " pca9538 " , 8 , } ,
{ " pca9539 " , 16 , } ,
/* REVISIT several pca955x parts should work here too */
} ;
2008-02-05 09:28:26 +03:00
2008-02-06 12:39:03 +03:00
struct pca953x_chip {
2008-02-05 09:28:26 +03:00
unsigned gpio_start ;
uint16_t reg_output ;
uint16_t reg_direction ;
struct i2c_client * client ;
struct gpio_chip gpio_chip ;
} ;
/* NOTE: we can't currently rely on fault codes to come from SMBus
* calls , so we map all errors to EIO here and return zero otherwise .
*/
2008-02-06 12:39:03 +03:00
static int pca953x_write_reg ( struct pca953x_chip * chip , int reg , uint16_t val )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:04 +03:00
int ret ;
if ( chip - > gpio_chip . ngpio < = 8 )
ret = i2c_smbus_write_byte_data ( chip - > client , reg , val ) ;
2008-02-05 09:28:26 +03:00
else
2008-02-06 12:39:04 +03:00
ret = i2c_smbus_write_word_data ( chip - > client , reg < < 1 , val ) ;
if ( ret < 0 ) {
dev_err ( & chip - > client - > dev , " failed writing register \n " ) ;
return - EIO ;
}
return 0 ;
2008-02-05 09:28:26 +03:00
}
2008-02-06 12:39:03 +03:00
static int pca953x_read_reg ( struct pca953x_chip * chip , int reg , uint16_t * val )
2008-02-05 09:28:26 +03:00
{
int ret ;
2008-02-06 12:39:04 +03:00
if ( chip - > gpio_chip . ngpio < = 8 )
ret = i2c_smbus_read_byte_data ( chip - > client , reg ) ;
else
ret = i2c_smbus_read_word_data ( chip - > client , reg < < 1 ) ;
2008-02-05 09:28:26 +03:00
if ( ret < 0 ) {
dev_err ( & chip - > client - > dev , " failed reading register \n " ) ;
return - EIO ;
}
* val = ( uint16_t ) ret ;
return 0 ;
}
2008-02-06 12:39:03 +03:00
static int pca953x_gpio_direction_input ( struct gpio_chip * gc , unsigned off )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:03 +03:00
struct pca953x_chip * chip ;
2008-02-05 09:28:26 +03:00
uint16_t reg_val ;
int ret ;
2008-02-06 12:39:03 +03:00
chip = container_of ( gc , struct pca953x_chip , gpio_chip ) ;
2008-02-05 09:28:26 +03:00
reg_val = chip - > reg_direction | ( 1u < < off ) ;
2008-02-06 12:39:03 +03:00
ret = pca953x_write_reg ( chip , PCA953X_DIRECTION , reg_val ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
return ret ;
chip - > reg_direction = reg_val ;
return 0 ;
}
2008-02-06 12:39:03 +03:00
static int pca953x_gpio_direction_output ( struct gpio_chip * gc ,
2008-02-05 09:28:26 +03:00
unsigned off , int val )
{
2008-02-06 12:39:03 +03:00
struct pca953x_chip * chip ;
2008-02-05 09:28:26 +03:00
uint16_t reg_val ;
int ret ;
2008-02-06 12:39:03 +03:00
chip = container_of ( gc , struct pca953x_chip , gpio_chip ) ;
2008-02-05 09:28:26 +03:00
/* set output level */
if ( val )
reg_val = chip - > reg_output | ( 1u < < off ) ;
else
reg_val = chip - > reg_output & ~ ( 1u < < off ) ;
2008-02-06 12:39:03 +03:00
ret = pca953x_write_reg ( chip , PCA953X_OUTPUT , reg_val ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
return ret ;
chip - > reg_output = reg_val ;
/* then direction */
reg_val = chip - > reg_direction & ~ ( 1u < < off ) ;
2008-02-06 12:39:03 +03:00
ret = pca953x_write_reg ( chip , PCA953X_DIRECTION , reg_val ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
return ret ;
chip - > reg_direction = reg_val ;
return 0 ;
}
2008-02-06 12:39:03 +03:00
static int pca953x_gpio_get_value ( struct gpio_chip * gc , unsigned off )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:03 +03:00
struct pca953x_chip * chip ;
2008-02-05 09:28:26 +03:00
uint16_t reg_val ;
int ret ;
2008-02-06 12:39:03 +03:00
chip = container_of ( gc , struct pca953x_chip , gpio_chip ) ;
2008-02-05 09:28:26 +03:00
2008-02-06 12:39:03 +03:00
ret = pca953x_read_reg ( chip , PCA953X_INPUT , & reg_val ) ;
2008-02-05 09:28:26 +03:00
if ( ret < 0 ) {
/* NOTE: diagnostic already emitted; that's all we should
* do unless gpio_ * _value_cansleep ( ) calls become different
* from their nonsleeping siblings ( and report faults ) .
*/
return 0 ;
}
return ( reg_val & ( 1u < < off ) ) ? 1 : 0 ;
}
2008-02-06 12:39:03 +03:00
static void pca953x_gpio_set_value ( struct gpio_chip * gc , unsigned off , int val )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:03 +03:00
struct pca953x_chip * chip ;
2008-02-05 09:28:26 +03:00
uint16_t reg_val ;
int ret ;
2008-02-06 12:39:03 +03:00
chip = container_of ( gc , struct pca953x_chip , gpio_chip ) ;
2008-02-05 09:28:26 +03:00
if ( val )
reg_val = chip - > reg_output | ( 1u < < off ) ;
else
reg_val = chip - > reg_output & ~ ( 1u < < off ) ;
2008-02-06 12:39:03 +03:00
ret = pca953x_write_reg ( chip , PCA953X_OUTPUT , reg_val ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
return ;
chip - > reg_output = reg_val ;
}
2008-02-06 12:39:04 +03:00
static void pca953x_setup_gpio ( struct pca953x_chip * chip , int gpios )
2008-02-05 09:28:26 +03:00
{
struct gpio_chip * gc ;
gc = & chip - > gpio_chip ;
2008-02-06 12:39:03 +03:00
gc - > direction_input = pca953x_gpio_direction_input ;
gc - > direction_output = pca953x_gpio_direction_output ;
gc - > get = pca953x_gpio_get_value ;
gc - > set = pca953x_gpio_set_value ;
2008-03-10 21:43:48 +03:00
gc - > can_sleep = 1 ;
2008-02-05 09:28:26 +03:00
gc - > base = chip - > gpio_start ;
2008-02-06 12:39:04 +03:00
gc - > ngpio = gpios ;
gc - > label = chip - > client - > name ;
2008-02-05 09:28:26 +03:00
}
2008-02-06 12:39:03 +03:00
static int __devinit pca953x_probe ( struct i2c_client * client )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:03 +03:00
struct pca953x_platform_data * pdata ;
struct pca953x_chip * chip ;
2008-02-06 12:39:04 +03:00
int ret , i ;
const struct pca953x_desc * id = NULL ;
2008-02-05 09:28:26 +03:00
pdata = client - > dev . platform_data ;
if ( pdata = = NULL )
return - ENODEV ;
2008-02-06 12:39:04 +03:00
/* this loop vanishes when we get i2c_device_id */
for ( i = 0 ; i < ARRAY_SIZE ( pca953x_descs ) ; i + + )
if ( ! strcmp ( pca953x_descs [ i ] . name , client - > name ) ) {
id = pca953x_descs + i ;
break ;
}
if ( ! id )
return - ENODEV ;
2008-02-06 12:39:03 +03:00
chip = kzalloc ( sizeof ( struct pca953x_chip ) , GFP_KERNEL ) ;
2008-02-05 09:28:26 +03:00
if ( chip = = NULL )
return - ENOMEM ;
chip - > client = client ;
chip - > gpio_start = pdata - > gpio_base ;
/* initialize cached registers from their original values.
* we can ' t share this chip with another i2c master .
*/
2008-02-06 12:39:04 +03:00
pca953x_setup_gpio ( chip , id - > driver_data ) ;
2008-02-06 12:39:03 +03:00
ret = pca953x_read_reg ( chip , PCA953X_OUTPUT , & chip - > reg_output ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
goto out_failed ;
2008-02-06 12:39:03 +03:00
ret = pca953x_read_reg ( chip , PCA953X_DIRECTION , & chip - > reg_direction ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
goto out_failed ;
/* set platform specific polarity inversion */
2008-02-06 12:39:03 +03:00
ret = pca953x_write_reg ( chip , PCA953X_INVERT , pdata - > invert ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
goto out_failed ;
2008-02-06 12:39:04 +03:00
ret = gpiochip_add ( & chip - > gpio_chip ) ;
2008-02-05 09:28:26 +03:00
if ( ret )
goto out_failed ;
if ( pdata - > setup ) {
ret = pdata - > setup ( client , chip - > gpio_chip . base ,
chip - > gpio_chip . ngpio , pdata - > context ) ;
if ( ret < 0 )
dev_warn ( & client - > dev , " setup failed, %d \n " , ret ) ;
}
i2c_set_clientdata ( client , chip ) ;
return 0 ;
out_failed :
kfree ( chip ) ;
return ret ;
}
2008-02-06 12:39:03 +03:00
static int pca953x_remove ( struct i2c_client * client )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:03 +03:00
struct pca953x_platform_data * pdata = client - > dev . platform_data ;
struct pca953x_chip * chip = i2c_get_clientdata ( client ) ;
2008-02-05 09:28:26 +03:00
int ret = 0 ;
if ( pdata - > teardown ) {
ret = pdata - > teardown ( client , chip - > gpio_chip . base ,
chip - > gpio_chip . ngpio , pdata - > context ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " %s failed, %d \n " ,
" teardown " , ret ) ;
return ret ;
}
}
ret = gpiochip_remove ( & chip - > gpio_chip ) ;
if ( ret ) {
dev_err ( & client - > dev , " %s failed, %d \n " ,
" gpiochip_remove() " , ret ) ;
return ret ;
}
kfree ( chip ) ;
return 0 ;
}
2008-02-06 12:39:03 +03:00
static struct i2c_driver pca953x_driver = {
2008-02-05 09:28:26 +03:00
. driver = {
2008-02-06 12:39:03 +03:00
. name = " pca953x " ,
2008-02-05 09:28:26 +03:00
} ,
2008-02-06 12:39:03 +03:00
. probe = pca953x_probe ,
. remove = pca953x_remove ,
2008-02-05 09:28:26 +03:00
} ;
2008-02-06 12:39:03 +03:00
static int __init pca953x_init ( void )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:03 +03:00
return i2c_add_driver ( & pca953x_driver ) ;
2008-02-05 09:28:26 +03:00
}
2008-02-06 12:39:03 +03:00
module_init ( pca953x_init ) ;
2008-02-05 09:28:26 +03:00
2008-02-06 12:39:03 +03:00
static void __exit pca953x_exit ( void )
2008-02-05 09:28:26 +03:00
{
2008-02-06 12:39:03 +03:00
i2c_del_driver ( & pca953x_driver ) ;
2008-02-05 09:28:26 +03:00
}
2008-02-06 12:39:03 +03:00
module_exit ( pca953x_exit ) ;
2008-02-05 09:28:26 +03:00
MODULE_AUTHOR ( " eric miao <eric.miao@marvell.com> " ) ;
2008-02-06 12:39:03 +03:00
MODULE_DESCRIPTION ( " GPIO expander driver for PCA953x " ) ;
2008-02-05 09:28:26 +03:00
MODULE_LICENSE ( " GPL " ) ;