2012-05-18 09:29:34 -06:00
/*
* I2C multiplexer using pinctrl API
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/i2c.h>
# include <linux/i2c-mux.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/of_i2c.h>
# include <linux/pinctrl/consumer.h>
# include <linux/i2c-mux-pinctrl.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
struct i2c_mux_pinctrl {
struct device * dev ;
struct i2c_mux_pinctrl_platform_data * pdata ;
struct pinctrl * pinctrl ;
struct pinctrl_state * * states ;
struct pinctrl_state * state_idle ;
struct i2c_adapter * parent ;
struct i2c_adapter * * busses ;
} ;
static int i2c_mux_pinctrl_select ( struct i2c_adapter * adap , void * data ,
u32 chan )
{
struct i2c_mux_pinctrl * mux = data ;
return pinctrl_select_state ( mux - > pinctrl , mux - > states [ chan ] ) ;
}
static int i2c_mux_pinctrl_deselect ( struct i2c_adapter * adap , void * data ,
u32 chan )
{
struct i2c_mux_pinctrl * mux = data ;
return pinctrl_select_state ( mux - > pinctrl , mux - > state_idle ) ;
}
# ifdef CONFIG_OF
static int i2c_mux_pinctrl_parse_dt ( struct i2c_mux_pinctrl * mux ,
struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
int num_names , i , ret ;
struct device_node * adapter_np ;
struct i2c_adapter * adapter ;
if ( ! np )
return 0 ;
mux - > pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * mux - > pdata ) , GFP_KERNEL ) ;
if ( ! mux - > pdata ) {
dev_err ( mux - > dev ,
" Cannot allocate i2c_mux_pinctrl_platform_data \n " ) ;
return - ENOMEM ;
}
num_names = of_property_count_strings ( np , " pinctrl-names " ) ;
if ( num_names < 0 ) {
dev_err ( mux - > dev , " Cannot parse pinctrl-names: %d \n " ,
num_names ) ;
return num_names ;
}
mux - > pdata - > pinctrl_states = devm_kzalloc ( & pdev - > dev ,
sizeof ( * mux - > pdata - > pinctrl_states ) * num_names ,
GFP_KERNEL ) ;
if ( ! mux - > pdata - > pinctrl_states ) {
dev_err ( mux - > dev , " Cannot allocate pinctrl_states \n " ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < num_names ; i + + ) {
ret = of_property_read_string_index ( np , " pinctrl-names " , i ,
& mux - > pdata - > pinctrl_states [ mux - > pdata - > bus_count ] ) ;
if ( ret < 0 ) {
dev_err ( mux - > dev , " Cannot parse pinctrl-names: %d \n " ,
ret ) ;
return ret ;
}
if ( ! strcmp ( mux - > pdata - > pinctrl_states [ mux - > pdata - > bus_count ] ,
" idle " ) ) {
if ( i ! = num_names - 1 ) {
dev_err ( mux - > dev , " idle state must be last \n " ) ;
return - EINVAL ;
}
mux - > pdata - > pinctrl_state_idle = " idle " ;
} else {
mux - > pdata - > bus_count + + ;
}
}
adapter_np = of_parse_phandle ( np , " i2c-parent " , 0 ) ;
if ( ! adapter_np ) {
dev_err ( mux - > dev , " Cannot parse i2c-parent \n " ) ;
return - ENODEV ;
}
adapter = of_find_i2c_adapter_by_node ( adapter_np ) ;
if ( ! adapter ) {
dev_err ( mux - > dev , " Cannot find parent bus \n " ) ;
return - ENODEV ;
}
mux - > pdata - > parent_bus_num = i2c_adapter_id ( adapter ) ;
put_device ( & adapter - > dev ) ;
return 0 ;
}
# else
static inline int i2c_mux_pinctrl_parse_dt ( struct i2c_mux_pinctrl * mux ,
struct platform_device * pdev )
{
return 0 ;
}
# endif
2012-11-27 15:59:38 -05:00
static int i2c_mux_pinctrl_probe ( struct platform_device * pdev )
2012-05-18 09:29:34 -06:00
{
struct i2c_mux_pinctrl * mux ;
int ( * deselect ) ( struct i2c_adapter * , void * , u32 ) ;
int i , ret ;
mux = devm_kzalloc ( & pdev - > dev , sizeof ( * mux ) , GFP_KERNEL ) ;
if ( ! mux ) {
dev_err ( & pdev - > dev , " Cannot allocate i2c_mux_pinctrl \n " ) ;
ret = - ENOMEM ;
goto err ;
}
platform_set_drvdata ( pdev , mux ) ;
mux - > dev = & pdev - > dev ;
mux - > pdata = pdev - > dev . platform_data ;
if ( ! mux - > pdata ) {
ret = i2c_mux_pinctrl_parse_dt ( mux , pdev ) ;
if ( ret < 0 )
goto err ;
}
if ( ! mux - > pdata ) {
dev_err ( & pdev - > dev , " Missing platform data \n " ) ;
ret = - ENODEV ;
goto err ;
}
mux - > states = devm_kzalloc ( & pdev - > dev ,
sizeof ( * mux - > states ) * mux - > pdata - > bus_count ,
GFP_KERNEL ) ;
if ( ! mux - > states ) {
dev_err ( & pdev - > dev , " Cannot allocate states \n " ) ;
ret = - ENOMEM ;
goto err ;
}
mux - > busses = devm_kzalloc ( & pdev - > dev ,
2013-01-08 14:40:09 +01:00
sizeof ( * mux - > busses ) * mux - > pdata - > bus_count ,
2012-05-18 09:29:34 -06:00
GFP_KERNEL ) ;
2012-11-13 22:27:19 +01:00
if ( ! mux - > busses ) {
2012-05-18 09:29:34 -06:00
dev_err ( & pdev - > dev , " Cannot allocate busses \n " ) ;
ret = - ENOMEM ;
goto err ;
}
mux - > pinctrl = devm_pinctrl_get ( & pdev - > dev ) ;
if ( IS_ERR ( mux - > pinctrl ) ) {
ret = PTR_ERR ( mux - > pinctrl ) ;
dev_err ( & pdev - > dev , " Cannot get pinctrl: %d \n " , ret ) ;
goto err ;
}
for ( i = 0 ; i < mux - > pdata - > bus_count ; i + + ) {
mux - > states [ i ] = pinctrl_lookup_state ( mux - > pinctrl ,
mux - > pdata - > pinctrl_states [ i ] ) ;
if ( IS_ERR ( mux - > states [ i ] ) ) {
ret = PTR_ERR ( mux - > states [ i ] ) ;
dev_err ( & pdev - > dev ,
" Cannot look up pinctrl state %s: %d \n " ,
mux - > pdata - > pinctrl_states [ i ] , ret ) ;
goto err ;
}
}
if ( mux - > pdata - > pinctrl_state_idle ) {
mux - > state_idle = pinctrl_lookup_state ( mux - > pinctrl ,
mux - > pdata - > pinctrl_state_idle ) ;
if ( IS_ERR ( mux - > state_idle ) ) {
ret = PTR_ERR ( mux - > state_idle ) ;
dev_err ( & pdev - > dev ,
" Cannot look up pinctrl state %s: %d \n " ,
mux - > pdata - > pinctrl_state_idle , ret ) ;
goto err ;
}
deselect = i2c_mux_pinctrl_deselect ;
} else {
deselect = NULL ;
}
mux - > parent = i2c_get_adapter ( mux - > pdata - > parent_bus_num ) ;
if ( ! mux - > parent ) {
dev_err ( & pdev - > dev , " Parent adapter (%d) not found \n " ,
mux - > pdata - > parent_bus_num ) ;
ret = - ENODEV ;
goto err ;
}
for ( i = 0 ; i < mux - > pdata - > bus_count ; i + + ) {
u32 bus = mux - > pdata - > base_bus_num ?
( mux - > pdata - > base_bus_num + i ) : 0 ;
mux - > busses [ i ] = i2c_add_mux_adapter ( mux - > parent , & pdev - > dev ,
2012-10-05 22:23:51 +02:00
mux , bus , i , 0 ,
2012-05-18 09:29:34 -06:00
i2c_mux_pinctrl_select ,
deselect ) ;
if ( ! mux - > busses [ i ] ) {
ret = - ENODEV ;
dev_err ( & pdev - > dev , " Failed to add adapter %d \n " , i ) ;
goto err_del_adapter ;
}
}
return 0 ;
err_del_adapter :
for ( ; i > 0 ; i - - )
i2c_del_mux_adapter ( mux - > busses [ i - 1 ] ) ;
i2c_put_adapter ( mux - > parent ) ;
err :
return ret ;
}
2012-11-27 15:59:38 -05:00
static int i2c_mux_pinctrl_remove ( struct platform_device * pdev )
2012-05-18 09:29:34 -06:00
{
struct i2c_mux_pinctrl * mux = platform_get_drvdata ( pdev ) ;
int i ;
for ( i = 0 ; i < mux - > pdata - > bus_count ; i + + )
i2c_del_mux_adapter ( mux - > busses [ i ] ) ;
i2c_put_adapter ( mux - > parent ) ;
return 0 ;
}
# ifdef CONFIG_OF
2012-11-27 15:59:38 -05:00
static const struct of_device_id i2c_mux_pinctrl_of_match [ ] = {
2012-05-18 09:29:34 -06:00
{ . compatible = " i2c-mux-pinctrl " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , i2c_mux_pinctrl_of_match ) ;
# endif
static struct platform_driver i2c_mux_pinctrl_driver = {
. driver = {
. name = " i2c-mux-pinctrl " ,
. owner = THIS_MODULE ,
. of_match_table = of_match_ptr ( i2c_mux_pinctrl_of_match ) ,
} ,
. probe = i2c_mux_pinctrl_probe ,
2012-11-27 15:59:38 -05:00
. remove = i2c_mux_pinctrl_remove ,
2012-05-18 09:29:34 -06:00
} ;
module_platform_driver ( i2c_mux_pinctrl_driver ) ;
MODULE_DESCRIPTION ( " pinctrl-based I2C multiplexer driver " ) ;
MODULE_AUTHOR ( " Stephen Warren <swarren@nvidia.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:i2c-mux-pinctrl " ) ;