2013-02-20 09:32:19 +13:00
/*
* Pinctrl driver for the Wondermedia SoC ' s
*
* Copyright ( c ) 2013 Tony Prisk < linux @ prisktech . co . nz >
*
* 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 .
*/
# include <linux/err.h>
# include <linux/gpio.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/pinctrl/consumer.h>
# include <linux/pinctrl/machine.h>
# include <linux/pinctrl/pinconf.h>
# include <linux/pinctrl/pinconf-generic.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include "pinctrl-wmt.h"
static inline void wmt_setbits ( struct wmt_pinctrl_data * data , u32 reg ,
u32 mask )
{
u32 val ;
val = readl_relaxed ( data - > base + reg ) ;
val | = mask ;
writel_relaxed ( val , data - > base + reg ) ;
}
static inline void wmt_clearbits ( struct wmt_pinctrl_data * data , u32 reg ,
u32 mask )
{
u32 val ;
val = readl_relaxed ( data - > base + reg ) ;
val & = ~ mask ;
writel_relaxed ( val , data - > base + reg ) ;
}
enum wmt_func_sel {
WMT_FSEL_GPIO_IN = 0 ,
WMT_FSEL_GPIO_OUT = 1 ,
WMT_FSEL_ALT = 2 ,
WMT_FSEL_COUNT = 3 ,
} ;
static const char * const wmt_functions [ WMT_FSEL_COUNT ] = {
[ WMT_FSEL_GPIO_IN ] = " gpio_in " ,
[ WMT_FSEL_GPIO_OUT ] = " gpio_out " ,
[ WMT_FSEL_ALT ] = " alt " ,
} ;
static int wmt_pmx_get_functions_count ( struct pinctrl_dev * pctldev )
{
return WMT_FSEL_COUNT ;
}
static const char * wmt_pmx_get_function_name ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
return wmt_functions [ selector ] ;
}
static int wmt_pmx_get_function_groups ( struct pinctrl_dev * pctldev ,
unsigned selector ,
const char * const * * groups ,
unsigned * const num_groups )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
/* every pin does every function */
* groups = data - > groups ;
* num_groups = data - > ngroups ;
return 0 ;
}
static int wmt_set_pinmux ( struct wmt_pinctrl_data * data , unsigned func ,
unsigned pin )
{
u32 bank = WMT_BANK_FROM_PIN ( pin ) ;
u32 bit = WMT_BIT_FROM_PIN ( pin ) ;
u32 reg_en = data - > banks [ bank ] . reg_en ;
u32 reg_dir = data - > banks [ bank ] . reg_dir ;
if ( reg_dir = = NO_REG ) {
dev_err ( data - > dev , " pin:%d no direction register defined \n " ,
pin ) ;
return - EINVAL ;
}
/*
* If reg_en = = NO_REG , we assume it is a dedicated GPIO and cannot be
* disabled ( as on VT8500 ) and that no alternate function is available .
*/
switch ( func ) {
case WMT_FSEL_GPIO_IN :
if ( reg_en ! = NO_REG )
wmt_setbits ( data , reg_en , BIT ( bit ) ) ;
wmt_clearbits ( data , reg_dir , BIT ( bit ) ) ;
break ;
case WMT_FSEL_GPIO_OUT :
if ( reg_en ! = NO_REG )
wmt_setbits ( data , reg_en , BIT ( bit ) ) ;
wmt_setbits ( data , reg_dir , BIT ( bit ) ) ;
break ;
case WMT_FSEL_ALT :
if ( reg_en = = NO_REG ) {
dev_err ( data - > dev , " pin:%d no alt function available \n " ,
pin ) ;
return - EINVAL ;
}
wmt_clearbits ( data , reg_en , BIT ( bit ) ) ;
}
return 0 ;
}
static int wmt_pmx_enable ( struct pinctrl_dev * pctldev ,
unsigned func_selector ,
unsigned group_selector )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
u32 pinnum = data - > pins [ group_selector ] . number ;
return wmt_set_pinmux ( data , func_selector , pinnum ) ;
}
static void wmt_pmx_disable ( struct pinctrl_dev * pctldev ,
unsigned func_selector ,
unsigned group_selector )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
u32 pinnum = data - > pins [ group_selector ] . number ;
/* disable by setting GPIO_IN */
wmt_set_pinmux ( data , WMT_FSEL_GPIO_IN , pinnum ) ;
}
static void wmt_pmx_gpio_disable_free ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range ,
unsigned offset )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
/* disable by setting GPIO_IN */
wmt_set_pinmux ( data , WMT_FSEL_GPIO_IN , offset ) ;
}
static int wmt_pmx_gpio_set_direction ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range ,
unsigned offset ,
bool input )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
wmt_set_pinmux ( data , ( input ? WMT_FSEL_GPIO_IN : WMT_FSEL_GPIO_OUT ) ,
offset ) ;
return 0 ;
}
static struct pinmux_ops wmt_pinmux_ops = {
. get_functions_count = wmt_pmx_get_functions_count ,
. get_function_name = wmt_pmx_get_function_name ,
. get_function_groups = wmt_pmx_get_function_groups ,
. enable = wmt_pmx_enable ,
. disable = wmt_pmx_disable ,
. gpio_disable_free = wmt_pmx_gpio_disable_free ,
. gpio_set_direction = wmt_pmx_gpio_set_direction ,
} ;
static int wmt_get_groups_count ( struct pinctrl_dev * pctldev )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
return data - > ngroups ;
}
static const char * wmt_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
return data - > groups [ selector ] ;
}
static int wmt_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned selector ,
const unsigned * * pins ,
unsigned * num_pins )
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = & data - > pins [ selector ] . number ;
* num_pins = 1 ;
return 0 ;
}
static int wmt_pctl_find_group_by_pin ( struct wmt_pinctrl_data * data , u32 pin )
{
int i ;
for ( i = 0 ; i < data - > npins ; i + + ) {
if ( data - > pins [ i ] . number = = pin )
return i ;
}
return - EINVAL ;
}
static int wmt_pctl_dt_node_to_map_func ( struct wmt_pinctrl_data * data ,
struct device_node * np ,
u32 pin , u32 fnum ,
struct pinctrl_map * * maps )
{
int group ;
struct pinctrl_map * map = * maps ;
if ( fnum > = ARRAY_SIZE ( wmt_functions ) ) {
dev_err ( data - > dev , " invalid wm,function %d \n " , fnum ) ;
return - EINVAL ;
}
group = wmt_pctl_find_group_by_pin ( data , pin ) ;
if ( group < 0 ) {
dev_err ( data - > dev , " unable to match pin %d to group \n " , pin ) ;
return group ;
}
map - > type = PIN_MAP_TYPE_MUX_GROUP ;
map - > data . mux . group = data - > groups [ group ] ;
map - > data . mux . function = wmt_functions [ fnum ] ;
( * maps ) + + ;
return 0 ;
}
static int wmt_pctl_dt_node_to_map_pull ( struct wmt_pinctrl_data * data ,
struct device_node * np ,
u32 pin , u32 pull ,
struct pinctrl_map * * maps )
{
int group ;
unsigned long * configs ;
struct pinctrl_map * map = * maps ;
if ( pull > 2 ) {
dev_err ( data - > dev , " invalid wm,pull %d \n " , pull ) ;
return - EINVAL ;
}
group = wmt_pctl_find_group_by_pin ( data , pin ) ;
if ( group < 0 ) {
dev_err ( data - > dev , " unable to match pin %d to group \n " , pin ) ;
return group ;
}
configs = kzalloc ( sizeof ( * configs ) , GFP_KERNEL ) ;
if ( ! configs )
return - ENOMEM ;
configs [ 0 ] = pull ;
map - > type = PIN_MAP_TYPE_CONFIGS_PIN ;
map - > data . configs . group_or_pin = data - > groups [ group ] ;
map - > data . configs . configs = configs ;
map - > data . configs . num_configs = 1 ;
( * maps ) + + ;
return 0 ;
}
static void wmt_pctl_dt_free_map ( struct pinctrl_dev * pctldev ,
struct pinctrl_map * maps ,
unsigned num_maps )
{
int i ;
for ( i = 0 ; i < num_maps ; i + + )
if ( maps [ i ] . type = = PIN_MAP_TYPE_CONFIGS_PIN )
kfree ( maps [ i ] . data . configs . configs ) ;
kfree ( maps ) ;
}
static int wmt_pctl_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np ,
struct pinctrl_map * * map ,
unsigned * num_maps )
{
struct pinctrl_map * maps , * cur_map ;
struct property * pins , * funcs , * pulls ;
u32 pin , func , pull ;
int num_pins , num_funcs , num_pulls , maps_per_pin ;
int i , err ;
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
pins = of_find_property ( np , " wm,pins " , NULL ) ;
if ( ! pins ) {
dev_err ( data - > dev , " missing wmt,pins property \n " ) ;
return - EINVAL ;
}
funcs = of_find_property ( np , " wm,function " , NULL ) ;
pulls = of_find_property ( np , " wm,pull " , NULL ) ;
if ( ! funcs & & ! pulls ) {
dev_err ( data - > dev , " neither wm,function nor wm,pull specified \n " ) ;
return - EINVAL ;
}
/*
* The following lines calculate how many values are defined for each
* of the properties .
*/
num_pins = pins - > length / sizeof ( u32 ) ;
num_funcs = funcs ? ( funcs - > length / sizeof ( u32 ) ) : 0 ;
num_pulls = pulls ? ( pulls - > length / sizeof ( u32 ) ) : 0 ;
if ( num_funcs > 1 & & num_funcs ! = num_pins ) {
dev_err ( data - > dev , " wm,function must have 1 or %d entries \n " ,
num_pins ) ;
return - EINVAL ;
}
if ( num_pulls > 1 & & num_pulls ! = num_pins ) {
dev_err ( data - > dev , " wm,pull must have 1 or %d entries \n " ,
num_pins ) ;
return - EINVAL ;
}
maps_per_pin = 0 ;
if ( num_funcs )
maps_per_pin + + ;
if ( num_pulls )
maps_per_pin + + ;
cur_map = maps = kzalloc ( num_pins * maps_per_pin * sizeof ( * maps ) ,
GFP_KERNEL ) ;
if ( ! maps )
return - ENOMEM ;
for ( i = 0 ; i < num_pins ; i + + ) {
err = of_property_read_u32_index ( np , " wm,pins " , i , & pin ) ;
if ( err )
goto fail ;
if ( pin > = ( data - > nbanks * 32 ) ) {
dev_err ( data - > dev , " invalid wm,pins value \n " ) ;
err = - EINVAL ;
goto fail ;
}
if ( num_funcs ) {
err = of_property_read_u32_index ( np , " wm,function " ,
( num_funcs > 1 ? i : 0 ) , & func ) ;
if ( err )
goto fail ;
err = wmt_pctl_dt_node_to_map_func ( data , np , pin , func ,
& cur_map ) ;
if ( err )
goto fail ;
}
if ( num_pulls ) {
err = of_property_read_u32_index ( np , " wm,pull " ,
( num_pulls > 1 ? i : 0 ) , & pull ) ;
if ( err )
goto fail ;
err = wmt_pctl_dt_node_to_map_pull ( data , np , pin , pull ,
& cur_map ) ;
if ( err )
goto fail ;
}
}
* map = maps ;
* num_maps = num_pins * maps_per_pin ;
return 0 ;
/*
* The fail path removes any maps that have been allocated . The fail path is
* only called from code after maps has been kzalloc ' d . It is also safe to
* pass ' num_pins * maps_per_pin ' as the map count even though we probably
* failed before all the mappings were read as all maps are allocated at once ,
* and configs are only allocated for . type = PIN_MAP_TYPE_CONFIGS_PIN - there
* is no failpath where a config can be allocated without . type being set .
*/
fail :
wmt_pctl_dt_free_map ( pctldev , maps , num_pins * maps_per_pin ) ;
return err ;
}
static struct pinctrl_ops wmt_pctl_ops = {
. get_groups_count = wmt_get_groups_count ,
. get_group_name = wmt_get_group_name ,
. get_group_pins = wmt_get_group_pins ,
. dt_node_to_map = wmt_pctl_dt_node_to_map ,
. dt_free_map = wmt_pctl_dt_free_map ,
} ;
static int wmt_pinconf_get ( struct pinctrl_dev * pctldev , unsigned pin ,
unsigned long * config )
{
return - ENOTSUPP ;
}
static int wmt_pinconf_set ( struct pinctrl_dev * pctldev , unsigned pin ,
2013-08-27 11:32:12 -07:00
unsigned long * configs , unsigned num_configs )
2013-02-20 09:32:19 +13:00
{
struct wmt_pinctrl_data * data = pinctrl_dev_get_drvdata ( pctldev ) ;
2013-08-27 11:32:12 -07:00
enum pin_config_param param ;
u16 arg ;
2013-02-20 09:32:19 +13:00
u32 bank = WMT_BANK_FROM_PIN ( pin ) ;
u32 bit = WMT_BIT_FROM_PIN ( pin ) ;
u32 reg_pull_en = data - > banks [ bank ] . reg_pull_en ;
u32 reg_pull_cfg = data - > banks [ bank ] . reg_pull_cfg ;
2013-08-27 11:32:12 -07:00
int i ;
2013-02-20 09:32:19 +13:00
if ( ( reg_pull_en = = NO_REG ) | | ( reg_pull_cfg = = NO_REG ) ) {
dev_err ( data - > dev , " bias functions not supported on pin %d \n " ,
pin ) ;
return - EINVAL ;
}
2013-08-27 11:32:12 -07:00
for ( i = 0 ; i < num_configs ; i + + ) {
param = pinconf_to_config_param ( configs [ i ] ) ;
arg = pinconf_to_config_argument ( configs [ i ] ) ;
2013-02-20 09:32:19 +13:00
2013-08-27 11:32:12 -07:00
if ( ( param = = PIN_CONFIG_BIAS_PULL_DOWN ) | |
( param = = PIN_CONFIG_BIAS_PULL_UP ) ) {
if ( arg = = 0 )
param = PIN_CONFIG_BIAS_DISABLE ;
}
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
wmt_clearbits ( data , reg_pull_en , BIT ( bit ) ) ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
wmt_clearbits ( data , reg_pull_cfg , BIT ( bit ) ) ;
wmt_setbits ( data , reg_pull_en , BIT ( bit ) ) ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
wmt_setbits ( data , reg_pull_cfg , BIT ( bit ) ) ;
wmt_setbits ( data , reg_pull_en , BIT ( bit ) ) ;
break ;
default :
dev_err ( data - > dev , " unknown pinconf param \n " ) ;
return - EINVAL ;
}
} /* for each config */
2013-02-20 09:32:19 +13:00
return 0 ;
}
static struct pinconf_ops wmt_pinconf_ops = {
. pin_config_get = wmt_pinconf_get ,
. pin_config_set = wmt_pinconf_set ,
} ;
static struct pinctrl_desc wmt_desc = {
. owner = THIS_MODULE ,
. name = " pinctrl-wmt " ,
. pctlops = & wmt_pctl_ops ,
. pmxops = & wmt_pinmux_ops ,
. confops = & wmt_pinconf_ops ,
} ;
static int wmt_gpio_request ( struct gpio_chip * chip , unsigned offset )
{
return pinctrl_request_gpio ( chip - > base + offset ) ;
}
static void wmt_gpio_free ( struct gpio_chip * chip , unsigned offset )
{
pinctrl_free_gpio ( chip - > base + offset ) ;
}
static int wmt_gpio_get_direction ( struct gpio_chip * chip , unsigned offset )
{
struct wmt_pinctrl_data * data = dev_get_drvdata ( chip - > dev ) ;
u32 bank = WMT_BANK_FROM_PIN ( offset ) ;
u32 bit = WMT_BIT_FROM_PIN ( offset ) ;
u32 reg_dir = data - > banks [ bank ] . reg_dir ;
u32 val ;
val = readl_relaxed ( data - > base + reg_dir ) ;
if ( val & BIT ( bit ) )
return GPIOF_DIR_OUT ;
else
return GPIOF_DIR_IN ;
}
static int wmt_gpio_direction_input ( struct gpio_chip * chip , unsigned offset )
{
return pinctrl_gpio_direction_input ( chip - > base + offset ) ;
}
static int wmt_gpio_direction_output ( struct gpio_chip * chip , unsigned offset ,
int value )
{
return pinctrl_gpio_direction_output ( chip - > base + offset ) ;
}
static int wmt_gpio_get_value ( struct gpio_chip * chip , unsigned offset )
{
struct wmt_pinctrl_data * data = dev_get_drvdata ( chip - > dev ) ;
u32 bank = WMT_BANK_FROM_PIN ( offset ) ;
u32 bit = WMT_BIT_FROM_PIN ( offset ) ;
u32 reg_data_in = data - > banks [ bank ] . reg_data_in ;
if ( reg_data_in = = NO_REG ) {
dev_err ( data - > dev , " no data in register defined \n " ) ;
return - EINVAL ;
}
return ! ! ( readl_relaxed ( data - > base + reg_data_in ) & BIT ( bit ) ) ;
}
static void wmt_gpio_set_value ( struct gpio_chip * chip , unsigned offset ,
int val )
{
struct wmt_pinctrl_data * data = dev_get_drvdata ( chip - > dev ) ;
u32 bank = WMT_BANK_FROM_PIN ( offset ) ;
u32 bit = WMT_BIT_FROM_PIN ( offset ) ;
u32 reg_data_out = data - > banks [ bank ] . reg_data_out ;
if ( reg_data_out = = NO_REG ) {
dev_err ( data - > dev , " no data out register defined \n " ) ;
return ;
}
if ( val )
wmt_setbits ( data , reg_data_out , BIT ( bit ) ) ;
else
wmt_clearbits ( data , reg_data_out , BIT ( bit ) ) ;
}
static struct gpio_chip wmt_gpio_chip = {
. label = " gpio-wmt " ,
. owner = THIS_MODULE ,
. request = wmt_gpio_request ,
. free = wmt_gpio_free ,
. get_direction = wmt_gpio_get_direction ,
. direction_input = wmt_gpio_direction_input ,
. direction_output = wmt_gpio_direction_output ,
. get = wmt_gpio_get_value ,
. set = wmt_gpio_set_value ,
. can_sleep = 0 ,
} ;
int wmt_pinctrl_probe ( struct platform_device * pdev ,
struct wmt_pinctrl_data * data )
{
int err ;
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-05-12 16:59:51 +02:00
data - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
2013-06-26 09:56:30 +08:00
if ( IS_ERR ( data - > base ) )
2013-05-12 16:59:51 +02:00
return PTR_ERR ( data - > base ) ;
2013-02-20 09:32:19 +13:00
wmt_desc . pins = data - > pins ;
wmt_desc . npins = data - > npins ;
data - > gpio_chip = wmt_gpio_chip ;
data - > gpio_chip . dev = & pdev - > dev ;
data - > gpio_chip . of_node = pdev - > dev . of_node ;
data - > gpio_chip . ngpio = data - > nbanks * 32 ;
platform_set_drvdata ( pdev , data ) ;
data - > dev = & pdev - > dev ;
data - > pctl_dev = pinctrl_register ( & wmt_desc , & pdev - > dev , data ) ;
2013-04-09 23:59:58 +08:00
if ( ! data - > pctl_dev ) {
2013-02-20 09:32:19 +13:00
dev_err ( & pdev - > dev , " Failed to register pinctrl \n " ) ;
return - EINVAL ;
}
err = gpiochip_add ( & data - > gpio_chip ) ;
if ( err ) {
dev_err ( & pdev - > dev , " could not add GPIO chip \n " ) ;
goto fail_gpio ;
}
err = gpiochip_add_pin_range ( & data - > gpio_chip , dev_name ( data - > dev ) ,
0 , 0 , data - > nbanks * 32 ) ;
if ( err )
goto fail_range ;
dev_info ( & pdev - > dev , " Pin controller initialized \n " ) ;
return 0 ;
fail_range :
2013-05-19 13:58:37 +08:00
if ( gpiochip_remove ( & data - > gpio_chip ) )
2013-02-20 09:32:19 +13:00
dev_err ( & pdev - > dev , " failed to remove gpio chip \n " ) ;
fail_gpio :
pinctrl_unregister ( data - > pctl_dev ) ;
return err ;
}
int wmt_pinctrl_remove ( struct platform_device * pdev )
{
struct wmt_pinctrl_data * data = platform_get_drvdata ( pdev ) ;
int err ;
err = gpiochip_remove ( & data - > gpio_chip ) ;
if ( err )
dev_err ( & pdev - > dev , " failed to remove gpio chip \n " ) ;
pinctrl_unregister ( data - > pctl_dev ) ;
return 0 ;
}