2011-11-24 21:27:15 +04:00
/*
* Core driver for the generic pin config portions of the pin control subsystem
*
* Copyright ( C ) 2011 ST - Ericsson SA
* Written on behalf of Linaro for ST - Ericsson
*
* Author : Linus Walleij < linus . walleij @ linaro . org >
*
* License terms : GNU General Public License ( GPL ) version 2
*/
# define pr_fmt(fmt) "generic pinconfig core: " fmt
# include <linux/kernel.h>
2013-02-17 15:42:53 +04:00
# include <linux/module.h>
2011-11-24 21:27:15 +04:00
# include <linux/init.h>
# include <linux/device.h>
# include <linux/slab.h>
# include <linux/debugfs.h>
# include <linux/seq_file.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinconf.h>
# include <linux/pinctrl/pinconf-generic.h>
2013-06-10 23:40:29 +04:00
# include <linux/of.h>
2011-11-24 21:27:15 +04:00
# include "core.h"
# include "pinconf.h"
2013-08-06 17:12:34 +04:00
# include "pinctrl-utils.h"
2011-11-24 21:27:15 +04:00
# ifdef CONFIG_DEBUG_FS
struct pin_config_item {
const enum pin_config_param param ;
const char * const display ;
const char * const format ;
} ;
# define PCONFDUMP(a, b, c) { .param = a, .display = b, .format = c }
2013-03-15 08:53:15 +04:00
static struct pin_config_item conf_items [ ] = {
2011-11-24 21:27:15 +04:00
PCONFDUMP ( PIN_CONFIG_BIAS_DISABLE , " input bias disabled " , NULL ) ,
PCONFDUMP ( PIN_CONFIG_BIAS_HIGH_IMPEDANCE , " input bias high impedance " , NULL ) ,
2013-05-24 20:21:12 +04:00
PCONFDUMP ( PIN_CONFIG_BIAS_BUS_HOLD , " input bias bus hold " , NULL ) ,
2011-11-24 21:27:15 +04:00
PCONFDUMP ( PIN_CONFIG_BIAS_PULL_UP , " input bias pull up " , NULL ) ,
PCONFDUMP ( PIN_CONFIG_BIAS_PULL_DOWN , " input bias pull down " , NULL ) ,
2013-06-06 18:44:25 +04:00
PCONFDUMP ( PIN_CONFIG_BIAS_PULL_PIN_DEFAULT ,
" input bias pull to pin specific state " , NULL ) ,
2011-11-24 21:27:15 +04:00
PCONFDUMP ( PIN_CONFIG_DRIVE_PUSH_PULL , " output drive push pull " , NULL ) ,
PCONFDUMP ( PIN_CONFIG_DRIVE_OPEN_DRAIN , " output drive open drain " , NULL ) ,
PCONFDUMP ( PIN_CONFIG_DRIVE_OPEN_SOURCE , " output drive open source " , NULL ) ,
2013-05-24 20:21:11 +04:00
PCONFDUMP ( PIN_CONFIG_DRIVE_STRENGTH , " output drive strength " , " mA " ) ,
2013-12-11 22:37:17 +04:00
PCONFDUMP ( PIN_CONFIG_INPUT_ENABLE , " input enabled " , NULL ) ,
2013-02-11 21:10:57 +04:00
PCONFDUMP ( PIN_CONFIG_INPUT_SCHMITT_ENABLE , " input schmitt enabled " , NULL ) ,
2011-11-24 21:27:15 +04:00
PCONFDUMP ( PIN_CONFIG_INPUT_SCHMITT , " input schmitt trigger " , NULL ) ,
2013-06-25 16:56:11 +04:00
PCONFDUMP ( PIN_CONFIG_INPUT_DEBOUNCE , " input debounce " , " usec " ) ,
2011-11-24 21:27:15 +04:00
PCONFDUMP ( PIN_CONFIG_POWER_SOURCE , " pin power source " , " selector " ) ,
2013-01-18 11:31:15 +04:00
PCONFDUMP ( PIN_CONFIG_SLEW_RATE , " slew rate " , NULL ) ,
2011-11-24 21:27:15 +04:00
PCONFDUMP ( PIN_CONFIG_LOW_POWER_MODE , " pin low power " , " mode " ) ,
2013-01-04 20:57:40 +04:00
PCONFDUMP ( PIN_CONFIG_OUTPUT , " pin output " , " level " ) ,
2011-11-24 21:27:15 +04:00
} ;
void pinconf_generic_dump_pin ( struct pinctrl_dev * pctldev ,
struct seq_file * s , unsigned pin )
{
const struct pinconf_ops * ops = pctldev - > desc - > confops ;
int i ;
if ( ! ops - > is_generic )
return ;
2013-03-15 08:53:16 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( conf_items ) ; i + + ) {
2011-11-24 21:27:15 +04:00
unsigned long config ;
int ret ;
/* We want to check out this parameter */
config = pinconf_to_config_packed ( conf_items [ i ] . param , 0 ) ;
ret = pin_config_get_for_pin ( pctldev , pin , & config ) ;
/* These are legal errors */
if ( ret = = - EINVAL | | ret = = - ENOTSUPP )
continue ;
if ( ret ) {
seq_printf ( s , " ERROR READING CONFIG SETTING %d " , i ) ;
continue ;
}
/* Space between multiple configs */
seq_puts ( s , " " ) ;
seq_puts ( s , conf_items [ i ] . display ) ;
/* Print unit if available */
if ( conf_items [ i ] . format & &
pinconf_to_config_argument ( config ) ! = 0 )
seq_printf ( s , " (%u %s) " ,
pinconf_to_config_argument ( config ) ,
conf_items [ i ] . format ) ;
}
}
void pinconf_generic_dump_group ( struct pinctrl_dev * pctldev ,
struct seq_file * s , const char * gname )
{
const struct pinconf_ops * ops = pctldev - > desc - > confops ;
int i ;
if ( ! ops - > is_generic )
return ;
2013-03-15 08:53:16 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( conf_items ) ; i + + ) {
2011-11-24 21:27:15 +04:00
unsigned long config ;
int ret ;
/* We want to check out this parameter */
config = pinconf_to_config_packed ( conf_items [ i ] . param , 0 ) ;
ret = pin_config_group_get ( dev_name ( pctldev - > dev ) , gname ,
& config ) ;
/* These are legal errors */
if ( ret = = - EINVAL | | ret = = - ENOTSUPP )
continue ;
if ( ret ) {
seq_printf ( s , " ERROR READING CONFIG SETTING %d " , i ) ;
continue ;
}
/* Space between multiple configs */
seq_puts ( s , " " ) ;
seq_puts ( s , conf_items [ i ] . display ) ;
/* Print unit if available */
if ( conf_items [ i ] . format & & config ! = 0 )
seq_printf ( s , " (%u %s) " ,
pinconf_to_config_argument ( config ) ,
conf_items [ i ] . format ) ;
}
}
2013-02-17 15:42:53 +04:00
void pinconf_generic_dump_config ( struct pinctrl_dev * pctldev ,
struct seq_file * s , unsigned long config )
{
int i ;
2013-03-15 08:53:16 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( conf_items ) ; i + + ) {
2013-02-17 15:42:53 +04:00
if ( pinconf_to_config_param ( config ) ! = conf_items [ i ] . param )
continue ;
seq_printf ( s , " %s: 0x%x " , conf_items [ i ] . display ,
pinconf_to_config_argument ( config ) ) ;
}
}
EXPORT_SYMBOL_GPL ( pinconf_generic_dump_config ) ;
2011-11-24 21:27:15 +04:00
# endif
2013-06-10 23:40:29 +04:00
# ifdef CONFIG_OF
struct pinconf_generic_dt_params {
const char * const property ;
enum pin_config_param param ;
u32 default_value ;
} ;
static struct pinconf_generic_dt_params dt_params [ ] = {
{ " bias-disable " , PIN_CONFIG_BIAS_DISABLE , 0 } ,
{ " bias-high-impedance " , PIN_CONFIG_BIAS_HIGH_IMPEDANCE , 0 } ,
{ " bias-bus-hold " , PIN_CONFIG_BIAS_BUS_HOLD , 0 } ,
2013-06-14 19:42:49 +04:00
{ " bias-pull-up " , PIN_CONFIG_BIAS_PULL_UP , 1 } ,
{ " bias-pull-down " , PIN_CONFIG_BIAS_PULL_DOWN , 1 } ,
{ " bias-pull-pin-default " , PIN_CONFIG_BIAS_PULL_PIN_DEFAULT , 1 } ,
2013-06-10 23:40:29 +04:00
{ " drive-push-pull " , PIN_CONFIG_DRIVE_PUSH_PULL , 0 } ,
{ " drive-open-drain " , PIN_CONFIG_DRIVE_OPEN_DRAIN , 0 } ,
{ " drive-open-source " , PIN_CONFIG_DRIVE_OPEN_SOURCE , 0 } ,
{ " drive-strength " , PIN_CONFIG_DRIVE_STRENGTH , 0 } ,
2013-12-11 22:37:17 +04:00
{ " input-enable " , PIN_CONFIG_INPUT_ENABLE , 1 } ,
{ " input-disable " , PIN_CONFIG_INPUT_ENABLE , 0 } ,
2013-06-10 23:40:29 +04:00
{ " input-schmitt-enable " , PIN_CONFIG_INPUT_SCHMITT_ENABLE , 1 } ,
{ " input-schmitt-disable " , PIN_CONFIG_INPUT_SCHMITT_ENABLE , 0 } ,
{ " input-debounce " , PIN_CONFIG_INPUT_DEBOUNCE , 0 } ,
2013-06-14 19:42:49 +04:00
{ " low-power-enable " , PIN_CONFIG_LOW_POWER_MODE , 1 } ,
{ " low-power-disable " , PIN_CONFIG_LOW_POWER_MODE , 0 } ,
2013-06-10 23:40:29 +04:00
{ " output-low " , PIN_CONFIG_OUTPUT , 0 , } ,
{ " output-high " , PIN_CONFIG_OUTPUT , 1 , } ,
2013-12-11 22:37:17 +04:00
{ " slew-rate " , PIN_CONFIG_SLEW_RATE , 0 } ,
2013-06-10 23:40:29 +04:00
} ;
/**
* pinconf_generic_parse_dt_config ( )
* parse the config properties into generic pinconfig values .
* @ np : node containing the pinconfig properties
* @ configs : array with nconfigs entries containing the generic pinconf values
* @ nconfigs : umber of configurations
*/
int pinconf_generic_parse_dt_config ( struct device_node * np ,
unsigned long * * configs ,
unsigned int * nconfigs )
{
2013-06-14 19:43:55 +04:00
unsigned long * cfg ;
2013-06-10 23:40:29 +04:00
unsigned int ncfg = 0 ;
int ret ;
int i ;
u32 val ;
if ( ! np )
return - EINVAL ;
2013-06-14 19:43:55 +04:00
/* allocate a temporary array big enough to hold one of each option */
cfg = kzalloc ( sizeof ( * cfg ) * ARRAY_SIZE ( dt_params ) , GFP_KERNEL ) ;
if ( ! cfg )
return - ENOMEM ;
2013-06-10 23:40:29 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( dt_params ) ; i + + ) {
struct pinconf_generic_dt_params * par = & dt_params [ i ] ;
ret = of_property_read_u32 ( np , par - > property , & val ) ;
/* property not found */
if ( ret = = - EINVAL )
continue ;
/* use default value, when no value is specified */
if ( ret )
val = par - > default_value ;
pr_debug ( " found %s with value %u \n " , par - > property , val ) ;
cfg [ ncfg ] = pinconf_to_config_packed ( par - > param , val ) ;
ncfg + + ;
}
2013-06-14 19:43:55 +04:00
ret = 0 ;
2013-06-14 19:43:21 +04:00
/* no configs found at all */
if ( ncfg = = 0 ) {
* configs = NULL ;
* nconfigs = 0 ;
2013-06-14 19:43:55 +04:00
goto out ;
2013-06-14 19:43:21 +04:00
}
2013-06-10 23:40:29 +04:00
/*
* Now limit the number of configs to the real number of
* found properties .
*/
* configs = kzalloc ( ncfg * sizeof ( unsigned long ) , GFP_KERNEL ) ;
2013-06-14 19:43:55 +04:00
if ( ! * configs ) {
ret = - ENOMEM ;
goto out ;
}
2013-06-10 23:40:29 +04:00
2013-06-14 19:43:55 +04:00
memcpy ( * configs , cfg , ncfg * sizeof ( unsigned long ) ) ;
2013-06-10 23:40:29 +04:00
* nconfigs = ncfg ;
2013-06-14 19:43:55 +04:00
out :
kfree ( cfg ) ;
return ret ;
2013-06-10 23:40:29 +04:00
}
2013-08-06 17:12:34 +04:00
int pinconf_generic_dt_subnode_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np , struct pinctrl_map * * map ,
2013-08-21 15:23:37 +04:00
unsigned * reserved_maps , unsigned * num_maps ,
enum pinctrl_map_type type )
2013-08-06 17:12:34 +04:00
{
int ret ;
const char * function ;
struct device * dev = pctldev - > dev ;
unsigned long * configs = NULL ;
unsigned num_configs = 0 ;
unsigned reserve ;
struct property * prop ;
const char * group ;
ret = of_property_read_string ( np , " function " , & function ) ;
if ( ret < 0 ) {
/* EINVAL=missing, which is fine since it's optional */
if ( ret ! = - EINVAL )
2013-08-29 06:05:06 +04:00
dev_err ( dev , " could not parse property function \n " ) ;
2013-08-06 17:12:34 +04:00
function = NULL ;
}
ret = pinconf_generic_parse_dt_config ( np , & configs , & num_configs ) ;
if ( ret < 0 ) {
dev_err ( dev , " could not parse node property \n " ) ;
return ret ;
}
reserve = 0 ;
if ( function ! = NULL )
reserve + + ;
if ( num_configs )
reserve + + ;
ret = of_property_count_strings ( np , " pins " ) ;
if ( ret < 0 ) {
2013-08-29 06:05:06 +04:00
dev_err ( dev , " could not parse property pins \n " ) ;
2013-08-06 17:12:34 +04:00
goto exit ;
}
reserve * = ret ;
ret = pinctrl_utils_reserve_map ( pctldev , map , reserved_maps ,
num_maps , reserve ) ;
if ( ret < 0 )
goto exit ;
of_property_for_each_string ( np , " pins " , prop , group ) {
if ( function ) {
ret = pinctrl_utils_add_map_mux ( pctldev , map ,
reserved_maps , num_maps , group ,
function ) ;
if ( ret < 0 )
goto exit ;
}
if ( num_configs ) {
ret = pinctrl_utils_add_map_configs ( pctldev , map ,
reserved_maps , num_maps , group , configs ,
2013-08-21 15:23:37 +04:00
num_configs , type ) ;
2013-08-06 17:12:34 +04:00
if ( ret < 0 )
goto exit ;
}
}
ret = 0 ;
exit :
kfree ( configs ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( pinconf_generic_dt_subnode_to_map ) ;
int pinconf_generic_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np_config , struct pinctrl_map * * map ,
2013-08-21 15:23:37 +04:00
unsigned * num_maps , enum pinctrl_map_type type )
2013-08-06 17:12:34 +04:00
{
unsigned reserved_maps ;
struct device_node * np ;
int ret ;
reserved_maps = 0 ;
* map = NULL ;
* num_maps = 0 ;
for_each_child_of_node ( np_config , np ) {
ret = pinconf_generic_dt_subnode_to_map ( pctldev , np , map ,
2013-08-21 15:23:37 +04:00
& reserved_maps , num_maps , type ) ;
2013-08-06 17:12:34 +04:00
if ( ret < 0 ) {
pinctrl_utils_dt_free_map ( pctldev , * map , * num_maps ) ;
return ret ;
}
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( pinconf_generic_dt_node_to_map ) ;
2013-06-10 23:40:29 +04:00
# endif