2019-05-09 11:58:51 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for STMicroelectronics Multi - Function eXpander ( STMFX ) GPIO expander
*
* Copyright ( C ) 2019 STMicroelectronics
* Author ( s ) : Amelie Delaunay < amelie . delaunay @ st . com > .
*/
# include <linux/gpio/driver.h>
# include <linux/interrupt.h>
# include <linux/mfd/stmfx.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/pinctrl/pinconf.h>
# include <linux/pinctrl/pinmux.h>
# include "core.h"
# include "pinctrl-utils.h"
/* GPIOs expander */
/* GPIO_STATE1 0x10, GPIO_STATE2 0x11, GPIO_STATE3 0x12 */
# define STMFX_REG_GPIO_STATE STMFX_REG_GPIO_STATE1 /* R */
/* GPIO_DIR1 0x60, GPIO_DIR2 0x61, GPIO_DIR3 0x63 */
# define STMFX_REG_GPIO_DIR STMFX_REG_GPIO_DIR1 /* RW */
/* GPIO_TYPE1 0x64, GPIO_TYPE2 0x65, GPIO_TYPE3 0x66 */
# define STMFX_REG_GPIO_TYPE STMFX_REG_GPIO_TYPE1 /* RW */
/* GPIO_PUPD1 0x68, GPIO_PUPD2 0x69, GPIO_PUPD3 0x6A */
# define STMFX_REG_GPIO_PUPD STMFX_REG_GPIO_PUPD1 /* RW */
/* GPO_SET1 0x6C, GPO_SET2 0x6D, GPO_SET3 0x6E */
# define STMFX_REG_GPO_SET STMFX_REG_GPO_SET1 /* RW */
/* GPO_CLR1 0x70, GPO_CLR2 0x71, GPO_CLR3 0x72 */
# define STMFX_REG_GPO_CLR STMFX_REG_GPO_CLR1 /* RW */
/* IRQ_GPI_SRC1 0x48, IRQ_GPI_SRC2 0x49, IRQ_GPI_SRC3 0x4A */
# define STMFX_REG_IRQ_GPI_SRC STMFX_REG_IRQ_GPI_SRC1 /* RW */
/* IRQ_GPI_EVT1 0x4C, IRQ_GPI_EVT2 0x4D, IRQ_GPI_EVT3 0x4E */
# define STMFX_REG_IRQ_GPI_EVT STMFX_REG_IRQ_GPI_EVT1 /* RW */
/* IRQ_GPI_TYPE1 0x50, IRQ_GPI_TYPE2 0x51, IRQ_GPI_TYPE3 0x52 */
# define STMFX_REG_IRQ_GPI_TYPE STMFX_REG_IRQ_GPI_TYPE1 /* RW */
/* IRQ_GPI_PENDING1 0x0C, IRQ_GPI_PENDING2 0x0D, IRQ_GPI_PENDING3 0x0E*/
# define STMFX_REG_IRQ_GPI_PENDING STMFX_REG_IRQ_GPI_PENDING1 /* R */
/* IRQ_GPI_ACK1 0x54, IRQ_GPI_ACK2 0x55, IRQ_GPI_ACK3 0x56 */
# define STMFX_REG_IRQ_GPI_ACK STMFX_REG_IRQ_GPI_ACK1 /* RW */
# define NR_GPIO_REGS 3
# define NR_GPIOS_PER_REG 8
# define get_reg(offset) ((offset) / NR_GPIOS_PER_REG)
# define get_shift(offset) ((offset) % NR_GPIOS_PER_REG)
# define get_mask(offset) (BIT(get_shift(offset)))
/*
* STMFX pinctrl can have up to 24 pins if STMFX other functions are not used .
* Pins availability is managed thanks to gpio - ranges property .
*/
static const struct pinctrl_pin_desc stmfx_pins [ ] = {
PINCTRL_PIN ( 0 , " gpio0 " ) ,
PINCTRL_PIN ( 1 , " gpio1 " ) ,
PINCTRL_PIN ( 2 , " gpio2 " ) ,
PINCTRL_PIN ( 3 , " gpio3 " ) ,
PINCTRL_PIN ( 4 , " gpio4 " ) ,
PINCTRL_PIN ( 5 , " gpio5 " ) ,
PINCTRL_PIN ( 6 , " gpio6 " ) ,
PINCTRL_PIN ( 7 , " gpio7 " ) ,
PINCTRL_PIN ( 8 , " gpio8 " ) ,
PINCTRL_PIN ( 9 , " gpio9 " ) ,
PINCTRL_PIN ( 10 , " gpio10 " ) ,
PINCTRL_PIN ( 11 , " gpio11 " ) ,
PINCTRL_PIN ( 12 , " gpio12 " ) ,
PINCTRL_PIN ( 13 , " gpio13 " ) ,
PINCTRL_PIN ( 14 , " gpio14 " ) ,
PINCTRL_PIN ( 15 , " gpio15 " ) ,
PINCTRL_PIN ( 16 , " agpio0 " ) ,
PINCTRL_PIN ( 17 , " agpio1 " ) ,
PINCTRL_PIN ( 18 , " agpio2 " ) ,
PINCTRL_PIN ( 19 , " agpio3 " ) ,
PINCTRL_PIN ( 20 , " agpio4 " ) ,
PINCTRL_PIN ( 21 , " agpio5 " ) ,
PINCTRL_PIN ( 22 , " agpio6 " ) ,
PINCTRL_PIN ( 23 , " agpio7 " ) ,
} ;
struct stmfx_pinctrl {
struct device * dev ;
struct stmfx * stmfx ;
struct pinctrl_dev * pctl_dev ;
struct pinctrl_desc pctl_desc ;
struct gpio_chip gpio_chip ;
struct irq_chip irq_chip ;
struct mutex lock ; /* IRQ bus lock */
unsigned long gpio_valid_mask ;
/* Cache of IRQ_GPI_* registers for bus_lock */
u8 irq_gpi_src [ NR_GPIO_REGS ] ;
u8 irq_gpi_type [ NR_GPIO_REGS ] ;
u8 irq_gpi_evt [ NR_GPIO_REGS ] ;
u8 irq_toggle_edge [ NR_GPIO_REGS ] ;
# ifdef CONFIG_PM
/* Backup of GPIO_* registers for suspend/resume */
u8 bkp_gpio_state [ NR_GPIO_REGS ] ;
u8 bkp_gpio_dir [ NR_GPIO_REGS ] ;
u8 bkp_gpio_type [ NR_GPIO_REGS ] ;
u8 bkp_gpio_pupd [ NR_GPIO_REGS ] ;
# endif
} ;
static int stmfx_gpio_get ( struct gpio_chip * gc , unsigned int offset )
{
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gc ) ;
u32 reg = STMFX_REG_GPIO_STATE + get_reg ( offset ) ;
u32 mask = get_mask ( offset ) ;
u32 value ;
int ret ;
ret = regmap_read ( pctl - > stmfx - > map , reg , & value ) ;
return ret ? ret : ! ! ( value & mask ) ;
}
static void stmfx_gpio_set ( struct gpio_chip * gc , unsigned int offset , int value )
{
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gc ) ;
u32 reg = value ? STMFX_REG_GPO_SET : STMFX_REG_GPO_CLR ;
u32 mask = get_mask ( offset ) ;
regmap_write_bits ( pctl - > stmfx - > map , reg + get_reg ( offset ) ,
mask , mask ) ;
}
static int stmfx_gpio_get_direction ( struct gpio_chip * gc , unsigned int offset )
{
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gc ) ;
u32 reg = STMFX_REG_GPIO_DIR + get_reg ( offset ) ;
u32 mask = get_mask ( offset ) ;
u32 val ;
int ret ;
ret = regmap_read ( pctl - > stmfx - > map , reg , & val ) ;
/*
* On stmfx , gpio pins direction is ( 0 ) input , ( 1 ) output .
*/
2020-02-14 16:57:12 +03:00
if ( ret )
return ret ;
2019-05-09 11:58:51 +03:00
2020-02-14 16:57:12 +03:00
if ( val & mask )
return GPIO_LINE_DIRECTION_OUT ;
return GPIO_LINE_DIRECTION_IN ;
2019-05-09 11:58:51 +03:00
}
static int stmfx_gpio_direction_input ( struct gpio_chip * gc , unsigned int offset )
{
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gc ) ;
u32 reg = STMFX_REG_GPIO_DIR + get_reg ( offset ) ;
u32 mask = get_mask ( offset ) ;
return regmap_write_bits ( pctl - > stmfx - > map , reg , mask , 0 ) ;
}
static int stmfx_gpio_direction_output ( struct gpio_chip * gc ,
unsigned int offset , int value )
{
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gc ) ;
u32 reg = STMFX_REG_GPIO_DIR + get_reg ( offset ) ;
u32 mask = get_mask ( offset ) ;
stmfx_gpio_set ( gc , offset , value ) ;
return regmap_write_bits ( pctl - > stmfx - > map , reg , mask , mask ) ;
}
static int stmfx_pinconf_get_pupd ( struct stmfx_pinctrl * pctl ,
unsigned int offset )
{
u32 reg = STMFX_REG_GPIO_PUPD + get_reg ( offset ) ;
u32 pupd , mask = get_mask ( offset ) ;
int ret ;
ret = regmap_read ( pctl - > stmfx - > map , reg , & pupd ) ;
if ( ret )
return ret ;
return ! ! ( pupd & mask ) ;
}
static int stmfx_pinconf_set_pupd ( struct stmfx_pinctrl * pctl ,
unsigned int offset , u32 pupd )
{
u32 reg = STMFX_REG_GPIO_PUPD + get_reg ( offset ) ;
u32 mask = get_mask ( offset ) ;
return regmap_write_bits ( pctl - > stmfx - > map , reg , mask , pupd ? mask : 0 ) ;
}
static int stmfx_pinconf_get_type ( struct stmfx_pinctrl * pctl ,
unsigned int offset )
{
u32 reg = STMFX_REG_GPIO_TYPE + get_reg ( offset ) ;
u32 type , mask = get_mask ( offset ) ;
int ret ;
ret = regmap_read ( pctl - > stmfx - > map , reg , & type ) ;
if ( ret )
return ret ;
return ! ! ( type & mask ) ;
}
static int stmfx_pinconf_set_type ( struct stmfx_pinctrl * pctl ,
unsigned int offset , u32 type )
{
u32 reg = STMFX_REG_GPIO_TYPE + get_reg ( offset ) ;
u32 mask = get_mask ( offset ) ;
return regmap_write_bits ( pctl - > stmfx - > map , reg , mask , type ? mask : 0 ) ;
}
static int stmfx_pinconf_get ( struct pinctrl_dev * pctldev ,
unsigned int pin , unsigned long * config )
{
struct stmfx_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
u32 param = pinconf_to_config_param ( * config ) ;
struct pinctrl_gpio_range * range ;
u32 arg = 0 ;
2019-05-13 09:36:16 +03:00
int ret , dir , type , pupd ;
2019-05-09 11:58:51 +03:00
range = pinctrl_find_gpio_range_from_pin_nolock ( pctldev , pin ) ;
if ( ! range )
return - EINVAL ;
dir = stmfx_gpio_get_direction ( & pctl - > gpio_chip , pin ) ;
if ( dir < 0 )
return dir ;
2020-02-14 16:57:12 +03:00
/*
* Currently the gpiolib IN is 1 and OUT is 0 but let ' s not count
* on it just to be on the safe side also in the future : )
*/
dir = ( dir = = GPIO_LINE_DIRECTION_IN ) ? 1 : 0 ;
2019-05-09 11:58:51 +03:00
type = stmfx_pinconf_get_type ( pctl , pin ) ;
if ( type < 0 )
return type ;
pupd = stmfx_pinconf_get_pupd ( pctl , pin ) ;
if ( pupd < 0 )
return pupd ;
switch ( param ) {
case PIN_CONFIG_BIAS_DISABLE :
if ( ( ! dir & & ( ! type | | ! pupd ) ) | | ( dir & & ! type ) )
arg = 1 ;
break ;
case PIN_CONFIG_BIAS_PULL_DOWN :
if ( dir & & type & & ! pupd )
arg = 1 ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
if ( type & & pupd )
arg = 1 ;
break ;
case PIN_CONFIG_DRIVE_OPEN_DRAIN :
if ( ( ! dir & & type ) | | ( dir & & ! type ) )
arg = 1 ;
break ;
case PIN_CONFIG_DRIVE_PUSH_PULL :
if ( ( ! dir & & ! type ) | | ( dir & & type ) )
arg = 1 ;
break ;
case PIN_CONFIG_OUTPUT :
if ( dir )
return - EINVAL ;
ret = stmfx_gpio_get ( & pctl - > gpio_chip , pin ) ;
if ( ret < 0 )
return ret ;
arg = ret ;
break ;
default :
return - ENOTSUPP ;
}
* config = pinconf_to_config_packed ( param , arg ) ;
return 0 ;
}
static int stmfx_pinconf_set ( struct pinctrl_dev * pctldev , unsigned int pin ,
unsigned long * configs , unsigned int num_configs )
{
struct stmfx_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct pinctrl_gpio_range * range ;
enum pin_config_param param ;
u32 arg ;
2020-04-22 10:25:13 +03:00
int i , ret ;
2019-05-09 11:58:51 +03:00
range = pinctrl_find_gpio_range_from_pin_nolock ( pctldev , pin ) ;
if ( ! range ) {
dev_err ( pctldev - > dev , " pin %d is not available \n " , pin ) ;
return - EINVAL ;
}
for ( i = 0 ; i < num_configs ; i + + ) {
param = pinconf_to_config_param ( configs [ i ] ) ;
arg = pinconf_to_config_argument ( configs [ i ] ) ;
switch ( param ) {
case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT :
case PIN_CONFIG_BIAS_DISABLE :
2019-07-25 14:16:56 +03:00
case PIN_CONFIG_DRIVE_PUSH_PULL :
ret = stmfx_pinconf_set_type ( pctl , pin , 0 ) ;
if ( ret )
return ret ;
break ;
2019-05-09 11:58:51 +03:00
case PIN_CONFIG_BIAS_PULL_DOWN :
2019-07-25 14:16:56 +03:00
ret = stmfx_pinconf_set_type ( pctl , pin , 1 ) ;
if ( ret )
return ret ;
2019-05-09 11:58:51 +03:00
ret = stmfx_pinconf_set_pupd ( pctl , pin , 0 ) ;
if ( ret )
return ret ;
break ;
case PIN_CONFIG_BIAS_PULL_UP :
2019-07-25 14:16:56 +03:00
ret = stmfx_pinconf_set_type ( pctl , pin , 1 ) ;
2019-05-09 11:58:51 +03:00
if ( ret )
return ret ;
2019-07-25 14:16:56 +03:00
ret = stmfx_pinconf_set_pupd ( pctl , pin , 1 ) ;
2019-05-09 11:58:51 +03:00
if ( ret )
return ret ;
break ;
2019-07-25 14:16:56 +03:00
case PIN_CONFIG_DRIVE_OPEN_DRAIN :
ret = stmfx_pinconf_set_type ( pctl , pin , 1 ) ;
2019-05-09 11:58:51 +03:00
if ( ret )
return ret ;
break ;
case PIN_CONFIG_OUTPUT :
ret = stmfx_gpio_direction_output ( & pctl - > gpio_chip ,
pin , arg ) ;
if ( ret )
return ret ;
break ;
default :
return - ENOTSUPP ;
}
}
return 0 ;
}
static void stmfx_pinconf_dbg_show ( struct pinctrl_dev * pctldev ,
struct seq_file * s , unsigned int offset )
{
struct stmfx_pinctrl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
struct pinctrl_gpio_range * range ;
int dir , type , pupd , val ;
range = pinctrl_find_gpio_range_from_pin_nolock ( pctldev , offset ) ;
if ( ! range )
return ;
dir = stmfx_gpio_get_direction ( & pctl - > gpio_chip , offset ) ;
if ( dir < 0 )
return ;
type = stmfx_pinconf_get_type ( pctl , offset ) ;
if ( type < 0 )
return ;
pupd = stmfx_pinconf_get_pupd ( pctl , offset ) ;
if ( pupd < 0 )
return ;
val = stmfx_gpio_get ( & pctl - > gpio_chip , offset ) ;
if ( val < 0 )
return ;
2020-02-14 16:57:12 +03:00
if ( dir = = GPIO_LINE_DIRECTION_OUT ) {
2019-05-09 11:58:51 +03:00
seq_printf ( s , " output %s " , val ? " high " : " low " ) ;
if ( type )
seq_printf ( s , " open drain %s internal pull-up " ,
pupd ? " with " : " without " ) ;
else
seq_puts ( s , " push pull no pull " ) ;
} else {
seq_printf ( s , " input %s " , val ? " high " : " low " ) ;
if ( type )
seq_printf ( s , " with internal pull-%s " ,
pupd ? " up " : " down " ) ;
else
seq_printf ( s , " %s " , pupd ? " floating " : " analog " ) ;
}
}
static const struct pinconf_ops stmfx_pinconf_ops = {
. pin_config_get = stmfx_pinconf_get ,
. pin_config_set = stmfx_pinconf_set ,
. pin_config_dbg_show = stmfx_pinconf_dbg_show ,
} ;
static int stmfx_pinctrl_get_groups_count ( struct pinctrl_dev * pctldev )
{
return 0 ;
}
static const char * stmfx_pinctrl_get_group_name ( struct pinctrl_dev * pctldev ,
unsigned int selector )
{
return NULL ;
}
static int stmfx_pinctrl_get_group_pins ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
const unsigned int * * pins ,
unsigned int * num_pins )
{
return - ENOTSUPP ;
}
static const struct pinctrl_ops stmfx_pinctrl_ops = {
. get_groups_count = stmfx_pinctrl_get_groups_count ,
. get_group_name = stmfx_pinctrl_get_group_name ,
. get_group_pins = stmfx_pinctrl_get_group_pins ,
. dt_node_to_map = pinconf_generic_dt_node_to_map_pin ,
. dt_free_map = pinctrl_utils_free_map ,
} ;
static void stmfx_pinctrl_irq_mask ( struct irq_data * data )
{
struct gpio_chip * gpio_chip = irq_data_get_irq_chip_data ( data ) ;
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gpio_chip ) ;
u32 reg = get_reg ( data - > hwirq ) ;
u32 mask = get_mask ( data - > hwirq ) ;
pctl - > irq_gpi_src [ reg ] & = ~ mask ;
}
static void stmfx_pinctrl_irq_unmask ( struct irq_data * data )
{
struct gpio_chip * gpio_chip = irq_data_get_irq_chip_data ( data ) ;
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gpio_chip ) ;
u32 reg = get_reg ( data - > hwirq ) ;
u32 mask = get_mask ( data - > hwirq ) ;
pctl - > irq_gpi_src [ reg ] | = mask ;
}
static int stmfx_pinctrl_irq_set_type ( struct irq_data * data , unsigned int type )
{
struct gpio_chip * gpio_chip = irq_data_get_irq_chip_data ( data ) ;
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gpio_chip ) ;
u32 reg = get_reg ( data - > hwirq ) ;
u32 mask = get_mask ( data - > hwirq ) ;
2019-05-13 09:38:04 +03:00
if ( type = = IRQ_TYPE_NONE )
2019-05-09 11:58:51 +03:00
return - EINVAL ;
if ( type & IRQ_TYPE_EDGE_BOTH ) {
pctl - > irq_gpi_evt [ reg ] | = mask ;
irq_set_handler_locked ( data , handle_edge_irq ) ;
} else {
pctl - > irq_gpi_evt [ reg ] & = ~ mask ;
irq_set_handler_locked ( data , handle_level_irq ) ;
}
if ( ( type & IRQ_TYPE_EDGE_RISING ) | | ( type & IRQ_TYPE_LEVEL_HIGH ) )
pctl - > irq_gpi_type [ reg ] | = mask ;
else
pctl - > irq_gpi_type [ reg ] & = ~ mask ;
/*
* In case of ( type & IRQ_TYPE_EDGE_BOTH ) , we need to know current
* GPIO value to set the right edge trigger . But in atomic context
* here we can ' t access registers over I2C . That ' s why ( type &
* IRQ_TYPE_EDGE_BOTH ) will be managed in . irq_sync_unlock .
*/
if ( ( type & IRQ_TYPE_EDGE_BOTH ) = = IRQ_TYPE_EDGE_BOTH )
pctl - > irq_toggle_edge [ reg ] | = mask ;
else
pctl - > irq_toggle_edge [ reg ] & = mask ;
return 0 ;
}
static void stmfx_pinctrl_irq_bus_lock ( struct irq_data * data )
{
struct gpio_chip * gpio_chip = irq_data_get_irq_chip_data ( data ) ;
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gpio_chip ) ;
mutex_lock ( & pctl - > lock ) ;
}
static void stmfx_pinctrl_irq_bus_sync_unlock ( struct irq_data * data )
{
struct gpio_chip * gpio_chip = irq_data_get_irq_chip_data ( data ) ;
struct stmfx_pinctrl * pctl = gpiochip_get_data ( gpio_chip ) ;
u32 reg = get_reg ( data - > hwirq ) ;
u32 mask = get_mask ( data - > hwirq ) ;
/*
* In case of IRQ_TYPE_EDGE_BOTH ) , read the current GPIO value
* ( this couldn ' t be done in . irq_set_type because of atomic context )
* to set the right irq trigger type .
*/
if ( pctl - > irq_toggle_edge [ reg ] & mask ) {
if ( stmfx_gpio_get ( gpio_chip , data - > hwirq ) )
pctl - > irq_gpi_type [ reg ] & = ~ mask ;
else
pctl - > irq_gpi_type [ reg ] | = mask ;
}
regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_EVT ,
pctl - > irq_gpi_evt , NR_GPIO_REGS ) ;
regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_TYPE ,
pctl - > irq_gpi_type , NR_GPIO_REGS ) ;
regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_SRC ,
pctl - > irq_gpi_src , NR_GPIO_REGS ) ;
mutex_unlock ( & pctl - > lock ) ;
}
2019-10-09 12:16:06 +03:00
static int stmfx_gpio_irq_request_resources ( struct irq_data * data )
{
struct gpio_chip * gpio_chip = irq_data_get_irq_chip_data ( data ) ;
int ret ;
ret = stmfx_gpio_direction_input ( gpio_chip , data - > hwirq ) ;
if ( ret )
return ret ;
return gpiochip_reqres_irq ( gpio_chip , data - > hwirq ) ;
}
static void stmfx_gpio_irq_release_resources ( struct irq_data * data )
{
struct gpio_chip * gpio_chip = irq_data_get_irq_chip_data ( data ) ;
return gpiochip_relres_irq ( gpio_chip , data - > hwirq ) ;
}
2019-05-09 11:58:51 +03:00
static void stmfx_pinctrl_irq_toggle_trigger ( struct stmfx_pinctrl * pctl ,
unsigned int offset )
{
u32 reg = get_reg ( offset ) ;
u32 mask = get_mask ( offset ) ;
int val ;
if ( ! ( pctl - > irq_toggle_edge [ reg ] & mask ) )
return ;
val = stmfx_gpio_get ( & pctl - > gpio_chip , offset ) ;
if ( val < 0 )
return ;
if ( val ) {
pctl - > irq_gpi_type [ reg ] & = mask ;
regmap_write_bits ( pctl - > stmfx - > map ,
STMFX_REG_IRQ_GPI_TYPE + reg ,
mask , 0 ) ;
} else {
pctl - > irq_gpi_type [ reg ] | = mask ;
regmap_write_bits ( pctl - > stmfx - > map ,
STMFX_REG_IRQ_GPI_TYPE + reg ,
mask , mask ) ;
}
}
static irqreturn_t stmfx_pinctrl_irq_thread_fn ( int irq , void * dev_id )
{
struct stmfx_pinctrl * pctl = ( struct stmfx_pinctrl * ) dev_id ;
struct gpio_chip * gc = & pctl - > gpio_chip ;
u8 pending [ NR_GPIO_REGS ] ;
u8 src [ NR_GPIO_REGS ] = { 0 , 0 , 0 } ;
unsigned long n , status ;
2021-07-25 21:08:30 +03:00
int i , ret ;
2019-05-09 11:58:51 +03:00
ret = regmap_bulk_read ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_PENDING ,
& pending , NR_GPIO_REGS ) ;
if ( ret )
return IRQ_NONE ;
regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_SRC ,
src , NR_GPIO_REGS ) ;
2021-07-25 21:08:30 +03:00
BUILD_BUG_ON ( NR_GPIO_REGS > sizeof ( status ) ) ;
for ( i = 0 , status = 0 ; i < NR_GPIO_REGS ; i + + )
status | = ( unsigned long ) pending [ i ] < < ( i * 8 ) ;
2019-05-09 11:58:51 +03:00
for_each_set_bit ( n , & status , gc - > ngpio ) {
handle_nested_irq ( irq_find_mapping ( gc - > irq . domain , n ) ) ;
stmfx_pinctrl_irq_toggle_trigger ( pctl , n ) ;
}
regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_SRC ,
pctl - > irq_gpi_src , NR_GPIO_REGS ) ;
return IRQ_HANDLED ;
}
static int stmfx_pinctrl_gpio_function_enable ( struct stmfx_pinctrl * pctl )
{
struct pinctrl_gpio_range * gpio_range ;
struct pinctrl_dev * pctl_dev = pctl - > pctl_dev ;
u32 func = STMFX_FUNC_GPIO ;
pctl - > gpio_valid_mask = GENMASK ( 15 , 0 ) ;
gpio_range = pinctrl_find_gpio_range_from_pin ( pctl_dev , 16 ) ;
if ( gpio_range ) {
func | = STMFX_FUNC_ALTGPIO_LOW ;
pctl - > gpio_valid_mask | = GENMASK ( 19 , 16 ) ;
}
gpio_range = pinctrl_find_gpio_range_from_pin ( pctl_dev , 20 ) ;
if ( gpio_range ) {
func | = STMFX_FUNC_ALTGPIO_HIGH ;
pctl - > gpio_valid_mask | = GENMASK ( 23 , 20 ) ;
}
return stmfx_function_enable ( pctl - > stmfx , func ) ;
}
static int stmfx_pinctrl_probe ( struct platform_device * pdev )
{
struct stmfx * stmfx = dev_get_drvdata ( pdev - > dev . parent ) ;
struct device_node * np = pdev - > dev . of_node ;
struct stmfx_pinctrl * pctl ;
2020-07-21 16:18:14 +03:00
struct gpio_irq_chip * girq ;
2019-05-09 11:58:51 +03:00
int irq , ret ;
pctl = devm_kzalloc ( stmfx - > dev , sizeof ( * pctl ) , GFP_KERNEL ) ;
if ( ! pctl )
return - ENOMEM ;
platform_set_drvdata ( pdev , pctl ) ;
pctl - > dev = & pdev - > dev ;
pctl - > stmfx = stmfx ;
if ( ! of_find_property ( np , " gpio-ranges " , NULL ) ) {
dev_err ( pctl - > dev , " missing required gpio-ranges property \n " ) ;
return - EINVAL ;
}
irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:33 +03:00
if ( irq < = 0 )
2019-05-09 11:58:51 +03:00
return - ENXIO ;
mutex_init ( & pctl - > lock ) ;
/* Register pin controller */
pctl - > pctl_desc . name = " stmfx-pinctrl " ;
pctl - > pctl_desc . pctlops = & stmfx_pinctrl_ops ;
pctl - > pctl_desc . confops = & stmfx_pinconf_ops ;
pctl - > pctl_desc . pins = stmfx_pins ;
pctl - > pctl_desc . npins = ARRAY_SIZE ( stmfx_pins ) ;
pctl - > pctl_desc . owner = THIS_MODULE ;
2019-05-22 18:29:25 +03:00
pctl - > pctl_desc . link_consumers = true ;
2019-05-09 11:58:51 +03:00
ret = devm_pinctrl_register_and_init ( pctl - > dev , & pctl - > pctl_desc ,
pctl , & pctl - > pctl_dev ) ;
if ( ret ) {
dev_err ( pctl - > dev , " pinctrl registration failed \n " ) ;
return ret ;
}
ret = pinctrl_enable ( pctl - > pctl_dev ) ;
if ( ret ) {
dev_err ( pctl - > dev , " pinctrl enable failed \n " ) ;
return ret ;
}
/* Register gpio controller */
pctl - > gpio_chip . label = " stmfx-gpio " ;
pctl - > gpio_chip . parent = pctl - > dev ;
pctl - > gpio_chip . get_direction = stmfx_gpio_get_direction ;
pctl - > gpio_chip . direction_input = stmfx_gpio_direction_input ;
pctl - > gpio_chip . direction_output = stmfx_gpio_direction_output ;
pctl - > gpio_chip . get = stmfx_gpio_get ;
pctl - > gpio_chip . set = stmfx_gpio_set ;
pctl - > gpio_chip . set_config = gpiochip_generic_config ;
pctl - > gpio_chip . base = - 1 ;
pctl - > gpio_chip . ngpio = pctl - > pctl_desc . npins ;
pctl - > gpio_chip . can_sleep = true ;
pctl - > irq_chip . name = dev_name ( pctl - > dev ) ;
pctl - > irq_chip . irq_mask = stmfx_pinctrl_irq_mask ;
pctl - > irq_chip . irq_unmask = stmfx_pinctrl_irq_unmask ;
pctl - > irq_chip . irq_set_type = stmfx_pinctrl_irq_set_type ;
pctl - > irq_chip . irq_bus_lock = stmfx_pinctrl_irq_bus_lock ;
pctl - > irq_chip . irq_bus_sync_unlock = stmfx_pinctrl_irq_bus_sync_unlock ;
2019-10-09 12:16:06 +03:00
pctl - > irq_chip . irq_request_resources = stmfx_gpio_irq_request_resources ;
pctl - > irq_chip . irq_release_resources = stmfx_gpio_irq_release_resources ;
2019-05-09 11:58:51 +03:00
2020-07-21 16:18:14 +03:00
girq = & pctl - > gpio_chip . irq ;
girq - > chip = & pctl - > irq_chip ;
/* This will let us handle the parent IRQ in the driver */
girq - > parent_handler = NULL ;
girq - > num_parents = 0 ;
girq - > parents = NULL ;
girq - > default_type = IRQ_TYPE_NONE ;
girq - > handler = handle_bad_irq ;
girq - > threaded = true ;
ret = devm_gpiochip_add_data ( pctl - > dev , & pctl - > gpio_chip , pctl ) ;
2019-05-09 11:58:51 +03:00
if ( ret ) {
2020-07-21 16:18:14 +03:00
dev_err ( pctl - > dev , " gpio_chip registration failed \n " ) ;
2019-05-09 11:58:51 +03:00
return ret ;
}
2020-07-21 16:18:14 +03:00
ret = stmfx_pinctrl_gpio_function_enable ( pctl ) ;
if ( ret )
return ret ;
2019-05-09 11:58:51 +03:00
ret = devm_request_threaded_irq ( pctl - > dev , irq , NULL ,
stmfx_pinctrl_irq_thread_fn ,
IRQF_ONESHOT ,
pctl - > irq_chip . name , pctl ) ;
if ( ret ) {
dev_err ( pctl - > dev , " cannot request irq%d \n " , irq ) ;
return ret ;
}
dev_info ( pctl - > dev ,
" %ld GPIOs available \n " , hweight_long ( pctl - > gpio_valid_mask ) ) ;
return 0 ;
}
static int stmfx_pinctrl_remove ( struct platform_device * pdev )
{
2019-10-04 15:23:42 +03:00
struct stmfx * stmfx = dev_get_drvdata ( pdev - > dev . parent ) ;
2019-05-09 11:58:51 +03:00
return stmfx_function_disable ( stmfx ,
STMFX_FUNC_GPIO |
STMFX_FUNC_ALTGPIO_LOW |
STMFX_FUNC_ALTGPIO_HIGH ) ;
}
# ifdef CONFIG_PM_SLEEP
static int stmfx_pinctrl_backup_regs ( struct stmfx_pinctrl * pctl )
{
int ret ;
ret = regmap_bulk_read ( pctl - > stmfx - > map , STMFX_REG_GPIO_STATE ,
& pctl - > bkp_gpio_state , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_read ( pctl - > stmfx - > map , STMFX_REG_GPIO_DIR ,
& pctl - > bkp_gpio_dir , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_read ( pctl - > stmfx - > map , STMFX_REG_GPIO_TYPE ,
& pctl - > bkp_gpio_type , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_read ( pctl - > stmfx - > map , STMFX_REG_GPIO_PUPD ,
& pctl - > bkp_gpio_pupd , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
return 0 ;
}
static int stmfx_pinctrl_restore_regs ( struct stmfx_pinctrl * pctl )
{
int ret ;
ret = regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_GPIO_DIR ,
pctl - > bkp_gpio_dir , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_GPIO_TYPE ,
pctl - > bkp_gpio_type , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_GPIO_PUPD ,
pctl - > bkp_gpio_pupd , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_GPO_SET ,
pctl - > bkp_gpio_state , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_EVT ,
pctl - > irq_gpi_evt , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_TYPE ,
pctl - > irq_gpi_type , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
ret = regmap_bulk_write ( pctl - > stmfx - > map , STMFX_REG_IRQ_GPI_SRC ,
pctl - > irq_gpi_src , NR_GPIO_REGS ) ;
if ( ret )
return ret ;
return 0 ;
}
static int stmfx_pinctrl_suspend ( struct device * dev )
{
struct stmfx_pinctrl * pctl = dev_get_drvdata ( dev ) ;
int ret ;
ret = stmfx_pinctrl_backup_regs ( pctl ) ;
if ( ret ) {
dev_err ( pctl - > dev , " registers backup failure \n " ) ;
return ret ;
}
return 0 ;
}
static int stmfx_pinctrl_resume ( struct device * dev )
{
struct stmfx_pinctrl * pctl = dev_get_drvdata ( dev ) ;
int ret ;
ret = stmfx_pinctrl_restore_regs ( pctl ) ;
if ( ret ) {
dev_err ( pctl - > dev , " registers restoration failure \n " ) ;
return ret ;
}
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( stmfx_pinctrl_dev_pm_ops ,
stmfx_pinctrl_suspend , stmfx_pinctrl_resume ) ;
static const struct of_device_id stmfx_pinctrl_of_match [ ] = {
{ . compatible = " st,stmfx-0300-pinctrl " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stmfx_pinctrl_of_match ) ;
static struct platform_driver stmfx_pinctrl_driver = {
. driver = {
. name = " stmfx-pinctrl " ,
. of_match_table = stmfx_pinctrl_of_match ,
. pm = & stmfx_pinctrl_dev_pm_ops ,
} ,
. probe = stmfx_pinctrl_probe ,
. remove = stmfx_pinctrl_remove ,
} ;
module_platform_driver ( stmfx_pinctrl_driver ) ;
MODULE_DESCRIPTION ( " STMFX pinctrl/GPIO driver " ) ;
MODULE_AUTHOR ( " Amelie Delaunay <amelie.delaunay@st.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;