2008-04-23 00:16:46 +04:00
/*
* i2c_pca_platform . c
*
* Platform driver for the PCA9564 I2C controller .
*
* Copyright ( C ) 2008 Pengutronix
*
* 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/init.h>
# include <linux/slab.h>
# include <linux/delay.h>
2009-03-28 23:34:45 +03:00
# include <linux/jiffies.h>
2008-04-23 00:16:46 +04:00
# include <linux/errno.h>
# include <linux/i2c.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/i2c-algo-pca.h>
# include <linux/i2c-pca-platform.h>
# include <linux/gpio.h>
2010-05-21 20:41:01 +04:00
# include <linux/io.h>
2008-04-23 00:16:46 +04:00
# include <asm/irq.h>
struct i2c_pca_pf_data {
void __iomem * reg_base ;
int irq ; /* if 0, use polling */
int gpio ;
wait_queue_head_t wait ;
struct i2c_adapter adap ;
struct i2c_algo_pca_data algo_data ;
unsigned long io_base ;
unsigned long io_size ;
} ;
/* Read/Write functions for different register alignments */
static int i2c_pca_pf_readbyte8 ( void * pd , int reg )
{
struct i2c_pca_pf_data * i2c = pd ;
return ioread8 ( i2c - > reg_base + reg ) ;
}
static int i2c_pca_pf_readbyte16 ( void * pd , int reg )
{
struct i2c_pca_pf_data * i2c = pd ;
return ioread8 ( i2c - > reg_base + reg * 2 ) ;
}
static int i2c_pca_pf_readbyte32 ( void * pd , int reg )
{
struct i2c_pca_pf_data * i2c = pd ;
return ioread8 ( i2c - > reg_base + reg * 4 ) ;
}
static void i2c_pca_pf_writebyte8 ( void * pd , int reg , int val )
{
struct i2c_pca_pf_data * i2c = pd ;
iowrite8 ( val , i2c - > reg_base + reg ) ;
}
static void i2c_pca_pf_writebyte16 ( void * pd , int reg , int val )
{
struct i2c_pca_pf_data * i2c = pd ;
iowrite8 ( val , i2c - > reg_base + reg * 2 ) ;
}
static void i2c_pca_pf_writebyte32 ( void * pd , int reg , int val )
{
struct i2c_pca_pf_data * i2c = pd ;
iowrite8 ( val , i2c - > reg_base + reg * 4 ) ;
}
static int i2c_pca_pf_waitforcompletion ( void * pd )
{
struct i2c_pca_pf_data * i2c = pd ;
2009-03-28 23:34:45 +03:00
unsigned long timeout ;
2010-09-30 16:14:22 +04:00
long ret ;
2008-04-23 00:16:46 +04:00
if ( i2c - > irq ) {
2010-01-16 22:43:13 +03:00
ret = wait_event_timeout ( i2c - > wait ,
2008-04-23 00:16:46 +04:00
i2c - > algo_data . read_byte ( i2c , I2C_PCA_CON )
2009-03-28 23:34:45 +03:00
& I2C_PCA_CON_SI , i2c - > adap . timeout ) ;
2008-04-23 00:16:46 +04:00
} else {
2009-03-28 23:34:45 +03:00
/* Do polling */
timeout = jiffies + i2c - > adap . timeout ;
2010-09-30 16:14:22 +04:00
do {
ret = time_before ( jiffies , timeout ) ;
if ( i2c - > algo_data . read_byte ( i2c , I2C_PCA_CON )
& I2C_PCA_CON_SI )
break ;
2008-04-23 00:16:46 +04:00
udelay ( 100 ) ;
2010-09-30 16:14:22 +04:00
} while ( ret ) ;
2008-04-23 00:16:46 +04:00
}
2009-03-28 23:34:45 +03:00
return ret > 0 ;
2008-04-23 00:16:46 +04:00
}
static void i2c_pca_pf_dummyreset ( void * pd )
{
struct i2c_pca_pf_data * i2c = pd ;
printk ( KERN_WARNING " %s: No reset-pin found. Chip may get stuck! \n " ,
i2c - > adap . name ) ;
}
static void i2c_pca_pf_resetchip ( void * pd )
{
struct i2c_pca_pf_data * i2c = pd ;
gpio_set_value ( i2c - > gpio , 0 ) ;
ndelay ( 100 ) ;
gpio_set_value ( i2c - > gpio , 1 ) ;
}
static irqreturn_t i2c_pca_pf_handler ( int this_irq , void * dev_id )
{
struct i2c_pca_pf_data * i2c = dev_id ;
if ( ( i2c - > algo_data . read_byte ( i2c , I2C_PCA_CON ) & I2C_PCA_CON_SI ) = = 0 )
return IRQ_NONE ;
2010-01-16 22:43:13 +03:00
wake_up ( & i2c - > wait ) ;
2008-04-23 00:16:46 +04:00
return IRQ_HANDLED ;
}
static int __devinit i2c_pca_pf_probe ( struct platform_device * pdev )
{
struct i2c_pca_pf_data * i2c ;
struct resource * res ;
struct i2c_pca9564_pf_platform_data * platform_data =
pdev - > dev . platform_data ;
int ret = 0 ;
int irq ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
irq = platform_get_irq ( pdev , 0 ) ;
/* If irq is 0, we do polling. */
if ( res = = NULL ) {
ret = - ENODEV ;
goto e_print ;
}
2009-06-14 02:20:36 +04:00
if ( ! request_mem_region ( res - > start , resource_size ( res ) , res - > name ) ) {
2008-04-23 00:16:46 +04:00
ret = - ENOMEM ;
goto e_print ;
}
i2c = kzalloc ( sizeof ( struct i2c_pca_pf_data ) , GFP_KERNEL ) ;
if ( ! i2c ) {
ret = - ENOMEM ;
goto e_alloc ;
}
init_waitqueue_head ( & i2c - > wait ) ;
2009-06-14 02:20:36 +04:00
i2c - > reg_base = ioremap ( res - > start , resource_size ( res ) ) ;
2008-04-23 00:16:46 +04:00
if ( ! i2c - > reg_base ) {
2008-07-15 00:38:26 +04:00
ret = - ENOMEM ;
2008-04-23 00:16:46 +04:00
goto e_remap ;
}
i2c - > io_base = res - > start ;
2009-06-14 02:20:36 +04:00
i2c - > io_size = resource_size ( res ) ;
2008-04-23 00:16:46 +04:00
i2c - > irq = irq ;
2012-03-27 13:10:28 +04:00
i2c - > adap . nr = pdev - > id ;
2008-04-23 00:16:46 +04:00
i2c - > adap . owner = THIS_MODULE ;
2009-03-28 23:34:44 +03:00
snprintf ( i2c - > adap . name , sizeof ( i2c - > adap . name ) ,
" PCA9564/PCA9665 at 0x%08lx " ,
( unsigned long ) res - > start ) ;
2008-04-23 00:16:46 +04:00
i2c - > adap . algo_data = & i2c - > algo_data ;
i2c - > adap . dev . parent = & pdev - > dev ;
2009-03-28 23:34:45 +03:00
if ( platform_data ) {
i2c - > adap . timeout = platform_data - > timeout ;
i2c - > algo_data . i2c_clock = platform_data - > i2c_clock_speed ;
i2c - > gpio = platform_data - > gpio ;
} else {
i2c - > adap . timeout = HZ ;
i2c - > algo_data . i2c_clock = 59000 ;
i2c - > gpio = - 1 ;
}
2008-04-23 00:16:46 +04:00
i2c - > algo_data . data = i2c ;
2009-03-28 23:34:45 +03:00
i2c - > algo_data . wait_for_completion = i2c_pca_pf_waitforcompletion ;
i2c - > algo_data . reset_chip = i2c_pca_pf_dummyreset ;
2008-04-23 00:16:46 +04:00
switch ( res - > flags & IORESOURCE_MEM_TYPE_MASK ) {
case IORESOURCE_MEM_32BIT :
i2c - > algo_data . write_byte = i2c_pca_pf_writebyte32 ;
i2c - > algo_data . read_byte = i2c_pca_pf_readbyte32 ;
break ;
case IORESOURCE_MEM_16BIT :
i2c - > algo_data . write_byte = i2c_pca_pf_writebyte16 ;
i2c - > algo_data . read_byte = i2c_pca_pf_readbyte16 ;
break ;
case IORESOURCE_MEM_8BIT :
default :
i2c - > algo_data . write_byte = i2c_pca_pf_writebyte8 ;
i2c - > algo_data . read_byte = i2c_pca_pf_readbyte8 ;
break ;
}
/* Use gpio_is_valid() when in mainline */
if ( i2c - > gpio > - 1 ) {
ret = gpio_request ( i2c - > gpio , i2c - > adap . name ) ;
if ( ret = = 0 ) {
gpio_direction_output ( i2c - > gpio , 1 ) ;
i2c - > algo_data . reset_chip = i2c_pca_pf_resetchip ;
} else {
printk ( KERN_WARNING " %s: Registering gpio failed! \n " ,
i2c - > adap . name ) ;
i2c - > gpio = ret ;
}
}
if ( irq ) {
ret = request_irq ( irq , i2c_pca_pf_handler ,
2010-10-24 20:16:57 +04:00
IRQF_TRIGGER_FALLING , pdev - > name , i2c ) ;
2008-04-23 00:16:46 +04:00
if ( ret )
goto e_reqirq ;
}
if ( i2c_pca_add_numbered_bus ( & i2c - > adap ) < 0 ) {
ret = - ENODEV ;
goto e_adapt ;
}
platform_set_drvdata ( pdev , i2c ) ;
printk ( KERN_INFO " %s registered. \n " , i2c - > adap . name ) ;
return 0 ;
e_adapt :
if ( irq )
free_irq ( irq , i2c ) ;
e_reqirq :
if ( i2c - > gpio > - 1 )
gpio_free ( i2c - > gpio ) ;
iounmap ( i2c - > reg_base ) ;
e_remap :
kfree ( i2c ) ;
e_alloc :
2009-06-14 02:20:36 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2008-04-23 00:16:46 +04:00
e_print :
2009-03-28 23:34:44 +03:00
printk ( KERN_ERR " Registering PCA9564/PCA9665 FAILED! (%d) \n " , ret ) ;
2008-04-23 00:16:46 +04:00
return ret ;
}
static int __devexit i2c_pca_pf_remove ( struct platform_device * pdev )
{
struct i2c_pca_pf_data * i2c = platform_get_drvdata ( pdev ) ;
platform_set_drvdata ( pdev , NULL ) ;
i2c_del_adapter ( & i2c - > adap ) ;
if ( i2c - > irq )
free_irq ( i2c - > irq , i2c ) ;
if ( i2c - > gpio > - 1 )
gpio_free ( i2c - > gpio ) ;
iounmap ( i2c - > reg_base ) ;
release_mem_region ( i2c - > io_base , i2c - > io_size ) ;
kfree ( i2c ) ;
return 0 ;
}
static struct platform_driver i2c_pca_pf_driver = {
. probe = i2c_pca_pf_probe ,
. remove = __devexit_p ( i2c_pca_pf_remove ) ,
. driver = {
. name = " i2c-pca-platform " ,
. owner = THIS_MODULE ,
} ,
} ;
2012-01-12 23:32:04 +04:00
module_platform_driver ( i2c_pca_pf_driver ) ;
2008-04-23 00:16:46 +04:00
MODULE_AUTHOR ( " Wolfram Sang <w.sang@pengutronix.de> " ) ;
2009-03-28 23:34:44 +03:00
MODULE_DESCRIPTION ( " I2C-PCA9564/PCA9665 platform driver " ) ;
2008-04-23 00:16:46 +04:00
MODULE_LICENSE ( " GPL " ) ;