2010-01-07 01:04:18 +03:00
/*
2010-01-08 14:01:24 +03:00
* I2C driver for Marvell 88 PM860x
2010-01-07 01:04:18 +03:00
*
* Copyright ( C ) 2009 Marvell International Ltd .
* Haojian Zhuang < haojian . zhuang @ marvell . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/i2c.h>
2010-01-08 14:01:24 +03:00
# include <linux/mfd/88pm860x.h>
2010-01-07 01:04:18 +03:00
2010-01-08 14:01:24 +03:00
static inline int pm860x_read_device ( struct i2c_client * i2c ,
2010-01-07 01:04:18 +03:00
int reg , int bytes , void * dest )
{
unsigned char data ;
int ret ;
data = ( unsigned char ) reg ;
ret = i2c_master_send ( i2c , & data , 1 ) ;
if ( ret < 0 )
return ret ;
ret = i2c_master_recv ( i2c , dest , bytes ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2010-01-08 14:01:24 +03:00
static inline int pm860x_write_device ( struct i2c_client * i2c ,
2010-01-07 01:04:18 +03:00
int reg , int bytes , void * src )
{
unsigned char buf [ bytes + 1 ] ;
int ret ;
buf [ 0 ] = ( unsigned char ) reg ;
memcpy ( & buf [ 1 ] , src , bytes ) ;
ret = i2c_master_send ( i2c , buf , bytes + 1 ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
2010-01-08 14:01:24 +03:00
int pm860x_reg_read ( struct i2c_client * i2c , int reg )
2010-01-07 01:04:18 +03:00
{
2010-01-08 14:01:24 +03:00
struct pm860x_chip * chip = i2c_get_clientdata ( i2c ) ;
2010-01-07 01:04:18 +03:00
unsigned char data ;
int ret ;
mutex_lock ( & chip - > io_lock ) ;
2010-01-08 14:01:24 +03:00
ret = pm860x_read_device ( i2c , reg , 1 , & data ) ;
2010-01-07 01:04:18 +03:00
mutex_unlock ( & chip - > io_lock ) ;
if ( ret < 0 )
return ret ;
else
return ( int ) data ;
}
2010-01-08 14:01:24 +03:00
EXPORT_SYMBOL ( pm860x_reg_read ) ;
2010-01-07 01:04:18 +03:00
2010-01-08 14:01:24 +03:00
int pm860x_reg_write ( struct i2c_client * i2c , int reg ,
2010-01-07 01:04:18 +03:00
unsigned char data )
{
2010-01-08 14:01:24 +03:00
struct pm860x_chip * chip = i2c_get_clientdata ( i2c ) ;
2010-01-07 01:04:18 +03:00
int ret ;
mutex_lock ( & chip - > io_lock ) ;
2010-01-08 14:01:24 +03:00
ret = pm860x_write_device ( i2c , reg , 1 , & data ) ;
2010-01-07 01:04:18 +03:00
mutex_unlock ( & chip - > io_lock ) ;
return ret ;
}
2010-01-08 14:01:24 +03:00
EXPORT_SYMBOL ( pm860x_reg_write ) ;
2010-01-07 01:04:18 +03:00
2010-01-08 14:01:24 +03:00
int pm860x_bulk_read ( struct i2c_client * i2c , int reg ,
2010-01-07 01:04:18 +03:00
int count , unsigned char * buf )
{
2010-01-08 14:01:24 +03:00
struct pm860x_chip * chip = i2c_get_clientdata ( i2c ) ;
2010-01-07 01:04:18 +03:00
int ret ;
mutex_lock ( & chip - > io_lock ) ;
2010-01-08 14:01:24 +03:00
ret = pm860x_read_device ( i2c , reg , count , buf ) ;
2010-01-07 01:04:18 +03:00
mutex_unlock ( & chip - > io_lock ) ;
return ret ;
}
2010-01-08 14:01:24 +03:00
EXPORT_SYMBOL ( pm860x_bulk_read ) ;
2010-01-07 01:04:18 +03:00
2010-01-08 14:01:24 +03:00
int pm860x_bulk_write ( struct i2c_client * i2c , int reg ,
2010-01-07 01:04:18 +03:00
int count , unsigned char * buf )
{
2010-01-08 14:01:24 +03:00
struct pm860x_chip * chip = i2c_get_clientdata ( i2c ) ;
2010-01-07 01:04:18 +03:00
int ret ;
mutex_lock ( & chip - > io_lock ) ;
2010-01-08 14:01:24 +03:00
ret = pm860x_write_device ( i2c , reg , count , buf ) ;
2010-01-07 01:04:18 +03:00
mutex_unlock ( & chip - > io_lock ) ;
return ret ;
}
2010-01-08 14:01:24 +03:00
EXPORT_SYMBOL ( pm860x_bulk_write ) ;
2010-01-07 01:04:18 +03:00
2010-01-08 14:01:24 +03:00
int pm860x_set_bits ( struct i2c_client * i2c , int reg ,
2010-01-07 01:04:18 +03:00
unsigned char mask , unsigned char data )
{
2010-01-08 14:01:24 +03:00
struct pm860x_chip * chip = i2c_get_clientdata ( i2c ) ;
2010-01-07 01:04:18 +03:00
unsigned char value ;
int ret ;
mutex_lock ( & chip - > io_lock ) ;
2010-01-08 14:01:24 +03:00
ret = pm860x_read_device ( i2c , reg , 1 , & value ) ;
2010-01-07 01:04:18 +03:00
if ( ret < 0 )
goto out ;
value & = ~ mask ;
value | = data ;
2010-01-08 14:01:24 +03:00
ret = pm860x_write_device ( i2c , reg , 1 , & value ) ;
2010-01-07 01:04:18 +03:00
out :
mutex_unlock ( & chip - > io_lock ) ;
return ret ;
}
2010-01-08 14:01:24 +03:00
EXPORT_SYMBOL ( pm860x_set_bits ) ;
2010-01-07 01:04:18 +03:00
static const struct i2c_device_id pm860x_id_table [ ] = {
2010-01-08 14:01:24 +03:00
{ " 88PM860x " , 0 } ,
2010-01-07 01:04:18 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , pm860x_id_table ) ;
2010-01-08 14:01:24 +03:00
static int verify_addr ( struct i2c_client * i2c )
{
unsigned short addr_8607 [ ] = { 0x30 , 0x34 } ;
unsigned short addr_8606 [ ] = { 0x10 , 0x11 } ;
int size , i ;
if ( i2c = = NULL )
return 0 ;
size = ARRAY_SIZE ( addr_8606 ) ;
for ( i = 0 ; i < size ; i + + ) {
if ( i2c - > addr = = * ( addr_8606 + i ) )
return CHIP_PM8606 ;
}
size = ARRAY_SIZE ( addr_8607 ) ;
for ( i = 0 ; i < size ; i + + ) {
if ( i2c - > addr = = * ( addr_8607 + i ) )
return CHIP_PM8607 ;
}
return 0 ;
}
2010-01-07 01:04:18 +03:00
static int __devinit pm860x_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
2010-01-08 14:01:24 +03:00
struct pm860x_platform_data * pdata = client - > dev . platform_data ;
2010-02-03 23:38:12 +03:00
struct pm860x_chip * chip ;
if ( ! pdata ) {
2010-01-08 14:01:24 +03:00
pr_info ( " No platform data in %s! \n " , __func__ ) ;
return - EINVAL ;
}
2010-02-03 23:38:12 +03:00
chip = kzalloc ( sizeof ( struct pm860x_chip ) , GFP_KERNEL ) ;
if ( chip = = NULL )
return - ENOMEM ;
chip - > id = verify_addr ( client ) ;
chip - > client = client ;
i2c_set_clientdata ( client , chip ) ;
chip - > dev = & client - > dev ;
mutex_init ( & chip - > io_lock ) ;
dev_set_drvdata ( chip - > dev , chip ) ;
2010-01-08 14:01:24 +03:00
/*
* Both client and companion client shares same platform driver .
* Driver distinguishes them by pdata - > companion_addr .
* pdata - > companion_addr is only assigned if companion chip exists .
* At the same time , the companion_addr shouldn ' t equal to client
* address .
*/
2010-02-03 23:38:12 +03:00
if ( pdata - > companion_addr & & ( pdata - > companion_addr ! = client - > addr ) ) {
chip - > companion_addr = pdata - > companion_addr ;
chip - > companion = i2c_new_dummy ( chip - > client - > adapter ,
chip - > companion_addr ) ;
2010-01-08 14:01:24 +03:00
i2c_set_clientdata ( chip - > companion , chip ) ;
}
2010-02-03 23:38:12 +03:00
pm860x_device_init ( chip , pdata ) ;
2010-01-07 01:04:18 +03:00
return 0 ;
}
static int __devexit pm860x_remove ( struct i2c_client * client )
{
2010-01-08 14:01:24 +03:00
struct pm860x_chip * chip = i2c_get_clientdata ( client ) ;
pm860x_device_exit ( chip ) ;
i2c_unregister_device ( chip - > companion ) ;
i2c_set_clientdata ( chip - > companion , NULL ) ;
i2c_set_clientdata ( chip - > client , NULL ) ;
2010-01-07 01:04:18 +03:00
kfree ( chip ) ;
return 0 ;
}
static struct i2c_driver pm860x_driver = {
. driver = {
. name = " 88PM860x " ,
. owner = THIS_MODULE ,
} ,
. probe = pm860x_probe ,
. remove = __devexit_p ( pm860x_remove ) ,
. id_table = pm860x_id_table ,
} ;
static int __init pm860x_i2c_init ( void )
{
int ret ;
ret = i2c_add_driver ( & pm860x_driver ) ;
if ( ret ! = 0 )
pr_err ( " Failed to register 88PM860x I2C driver: %d \n " , ret ) ;
return ret ;
}
subsys_initcall ( pm860x_i2c_init ) ;
static void __exit pm860x_i2c_exit ( void )
{
i2c_del_driver ( & pm860x_driver ) ;
}
module_exit ( pm860x_i2c_exit ) ;
MODULE_DESCRIPTION ( " I2C Driver for Marvell 88PM860x " ) ;
MODULE_AUTHOR ( " Haojian Zhuang <haojian.zhuang@marvell.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;