2012-10-10 09:41:12 +09:00
/*
* pin - controller / pin - mux / pin - config / gpio - driver for Samsung ' s EXYNOS5440 SoC .
*
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/gpio.h>
# include <linux/device.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/pinctrl/pinconf.h>
# include "core.h"
/* EXYNOS5440 GPIO and Pinctrl register offsets */
# define GPIO_MUX 0x00
# define GPIO_IE 0x04
# define GPIO_INT 0x08
# define GPIO_TYPE 0x0C
# define GPIO_VAL 0x10
# define GPIO_OE 0x14
# define GPIO_IN 0x18
# define GPIO_PE 0x1C
# define GPIO_PS 0x20
# define GPIO_SR 0x24
# define GPIO_DS0 0x28
# define GPIO_DS1 0x2C
# define EXYNOS5440_MAX_PINS 23
# define PIN_NAME_LENGTH 10
# define GROUP_SUFFIX "-grp"
# define GSUFFIX_LEN sizeof(GROUP_SUFFIX)
# define FUNCTION_SUFFIX "-mux"
# define FSUFFIX_LEN sizeof(FUNCTION_SUFFIX)
/*
* pin configuration type and its value are packed together into a 16 - bits .
* The upper 8 - bits represent the configuration type and the lower 8 - bits
* hold the value of the configuration type .
*/
# define PINCFG_TYPE_MASK 0xFF
# define PINCFG_VALUE_SHIFT 8
# define PINCFG_VALUE_MASK (0xFF << PINCFG_VALUE_SHIFT)
# define PINCFG_PACK(type, value) (((value) << PINCFG_VALUE_SHIFT) | type)
# define PINCFG_UNPACK_TYPE(cfg) ((cfg) & PINCFG_TYPE_MASK)
# define PINCFG_UNPACK_VALUE(cfg) (((cfg) & PINCFG_VALUE_MASK) >> \
PINCFG_VALUE_SHIFT )
/**
* enum pincfg_type - possible pin configuration types supported .
* @ PINCFG_TYPE_PUD : Pull up / down configuration .
* @ PINCFG_TYPE_DRV : Drive strength configuration .
* @ PINCFG_TYPE_SKEW_RATE : Skew rate configuration .
* @ PINCFG_TYPE_INPUT_TYPE : Pin input type configuration .
*/
enum pincfg_type {
PINCFG_TYPE_PUD ,
PINCFG_TYPE_DRV ,
PINCFG_TYPE_SKEW_RATE ,
PINCFG_TYPE_INPUT_TYPE
} ;
/**
* struct exynos5440_pin_group : represent group of pins for pincfg setting .
* @ name : name of the pin group , used to lookup the group .
* @ pins : the pins included in this group .
* @ num_pins : number of pins included in this group .
*/
struct exynos5440_pin_group {
const char * name ;
const unsigned int * pins ;
u8 num_pins ;
} ;
/**
* struct exynos5440_pmx_func : represent a pin function .
* @ name : name of the pin function , used to lookup the function .
* @ groups : one or more names of pin groups that provide this function .
* @ num_groups : number of groups included in @ groups .
* @ function : the function number to be programmed when selected .
*/
struct exynos5440_pmx_func {
const char * name ;
const char * * groups ;
u8 num_groups ;
unsigned long function ;
} ;
/**
* struct exynos5440_pinctrl_priv_data : driver ' s private runtime data .
* @ reg_base : ioremapped based address of the register space .
* @ gc : gpio chip registered with gpiolib .
* @ pin_groups : list of pin groups parsed from device tree .
* @ nr_groups : number of pin groups available .
* @ pmx_functions : list of pin functions parsed from device tree .
* @ nr_functions : number of pin functions available .
*/
struct exynos5440_pinctrl_priv_data {
void __iomem * reg_base ;
struct gpio_chip * gc ;
const struct exynos5440_pin_group * pin_groups ;
unsigned int nr_groups ;
const struct exynos5440_pmx_func * pmx_functions ;
unsigned int nr_functions ;
} ;
/* list of all possible config options supported */
2012-12-10 09:54:03 +09:00
static struct pin_config {
2012-10-10 09:41:12 +09:00
char * prop_cfg ;
unsigned int cfg_type ;
} pcfgs [ ] = {
{ " samsung,exynos5440-pin-pud " , PINCFG_TYPE_PUD } ,
{ " samsung,exynos5440-pin-drv " , PINCFG_TYPE_DRV } ,
{ " samsung,exynos5440-pin-skew-rate " , PINCFG_TYPE_SKEW_RATE } ,
{ " samsung,exynos5440-pin-input-type " , PINCFG_TYPE_INPUT_TYPE } ,
} ;
/* check if the selector is a valid pin group selector */
static int exynos5440_get_group_count ( struct pinctrl_dev * pctldev )
{
struct exynos5440_pinctrl_priv_data * priv ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
return priv - > nr_groups ;
}
/* return the name of the group selected by the group selector */
static const char * exynos5440_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
struct exynos5440_pinctrl_priv_data * priv ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
return priv - > pin_groups [ selector ] . name ;
}
/* return the pin numbers associated with the specified group */
static int exynos5440_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned selector , const unsigned * * pins , unsigned * num_pins )
{
struct exynos5440_pinctrl_priv_data * priv ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = priv - > pin_groups [ selector ] . pins ;
* num_pins = priv - > pin_groups [ selector ] . num_pins ;
return 0 ;
}
/* create pinctrl_map entries by parsing device tree nodes */
static int exynos5440_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np , struct pinctrl_map * * maps ,
unsigned * nmaps )
{
struct device * dev = pctldev - > dev ;
struct pinctrl_map * map ;
unsigned long * cfg = NULL ;
char * gname , * fname ;
int cfg_cnt = 0 , map_cnt = 0 , idx = 0 ;
/* count the number of config options specfied in the node */
for ( idx = 0 ; idx < ARRAY_SIZE ( pcfgs ) ; idx + + )
if ( of_find_property ( np , pcfgs [ idx ] . prop_cfg , NULL ) )
cfg_cnt + + ;
/*
* Find out the number of map entries to create . All the config options
* can be accomadated into a single config map entry .
*/
if ( cfg_cnt )
map_cnt = 1 ;
if ( of_find_property ( np , " samsung,exynos5440-pin-function " , NULL ) )
map_cnt + + ;
if ( ! map_cnt ) {
dev_err ( dev , " node %s does not have either config or function "
" configurations \n " , np - > name ) ;
return - EINVAL ;
}
/* Allocate memory for pin-map entries */
map = kzalloc ( sizeof ( * map ) * map_cnt , GFP_KERNEL ) ;
if ( ! map ) {
dev_err ( dev , " could not alloc memory for pin-maps \n " ) ;
return - ENOMEM ;
}
* nmaps = 0 ;
/*
* Allocate memory for pin group name . The pin group name is derived
* from the node name from which these map entries are be created .
*/
gname = kzalloc ( strlen ( np - > name ) + GSUFFIX_LEN , GFP_KERNEL ) ;
if ( ! gname ) {
dev_err ( dev , " failed to alloc memory for group name \n " ) ;
goto free_map ;
}
sprintf ( gname , " %s%s " , np - > name , GROUP_SUFFIX ) ;
/*
* don ' t have config options ? then skip over to creating function
* map entries .
*/
if ( ! cfg_cnt )
goto skip_cfgs ;
/* Allocate memory for config entries */
cfg = kzalloc ( sizeof ( * cfg ) * cfg_cnt , GFP_KERNEL ) ;
if ( ! cfg ) {
dev_err ( dev , " failed to alloc memory for configs \n " ) ;
goto free_gname ;
}
/* Prepare a list of config settings */
for ( idx = 0 , cfg_cnt = 0 ; idx < ARRAY_SIZE ( pcfgs ) ; idx + + ) {
u32 value ;
if ( ! of_property_read_u32 ( np , pcfgs [ idx ] . prop_cfg , & value ) )
cfg [ cfg_cnt + + ] =
PINCFG_PACK ( pcfgs [ idx ] . cfg_type , value ) ;
}
/* create the config map entry */
map [ * nmaps ] . data . configs . group_or_pin = gname ;
map [ * nmaps ] . data . configs . configs = cfg ;
map [ * nmaps ] . data . configs . num_configs = cfg_cnt ;
map [ * nmaps ] . type = PIN_MAP_TYPE_CONFIGS_GROUP ;
* nmaps + = 1 ;
skip_cfgs :
/* create the function map entry */
if ( of_find_property ( np , " samsung,exynos5440-pin-function " , NULL ) ) {
fname = kzalloc ( strlen ( np - > name ) + FSUFFIX_LEN , GFP_KERNEL ) ;
if ( ! fname ) {
dev_err ( dev , " failed to alloc memory for func name \n " ) ;
goto free_cfg ;
}
sprintf ( fname , " %s%s " , np - > name , FUNCTION_SUFFIX ) ;
map [ * nmaps ] . data . mux . group = gname ;
map [ * nmaps ] . data . mux . function = fname ;
map [ * nmaps ] . type = PIN_MAP_TYPE_MUX_GROUP ;
* nmaps + = 1 ;
}
* maps = map ;
return 0 ;
free_cfg :
kfree ( cfg ) ;
free_gname :
kfree ( gname ) ;
free_map :
kfree ( map ) ;
return - ENOMEM ;
}
/* free the memory allocated to hold the pin-map table */
static void exynos5440_dt_free_map ( struct pinctrl_dev * pctldev ,
struct pinctrl_map * map , unsigned num_maps )
{
int idx ;
for ( idx = 0 ; idx < num_maps ; idx + + ) {
if ( map [ idx ] . type = = PIN_MAP_TYPE_MUX_GROUP ) {
kfree ( map [ idx ] . data . mux . function ) ;
if ( ! idx )
kfree ( map [ idx ] . data . mux . group ) ;
} else if ( map - > type = = PIN_MAP_TYPE_CONFIGS_GROUP ) {
kfree ( map [ idx ] . data . configs . configs ) ;
if ( ! idx )
kfree ( map [ idx ] . data . configs . group_or_pin ) ;
}
} ;
kfree ( map ) ;
}
/* list of pinctrl callbacks for the pinctrl core */
static struct pinctrl_ops exynos5440_pctrl_ops = {
. get_groups_count = exynos5440_get_group_count ,
. get_group_name = exynos5440_get_group_name ,
. get_group_pins = exynos5440_get_group_pins ,
. dt_node_to_map = exynos5440_dt_node_to_map ,
. dt_free_map = exynos5440_dt_free_map ,
} ;
/* check if the selector is a valid pin function selector */
static int exynos5440_get_functions_count ( struct pinctrl_dev * pctldev )
{
struct exynos5440_pinctrl_priv_data * priv ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
return priv - > nr_functions ;
}
/* return the name of the pin function specified */
static const char * exynos5440_pinmux_get_fname ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
struct exynos5440_pinctrl_priv_data * priv ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
return priv - > pmx_functions [ selector ] . name ;
}
/* return the groups associated for the specified function selector */
static int exynos5440_pinmux_get_groups ( struct pinctrl_dev * pctldev ,
unsigned selector , const char * const * * groups ,
unsigned * const num_groups )
{
struct exynos5440_pinctrl_priv_data * priv ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
* groups = priv - > pmx_functions [ selector ] . groups ;
* num_groups = priv - > pmx_functions [ selector ] . num_groups ;
return 0 ;
}
/* enable or disable a pinmux function */
static void exynos5440_pinmux_setup ( struct pinctrl_dev * pctldev , unsigned selector ,
unsigned group , bool enable )
{
struct exynos5440_pinctrl_priv_data * priv ;
void __iomem * base ;
u32 function ;
u32 data ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
base = priv - > reg_base ;
function = priv - > pmx_functions [ selector ] . function ;
data = readl ( base + GPIO_MUX ) ;
if ( enable )
data | = ( 1 < < function ) ;
else
data & = ~ ( 1 < < function ) ;
writel ( data , base + GPIO_MUX ) ;
}
/* enable a specified pinmux by writing to registers */
static int exynos5440_pinmux_enable ( struct pinctrl_dev * pctldev , unsigned selector ,
unsigned group )
{
exynos5440_pinmux_setup ( pctldev , selector , group , true ) ;
return 0 ;
}
/* disable a specified pinmux by writing to registers */
static void exynos5440_pinmux_disable ( struct pinctrl_dev * pctldev ,
unsigned selector , unsigned group )
{
exynos5440_pinmux_setup ( pctldev , selector , group , false ) ;
}
/*
* The calls to gpio_direction_output ( ) and gpio_direction_input ( )
* leads to this function call ( via the pinctrl_gpio_direction_ { input | output } ( )
* function called from the gpiolib interface ) .
*/
static int exynos5440_pinmux_gpio_set_direction ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range , unsigned offset , bool input )
{
return 0 ;
}
/* list of pinmux callbacks for the pinmux vertical in pinctrl core */
static struct pinmux_ops exynos5440_pinmux_ops = {
. get_functions_count = exynos5440_get_functions_count ,
. get_function_name = exynos5440_pinmux_get_fname ,
. get_function_groups = exynos5440_pinmux_get_groups ,
. enable = exynos5440_pinmux_enable ,
. disable = exynos5440_pinmux_disable ,
. gpio_set_direction = exynos5440_pinmux_gpio_set_direction ,
} ;
/* set the pin config settings for a specified pin */
static int exynos5440_pinconf_set ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long config )
{
struct exynos5440_pinctrl_priv_data * priv ;
void __iomem * base ;
enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE ( config ) ;
u32 cfg_value = PINCFG_UNPACK_VALUE ( config ) ;
u32 data ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
base = priv - > reg_base ;
switch ( cfg_type ) {
case PINCFG_TYPE_PUD :
/* first set pull enable/disable bit */
data = readl ( base + GPIO_PE ) ;
data & = ~ ( 1 < < pin ) ;
if ( cfg_value )
data | = ( 1 < < pin ) ;
writel ( data , base + GPIO_PE ) ;
/* then set pull up/down bit */
data = readl ( base + GPIO_PS ) ;
data & = ~ ( 1 < < pin ) ;
if ( cfg_value = = 2 )
data | = ( 1 < < pin ) ;
writel ( data , base + GPIO_PS ) ;
break ;
case PINCFG_TYPE_DRV :
/* set the first bit of the drive strength */
data = readl ( base + GPIO_DS0 ) ;
data & = ~ ( 1 < < pin ) ;
data | = ( ( cfg_value & 1 ) < < pin ) ;
writel ( data , base + GPIO_DS0 ) ;
cfg_value > > = 1 ;
/* set the second bit of the driver strength */
data = readl ( base + GPIO_DS1 ) ;
data & = ~ ( 1 < < pin ) ;
data | = ( ( cfg_value & 1 ) < < pin ) ;
writel ( data , base + GPIO_DS1 ) ;
break ;
case PINCFG_TYPE_SKEW_RATE :
data = readl ( base + GPIO_SR ) ;
data & = ~ ( 1 < < pin ) ;
data | = ( ( cfg_value & 1 ) < < pin ) ;
writel ( data , base + GPIO_SR ) ;
break ;
case PINCFG_TYPE_INPUT_TYPE :
data = readl ( base + GPIO_TYPE ) ;
data & = ~ ( 1 < < pin ) ;
data | = ( ( cfg_value & 1 ) < < pin ) ;
writel ( data , base + GPIO_TYPE ) ;
break ;
default :
WARN_ON ( 1 ) ;
return - EINVAL ;
}
return 0 ;
}
/* get the pin config settings for a specified pin */
static int exynos5440_pinconf_get ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long * config )
{
struct exynos5440_pinctrl_priv_data * priv ;
void __iomem * base ;
enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE ( * config ) ;
u32 data ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
base = priv - > reg_base ;
switch ( cfg_type ) {
case PINCFG_TYPE_PUD :
data = readl ( base + GPIO_PE ) ;
data = ( data > > pin ) & 1 ;
if ( ! data )
* config = 0 ;
else
* config = ( ( readl ( base + GPIO_PS ) > > pin ) & 1 ) + 1 ;
break ;
case PINCFG_TYPE_DRV :
data = readl ( base + GPIO_DS0 ) ;
data = ( data > > pin ) & 1 ;
* config = data ;
data = readl ( base + GPIO_DS1 ) ;
data = ( data > > pin ) & 1 ;
* config | = ( data < < 1 ) ;
break ;
case PINCFG_TYPE_SKEW_RATE :
data = readl ( base + GPIO_SR ) ;
* config = ( data > > pin ) & 1 ;
break ;
case PINCFG_TYPE_INPUT_TYPE :
data = readl ( base + GPIO_TYPE ) ;
* config = ( data > > pin ) & 1 ;
break ;
default :
WARN_ON ( 1 ) ;
return - EINVAL ;
}
return 0 ;
}
/* set the pin config settings for a specified pin group */
static int exynos5440_pinconf_group_set ( struct pinctrl_dev * pctldev ,
unsigned group , unsigned long config )
{
struct exynos5440_pinctrl_priv_data * priv ;
const unsigned int * pins ;
unsigned int cnt ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
pins = priv - > pin_groups [ group ] . pins ;
for ( cnt = 0 ; cnt < priv - > pin_groups [ group ] . num_pins ; cnt + + )
exynos5440_pinconf_set ( pctldev , pins [ cnt ] , config ) ;
return 0 ;
}
/* get the pin config settings for a specified pin group */
static int exynos5440_pinconf_group_get ( struct pinctrl_dev * pctldev ,
unsigned int group , unsigned long * config )
{
struct exynos5440_pinctrl_priv_data * priv ;
const unsigned int * pins ;
priv = pinctrl_dev_get_drvdata ( pctldev ) ;
pins = priv - > pin_groups [ group ] . pins ;
exynos5440_pinconf_get ( pctldev , pins [ 0 ] , config ) ;
return 0 ;
}
/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */
static struct pinconf_ops exynos5440_pinconf_ops = {
. pin_config_get = exynos5440_pinconf_get ,
. pin_config_set = exynos5440_pinconf_set ,
. pin_config_group_get = exynos5440_pinconf_group_get ,
. pin_config_group_set = exynos5440_pinconf_group_set ,
} ;
/* gpiolib gpio_set callback function */
static void exynos5440_gpio_set ( struct gpio_chip * gc , unsigned offset , int value )
{
struct exynos5440_pinctrl_priv_data * priv = dev_get_drvdata ( gc - > dev ) ;
void __iomem * base = priv - > reg_base ;
u32 data ;
data = readl ( base + GPIO_VAL ) ;
data & = ~ ( 1 < < offset ) ;
if ( value )
data | = 1 < < offset ;
writel ( data , base + GPIO_VAL ) ;
}
/* gpiolib gpio_get callback function */
static int exynos5440_gpio_get ( struct gpio_chip * gc , unsigned offset )
{
struct exynos5440_pinctrl_priv_data * priv = dev_get_drvdata ( gc - > dev ) ;
void __iomem * base = priv - > reg_base ;
u32 data ;
data = readl ( base + GPIO_IN ) ;
data > > = offset ;
data & = 1 ;
return data ;
}
/* gpiolib gpio_direction_input callback function */
static int exynos5440_gpio_direction_input ( struct gpio_chip * gc , unsigned offset )
{
struct exynos5440_pinctrl_priv_data * priv = dev_get_drvdata ( gc - > dev ) ;
void __iomem * base = priv - > reg_base ;
u32 data ;
/* first disable the data output enable on this pin */
data = readl ( base + GPIO_OE ) ;
data & = ~ ( 1 < < offset ) ;
writel ( data , base + GPIO_OE ) ;
/* now enable input on this pin */
data = readl ( base + GPIO_IE ) ;
data | = 1 < < offset ;
writel ( data , base + GPIO_IE ) ;
return 0 ;
}
/* gpiolib gpio_direction_output callback function */
static int exynos5440_gpio_direction_output ( struct gpio_chip * gc , unsigned offset ,
int value )
{
struct exynos5440_pinctrl_priv_data * priv = dev_get_drvdata ( gc - > dev ) ;
void __iomem * base = priv - > reg_base ;
u32 data ;
exynos5440_gpio_set ( gc , offset , value ) ;
/* first disable the data input enable on this pin */
data = readl ( base + GPIO_IE ) ;
data & = ~ ( 1 < < offset ) ;
writel ( data , base + GPIO_IE ) ;
/* now enable output on this pin */
data = readl ( base + GPIO_OE ) ;
data | = 1 < < offset ;
writel ( data , base + GPIO_OE ) ;
return 0 ;
}
/* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */
2013-01-25 14:14:29 +00:00
static int exynos5440_pinctrl_parse_dt_pins ( struct platform_device * pdev ,
2012-10-10 09:41:12 +09:00
struct device_node * cfg_np , unsigned int * * pin_list ,
unsigned int * npins )
{
struct device * dev = & pdev - > dev ;
struct property * prop ;
prop = of_find_property ( cfg_np , " samsung,exynos5440-pins " , NULL ) ;
if ( ! prop )
return - ENOENT ;
* npins = prop - > length / sizeof ( unsigned long ) ;
if ( ! * npins ) {
dev_err ( dev , " invalid pin list in %s node " , cfg_np - > name ) ;
return - EINVAL ;
}
* pin_list = devm_kzalloc ( dev , * npins * sizeof ( * * pin_list ) , GFP_KERNEL ) ;
if ( ! * pin_list ) {
dev_err ( dev , " failed to allocate memory for pin list \n " ) ;
return - ENOMEM ;
}
return of_property_read_u32_array ( cfg_np , " samsung,exynos5440-pins " ,
* pin_list , * npins ) ;
}
/*
* Parse the information about all the available pin groups and pin functions
* from device node of the pin - controller .
*/
2013-01-25 14:14:29 +00:00
static int exynos5440_pinctrl_parse_dt ( struct platform_device * pdev ,
2012-10-10 09:41:12 +09:00
struct exynos5440_pinctrl_priv_data * priv )
{
struct device * dev = & pdev - > dev ;
struct device_node * dev_np = dev - > of_node ;
struct device_node * cfg_np ;
struct exynos5440_pin_group * groups , * grp ;
struct exynos5440_pmx_func * functions , * func ;
unsigned * pin_list ;
unsigned int npins , grp_cnt , func_idx = 0 ;
char * gname , * fname ;
int ret ;
grp_cnt = of_get_child_count ( dev_np ) ;
if ( ! grp_cnt )
return - EINVAL ;
groups = devm_kzalloc ( dev , grp_cnt * sizeof ( * groups ) , GFP_KERNEL ) ;
if ( ! groups ) {
dev_err ( dev , " failed allocate memory for ping group list \n " ) ;
return - EINVAL ;
}
grp = groups ;
functions = devm_kzalloc ( dev , grp_cnt * sizeof ( * functions ) , GFP_KERNEL ) ;
if ( ! functions ) {
dev_err ( dev , " failed to allocate memory for function list \n " ) ;
return - EINVAL ;
}
func = functions ;
/*
* Iterate over all the child nodes of the pin controller node
* and create pin groups and pin function lists .
*/
for_each_child_of_node ( dev_np , cfg_np ) {
u32 function ;
ret = exynos5440_pinctrl_parse_dt_pins ( pdev , cfg_np ,
& pin_list , & npins ) ;
if ( ret )
return ret ;
/* derive pin group name from the node name */
gname = devm_kzalloc ( dev , strlen ( cfg_np - > name ) + GSUFFIX_LEN ,
GFP_KERNEL ) ;
if ( ! gname ) {
dev_err ( dev , " failed to alloc memory for group name \n " ) ;
return - ENOMEM ;
}
sprintf ( gname , " %s%s " , cfg_np - > name , GROUP_SUFFIX ) ;
grp - > name = gname ;
grp - > pins = pin_list ;
grp - > num_pins = npins ;
grp + + ;
ret = of_property_read_u32 ( cfg_np , " samsung,exynos5440-pin-function " ,
& function ) ;
if ( ret )
continue ;
/* derive function name from the node name */
fname = devm_kzalloc ( dev , strlen ( cfg_np - > name ) + FSUFFIX_LEN ,
GFP_KERNEL ) ;
if ( ! fname ) {
dev_err ( dev , " failed to alloc memory for func name \n " ) ;
return - ENOMEM ;
}
sprintf ( fname , " %s%s " , cfg_np - > name , FUNCTION_SUFFIX ) ;
func - > name = fname ;
func - > groups = devm_kzalloc ( dev , sizeof ( char * ) , GFP_KERNEL ) ;
if ( ! func - > groups ) {
dev_err ( dev , " failed to alloc memory for group list "
" in pin function " ) ;
return - ENOMEM ;
}
func - > groups [ 0 ] = gname ;
func - > num_groups = 1 ;
func - > function = function ;
func + + ;
func_idx + + ;
}
priv - > pin_groups = groups ;
priv - > nr_groups = grp_cnt ;
priv - > pmx_functions = functions ;
priv - > nr_functions = func_idx ;
return 0 ;
}
/* register the pinctrl interface with the pinctrl subsystem */
2013-01-25 14:14:29 +00:00
static int exynos5440_pinctrl_register ( struct platform_device * pdev ,
2012-10-10 09:41:12 +09:00
struct exynos5440_pinctrl_priv_data * priv )
{
struct device * dev = & pdev - > dev ;
struct pinctrl_desc * ctrldesc ;
struct pinctrl_dev * pctl_dev ;
struct pinctrl_pin_desc * pindesc , * pdesc ;
struct pinctrl_gpio_range grange ;
char * pin_names ;
int pin , ret ;
ctrldesc = devm_kzalloc ( dev , sizeof ( * ctrldesc ) , GFP_KERNEL ) ;
if ( ! ctrldesc ) {
dev_err ( dev , " could not allocate memory for pinctrl desc \n " ) ;
return - ENOMEM ;
}
ctrldesc - > name = " exynos5440-pinctrl " ;
ctrldesc - > owner = THIS_MODULE ;
ctrldesc - > pctlops = & exynos5440_pctrl_ops ;
ctrldesc - > pmxops = & exynos5440_pinmux_ops ;
ctrldesc - > confops = & exynos5440_pinconf_ops ;
pindesc = devm_kzalloc ( & pdev - > dev , sizeof ( * pindesc ) *
EXYNOS5440_MAX_PINS , GFP_KERNEL ) ;
if ( ! pindesc ) {
dev_err ( & pdev - > dev , " mem alloc for pin descriptors failed \n " ) ;
return - ENOMEM ;
}
ctrldesc - > pins = pindesc ;
ctrldesc - > npins = EXYNOS5440_MAX_PINS ;
/* dynamically populate the pin number and pin name for pindesc */
for ( pin = 0 , pdesc = pindesc ; pin < ctrldesc - > npins ; pin + + , pdesc + + )
pdesc - > number = pin ;
/*
* allocate space for storing the dynamically generated names for all
* the pins which belong to this pin - controller .
*/
pin_names = devm_kzalloc ( & pdev - > dev , sizeof ( char ) * PIN_NAME_LENGTH *
ctrldesc - > npins , GFP_KERNEL ) ;
if ( ! pin_names ) {
dev_err ( & pdev - > dev , " mem alloc for pin names failed \n " ) ;
return - ENOMEM ;
}
/* for each pin, set the name of the pin */
for ( pin = 0 ; pin < ctrldesc - > npins ; pin + + ) {
sprintf ( pin_names , " gpio%02d " , pin ) ;
pdesc = pindesc + pin ;
pdesc - > name = pin_names ;
pin_names + = PIN_NAME_LENGTH ;
}
ret = exynos5440_pinctrl_parse_dt ( pdev , priv ) ;
if ( ret )
return ret ;
pctl_dev = pinctrl_register ( ctrldesc , & pdev - > dev , priv ) ;
if ( ! pctl_dev ) {
dev_err ( & pdev - > dev , " could not register pinctrl driver \n " ) ;
return - EINVAL ;
}
grange . name = " exynos5440-pctrl-gpio-range " ;
grange . id = 0 ;
grange . base = 0 ;
grange . npins = EXYNOS5440_MAX_PINS ;
grange . gc = priv - > gc ;
pinctrl_add_gpio_range ( pctl_dev , & grange ) ;
return 0 ;
}
/* register the gpiolib interface with the gpiolib subsystem */
2013-01-25 14:14:29 +00:00
static int exynos5440_gpiolib_register ( struct platform_device * pdev ,
2012-10-10 09:41:12 +09:00
struct exynos5440_pinctrl_priv_data * priv )
{
struct gpio_chip * gc ;
int ret ;
gc = devm_kzalloc ( & pdev - > dev , sizeof ( * gc ) , GFP_KERNEL ) ;
if ( ! gc ) {
dev_err ( & pdev - > dev , " mem alloc for gpio_chip failed \n " ) ;
return - ENOMEM ;
}
priv - > gc = gc ;
gc - > base = 0 ;
gc - > ngpio = EXYNOS5440_MAX_PINS ;
gc - > dev = & pdev - > dev ;
gc - > set = exynos5440_gpio_set ;
gc - > get = exynos5440_gpio_get ;
gc - > direction_input = exynos5440_gpio_direction_input ;
gc - > direction_output = exynos5440_gpio_direction_output ;
gc - > label = " gpiolib-exynos5440 " ;
gc - > owner = THIS_MODULE ;
ret = gpiochip_add ( gc ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register gpio_chip %s, error "
" code: %d \n " , gc - > label , ret ) ;
return ret ;
}
return 0 ;
}
/* unregister the gpiolib interface with the gpiolib subsystem */
2013-01-25 14:14:29 +00:00
static int exynos5440_gpiolib_unregister ( struct platform_device * pdev ,
2012-10-10 09:41:12 +09:00
struct exynos5440_pinctrl_priv_data * priv )
{
int ret = gpiochip_remove ( priv - > gc ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " gpio chip remove failed \n " ) ;
return ret ;
}
return 0 ;
}
2012-12-21 13:10:23 -08:00
static int exynos5440_pinctrl_probe ( struct platform_device * pdev )
2012-10-10 09:41:12 +09:00
{
struct device * dev = & pdev - > dev ;
struct exynos5440_pinctrl_priv_data * priv ;
struct resource * res ;
int ret ;
if ( ! dev - > of_node ) {
dev_err ( dev , " device tree node not found \n " ) ;
return - ENODEV ;
}
priv = devm_kzalloc ( dev , sizeof ( priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( dev , " could not allocate memory for private data \n " ) ;
return - ENOMEM ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( dev , " cannot find IO resource \n " ) ;
return - ENOENT ;
}
priv - > reg_base = devm_request_and_ioremap ( & pdev - > dev , res ) ;
if ( ! priv - > reg_base ) {
dev_err ( dev , " ioremap failed \n " ) ;
return - ENODEV ;
}
ret = exynos5440_gpiolib_register ( pdev , priv ) ;
if ( ret )
return ret ;
ret = exynos5440_pinctrl_register ( pdev , priv ) ;
if ( ret ) {
exynos5440_gpiolib_unregister ( pdev , priv ) ;
return ret ;
}
platform_set_drvdata ( pdev , priv ) ;
dev_info ( dev , " EXYNOS5440 pinctrl driver registered \n " ) ;
return 0 ;
}
static const struct of_device_id exynos5440_pinctrl_dt_match [ ] = {
{ . compatible = " samsung,exynos5440-pinctrl " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , exynos5440_pinctrl_dt_match ) ;
static struct platform_driver exynos5440_pinctrl_driver = {
. probe = exynos5440_pinctrl_probe ,
. driver = {
. name = " exynos5440-pinctrl " ,
. owner = THIS_MODULE ,
. of_match_table = of_match_ptr ( exynos5440_pinctrl_dt_match ) ,
} ,
} ;
static int __init exynos5440_pinctrl_drv_register ( void )
{
return platform_driver_register ( & exynos5440_pinctrl_driver ) ;
}
postcore_initcall ( exynos5440_pinctrl_drv_register ) ;
static void __exit exynos5440_pinctrl_drv_unregister ( void )
{
platform_driver_unregister ( & exynos5440_pinctrl_driver ) ;
}
module_exit ( exynos5440_pinctrl_drv_unregister ) ;
MODULE_AUTHOR ( " Thomas Abraham <thomas.ab@samsung.com> " ) ;
MODULE_DESCRIPTION ( " Samsung EXYNOS5440 SoC pinctrl driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;