Skip to content

Commit eb8cc2d

Browse files
Phil Elwellpopcornmix
Phil Elwell
authored andcommitted
bcm2835-aux: Add aux interrupt controller
The AUX block has a shared interrupt line with a register indicating which devices have active IRQs. Expose this as a nested interrupt controller to avoid sharing problems. See: #1484 #1573 Signed-off-by: Phil Elwell <[email protected]>
1 parent 2b93e91 commit eb8cc2d

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

drivers/clk/bcm/clk-bcm2835-aux.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,107 @@
1616
#include <linux/clk-provider.h>
1717
#include <linux/module.h>
1818
#include <linux/platform_device.h>
19+
#include <linux/interrupt.h>
20+
#include <linux/irqdomain.h>
21+
#include <linux/of_irq.h>
1922
#include <dt-bindings/clock/bcm2835-aux.h>
2023

2124
#define BCM2835_AUXIRQ 0x00
2225
#define BCM2835_AUXENB 0x04
2326

27+
#define BCM2835_AUXIRQ_NUM_IRQS 3
28+
29+
#define BCM2835_AUXIRQ_UART_IRQ 0
30+
#define BCM2835_AUXIRQ_SPI1_IRQ 1
31+
#define BCM2835_AUXIRQ_SPI2_IRQ 2
32+
33+
#define BCM2835_AUXIRQ_UART_MASK 0x01
34+
#define BCM2835_AUXIRQ_SPI1_MASK 0x02
35+
#define BCM2835_AUXIRQ_SPI2_MASK 0x04
36+
37+
#define BCM2835_AUXIRQ_ALL_MASK \
38+
(BCM2835_AUXIRQ_UART_MASK | \
39+
BCM2835_AUXIRQ_SPI1_MASK | \
40+
BCM2835_AUXIRQ_SPI2_MASK)
41+
42+
struct auxirq_state {
43+
void __iomem *status;
44+
u32 enables;
45+
struct irq_domain *domain;
46+
struct regmap *local_regmap;
47+
};
48+
49+
static struct auxirq_state auxirq __read_mostly;
50+
51+
static irqreturn_t bcm2835_auxirq_handler(int irq, void *dev_id)
52+
{
53+
u32 stat = readl_relaxed(auxirq.status);
54+
u32 masked = stat & auxirq.enables;
55+
56+
if (masked & BCM2835_AUXIRQ_UART_MASK)
57+
generic_handle_irq(irq_linear_revmap(auxirq.domain,
58+
BCM2835_AUXIRQ_UART_IRQ));
59+
60+
if (masked & BCM2835_AUXIRQ_SPI1_MASK)
61+
generic_handle_irq(irq_linear_revmap(auxirq.domain,
62+
BCM2835_AUXIRQ_SPI1_IRQ));
63+
64+
if (masked & BCM2835_AUXIRQ_SPI2_MASK)
65+
generic_handle_irq(irq_linear_revmap(auxirq.domain,
66+
BCM2835_AUXIRQ_SPI2_IRQ));
67+
68+
return (masked & BCM2835_AUXIRQ_ALL_MASK) ? IRQ_HANDLED : IRQ_NONE;
69+
}
70+
71+
static int bcm2835_auxirq_xlate(struct irq_domain *d,
72+
struct device_node *ctrlr,
73+
const u32 *intspec, unsigned int intsize,
74+
unsigned long *out_hwirq,
75+
unsigned int *out_type)
76+
{
77+
if (WARN_ON(intsize != 1))
78+
return -EINVAL;
79+
80+
if (WARN_ON(intspec[0] >= BCM2835_AUXIRQ_NUM_IRQS))
81+
return -EINVAL;
82+
83+
*out_hwirq = intspec[0];
84+
*out_type = IRQ_TYPE_NONE;
85+
return 0;
86+
}
87+
88+
static void bcm2835_auxirq_mask(struct irq_data *data)
89+
{
90+
irq_hw_number_t hwirq = irqd_to_hwirq(data);
91+
92+
auxirq.enables &= ~(1 << hwirq);
93+
}
94+
95+
static void bcm2835_auxirq_unmask(struct irq_data *data)
96+
{
97+
irq_hw_number_t hwirq = irqd_to_hwirq(data);
98+
99+
auxirq.enables |= (1 << hwirq);
100+
}
101+
102+
static struct irq_chip bcm2835_auxirq_chip = {
103+
.name = "bcm2835-auxirq",
104+
.irq_mask = bcm2835_auxirq_mask,
105+
.irq_unmask = bcm2835_auxirq_unmask,
106+
};
107+
108+
static const struct irq_domain_ops bcm2835_auxirq_ops = {
109+
.xlate = bcm2835_auxirq_xlate//irq_domain_xlate_onecell
110+
};
111+
24112
static int bcm2835_aux_clk_probe(struct platform_device *pdev)
25113
{
26114
struct device *dev = &pdev->dev;
115+
struct device_node *node = dev->of_node;
27116
struct clk_hw_onecell_data *onecell;
28117
const char *parent;
29118
struct clk *parent_clk;
119+
int parent_irq;
30120
struct resource *res;
31121
void __iomem *reg, *gate;
32122

@@ -40,6 +130,36 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev)
40130
if (IS_ERR(reg))
41131
return PTR_ERR(reg);
42132

133+
parent_irq = irq_of_parse_and_map(node, 0);
134+
if (parent_irq) {
135+
int ret;
136+
int i;
137+
138+
/* Manage the AUX irq as well */
139+
auxirq.status = reg + BCM2835_AUXIRQ;
140+
auxirq.domain = irq_domain_add_linear(node,
141+
BCM2835_AUXIRQ_NUM_IRQS,
142+
&bcm2835_auxirq_ops,
143+
NULL);
144+
if (!auxirq.domain)
145+
return -ENXIO;
146+
147+
for (i = 0; i < BCM2835_AUXIRQ_NUM_IRQS; i++) {
148+
unsigned int irq = irq_create_mapping(auxirq.domain, i);
149+
150+
if (irq == 0)
151+
return -ENXIO;
152+
153+
irq_set_chip_and_handler(irq, &bcm2835_auxirq_chip,
154+
handle_level_irq);
155+
}
156+
157+
ret = devm_request_irq(dev, parent_irq, bcm2835_auxirq_handler,
158+
0, "bcm2835-auxirq", NULL);
159+
if (ret)
160+
return ret;
161+
}
162+
43163
onecell = devm_kmalloc(dev, sizeof(*onecell) + sizeof(*onecell->hws) *
44164
BCM2835_AUX_CLOCK_COUNT, GFP_KERNEL);
45165
if (!onecell)

0 commit comments

Comments
 (0)