Skip to content

Commit 7f646e9

Browse files
ffainelliJason Cooper
authored andcommitted
irqchip: brcmstb-l2: Add Broadcom Set Top Box Level-2 interrupt controller
This patch adds support for the Level-2 interrupt controller hardware found in Broadcom Set Top Box System-on-a-Chip devices. This interrupt controller is implemented using the generic IRQ chip driver with separate enable and disable registers. Signed-off-by: Brian Norris <computersforpeace@gmail.com> Link: https://lkml.kernel.org/r/1400892054-24457-2-git-send-email-f.fainelli@gmail.com Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: Jason Cooper <jason@lakedaemon.net>
1 parent d8f17c4 commit 7f646e9

File tree

3 files changed

+209
-0
lines changed

3 files changed

+209
-0
lines changed

drivers/irqchip/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ config ARM_VIC_NR
3030
The maximum number of VICs available in the system, for
3131
power management.
3232

33+
config BRCMSTB_L2_IRQ
34+
bool
35+
depends on ARM
36+
select GENERIC_IRQ_CHIP
37+
select IRQ_DOMAIN
38+
3339
config DW_APB_ICTL
3440
bool
3541
select IRQ_DOMAIN

drivers/irqchip/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
2929
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
3030
obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
3131
obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
32+
obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o

drivers/irqchip/irq-brcmstb-l2.c

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* Generic Broadcom Set Top Box Level 2 Interrupt controller driver
3+
*
4+
* Copyright (C) 2014 Broadcom Corporation
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License version 2 as
8+
* published by the Free Software Foundation.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*/
15+
16+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17+
18+
#include <linux/init.h>
19+
#include <linux/slab.h>
20+
#include <linux/module.h>
21+
#include <linux/platform_device.h>
22+
#include <linux/of.h>
23+
#include <linux/of_irq.h>
24+
#include <linux/of_address.h>
25+
#include <linux/of_platform.h>
26+
#include <linux/interrupt.h>
27+
#include <linux/irq.h>
28+
#include <linux/io.h>
29+
#include <linux/irqdomain.h>
30+
#include <linux/irqchip.h>
31+
#include <linux/irqchip/chained_irq.h>
32+
33+
#include <asm/mach/irq.h>
34+
35+
#include "irqchip.h"
36+
37+
/* Register offsets in the L2 interrupt controller */
38+
#define CPU_STATUS 0x00
39+
#define CPU_SET 0x04
40+
#define CPU_CLEAR 0x08
41+
#define CPU_MASK_STATUS 0x0c
42+
#define CPU_MASK_SET 0x10
43+
#define CPU_MASK_CLEAR 0x14
44+
45+
/* L2 intc private data structure */
46+
struct brcmstb_l2_intc_data {
47+
int parent_irq;
48+
void __iomem *base;
49+
struct irq_domain *domain;
50+
bool can_wake;
51+
u32 saved_mask; /* for suspend/resume */
52+
};
53+
54+
static void brcmstb_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
55+
{
56+
struct brcmstb_l2_intc_data *b = irq_desc_get_handler_data(desc);
57+
struct irq_chip *chip = irq_desc_get_chip(desc);
58+
u32 status;
59+
60+
chained_irq_enter(chip, desc);
61+
62+
status = __raw_readl(b->base + CPU_STATUS) &
63+
~(__raw_readl(b->base + CPU_MASK_STATUS));
64+
65+
if (status == 0) {
66+
do_bad_IRQ(irq, desc);
67+
goto out;
68+
}
69+
70+
do {
71+
irq = ffs(status) - 1;
72+
/* ack at our level */
73+
__raw_writel(1 << irq, b->base + CPU_CLEAR);
74+
status &= ~(1 << irq);
75+
generic_handle_irq(irq_find_mapping(b->domain, irq));
76+
} while (status);
77+
out:
78+
chained_irq_exit(chip, desc);
79+
}
80+
81+
static void brcmstb_l2_intc_suspend(struct irq_data *d)
82+
{
83+
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
84+
struct brcmstb_l2_intc_data *b = gc->private;
85+
86+
irq_gc_lock(gc);
87+
/* Save the current mask */
88+
b->saved_mask = __raw_readl(b->base + CPU_MASK_STATUS);
89+
90+
if (b->can_wake) {
91+
/* Program the wakeup mask */
92+
__raw_writel(~gc->wake_active, b->base + CPU_MASK_SET);
93+
__raw_writel(gc->wake_active, b->base + CPU_MASK_CLEAR);
94+
}
95+
irq_gc_unlock(gc);
96+
}
97+
98+
static void brcmstb_l2_intc_resume(struct irq_data *d)
99+
{
100+
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
101+
struct brcmstb_l2_intc_data *b = gc->private;
102+
103+
irq_gc_lock(gc);
104+
/* Clear unmasked non-wakeup interrupts */
105+
__raw_writel(~b->saved_mask & ~gc->wake_active, b->base + CPU_CLEAR);
106+
107+
/* Restore the saved mask */
108+
__raw_writel(b->saved_mask, b->base + CPU_MASK_SET);
109+
__raw_writel(~b->saved_mask, b->base + CPU_MASK_CLEAR);
110+
irq_gc_unlock(gc);
111+
}
112+
113+
int __init brcmstb_l2_intc_of_init(struct device_node *np,
114+
struct device_node *parent)
115+
{
116+
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
117+
struct brcmstb_l2_intc_data *data;
118+
struct irq_chip_generic *gc;
119+
struct irq_chip_type *ct;
120+
int ret;
121+
122+
data = kzalloc(sizeof(*data), GFP_KERNEL);
123+
if (!data)
124+
return -ENOMEM;
125+
126+
data->base = of_iomap(np, 0);
127+
if (!data->base) {
128+
pr_err("failed to remap intc L2 registers\n");
129+
ret = -ENOMEM;
130+
goto out_free;
131+
}
132+
133+
/* Disable all interrupts by default */
134+
__raw_writel(0xffffffff, data->base + CPU_MASK_SET);
135+
__raw_writel(0xffffffff, data->base + CPU_CLEAR);
136+
137+
data->parent_irq = irq_of_parse_and_map(np, 0);
138+
if (data->parent_irq < 0) {
139+
pr_err("failed to find parent interrupt\n");
140+
ret = data->parent_irq;
141+
goto out_unmap;
142+
}
143+
144+
data->domain = irq_domain_add_linear(np, 32,
145+
&irq_generic_chip_ops, NULL);
146+
if (!data->domain) {
147+
ret = -ENOMEM;
148+
goto out_unmap;
149+
}
150+
151+
/* Allocate a single Generic IRQ chip for this node */
152+
ret = irq_alloc_domain_generic_chips(data->domain, 32, 1,
153+
np->full_name, handle_level_irq, clr, 0, 0);
154+
if (ret) {
155+
pr_err("failed to allocate generic irq chip\n");
156+
goto out_free_domain;
157+
}
158+
159+
/* Set the IRQ chaining logic */
160+
irq_set_handler_data(data->parent_irq, data);
161+
irq_set_chained_handler(data->parent_irq, brcmstb_l2_intc_irq_handle);
162+
163+
gc = irq_get_domain_generic_chip(data->domain, 0);
164+
gc->reg_base = data->base;
165+
gc->private = data;
166+
ct = gc->chip_types;
167+
168+
ct->chip.irq_ack = irq_gc_ack_set_bit;
169+
ct->regs.ack = CPU_CLEAR;
170+
171+
ct->chip.irq_mask = irq_gc_mask_disable_reg;
172+
ct->regs.disable = CPU_MASK_SET;
173+
174+
ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
175+
ct->regs.enable = CPU_MASK_CLEAR;
176+
177+
ct->chip.irq_suspend = brcmstb_l2_intc_suspend;
178+
ct->chip.irq_resume = brcmstb_l2_intc_resume;
179+
180+
if (of_property_read_bool(np, "brcm,irq-can-wake")) {
181+
data->can_wake = true;
182+
/* This IRQ chip can wake the system, set all child interrupts
183+
* in wake_enabled mask
184+
*/
185+
gc->wake_enabled = 0xffffffff;
186+
ct->chip.irq_set_wake = irq_gc_set_wake;
187+
}
188+
189+
pr_info("registered L2 intc (mem: 0x%p, parent irq: %d)\n",
190+
data->base, data->parent_irq);
191+
192+
return 0;
193+
194+
out_free_domain:
195+
irq_domain_remove(data->domain);
196+
out_unmap:
197+
iounmap(data->base);
198+
out_free:
199+
kfree(data);
200+
return ret;
201+
}
202+
IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,l2-intc", brcmstb_l2_intc_of_init);

0 commit comments

Comments
 (0)