2017-08-17 14:50:38 +08:00
/*
* Spreadtrum pin controller driver
* Copyright ( C ) 2017 Spreadtrum - http : //www.spreadtrum.com
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that 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/debugfs.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.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/slab.h>
# include "../core.h"
# include "../pinmux.h"
# include "../pinconf.h"
# include "../pinctrl-utils.h"
# include "pinctrl-sprd.h"
# define PINCTRL_BIT_MASK(width) (~(~0UL << (width)))
# define PINCTRL_REG_OFFSET 0x20
# define PINCTRL_REG_MISC_OFFSET 0x4020
# define PINCTRL_REG_LEN 0x4
# define PIN_FUNC_MASK (BIT(4) | BIT(5))
# define PIN_FUNC_SEL_1 ~PIN_FUNC_MASK
# define PIN_FUNC_SEL_2 BIT(4)
# define PIN_FUNC_SEL_3 BIT(5)
# define PIN_FUNC_SEL_4 PIN_FUNC_MASK
# define AP_SLEEP_MODE BIT(13)
# define PUBCP_SLEEP_MODE BIT(14)
# define TGLDSP_SLEEP_MODE BIT(15)
# define AGDSP_SLEEP_MODE BIT(16)
# define SLEEP_MODE_MASK GENMASK(3, 0)
# define SLEEP_MODE_SHIFT 13
# define SLEEP_INPUT BIT(1)
# define SLEEP_INPUT_MASK 0x1
# define SLEEP_INPUT_SHIFT 1
# define SLEEP_OUTPUT BIT(0)
# define SLEEP_OUTPUT_MASK 0x1
# define SLEEP_OUTPUT_SHIFT 0
# define DRIVE_STRENGTH_MASK GENMASK(3, 0)
# define DRIVE_STRENGTH_SHIFT 19
# define SLEEP_PULL_DOWN BIT(2)
# define SLEEP_PULL_DOWN_MASK 0x1
# define SLEEP_PULL_DOWN_SHIFT 2
# define PULL_DOWN BIT(6)
# define PULL_DOWN_MASK 0x1
# define PULL_DOWN_SHIFT 6
# define SLEEP_PULL_UP BIT(3)
# define SLEEP_PULL_UP_MASK 0x1
# define SLEEP_PULL_UP_SHIFT 3
# define PULL_UP_20K (BIT(12) | BIT(7))
# define PULL_UP_4_7K BIT(12)
# define PULL_UP_MASK 0x21
# define PULL_UP_SHIFT 7
# define INPUT_SCHMITT BIT(11)
# define INPUT_SCHMITT_MASK 0x1
# define INPUT_SCHMITT_SHIFT 11
enum pin_sleep_mode {
AP_SLEEP = BIT ( 0 ) ,
PUBCP_SLEEP = BIT ( 1 ) ,
TGLDSP_SLEEP = BIT ( 2 ) ,
AGDSP_SLEEP = BIT ( 3 ) ,
} ;
enum pin_func_sel {
PIN_FUNC_1 ,
PIN_FUNC_2 ,
PIN_FUNC_3 ,
PIN_FUNC_4 ,
PIN_FUNC_MAX ,
} ;
/**
* struct sprd_pin : represent one pin ' s description
* @ name : pin name
* @ number : pin number
* @ type : pin type , can be GLOBAL_CTRL_PIN / COMMON_PIN / MISC_PIN
* @ reg : pin register address
* @ bit_offset : bit offset in pin register
* @ bit_width : bit width in pin register
*/
struct sprd_pin {
const char * name ;
unsigned int number ;
enum pin_type type ;
unsigned long reg ;
unsigned long bit_offset ;
unsigned long bit_width ;
} ;
/**
* struct sprd_pin_group : represent one group ' s description
* @ name : group name
* @ npins : pin numbers of this group
* @ pins : pointer to pins array
*/
struct sprd_pin_group {
const char * name ;
unsigned int npins ;
unsigned int * pins ;
} ;
/**
* struct sprd_pinctrl_soc_info : represent the SoC ' s pins description
* @ groups : pointer to groups of pins
* @ ngroups : group numbers of the whole SoC
* @ pins : pointer to pins description
* @ npins : pin numbers of the whole SoC
* @ grp_names : pointer to group names array
*/
struct sprd_pinctrl_soc_info {
struct sprd_pin_group * groups ;
unsigned int ngroups ;
struct sprd_pin * pins ;
unsigned int npins ;
const char * * grp_names ;
} ;
/**
* struct sprd_pinctrl : represent the pin controller device
* @ dev : pointer to the device structure
* @ pctl : pointer to the pinctrl handle
* @ base : base address of the controller
* @ info : pointer to SoC ' s pins description information
*/
struct sprd_pinctrl {
struct device * dev ;
struct pinctrl_dev * pctl ;
void __iomem * base ;
struct sprd_pinctrl_soc_info * info ;
} ;
enum sprd_pinconf_params {
SPRD_PIN_CONFIG_CONTROL = PIN_CONFIG_END + 1 ,
SPRD_PIN_CONFIG_SLEEP_MODE = PIN_CONFIG_END + 2 ,
} ;
static int sprd_pinctrl_get_id_by_name ( struct sprd_pinctrl * sprd_pctl ,
const char * name )
{
struct sprd_pinctrl_soc_info * info = sprd_pctl - > info ;
int i ;
for ( i = 0 ; i < info - > npins ; i + + ) {
if ( ! strcmp ( info - > pins [ i ] . name , name ) )
return info - > pins [ i ] . number ;
}
return - ENODEV ;
}
static struct sprd_pin *
sprd_pinctrl_get_pin_by_id ( struct sprd_pinctrl * sprd_pctl , unsigned int id )
{
struct sprd_pinctrl_soc_info * info = sprd_pctl - > info ;
struct sprd_pin * pin = NULL ;
int i ;
for ( i = 0 ; i < info - > npins ; i + + ) {
if ( info - > pins [ i ] . number = = id ) {
pin = & info - > pins [ i ] ;
break ;
}
}
return pin ;
}
static const struct sprd_pin_group *
sprd_pinctrl_find_group_by_name ( struct sprd_pinctrl * sprd_pctl ,
const char * name )
{
struct sprd_pinctrl_soc_info * info = sprd_pctl - > info ;
const struct sprd_pin_group * grp = NULL ;
int i ;
for ( i = 0 ; i < info - > ngroups ; i + + ) {
if ( ! strcmp ( info - > groups [ i ] . name , name ) ) {
grp = & info - > groups [ i ] ;
break ;
}
}
return grp ;
}
static int sprd_pctrl_group_count ( struct pinctrl_dev * pctldev )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
return info - > ngroups ;
}
static const char * sprd_pctrl_group_name ( struct pinctrl_dev * pctldev ,
unsigned int selector )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
return info - > groups [ selector ] . name ;
}
static int sprd_pctrl_group_pins ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
const unsigned int * * pins ,
unsigned int * npins )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
if ( selector > = info - > ngroups )
return - EINVAL ;
* pins = info - > groups [ selector ] . pins ;
* npins = info - > groups [ selector ] . npins ;
return 0 ;
}
static int sprd_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np ,
struct pinctrl_map * * map ,
unsigned int * num_maps )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
const struct sprd_pin_group * grp ;
unsigned long * configs = NULL ;
unsigned int num_configs = 0 ;
unsigned int reserved_maps = 0 ;
unsigned int reserve = 0 ;
const char * function ;
enum pinctrl_map_type type ;
int ret ;
grp = sprd_pinctrl_find_group_by_name ( pctl , np - > name ) ;
if ( ! grp ) {
dev_err ( pctl - > dev , " unable to find group for node %s \n " ,
of_node_full_name ( np ) ) ;
return - EINVAL ;
}
ret = of_property_count_strings ( np , " pins " ) ;
if ( ret < 0 )
return ret ;
if ( ret = = 1 )
type = PIN_MAP_TYPE_CONFIGS_PIN ;
else
type = PIN_MAP_TYPE_CONFIGS_GROUP ;
ret = of_property_read_string ( np , " function " , & function ) ;
if ( ret < 0 ) {
if ( ret ! = - EINVAL )
dev_err ( pctl - > dev ,
" %s: could not parse property function \n " ,
of_node_full_name ( np ) ) ;
function = NULL ;
}
ret = pinconf_generic_parse_dt_config ( np , pctldev , & configs ,
& num_configs ) ;
if ( ret < 0 ) {
dev_err ( pctl - > dev , " %s: could not parse node property \n " ,
of_node_full_name ( np ) ) ;
return ret ;
}
* map = NULL ;
* num_maps = 0 ;
if ( function ! = NULL )
reserve + + ;
if ( num_configs )
reserve + + ;
ret = pinctrl_utils_reserve_map ( pctldev , map , & reserved_maps ,
num_maps , reserve ) ;
if ( ret < 0 )
goto out ;
if ( function ) {
ret = pinctrl_utils_add_map_mux ( pctldev , map ,
& reserved_maps , num_maps ,
grp - > name , function ) ;
if ( ret < 0 )
goto out ;
}
if ( num_configs ) {
const char * group_or_pin ;
unsigned int pin_id ;
if ( type = = PIN_MAP_TYPE_CONFIGS_PIN ) {
pin_id = grp - > pins [ 0 ] ;
group_or_pin = pin_get_name ( pctldev , pin_id ) ;
} else {
group_or_pin = grp - > name ;
}
ret = pinctrl_utils_add_map_configs ( pctldev , map ,
& reserved_maps , num_maps ,
group_or_pin , configs ,
num_configs , type ) ;
}
out :
kfree ( configs ) ;
return ret ;
}
static void sprd_pctrl_dbg_show ( struct pinctrl_dev * pctldev , struct seq_file * s ,
unsigned int offset )
{
seq_printf ( s , " %s " , dev_name ( pctldev - > dev ) ) ;
}
static const struct pinctrl_ops sprd_pctrl_ops = {
. get_groups_count = sprd_pctrl_group_count ,
. get_group_name = sprd_pctrl_group_name ,
. get_group_pins = sprd_pctrl_group_pins ,
. pin_dbg_show = sprd_pctrl_dbg_show ,
. dt_node_to_map = sprd_dt_node_to_map ,
. dt_free_map = pinctrl_utils_free_map ,
} ;
2017-09-04 11:53:22 +01:00
static int sprd_pmx_get_function_count ( struct pinctrl_dev * pctldev )
2017-08-17 14:50:38 +08:00
{
return PIN_FUNC_MAX ;
}
2017-09-04 11:53:22 +01:00
static const char * sprd_pmx_get_function_name ( struct pinctrl_dev * pctldev ,
unsigned int selector )
2017-08-17 14:50:38 +08:00
{
switch ( selector ) {
case PIN_FUNC_1 :
return " func1 " ;
case PIN_FUNC_2 :
return " func2 " ;
case PIN_FUNC_3 :
return " func3 " ;
case PIN_FUNC_4 :
return " func4 " ;
default :
return " null " ;
}
}
2017-09-04 11:53:22 +01:00
static int sprd_pmx_get_function_groups ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
const char * const * * groups ,
unsigned int * const num_groups )
2017-08-17 14:50:38 +08:00
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
* groups = info - > grp_names ;
* num_groups = info - > ngroups ;
return 0 ;
}
static int sprd_pmx_set_mux ( struct pinctrl_dev * pctldev ,
unsigned int func_selector ,
unsigned int group_selector )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
struct sprd_pin_group * grp = & info - > groups [ group_selector ] ;
unsigned int i , grp_pins = grp - > npins ;
unsigned long reg ;
unsigned int val = 0 ;
if ( group_selector > info - > ngroups )
return - EINVAL ;
switch ( func_selector ) {
case PIN_FUNC_1 :
val & = PIN_FUNC_SEL_1 ;
break ;
case PIN_FUNC_2 :
val | = PIN_FUNC_SEL_2 ;
break ;
case PIN_FUNC_3 :
val | = PIN_FUNC_SEL_3 ;
break ;
case PIN_FUNC_4 :
val | = PIN_FUNC_SEL_4 ;
break ;
default :
break ;
}
for ( i = 0 ; i < grp_pins ; i + + ) {
unsigned int pin_id = grp - > pins [ i ] ;
struct sprd_pin * pin = sprd_pinctrl_get_pin_by_id ( pctl , pin_id ) ;
if ( ! pin | | pin - > type ! = COMMON_PIN )
continue ;
reg = readl ( ( void __iomem * ) pin - > reg ) ;
reg & = ~ PIN_FUNC_MASK ;
reg | = val ;
writel ( reg , ( void __iomem * ) pin - > reg ) ;
}
return 0 ;
}
static const struct pinmux_ops sprd_pmx_ops = {
. get_functions_count = sprd_pmx_get_function_count ,
. get_function_name = sprd_pmx_get_function_name ,
. get_function_groups = sprd_pmx_get_function_groups ,
. set_mux = sprd_pmx_set_mux ,
} ;
static int sprd_pinconf_get ( struct pinctrl_dev * pctldev , unsigned int pin_id ,
unsigned long * config )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pin * pin = sprd_pinctrl_get_pin_by_id ( pctl , pin_id ) ;
unsigned int param = pinconf_to_config_param ( * config ) ;
unsigned int reg , arg ;
if ( ! pin )
return - EINVAL ;
if ( pin - > type = = GLOBAL_CTRL_PIN ) {
reg = ( readl ( ( void __iomem * ) pin - > reg ) > >
pin - > bit_offset ) & PINCTRL_BIT_MASK ( pin - > bit_width ) ;
} else {
reg = readl ( ( void __iomem * ) pin - > reg ) ;
}
if ( pin - > type = = GLOBAL_CTRL_PIN & &
param = = SPRD_PIN_CONFIG_CONTROL ) {
arg = reg ;
} else if ( pin - > type = = COMMON_PIN ) {
switch ( param ) {
case SPRD_PIN_CONFIG_SLEEP_MODE :
arg = ( reg > > SLEEP_MODE_SHIFT ) & SLEEP_MODE_MASK ;
break ;
case PIN_CONFIG_INPUT_ENABLE :
arg = ( reg > > SLEEP_INPUT_SHIFT ) & SLEEP_INPUT_MASK ;
break ;
case PIN_CONFIG_OUTPUT :
arg = reg & SLEEP_OUTPUT_MASK ;
break ;
case PIN_CONFIG_SLEEP_HARDWARE_STATE :
arg = 0 ;
break ;
default :
return - ENOTSUPP ;
}
} else if ( pin - > type = = MISC_PIN ) {
switch ( param ) {
case PIN_CONFIG_DRIVE_STRENGTH :
arg = ( reg > > DRIVE_STRENGTH_SHIFT ) &
DRIVE_STRENGTH_MASK ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
/* combine sleep pull down and pull down config */
arg = ( ( reg > > SLEEP_PULL_DOWN_SHIFT ) &
SLEEP_PULL_DOWN_MASK ) < < 16 ;
arg | = ( reg > > PULL_DOWN_SHIFT ) & PULL_DOWN_MASK ;
break ;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE :
arg = ( reg > > INPUT_SCHMITT_SHIFT ) & INPUT_SCHMITT_MASK ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
/* combine sleep pull up and pull up config */
arg = ( ( reg > > SLEEP_PULL_UP_SHIFT ) &
SLEEP_PULL_UP_MASK ) < < 16 ;
arg | = ( reg > > PULL_UP_SHIFT ) & PULL_UP_MASK ;
break ;
case PIN_CONFIG_SLEEP_HARDWARE_STATE :
arg = 0 ;
break ;
default :
return - ENOTSUPP ;
}
} else {
return - ENOTSUPP ;
}
* config = pinconf_to_config_packed ( param , arg ) ;
return 0 ;
}
static unsigned int sprd_pinconf_drive ( unsigned int mA )
{
unsigned int val = 0 ;
switch ( mA ) {
case 2 :
break ;
case 4 :
val | = BIT ( 19 ) ;
break ;
case 6 :
val | = BIT ( 20 ) ;
break ;
case 8 :
val | = BIT ( 19 ) | BIT ( 20 ) ;
break ;
case 10 :
val | = BIT ( 21 ) ;
break ;
case 12 :
val | = BIT ( 21 ) | BIT ( 19 ) ;
break ;
case 14 :
val | = BIT ( 21 ) | BIT ( 20 ) ;
break ;
case 16 :
val | = BIT ( 19 ) | BIT ( 20 ) | BIT ( 21 ) ;
break ;
case 20 :
val | = BIT ( 22 ) ;
break ;
case 21 :
val | = BIT ( 22 ) | BIT ( 19 ) ;
break ;
case 24 :
val | = BIT ( 22 ) | BIT ( 20 ) ;
break ;
case 25 :
val | = BIT ( 22 ) | BIT ( 20 ) | BIT ( 19 ) ;
break ;
case 27 :
val | = BIT ( 22 ) | BIT ( 21 ) ;
break ;
case 29 :
val | = BIT ( 22 ) | BIT ( 21 ) | BIT ( 19 ) ;
break ;
case 31 :
val | = BIT ( 22 ) | BIT ( 21 ) | BIT ( 20 ) ;
break ;
case 33 :
val | = BIT ( 22 ) | BIT ( 21 ) | BIT ( 20 ) | BIT ( 19 ) ;
break ;
default :
break ;
}
return val ;
}
static bool sprd_pinctrl_check_sleep_config ( unsigned long * configs ,
unsigned int num_configs )
{
unsigned int param ;
int i ;
for ( i = 0 ; i < num_configs ; i + + ) {
param = pinconf_to_config_param ( configs [ i ] ) ;
if ( param = = PIN_CONFIG_SLEEP_HARDWARE_STATE )
return true ;
}
return false ;
}
static int sprd_pinconf_set ( struct pinctrl_dev * pctldev , unsigned int pin_id ,
unsigned long * configs , unsigned int num_configs )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pin * pin = sprd_pinctrl_get_pin_by_id ( pctl , pin_id ) ;
bool is_sleep_config ;
unsigned long reg ;
int i ;
if ( ! pin )
return - EINVAL ;
is_sleep_config = sprd_pinctrl_check_sleep_config ( configs , num_configs ) ;
for ( i = 0 ; i < num_configs ; i + + ) {
unsigned int param , arg , shift , mask , val ;
param = pinconf_to_config_param ( configs [ i ] ) ;
arg = pinconf_to_config_argument ( configs [ i ] ) ;
val = 0 ;
shift = 0 ;
mask = 0 ;
if ( pin - > type = = GLOBAL_CTRL_PIN & &
param = = SPRD_PIN_CONFIG_CONTROL ) {
val = arg ;
} else if ( pin - > type = = COMMON_PIN ) {
switch ( param ) {
case SPRD_PIN_CONFIG_SLEEP_MODE :
if ( arg & AP_SLEEP )
val | = AP_SLEEP_MODE ;
if ( arg & PUBCP_SLEEP )
val | = PUBCP_SLEEP_MODE ;
if ( arg & TGLDSP_SLEEP )
val | = TGLDSP_SLEEP_MODE ;
if ( arg & AGDSP_SLEEP )
val | = AGDSP_SLEEP_MODE ;
mask = SLEEP_MODE_MASK ;
shift = SLEEP_MODE_SHIFT ;
break ;
case PIN_CONFIG_INPUT_ENABLE :
if ( is_sleep_config = = true ) {
if ( arg > 0 )
val | = SLEEP_INPUT ;
else
val & = ~ SLEEP_INPUT ;
mask = SLEEP_INPUT_MASK ;
shift = SLEEP_INPUT_SHIFT ;
}
break ;
case PIN_CONFIG_OUTPUT :
if ( is_sleep_config = = true ) {
val | = SLEEP_OUTPUT ;
mask = SLEEP_OUTPUT_MASK ;
shift = SLEEP_OUTPUT_SHIFT ;
}
break ;
case PIN_CONFIG_SLEEP_HARDWARE_STATE :
continue ;
default :
return - ENOTSUPP ;
}
} else if ( pin - > type = = MISC_PIN ) {
switch ( param ) {
case PIN_CONFIG_DRIVE_STRENGTH :
if ( arg < 2 | | arg > 60 )
return - EINVAL ;
val = sprd_pinconf_drive ( arg ) ;
mask = DRIVE_STRENGTH_MASK ;
shift = DRIVE_STRENGTH_SHIFT ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
if ( is_sleep_config = = true ) {
val | = SLEEP_PULL_DOWN ;
mask = SLEEP_PULL_DOWN_MASK ;
shift = SLEEP_PULL_DOWN_SHIFT ;
} else {
val | = PULL_DOWN ;
mask = PULL_DOWN_MASK ;
shift = PULL_DOWN_SHIFT ;
}
break ;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE :
if ( arg > 0 )
val | = INPUT_SCHMITT ;
else
val & = ~ INPUT_SCHMITT ;
mask = INPUT_SCHMITT_MASK ;
shift = INPUT_SCHMITT_SHIFT ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
if ( is_sleep_config = = true ) {
val | = SLEEP_PULL_UP ;
mask = SLEEP_PULL_UP_MASK ;
shift = SLEEP_PULL_UP_SHIFT ;
} else {
if ( arg = = 20000 )
val | = PULL_UP_20K ;
else if ( arg = = 4700 )
val | = PULL_UP_4_7K ;
mask = PULL_UP_MASK ;
shift = PULL_UP_SHIFT ;
}
break ;
case PIN_CONFIG_SLEEP_HARDWARE_STATE :
continue ;
default :
return - ENOTSUPP ;
}
} else {
return - ENOTSUPP ;
}
if ( pin - > type = = GLOBAL_CTRL_PIN ) {
reg = readl ( ( void __iomem * ) pin - > reg ) ;
reg & = ~ ( PINCTRL_BIT_MASK ( pin - > bit_width )
< < pin - > bit_offset ) ;
reg | = ( val & PINCTRL_BIT_MASK ( pin - > bit_width ) )
< < pin - > bit_offset ;
writel ( reg , ( void __iomem * ) pin - > reg ) ;
} else {
reg = readl ( ( void __iomem * ) pin - > reg ) ;
reg & = ~ ( mask < < shift ) ;
reg | = val ;
writel ( reg , ( void __iomem * ) pin - > reg ) ;
}
}
return 0 ;
}
static int sprd_pinconf_group_get ( struct pinctrl_dev * pctldev ,
unsigned int selector , unsigned long * config )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
struct sprd_pin_group * grp ;
unsigned int pin_id ;
if ( selector > info - > ngroups )
return - EINVAL ;
grp = & info - > groups [ selector ] ;
pin_id = grp - > pins [ 0 ] ;
return sprd_pinconf_get ( pctldev , pin_id , config ) ;
}
static int sprd_pinconf_group_set ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
unsigned long * configs ,
unsigned int num_configs )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
struct sprd_pin_group * grp ;
int ret , i ;
if ( selector > info - > ngroups )
return - EINVAL ;
grp = & info - > groups [ selector ] ;
for ( i = 0 ; i < grp - > npins ; i + + ) {
unsigned int pin_id = grp - > pins [ i ] ;
ret = sprd_pinconf_set ( pctldev , pin_id , configs , num_configs ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int sprd_pinconf_get_config ( struct pinctrl_dev * pctldev ,
unsigned int pin_id ,
unsigned long * config )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pin * pin = sprd_pinctrl_get_pin_by_id ( pctl , pin_id ) ;
if ( ! pin )
return - EINVAL ;
if ( pin - > type = = GLOBAL_CTRL_PIN ) {
* config = ( readl ( ( void __iomem * ) pin - > reg ) > >
pin - > bit_offset ) & PINCTRL_BIT_MASK ( pin - > bit_width ) ;
} else {
* config = readl ( ( void __iomem * ) pin - > reg ) ;
}
return 0 ;
}
static void sprd_pinconf_dbg_show ( struct pinctrl_dev * pctldev ,
struct seq_file * s , unsigned int pin_id )
{
unsigned long config ;
int ret ;
ret = sprd_pinconf_get_config ( pctldev , pin_id , & config ) ;
if ( ret )
return ;
seq_printf ( s , " 0x%lx " , config ) ;
}
static void sprd_pinconf_group_dbg_show ( struct pinctrl_dev * pctldev ,
struct seq_file * s ,
unsigned int selector )
{
struct sprd_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct sprd_pinctrl_soc_info * info = pctl - > info ;
struct sprd_pin_group * grp ;
unsigned long config ;
const char * name ;
int i , ret ;
if ( selector > info - > ngroups )
return ;
grp = & info - > groups [ selector ] ;
seq_printf ( s , " \n " ) ;
for ( i = 0 ; i < grp - > npins ; i + + , config + + ) {
unsigned int pin_id = grp - > pins [ i ] ;
name = pin_get_name ( pctldev , pin_id ) ;
ret = sprd_pinconf_get_config ( pctldev , pin_id , & config ) ;
if ( ret )
return ;
seq_printf ( s , " %s: 0x%lx " , name , config ) ;
}
}
static const struct pinconf_ops sprd_pinconf_ops = {
. is_generic = true ,
. pin_config_get = sprd_pinconf_get ,
. pin_config_set = sprd_pinconf_set ,
. pin_config_group_get = sprd_pinconf_group_get ,
. pin_config_group_set = sprd_pinconf_group_set ,
. pin_config_dbg_show = sprd_pinconf_dbg_show ,
. pin_config_group_dbg_show = sprd_pinconf_group_dbg_show ,
} ;
static const struct pinconf_generic_params sprd_dt_params [ ] = {
{ " sprd,control " , SPRD_PIN_CONFIG_CONTROL , 0 } ,
{ " sprd,sleep-mode " , SPRD_PIN_CONFIG_SLEEP_MODE , 0 } ,
} ;
# ifdef CONFIG_DEBUG_FS
static const struct pin_config_item sprd_conf_items [ ] = {
PCONFDUMP ( SPRD_PIN_CONFIG_CONTROL , " global control " , NULL , true ) ,
PCONFDUMP ( SPRD_PIN_CONFIG_SLEEP_MODE , " sleep mode " , NULL , true ) ,
} ;
# endif
static struct pinctrl_desc sprd_pinctrl_desc = {
. pctlops = & sprd_pctrl_ops ,
. pmxops = & sprd_pmx_ops ,
. confops = & sprd_pinconf_ops ,
. num_custom_params = ARRAY_SIZE ( sprd_dt_params ) ,
. custom_params = sprd_dt_params ,
# ifdef CONFIG_DEBUG_FS
. custom_conf_items = sprd_conf_items ,
# endif
. owner = THIS_MODULE ,
} ;
static int sprd_pinctrl_parse_groups ( struct device_node * np ,
struct sprd_pinctrl * sprd_pctl ,
struct sprd_pin_group * grp )
{
struct property * prop ;
const char * pin_name ;
int ret , i = 0 ;
ret = of_property_count_strings ( np , " pins " ) ;
if ( ret < 0 )
return ret ;
grp - > name = np - > name ;
grp - > npins = ret ;
grp - > pins = devm_kzalloc ( sprd_pctl - > dev , grp - > npins *
sizeof ( unsigned int ) , GFP_KERNEL ) ;
if ( ! grp - > pins )
return - ENOMEM ;
of_property_for_each_string ( np , " pins " , prop , pin_name ) {
ret = sprd_pinctrl_get_id_by_name ( sprd_pctl , pin_name ) ;
if ( ret > = 0 )
grp - > pins [ i + + ] = ret ;
}
for ( i = 0 ; i < grp - > npins ; i + + ) {
dev_dbg ( sprd_pctl - > dev ,
" Group[%s] contains [%d] pins: id = %d \n " ,
grp - > name , grp - > npins , grp - > pins [ i ] ) ;
}
return 0 ;
}
static unsigned int sprd_pinctrl_get_groups ( struct device_node * np )
{
struct device_node * child ;
unsigned int group_cnt , cnt ;
group_cnt = of_get_child_count ( np ) ;
for_each_child_of_node ( np , child ) {
cnt = of_get_child_count ( child ) ;
if ( cnt > 0 )
group_cnt + = cnt ;
}
return group_cnt ;
}
static int sprd_pinctrl_parse_dt ( struct sprd_pinctrl * sprd_pctl )
{
struct sprd_pinctrl_soc_info * info = sprd_pctl - > info ;
struct device_node * np = sprd_pctl - > dev - > of_node ;
struct device_node * child , * sub_child ;
struct sprd_pin_group * grp ;
const char * * temp ;
int ret ;
if ( ! np )
return - ENODEV ;
info - > ngroups = sprd_pinctrl_get_groups ( np ) ;
if ( ! info - > ngroups )
return 0 ;
info - > groups = devm_kzalloc ( sprd_pctl - > dev , info - > ngroups *
sizeof ( struct sprd_pin_group ) ,
GFP_KERNEL ) ;
if ( ! info - > groups )
return - ENOMEM ;
info - > grp_names = devm_kzalloc ( sprd_pctl - > dev ,
info - > ngroups * sizeof ( char * ) ,
GFP_KERNEL ) ;
if ( ! info - > grp_names )
return - ENOMEM ;
temp = info - > grp_names ;
grp = info - > groups ;
for_each_child_of_node ( np , child ) {
ret = sprd_pinctrl_parse_groups ( child , sprd_pctl , grp ) ;
if ( ret )
return ret ;
* temp + + = grp - > name ;
grp + + ;
if ( of_get_child_count ( child ) > 0 ) {
for_each_child_of_node ( child , sub_child ) {
ret = sprd_pinctrl_parse_groups ( sub_child ,
sprd_pctl , grp ) ;
if ( ret )
return ret ;
* temp + + = grp - > name ;
grp + + ;
}
}
}
return 0 ;
}
static int sprd_pinctrl_add_pins ( struct sprd_pinctrl * sprd_pctl ,
struct sprd_pins_info * sprd_soc_pin_info ,
int pins_cnt )
{
struct sprd_pinctrl_soc_info * info = sprd_pctl - > info ;
unsigned int ctrl_pin = 0 , com_pin = 0 ;
struct sprd_pin * pin ;
int i ;
info - > npins = pins_cnt ;
info - > pins = devm_kzalloc ( sprd_pctl - > dev ,
info - > npins * sizeof ( struct sprd_pin ) ,
GFP_KERNEL ) ;
if ( ! info - > pins )
return - ENOMEM ;
for ( i = 0 , pin = info - > pins ; i < info - > npins ; i + + , pin + + ) {
unsigned int reg ;
pin - > name = sprd_soc_pin_info [ i ] . name ;
pin - > type = sprd_soc_pin_info [ i ] . type ;
pin - > number = sprd_soc_pin_info [ i ] . num ;
reg = sprd_soc_pin_info [ i ] . reg ;
if ( pin - > type = = GLOBAL_CTRL_PIN ) {
pin - > reg = ( unsigned long ) sprd_pctl - > base +
PINCTRL_REG_LEN * reg ;
pin - > bit_offset = sprd_soc_pin_info [ i ] . bit_offset ;
pin - > bit_width = sprd_soc_pin_info [ i ] . bit_width ;
ctrl_pin + + ;
} else if ( pin - > type = = COMMON_PIN ) {
pin - > reg = ( unsigned long ) sprd_pctl - > base +
PINCTRL_REG_OFFSET + PINCTRL_REG_LEN *
( i - ctrl_pin ) ;
com_pin + + ;
} else if ( pin - > type = = MISC_PIN ) {
pin - > reg = ( unsigned long ) sprd_pctl - > base +
PINCTRL_REG_MISC_OFFSET + PINCTRL_REG_LEN *
( i - ctrl_pin - com_pin ) ;
}
}
for ( i = 0 , pin = info - > pins ; i < info - > npins ; pin + + , i + + ) {
dev_dbg ( sprd_pctl - > dev , " pin name[%s-%d], type = %d, "
" bit offset = %ld, bit width = %ld, reg = 0x%lx \n " ,
pin - > name , pin - > number , pin - > type ,
pin - > bit_offset , pin - > bit_width , pin - > reg ) ;
}
return 0 ;
}
int sprd_pinctrl_core_probe ( struct platform_device * pdev ,
struct sprd_pins_info * sprd_soc_pin_info ,
int pins_cnt )
{
struct sprd_pinctrl * sprd_pctl ;
struct sprd_pinctrl_soc_info * pinctrl_info ;
struct pinctrl_pin_desc * pin_desc ;
struct resource * res ;
int ret , i ;
sprd_pctl = devm_kzalloc ( & pdev - > dev , sizeof ( struct sprd_pinctrl ) ,
GFP_KERNEL ) ;
if ( ! sprd_pctl )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
sprd_pctl - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( sprd_pctl - > base ) )
return PTR_ERR ( sprd_pctl - > base ) ;
pinctrl_info = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct sprd_pinctrl_soc_info ) ,
GFP_KERNEL ) ;
if ( ! pinctrl_info )
return - ENOMEM ;
sprd_pctl - > info = pinctrl_info ;
sprd_pctl - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , sprd_pctl ) ;
ret = sprd_pinctrl_add_pins ( sprd_pctl , sprd_soc_pin_info , pins_cnt ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " fail to add pins information \n " ) ;
return ret ;
}
pin_desc = devm_kzalloc ( & pdev - > dev , pinctrl_info - > npins *
sizeof ( struct pinctrl_pin_desc ) ,
GFP_KERNEL ) ;
if ( ! pin_desc )
return - ENOMEM ;
for ( i = 0 ; i < pinctrl_info - > npins ; i + + ) {
pin_desc [ i ] . number = pinctrl_info - > pins [ i ] . number ;
pin_desc [ i ] . name = pinctrl_info - > pins [ i ] . name ;
pin_desc [ i ] . drv_data = pinctrl_info ;
}
sprd_pinctrl_desc . pins = pin_desc ;
sprd_pinctrl_desc . name = dev_name ( & pdev - > dev ) ;
sprd_pinctrl_desc . npins = pinctrl_info - > npins ;
sprd_pctl - > pctl = pinctrl_register ( & sprd_pinctrl_desc ,
& pdev - > dev , ( void * ) sprd_pctl ) ;
if ( IS_ERR ( sprd_pctl - > pctl ) ) {
dev_err ( & pdev - > dev , " could not register pinctrl driver \n " ) ;
return PTR_ERR ( sprd_pctl - > pctl ) ;
}
ret = sprd_pinctrl_parse_dt ( sprd_pctl ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " fail to parse dt properties \n " ) ;
pinctrl_unregister ( sprd_pctl - > pctl ) ;
return ret ;
}
return 0 ;
}
int sprd_pinctrl_remove ( struct platform_device * pdev )
{
struct sprd_pinctrl * sprd_pctl = platform_get_drvdata ( pdev ) ;
pinctrl_unregister ( sprd_pctl - > pctl ) ;
return 0 ;
}
void sprd_pinctrl_shutdown ( struct platform_device * pdev )
{
2017-09-07 10:29:26 +03:00
struct pinctrl * pinctl ;
2017-08-17 14:50:38 +08:00
struct pinctrl_state * state ;
2017-09-07 10:29:26 +03:00
pinctl = devm_pinctrl_get ( & pdev - > dev ) ;
if ( IS_ERR ( pinctl ) )
return ;
2017-08-17 14:50:38 +08:00
state = pinctrl_lookup_state ( pinctl , " shutdown " ) ;
2017-09-07 10:29:26 +03:00
if ( IS_ERR ( state ) )
return ;
pinctrl_select_state ( pinctl , state ) ;
2017-08-17 14:50:38 +08:00
}
MODULE_DESCRIPTION ( " SPREADTRUM Pin Controller Driver " ) ;
MODULE_AUTHOR ( " Baolin Wang <baolin.wang@spreadtrum.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;