2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-10-22 13:58:47 +04:00
/*
* Copyright ( c ) 2012 - 2014 , The Linux Foundation . All rights reserved .
*/
2018-09-13 14:58:21 +03:00
# include <linux/gpio/driver.h>
2014-10-22 13:58:47 +04:00
# include <linux/module.h>
# include <linux/of.h>
2015-11-18 22:33:17 +03:00
# include <linux/of_irq.h>
2014-10-22 13:58:47 +04:00
# include <linux/pinctrl/pinconf-generic.h>
# include <linux/pinctrl/pinconf.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <dt-bindings/pinctrl/qcom,pmic-mpp.h>
# include "../core.h"
# include "../pinctrl-utils.h"
# define PMIC_MPP_ADDRESS_RANGE 0x100
/*
* Pull Up Values - it indicates whether a pull - up should be
* applied for bidirectional mode only . The hardware ignores the
* configuration when operating in other modes .
*/
# define PMIC_MPP_PULL_UP_0P6KOHM 0
# define PMIC_MPP_PULL_UP_10KOHM 1
# define PMIC_MPP_PULL_UP_30KOHM 2
# define PMIC_MPP_PULL_UP_OPEN 3
/* type registers base address bases */
# define PMIC_MPP_REG_TYPE 0x4
# define PMIC_MPP_REG_SUBTYPE 0x5
/* mpp peripheral type and subtype values */
# define PMIC_MPP_TYPE 0x11
# define PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT 0x3
# define PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT 0x4
# define PMIC_MPP_SUBTYPE_4CH_NO_SINK 0x5
# define PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK 0x6
# define PMIC_MPP_SUBTYPE_4CH_FULL_FUNC 0x7
# define PMIC_MPP_SUBTYPE_8CH_FULL_FUNC 0xf
# define PMIC_MPP_REG_RT_STS 0x10
# define PMIC_MPP_REG_RT_STS_VAL_MASK 0x1
/* control register base address bases */
# define PMIC_MPP_REG_MODE_CTL 0x40
# define PMIC_MPP_REG_DIG_VIN_CTL 0x41
# define PMIC_MPP_REG_DIG_PULL_CTL 0x42
# define PMIC_MPP_REG_DIG_IN_CTL 0x43
# define PMIC_MPP_REG_EN_CTL 0x46
2015-07-15 09:40:33 +03:00
# define PMIC_MPP_REG_AOUT_CTL 0x48
2014-10-22 13:58:47 +04:00
# define PMIC_MPP_REG_AIN_CTL 0x4a
2015-06-18 09:47:28 +03:00
# define PMIC_MPP_REG_SINK_CTL 0x4c
2014-10-22 13:58:47 +04:00
/* PMIC_MPP_REG_MODE_CTL */
# define PMIC_MPP_REG_MODE_VALUE_MASK 0x1
# define PMIC_MPP_REG_MODE_FUNCTION_SHIFT 1
# define PMIC_MPP_REG_MODE_FUNCTION_MASK 0x7
# define PMIC_MPP_REG_MODE_DIR_SHIFT 4
# define PMIC_MPP_REG_MODE_DIR_MASK 0x7
/* PMIC_MPP_REG_DIG_VIN_CTL */
# define PMIC_MPP_REG_VIN_SHIFT 0
# define PMIC_MPP_REG_VIN_MASK 0x7
/* PMIC_MPP_REG_DIG_PULL_CTL */
# define PMIC_MPP_REG_PULL_SHIFT 0
# define PMIC_MPP_REG_PULL_MASK 0x7
/* PMIC_MPP_REG_EN_CTL */
# define PMIC_MPP_REG_MASTER_EN_SHIFT 7
/* PMIC_MPP_REG_AIN_CTL */
# define PMIC_MPP_REG_AIN_ROUTE_SHIFT 0
# define PMIC_MPP_REG_AIN_ROUTE_MASK 0x7
2015-06-18 09:47:27 +03:00
# define PMIC_MPP_MODE_DIGITAL_INPUT 0
# define PMIC_MPP_MODE_DIGITAL_OUTPUT 1
# define PMIC_MPP_MODE_DIGITAL_BIDIR 2
# define PMIC_MPP_MODE_ANALOG_BIDIR 3
# define PMIC_MPP_MODE_ANALOG_INPUT 4
# define PMIC_MPP_MODE_ANALOG_OUTPUT 5
# define PMIC_MPP_MODE_CURRENT_SINK 6
2015-06-18 09:47:30 +03:00
# define PMIC_MPP_SELECTOR_NORMAL 0
# define PMIC_MPP_SELECTOR_PAIRED 1
# define PMIC_MPP_SELECTOR_DTEST_FIRST 4
2014-10-22 13:58:47 +04:00
# define PMIC_MPP_PHYSICAL_OFFSET 1
/* Qualcomm specific pin configurations */
# define PMIC_MPP_CONF_AMUX_ROUTE (PIN_CONFIG_END + 1)
2015-06-18 09:47:30 +03:00
# define PMIC_MPP_CONF_ANALOG_LEVEL (PIN_CONFIG_END + 2)
# define PMIC_MPP_CONF_DTEST_SELECTOR (PIN_CONFIG_END + 3)
# define PMIC_MPP_CONF_PAIRED (PIN_CONFIG_END + 4)
2014-10-22 13:58:47 +04:00
/**
* struct pmic_mpp_pad - keep current MPP settings
* @ base : Address base in SPMI device .
* @ irq : IRQ number which this MPP generate .
* @ is_enabled : Set to false when MPP should be put in high Z state .
* @ out_value : Cached pin output value .
* @ output_enabled : Set to true if MPP output logic is enabled .
* @ input_enabled : Set to true if MPP input buffer logic is enabled .
2015-06-18 09:47:30 +03:00
* @ paired : Pin operates in paired mode
2016-01-08 23:25:53 +03:00
* @ has_pullup : Pin has support to configure pullup
2014-10-22 13:58:47 +04:00
* @ num_sources : Number of power - sources supported by this MPP .
* @ power_source : Current power - source used .
* @ amux_input : Set the source for analog input .
2015-07-15 09:40:33 +03:00
* @ aout_level : Analog output level
2014-10-22 13:58:47 +04:00
* @ pullup : Pullup resistor value . Valid in Bidirectional mode only .
* @ function : See pmic_mpp_functions [ ] .
2015-06-18 09:47:28 +03:00
* @ drive_strength : Amount of current in sink mode
2015-06-18 09:47:30 +03:00
* @ dtest : DTEST route selector
2014-10-22 13:58:47 +04:00
*/
struct pmic_mpp_pad {
u16 base ;
int irq ;
bool is_enabled ;
bool out_value ;
bool output_enabled ;
bool input_enabled ;
2015-06-18 09:47:30 +03:00
bool paired ;
2016-01-08 23:25:53 +03:00
bool has_pullup ;
2014-10-22 13:58:47 +04:00
unsigned int num_sources ;
unsigned int power_source ;
unsigned int amux_input ;
2015-07-15 09:40:33 +03:00
unsigned int aout_level ;
2014-10-22 13:58:47 +04:00
unsigned int pullup ;
unsigned int function ;
2015-06-18 09:47:28 +03:00
unsigned int drive_strength ;
2015-06-18 09:47:30 +03:00
unsigned int dtest ;
2014-10-22 13:58:47 +04:00
} ;
struct pmic_mpp_state {
struct device * dev ;
struct regmap * map ;
struct pinctrl_dev * ctrl ;
struct gpio_chip chip ;
} ;
2015-06-18 09:47:25 +03:00
static const struct pinconf_generic_params pmic_mpp_bindings [ ] = {
{ " qcom,amux-route " , PMIC_MPP_CONF_AMUX_ROUTE , 0 } ,
2015-07-15 09:40:33 +03:00
{ " qcom,analog-level " , PMIC_MPP_CONF_ANALOG_LEVEL , 0 } ,
2015-06-18 09:47:30 +03:00
{ " qcom,dtest " , PMIC_MPP_CONF_DTEST_SELECTOR , 0 } ,
{ " qcom,paired " , PMIC_MPP_CONF_PAIRED , 0 } ,
2014-10-22 13:58:47 +04:00
} ;
2015-06-18 09:47:25 +03:00
# ifdef CONFIG_DEBUG_FS
static const struct pin_config_item pmic_conf_items [ ] = {
PCONFDUMP ( PMIC_MPP_CONF_AMUX_ROUTE , " analog mux " , NULL , true ) ,
2015-07-15 09:40:33 +03:00
PCONFDUMP ( PMIC_MPP_CONF_ANALOG_LEVEL , " analog level " , NULL , true ) ,
2015-06-18 09:47:30 +03:00
PCONFDUMP ( PMIC_MPP_CONF_DTEST_SELECTOR , " dtest " , NULL , true ) ,
PCONFDUMP ( PMIC_MPP_CONF_PAIRED , " paired " , NULL , false ) ,
2014-10-22 13:58:47 +04:00
} ;
2015-06-18 09:47:25 +03:00
# endif
2014-10-22 13:58:47 +04:00
static const char * const pmic_mpp_groups [ ] = {
" mpp1 " , " mpp2 " , " mpp3 " , " mpp4 " , " mpp5 " , " mpp6 " , " mpp7 " , " mpp8 " ,
} ;
2015-06-18 09:47:30 +03:00
# define PMIC_MPP_DIGITAL 0
# define PMIC_MPP_ANALOG 1
# define PMIC_MPP_SINK 2
2014-10-22 13:58:47 +04:00
static const char * const pmic_mpp_functions [ ] = {
2015-06-18 09:47:30 +03:00
" digital " , " analog " , " sink "
2014-10-22 13:58:47 +04:00
} ;
static int pmic_mpp_read ( struct pmic_mpp_state * state ,
struct pmic_mpp_pad * pad , unsigned int addr )
{
unsigned int val ;
int ret ;
ret = regmap_read ( state - > map , pad - > base + addr , & val ) ;
if ( ret < 0 )
dev_err ( state - > dev , " read 0x%x failed \n " , addr ) ;
else
ret = val ;
return ret ;
}
static int pmic_mpp_write ( struct pmic_mpp_state * state ,
struct pmic_mpp_pad * pad , unsigned int addr ,
unsigned int val )
{
int ret ;
ret = regmap_write ( state - > map , pad - > base + addr , val ) ;
if ( ret < 0 )
dev_err ( state - > dev , " write 0x%x failed \n " , addr ) ;
return ret ;
}
static int pmic_mpp_get_groups_count ( struct pinctrl_dev * pctldev )
{
/* Every PIN is a group */
return pctldev - > desc - > npins ;
}
static const char * pmic_mpp_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned pin )
{
return pctldev - > desc - > pins [ pin ] . name ;
}
static int pmic_mpp_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned pin ,
const unsigned * * pins , unsigned * num_pins )
{
* pins = & pctldev - > desc - > pins [ pin ] . number ;
* num_pins = 1 ;
return 0 ;
}
static const struct pinctrl_ops pmic_mpp_pinctrl_ops = {
. get_groups_count = pmic_mpp_get_groups_count ,
. get_group_name = pmic_mpp_get_group_name ,
. get_group_pins = pmic_mpp_get_group_pins ,
2015-06-18 09:47:25 +03:00
. dt_node_to_map = pinconf_generic_dt_node_to_map_group ,
2016-03-31 14:44:42 +03:00
. dt_free_map = pinctrl_utils_free_map ,
2014-10-22 13:58:47 +04:00
} ;
static int pmic_mpp_get_functions_count ( struct pinctrl_dev * pctldev )
{
return ARRAY_SIZE ( pmic_mpp_functions ) ;
}
static const char * pmic_mpp_get_function_name ( struct pinctrl_dev * pctldev ,
unsigned function )
{
return pmic_mpp_functions [ function ] ;
}
static int pmic_mpp_get_function_groups ( struct pinctrl_dev * pctldev ,
unsigned function ,
const char * const * * groups ,
unsigned * const num_qgroups )
{
* groups = pmic_mpp_groups ;
* num_qgroups = pctldev - > desc - > npins ;
return 0 ;
}
2015-06-18 09:47:28 +03:00
static int pmic_mpp_write_mode_ctl ( struct pmic_mpp_state * state ,
struct pmic_mpp_pad * pad )
2014-10-22 13:58:47 +04:00
{
2015-06-18 09:47:30 +03:00
unsigned int mode ;
unsigned int sel ;
2014-10-22 13:58:47 +04:00
unsigned int val ;
2015-06-18 09:47:30 +03:00
unsigned int en ;
switch ( pad - > function ) {
case PMIC_MPP_ANALOG :
if ( pad - > input_enabled & & pad - > output_enabled )
mode = PMIC_MPP_MODE_ANALOG_BIDIR ;
else if ( pad - > input_enabled )
mode = PMIC_MPP_MODE_ANALOG_INPUT ;
else
mode = PMIC_MPP_MODE_ANALOG_OUTPUT ;
break ;
case PMIC_MPP_DIGITAL :
if ( pad - > input_enabled & & pad - > output_enabled )
mode = PMIC_MPP_MODE_DIGITAL_BIDIR ;
else if ( pad - > input_enabled )
mode = PMIC_MPP_MODE_DIGITAL_INPUT ;
else
mode = PMIC_MPP_MODE_DIGITAL_OUTPUT ;
break ;
case PMIC_MPP_SINK :
default :
mode = PMIC_MPP_MODE_CURRENT_SINK ;
break ;
2014-10-22 13:58:47 +04:00
}
2015-06-18 09:47:30 +03:00
if ( pad - > dtest )
sel = PMIC_MPP_SELECTOR_DTEST_FIRST + pad - > dtest - 1 ;
else if ( pad - > paired )
sel = PMIC_MPP_SELECTOR_PAIRED ;
else
sel = PMIC_MPP_SELECTOR_NORMAL ;
en = ! ! pad - > out_value ;
val = mode < < PMIC_MPP_REG_MODE_DIR_SHIFT |
sel < < PMIC_MPP_REG_MODE_FUNCTION_SHIFT |
en ;
2014-10-22 13:58:47 +04:00
2015-06-18 09:47:28 +03:00
return pmic_mpp_write ( state , pad , PMIC_MPP_REG_MODE_CTL , val ) ;
}
static int pmic_mpp_set_mux ( struct pinctrl_dev * pctldev , unsigned function ,
unsigned pin )
{
struct pmic_mpp_state * state = pinctrl_dev_get_drvdata ( pctldev ) ;
struct pmic_mpp_pad * pad ;
unsigned int val ;
int ret ;
pad = pctldev - > desc - > pins [ pin ] . drv_data ;
pad - > function = function ;
ret = pmic_mpp_write_mode_ctl ( state , pad ) ;
2018-09-20 04:58:18 +03:00
if ( ret < 0 )
return ret ;
2014-10-22 13:58:47 +04:00
val = pad - > is_enabled < < PMIC_MPP_REG_MASTER_EN_SHIFT ;
return pmic_mpp_write ( state , pad , PMIC_MPP_REG_EN_CTL , val ) ;
}
static const struct pinmux_ops pmic_mpp_pinmux_ops = {
. get_functions_count = pmic_mpp_get_functions_count ,
. get_function_name = pmic_mpp_get_function_name ,
. get_function_groups = pmic_mpp_get_function_groups ,
. set_mux = pmic_mpp_set_mux ,
} ;
static int pmic_mpp_config_get ( struct pinctrl_dev * pctldev ,
unsigned int pin , unsigned long * config )
{
unsigned param = pinconf_to_config_param ( * config ) ;
struct pmic_mpp_pad * pad ;
unsigned arg = 0 ;
pad = pctldev - > desc - > pins [ pin ] . drv_data ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
2018-08-30 18:23:39 +03:00
if ( pad - > pullup ! = PMIC_MPP_PULL_UP_OPEN )
return - EINVAL ;
arg = 1 ;
2014-10-22 13:58:47 +04:00
break ;
case PIN_CONFIG_BIAS_PULL_UP :
switch ( pad - > pullup ) {
case PMIC_MPP_PULL_UP_0P6KOHM :
arg = 600 ;
break ;
case PMIC_MPP_PULL_UP_10KOHM :
arg = 10000 ;
break ;
case PMIC_MPP_PULL_UP_30KOHM :
arg = 30000 ;
break ;
default :
return - EINVAL ;
}
break ;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE :
2018-08-30 18:23:39 +03:00
if ( pad - > is_enabled )
return - EINVAL ;
arg = 1 ;
2014-10-22 13:58:47 +04:00
break ;
case PIN_CONFIG_POWER_SOURCE :
arg = pad - > power_source ;
break ;
case PIN_CONFIG_INPUT_ENABLE :
2018-08-30 18:23:39 +03:00
if ( ! pad - > input_enabled )
return - EINVAL ;
arg = 1 ;
2014-10-22 13:58:47 +04:00
break ;
case PIN_CONFIG_OUTPUT :
arg = pad - > out_value ;
break ;
2015-06-18 09:47:30 +03:00
case PMIC_MPP_CONF_DTEST_SELECTOR :
arg = pad - > dtest ;
break ;
2014-10-22 13:58:47 +04:00
case PMIC_MPP_CONF_AMUX_ROUTE :
arg = pad - > amux_input ;
break ;
2015-06-18 09:47:30 +03:00
case PMIC_MPP_CONF_PAIRED :
2018-08-30 18:23:39 +03:00
if ( ! pad - > paired )
return - EINVAL ;
arg = 1 ;
2015-06-18 09:47:30 +03:00
break ;
2015-06-18 09:47:28 +03:00
case PIN_CONFIG_DRIVE_STRENGTH :
arg = pad - > drive_strength ;
break ;
2015-07-15 09:40:33 +03:00
case PMIC_MPP_CONF_ANALOG_LEVEL :
arg = pad - > aout_level ;
break ;
2014-10-22 13:58:47 +04:00
default :
return - EINVAL ;
}
/* Convert register value to pinconf value */
* config = pinconf_to_config_packed ( param , arg ) ;
return 0 ;
}
static int pmic_mpp_config_set ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long * configs , unsigned nconfs )
{
struct pmic_mpp_state * state = pinctrl_dev_get_drvdata ( pctldev ) ;
struct pmic_mpp_pad * pad ;
unsigned param , arg ;
unsigned int val ;
int i , ret ;
pad = pctldev - > desc - > pins [ pin ] . drv_data ;
2015-06-18 09:47:26 +03:00
/* Make it possible to enable the pin, by not setting high impedance */
pad - > is_enabled = true ;
2014-10-22 13:58:47 +04:00
for ( i = 0 ; i < nconfs ; i + + ) {
param = pinconf_to_config_param ( configs [ i ] ) ;
arg = pinconf_to_config_argument ( configs [ i ] ) ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
pad - > pullup = PMIC_MPP_PULL_UP_OPEN ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
switch ( arg ) {
case 600 :
pad - > pullup = PMIC_MPP_PULL_UP_0P6KOHM ;
break ;
case 10000 :
pad - > pullup = PMIC_MPP_PULL_UP_10KOHM ;
break ;
case 30000 :
pad - > pullup = PMIC_MPP_PULL_UP_30KOHM ;
break ;
default :
return - EINVAL ;
}
break ;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE :
pad - > is_enabled = false ;
break ;
case PIN_CONFIG_POWER_SOURCE :
if ( arg > = pad - > num_sources )
return - EINVAL ;
pad - > power_source = arg ;
break ;
case PIN_CONFIG_INPUT_ENABLE :
pad - > input_enabled = arg ? true : false ;
break ;
case PIN_CONFIG_OUTPUT :
pad - > output_enabled = true ;
pad - > out_value = arg ;
break ;
2015-06-18 09:47:30 +03:00
case PMIC_MPP_CONF_DTEST_SELECTOR :
pad - > dtest = arg ;
break ;
2015-06-18 09:47:28 +03:00
case PIN_CONFIG_DRIVE_STRENGTH :
2018-08-31 03:58:52 +03:00
pad - > drive_strength = arg ;
2015-06-18 09:47:28 +03:00
break ;
2014-10-22 13:58:47 +04:00
case PMIC_MPP_CONF_AMUX_ROUTE :
if ( arg > = PMIC_MPP_AMUX_ROUTE_ABUS4 )
return - EINVAL ;
pad - > amux_input = arg ;
break ;
2015-07-15 09:40:33 +03:00
case PMIC_MPP_CONF_ANALOG_LEVEL :
pad - > aout_level = arg ;
break ;
2015-06-18 09:47:30 +03:00
case PMIC_MPP_CONF_PAIRED :
pad - > paired = ! ! arg ;
2014-10-22 13:58:47 +04:00
break ;
default :
return - EINVAL ;
}
}
val = pad - > power_source < < PMIC_MPP_REG_VIN_SHIFT ;
ret = pmic_mpp_write ( state , pad , PMIC_MPP_REG_DIG_VIN_CTL , val ) ;
if ( ret < 0 )
return ret ;
2016-01-08 23:25:53 +03:00
if ( pad - > has_pullup ) {
val = pad - > pullup < < PMIC_MPP_REG_PULL_SHIFT ;
2014-10-22 13:58:47 +04:00
2016-01-08 23:25:53 +03:00
ret = pmic_mpp_write ( state , pad , PMIC_MPP_REG_DIG_PULL_CTL ,
val ) ;
if ( ret < 0 )
return ret ;
}
2014-10-22 13:58:47 +04:00
val = pad - > amux_input & PMIC_MPP_REG_AIN_ROUTE_MASK ;
ret = pmic_mpp_write ( state , pad , PMIC_MPP_REG_AIN_CTL , val ) ;
if ( ret < 0 )
return ret ;
2015-07-15 09:40:33 +03:00
ret = pmic_mpp_write ( state , pad , PMIC_MPP_REG_AOUT_CTL , pad - > aout_level ) ;
if ( ret < 0 )
return ret ;
2015-06-18 09:47:28 +03:00
ret = pmic_mpp_write_mode_ctl ( state , pad ) ;
2015-06-18 09:47:26 +03:00
if ( ret < 0 )
return ret ;
2018-08-31 03:58:52 +03:00
ret = pmic_mpp_write ( state , pad , PMIC_MPP_REG_SINK_CTL , pad - > drive_strength ) ;
if ( ret < 0 )
return ret ;
2015-06-18 09:47:26 +03:00
val = pad - > is_enabled < < PMIC_MPP_REG_MASTER_EN_SHIFT ;
return pmic_mpp_write ( state , pad , PMIC_MPP_REG_EN_CTL , val ) ;
2014-10-22 13:58:47 +04:00
}
static void pmic_mpp_config_dbg_show ( struct pinctrl_dev * pctldev ,
struct seq_file * s , unsigned pin )
{
struct pmic_mpp_state * state = pinctrl_dev_get_drvdata ( pctldev ) ;
struct pmic_mpp_pad * pad ;
2015-06-18 09:47:26 +03:00
int ret ;
2014-10-22 13:58:47 +04:00
static const char * const biases [ ] = {
" 0.6kOhm " , " 10kOhm " , " 30kOhm " , " Disabled "
} ;
pad = pctldev - > desc - > pins [ pin ] . drv_data ;
seq_printf ( s , " mpp%-2d: " , pin + PMIC_MPP_PHYSICAL_OFFSET ) ;
2015-06-18 09:47:26 +03:00
if ( ! pad - > is_enabled ) {
2014-10-22 13:58:47 +04:00
seq_puts ( s , " --- " ) ;
} else {
if ( pad - > input_enabled ) {
ret = pmic_mpp_read ( state , pad , PMIC_MPP_REG_RT_STS ) ;
2015-04-09 18:18:37 +03:00
if ( ret < 0 )
return ;
ret & = PMIC_MPP_REG_RT_STS_VAL_MASK ;
pad - > out_value = ret ;
2014-10-22 13:58:47 +04:00
}
seq_printf ( s , " %-4s " , pad - > output_enabled ? " out " : " in " ) ;
seq_printf ( s , " %-7s " , pmic_mpp_functions [ pad - > function ] ) ;
seq_printf ( s , " vin-%d " , pad - > power_source ) ;
2015-07-15 09:40:33 +03:00
seq_printf ( s , " %d " , pad - > aout_level ) ;
2016-01-08 23:25:53 +03:00
if ( pad - > has_pullup )
seq_printf ( s , " %-8s " , biases [ pad - > pullup ] ) ;
2014-10-22 13:58:47 +04:00
seq_printf ( s , " %-4s " , pad - > out_value ? " high " : " low " ) ;
2015-06-18 09:47:30 +03:00
if ( pad - > dtest )
seq_printf ( s , " dtest%d " , pad - > dtest ) ;
if ( pad - > paired )
seq_puts ( s , " paired " ) ;
2014-10-22 13:58:47 +04:00
}
}
static const struct pinconf_ops pmic_mpp_pinconf_ops = {
2015-06-18 09:47:25 +03:00
. is_generic = true ,
2014-10-22 13:58:47 +04:00
. pin_config_group_get = pmic_mpp_config_get ,
. pin_config_group_set = pmic_mpp_config_set ,
. pin_config_group_dbg_show = pmic_mpp_config_dbg_show ,
} ;
static int pmic_mpp_direction_input ( struct gpio_chip * chip , unsigned pin )
{
2015-12-08 11:53:39 +03:00
struct pmic_mpp_state * state = gpiochip_get_data ( chip ) ;
2014-10-22 13:58:47 +04:00
unsigned long config ;
config = pinconf_to_config_packed ( PIN_CONFIG_INPUT_ENABLE , 1 ) ;
return pmic_mpp_config_set ( state - > ctrl , pin , & config , 1 ) ;
}
static int pmic_mpp_direction_output ( struct gpio_chip * chip ,
unsigned pin , int val )
{
2015-12-08 11:53:39 +03:00
struct pmic_mpp_state * state = gpiochip_get_data ( chip ) ;
2014-10-22 13:58:47 +04:00
unsigned long config ;
config = pinconf_to_config_packed ( PIN_CONFIG_OUTPUT , val ) ;
return pmic_mpp_config_set ( state - > ctrl , pin , & config , 1 ) ;
}
static int pmic_mpp_get ( struct gpio_chip * chip , unsigned pin )
{
2015-12-08 11:53:39 +03:00
struct pmic_mpp_state * state = gpiochip_get_data ( chip ) ;
2014-10-22 13:58:47 +04:00
struct pmic_mpp_pad * pad ;
int ret ;
pad = state - > ctrl - > desc - > pins [ pin ] . drv_data ;
if ( pad - > input_enabled ) {
ret = pmic_mpp_read ( state , pad , PMIC_MPP_REG_RT_STS ) ;
if ( ret < 0 )
return ret ;
pad - > out_value = ret & PMIC_MPP_REG_RT_STS_VAL_MASK ;
}
2015-12-21 18:31:06 +03:00
return ! ! pad - > out_value ;
2014-10-22 13:58:47 +04:00
}
static void pmic_mpp_set ( struct gpio_chip * chip , unsigned pin , int value )
{
2015-12-08 11:53:39 +03:00
struct pmic_mpp_state * state = gpiochip_get_data ( chip ) ;
2014-10-22 13:58:47 +04:00
unsigned long config ;
config = pinconf_to_config_packed ( PIN_CONFIG_OUTPUT , value ) ;
pmic_mpp_config_set ( state - > ctrl , pin , & config , 1 ) ;
}
static int pmic_mpp_of_xlate ( struct gpio_chip * chip ,
const struct of_phandle_args * gpio_desc ,
u32 * flags )
{
if ( chip - > of_gpio_n_cells < 2 )
return - EINVAL ;
if ( flags )
* flags = gpio_desc - > args [ 1 ] ;
return gpio_desc - > args [ 0 ] - PMIC_MPP_PHYSICAL_OFFSET ;
}
static int pmic_mpp_to_irq ( struct gpio_chip * chip , unsigned pin )
{
2015-12-08 11:53:39 +03:00
struct pmic_mpp_state * state = gpiochip_get_data ( chip ) ;
2014-10-22 13:58:47 +04:00
struct pmic_mpp_pad * pad ;
pad = state - > ctrl - > desc - > pins [ pin ] . drv_data ;
return pad - > irq ;
}
static void pmic_mpp_dbg_show ( struct seq_file * s , struct gpio_chip * chip )
{
2015-12-08 11:53:39 +03:00
struct pmic_mpp_state * state = gpiochip_get_data ( chip ) ;
2014-10-22 13:58:47 +04:00
unsigned i ;
for ( i = 0 ; i < chip - > ngpio ; i + + ) {
pmic_mpp_config_dbg_show ( state - > ctrl , s , i ) ;
seq_puts ( s , " \n " ) ;
}
}
static const struct gpio_chip pmic_mpp_gpio_template = {
. direction_input = pmic_mpp_direction_input ,
. direction_output = pmic_mpp_direction_output ,
. get = pmic_mpp_get ,
. set = pmic_mpp_set ,
2015-10-11 18:34:19 +03:00
. request = gpiochip_generic_request ,
. free = gpiochip_generic_free ,
2014-10-22 13:58:47 +04:00
. of_xlate = pmic_mpp_of_xlate ,
. to_irq = pmic_mpp_to_irq ,
. dbg_show = pmic_mpp_dbg_show ,
} ;
static int pmic_mpp_populate ( struct pmic_mpp_state * state ,
struct pmic_mpp_pad * pad )
{
int type , subtype , val , dir ;
2015-06-18 09:47:30 +03:00
unsigned int sel ;
2014-10-22 13:58:47 +04:00
type = pmic_mpp_read ( state , pad , PMIC_MPP_REG_TYPE ) ;
if ( type < 0 )
return type ;
if ( type ! = PMIC_MPP_TYPE ) {
dev_err ( state - > dev , " incorrect block type 0x%x at 0x%x \n " ,
type , pad - > base ) ;
return - ENODEV ;
}
subtype = pmic_mpp_read ( state , pad , PMIC_MPP_REG_SUBTYPE ) ;
if ( subtype < 0 )
return subtype ;
switch ( subtype ) {
case PMIC_MPP_SUBTYPE_4CH_NO_ANA_OUT :
case PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT :
case PMIC_MPP_SUBTYPE_4CH_NO_SINK :
case PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK :
case PMIC_MPP_SUBTYPE_4CH_FULL_FUNC :
pad - > num_sources = 4 ;
break ;
case PMIC_MPP_SUBTYPE_8CH_FULL_FUNC :
pad - > num_sources = 8 ;
break ;
default :
dev_err ( state - > dev , " unknown MPP type 0x%x at 0x%x \n " ,
subtype , pad - > base ) ;
return - ENODEV ;
}
val = pmic_mpp_read ( state , pad , PMIC_MPP_REG_MODE_CTL ) ;
if ( val < 0 )
return val ;
pad - > out_value = val & PMIC_MPP_REG_MODE_VALUE_MASK ;
dir = val > > PMIC_MPP_REG_MODE_DIR_SHIFT ;
dir & = PMIC_MPP_REG_MODE_DIR_MASK ;
switch ( dir ) {
2015-06-18 09:47:27 +03:00
case PMIC_MPP_MODE_DIGITAL_INPUT :
2014-10-22 13:58:47 +04:00
pad - > input_enabled = true ;
pad - > output_enabled = false ;
2015-06-18 09:47:30 +03:00
pad - > function = PMIC_MPP_DIGITAL ;
2014-10-22 13:58:47 +04:00
break ;
2015-06-18 09:47:27 +03:00
case PMIC_MPP_MODE_DIGITAL_OUTPUT :
2014-10-22 13:58:47 +04:00
pad - > input_enabled = false ;
pad - > output_enabled = true ;
2015-06-18 09:47:30 +03:00
pad - > function = PMIC_MPP_DIGITAL ;
2014-10-22 13:58:47 +04:00
break ;
2015-06-18 09:47:27 +03:00
case PMIC_MPP_MODE_DIGITAL_BIDIR :
2014-10-22 13:58:47 +04:00
pad - > input_enabled = true ;
pad - > output_enabled = true ;
2015-06-18 09:47:30 +03:00
pad - > function = PMIC_MPP_DIGITAL ;
2014-10-22 13:58:47 +04:00
break ;
2015-06-18 09:47:27 +03:00
case PMIC_MPP_MODE_ANALOG_BIDIR :
2014-10-22 13:58:47 +04:00
pad - > input_enabled = true ;
pad - > output_enabled = true ;
2015-06-18 09:47:30 +03:00
pad - > function = PMIC_MPP_ANALOG ;
2014-10-22 13:58:47 +04:00
break ;
2015-06-18 09:47:27 +03:00
case PMIC_MPP_MODE_ANALOG_INPUT :
2014-10-22 13:58:47 +04:00
pad - > input_enabled = true ;
pad - > output_enabled = false ;
2015-06-18 09:47:30 +03:00
pad - > function = PMIC_MPP_ANALOG ;
2014-10-22 13:58:47 +04:00
break ;
2015-06-18 09:47:27 +03:00
case PMIC_MPP_MODE_ANALOG_OUTPUT :
2014-10-22 13:58:47 +04:00
pad - > input_enabled = false ;
pad - > output_enabled = true ;
2015-06-18 09:47:30 +03:00
pad - > function = PMIC_MPP_ANALOG ;
2015-06-18 09:47:28 +03:00
break ;
case PMIC_MPP_MODE_CURRENT_SINK :
pad - > input_enabled = false ;
pad - > output_enabled = true ;
2015-06-18 09:47:30 +03:00
pad - > function = PMIC_MPP_SINK ;
2014-10-22 13:58:47 +04:00
break ;
default :
dev_err ( state - > dev , " unknown MPP direction \n " ) ;
return - ENODEV ;
}
2015-06-18 09:47:30 +03:00
sel = val > > PMIC_MPP_REG_MODE_FUNCTION_SHIFT ;
sel & = PMIC_MPP_REG_MODE_FUNCTION_MASK ;
if ( sel > = PMIC_MPP_SELECTOR_DTEST_FIRST )
pad - > dtest = sel + 1 ;
else if ( sel = = PMIC_MPP_SELECTOR_PAIRED )
pad - > paired = true ;
2014-10-22 13:58:47 +04:00
val = pmic_mpp_read ( state , pad , PMIC_MPP_REG_DIG_VIN_CTL ) ;
if ( val < 0 )
return val ;
pad - > power_source = val > > PMIC_MPP_REG_VIN_SHIFT ;
pad - > power_source & = PMIC_MPP_REG_VIN_MASK ;
2016-01-08 23:25:53 +03:00
if ( subtype ! = PMIC_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT & &
subtype ! = PMIC_MPP_SUBTYPE_ULT_4CH_NO_SINK ) {
val = pmic_mpp_read ( state , pad , PMIC_MPP_REG_DIG_PULL_CTL ) ;
if ( val < 0 )
return val ;
2014-10-22 13:58:47 +04:00
2016-01-08 23:25:53 +03:00
pad - > pullup = val > > PMIC_MPP_REG_PULL_SHIFT ;
pad - > pullup & = PMIC_MPP_REG_PULL_MASK ;
pad - > has_pullup = true ;
}
2014-10-22 13:58:47 +04:00
val = pmic_mpp_read ( state , pad , PMIC_MPP_REG_AIN_CTL ) ;
if ( val < 0 )
return val ;
pad - > amux_input = val > > PMIC_MPP_REG_AIN_ROUTE_SHIFT ;
pad - > amux_input & = PMIC_MPP_REG_AIN_ROUTE_MASK ;
2015-06-18 09:47:28 +03:00
val = pmic_mpp_read ( state , pad , PMIC_MPP_REG_SINK_CTL ) ;
if ( val < 0 )
return val ;
pad - > drive_strength = val ;
2015-07-15 09:40:33 +03:00
val = pmic_mpp_read ( state , pad , PMIC_MPP_REG_AOUT_CTL ) ;
if ( val < 0 )
return val ;
pad - > aout_level = val ;
2015-06-18 09:47:26 +03:00
val = pmic_mpp_read ( state , pad , PMIC_MPP_REG_EN_CTL ) ;
if ( val < 0 )
return val ;
pad - > is_enabled = ! ! val ;
2014-10-22 13:58:47 +04:00
return 0 ;
}
static int pmic_mpp_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct pinctrl_pin_desc * pindesc ;
struct pinctrl_desc * pctrldesc ;
struct pmic_mpp_pad * pad , * pads ;
struct pmic_mpp_state * state ;
int ret , npins , i ;
2015-11-18 22:33:17 +03:00
u32 reg ;
2014-10-22 13:58:47 +04:00
2015-11-18 22:33:17 +03:00
ret = of_property_read_u32 ( dev - > of_node , " reg " , & reg ) ;
2014-10-22 13:58:47 +04:00
if ( ret < 0 ) {
2015-11-18 22:33:17 +03:00
dev_err ( dev , " missing base address " ) ;
2014-10-22 13:58:47 +04:00
return ret ;
}
2016-01-07 04:37:41 +03:00
npins = platform_irq_count ( pdev ) ;
2014-10-22 13:58:47 +04:00
if ( ! npins )
return - EINVAL ;
2016-01-07 04:37:41 +03:00
if ( npins < 0 )
return npins ;
2014-10-22 13:58:47 +04:00
BUG_ON ( npins > ARRAY_SIZE ( pmic_mpp_groups ) ) ;
state = devm_kzalloc ( dev , sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
return - ENOMEM ;
platform_set_drvdata ( pdev , state ) ;
state - > dev = & pdev - > dev ;
state - > map = dev_get_regmap ( dev - > parent , NULL ) ;
pindesc = devm_kcalloc ( dev , npins , sizeof ( * pindesc ) , GFP_KERNEL ) ;
if ( ! pindesc )
return - ENOMEM ;
pads = devm_kcalloc ( dev , npins , sizeof ( * pads ) , GFP_KERNEL ) ;
if ( ! pads )
return - ENOMEM ;
pctrldesc = devm_kzalloc ( dev , sizeof ( * pctrldesc ) , GFP_KERNEL ) ;
if ( ! pctrldesc )
return - ENOMEM ;
pctrldesc - > pctlops = & pmic_mpp_pinctrl_ops ;
pctrldesc - > pmxops = & pmic_mpp_pinmux_ops ;
pctrldesc - > confops = & pmic_mpp_pinconf_ops ;
pctrldesc - > owner = THIS_MODULE ;
pctrldesc - > name = dev_name ( dev ) ;
pctrldesc - > pins = pindesc ;
pctrldesc - > npins = npins ;
2015-06-18 09:47:25 +03:00
pctrldesc - > num_custom_params = ARRAY_SIZE ( pmic_mpp_bindings ) ;
pctrldesc - > custom_params = pmic_mpp_bindings ;
# ifdef CONFIG_DEBUG_FS
pctrldesc - > custom_conf_items = pmic_conf_items ;
# endif
2014-10-22 13:58:47 +04:00
for ( i = 0 ; i < npins ; i + + , pindesc + + ) {
pad = & pads [ i ] ;
pindesc - > drv_data = pad ;
pindesc - > number = i ;
pindesc - > name = pmic_mpp_groups [ i ] ;
pad - > irq = platform_get_irq ( pdev , i ) ;
if ( pad - > irq < 0 )
return pad - > irq ;
2015-11-18 22:33:17 +03:00
pad - > base = reg + i * PMIC_MPP_ADDRESS_RANGE ;
2014-10-22 13:58:47 +04:00
ret = pmic_mpp_populate ( state , pad ) ;
if ( ret < 0 )
return ret ;
}
state - > chip = pmic_mpp_gpio_template ;
2015-11-04 11:56:26 +03:00
state - > chip . parent = dev ;
2014-10-22 13:58:47 +04:00
state - > chip . base = - 1 ;
state - > chip . ngpio = npins ;
state - > chip . label = dev_name ( dev ) ;
state - > chip . of_gpio_n_cells = 2 ;
state - > chip . can_sleep = false ;
2016-02-24 12:14:07 +03:00
state - > ctrl = devm_pinctrl_register ( dev , pctrldesc , state ) ;
2015-06-09 07:01:16 +03:00
if ( IS_ERR ( state - > ctrl ) )
return PTR_ERR ( state - > ctrl ) ;
2014-10-22 13:58:47 +04:00
2015-12-08 11:53:39 +03:00
ret = gpiochip_add_data ( & state - > chip , state ) ;
2014-10-22 13:58:47 +04:00
if ( ret ) {
dev_err ( state - > dev , " can't add gpio chip \n " ) ;
2016-02-24 12:14:07 +03:00
return ret ;
2014-10-22 13:58:47 +04:00
}
ret = gpiochip_add_pin_range ( & state - > chip , dev_name ( dev ) , 0 , 0 , npins ) ;
if ( ret ) {
dev_err ( dev , " failed to add pin range \n " ) ;
goto err_range ;
}
return 0 ;
err_range :
gpiochip_remove ( & state - > chip ) ;
return ret ;
}
static int pmic_mpp_remove ( struct platform_device * pdev )
{
struct pmic_mpp_state * state = platform_get_drvdata ( pdev ) ;
gpiochip_remove ( & state - > chip ) ;
return 0 ;
}
static const struct of_device_id pmic_mpp_of_match [ ] = {
2021-01-15 20:11:14 +03:00
{ . compatible = " qcom,pm8019-mpp " } , /* 6 MPP's */
2014-10-22 13:58:47 +04:00
{ . compatible = " qcom,pm8841-mpp " } , /* 4 MPP's */
2015-03-31 12:37:18 +03:00
{ . compatible = " qcom,pm8916-mpp " } , /* 4 MPP's */
2014-10-22 13:58:47 +04:00
{ . compatible = " qcom,pm8941-mpp " } , /* 8 MPP's */
2019-10-31 13:35:06 +03:00
{ . compatible = " qcom,pm8950-mpp " } , /* 4 MPP's */
{ . compatible = " qcom,pmi8950-mpp " } , /* 4 MPP's */
2015-11-18 03:52:33 +03:00
{ . compatible = " qcom,pm8994-mpp " } , /* 8 MPP's */
2014-10-22 13:58:47 +04:00
{ . compatible = " qcom,pma8084-mpp " } , /* 8 MPP's */
2016-07-11 22:01:09 +03:00
{ . compatible = " qcom,spmi-mpp " } , /* Generic */
2014-10-22 13:58:47 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , pmic_mpp_of_match ) ;
static struct platform_driver pmic_mpp_driver = {
. driver = {
. name = " qcom-spmi-mpp " ,
. of_match_table = pmic_mpp_of_match ,
} ,
. probe = pmic_mpp_probe ,
. remove = pmic_mpp_remove ,
} ;
module_platform_driver ( pmic_mpp_driver ) ;
MODULE_AUTHOR ( " Ivan T. Ivanov <iivanov@mm-sol.com> " ) ;
MODULE_DESCRIPTION ( " Qualcomm SPMI PMIC MPP pin control driver " ) ;
MODULE_ALIAS ( " platform:qcom-spmi-mpp " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;