2017-11-30 11:45:00 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-09-20 19:00:57 +03:00
/*
* Copyright ( C ) Maxime Coquelin 2015
2017-11-30 11:45:00 +03:00
* Copyright ( C ) STMicroelectronics 2017
2016-09-20 19:00:57 +03:00
* Author : Maxime Coquelin < mcoquelin . stm32 @ gmail . com >
*/
# include <linux/bitops.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/irqchip.h>
# include <linux/irqchip/chained_irq.h>
# include <linux/irqdomain.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2017-11-06 20:03:32 +03:00
# define IRQS_PER_BANK 32
struct stm32_exti_bank {
u32 imr_ofst ;
u32 emr_ofst ;
u32 rtsr_ofst ;
u32 ftsr_ofst ;
u32 swier_ofst ;
2018-04-26 19:18:26 +03:00
u32 rpr_ofst ;
u32 fpr_ofst ;
2017-11-06 20:03:32 +03:00
} ;
2018-04-26 19:18:26 +03:00
# define UNDEF_REG ~0
2018-04-26 19:18:27 +03:00
struct stm32_exti_chip_data {
const struct stm32_exti_bank * reg_bank ;
u32 rtsr_cache ;
u32 ftsr_cache ;
} ;
static struct stm32_exti_chip_data * stm32_exti_data ;
2017-11-06 20:03:32 +03:00
static const struct stm32_exti_bank stm32f4xx_exti_b1 = {
. imr_ofst = 0x00 ,
. emr_ofst = 0x04 ,
. rtsr_ofst = 0x08 ,
. ftsr_ofst = 0x0C ,
. swier_ofst = 0x10 ,
2018-04-26 19:18:26 +03:00
. rpr_ofst = 0x14 ,
. fpr_ofst = UNDEF_REG ,
2017-11-06 20:03:32 +03:00
} ;
static const struct stm32_exti_bank * stm32f4xx_exti_banks [ ] = {
& stm32f4xx_exti_b1 ,
} ;
2017-11-06 20:03:34 +03:00
static const struct stm32_exti_bank stm32h7xx_exti_b1 = {
. imr_ofst = 0x80 ,
. emr_ofst = 0x84 ,
. rtsr_ofst = 0x00 ,
. ftsr_ofst = 0x04 ,
. swier_ofst = 0x08 ,
2018-04-26 19:18:26 +03:00
. rpr_ofst = 0x88 ,
. fpr_ofst = UNDEF_REG ,
2017-11-06 20:03:34 +03:00
} ;
static const struct stm32_exti_bank stm32h7xx_exti_b2 = {
. imr_ofst = 0x90 ,
. emr_ofst = 0x94 ,
. rtsr_ofst = 0x20 ,
. ftsr_ofst = 0x24 ,
. swier_ofst = 0x28 ,
2018-04-26 19:18:26 +03:00
. rpr_ofst = 0x98 ,
. fpr_ofst = UNDEF_REG ,
2017-11-06 20:03:34 +03:00
} ;
static const struct stm32_exti_bank stm32h7xx_exti_b3 = {
. imr_ofst = 0xA0 ,
. emr_ofst = 0xA4 ,
. rtsr_ofst = 0x40 ,
. ftsr_ofst = 0x44 ,
. swier_ofst = 0x48 ,
2018-04-26 19:18:26 +03:00
. rpr_ofst = 0xA8 ,
. fpr_ofst = UNDEF_REG ,
2017-11-06 20:03:34 +03:00
} ;
static const struct stm32_exti_bank * stm32h7xx_exti_banks [ ] = {
& stm32h7xx_exti_b1 ,
& stm32h7xx_exti_b2 ,
& stm32h7xx_exti_b3 ,
} ;
2017-11-06 20:03:32 +03:00
static unsigned long stm32_exti_pending ( struct irq_chip_generic * gc )
{
2018-04-26 19:18:27 +03:00
struct stm32_exti_chip_data * chip_data = gc - > private ;
const struct stm32_exti_bank * stm32_bank = chip_data - > reg_bank ;
2018-04-26 19:18:26 +03:00
unsigned long pending ;
pending = irq_reg_readl ( gc , stm32_bank - > rpr_ofst ) ;
if ( stm32_bank - > fpr_ofst ! = UNDEF_REG )
pending | = irq_reg_readl ( gc , stm32_bank - > fpr_ofst ) ;
2017-11-06 20:03:32 +03:00
2018-04-26 19:18:26 +03:00
return pending ;
2017-11-06 20:03:32 +03:00
}
2016-09-20 19:00:57 +03:00
static void stm32_irq_handler ( struct irq_desc * desc )
{
struct irq_domain * domain = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
2017-11-06 20:03:32 +03:00
unsigned int virq , nbanks = domain - > gc - > num_chips ;
struct irq_chip_generic * gc ;
2016-09-20 19:00:57 +03:00
unsigned long pending ;
2017-11-06 20:03:32 +03:00
int n , i , irq_base = 0 ;
2016-09-20 19:00:57 +03:00
chained_irq_enter ( chip , desc ) ;
2017-11-06 20:03:32 +03:00
for ( i = 0 ; i < nbanks ; i + + , irq_base + = IRQS_PER_BANK ) {
gc = irq_get_domain_generic_chip ( domain , irq_base ) ;
while ( ( pending = stm32_exti_pending ( gc ) ) ) {
for_each_set_bit ( n , & pending , IRQS_PER_BANK ) {
virq = irq_find_mapping ( domain , irq_base + n ) ;
generic_handle_irq ( virq ) ;
}
2016-09-20 19:00:57 +03:00
}
}
chained_irq_exit ( chip , desc ) ;
}
static int stm32_irq_set_type ( struct irq_data * data , unsigned int type )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( data ) ;
2018-04-26 19:18:27 +03:00
struct stm32_exti_chip_data * chip_data = gc - > private ;
const struct stm32_exti_bank * stm32_bank = chip_data - > reg_bank ;
2017-11-06 20:03:32 +03:00
int pin = data - > hwirq % IRQS_PER_BANK ;
2016-09-20 19:00:57 +03:00
u32 rtsr , ftsr ;
irq_gc_lock ( gc ) ;
2017-11-06 20:03:32 +03:00
rtsr = irq_reg_readl ( gc , stm32_bank - > rtsr_ofst ) ;
ftsr = irq_reg_readl ( gc , stm32_bank - > ftsr_ofst ) ;
2016-09-20 19:00:57 +03:00
switch ( type ) {
case IRQ_TYPE_EDGE_RISING :
rtsr | = BIT ( pin ) ;
ftsr & = ~ BIT ( pin ) ;
break ;
case IRQ_TYPE_EDGE_FALLING :
rtsr & = ~ BIT ( pin ) ;
ftsr | = BIT ( pin ) ;
break ;
case IRQ_TYPE_EDGE_BOTH :
rtsr | = BIT ( pin ) ;
ftsr | = BIT ( pin ) ;
break ;
default :
irq_gc_unlock ( gc ) ;
return - EINVAL ;
}
2017-11-06 20:03:32 +03:00
irq_reg_writel ( gc , rtsr , stm32_bank - > rtsr_ofst ) ;
irq_reg_writel ( gc , ftsr , stm32_bank - > ftsr_ofst ) ;
2016-09-20 19:00:57 +03:00
irq_gc_unlock ( gc ) ;
return 0 ;
}
2018-04-26 19:18:27 +03:00
static void stm32_irq_suspend ( struct irq_chip_generic * gc )
2016-09-20 19:00:57 +03:00
{
2018-04-26 19:18:27 +03:00
struct stm32_exti_chip_data * chip_data = gc - > private ;
const struct stm32_exti_bank * stm32_bank = chip_data - > reg_bank ;
2016-09-20 19:00:57 +03:00
irq_gc_lock ( gc ) ;
2018-04-26 19:18:27 +03:00
/* save rtsr, ftsr registers */
chip_data - > rtsr_cache = irq_reg_readl ( gc , stm32_bank - > rtsr_ofst ) ;
chip_data - > ftsr_cache = irq_reg_readl ( gc , stm32_bank - > ftsr_ofst ) ;
irq_reg_writel ( gc , gc - > wake_active , stm32_bank - > imr_ofst ) ;
2016-09-20 19:00:57 +03:00
irq_gc_unlock ( gc ) ;
2018-04-26 19:18:27 +03:00
}
2016-09-20 19:00:57 +03:00
2018-04-26 19:18:27 +03:00
static void stm32_irq_resume ( struct irq_chip_generic * gc )
{
struct stm32_exti_chip_data * chip_data = gc - > private ;
const struct stm32_exti_bank * stm32_bank = chip_data - > reg_bank ;
irq_gc_lock ( gc ) ;
/* restore rtsr, ftsr registers */
irq_reg_writel ( gc , chip_data - > rtsr_cache , stm32_bank - > rtsr_ofst ) ;
irq_reg_writel ( gc , chip_data - > ftsr_cache , stm32_bank - > ftsr_ofst ) ;
irq_reg_writel ( gc , gc - > mask_cache , stm32_bank - > imr_ofst ) ;
irq_gc_unlock ( gc ) ;
2016-09-20 19:00:57 +03:00
}
static int stm32_exti_alloc ( struct irq_domain * d , unsigned int virq ,
unsigned int nr_irqs , void * data )
{
struct irq_fwspec * fwspec = data ;
irq_hw_number_t hwirq ;
hwirq = fwspec - > param [ 0 ] ;
irq_map_generic_chip ( d , virq , hwirq ) ;
return 0 ;
}
static void stm32_exti_free ( struct irq_domain * d , unsigned int virq ,
unsigned int nr_irqs )
{
struct irq_data * data = irq_domain_get_irq_data ( d , virq ) ;
irq_domain_reset_irq_data ( data ) ;
}
2018-04-26 19:18:25 +03:00
static const struct irq_domain_ops irq_exti_domain_ops = {
2016-09-20 19:00:57 +03:00
. map = irq_map_generic_chip ,
. alloc = stm32_exti_alloc ,
. free = stm32_exti_free ,
} ;
2018-04-26 19:18:26 +03:00
static void stm32_irq_ack ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2018-04-26 19:18:27 +03:00
struct stm32_exti_chip_data * chip_data = gc - > private ;
const struct stm32_exti_bank * stm32_bank = chip_data - > reg_bank ;
2018-04-26 19:18:26 +03:00
irq_gc_lock ( gc ) ;
irq_reg_writel ( gc , d - > mask , stm32_bank - > rpr_ofst ) ;
if ( stm32_bank - > fpr_ofst ! = UNDEF_REG )
irq_reg_writel ( gc , d - > mask , stm32_bank - > fpr_ofst ) ;
irq_gc_unlock ( gc ) ;
}
2017-11-06 20:03:32 +03:00
static int
__init stm32_exti_init ( const struct stm32_exti_bank * * stm32_exti_banks ,
int bank_nr , struct device_node * node )
2016-09-20 19:00:57 +03:00
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN ;
int nr_irqs , nr_exti , ret , i ;
struct irq_chip_generic * gc ;
struct irq_domain * domain ;
void * base ;
base = of_iomap ( node , 0 ) ;
if ( ! base ) {
2017-07-19 00:43:10 +03:00
pr_err ( " %pOF: Unable to map registers \n " , node ) ;
2016-09-20 19:00:57 +03:00
return - ENOMEM ;
}
2018-04-26 19:18:27 +03:00
stm32_exti_data = kcalloc ( bank_nr , sizeof ( * stm32_exti_data ) ,
GFP_KERNEL ) ;
if ( ! stm32_exti_data )
return - ENOMEM ;
2017-11-06 20:03:32 +03:00
domain = irq_domain_add_linear ( node , bank_nr * IRQS_PER_BANK ,
2016-09-20 19:00:57 +03:00
& irq_exti_domain_ops , NULL ) ;
if ( ! domain ) {
pr_err ( " %s: Could not register interrupt domain. \n " ,
2017-11-06 20:03:32 +03:00
node - > name ) ;
2016-09-20 19:00:57 +03:00
ret = - ENOMEM ;
goto out_unmap ;
}
2017-11-06 20:03:32 +03:00
ret = irq_alloc_domain_generic_chips ( domain , IRQS_PER_BANK , 1 , " exti " ,
2016-09-20 19:00:57 +03:00
handle_edge_irq , clr , 0 , 0 ) ;
if ( ret ) {
2017-07-19 00:43:10 +03:00
pr_err ( " %pOF: Could not allocate generic interrupt chip. \n " ,
2018-04-26 19:18:25 +03:00
node ) ;
2016-09-20 19:00:57 +03:00
goto out_free_domain ;
}
2017-11-06 20:03:32 +03:00
for ( i = 0 ; i < bank_nr ; i + + ) {
const struct stm32_exti_bank * stm32_bank = stm32_exti_banks [ i ] ;
2018-04-26 19:18:27 +03:00
struct stm32_exti_chip_data * chip_data = & stm32_exti_data [ i ] ;
2017-11-06 20:03:32 +03:00
u32 irqs_mask ;
2018-04-26 19:18:27 +03:00
chip_data - > reg_bank = stm32_bank ;
2017-11-06 20:03:32 +03:00
gc = irq_get_domain_generic_chip ( domain , i * IRQS_PER_BANK ) ;
gc - > reg_base = base ;
gc - > chip_types - > type = IRQ_TYPE_EDGE_BOTH ;
2018-04-26 19:18:26 +03:00
gc - > chip_types - > chip . irq_ack = stm32_irq_ack ;
2017-11-06 20:03:32 +03:00
gc - > chip_types - > chip . irq_mask = irq_gc_mask_clr_bit ;
gc - > chip_types - > chip . irq_unmask = irq_gc_mask_set_bit ;
gc - > chip_types - > chip . irq_set_type = stm32_irq_set_type ;
2018-04-26 19:18:27 +03:00
gc - > chip_types - > chip . irq_set_wake = irq_gc_set_wake ;
gc - > suspend = stm32_irq_suspend ;
gc - > resume = stm32_irq_resume ;
gc - > wake_enabled = IRQ_MSK ( IRQS_PER_BANK ) ;
2017-11-06 20:03:32 +03:00
gc - > chip_types - > regs . mask = stm32_bank - > imr_ofst ;
2018-04-26 19:18:27 +03:00
gc - > private = ( void * ) chip_data ;
2017-11-06 20:03:32 +03:00
/* Determine number of irqs supported */
writel_relaxed ( ~ 0UL , base + stm32_bank - > rtsr_ofst ) ;
irqs_mask = readl_relaxed ( base + stm32_bank - > rtsr_ofst ) ;
nr_exti = fls ( readl_relaxed ( base + stm32_bank - > rtsr_ofst ) ) ;
2017-11-06 20:03:35 +03:00
/*
* This IP has no reset , so after hot reboot we should
* clear registers to avoid residue
*/
writel_relaxed ( 0 , base + stm32_bank - > imr_ofst ) ;
writel_relaxed ( 0 , base + stm32_bank - > emr_ofst ) ;
2017-11-06 20:03:32 +03:00
writel_relaxed ( 0 , base + stm32_bank - > rtsr_ofst ) ;
2017-11-06 20:03:35 +03:00
writel_relaxed ( 0 , base + stm32_bank - > ftsr_ofst ) ;
2018-04-26 19:18:26 +03:00
writel_relaxed ( ~ 0UL , base + stm32_bank - > rpr_ofst ) ;
if ( stm32_bank - > fpr_ofst ! = UNDEF_REG )
writel_relaxed ( ~ 0UL , base + stm32_bank - > fpr_ofst ) ;
2017-11-06 20:03:32 +03:00
pr_info ( " %s: bank%d, External IRQs available:%#x \n " ,
node - > full_name , i , irqs_mask ) ;
}
2016-09-20 19:00:57 +03:00
nr_irqs = of_irq_count ( node ) ;
for ( i = 0 ; i < nr_irqs ; i + + ) {
unsigned int irq = irq_of_parse_and_map ( node , i ) ;
irq_set_handler_data ( irq , domain ) ;
irq_set_chained_handler ( irq , stm32_irq_handler ) ;
}
return 0 ;
out_free_domain :
irq_domain_remove ( domain ) ;
out_unmap :
iounmap ( base ) ;
2018-04-26 19:18:27 +03:00
kfree ( stm32_exti_data ) ;
2016-09-20 19:00:57 +03:00
return ret ;
}
2017-11-06 20:03:32 +03:00
static int __init stm32f4_exti_of_init ( struct device_node * np ,
struct device_node * parent )
{
return stm32_exti_init ( stm32f4xx_exti_banks ,
ARRAY_SIZE ( stm32f4xx_exti_banks ) , np ) ;
}
IRQCHIP_DECLARE ( stm32f4_exti , " st,stm32-exti " , stm32f4_exti_of_init ) ;
2017-11-06 20:03:34 +03:00
static int __init stm32h7_exti_of_init ( struct device_node * np ,
struct device_node * parent )
{
return stm32_exti_init ( stm32h7xx_exti_banks ,
ARRAY_SIZE ( stm32h7xx_exti_banks ) , np ) ;
}
IRQCHIP_DECLARE ( stm32h7_exti , " st,stm32h7-exti " , stm32h7_exti_of_init ) ;