2011-01-10 22:11:23 +01:00
/*
* I2C multiplexer using GPIO API
*
* Peter Korsgaard < peter . korsgaard @ barco . 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/i2c.h>
# include <linux/i2c-mux.h>
2012-04-28 15:32:06 +02:00
# include <linux/i2c-mux-gpio.h>
2011-01-10 22:11:23 +01:00
# include <linux/platform_device.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/gpio.h>
2012-10-25 18:23:53 +02:00
# include <linux/of_gpio.h>
2011-01-10 22:11:23 +01:00
struct gpiomux {
struct i2c_adapter * parent ;
struct i2c_adapter * * adap ; /* child busses */
2012-04-28 15:32:06 +02:00
struct i2c_mux_gpio_platform_data data ;
2012-10-05 22:23:54 +02:00
unsigned gpio_base ;
2011-01-10 22:11:23 +01:00
} ;
2012-04-28 15:32:06 +02:00
static void i2c_mux_gpio_set ( const struct gpiomux * mux , unsigned val )
2011-01-10 22:11:23 +01:00
{
int i ;
for ( i = 0 ; i < mux - > data . n_gpios ; i + + )
2013-10-11 14:17:10 +02:00
gpio_set_value_cansleep ( mux - > gpio_base + mux - > data . gpios [ i ] ,
val & ( 1 < < i ) ) ;
2011-01-10 22:11:23 +01:00
}
2012-04-28 15:32:06 +02:00
static int i2c_mux_gpio_select ( struct i2c_adapter * adap , void * data , u32 chan )
2011-01-10 22:11:23 +01:00
{
struct gpiomux * mux = data ;
2013-10-11 12:09:57 +02:00
i2c_mux_gpio_set ( mux , chan ) ;
2011-01-10 22:11:23 +01:00
return 0 ;
}
2012-04-28 15:32:06 +02:00
static int i2c_mux_gpio_deselect ( struct i2c_adapter * adap , void * data , u32 chan )
2011-01-10 22:11:23 +01:00
{
struct gpiomux * mux = data ;
2012-04-28 15:32:06 +02:00
i2c_mux_gpio_set ( mux , mux - > data . idle ) ;
2011-01-10 22:11:23 +01:00
return 0 ;
}
2012-11-27 15:59:38 -05:00
static int match_gpio_chip_by_label ( struct gpio_chip * chip ,
2012-10-05 22:23:54 +02:00
void * data )
{
return ! strcmp ( chip - > label , data ) ;
}
2012-10-25 18:23:53 +02:00
# ifdef CONFIG_OF
2012-11-27 15:59:38 -05:00
static int i2c_mux_gpio_probe_dt ( struct gpiomux * mux ,
2012-10-25 18:23:53 +02:00
struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct device_node * adapter_np , * child ;
struct i2c_adapter * adapter ;
unsigned * values , * gpios ;
2013-10-08 15:51:53 +02:00
int i = 0 , ret ;
2012-10-25 18:23:53 +02:00
if ( ! np )
return - ENODEV ;
adapter_np = of_parse_phandle ( np , " i2c-parent " , 0 ) ;
if ( ! adapter_np ) {
dev_err ( & pdev - > dev , " Cannot parse i2c-parent \n " ) ;
return - ENODEV ;
}
adapter = of_find_i2c_adapter_by_node ( adapter_np ) ;
if ( ! adapter ) {
dev_err ( & pdev - > dev , " Cannot find parent bus \n " ) ;
2013-10-09 11:50:45 +02:00
return - EPROBE_DEFER ;
2012-10-25 18:23:53 +02:00
}
mux - > data . parent = i2c_adapter_id ( adapter ) ;
put_device ( & adapter - > dev ) ;
mux - > data . n_values = of_get_child_count ( np ) ;
values = devm_kzalloc ( & pdev - > dev ,
sizeof ( * mux - > data . values ) * mux - > data . n_values ,
GFP_KERNEL ) ;
if ( ! values ) {
dev_err ( & pdev - > dev , " Cannot allocate values array " ) ;
return - ENOMEM ;
}
for_each_child_of_node ( np , child ) {
of_property_read_u32 ( child , " reg " , values + i ) ;
i + + ;
}
mux - > data . values = values ;
if ( of_property_read_u32 ( np , " idle-state " , & mux - > data . idle ) )
mux - > data . idle = I2C_MUX_GPIO_NO_IDLE ;
mux - > data . n_gpios = of_gpio_named_count ( np , " mux-gpios " ) ;
if ( mux - > data . n_gpios < 0 ) {
dev_err ( & pdev - > dev , " Missing mux-gpios property in the DT. \n " ) ;
return - EINVAL ;
}
gpios = devm_kzalloc ( & pdev - > dev ,
sizeof ( * mux - > data . gpios ) * mux - > data . n_gpios , GFP_KERNEL ) ;
if ( ! gpios ) {
dev_err ( & pdev - > dev , " Cannot allocate gpios array " ) ;
return - ENOMEM ;
}
2013-10-08 15:51:53 +02:00
for ( i = 0 ; i < mux - > data . n_gpios ; i + + ) {
ret = of_get_named_gpio ( np , " mux-gpios " , i ) ;
if ( ret < 0 )
return ret ;
gpios [ i ] = ret ;
}
2012-10-25 18:23:53 +02:00
mux - > data . gpios = gpios ;
return 0 ;
}
# else
2012-11-27 15:59:38 -05:00
static int i2c_mux_gpio_probe_dt ( struct gpiomux * mux ,
2012-10-25 18:23:53 +02:00
struct platform_device * pdev )
{
return 0 ;
}
# endif
2012-11-27 15:59:38 -05:00
static int i2c_mux_gpio_probe ( struct platform_device * pdev )
2011-01-10 22:11:23 +01:00
{
struct gpiomux * mux ;
struct i2c_adapter * parent ;
int ( * deselect ) ( struct i2c_adapter * , void * , u32 ) ;
2012-10-05 22:23:54 +02:00
unsigned initial_state , gpio_base ;
2011-01-10 22:11:23 +01:00
int i , ret ;
2012-10-25 18:23:53 +02:00
mux = devm_kzalloc ( & pdev - > dev , sizeof ( * mux ) , GFP_KERNEL ) ;
if ( ! mux ) {
dev_err ( & pdev - > dev , " Cannot allocate gpiomux structure " ) ;
return - ENOMEM ;
2011-01-10 22:11:23 +01:00
}
2012-10-25 18:23:53 +02:00
platform_set_drvdata ( pdev , mux ) ;
2013-07-30 16:59:33 +09:00
if ( ! dev_get_platdata ( & pdev - > dev ) ) {
2012-10-25 18:23:53 +02:00
ret = i2c_mux_gpio_probe_dt ( mux , pdev ) ;
if ( ret < 0 )
return ret ;
2013-07-30 16:59:33 +09:00
} else {
memcpy ( & mux - > data , dev_get_platdata ( & pdev - > dev ) ,
sizeof ( mux - > data ) ) ;
}
2012-10-25 18:23:53 +02:00
2012-10-05 22:23:54 +02:00
/*
* If a GPIO chip name is provided , the GPIO pin numbers provided are
* relative to its base GPIO number . Otherwise they are absolute .
*/
2012-10-25 18:23:53 +02:00
if ( mux - > data . gpio_chip ) {
2012-10-05 22:23:54 +02:00
struct gpio_chip * gpio ;
2012-10-25 18:23:53 +02:00
gpio = gpiochip_find ( mux - > data . gpio_chip ,
2012-10-05 22:23:54 +02:00
match_gpio_chip_by_label ) ;
if ( ! gpio )
return - EPROBE_DEFER ;
gpio_base = gpio - > base ;
} else {
gpio_base = 0 ;
}
2012-10-25 18:23:53 +02:00
parent = i2c_get_adapter ( mux - > data . parent ) ;
2011-01-10 22:11:23 +01:00
if ( ! parent ) {
dev_err ( & pdev - > dev , " Parent adapter (%d) not found \n " ,
2012-10-25 18:23:53 +02:00
mux - > data . parent ) ;
2013-10-09 11:50:45 +02:00
return - EPROBE_DEFER ;
2011-01-10 22:11:23 +01:00
}
mux - > parent = parent ;
2012-10-05 22:23:54 +02:00
mux - > gpio_base = gpio_base ;
2012-10-25 18:23:53 +02:00
2012-10-05 22:23:53 +02:00
mux - > adap = devm_kzalloc ( & pdev - > dev ,
2012-10-25 18:23:53 +02:00
sizeof ( * mux - > adap ) * mux - > data . n_values ,
2012-10-05 22:23:53 +02:00
GFP_KERNEL ) ;
2011-01-10 22:11:23 +01:00
if ( ! mux - > adap ) {
2012-10-25 18:23:53 +02:00
dev_err ( & pdev - > dev , " Cannot allocate i2c_adapter structure " ) ;
2011-01-10 22:11:23 +01:00
ret = - ENOMEM ;
2012-10-05 22:23:53 +02:00
goto alloc_failed ;
2011-01-10 22:11:23 +01:00
}
2012-10-25 18:23:53 +02:00
if ( mux - > data . idle ! = I2C_MUX_GPIO_NO_IDLE ) {
initial_state = mux - > data . idle ;
2012-04-28 15:32:06 +02:00
deselect = i2c_mux_gpio_deselect ;
2011-01-10 22:11:23 +01:00
} else {
2012-10-25 18:23:53 +02:00
initial_state = mux - > data . values [ 0 ] ;
2011-01-10 22:11:23 +01:00
deselect = NULL ;
}
2012-10-25 18:23:53 +02:00
for ( i = 0 ; i < mux - > data . n_gpios ; i + + ) {
ret = gpio_request ( gpio_base + mux - > data . gpios [ i ] , " i2c-mux-gpio " ) ;
2013-03-06 22:35:53 +00:00
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to request GPIO %d \n " ,
mux - > data . gpios [ i ] ) ;
2011-01-10 22:11:23 +01:00
goto err_request_gpio ;
2013-03-06 22:35:53 +00:00
}
ret = gpio_direction_output ( gpio_base + mux - > data . gpios [ i ] ,
initial_state & ( 1 < < i ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" Failed to set direction of GPIO %d to output \n " ,
mux - > data . gpios [ i ] ) ;
i + + ; /* gpio_request above succeeded, so must free */
goto err_request_gpio ;
}
2011-01-10 22:11:23 +01:00
}
2012-10-25 18:23:53 +02:00
for ( i = 0 ; i < mux - > data . n_values ; i + + ) {
u32 nr = mux - > data . base_nr ? ( mux - > data . base_nr + i ) : 0 ;
unsigned int class = mux - > data . classes ? mux - > data . classes [ i ] : 0 ;
2011-01-10 22:11:23 +01:00
2012-10-05 22:23:51 +02:00
mux - > adap [ i ] = i2c_add_mux_adapter ( parent , & pdev - > dev , mux , nr ,
2013-10-11 12:09:57 +02:00
mux - > data . values [ i ] , class ,
2012-04-28 15:32:06 +02:00
i2c_mux_gpio_select , deselect ) ;
2011-01-10 22:11:23 +01:00
if ( ! mux - > adap [ i ] ) {
ret = - ENODEV ;
dev_err ( & pdev - > dev , " Failed to add adapter %d \n " , i ) ;
goto add_adapter_failed ;
}
}
dev_info ( & pdev - > dev , " %d port mux on %s adapter \n " ,
2012-10-25 18:23:53 +02:00
mux - > data . n_values , parent - > name ) ;
2011-01-10 22:11:23 +01:00
return 0 ;
add_adapter_failed :
for ( ; i > 0 ; i - - )
i2c_del_mux_adapter ( mux - > adap [ i - 1 ] ) ;
2012-10-25 18:23:53 +02:00
i = mux - > data . n_gpios ;
2011-01-10 22:11:23 +01:00
err_request_gpio :
for ( ; i > 0 ; i - - )
2012-10-25 18:23:53 +02:00
gpio_free ( gpio_base + mux - > data . gpios [ i - 1 ] ) ;
2011-01-10 22:11:23 +01:00
alloc_failed :
i2c_put_adapter ( parent ) ;
return ret ;
}
2012-11-27 15:59:38 -05:00
static int i2c_mux_gpio_remove ( struct platform_device * pdev )
2011-01-10 22:11:23 +01:00
{
struct gpiomux * mux = platform_get_drvdata ( pdev ) ;
int i ;
for ( i = 0 ; i < mux - > data . n_values ; i + + )
i2c_del_mux_adapter ( mux - > adap [ i ] ) ;
for ( i = 0 ; i < mux - > data . n_gpios ; i + + )
2012-10-05 22:23:54 +02:00
gpio_free ( mux - > gpio_base + mux - > data . gpios [ i ] ) ;
2011-01-10 22:11:23 +01:00
i2c_put_adapter ( mux - > parent ) ;
return 0 ;
}
2012-11-27 15:59:38 -05:00
static const struct of_device_id i2c_mux_gpio_of_match [ ] = {
2012-10-25 18:23:53 +02:00
{ . compatible = " i2c-mux-gpio " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , i2c_mux_gpio_of_match ) ;
2012-04-28 15:32:06 +02:00
static struct platform_driver i2c_mux_gpio_driver = {
. probe = i2c_mux_gpio_probe ,
2012-11-27 15:59:38 -05:00
. remove = i2c_mux_gpio_remove ,
2011-01-10 22:11:23 +01:00
. driver = {
. owner = THIS_MODULE ,
2012-04-28 15:32:06 +02:00
. name = " i2c-mux-gpio " ,
2013-09-30 09:04:25 +05:30
. of_match_table = i2c_mux_gpio_of_match ,
2011-01-10 22:11:23 +01:00
} ,
} ;
2012-04-28 15:32:06 +02:00
module_platform_driver ( i2c_mux_gpio_driver ) ;
2011-01-10 22:11:23 +01:00
MODULE_DESCRIPTION ( " GPIO-based I2C multiplexer driver " ) ;
MODULE_AUTHOR ( " Peter Korsgaard <peter.korsgaard@barco.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2012-04-28 15:32:06 +02:00
MODULE_ALIAS ( " platform:i2c-mux-gpio " ) ;