2013-03-19 01:31:55 +04:00
/*
* S3C64xx specific support for pinctrl - samsung driver .
*
* Copyright ( c ) 2013 Tomasz Figa < tomasz . figa @ gmail . com >
*
* Based on pinctrl - exynos . c , please see the file for original copyrights .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This file contains the Samsung S3C64xx specific information required by the
* the Samsung pinctrl / gpiolib driver . It also includes the implementation of
* external gpio and wakeup interrupt support .
*/
# include <linux/module.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/irqdomain.h>
# include <linux/irq.h>
# include <linux/of_irq.h>
# include <linux/io.h>
2013-04-17 02:23:59 +04:00
# include <linux/irqchip/chained_irq.h>
2013-03-19 01:31:55 +04:00
# include <linux/slab.h>
# include <linux/err.h>
# include "pinctrl-samsung.h"
# define NUM_EINT0 28
# define NUM_EINT0_IRQ 4
# define EINT_MAX_PER_REG 16
# define EINT_MAX_PER_GROUP 16
/* External GPIO and wakeup interrupt related definitions */
# define SVC_GROUP_SHIFT 4
# define SVC_GROUP_MASK 0xf
# define SVC_NUM_MASK 0xf
# define SVC_GROUP(x) ((x >> SVC_GROUP_SHIFT) & \
SVC_GROUP_MASK )
# define EINT12CON_REG 0x200
# define EINT12MASK_REG 0x240
# define EINT12PEND_REG 0x260
# define EINT_OFFS(i) ((i) % (2 * EINT_MAX_PER_GROUP))
# define EINT_GROUP(i) ((i) / EINT_MAX_PER_GROUP)
# define EINT_REG(g) (4 * ((g) / 2))
# define EINTCON_REG(i) (EINT12CON_REG + EINT_REG(EINT_GROUP(i)))
# define EINTMASK_REG(i) (EINT12MASK_REG + EINT_REG(EINT_GROUP(i)))
# define EINTPEND_REG(i) (EINT12PEND_REG + EINT_REG(EINT_GROUP(i)))
# define SERVICE_REG 0x284
# define SERVICEPEND_REG 0x288
# define EINT0CON0_REG 0x900
# define EINT0MASK_REG 0x920
# define EINT0PEND_REG 0x924
/* S3C64xx specific external interrupt trigger types */
# define EINT_LEVEL_LOW 0
# define EINT_LEVEL_HIGH 1
# define EINT_EDGE_FALLING 2
# define EINT_EDGE_RISING 4
# define EINT_EDGE_BOTH 6
# define EINT_CON_MASK 0xF
# define EINT_CON_LEN 4
static struct samsung_pin_bank_type bank_type_4bit_off = {
. fld_width = { 4 , 1 , 2 , 0 , 2 , 2 , } ,
. reg_offset = { 0x00 , 0x04 , 0x08 , 0 , 0x0c , 0x10 , } ,
} ;
static struct samsung_pin_bank_type bank_type_4bit_alive = {
. fld_width = { 4 , 1 , 2 , } ,
. reg_offset = { 0x00 , 0x04 , 0x08 , } ,
} ;
static struct samsung_pin_bank_type bank_type_4bit2_off = {
. fld_width = { 4 , 1 , 2 , 0 , 2 , 2 , } ,
. reg_offset = { 0x00 , 0x08 , 0x0c , 0 , 0x10 , 0x14 , } ,
} ;
static struct samsung_pin_bank_type bank_type_4bit2_alive = {
. fld_width = { 4 , 1 , 2 , } ,
. reg_offset = { 0x00 , 0x08 , 0x0c , } ,
} ;
static struct samsung_pin_bank_type bank_type_2bit_off = {
. fld_width = { 2 , 1 , 2 , 0 , 2 , 2 , } ,
. reg_offset = { 0x00 , 0x04 , 0x08 , 0 , 0x0c , 0x10 , } ,
} ;
static struct samsung_pin_bank_type bank_type_2bit_alive = {
. fld_width = { 2 , 1 , 2 , } ,
. reg_offset = { 0x00 , 0x04 , 0x08 , } ,
} ;
# define PIN_BANK_4BIT(pins, reg, id) \
{ \
. type = & bank_type_4bit_off , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_NONE , \
. name = id \
}
# define PIN_BANK_4BIT_EINTG(pins, reg, id, eoffs) \
{ \
. type = & bank_type_4bit_off , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_GPIO , \
. eint_func = 7 , \
. eint_mask = ( 1 < < ( pins ) ) - 1 , \
. eint_offset = eoffs , \
. name = id \
}
# define PIN_BANK_4BIT_EINTW(pins, reg, id, eoffs, emask) \
{ \
. type = & bank_type_4bit_alive , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_WKUP , \
. eint_func = 3 , \
. eint_mask = emask , \
. eint_offset = eoffs , \
. name = id \
}
# define PIN_BANK_4BIT2_EINTG(pins, reg, id, eoffs) \
{ \
. type = & bank_type_4bit2_off , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_GPIO , \
. eint_func = 7 , \
. eint_mask = ( 1 < < ( pins ) ) - 1 , \
. eint_offset = eoffs , \
. name = id \
}
# define PIN_BANK_4BIT2_EINTW(pins, reg, id, eoffs, emask) \
{ \
. type = & bank_type_4bit2_alive , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_WKUP , \
. eint_func = 3 , \
. eint_mask = emask , \
. eint_offset = eoffs , \
. name = id \
}
# define PIN_BANK_4BIT2_ALIVE(pins, reg, id) \
{ \
. type = & bank_type_4bit2_alive , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_NONE , \
. name = id \
}
# define PIN_BANK_2BIT(pins, reg, id) \
{ \
. type = & bank_type_2bit_off , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_NONE , \
. name = id \
}
# define PIN_BANK_2BIT_EINTG(pins, reg, id, eoffs, emask) \
{ \
. type = & bank_type_2bit_off , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_GPIO , \
. eint_func = 3 , \
. eint_mask = emask , \
. eint_offset = eoffs , \
. name = id \
}
# define PIN_BANK_2BIT_EINTW(pins, reg, id, eoffs) \
{ \
. type = & bank_type_2bit_alive , \
. pctl_offset = reg , \
. nr_pins = pins , \
. eint_type = EINT_TYPE_WKUP , \
. eint_func = 2 , \
. eint_mask = ( 1 < < ( pins ) ) - 1 , \
. eint_offset = eoffs , \
. name = id \
}
/**
* struct s3c64xx_eint0_data : EINT0 common data
* @ drvdata : pin controller driver data
* @ domains : IRQ domains of particular EINT0 interrupts
* @ pins : pin offsets inside of banks of particular EINT0 interrupts
*/
struct s3c64xx_eint0_data {
struct samsung_pinctrl_drv_data * drvdata ;
struct irq_domain * domains [ NUM_EINT0 ] ;
u8 pins [ NUM_EINT0 ] ;
} ;
/**
* struct s3c64xx_eint0_domain_data : EINT0 per - domain data
* @ bank : pin bank related to the domain
* @ eints : EINT0 interrupts related to the domain
*/
struct s3c64xx_eint0_domain_data {
struct samsung_pin_bank * bank ;
u8 eints [ ] ;
} ;
/**
* struct s3c64xx_eint_gpio_data : GPIO EINT data
* @ drvdata : pin controller driver data
* @ domains : array of domains related to EINT interrupt groups
*/
struct s3c64xx_eint_gpio_data {
struct samsung_pinctrl_drv_data * drvdata ;
struct irq_domain * domains [ ] ;
} ;
/*
* Common functions for S3C64xx EINT configuration
*/
static int s3c64xx_irq_get_trigger ( unsigned int type )
{
int trigger ;
switch ( type ) {
case IRQ_TYPE_EDGE_RISING :
trigger = EINT_EDGE_RISING ;
break ;
case IRQ_TYPE_EDGE_FALLING :
trigger = EINT_EDGE_FALLING ;
break ;
case IRQ_TYPE_EDGE_BOTH :
trigger = EINT_EDGE_BOTH ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
trigger = EINT_LEVEL_HIGH ;
break ;
case IRQ_TYPE_LEVEL_LOW :
trigger = EINT_LEVEL_LOW ;
break ;
default :
return - EINVAL ;
}
return trigger ;
}
static void s3c64xx_irq_set_handler ( unsigned int irq , unsigned int type )
{
/* Edge- and level-triggered interrupts need different handlers */
if ( type & IRQ_TYPE_EDGE_BOTH )
__irq_set_handler_locked ( irq , handle_edge_irq ) ;
else
__irq_set_handler_locked ( irq , handle_level_irq ) ;
}
static void s3c64xx_irq_set_function ( struct samsung_pinctrl_drv_data * d ,
struct samsung_pin_bank * bank , int pin )
{
struct samsung_pin_bank_type * bank_type = bank - > type ;
unsigned long flags ;
void __iomem * reg ;
u8 shift ;
u32 mask ;
u32 val ;
/* Make sure that pin is configured as interrupt */
reg = d - > virt_base + bank - > pctl_offset ;
shift = pin ;
if ( bank_type - > fld_width [ PINCFG_TYPE_FUNC ] * shift > = 32 ) {
/* 4-bit bank type with 2 con regs */
reg + = 4 ;
shift - = 8 ;
}
shift = shift * bank_type - > fld_width [ PINCFG_TYPE_FUNC ] ;
mask = ( 1 < < bank_type - > fld_width [ PINCFG_TYPE_FUNC ] ) - 1 ;
spin_lock_irqsave ( & bank - > slock , flags ) ;
val = readl ( reg ) ;
val & = ~ ( mask < < shift ) ;
val | = bank - > eint_func < < shift ;
writel ( val , reg ) ;
spin_unlock_irqrestore ( & bank - > slock , flags ) ;
}
/*
* Functions for EINT GPIO configuration ( EINT groups 1 - 9 )
*/
static inline void s3c64xx_gpio_irq_set_mask ( struct irq_data * irqd , bool mask )
{
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
struct samsung_pinctrl_drv_data * d = bank - > drvdata ;
unsigned char index = EINT_OFFS ( bank - > eint_offset ) + irqd - > hwirq ;
void __iomem * reg = d - > virt_base + EINTMASK_REG ( bank - > eint_offset ) ;
u32 val ;
val = readl ( reg ) ;
if ( mask )
val | = 1 < < index ;
else
val & = ~ ( 1 < < index ) ;
writel ( val , reg ) ;
}
static void s3c64xx_gpio_irq_unmask ( struct irq_data * irqd )
{
s3c64xx_gpio_irq_set_mask ( irqd , false ) ;
}
static void s3c64xx_gpio_irq_mask ( struct irq_data * irqd )
{
s3c64xx_gpio_irq_set_mask ( irqd , true ) ;
}
static void s3c64xx_gpio_irq_ack ( struct irq_data * irqd )
{
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
struct samsung_pinctrl_drv_data * d = bank - > drvdata ;
unsigned char index = EINT_OFFS ( bank - > eint_offset ) + irqd - > hwirq ;
void __iomem * reg = d - > virt_base + EINTPEND_REG ( bank - > eint_offset ) ;
writel ( 1 < < index , reg ) ;
}
static int s3c64xx_gpio_irq_set_type ( struct irq_data * irqd , unsigned int type )
{
struct samsung_pin_bank * bank = irq_data_get_irq_chip_data ( irqd ) ;
struct samsung_pinctrl_drv_data * d = bank - > drvdata ;
void __iomem * reg ;
int trigger ;
u8 shift ;
u32 val ;
trigger = s3c64xx_irq_get_trigger ( type ) ;
if ( trigger < 0 ) {
pr_err ( " unsupported external interrupt type \n " ) ;
return - EINVAL ;
}
s3c64xx_irq_set_handler ( irqd - > irq , type ) ;
/* Set up interrupt trigger */
reg = d - > virt_base + EINTCON_REG ( bank - > eint_offset ) ;
shift = EINT_OFFS ( bank - > eint_offset ) + irqd - > hwirq ;
shift = 4 * ( shift / 4 ) ; /* 4 EINTs per trigger selector */
val = readl ( reg ) ;
val & = ~ ( EINT_CON_MASK < < shift ) ;
val | = trigger < < shift ;
writel ( val , reg ) ;
s3c64xx_irq_set_function ( d , bank , irqd - > hwirq ) ;
return 0 ;
}
/*
* irq_chip for gpio interrupts .
*/
static struct irq_chip s3c64xx_gpio_irq_chip = {
. name = " GPIO " ,
. irq_unmask = s3c64xx_gpio_irq_unmask ,
. irq_mask = s3c64xx_gpio_irq_mask ,
. irq_ack = s3c64xx_gpio_irq_ack ,
. irq_set_type = s3c64xx_gpio_irq_set_type ,
} ;
static int s3c64xx_gpio_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
{
struct samsung_pin_bank * bank = h - > host_data ;
if ( ! ( bank - > eint_mask & ( 1 < < hw ) ) )
return - EINVAL ;
irq_set_chip_and_handler ( virq ,
& s3c64xx_gpio_irq_chip , handle_level_irq ) ;
irq_set_chip_data ( virq , bank ) ;
set_irq_flags ( virq , IRQF_VALID ) ;
return 0 ;
}
/*
* irq domain callbacks for external gpio interrupt controller .
*/
static const struct irq_domain_ops s3c64xx_gpio_irqd_ops = {
. map = s3c64xx_gpio_irq_map ,
. xlate = irq_domain_xlate_twocell ,
} ;
static void s3c64xx_eint_gpio_irq ( unsigned int irq , struct irq_desc * desc )
{
struct irq_chip * chip = irq_get_chip ( irq ) ;
struct s3c64xx_eint_gpio_data * data = irq_get_handler_data ( irq ) ;
struct samsung_pinctrl_drv_data * drvdata = data - > drvdata ;
chained_irq_enter ( chip , desc ) ;
do {
unsigned int svc ;
unsigned int group ;
unsigned int pin ;
unsigned int virq ;
svc = readl ( drvdata - > virt_base + SERVICE_REG ) ;
group = SVC_GROUP ( svc ) ;
pin = svc & SVC_NUM_MASK ;
if ( ! group )
break ;
/* Group 1 is used for two pin banks */
if ( group = = 1 ) {
if ( pin < 8 )
group = 0 ;
else
pin - = 8 ;
}
virq = irq_linear_revmap ( data - > domains [ group ] , pin ) ;
/*
* Something must be really wrong if an unmapped EINT
* was unmasked . . .
*/
BUG_ON ( ! virq ) ;
generic_handle_irq ( virq ) ;
} while ( 1 ) ;
chained_irq_exit ( chip , desc ) ;
}
/**
* s3c64xx_eint_gpio_init ( ) - setup handling of external gpio interrupts .
* @ d : driver data of samsung pinctrl driver .
*/
static int s3c64xx_eint_gpio_init ( struct samsung_pinctrl_drv_data * d )
{
struct s3c64xx_eint_gpio_data * data ;
struct samsung_pin_bank * bank ;
struct device * dev = d - > dev ;
unsigned int nr_domains ;
unsigned int i ;
if ( ! d - > irq ) {
dev_err ( dev , " irq number not available \n " ) ;
return - EINVAL ;
}
nr_domains = 0 ;
bank = d - > ctrl - > pin_banks ;
for ( i = 0 ; i < d - > ctrl - > nr_banks ; + + i , + + bank ) {
unsigned int nr_eints ;
unsigned int mask ;
if ( bank - > eint_type ! = EINT_TYPE_GPIO )
continue ;
mask = bank - > eint_mask ;
nr_eints = fls ( mask ) ;
bank - > irq_domain = irq_domain_add_linear ( bank - > of_node ,
nr_eints , & s3c64xx_gpio_irqd_ops , bank ) ;
if ( ! bank - > irq_domain ) {
dev_err ( dev , " gpio irq domain add failed \n " ) ;
return - ENXIO ;
}
+ + nr_domains ;
}
data = devm_kzalloc ( dev , sizeof ( * data )
+ nr_domains * sizeof ( * data - > domains ) , GFP_KERNEL ) ;
if ( ! data ) {
dev_err ( dev , " failed to allocate handler data \n " ) ;
return - ENOMEM ;
}
data - > drvdata = d ;
bank = d - > ctrl - > pin_banks ;
nr_domains = 0 ;
for ( i = 0 ; i < d - > ctrl - > nr_banks ; + + i , + + bank ) {
if ( bank - > eint_type ! = EINT_TYPE_GPIO )
continue ;
data - > domains [ nr_domains + + ] = bank - > irq_domain ;
}
irq_set_chained_handler ( d - > irq , s3c64xx_eint_gpio_irq ) ;
irq_set_handler_data ( d - > irq , data ) ;
return 0 ;
}
/*
* Functions for configuration of EINT0 wake - up interrupts
*/
static inline void s3c64xx_eint0_irq_set_mask ( struct irq_data * irqd , bool mask )
{
struct s3c64xx_eint0_domain_data * ddata =
irq_data_get_irq_chip_data ( irqd ) ;
struct samsung_pinctrl_drv_data * d = ddata - > bank - > drvdata ;
u32 val ;
val = readl ( d - > virt_base + EINT0MASK_REG ) ;
if ( mask )
val | = 1 < < ddata - > eints [ irqd - > hwirq ] ;
else
val & = ~ ( 1 < < ddata - > eints [ irqd - > hwirq ] ) ;
writel ( val , d - > virt_base + EINT0MASK_REG ) ;
}
static void s3c64xx_eint0_irq_unmask ( struct irq_data * irqd )
{
s3c64xx_eint0_irq_set_mask ( irqd , false ) ;
}
static void s3c64xx_eint0_irq_mask ( struct irq_data * irqd )
{
s3c64xx_eint0_irq_set_mask ( irqd , true ) ;
}
static void s3c64xx_eint0_irq_ack ( struct irq_data * irqd )
{
struct s3c64xx_eint0_domain_data * ddata =
irq_data_get_irq_chip_data ( irqd ) ;
struct samsung_pinctrl_drv_data * d = ddata - > bank - > drvdata ;
writel ( 1 < < ddata - > eints [ irqd - > hwirq ] ,
d - > virt_base + EINT0PEND_REG ) ;
}
static int s3c64xx_eint0_irq_set_type ( struct irq_data * irqd , unsigned int type )
{
struct s3c64xx_eint0_domain_data * ddata =
irq_data_get_irq_chip_data ( irqd ) ;
struct samsung_pin_bank * bank = ddata - > bank ;
struct samsung_pinctrl_drv_data * d = bank - > drvdata ;
void __iomem * reg ;
int trigger ;
u8 shift ;
u32 val ;
trigger = s3c64xx_irq_get_trigger ( type ) ;
if ( trigger < 0 ) {
pr_err ( " unsupported external interrupt type \n " ) ;
return - EINVAL ;
}
s3c64xx_irq_set_handler ( irqd - > irq , type ) ;
/* Set up interrupt trigger */
reg = d - > virt_base + EINT0CON0_REG ;
shift = ddata - > eints [ irqd - > hwirq ] ;
if ( shift > = EINT_MAX_PER_REG ) {
reg + = 4 ;
shift - = EINT_MAX_PER_REG ;
}
shift = EINT_CON_LEN * ( shift / 2 ) ;
val = readl ( reg ) ;
val & = ~ ( EINT_CON_MASK < < shift ) ;
val | = trigger < < shift ;
writel ( val , reg ) ;
s3c64xx_irq_set_function ( d , bank , irqd - > hwirq ) ;
return 0 ;
}
/*
* irq_chip for wakeup interrupts
*/
static struct irq_chip s3c64xx_eint0_irq_chip = {
. name = " EINT0 " ,
. irq_unmask = s3c64xx_eint0_irq_unmask ,
. irq_mask = s3c64xx_eint0_irq_mask ,
. irq_ack = s3c64xx_eint0_irq_ack ,
. irq_set_type = s3c64xx_eint0_irq_set_type ,
} ;
static inline void s3c64xx_irq_demux_eint ( unsigned int irq ,
struct irq_desc * desc , u32 range )
{
struct irq_chip * chip = irq_get_chip ( irq ) ;
struct s3c64xx_eint0_data * data = irq_get_handler_data ( irq ) ;
struct samsung_pinctrl_drv_data * drvdata = data - > drvdata ;
unsigned int pend , mask ;
chained_irq_enter ( chip , desc ) ;
pend = readl ( drvdata - > virt_base + EINT0PEND_REG ) ;
mask = readl ( drvdata - > virt_base + EINT0MASK_REG ) ;
pend = pend & range & ~ mask ;
pend & = range ;
while ( pend ) {
unsigned int virq ;
irq = fls ( pend ) - 1 ;
pend & = ~ ( 1 < < irq ) ;
virq = irq_linear_revmap ( data - > domains [ irq ] , data - > pins [ irq ] ) ;
/*
* Something must be really wrong if an unmapped EINT
* was unmasked . . .
*/
BUG_ON ( ! virq ) ;
generic_handle_irq ( virq ) ;
}
chained_irq_exit ( chip , desc ) ;
}
static void s3c64xx_demux_eint0_3 ( unsigned int irq , struct irq_desc * desc )
{
s3c64xx_irq_demux_eint ( irq , desc , 0xf ) ;
}
static void s3c64xx_demux_eint4_11 ( unsigned int irq , struct irq_desc * desc )
{
s3c64xx_irq_demux_eint ( irq , desc , 0xff0 ) ;
}
static void s3c64xx_demux_eint12_19 ( unsigned int irq , struct irq_desc * desc )
{
s3c64xx_irq_demux_eint ( irq , desc , 0xff000 ) ;
}
static void s3c64xx_demux_eint20_27 ( unsigned int irq , struct irq_desc * desc )
{
s3c64xx_irq_demux_eint ( irq , desc , 0xff00000 ) ;
}
static irq_flow_handler_t s3c64xx_eint0_handlers [ NUM_EINT0_IRQ ] = {
s3c64xx_demux_eint0_3 ,
s3c64xx_demux_eint4_11 ,
s3c64xx_demux_eint12_19 ,
s3c64xx_demux_eint20_27 ,
} ;
static int s3c64xx_eint0_irq_map ( struct irq_domain * h , unsigned int virq ,
irq_hw_number_t hw )
{
struct s3c64xx_eint0_domain_data * ddata = h - > host_data ;
struct samsung_pin_bank * bank = ddata - > bank ;
if ( ! ( bank - > eint_mask & ( 1 < < hw ) ) )
return - EINVAL ;
irq_set_chip_and_handler ( virq ,
& s3c64xx_eint0_irq_chip , handle_level_irq ) ;
irq_set_chip_data ( virq , ddata ) ;
set_irq_flags ( virq , IRQF_VALID ) ;
return 0 ;
}
/*
* irq domain callbacks for external wakeup interrupt controller .
*/
static const struct irq_domain_ops s3c64xx_eint0_irqd_ops = {
. map = s3c64xx_eint0_irq_map ,
. xlate = irq_domain_xlate_twocell ,
} ;
/* list of external wakeup controllers supported */
static const struct of_device_id s3c64xx_eint0_irq_ids [ ] = {
{ . compatible = " samsung,s3c64xx-wakeup-eint " , } ,
{ }
} ;
/**
* s3c64xx_eint_eint0_init ( ) - setup handling of external wakeup interrupts .
* @ d : driver data of samsung pinctrl driver .
*/
static int s3c64xx_eint_eint0_init ( struct samsung_pinctrl_drv_data * d )
{
struct device * dev = d - > dev ;
struct device_node * eint0_np = NULL ;
struct device_node * np ;
struct samsung_pin_bank * bank ;
struct s3c64xx_eint0_data * data ;
unsigned int i ;
for_each_child_of_node ( dev - > of_node , np ) {
if ( of_match_node ( s3c64xx_eint0_irq_ids , np ) ) {
eint0_np = np ;
break ;
}
}
if ( ! eint0_np )
return - ENODEV ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data ) {
dev_err ( dev , " could not allocate memory for wkup eint data \n " ) ;
return - ENOMEM ;
}
data - > drvdata = d ;
for ( i = 0 ; i < NUM_EINT0_IRQ ; + + i ) {
unsigned int irq ;
irq = irq_of_parse_and_map ( eint0_np , i ) ;
if ( ! irq ) {
dev_err ( dev , " failed to get wakeup EINT IRQ %d \n " , i ) ;
return - ENXIO ;
}
irq_set_chained_handler ( irq , s3c64xx_eint0_handlers [ i ] ) ;
irq_set_handler_data ( irq , data ) ;
}
bank = d - > ctrl - > pin_banks ;
for ( i = 0 ; i < d - > ctrl - > nr_banks ; + + i , + + bank ) {
struct s3c64xx_eint0_domain_data * ddata ;
unsigned int nr_eints ;
unsigned int mask ;
unsigned int irq ;
unsigned int pin ;
if ( bank - > eint_type ! = EINT_TYPE_WKUP )
continue ;
mask = bank - > eint_mask ;
nr_eints = fls ( mask ) ;
ddata = devm_kzalloc ( dev ,
sizeof ( * ddata ) + nr_eints , GFP_KERNEL ) ;
if ( ! ddata ) {
dev_err ( dev , " failed to allocate domain data \n " ) ;
return - ENOMEM ;
}
ddata - > bank = bank ;
bank - > irq_domain = irq_domain_add_linear ( bank - > of_node ,
nr_eints , & s3c64xx_eint0_irqd_ops , ddata ) ;
if ( ! bank - > irq_domain ) {
dev_err ( dev , " wkup irq domain add failed \n " ) ;
return - ENXIO ;
}
irq = bank - > eint_offset ;
mask = bank - > eint_mask ;
for ( pin = 0 ; mask ; + + pin , mask > > = 1 ) {
if ( ! ( mask & 1 ) )
continue ;
data - > domains [ irq ] = bank - > irq_domain ;
data - > pins [ irq ] = pin ;
ddata - > eints [ pin ] = irq ;
+ + irq ;
}
}
return 0 ;
}
/* pin banks of s3c64xx pin-controller 0 */
static struct samsung_pin_bank s3c64xx_pin_banks0 [ ] = {
PIN_BANK_4BIT_EINTG ( 8 , 0x000 , " gpa " , 0 ) ,
PIN_BANK_4BIT_EINTG ( 7 , 0x020 , " gpb " , 8 ) ,
PIN_BANK_4BIT_EINTG ( 8 , 0x040 , " gpc " , 16 ) ,
PIN_BANK_4BIT_EINTG ( 5 , 0x060 , " gpd " , 32 ) ,
PIN_BANK_4BIT ( 5 , 0x080 , " gpe " ) ,
PIN_BANK_2BIT_EINTG ( 16 , 0x0a0 , " gpf " , 48 , 0x3fff ) ,
PIN_BANK_4BIT_EINTG ( 7 , 0x0c0 , " gpg " , 64 ) ,
PIN_BANK_4BIT2_EINTG ( 10 , 0x0e0 , " gph " , 80 ) ,
PIN_BANK_2BIT ( 16 , 0x100 , " gpi " ) ,
PIN_BANK_2BIT ( 12 , 0x120 , " gpj " ) ,
PIN_BANK_4BIT2_ALIVE ( 16 , 0x800 , " gpk " ) ,
PIN_BANK_4BIT2_EINTW ( 15 , 0x810 , " gpl " , 16 , 0x7f00 ) ,
PIN_BANK_4BIT_EINTW ( 6 , 0x820 , " gpm " , 23 , 0x1f ) ,
PIN_BANK_2BIT_EINTW ( 16 , 0x830 , " gpn " , 0 ) ,
PIN_BANK_2BIT_EINTG ( 16 , 0x140 , " gpo " , 96 , 0xffff ) ,
PIN_BANK_2BIT_EINTG ( 15 , 0x160 , " gpp " , 112 , 0x7fff ) ,
PIN_BANK_2BIT_EINTG ( 9 , 0x180 , " gpq " , 128 , 0x1ff ) ,
} ;
/*
* Samsung pinctrl driver data for S3C64xx SoC . S3C64xx SoC includes
* one gpio / pin - mux / pinconfig controller .
*/
struct samsung_pin_ctrl s3c64xx_pin_ctrl [ ] = {
{
/* pin-controller instance 1 data */
. pin_banks = s3c64xx_pin_banks0 ,
. nr_banks = ARRAY_SIZE ( s3c64xx_pin_banks0 ) ,
. eint_gpio_init = s3c64xx_eint_gpio_init ,
. eint_wkup_init = s3c64xx_eint_eint0_init ,
. label = " S3C64xx-GPIO " ,
} ,
} ;