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/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>
2018-04-19 23:00:11 +03:00
# include <linux/platform_data/i2c-pca-platform.h>
2008-04-23 00:16:46 +04:00
# include <linux/gpio.h>
2017-06-26 03:44:31 +03:00
# include <linux/gpio/consumer.h>
2010-05-21 20:41:01 +04:00
# include <linux/io.h>
2017-06-26 03:44:32 +03:00
# include <linux/of.h>
# include <linux/of_device.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 */
2017-06-26 03:44:31 +03:00
struct gpio_desc * gpio ;
2008-04-23 00:16:46 +04:00
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 ;
2017-06-26 03:44:34 +03:00
dev_warn ( & i2c - > adap . dev , " No reset-pin found. Chip may get stuck! \n " ) ;
2008-04-23 00:16:46 +04:00
}
static void i2c_pca_pf_resetchip ( void * pd )
{
struct i2c_pca_pf_data * i2c = pd ;
2017-06-26 03:44:31 +03:00
gpiod_set_value ( i2c - > gpio , 1 ) ;
2008-04-23 00:16:46 +04:00
ndelay ( 100 ) ;
2017-06-26 03:44:31 +03:00
gpiod_set_value ( i2c - > gpio , 0 ) ;
2008-04-23 00:16:46 +04:00
}
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 ;
}
2012-11-28 00:59:38 +04:00
static int i2c_pca_pf_probe ( struct platform_device * pdev )
2008-04-23 00:16:46 +04:00
{
struct i2c_pca_pf_data * i2c ;
struct resource * res ;
struct i2c_pca9564_pf_platform_data * platform_data =
2013-07-30 11:59:33 +04:00
dev_get_platdata ( & pdev - > dev ) ;
2017-06-26 03:44:32 +03:00
struct device_node * np = pdev - > dev . of_node ;
2008-04-23 00:16:46 +04:00
int ret = 0 ;
int irq ;
irq = platform_get_irq ( pdev , 0 ) ;
/* If irq is 0, we do polling. */
2017-06-26 03:44:32 +03:00
if ( irq < 0 )
irq = 0 ;
2008-04-23 00:16:46 +04:00
2017-06-26 03:44:33 +03:00
i2c = devm_kzalloc ( & pdev - > dev , sizeof ( * i2c ) , GFP_KERNEL ) ;
if ( ! i2c )
return - ENOMEM ;
2008-04-23 00:16:46 +04:00
2017-06-26 03:44:33 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
i2c - > reg_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( i2c - > reg_base ) )
return PTR_ERR ( i2c - > reg_base ) ;
2008-04-23 00:16:46 +04:00
init_waitqueue_head ( & i2c - > wait ) ;
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 ;
2017-06-26 03:44:32 +03:00
i2c - > adap . dev . of_node = np ;
2008-04-23 00:16:46 +04:00
2017-07-05 13:13:56 +03:00
i2c - > gpio = devm_gpiod_get_optional ( & pdev - > dev , " reset-gpios " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( i2c - > gpio ) )
return PTR_ERR ( i2c - > gpio ) ;
2017-07-05 13:13:57 +03:00
i2c - > adap . timeout = HZ ;
ret = device_property_read_u32 ( & pdev - > dev , " clock-frequency " ,
& i2c - > algo_data . i2c_clock ) ;
if ( ret )
i2c - > algo_data . i2c_clock = 59000 ;
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 ;
}
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 ;
2017-06-30 03:54:04 +03:00
if ( i2c - > gpio )
i2c - > algo_data . reset_chip = i2c_pca_pf_resetchip ;
else
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 ;
}
if ( irq ) {
2017-06-26 03:44:33 +03:00
ret = devm_request_irq ( & pdev - > dev , 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 )
2017-06-26 03:44:33 +03:00
return ret ;
2008-04-23 00:16:46 +04:00
}
2017-06-30 03:54:05 +03:00
ret = i2c_pca_add_numbered_bus ( & i2c - > adap ) ;
if ( ret )
return ret ;
2008-04-23 00:16:46 +04:00
platform_set_drvdata ( pdev , i2c ) ;
2017-06-26 03:44:34 +03:00
dev_info ( & pdev - > dev , " registered. \n " ) ;
2008-04-23 00:16:46 +04:00
return 0 ;
}
2012-11-28 00:59:38 +04:00
static int i2c_pca_pf_remove ( struct platform_device * pdev )
2008-04-23 00:16:46 +04:00
{
struct i2c_pca_pf_data * i2c = platform_get_drvdata ( pdev ) ;
i2c_del_adapter ( & i2c - > adap ) ;
return 0 ;
}
2017-06-26 03:44:32 +03:00
# ifdef CONFIG_OF
static const struct of_device_id i2c_pca_of_match_table [ ] = {
{ . compatible = " nxp,pca9564 " } ,
{ . compatible = " nxp,pca9665 " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , i2c_pca_of_match_table ) ;
# endif
2008-04-23 00:16:46 +04:00
static struct platform_driver i2c_pca_pf_driver = {
. probe = i2c_pca_pf_probe ,
2012-11-28 00:59:38 +04:00
. remove = i2c_pca_pf_remove ,
2008-04-23 00:16:46 +04:00
. driver = {
. name = " i2c-pca-platform " ,
2017-06-26 03:44:32 +03:00
. of_match_table = of_match_ptr ( i2c_pca_of_match_table ) ,
2008-04-23 00:16:46 +04:00
} ,
} ;
2012-01-12 23:32:04 +04:00
module_platform_driver ( i2c_pca_pf_driver ) ;
2008-04-23 00:16:46 +04:00
2015-04-20 16:51:40 +03:00
MODULE_AUTHOR ( " Wolfram Sang <kernel@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 " ) ;