2015-01-21 13:28:15 +08:00
/*
* mt65xx pinctrl driver based on Allwinner A1X pinctrl driver .
* Copyright ( c ) 2014 MediaTek Inc .
* Author : Hongzhou . Yang < hongzhou . yang @ mediatek . 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/io.h>
2015-12-08 22:06:23 +01:00
# include <linux/gpio/driver.h>
2015-01-21 13:28:15 +08:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
# include <linux/pinctrl/consumer.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/platform_device.h>
# include <linux/slab.h>
# include <linux/bitops.h>
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
2015-01-21 13:28:16 +08:00
# include <linux/delay.h>
2015-01-27 15:13:55 +08:00
# include <linux/interrupt.h>
2015-08-14 16:38:06 +08:00
# include <linux/pm.h>
2015-01-21 13:28:15 +08:00
# include <dt-bindings/pinctrl/mt65xx.h>
# include "../core.h"
# include "../pinconf.h"
# include "../pinctrl-utils.h"
# include "pinctrl-mtk-common.h"
# define MAX_GPIO_MODE_PER_REG 5
# define GPIO_MODE_BITS 3
2016-02-03 09:24:46 +08:00
# define GPIO_MODE_PREFIX "GPIO"
2015-01-21 13:28:15 +08:00
static const char * const mtk_gpio_functions [ ] = {
" func0 " , " func1 " , " func2 " , " func3 " ,
" func4 " , " func5 " , " func6 " , " func7 " ,
2016-01-27 09:24:42 +08:00
" func8 " , " func9 " , " func10 " , " func11 " ,
" func12 " , " func13 " , " func14 " , " func15 " ,
2015-01-21 13:28:15 +08:00
} ;
/*
* There are two base address for pull related configuration
* in mt8135 , and different GPIO pins use different base address .
* When pin number greater than type1_start and less than type1_end ,
* should use the second base address .
*/
static struct regmap * mtk_get_regmap ( struct mtk_pinctrl * pctl ,
unsigned long pin )
{
if ( pin > = pctl - > devdata - > type1_start & & pin < pctl - > devdata - > type1_end )
return pctl - > regmap2 ;
return pctl - > regmap1 ;
}
static unsigned int mtk_get_port ( struct mtk_pinctrl * pctl , unsigned long pin )
{
/* Different SoC has different mask and port shift. */
return ( ( pin > > 4 ) & pctl - > devdata - > port_mask )
< < pctl - > devdata - > port_shf ;
}
static int mtk_pmx_gpio_set_direction ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range , unsigned offset ,
bool input )
{
unsigned int reg_addr ;
unsigned int bit ;
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
reg_addr = mtk_get_port ( pctl , offset ) + pctl - > devdata - > dir_offset ;
bit = BIT ( offset & 0xf ) ;
2016-01-27 09:24:42 +08:00
if ( pctl - > devdata - > spec_dir_set )
pctl - > devdata - > spec_dir_set ( & reg_addr , offset ) ;
2015-01-21 13:28:15 +08:00
if ( input )
/* Different SoC has different alignment offset. */
reg_addr = CLR_ADDR ( reg_addr , pctl ) ;
else
reg_addr = SET_ADDR ( reg_addr , pctl ) ;
regmap_write ( mtk_get_regmap ( pctl , offset ) , reg_addr , bit ) ;
return 0 ;
}
static void mtk_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
unsigned int reg_addr ;
unsigned int bit ;
2015-12-08 22:06:23 +01:00
struct mtk_pinctrl * pctl = gpiochip_get_data ( chip ) ;
2015-01-21 13:28:15 +08:00
reg_addr = mtk_get_port ( pctl , offset ) + pctl - > devdata - > dout_offset ;
bit = BIT ( offset & 0xf ) ;
if ( value )
reg_addr = SET_ADDR ( reg_addr , pctl ) ;
else
reg_addr = CLR_ADDR ( reg_addr , pctl ) ;
regmap_write ( mtk_get_regmap ( pctl , offset ) , reg_addr , bit ) ;
}
2015-05-18 23:11:16 -07:00
static int mtk_pconf_set_ies_smt ( struct mtk_pinctrl * pctl , unsigned pin ,
int value , enum pin_config_param arg )
2015-01-21 13:28:15 +08:00
{
unsigned int reg_addr , offset ;
unsigned int bit ;
2015-05-18 23:11:16 -07:00
/**
* Due to some soc are not support ies / smt config , add this special
* control to handle it .
*/
if ( ! pctl - > devdata - > spec_ies_smt_set & &
pctl - > devdata - > ies_offset = = MTK_PINCTRL_NOT_SUPPORT & &
arg = = PIN_CONFIG_INPUT_ENABLE )
return - EINVAL ;
if ( ! pctl - > devdata - > spec_ies_smt_set & &
pctl - > devdata - > smt_offset = = MTK_PINCTRL_NOT_SUPPORT & &
arg = = PIN_CONFIG_INPUT_SCHMITT_ENABLE )
return - EINVAL ;
2015-01-27 15:13:55 +08:00
/*
* Due to some pins are irregular , their input enable and smt
2015-05-18 23:11:16 -07:00
* control register are discontinuous , so we need this special handle .
2015-01-27 15:13:55 +08:00
*/
if ( pctl - > devdata - > spec_ies_smt_set ) {
2015-05-18 23:11:16 -07:00
return pctl - > devdata - > spec_ies_smt_set ( mtk_get_regmap ( pctl , pin ) ,
pin , pctl - > devdata - > port_align , value , arg ) ;
2015-01-27 15:13:55 +08:00
}
2015-01-21 13:28:15 +08:00
bit = BIT ( pin & 0xf ) ;
2015-05-18 23:11:16 -07:00
if ( arg = = PIN_CONFIG_INPUT_ENABLE )
2015-01-21 13:28:15 +08:00
offset = pctl - > devdata - > ies_offset ;
else
offset = pctl - > devdata - > smt_offset ;
if ( value )
reg_addr = SET_ADDR ( mtk_get_port ( pctl , pin ) + offset , pctl ) ;
else
reg_addr = CLR_ADDR ( mtk_get_port ( pctl , pin ) + offset , pctl ) ;
regmap_write ( mtk_get_regmap ( pctl , pin ) , reg_addr , bit ) ;
2015-05-18 23:11:16 -07:00
return 0 ;
}
int mtk_pconf_spec_set_ies_smt_range ( struct regmap * regmap ,
const struct mtk_pin_ies_smt_set * ies_smt_infos , unsigned int info_num ,
unsigned int pin , unsigned char align , int value )
{
unsigned int i , reg_addr , bit ;
for ( i = 0 ; i < info_num ; i + + ) {
if ( pin > = ies_smt_infos [ i ] . start & &
pin < = ies_smt_infos [ i ] . end ) {
break ;
}
}
if ( i = = info_num )
return - EINVAL ;
if ( value )
reg_addr = ies_smt_infos [ i ] . offset + align ;
else
reg_addr = ies_smt_infos [ i ] . offset + ( align < < 1 ) ;
bit = BIT ( ies_smt_infos [ i ] . bit ) ;
regmap_write ( regmap , reg_addr , bit ) ;
return 0 ;
2015-01-21 13:28:15 +08:00
}
static const struct mtk_pin_drv_grp * mtk_find_pin_drv_grp_by_pin (
struct mtk_pinctrl * pctl , unsigned long pin ) {
int i ;
for ( i = 0 ; i < pctl - > devdata - > n_pin_drv_grps ; i + + ) {
const struct mtk_pin_drv_grp * pin_drv =
pctl - > devdata - > pin_drv_grp + i ;
if ( pin = = pin_drv - > pin )
return pin_drv ;
}
return NULL ;
}
static int mtk_pconf_set_driving ( struct mtk_pinctrl * pctl ,
unsigned int pin , unsigned char driving )
{
const struct mtk_pin_drv_grp * pin_drv ;
unsigned int val ;
unsigned int bits , mask , shift ;
const struct mtk_drv_group_desc * drv_grp ;
if ( pin > = pctl - > devdata - > npins )
return - EINVAL ;
pin_drv = mtk_find_pin_drv_grp_by_pin ( pctl , pin ) ;
if ( ! pin_drv | | pin_drv - > grp > pctl - > devdata - > n_grp_cls )
return - EINVAL ;
drv_grp = pctl - > devdata - > grp_desc + pin_drv - > grp ;
if ( driving > = drv_grp - > min_drv & & driving < = drv_grp - > max_drv
& & ! ( driving % drv_grp - > step ) ) {
val = driving / drv_grp - > step - 1 ;
bits = drv_grp - > high_bit - drv_grp - > low_bit + 1 ;
mask = BIT ( bits ) - 1 ;
shift = pin_drv - > bit + drv_grp - > low_bit ;
mask < < = shift ;
val < < = shift ;
return regmap_update_bits ( mtk_get_regmap ( pctl , pin ) ,
pin_drv - > offset , mask , val ) ;
}
return - EINVAL ;
}
2015-05-18 23:11:15 -07:00
int mtk_pctrl_spec_pull_set_samereg ( struct regmap * regmap ,
const struct mtk_pin_spec_pupd_set_samereg * pupd_infos ,
unsigned int info_num , unsigned int pin ,
unsigned char align , bool isup , unsigned int r1r0 )
{
unsigned int i ;
unsigned int reg_pupd , reg_set , reg_rst ;
unsigned int bit_pupd , bit_r0 , bit_r1 ;
const struct mtk_pin_spec_pupd_set_samereg * spec_pupd_pin ;
bool find = false ;
for ( i = 0 ; i < info_num ; i + + ) {
if ( pin = = pupd_infos [ i ] . pin ) {
find = true ;
break ;
}
}
if ( ! find )
return - EINVAL ;
spec_pupd_pin = pupd_infos + i ;
reg_set = spec_pupd_pin - > offset + align ;
reg_rst = spec_pupd_pin - > offset + ( align < < 1 ) ;
if ( isup )
reg_pupd = reg_rst ;
else
reg_pupd = reg_set ;
bit_pupd = BIT ( spec_pupd_pin - > pupd_bit ) ;
regmap_write ( regmap , reg_pupd , bit_pupd ) ;
bit_r0 = BIT ( spec_pupd_pin - > r0_bit ) ;
bit_r1 = BIT ( spec_pupd_pin - > r1_bit ) ;
switch ( r1r0 ) {
case MTK_PUPD_SET_R1R0_00 :
regmap_write ( regmap , reg_rst , bit_r0 ) ;
regmap_write ( regmap , reg_rst , bit_r1 ) ;
break ;
case MTK_PUPD_SET_R1R0_01 :
regmap_write ( regmap , reg_set , bit_r0 ) ;
regmap_write ( regmap , reg_rst , bit_r1 ) ;
break ;
case MTK_PUPD_SET_R1R0_10 :
regmap_write ( regmap , reg_rst , bit_r0 ) ;
regmap_write ( regmap , reg_set , bit_r1 ) ;
break ;
case MTK_PUPD_SET_R1R0_11 :
regmap_write ( regmap , reg_set , bit_r0 ) ;
regmap_write ( regmap , reg_set , bit_r1 ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2015-01-21 13:28:15 +08:00
static int mtk_pconf_set_pull_select ( struct mtk_pinctrl * pctl ,
unsigned int pin , bool enable , bool isup , unsigned int arg )
{
unsigned int bit ;
unsigned int reg_pullen , reg_pullsel ;
int ret ;
/* Some pins' pull setting are very different,
* they have separate pull up / down bit , R0 and R1
* resistor bit , so we need this special handle .
*/
if ( pctl - > devdata - > spec_pull_set ) {
ret = pctl - > devdata - > spec_pull_set ( mtk_get_regmap ( pctl , pin ) ,
pin , pctl - > devdata - > port_align , isup , arg ) ;
if ( ! ret )
return 0 ;
}
/* For generic pull config, default arg value should be 0 or 1. */
if ( arg ! = 0 & & arg ! = 1 ) {
dev_err ( pctl - > dev , " invalid pull-up argument %d on pin %d . \n " ,
arg , pin ) ;
return - EINVAL ;
}
bit = BIT ( pin & 0xf ) ;
if ( enable )
reg_pullen = SET_ADDR ( mtk_get_port ( pctl , pin ) +
pctl - > devdata - > pullen_offset , pctl ) ;
else
reg_pullen = CLR_ADDR ( mtk_get_port ( pctl , pin ) +
pctl - > devdata - > pullen_offset , pctl ) ;
if ( isup )
reg_pullsel = SET_ADDR ( mtk_get_port ( pctl , pin ) +
pctl - > devdata - > pullsel_offset , pctl ) ;
else
reg_pullsel = CLR_ADDR ( mtk_get_port ( pctl , pin ) +
pctl - > devdata - > pullsel_offset , pctl ) ;
regmap_write ( mtk_get_regmap ( pctl , pin ) , reg_pullen , bit ) ;
regmap_write ( mtk_get_regmap ( pctl , pin ) , reg_pullsel , bit ) ;
return 0 ;
}
static int mtk_pconf_parse_conf ( struct pinctrl_dev * pctldev ,
unsigned int pin , enum pin_config_param param ,
enum pin_config_param arg )
{
2015-05-18 23:11:16 -07:00
int ret = 0 ;
2015-01-21 13:28:15 +08:00
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
2015-05-18 23:11:16 -07:00
ret = mtk_pconf_set_pull_select ( pctl , pin , false , false , arg ) ;
2015-01-21 13:28:15 +08:00
break ;
case PIN_CONFIG_BIAS_PULL_UP :
2015-05-18 23:11:16 -07:00
ret = mtk_pconf_set_pull_select ( pctl , pin , true , true , arg ) ;
2015-01-21 13:28:15 +08:00
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
2015-05-18 23:11:16 -07:00
ret = mtk_pconf_set_pull_select ( pctl , pin , true , false , arg ) ;
2015-01-21 13:28:15 +08:00
break ;
case PIN_CONFIG_INPUT_ENABLE :
2016-02-03 09:24:45 +08:00
mtk_pmx_gpio_set_direction ( pctldev , NULL , pin , true ) ;
2015-05-18 23:11:16 -07:00
ret = mtk_pconf_set_ies_smt ( pctl , pin , arg , param ) ;
2015-01-21 13:28:15 +08:00
break ;
case PIN_CONFIG_OUTPUT :
mtk_gpio_set ( pctl - > chip , pin , arg ) ;
2015-05-18 23:11:16 -07:00
ret = mtk_pmx_gpio_set_direction ( pctldev , NULL , pin , false ) ;
2015-01-21 13:28:15 +08:00
break ;
case PIN_CONFIG_INPUT_SCHMITT_ENABLE :
2016-02-03 09:24:45 +08:00
mtk_pmx_gpio_set_direction ( pctldev , NULL , pin , true ) ;
2015-05-18 23:11:16 -07:00
ret = mtk_pconf_set_ies_smt ( pctl , pin , arg , param ) ;
2015-01-21 13:28:15 +08:00
break ;
case PIN_CONFIG_DRIVE_STRENGTH :
2015-05-18 23:11:16 -07:00
ret = mtk_pconf_set_driving ( pctl , pin , arg ) ;
2015-01-21 13:28:15 +08:00
break ;
default :
2015-05-18 23:11:16 -07:00
ret = - EINVAL ;
2015-01-21 13:28:15 +08:00
}
2015-05-18 23:11:16 -07:00
return ret ;
2015-01-21 13:28:15 +08:00
}
static int mtk_pconf_group_get ( struct pinctrl_dev * pctldev ,
unsigned group ,
unsigned long * config )
{
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
* config = pctl - > groups [ group ] . config ;
return 0 ;
}
static int mtk_pconf_group_set ( struct pinctrl_dev * pctldev , unsigned group ,
unsigned long * configs , unsigned num_configs )
{
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct mtk_pinctrl_group * g = & pctl - > groups [ group ] ;
2015-05-18 23:11:16 -07:00
int i , ret ;
2015-01-21 13:28:15 +08:00
for ( i = 0 ; i < num_configs ; i + + ) {
2015-05-18 23:11:16 -07:00
ret = mtk_pconf_parse_conf ( pctldev , g - > pin ,
2015-01-21 13:28:15 +08:00
pinconf_to_config_param ( configs [ i ] ) ,
pinconf_to_config_argument ( configs [ i ] ) ) ;
2015-05-18 23:11:16 -07:00
if ( ret < 0 )
return ret ;
2015-01-21 13:28:15 +08:00
g - > config = configs [ i ] ;
}
return 0 ;
}
static const struct pinconf_ops mtk_pconf_ops = {
. pin_config_group_get = mtk_pconf_group_get ,
. pin_config_group_set = mtk_pconf_group_set ,
} ;
static struct mtk_pinctrl_group *
mtk_pctrl_find_group_by_pin ( struct mtk_pinctrl * pctl , u32 pin )
{
int i ;
for ( i = 0 ; i < pctl - > ngroups ; i + + ) {
struct mtk_pinctrl_group * grp = pctl - > groups + i ;
if ( grp - > pin = = pin )
return grp ;
}
return NULL ;
}
static const struct mtk_desc_function * mtk_pctrl_find_function_by_pin (
struct mtk_pinctrl * pctl , u32 pin_num , u32 fnum )
{
const struct mtk_desc_pin * pin = pctl - > devdata - > pins + pin_num ;
const struct mtk_desc_function * func = pin - > functions ;
while ( func & & func - > name ) {
if ( func - > muxval = = fnum )
return func ;
func + + ;
}
return NULL ;
}
static bool mtk_pctrl_is_function_valid ( struct mtk_pinctrl * pctl ,
u32 pin_num , u32 fnum )
{
int i ;
for ( i = 0 ; i < pctl - > devdata - > npins ; i + + ) {
const struct mtk_desc_pin * pin = pctl - > devdata - > pins + i ;
if ( pin - > pin . number = = pin_num ) {
const struct mtk_desc_function * func =
pin - > functions ;
while ( func & & func - > name ) {
if ( func - > muxval = = fnum )
return true ;
func + + ;
}
break ;
}
}
return false ;
}
static int mtk_pctrl_dt_node_to_map_func ( struct mtk_pinctrl * pctl ,
u32 pin , u32 fnum , struct mtk_pinctrl_group * grp ,
struct pinctrl_map * * map , unsigned * reserved_maps ,
unsigned * num_maps )
{
bool ret ;
if ( * num_maps = = * reserved_maps )
return - ENOSPC ;
( * map ) [ * num_maps ] . type = PIN_MAP_TYPE_MUX_GROUP ;
( * map ) [ * num_maps ] . data . mux . group = grp - > name ;
ret = mtk_pctrl_is_function_valid ( pctl , pin , fnum ) ;
if ( ! ret ) {
dev_err ( pctl - > dev , " invalid function %d on pin %d . \n " ,
fnum , pin ) ;
return - EINVAL ;
}
( * map ) [ * num_maps ] . data . mux . function = mtk_gpio_functions [ fnum ] ;
( * num_maps ) + + ;
return 0 ;
}
static int mtk_pctrl_dt_subnode_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * node ,
struct pinctrl_map * * map ,
unsigned * reserved_maps ,
unsigned * num_maps )
{
struct property * pins ;
u32 pinfunc , pin , func ;
int num_pins , num_funcs , maps_per_pin ;
unsigned long * configs ;
unsigned int num_configs ;
bool has_config = 0 ;
int i , err ;
unsigned reserve = 0 ;
struct mtk_pinctrl_group * grp ;
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
pins = of_find_property ( node , " pinmux " , NULL ) ;
if ( ! pins ) {
dev_err ( pctl - > dev , " missing pins property in node %s . \n " ,
node - > name ) ;
return - EINVAL ;
}
2015-02-11 23:56:11 -08:00
err = pinconf_generic_parse_dt_config ( node , pctldev , & configs ,
& num_configs ) ;
2015-11-17 14:33:41 -08:00
if ( err )
return err ;
2015-01-21 13:28:15 +08:00
if ( num_configs )
has_config = 1 ;
num_pins = pins - > length / sizeof ( u32 ) ;
num_funcs = num_pins ;
maps_per_pin = 0 ;
if ( num_funcs )
maps_per_pin + + ;
if ( has_config & & num_pins > = 1 )
maps_per_pin + + ;
2015-11-17 14:33:41 -08:00
if ( ! num_pins | | ! maps_per_pin ) {
err = - EINVAL ;
goto exit ;
}
2015-01-21 13:28:15 +08:00
reserve = num_pins * maps_per_pin ;
err = pinctrl_utils_reserve_map ( pctldev , map ,
reserved_maps , num_maps , reserve ) ;
if ( err < 0 )
2015-11-17 14:33:41 -08:00
goto exit ;
2015-01-21 13:28:15 +08:00
for ( i = 0 ; i < num_pins ; i + + ) {
err = of_property_read_u32_index ( node , " pinmux " ,
i , & pinfunc ) ;
if ( err )
2015-11-17 14:33:41 -08:00
goto exit ;
2015-01-21 13:28:15 +08:00
pin = MTK_GET_PIN_NO ( pinfunc ) ;
func = MTK_GET_PIN_FUNC ( pinfunc ) ;
if ( pin > = pctl - > devdata - > npins | |
func > = ARRAY_SIZE ( mtk_gpio_functions ) ) {
dev_err ( pctl - > dev , " invalid pins value. \n " ) ;
err = - EINVAL ;
2015-11-17 14:33:41 -08:00
goto exit ;
2015-01-21 13:28:15 +08:00
}
grp = mtk_pctrl_find_group_by_pin ( pctl , pin ) ;
if ( ! grp ) {
dev_err ( pctl - > dev , " unable to match pin %d to group \n " ,
pin ) ;
2015-11-17 14:33:41 -08:00
err = - EINVAL ;
goto exit ;
2015-01-21 13:28:15 +08:00
}
err = mtk_pctrl_dt_node_to_map_func ( pctl , pin , func , grp , map ,
reserved_maps , num_maps ) ;
if ( err < 0 )
2015-11-17 14:33:41 -08:00
goto exit ;
2015-01-21 13:28:15 +08:00
if ( has_config ) {
err = pinctrl_utils_add_map_configs ( pctldev , map ,
reserved_maps , num_maps , grp - > name ,
configs , num_configs ,
PIN_MAP_TYPE_CONFIGS_GROUP ) ;
if ( err < 0 )
2015-11-17 14:33:41 -08:00
goto exit ;
2015-01-21 13:28:15 +08:00
}
}
2015-11-17 14:33:41 -08:00
err = 0 ;
2015-01-21 13:28:15 +08:00
2015-11-17 14:33:41 -08:00
exit :
kfree ( configs ) ;
2015-01-21 13:28:15 +08:00
return err ;
}
static int mtk_pctrl_dt_node_to_map ( struct pinctrl_dev * pctldev ,
struct device_node * np_config ,
struct pinctrl_map * * map , unsigned * num_maps )
{
struct device_node * np ;
unsigned reserved_maps ;
int ret ;
* map = NULL ;
* num_maps = 0 ;
reserved_maps = 0 ;
for_each_child_of_node ( np_config , np ) {
ret = mtk_pctrl_dt_subnode_to_map ( pctldev , np , map ,
& reserved_maps , num_maps ) ;
if ( ret < 0 ) {
2016-03-31 14:44:42 +03:00
pinctrl_utils_free_map ( pctldev , * map , * num_maps ) ;
2015-12-21 17:39:48 +01:00
of_node_put ( np ) ;
2015-01-21 13:28:15 +08:00
return ret ;
}
}
return 0 ;
}
static int mtk_pctrl_get_groups_count ( struct pinctrl_dev * pctldev )
{
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctl - > ngroups ;
}
static const char * mtk_pctrl_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned group )
{
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
return pctl - > groups [ group ] . name ;
}
static int mtk_pctrl_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned group ,
const unsigned * * pins ,
unsigned * num_pins )
{
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
* pins = ( unsigned * ) & pctl - > groups [ group ] . pin ;
* num_pins = 1 ;
return 0 ;
}
static const struct pinctrl_ops mtk_pctrl_ops = {
. dt_node_to_map = mtk_pctrl_dt_node_to_map ,
2016-03-31 14:44:42 +03:00
. dt_free_map = pinctrl_utils_free_map ,
2015-01-21 13:28:15 +08:00
. get_groups_count = mtk_pctrl_get_groups_count ,
. get_group_name = mtk_pctrl_get_group_name ,
. get_group_pins = mtk_pctrl_get_group_pins ,
} ;
static int mtk_pmx_get_funcs_cnt ( struct pinctrl_dev * pctldev )
{
return ARRAY_SIZE ( mtk_gpio_functions ) ;
}
static const char * mtk_pmx_get_func_name ( struct pinctrl_dev * pctldev ,
unsigned selector )
{
return mtk_gpio_functions [ selector ] ;
}
static int mtk_pmx_get_func_groups ( struct pinctrl_dev * pctldev ,
unsigned function ,
const char * const * * groups ,
unsigned * const num_groups )
{
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
* groups = pctl - > grp_names ;
* num_groups = pctl - > ngroups ;
return 0 ;
}
static int mtk_pmx_set_mode ( struct pinctrl_dev * pctldev ,
unsigned long pin , unsigned long mode )
{
unsigned int reg_addr ;
unsigned char bit ;
unsigned int val ;
unsigned int mask = ( 1L < < GPIO_MODE_BITS ) - 1 ;
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2016-01-27 09:24:42 +08:00
if ( pctl - > devdata - > spec_pinmux_set )
pctl - > devdata - > spec_pinmux_set ( mtk_get_regmap ( pctl , pin ) ,
pin , mode ) ;
2015-01-21 13:28:15 +08:00
reg_addr = ( ( pin / MAX_GPIO_MODE_PER_REG ) < < pctl - > devdata - > port_shf )
+ pctl - > devdata - > pinmux_offset ;
2016-01-27 09:24:42 +08:00
mode & = mask ;
2015-01-21 13:28:15 +08:00
bit = pin % MAX_GPIO_MODE_PER_REG ;
mask < < = ( GPIO_MODE_BITS * bit ) ;
val = ( mode < < ( GPIO_MODE_BITS * bit ) ) ;
return regmap_update_bits ( mtk_get_regmap ( pctl , pin ) ,
reg_addr , mask , val ) ;
}
2015-01-21 13:28:16 +08:00
static const struct mtk_desc_pin *
mtk_find_pin_by_eint_num ( struct mtk_pinctrl * pctl , unsigned int eint_num )
{
int i ;
const struct mtk_desc_pin * pin ;
for ( i = 0 ; i < pctl - > devdata - > npins ; i + + ) {
pin = pctl - > devdata - > pins + i ;
if ( pin - > eint . eintnum = = eint_num )
return pin ;
}
return NULL ;
}
2015-01-21 13:28:15 +08:00
static int mtk_pmx_set_mux ( struct pinctrl_dev * pctldev ,
unsigned function ,
unsigned group )
{
bool ret ;
const struct mtk_desc_function * desc ;
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct mtk_pinctrl_group * g = pctl - > groups + group ;
ret = mtk_pctrl_is_function_valid ( pctl , g - > pin , function ) ;
if ( ! ret ) {
2015-08-03 00:10:45 +01:00
dev_err ( pctl - > dev , " invalid function %d on group %d . \n " ,
2015-01-21 13:28:15 +08:00
function , group ) ;
return - EINVAL ;
}
desc = mtk_pctrl_find_function_by_pin ( pctl , g - > pin , function ) ;
if ( ! desc )
return - EINVAL ;
mtk_pmx_set_mode ( pctldev , g - > pin , desc - > muxval ) ;
return 0 ;
}
2016-02-03 09:24:46 +08:00
static int mtk_pmx_find_gpio_mode ( struct mtk_pinctrl * pctl ,
unsigned offset )
{
const struct mtk_desc_pin * pin = pctl - > devdata - > pins + offset ;
const struct mtk_desc_function * func = pin - > functions ;
while ( func & & func - > name ) {
if ( ! strncmp ( func - > name , GPIO_MODE_PREFIX ,
sizeof ( GPIO_MODE_PREFIX ) - 1 ) )
return func - > muxval ;
func + + ;
}
return - EINVAL ;
}
static int mtk_pmx_gpio_request_enable ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range ,
unsigned offset )
{
2016-02-11 15:34:08 +01:00
int muxval ;
2016-02-03 09:24:46 +08:00
struct mtk_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
muxval = mtk_pmx_find_gpio_mode ( pctl , offset ) ;
if ( muxval < 0 ) {
dev_err ( pctl - > dev , " invalid gpio pin %d. \n " , offset ) ;
return - EINVAL ;
}
mtk_pmx_set_mode ( pctldev , offset , muxval ) ;
2016-02-17 03:16:33 +08:00
mtk_pconf_set_ies_smt ( pctl , offset , 1 , PIN_CONFIG_INPUT_ENABLE ) ;
2016-02-03 09:24:46 +08:00
return 0 ;
}
2015-01-21 13:28:15 +08:00
static const struct pinmux_ops mtk_pmx_ops = {
. get_functions_count = mtk_pmx_get_funcs_cnt ,
. get_function_name = mtk_pmx_get_func_name ,
. get_function_groups = mtk_pmx_get_func_groups ,
. set_mux = mtk_pmx_set_mux ,
. gpio_set_direction = mtk_pmx_gpio_set_direction ,
2016-02-03 09:24:46 +08:00
. gpio_request_enable = mtk_pmx_gpio_request_enable ,
2015-01-21 13:28:15 +08:00
} ;
static int mtk_gpio_direction_input ( struct gpio_chip * chip ,
unsigned offset )
{
return pinctrl_gpio_direction_input ( chip - > base + offset ) ;
}
static int mtk_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
mtk_gpio_set ( chip , offset , value ) ;
return pinctrl_gpio_direction_output ( chip - > base + offset ) ;
}
static int mtk_gpio_get_direction ( struct gpio_chip * chip , unsigned offset )
{
unsigned int reg_addr ;
unsigned int bit ;
unsigned int read_val = 0 ;
2015-12-08 22:06:23 +01:00
struct mtk_pinctrl * pctl = gpiochip_get_data ( chip ) ;
2015-01-21 13:28:15 +08:00
reg_addr = mtk_get_port ( pctl , offset ) + pctl - > devdata - > dir_offset ;
bit = BIT ( offset & 0xf ) ;
2016-01-27 09:24:42 +08:00
if ( pctl - > devdata - > spec_dir_set )
pctl - > devdata - > spec_dir_set ( & reg_addr , offset ) ;
2015-01-21 13:28:15 +08:00
regmap_read ( pctl - > regmap1 , reg_addr , & read_val ) ;
2015-11-17 14:17:13 -08:00
return ! ( read_val & bit ) ;
2015-01-21 13:28:15 +08:00
}
static int mtk_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
unsigned int reg_addr ;
unsigned int bit ;
unsigned int read_val = 0 ;
2015-12-08 22:06:23 +01:00
struct mtk_pinctrl * pctl = gpiochip_get_data ( chip ) ;
2015-01-21 13:28:15 +08:00
2015-11-17 14:17:13 -08:00
reg_addr = mtk_get_port ( pctl , offset ) +
pctl - > devdata - > din_offset ;
2015-01-21 13:28:15 +08:00
bit = BIT ( offset & 0xf ) ;
regmap_read ( pctl - > regmap1 , reg_addr , & read_val ) ;
return ! ! ( read_val & bit ) ;
}
2015-01-21 13:28:16 +08:00
static int mtk_gpio_to_irq ( struct gpio_chip * chip , unsigned offset )
{
const struct mtk_desc_pin * pin ;
2015-12-08 22:06:23 +01:00
struct mtk_pinctrl * pctl = gpiochip_get_data ( chip ) ;
2015-01-21 13:28:16 +08:00
int irq ;
pin = pctl - > devdata - > pins + offset ;
if ( pin - > eint . eintnum = = NO_EINT_SUPPORT )
return - EINVAL ;
irq = irq_find_mapping ( pctl - > domain , pin - > eint . eintnum ) ;
if ( ! irq )
return - EINVAL ;
return irq ;
}
static int mtk_pinctrl_irq_request_resources ( struct irq_data * d )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
const struct mtk_desc_pin * pin ;
int ret ;
pin = mtk_find_pin_by_eint_num ( pctl , d - > hwirq ) ;
if ( ! pin ) {
dev_err ( pctl - > dev , " Can not find pin \n " ) ;
return - EINVAL ;
}
ret = gpiochip_lock_as_irq ( pctl - > chip , pin - > pin . number ) ;
if ( ret ) {
dev_err ( pctl - > dev , " unable to lock HW IRQ %lu for IRQ \n " ,
irqd_to_hwirq ( d ) ) ;
return ret ;
}
/* set mux to INT mode */
mtk_pmx_set_mode ( pctl - > pctl_dev , pin - > pin . number , pin - > eint . eintmux ) ;
2016-02-17 03:16:34 +08:00
/* set gpio direction to input */
mtk_pmx_gpio_set_direction ( pctl - > pctl_dev , NULL , pin - > pin . number , true ) ;
/* set input-enable */
mtk_pconf_set_ies_smt ( pctl , pin - > pin . number , 1 , PIN_CONFIG_INPUT_ENABLE ) ;
2015-01-21 13:28:16 +08:00
return 0 ;
}
static void mtk_pinctrl_irq_release_resources ( struct irq_data * d )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
const struct mtk_desc_pin * pin ;
pin = mtk_find_pin_by_eint_num ( pctl , d - > hwirq ) ;
if ( ! pin ) {
dev_err ( pctl - > dev , " Can not find pin \n " ) ;
return ;
}
gpiochip_unlock_as_irq ( pctl - > chip , pin - > pin . number ) ;
}
static void __iomem * mtk_eint_get_offset ( struct mtk_pinctrl * pctl ,
unsigned int eint_num , unsigned int offset )
{
unsigned int eint_base = 0 ;
void __iomem * reg ;
if ( eint_num > = pctl - > devdata - > ap_num )
eint_base = pctl - > devdata - > ap_num ;
reg = pctl - > eint_reg_base + offset + ( ( eint_num - eint_base ) / 32 ) * 4 ;
return reg ;
}
/*
* mtk_can_en_debounce : Check the EINT number is able to enable debounce or not
* @ eint_num : the EINT number to setmtk_pinctrl
*/
static unsigned int mtk_eint_can_en_debounce ( struct mtk_pinctrl * pctl ,
unsigned int eint_num )
{
unsigned int sens ;
unsigned int bit = BIT ( eint_num % 32 ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
void __iomem * reg = mtk_eint_get_offset ( pctl , eint_num ,
eint_offsets - > sens ) ;
if ( readl ( reg ) & bit )
sens = MT_LEVEL_SENSITIVE ;
else
sens = MT_EDGE_SENSITIVE ;
if ( ( eint_num < pctl - > devdata - > db_cnt ) & & ( sens ! = MT_EDGE_SENSITIVE ) )
return 1 ;
else
return 0 ;
}
/*
* mtk_eint_get_mask : To get the eint mask
* @ eint_num : the EINT number to get
*/
static unsigned int mtk_eint_get_mask ( struct mtk_pinctrl * pctl ,
unsigned int eint_num )
{
unsigned int bit = BIT ( eint_num % 32 ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
void __iomem * reg = mtk_eint_get_offset ( pctl , eint_num ,
eint_offsets - > mask ) ;
return ! ! ( readl ( reg ) & bit ) ;
}
2015-01-27 14:15:26 +08:00
static int mtk_eint_flip_edge ( struct mtk_pinctrl * pctl , int hwirq )
{
int start_level , curr_level ;
unsigned int reg_offset ;
const struct mtk_eint_offsets * eint_offsets = & ( pctl - > devdata - > eint_offsets ) ;
2015-08-29 01:25:01 +02:00
u32 mask = BIT ( hwirq & 0x1f ) ;
2015-01-27 14:15:26 +08:00
u32 port = ( hwirq > > 5 ) & eint_offsets - > port_mask ;
void __iomem * reg = pctl - > eint_reg_base + ( port < < 2 ) ;
const struct mtk_desc_pin * pin ;
pin = mtk_find_pin_by_eint_num ( pctl , hwirq ) ;
curr_level = mtk_gpio_get ( pctl - > chip , pin - > pin . number ) ;
do {
start_level = curr_level ;
if ( start_level )
reg_offset = eint_offsets - > pol_clr ;
else
reg_offset = eint_offsets - > pol_set ;
writel ( mask , reg + reg_offset ) ;
curr_level = mtk_gpio_get ( pctl - > chip , pin - > pin . number ) ;
} while ( start_level ! = curr_level ) ;
return start_level ;
}
2015-01-21 13:28:16 +08:00
static void mtk_eint_mask ( struct irq_data * d )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
u32 mask = BIT ( d - > hwirq & 0x1f ) ;
void __iomem * reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > mask_set ) ;
writel ( mask , reg ) ;
}
static void mtk_eint_unmask ( struct irq_data * d )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
u32 mask = BIT ( d - > hwirq & 0x1f ) ;
void __iomem * reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > mask_clr ) ;
writel ( mask , reg ) ;
2015-01-27 14:15:26 +08:00
if ( pctl - > eint_dual_edges [ d - > hwirq ] )
mtk_eint_flip_edge ( pctl , d - > hwirq ) ;
2015-01-21 13:28:16 +08:00
}
static int mtk_gpio_set_debounce ( struct gpio_chip * chip , unsigned offset ,
unsigned debounce )
{
2015-11-04 09:56:26 +01:00
struct mtk_pinctrl * pctl = dev_get_drvdata ( chip - > parent ) ;
2015-01-21 13:28:16 +08:00
int eint_num , virq , eint_offset ;
unsigned int set_offset , bit , clr_bit , clr_offset , rst , i , unmask , dbnc ;
2016-04-02 14:57:49 +08:00
static const unsigned int debounce_time [ ] = { 500 , 1000 , 16000 , 32000 , 64000 ,
128000 , 256000 } ;
2015-01-21 13:28:16 +08:00
const struct mtk_desc_pin * pin ;
struct irq_data * d ;
pin = pctl - > devdata - > pins + offset ;
if ( pin - > eint . eintnum = = NO_EINT_SUPPORT )
return - EINVAL ;
eint_num = pin - > eint . eintnum ;
virq = irq_find_mapping ( pctl - > domain , eint_num ) ;
eint_offset = ( eint_num % 4 ) * 8 ;
d = irq_get_irq_data ( virq ) ;
set_offset = ( eint_num / 4 ) * 4 + pctl - > devdata - > eint_offsets . dbnc_set ;
clr_offset = ( eint_num / 4 ) * 4 + pctl - > devdata - > eint_offsets . dbnc_clr ;
if ( ! mtk_eint_can_en_debounce ( pctl , eint_num ) )
return - ENOSYS ;
2016-04-02 14:57:49 +08:00
dbnc = ARRAY_SIZE ( debounce_time ) ;
for ( i = 0 ; i < ARRAY_SIZE ( debounce_time ) ; i + + ) {
if ( debounce < = debounce_time [ i ] ) {
2015-01-21 13:28:16 +08:00
dbnc = i ;
break ;
}
}
if ( ! mtk_eint_get_mask ( pctl , eint_num ) ) {
mtk_eint_mask ( d ) ;
unmask = 1 ;
2015-04-20 10:59:17 -05:00
} else {
unmask = 0 ;
2015-01-21 13:28:16 +08:00
}
clr_bit = 0xff < < eint_offset ;
writel ( clr_bit , pctl - > eint_reg_base + clr_offset ) ;
bit = ( ( dbnc < < EINT_DBNC_SET_DBNC_BITS ) | EINT_DBNC_SET_EN ) < <
eint_offset ;
rst = EINT_DBNC_RST_BIT < < eint_offset ;
writel ( rst | bit , pctl - > eint_reg_base + set_offset ) ;
/* Delay a while (more than 2T) to wait for hw debounce counter reset
work correctly */
udelay ( 1 ) ;
if ( unmask = = 1 )
mtk_eint_unmask ( d ) ;
return 0 ;
}
2017-01-23 15:34:34 +03:00
static int mtk_gpio_set_config ( struct gpio_chip * chip , unsigned offset ,
unsigned long config )
{
u32 debounce ;
if ( pinconf_to_config_param ( config ) ! = PIN_CONFIG_INPUT_DEBOUNCE )
return - ENOTSUPP ;
debounce = pinconf_to_config_argument ( config ) ;
return mtk_gpio_set_debounce ( chip , offset , debounce ) ;
}
2016-09-11 14:14:39 +02:00
static const struct gpio_chip mtk_gpio_chip = {
2015-01-21 13:28:15 +08:00
. owner = THIS_MODULE ,
2015-10-11 17:34:19 +02:00
. request = gpiochip_generic_request ,
. free = gpiochip_generic_free ,
2015-11-17 14:17:13 -08:00
. get_direction = mtk_gpio_get_direction ,
2015-01-21 13:28:15 +08:00
. direction_input = mtk_gpio_direction_input ,
. direction_output = mtk_gpio_direction_output ,
. get = mtk_gpio_get ,
. set = mtk_gpio_set ,
2015-01-21 13:28:16 +08:00
. to_irq = mtk_gpio_to_irq ,
2017-01-23 15:34:34 +03:00
. set_config = mtk_gpio_set_config ,
2015-01-21 13:28:15 +08:00
. of_gpio_n_cells = 2 ,
} ;
2015-01-21 13:28:16 +08:00
static int mtk_eint_set_type ( struct irq_data * d ,
unsigned int type )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
u32 mask = BIT ( d - > hwirq & 0x1f ) ;
void __iomem * reg ;
if ( ( ( type & IRQ_TYPE_EDGE_BOTH ) & & ( type & IRQ_TYPE_LEVEL_MASK ) ) | |
( ( type & IRQ_TYPE_LEVEL_MASK ) = = IRQ_TYPE_LEVEL_MASK ) ) {
dev_err ( pctl - > dev , " Can't configure IRQ%d (EINT%lu) for type 0x%X \n " ,
d - > irq , d - > hwirq , type ) ;
return - EINVAL ;
}
2015-01-27 14:15:26 +08:00
if ( ( type & IRQ_TYPE_EDGE_BOTH ) = = IRQ_TYPE_EDGE_BOTH )
pctl - > eint_dual_edges [ d - > hwirq ] = 1 ;
else
pctl - > eint_dual_edges [ d - > hwirq ] = 0 ;
2015-01-21 13:28:16 +08:00
if ( type & ( IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING ) ) {
reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > pol_clr ) ;
writel ( mask , reg ) ;
} else {
reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > pol_set ) ;
writel ( mask , reg ) ;
}
if ( type & ( IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING ) ) {
reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > sens_clr ) ;
writel ( mask , reg ) ;
} else {
reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > sens_set ) ;
writel ( mask , reg ) ;
}
2015-01-27 14:15:26 +08:00
if ( pctl - > eint_dual_edges [ d - > hwirq ] )
mtk_eint_flip_edge ( pctl , d - > hwirq ) ;
2015-01-21 13:28:16 +08:00
return 0 ;
}
2015-08-14 16:38:06 +08:00
static int mtk_eint_irq_set_wake ( struct irq_data * d , unsigned int on )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
int shift = d - > hwirq & 0x1f ;
int reg = d - > hwirq > > 5 ;
if ( on )
pctl - > wake_mask [ reg ] | = BIT ( shift ) ;
else
pctl - > wake_mask [ reg ] & = ~ BIT ( shift ) ;
return 0 ;
}
static void mtk_eint_chip_write_mask ( const struct mtk_eint_offsets * chip ,
void __iomem * eint_reg_base , u32 * buf )
{
int port ;
void __iomem * reg ;
for ( port = 0 ; port < chip - > ports ; port + + ) {
reg = eint_reg_base + ( port < < 2 ) ;
writel_relaxed ( ~ buf [ port ] , reg + chip - > mask_set ) ;
writel_relaxed ( buf [ port ] , reg + chip - > mask_clr ) ;
}
}
static void mtk_eint_chip_read_mask ( const struct mtk_eint_offsets * chip ,
void __iomem * eint_reg_base , u32 * buf )
{
int port ;
void __iomem * reg ;
for ( port = 0 ; port < chip - > ports ; port + + ) {
reg = eint_reg_base + chip - > mask + ( port < < 2 ) ;
buf [ port ] = ~ readl_relaxed ( reg ) ;
/* Mask is 0 when irq is enabled, and 1 when disabled. */
}
}
static int mtk_eint_suspend ( struct device * device )
{
void __iomem * reg ;
struct mtk_pinctrl * pctl = dev_get_drvdata ( device ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
reg = pctl - > eint_reg_base ;
mtk_eint_chip_read_mask ( eint_offsets , reg , pctl - > cur_mask ) ;
mtk_eint_chip_write_mask ( eint_offsets , reg , pctl - > wake_mask ) ;
return 0 ;
}
static int mtk_eint_resume ( struct device * device )
{
struct mtk_pinctrl * pctl = dev_get_drvdata ( device ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
mtk_eint_chip_write_mask ( eint_offsets ,
pctl - > eint_reg_base , pctl - > cur_mask ) ;
return 0 ;
}
const struct dev_pm_ops mtk_eint_pm_ops = {
2016-06-03 11:28:14 +08:00
. suspend_noirq = mtk_eint_suspend ,
. resume_noirq = mtk_eint_resume ,
2015-08-14 16:38:06 +08:00
} ;
2015-01-21 13:28:16 +08:00
static void mtk_eint_ack ( struct irq_data * d )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
u32 mask = BIT ( d - > hwirq & 0x1f ) ;
void __iomem * reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > ack ) ;
writel ( mask , reg ) ;
}
static struct irq_chip mtk_pinctrl_irq_chip = {
. name = " mt-eint " ,
2015-08-14 16:38:06 +08:00
. irq_disable = mtk_eint_mask ,
2015-01-21 13:28:16 +08:00
. irq_mask = mtk_eint_mask ,
. irq_unmask = mtk_eint_unmask ,
. irq_ack = mtk_eint_ack ,
. irq_set_type = mtk_eint_set_type ,
2015-08-14 16:38:06 +08:00
. irq_set_wake = mtk_eint_irq_set_wake ,
2015-01-21 13:28:16 +08:00
. irq_request_resources = mtk_pinctrl_irq_request_resources ,
. irq_release_resources = mtk_pinctrl_irq_release_resources ,
} ;
static unsigned int mtk_eint_init ( struct mtk_pinctrl * pctl )
{
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
void __iomem * reg = pctl - > eint_reg_base + eint_offsets - > dom_en ;
unsigned int i ;
for ( i = 0 ; i < pctl - > devdata - > ap_num ; i + = 32 ) {
writel ( 0xffffffff , reg ) ;
reg + = 4 ;
}
return 0 ;
}
static inline void
mtk_eint_debounce_process ( struct mtk_pinctrl * pctl , int index )
{
unsigned int rst , ctrl_offset ;
unsigned int bit , dbnc ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
ctrl_offset = ( index / 4 ) * 4 + eint_offsets - > dbnc_ctrl ;
dbnc = readl ( pctl - > eint_reg_base + ctrl_offset ) ;
bit = EINT_DBNC_SET_EN < < ( ( index % 4 ) * 8 ) ;
if ( ( bit & dbnc ) > 0 ) {
ctrl_offset = ( index / 4 ) * 4 + eint_offsets - > dbnc_set ;
rst = EINT_DBNC_RST_BIT < < ( ( index % 4 ) * 8 ) ;
writel ( rst , pctl - > eint_reg_base + ctrl_offset ) ;
}
}
2015-09-14 10:42:37 +02:00
static void mtk_eint_irq_handler ( struct irq_desc * desc )
2015-01-21 13:28:16 +08:00
{
2015-06-04 12:13:16 +08:00
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
struct mtk_pinctrl * pctl = irq_desc_get_handler_data ( desc ) ;
2015-01-21 13:28:16 +08:00
unsigned int status , eint_num ;
int offset , index , virq ;
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
void __iomem * reg = mtk_eint_get_offset ( pctl , 0 , eint_offsets - > stat ) ;
2015-01-27 14:15:26 +08:00
int dual_edges , start_level , curr_level ;
const struct mtk_desc_pin * pin ;
2015-01-21 13:28:16 +08:00
chained_irq_enter ( chip , desc ) ;
2016-05-21 15:23:39 +08:00
for ( eint_num = 0 ;
eint_num < pctl - > devdata - > ap_num ;
eint_num + = 32 , reg + = 4 ) {
2015-01-21 13:28:16 +08:00
status = readl ( reg ) ;
while ( status ) {
offset = __ffs ( status ) ;
index = eint_num + offset ;
virq = irq_find_mapping ( pctl - > domain , index ) ;
status & = ~ BIT ( offset ) ;
2015-01-27 14:15:26 +08:00
dual_edges = pctl - > eint_dual_edges [ index ] ;
if ( dual_edges ) {
/* Clear soft-irq in case we raised it
last time */
writel ( BIT ( offset ) , reg - eint_offsets - > stat +
eint_offsets - > soft_clr ) ;
pin = mtk_find_pin_by_eint_num ( pctl , index ) ;
start_level = mtk_gpio_get ( pctl - > chip ,
pin - > pin . number ) ;
}
2015-01-21 13:28:16 +08:00
generic_handle_irq ( virq ) ;
2015-01-27 14:15:26 +08:00
if ( dual_edges ) {
curr_level = mtk_eint_flip_edge ( pctl , index ) ;
/* If level changed, we might lost one edge
interrupt , raised it through soft - irq */
if ( start_level ! = curr_level )
writel ( BIT ( offset ) , reg -
eint_offsets - > stat +
eint_offsets - > soft_set ) ;
}
2015-01-21 13:28:16 +08:00
if ( index < pctl - > devdata - > db_cnt )
mtk_eint_debounce_process ( pctl , index ) ;
}
}
chained_irq_exit ( chip , desc ) ;
}
2015-01-21 13:28:15 +08:00
static int mtk_pctrl_build_state ( struct platform_device * pdev )
{
struct mtk_pinctrl * pctl = platform_get_drvdata ( pdev ) ;
int i ;
pctl - > ngroups = pctl - > devdata - > npins ;
/* Allocate groups */
2015-03-12 21:53:32 +08:00
pctl - > groups = devm_kcalloc ( & pdev - > dev , pctl - > ngroups ,
sizeof ( * pctl - > groups ) , GFP_KERNEL ) ;
2015-01-21 13:28:15 +08:00
if ( ! pctl - > groups )
return - ENOMEM ;
/* We assume that one pin is one group, use pin name as group name. */
2015-03-12 21:53:32 +08:00
pctl - > grp_names = devm_kcalloc ( & pdev - > dev , pctl - > ngroups ,
sizeof ( * pctl - > grp_names ) , GFP_KERNEL ) ;
2015-01-21 13:28:15 +08:00
if ( ! pctl - > grp_names )
return - ENOMEM ;
for ( i = 0 ; i < pctl - > devdata - > npins ; i + + ) {
const struct mtk_desc_pin * pin = pctl - > devdata - > pins + i ;
struct mtk_pinctrl_group * group = pctl - > groups + i ;
group - > name = pin - > pin . name ;
group - > pin = pin - > pin . number ;
pctl - > grp_names [ i ] = pin - > pin . name ;
}
return 0 ;
}
int mtk_pctrl_init ( struct platform_device * pdev ,
2015-05-18 23:11:17 -07:00
const struct mtk_pinctrl_devdata * data ,
struct regmap * regmap )
2015-01-21 13:28:15 +08:00
{
struct pinctrl_pin_desc * pins ;
struct mtk_pinctrl * pctl ;
struct device_node * np = pdev - > dev . of_node , * node ;
struct property * prop ;
2015-01-21 13:28:16 +08:00
struct resource * res ;
2015-08-14 16:38:06 +08:00
int i , ret , irq , ports_buf ;
2015-01-21 13:28:15 +08:00
pctl = devm_kzalloc ( & pdev - > dev , sizeof ( * pctl ) , GFP_KERNEL ) ;
if ( ! pctl )
return - ENOMEM ;
platform_set_drvdata ( pdev , pctl ) ;
prop = of_find_property ( np , " pins-are-numbered " , NULL ) ;
if ( ! prop ) {
2015-02-11 23:56:11 -08:00
dev_err ( & pdev - > dev , " only support pins-are-numbered format \n " ) ;
2015-01-21 13:28:15 +08:00
return - EINVAL ;
}
node = of_parse_phandle ( np , " mediatek,pctl-regmap " , 0 ) ;
if ( node ) {
pctl - > regmap1 = syscon_node_to_regmap ( node ) ;
if ( IS_ERR ( pctl - > regmap1 ) )
return PTR_ERR ( pctl - > regmap1 ) ;
2015-05-18 23:11:17 -07:00
} else if ( regmap ) {
pctl - > regmap1 = regmap ;
} else {
dev_err ( & pdev - > dev , " Pinctrl node has not register regmap. \n " ) ;
return - EINVAL ;
2015-01-21 13:28:15 +08:00
}
/* Only 8135 has two base addr, other SoCs have only one. */
node = of_parse_phandle ( np , " mediatek,pctl-regmap " , 1 ) ;
if ( node ) {
pctl - > regmap2 = syscon_node_to_regmap ( node ) ;
if ( IS_ERR ( pctl - > regmap2 ) )
return PTR_ERR ( pctl - > regmap2 ) ;
}
pctl - > devdata = data ;
ret = mtk_pctrl_build_state ( pdev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " build state failed: %d \n " , ret ) ;
return - EINVAL ;
}
2015-03-12 21:53:32 +08:00
pins = devm_kcalloc ( & pdev - > dev , pctl - > devdata - > npins , sizeof ( * pins ) ,
2015-01-21 13:28:15 +08:00
GFP_KERNEL ) ;
if ( ! pins )
return - ENOMEM ;
for ( i = 0 ; i < pctl - > devdata - > npins ; i + + )
pins [ i ] = pctl - > devdata - > pins [ i ] . pin ;
2015-08-25 17:32:45 -07:00
pctl - > pctl_desc . name = dev_name ( & pdev - > dev ) ;
pctl - > pctl_desc . owner = THIS_MODULE ;
pctl - > pctl_desc . pins = pins ;
pctl - > pctl_desc . npins = pctl - > devdata - > npins ;
pctl - > pctl_desc . confops = & mtk_pconf_ops ;
pctl - > pctl_desc . pctlops = & mtk_pctrl_ops ;
pctl - > pctl_desc . pmxops = & mtk_pmx_ops ;
2015-01-21 13:28:15 +08:00
pctl - > dev = & pdev - > dev ;
2015-08-25 17:32:45 -07:00
2016-02-28 14:43:15 +05:30
pctl - > pctl_dev = devm_pinctrl_register ( & pdev - > dev , & pctl - > pctl_desc ,
pctl ) ;
2015-06-09 13:01:16 +09:00
if ( IS_ERR ( pctl - > pctl_dev ) ) {
2015-01-21 13:28:15 +08:00
dev_err ( & pdev - > dev , " couldn't register pinctrl driver \n " ) ;
2015-06-09 13:01:16 +09:00
return PTR_ERR ( pctl - > pctl_dev ) ;
2015-01-21 13:28:15 +08:00
}
pctl - > chip = devm_kzalloc ( & pdev - > dev , sizeof ( * pctl - > chip ) , GFP_KERNEL ) ;
2016-02-28 14:43:15 +05:30
if ( ! pctl - > chip )
return - ENOMEM ;
2015-01-21 13:28:15 +08:00
2015-05-27 02:43:55 -07:00
* pctl - > chip = mtk_gpio_chip ;
2015-01-21 13:28:15 +08:00
pctl - > chip - > ngpio = pctl - > devdata - > npins ;
pctl - > chip - > label = dev_name ( & pdev - > dev ) ;
2015-11-04 09:56:26 +01:00
pctl - > chip - > parent = & pdev - > dev ;
2015-05-18 23:11:17 -07:00
pctl - > chip - > base = - 1 ;
2015-01-21 13:28:15 +08:00
2015-12-08 22:06:23 +01:00
ret = gpiochip_add_data ( pctl - > chip , pctl ) ;
2016-02-28 14:43:15 +05:30
if ( ret )
return - EINVAL ;
2015-01-21 13:28:15 +08:00
/* Register the GPIO to pin mappings. */
ret = gpiochip_add_pin_range ( pctl - > chip , dev_name ( & pdev - > dev ) ,
0 , 0 , pctl - > devdata - > npins ) ;
if ( ret ) {
ret = - EINVAL ;
goto chip_error ;
}
2015-05-27 02:43:55 -07:00
if ( ! of_property_read_bool ( np , " interrupt-controller " ) )
2015-05-18 23:11:17 -07:00
return 0 ;
2015-01-21 13:28:16 +08:00
/* Get EINT register base from dts. */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " Unable to get Pinctrl resource \n " ) ;
ret = - EINVAL ;
goto chip_error ;
}
pctl - > eint_reg_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( pctl - > eint_reg_base ) ) {
ret = - EINVAL ;
goto chip_error ;
}
2015-08-14 16:38:06 +08:00
ports_buf = pctl - > devdata - > eint_offsets . ports ;
pctl - > wake_mask = devm_kcalloc ( & pdev - > dev , ports_buf ,
sizeof ( * pctl - > wake_mask ) , GFP_KERNEL ) ;
if ( ! pctl - > wake_mask ) {
ret = - ENOMEM ;
goto chip_error ;
}
pctl - > cur_mask = devm_kcalloc ( & pdev - > dev , ports_buf ,
sizeof ( * pctl - > cur_mask ) , GFP_KERNEL ) ;
if ( ! pctl - > cur_mask ) {
ret = - ENOMEM ;
goto chip_error ;
}
2015-03-12 21:53:32 +08:00
pctl - > eint_dual_edges = devm_kcalloc ( & pdev - > dev , pctl - > devdata - > ap_num ,
sizeof ( int ) , GFP_KERNEL ) ;
2015-01-27 14:15:26 +08:00
if ( ! pctl - > eint_dual_edges ) {
ret = - ENOMEM ;
goto chip_error ;
}
2015-01-21 13:28:16 +08:00
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! irq ) {
dev_err ( & pdev - > dev , " couldn't parse and map irq \n " ) ;
ret = - EINVAL ;
2015-03-12 21:52:33 +08:00
goto chip_error ;
2015-01-21 13:28:16 +08:00
}
pctl - > domain = irq_domain_add_linear ( np ,
pctl - > devdata - > ap_num , & irq_domain_simple_ops , NULL ) ;
if ( ! pctl - > domain ) {
dev_err ( & pdev - > dev , " Couldn't register IRQ domain \n " ) ;
ret = - ENOMEM ;
2015-03-12 21:52:33 +08:00
goto chip_error ;
2015-01-21 13:28:16 +08:00
}
mtk_eint_init ( pctl ) ;
for ( i = 0 ; i < pctl - > devdata - > ap_num ; i + + ) {
int virq = irq_create_mapping ( pctl - > domain , i ) ;
irq_set_chip_and_handler ( virq , & mtk_pinctrl_irq_chip ,
handle_level_irq ) ;
irq_set_chip_data ( virq , pctl ) ;
2015-09-16 10:28:30 +02:00
}
2015-01-21 13:28:16 +08:00
pinctrl/mediatek: Fix race in installing chained IRQ handler
Fix a race where a pending interrupt could be received and the handler
called before the handler's data has been setup, by converting to
irq_set_chained_handler_and_data().
Search and conversion was done with coccinelle:
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
|
-irq_set_chained_handler(E1, E3);
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
...
|
-irq_set_chained_handler(E1, E3);
...
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Julia Lawall <Julia.Lawall@lip6.fr>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Hongzhou Yang <hongzhou.yang@mediatek.com>
Cc: Axel Lin <axel.lin@ingics.com>
Cc: Yingjoe Chen <yingjoe.chen@mediatek.com>
Cc: Maoguang Meng <maoguang.meng@mediatek.com>
Cc: Colin Ian King <colin.king@canonical.com>
Cc: linux-gpio@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mediatek@lists.infradead.org
2015-06-21 20:16:09 +02:00
irq_set_chained_handler_and_data ( irq , mtk_eint_irq_handler , pctl ) ;
2015-01-21 13:28:15 +08:00
return 0 ;
chip_error :
gpiochip_remove ( pctl - > chip ) ;
return ret ;
}