@ -792,6 +792,32 @@ static unsigned int mtk_eint_get_mask(struct mtk_pinctrl *pctl,
return ! ! ( readl ( reg ) & bit ) ;
}
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 ) ;
u32 mask = 1 < < ( hwirq & 0x1f ) ;
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 ;
}
static void mtk_eint_mask ( struct irq_data * d )
{
struct mtk_pinctrl * pctl = irq_data_get_irq_chip_data ( d ) ;
@ -814,6 +840,9 @@ static void mtk_eint_unmask(struct irq_data *d)
eint_offsets - > mask_clr ) ;
writel ( mask , reg ) ;
if ( pctl - > eint_dual_edges [ d - > hwirq ] )
mtk_eint_flip_edge ( pctl , d - > hwirq ) ;
}
static int mtk_gpio_set_debounce ( struct gpio_chip * chip , unsigned offset ,
@ -893,13 +922,17 @@ static int mtk_eint_set_type(struct irq_data *d,
void __iomem * reg ;
if ( ( ( type & IRQ_TYPE_EDGE_BOTH ) & & ( type & IRQ_TYPE_LEVEL_MASK ) ) | |
( ( type & IRQ_TYPE_EDGE_BOTH ) = = IRQ_TYPE_EDGE_BOTH ) | |
( ( 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 ;
}
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 ;
if ( type & ( IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING ) ) {
reg = mtk_eint_get_offset ( pctl , d - > hwirq ,
eint_offsets - > pol_clr ) ;
@ -920,6 +953,9 @@ static int mtk_eint_set_type(struct irq_data *d,
writel ( mask , reg ) ;
}
if ( pctl - > eint_dual_edges [ d - > hwirq ] )
mtk_eint_flip_edge ( pctl , d - > hwirq ) ;
return 0 ;
}
@ -986,6 +1022,8 @@ static void mtk_eint_irq_handler(unsigned irq, struct irq_desc *desc)
const struct mtk_eint_offsets * eint_offsets =
& pctl - > devdata - > eint_offsets ;
void __iomem * reg = mtk_eint_get_offset ( pctl , 0 , eint_offsets - > stat ) ;
int dual_edges , start_level , curr_level ;
const struct mtk_desc_pin * pin ;
chained_irq_enter ( chip , desc ) ;
for ( eint_num = 0 ; eint_num < pctl - > devdata - > ap_num ; eint_num + = 32 ) {
@ -997,8 +1035,31 @@ static void mtk_eint_irq_handler(unsigned irq, struct irq_desc *desc)
virq = irq_find_mapping ( pctl - > domain , index ) ;
status & = ~ BIT ( offset ) ;
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 ) ;
}
generic_handle_irq ( virq ) ;
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 ) ;
}
if ( index < pctl - > devdata - > db_cnt )
mtk_eint_debounce_process ( pctl , index ) ;
}
@ -1149,11 +1210,18 @@ int mtk_pctrl_init(struct platform_device *pdev,
goto chip_error ;
}
pctl - > eint_dual_edges = devm_kzalloc ( & pdev - > dev ,
sizeof ( int ) * pctl - > devdata - > ap_num , GFP_KERNEL ) ;
if ( ! pctl - > eint_dual_edges ) {
ret = - ENOMEM ;
goto chip_error ;
}
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! irq ) {
dev_err ( & pdev - > dev , " couldn't parse and map irq \n " ) ;
ret = - EINVAL ;
goto chip_error ;
goto free_edges ;
}
pctl - > domain = irq_domain_add_linear ( np ,
@ -1161,7 +1229,7 @@ int mtk_pctrl_init(struct platform_device *pdev,
if ( ! pctl - > domain ) {
dev_err ( & pdev - > dev , " Couldn't register IRQ domain \n " ) ;
ret = - ENOMEM ;
goto chip_error ;
goto free_edges ;
}
mtk_eint_init ( pctl ) ;
@ -1179,6 +1247,8 @@ int mtk_pctrl_init(struct platform_device *pdev,
set_irq_flags ( irq , IRQF_VALID ) ;
return 0 ;
free_edges :
kfree ( pctl - > eint_dual_edges ) ;
chip_error :
gpiochip_remove ( pctl - > chip ) ;
pctrl_error :