2012-10-10 04:41:12 +04: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>
2013-03-26 19:12:33 +04:00
# include <linux/interrupt.h>
# include <linux/irqdomain.h>
# include <linux/of_irq.h>
2012-10-10 04:41:12 +04:00
# 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
2013-03-26 19:12:33 +04:00
# define EXYNOS5440_MAX_GPIO_INT 8
2012-10-10 04:41:12 +04:00
# 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 ;
2013-03-26 19:12:33 +04:00
struct irq_domain * irq_domain ;
2012-10-10 04:41:12 +04:00
const struct exynos5440_pin_group * pin_groups ;
unsigned int nr_groups ;
const struct exynos5440_pmx_func * pmx_functions ;
unsigned int nr_functions ;
} ;
2013-03-26 19:12:33 +04:00
/**
* struct exynos5440_gpio_intr_data : private data for gpio interrupts .
* @ priv : driver ' s private runtime data .
* @ gpio_int : gpio interrupt number .
*/
struct exynos5440_gpio_intr_data {
struct exynos5440_pinctrl_priv_data * priv ;
unsigned int gpio_int ;
} ;
2012-10-10 04:41:12 +04:00
/* list of all possible config options supported */
2012-12-10 04:54:03 +04:00
static struct pin_config {
2012-10-10 04:41:12 +04: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 */
2013-02-16 13:25:07 +04:00
static const struct pinctrl_ops exynos5440_pctrl_ops = {
2012-10-10 04:41:12 +04:00
. 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 */
2013-02-16 13:25:07 +04:00
static const struct pinmux_ops exynos5440_pinmux_ops = {
2012-10-10 04:41:12 +04:00
. 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 */
2013-02-16 13:25:07 +04:00
static const struct pinconf_ops exynos5440_pinconf_ops = {
2012-10-10 04:41:12 +04:00
. 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 ;
}
2013-03-26 19:12:33 +04:00
/* gpiolib gpio_to_irq callback function */
static int exynos5440_gpio_to_irq ( struct gpio_chip * gc , unsigned offset )
{
struct exynos5440_pinctrl_priv_data * priv = dev_get_drvdata ( gc - > dev ) ;
unsigned int virq ;
if ( offset < 16 | | offset > 23 )
return - ENXIO ;
if ( ! priv - > irq_domain )
return - ENXIO ;
virq = irq_create_mapping ( priv - > irq_domain , offset - 16 ) ;
return virq ? : - ENXIO ;
}
2012-10-10 04:41:12 +04:00
/* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */
2013-01-25 18:14:29 +04:00
static int exynos5440_pinctrl_parse_dt_pins ( struct platform_device * pdev ,
2012-10-10 04:41:12 +04: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 18:14:29 +04:00
static int exynos5440_pinctrl_parse_dt ( struct platform_device * pdev ,
2012-10-10 04:41:12 +04: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 ) ;
2013-03-26 19:12:32 +04:00
if ( ret ) {
gname = NULL ;
goto skip_to_pin_function ;
}
2012-10-10 04:41:12 +04:00
/* 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 + + ;
2013-03-26 19:12:32 +04:00
skip_to_pin_function :
2012-10-10 04:41:12 +04:00
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 ;
2013-03-26 19:12:32 +04:00
func - > num_groups = gname ? 1 : 0 ;
2012-10-10 04:41:12 +04:00
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 18:14:29 +04:00
static int exynos5440_pinctrl_register ( struct platform_device * pdev ,
2012-10-10 04:41:12 +04: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 18:14:29 +04:00
static int exynos5440_gpiolib_register ( struct platform_device * pdev ,
2012-10-10 04:41:12 +04: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 ;
2013-03-26 19:12:33 +04:00
gc - > to_irq = exynos5440_gpio_to_irq ;
2012-10-10 04:41:12 +04:00
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 18:14:29 +04:00
static int exynos5440_gpiolib_unregister ( struct platform_device * pdev ,
2012-10-10 04:41:12 +04: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 ;
}
2013-03-26 19:12:33 +04:00
static void exynos5440_gpio_irq_unmask ( struct irq_data * irqd )
{
struct exynos5440_pinctrl_priv_data * d ;
unsigned long gpio_int ;
d = irq_data_get_irq_chip_data ( irqd ) ;
gpio_int = readl ( d - > reg_base + GPIO_INT ) ;
gpio_int | = 1 < < irqd - > hwirq ;
writel ( gpio_int , d - > reg_base + GPIO_INT ) ;
}
static void exynos5440_gpio_irq_mask ( struct irq_data * irqd )
{
struct exynos5440_pinctrl_priv_data * d ;
unsigned long gpio_int ;
d = irq_data_get_irq_chip_data ( irqd ) ;
gpio_int = readl ( d - > reg_base + GPIO_INT ) ;
gpio_int & = ~ ( 1 < < irqd - > hwirq ) ;
writel ( gpio_int , d - > reg_base + GPIO_INT ) ;
}
/* irq_chip for gpio interrupts */
static struct irq_chip exynos5440_gpio_irq_chip = {
. name = " exynos5440_gpio_irq_chip " ,
. irq_unmask = exynos5440_gpio_irq_unmask ,
. irq_mask = exynos5440_gpio_irq_mask ,
} ;
/* interrupt handler for GPIO interrupts 0..7 */
static irqreturn_t exynos5440_gpio_irq ( int irq , void * data )
{
struct exynos5440_gpio_intr_data * intd = data ;
struct exynos5440_pinctrl_priv_data * d = intd - > priv ;
int virq ;
virq = irq_linear_revmap ( d - > irq_domain , intd - > gpio_int ) ;
if ( ! virq )
return IRQ_NONE ;
generic_handle_irq ( virq ) ;
return IRQ_HANDLED ;
}
static int exynos5440_gpio_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
{
struct exynos5440_pinctrl_priv_data * d = h - > host_data ;
irq_set_chip_data ( virq , d ) ;
irq_set_chip_and_handler ( virq , & exynos5440_gpio_irq_chip ,
handle_level_irq ) ;
set_irq_flags ( virq , IRQF_VALID ) ;
return 0 ;
}
/* irq domain callbacks for gpio interrupt controller */
static const struct irq_domain_ops exynos5440_gpio_irqd_ops = {
. map = exynos5440_gpio_irq_map ,
. xlate = irq_domain_xlate_twocell ,
} ;
/* setup handling of gpio interrupts */
static int exynos5440_gpio_irq_init ( struct platform_device * pdev ,
struct exynos5440_pinctrl_priv_data * priv )
{
struct device * dev = & pdev - > dev ;
struct exynos5440_gpio_intr_data * intd ;
int i , irq , ret ;
intd = devm_kzalloc ( dev , sizeof ( * intd ) * EXYNOS5440_MAX_GPIO_INT ,
GFP_KERNEL ) ;
if ( ! intd ) {
dev_err ( dev , " failed to allocate memory for gpio intr data \n " ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < EXYNOS5440_MAX_GPIO_INT ; i + + ) {
irq = irq_of_parse_and_map ( dev - > of_node , i ) ;
if ( irq < = 0 ) {
dev_err ( dev , " irq parsing failed \n " ) ;
return - EINVAL ;
}
intd - > gpio_int = i ;
intd - > priv = priv ;
ret = devm_request_irq ( dev , irq , exynos5440_gpio_irq ,
0 , dev_name ( dev ) , intd + + ) ;
if ( ret ) {
dev_err ( dev , " irq request failed \n " ) ;
return - ENXIO ;
}
}
priv - > irq_domain = irq_domain_add_linear ( dev - > of_node ,
EXYNOS5440_MAX_GPIO_INT ,
& exynos5440_gpio_irqd_ops , priv ) ;
if ( ! priv - > irq_domain ) {
dev_err ( dev , " failed to create irq domain \n " ) ;
return - ENXIO ;
}
return 0 ;
}
2012-12-22 01:10:23 +04:00
static int exynos5440_pinctrl_probe ( struct platform_device * pdev )
2012-10-10 04:41:12 +04: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 ;
}
2013-03-14 17:59:38 +04:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2012-10-10 04:41:12 +04:00
if ( ! priv ) {
dev_err ( dev , " could not allocate memory for private data \n " ) ;
return - ENOMEM ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 14:09:14 +04:00
priv - > reg_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( priv - > reg_base ) )
return PTR_ERR ( priv - > reg_base ) ;
2012-10-10 04:41:12 +04:00
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 ;
2013-03-26 19:12:33 +04:00
}
ret = exynos5440_gpio_irq_init ( pdev , priv ) ;
if ( ret ) {
dev_err ( dev , " failed to setup gpio interrupts \n " ) ;
return ret ;
2012-10-10 04:41:12 +04:00
}
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 " ) ;