@ -20,6 +20,9 @@
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
# include <linux/pinctrl/pinconf.h>
# include <linux/interrupt.h>
# include <linux/irqdomain.h>
# include <linux/of_irq.h>
# include "core.h"
/* EXYNOS5440 GPIO and Pinctrl register offsets */
@ -37,6 +40,7 @@
# define GPIO_DS1 0x2C
# define EXYNOS5440_MAX_PINS 23
# define EXYNOS5440_MAX_GPIO_INT 8
# define PIN_NAME_LENGTH 10
# define GROUP_SUFFIX "-grp"
@ -109,6 +113,7 @@ struct exynos5440_pmx_func {
struct exynos5440_pinctrl_priv_data {
void __iomem * reg_base ;
struct gpio_chip * gc ;
struct irq_domain * irq_domain ;
const struct exynos5440_pin_group * pin_groups ;
unsigned int nr_groups ;
@ -116,6 +121,16 @@ struct exynos5440_pinctrl_priv_data {
unsigned int nr_functions ;
} ;
/**
* struct exynos5440_gpio_intr_data : private data for gpio interrupts .
* @ priv : driver ' s private runtime data .
* @ gpio_int : gpio interrupt number .
*/
struct exynos5440_gpio_intr_data {
struct exynos5440_pinctrl_priv_data * priv ;
unsigned int gpio_int ;
} ;
/* list of all possible config options supported */
static struct pin_config {
char * prop_cfg ;
@ -598,6 +613,22 @@ static int exynos5440_gpio_direction_output(struct gpio_chip *gc, unsigned offse
return 0 ;
}
/* gpiolib gpio_to_irq callback function */
static int exynos5440_gpio_to_irq ( struct gpio_chip * gc , unsigned offset )
{
struct exynos5440_pinctrl_priv_data * priv = dev_get_drvdata ( gc - > dev ) ;
unsigned int virq ;
if ( offset < 16 | | offset > 23 )
return - ENXIO ;
if ( ! priv - > irq_domain )
return - ENXIO ;
virq = irq_create_mapping ( priv - > irq_domain , offset - 16 ) ;
return virq ? : - ENXIO ;
}
/* parse the pin numbers listed in the 'samsung,exynos5440-pins' property */
static int exynos5440_pinctrl_parse_dt_pins ( struct platform_device * pdev ,
struct device_node * cfg_np , unsigned int * * pin_list ,
@ -821,6 +852,7 @@ static int exynos5440_gpiolib_register(struct platform_device *pdev,
gc - > get = exynos5440_gpio_get ;
gc - > direction_input = exynos5440_gpio_direction_input ;
gc - > direction_output = exynos5440_gpio_direction_output ;
gc - > to_irq = exynos5440_gpio_to_irq ;
gc - > label = " gpiolib-exynos5440 " ;
gc - > owner = THIS_MODULE ;
ret = gpiochip_add ( gc ) ;
@ -845,6 +877,110 @@ static int exynos5440_gpiolib_unregister(struct platform_device *pdev,
return 0 ;
}
static void exynos5440_gpio_irq_unmask ( struct irq_data * irqd )
{
struct exynos5440_pinctrl_priv_data * d ;
unsigned long gpio_int ;
d = irq_data_get_irq_chip_data ( irqd ) ;
gpio_int = readl ( d - > reg_base + GPIO_INT ) ;
gpio_int | = 1 < < irqd - > hwirq ;
writel ( gpio_int , d - > reg_base + GPIO_INT ) ;
}
static void exynos5440_gpio_irq_mask ( struct irq_data * irqd )
{
struct exynos5440_pinctrl_priv_data * d ;
unsigned long gpio_int ;
d = irq_data_get_irq_chip_data ( irqd ) ;
gpio_int = readl ( d - > reg_base + GPIO_INT ) ;
gpio_int & = ~ ( 1 < < irqd - > hwirq ) ;
writel ( gpio_int , d - > reg_base + GPIO_INT ) ;
}
/* irq_chip for gpio interrupts */
static struct irq_chip exynos5440_gpio_irq_chip = {
. name = " exynos5440_gpio_irq_chip " ,
. irq_unmask = exynos5440_gpio_irq_unmask ,
. irq_mask = exynos5440_gpio_irq_mask ,
} ;
/* interrupt handler for GPIO interrupts 0..7 */
static irqreturn_t exynos5440_gpio_irq ( int irq , void * data )
{
struct exynos5440_gpio_intr_data * intd = data ;
struct exynos5440_pinctrl_priv_data * d = intd - > priv ;
int virq ;
virq = irq_linear_revmap ( d - > irq_domain , intd - > gpio_int ) ;
if ( ! virq )
return IRQ_NONE ;
generic_handle_irq ( virq ) ;
return IRQ_HANDLED ;
}
static int exynos5440_gpio_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
{
struct exynos5440_pinctrl_priv_data * d = h - > host_data ;
irq_set_chip_data ( virq , d ) ;
irq_set_chip_and_handler ( virq , & exynos5440_gpio_irq_chip ,
handle_level_irq ) ;
set_irq_flags ( virq , IRQF_VALID ) ;
return 0 ;
}
/* irq domain callbacks for gpio interrupt controller */
static const struct irq_domain_ops exynos5440_gpio_irqd_ops = {
. map = exynos5440_gpio_irq_map ,
. xlate = irq_domain_xlate_twocell ,
} ;
/* setup handling of gpio interrupts */
static int exynos5440_gpio_irq_init ( struct platform_device * pdev ,
struct exynos5440_pinctrl_priv_data * priv )
{
struct device * dev = & pdev - > dev ;
struct exynos5440_gpio_intr_data * intd ;
int i , irq , ret ;
intd = devm_kzalloc ( dev , sizeof ( * intd ) * EXYNOS5440_MAX_GPIO_INT ,
GFP_KERNEL ) ;
if ( ! intd ) {
dev_err ( dev , " failed to allocate memory for gpio intr data \n " ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < EXYNOS5440_MAX_GPIO_INT ; i + + ) {
irq = irq_of_parse_and_map ( dev - > of_node , i ) ;
if ( irq < = 0 ) {
dev_err ( dev , " irq parsing failed \n " ) ;
return - EINVAL ;
}
intd - > gpio_int = i ;
intd - > priv = priv ;
ret = devm_request_irq ( dev , irq , exynos5440_gpio_irq ,
0 , dev_name ( dev ) , intd + + ) ;
if ( ret ) {
dev_err ( dev , " irq request failed \n " ) ;
return - ENXIO ;
}
}
priv - > irq_domain = irq_domain_add_linear ( dev - > of_node ,
EXYNOS5440_MAX_GPIO_INT ,
& exynos5440_gpio_irqd_ops , priv ) ;
if ( ! priv - > irq_domain ) {
dev_err ( dev , " failed to create irq domain \n " ) ;
return - ENXIO ;
}
return 0 ;
}
static int exynos5440_pinctrl_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
@ -883,6 +1019,12 @@ static int exynos5440_pinctrl_probe(struct platform_device *pdev)
return ret ;
}
ret = exynos5440_gpio_irq_init ( pdev , priv ) ;
if ( ret ) {
dev_err ( dev , " failed to setup gpio interrupts \n " ) ;
return ret ;
}
platform_set_drvdata ( pdev , priv ) ;
dev_info ( dev , " EXYNOS5440 pinctrl driver registered \n " ) ;
return 0 ;