2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-05-24 04:40:53 +04:00
/*
* Generic Broadcom Set Top Box Level 2 Interrupt controller driver
*
2017-09-19 03:59:58 +03:00
* Copyright ( C ) 2014 - 2017 Broadcom
2014-05-24 04:40:53 +04:00
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2014-11-07 09:44:20 +03:00
# include <linux/spinlock.h>
2014-05-24 04:40:53 +04:00
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/io.h>
# include <linux/irqdomain.h>
# include <linux/irqchip.h>
# include <linux/irqchip/chained_irq.h>
2017-09-19 04:00:00 +03:00
struct brcmstb_intc_init_params {
irq_flow_handler_t handler ;
int cpu_status ;
int cpu_clear ;
int cpu_mask_status ;
int cpu_mask_set ;
int cpu_mask_clear ;
} ;
/* Register offsets in the L2 latched interrupt controller */
static const struct brcmstb_intc_init_params l2_edge_intc_init = {
. handler = handle_edge_irq ,
. cpu_status = 0x00 ,
. cpu_clear = 0x08 ,
. cpu_mask_status = 0x0c ,
. cpu_mask_set = 0x10 ,
. cpu_mask_clear = 0x14
} ;
/* Register offsets in the L2 level interrupt controller */
static const struct brcmstb_intc_init_params l2_lvl_intc_init = {
. handler = handle_level_irq ,
. cpu_status = 0x00 ,
. cpu_clear = - 1 , /* Register not present */
. cpu_mask_status = 0x04 ,
. cpu_mask_set = 0x08 ,
. cpu_mask_clear = 0x0C
} ;
2014-05-24 04:40:53 +04:00
/* L2 intc private data structure */
struct brcmstb_l2_intc_data {
struct irq_domain * domain ;
2017-09-19 03:59:58 +03:00
struct irq_chip_generic * gc ;
2017-09-19 03:59:59 +03:00
int status_offset ;
int mask_offset ;
2014-05-24 04:40:53 +04:00
bool can_wake ;
u32 saved_mask ; /* for suspend/resume */
} ;
2017-09-19 03:59:58 +03:00
/**
* brcmstb_l2_mask_and_ack - Mask and ack pending interrupt
* @ d : irq_data
*
* Chip has separate enable / disable registers instead of a single mask
* register and pending interrupt is acknowledged by setting a bit .
*
* Note : This function is generic and could easily be added to the
* generic irqchip implementation if there ever becomes a will to do so .
* Perhaps with a name like irq_gc_mask_disable_and_ack_set ( ) .
*
* e . g . : https : //patchwork.kernel.org/patch/9831047/
*/
static void brcmstb_l2_mask_and_ack ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
u32 mask = d - > mask ;
irq_gc_lock ( gc ) ;
irq_reg_writel ( gc , mask , ct - > regs . disable ) ;
* ct - > mask_cache & = ~ mask ;
irq_reg_writel ( gc , mask , ct - > regs . ack ) ;
irq_gc_unlock ( gc ) ;
}
2015-09-14 11:42:37 +03:00
static void brcmstb_l2_intc_irq_handle ( struct irq_desc * desc )
2014-05-24 04:40:53 +04:00
{
struct brcmstb_l2_intc_data * b = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * chip = irq_desc_get_chip ( desc ) ;
2015-09-14 11:42:37 +03:00
unsigned int irq ;
2014-05-24 04:40:53 +04:00
u32 status ;
chained_irq_enter ( chip , desc ) ;
2017-09-19 03:59:59 +03:00
status = irq_reg_readl ( b - > gc , b - > status_offset ) &
~ ( irq_reg_readl ( b - > gc , b - > mask_offset ) ) ;
2014-05-24 04:40:53 +04:00
if ( status = = 0 ) {
2014-11-07 09:44:20 +03:00
raw_spin_lock ( & desc - > lock ) ;
2015-09-14 11:42:37 +03:00
handle_bad_irq ( desc ) ;
2014-11-07 09:44:20 +03:00
raw_spin_unlock ( & desc - > lock ) ;
2014-05-24 04:40:53 +04:00
goto out ;
}
do {
irq = ffs ( status ) - 1 ;
status & = ~ ( 1 < < irq ) ;
2017-09-19 03:59:58 +03:00
generic_handle_irq ( irq_linear_revmap ( b - > domain , irq ) ) ;
2014-05-24 04:40:53 +04:00
} while ( status ) ;
out :
chained_irq_exit ( chip , desc ) ;
}
static void brcmstb_l2_intc_suspend ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2017-09-19 03:59:59 +03:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2014-05-24 04:40:53 +04:00
struct brcmstb_l2_intc_data * b = gc - > private ;
2019-02-21 01:15:28 +03:00
unsigned long flags ;
2014-05-24 04:40:53 +04:00
2019-02-21 01:15:28 +03:00
irq_gc_lock_irqsave ( gc , flags ) ;
2014-05-24 04:40:53 +04:00
/* Save the current mask */
2017-09-19 03:59:59 +03:00
b - > saved_mask = irq_reg_readl ( gc , ct - > regs . mask ) ;
2014-05-24 04:40:53 +04:00
if ( b - > can_wake ) {
/* Program the wakeup mask */
2017-09-19 03:59:59 +03:00
irq_reg_writel ( gc , ~ gc - > wake_active , ct - > regs . disable ) ;
irq_reg_writel ( gc , gc - > wake_active , ct - > regs . enable ) ;
2014-05-24 04:40:53 +04:00
}
2019-02-21 01:15:28 +03:00
irq_gc_unlock_irqrestore ( gc , flags ) ;
2014-05-24 04:40:53 +04:00
}
static void brcmstb_l2_intc_resume ( struct irq_data * d )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( d ) ;
2017-09-19 03:59:59 +03:00
struct irq_chip_type * ct = irq_data_get_chip_type ( d ) ;
2014-05-24 04:40:53 +04:00
struct brcmstb_l2_intc_data * b = gc - > private ;
2019-02-21 01:15:28 +03:00
unsigned long flags ;
2014-05-24 04:40:53 +04:00
2019-02-21 01:15:28 +03:00
irq_gc_lock_irqsave ( gc , flags ) ;
2017-09-19 04:00:00 +03:00
if ( ct - > chip . irq_ack ) {
2017-09-19 03:59:59 +03:00
/* Clear unmasked non-wakeup interrupts */
irq_reg_writel ( gc , ~ b - > saved_mask & ~ gc - > wake_active ,
ct - > regs . ack ) ;
}
2014-05-24 04:40:53 +04:00
/* Restore the saved mask */
2017-09-19 03:59:59 +03:00
irq_reg_writel ( gc , b - > saved_mask , ct - > regs . disable ) ;
irq_reg_writel ( gc , ~ b - > saved_mask , ct - > regs . enable ) ;
2019-02-21 01:15:28 +03:00
irq_gc_unlock_irqrestore ( gc , flags ) ;
2014-05-24 04:40:53 +04:00
}
2016-06-08 21:02:20 +03:00
static int __init brcmstb_l2_intc_of_init ( struct device_node * np ,
2017-09-19 04:00:00 +03:00
struct device_node * parent ,
const struct brcmstb_intc_init_params
* init_params )
2014-05-24 04:40:53 +04:00
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN ;
struct brcmstb_l2_intc_data * data ;
struct irq_chip_type * ct ;
int ret ;
2014-11-07 09:44:29 +03:00
unsigned int flags ;
2017-09-19 03:59:58 +03:00
int parent_irq ;
void __iomem * base ;
2014-05-24 04:40:53 +04:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2017-09-19 03:59:58 +03:00
base = of_iomap ( np , 0 ) ;
if ( ! base ) {
2014-05-24 04:40:53 +04:00
pr_err ( " failed to remap intc L2 registers \n " ) ;
ret = - ENOMEM ;
goto out_free ;
}
/* Disable all interrupts by default */
2017-09-19 04:00:00 +03:00
writel ( 0xffffffff , base + init_params - > cpu_mask_set ) ;
2014-12-25 20:49:02 +03:00
/* Wakeup interrupts may be retained from S5 (cold boot) */
data - > can_wake = of_property_read_bool ( np , " brcm,irq-can-wake " ) ;
2017-09-19 04:00:00 +03:00
if ( ! data - > can_wake & & ( init_params - > cpu_clear > = 0 ) )
writel ( 0xffffffff , base + init_params - > cpu_clear ) ;
2014-05-24 04:40:53 +04:00
2017-09-19 03:59:58 +03:00
parent_irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! parent_irq ) {
2014-05-24 04:40:53 +04:00
pr_err ( " failed to find parent interrupt \n " ) ;
2014-11-15 01:16:42 +03:00
ret = - EINVAL ;
2014-05-24 04:40:53 +04:00
goto out_unmap ;
}
data - > domain = irq_domain_add_linear ( np , 32 ,
& irq_generic_chip_ops , NULL ) ;
if ( ! data - > domain ) {
ret = - ENOMEM ;
goto out_unmap ;
}
2014-11-07 09:44:29 +03:00
/* MIPS chips strapped for BE will automagically configure the
* peripheral registers for CPU - native byte order .
*/
flags = 0 ;
if ( IS_ENABLED ( CONFIG_MIPS ) & & IS_ENABLED ( CONFIG_CPU_BIG_ENDIAN ) )
flags | = IRQ_GC_BE_IO ;
2014-05-24 04:40:53 +04:00
/* Allocate a single Generic IRQ chip for this node */
ret = irq_alloc_domain_generic_chips ( data - > domain , 32 , 1 ,
2017-09-19 04:00:00 +03:00
np - > full_name , init_params - > handler , clr , 0 , flags ) ;
2014-05-24 04:40:53 +04:00
if ( ret ) {
pr_err ( " failed to allocate generic irq chip \n " ) ;
goto out_free_domain ;
}
/* Set the IRQ chaining logic */
2017-09-19 03:59:58 +03:00
irq_set_chained_handler_and_data ( parent_irq ,
irqchip/brcmstb-l2: Consolidate chained IRQ handler install/remove
Chained irq handlers usually set up handler data as well. We now have
a function to set both under irq_desc->lock. Replace the two calls
with one.
Search and conversion was done with coccinelle:
@@
expression E1, E2, E3;
@@
(
-if (irq_set_handler_data(E1, E2) != 0)
- BUG();
|
-irq_set_handler_data(E1, E2);
)
-irq_set_chained_handler(E1, E3);
+irq_set_chained_handler_and_data(E1, E3, E2);
@@
expression E1, E2, E3;
@@
(
-if (irq_set_handler_data(E1, E2) != 0)
- BUG();
...
|
-irq_set_handler_data(E1, E2);
...
)
-irq_set_chained_handler(E1, E3);
+irq_set_chained_handler_and_data(E1, E3, E2);
Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Julia Lawall <Julia.Lawall@lip6.fr>
Cc: Kevin Cernekee <cernekee@gmail.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: linux-mips@linux-mips.org
2015-06-21 22:10:52 +03:00
brcmstb_l2_intc_irq_handle , data ) ;
2014-05-24 04:40:53 +04:00
2017-09-19 03:59:58 +03:00
data - > gc = irq_get_domain_generic_chip ( data - > domain , 0 ) ;
data - > gc - > reg_base = base ;
data - > gc - > private = data ;
2017-09-19 04:00:00 +03:00
data - > status_offset = init_params - > cpu_status ;
data - > mask_offset = init_params - > cpu_mask_status ;
2017-09-19 03:59:59 +03:00
2017-09-19 03:59:58 +03:00
ct = data - > gc - > chip_types ;
2014-05-24 04:40:53 +04:00
2017-09-19 04:00:00 +03:00
if ( init_params - > cpu_clear > = 0 ) {
ct - > regs . ack = init_params - > cpu_clear ;
ct - > chip . irq_ack = irq_gc_ack_set_bit ;
ct - > chip . irq_mask_ack = brcmstb_l2_mask_and_ack ;
} else {
/* No Ack - but still slightly more efficient to define this */
ct - > chip . irq_mask_ack = irq_gc_mask_disable_reg ;
}
2014-05-24 04:40:53 +04:00
ct - > chip . irq_mask = irq_gc_mask_disable_reg ;
2017-09-19 04:00:00 +03:00
ct - > regs . disable = init_params - > cpu_mask_set ;
ct - > regs . mask = init_params - > cpu_mask_status ;
2014-05-24 04:40:53 +04:00
ct - > chip . irq_unmask = irq_gc_unmask_enable_reg ;
2017-09-19 04:00:00 +03:00
ct - > regs . enable = init_params - > cpu_mask_clear ;
2014-05-24 04:40:53 +04:00
ct - > chip . irq_suspend = brcmstb_l2_intc_suspend ;
ct - > chip . irq_resume = brcmstb_l2_intc_resume ;
2017-07-28 01:38:17 +03:00
ct - > chip . irq_pm_shutdown = brcmstb_l2_intc_suspend ;
2014-05-24 04:40:53 +04:00
2014-12-25 20:49:02 +03:00
if ( data - > can_wake ) {
2014-05-24 04:40:53 +04:00
/* This IRQ chip can wake the system, set all child interrupts
* in wake_enabled mask
*/
2017-09-19 03:59:58 +03:00
data - > gc - > wake_enabled = 0xffffffff ;
2014-05-24 04:40:53 +04:00
ct - > chip . irq_set_wake = irq_gc_set_wake ;
2020-07-10 01:30:12 +03:00
enable_irq_wake ( parent_irq ) ;
2014-05-24 04:40:53 +04:00
}
2019-03-20 22:39:19 +03:00
pr_info ( " registered L2 intc (%pOF, parent irq: %d) \n " , np , parent_irq ) ;
2014-05-24 04:40:53 +04:00
return 0 ;
out_free_domain :
irq_domain_remove ( data - > domain ) ;
out_unmap :
2017-09-19 03:59:58 +03:00
iounmap ( base ) ;
2014-05-24 04:40:53 +04:00
out_free :
kfree ( data ) ;
return ret ;
}
2017-09-19 04:00:00 +03:00
2019-03-20 17:22:20 +03:00
static int __init brcmstb_l2_edge_intc_of_init ( struct device_node * np ,
2017-09-19 04:00:00 +03:00
struct device_node * parent )
{
return brcmstb_l2_intc_of_init ( np , parent , & l2_edge_intc_init ) ;
}
IRQCHIP_DECLARE ( brcmstb_l2_intc , " brcm,l2-intc " , brcmstb_l2_edge_intc_of_init ) ;
2019-03-20 17:22:20 +03:00
static int __init brcmstb_l2_lvl_intc_of_init ( struct device_node * np ,
2017-09-19 04:00:00 +03:00
struct device_node * parent )
{
return brcmstb_l2_intc_of_init ( np , parent , & l2_lvl_intc_init ) ;
}
IRQCHIP_DECLARE ( bcm7271_l2_intc , " brcm,bcm7271-l2-intc " ,
brcmstb_l2_lvl_intc_of_init ) ;