bf1c1ab7449eed27ed5f3ccc73ad656657e406f5
[openwrt/staging/lynxis.git] / target / linux / brcm2708 / patches-4.14 / 950-0108-bcm2835-aux-Add-aux-interrupt-controller.patch
1 From 61f87edd9a522d70d979f679d11849d1dc20cedb Mon Sep 17 00:00:00 2001
2 From: Phil Elwell <phil@raspberrypi.org>
3 Date: Thu, 23 Mar 2017 16:34:46 +0000
4 Subject: [PATCH 108/454] bcm2835-aux: Add aux interrupt controller
5
6 The AUX block has a shared interrupt line with a register indicating
7 which devices have active IRQs. Expose this as a nested interrupt
8 controller to avoid sharing problems.
9
10 See: https://github.com/raspberrypi/linux/issues/1484
11 https://github.com/raspberrypi/linux/issues/1573
12
13 Signed-off-by: Phil Elwell <phil@raspberrypi.org>
14 ---
15 drivers/clk/bcm/clk-bcm2835-aux.c | 120 ++++++++++++++++++++++++++++++
16 1 file changed, 120 insertions(+)
17
18 --- a/drivers/clk/bcm/clk-bcm2835-aux.c
19 +++ b/drivers/clk/bcm/clk-bcm2835-aux.c
20 @@ -17,17 +17,107 @@
21 #include <linux/clk/bcm2835.h>
22 #include <linux/module.h>
23 #include <linux/platform_device.h>
24 +#include <linux/interrupt.h>
25 +#include <linux/irqdomain.h>
26 +#include <linux/of_irq.h>
27 #include <dt-bindings/clock/bcm2835-aux.h>
28
29 #define BCM2835_AUXIRQ 0x00
30 #define BCM2835_AUXENB 0x04
31
32 +#define BCM2835_AUXIRQ_NUM_IRQS 3
33 +
34 +#define BCM2835_AUXIRQ_UART_IRQ 0
35 +#define BCM2835_AUXIRQ_SPI1_IRQ 1
36 +#define BCM2835_AUXIRQ_SPI2_IRQ 2
37 +
38 +#define BCM2835_AUXIRQ_UART_MASK 0x01
39 +#define BCM2835_AUXIRQ_SPI1_MASK 0x02
40 +#define BCM2835_AUXIRQ_SPI2_MASK 0x04
41 +
42 +#define BCM2835_AUXIRQ_ALL_MASK \
43 + (BCM2835_AUXIRQ_UART_MASK | \
44 + BCM2835_AUXIRQ_SPI1_MASK | \
45 + BCM2835_AUXIRQ_SPI2_MASK)
46 +
47 +struct auxirq_state {
48 + void __iomem *status;
49 + u32 enables;
50 + struct irq_domain *domain;
51 + struct regmap *local_regmap;
52 +};
53 +
54 +static struct auxirq_state auxirq __read_mostly;
55 +
56 +static irqreturn_t bcm2835_auxirq_handler(int irq, void *dev_id)
57 +{
58 + u32 stat = readl_relaxed(auxirq.status);
59 + u32 masked = stat & auxirq.enables;
60 +
61 + if (masked & BCM2835_AUXIRQ_UART_MASK)
62 + generic_handle_irq(irq_linear_revmap(auxirq.domain,
63 + BCM2835_AUXIRQ_UART_IRQ));
64 +
65 + if (masked & BCM2835_AUXIRQ_SPI1_MASK)
66 + generic_handle_irq(irq_linear_revmap(auxirq.domain,
67 + BCM2835_AUXIRQ_SPI1_IRQ));
68 +
69 + if (masked & BCM2835_AUXIRQ_SPI2_MASK)
70 + generic_handle_irq(irq_linear_revmap(auxirq.domain,
71 + BCM2835_AUXIRQ_SPI2_IRQ));
72 +
73 + return (masked & BCM2835_AUXIRQ_ALL_MASK) ? IRQ_HANDLED : IRQ_NONE;
74 +}
75 +
76 +static int bcm2835_auxirq_xlate(struct irq_domain *d,
77 + struct device_node *ctrlr,
78 + const u32 *intspec, unsigned int intsize,
79 + unsigned long *out_hwirq,
80 + unsigned int *out_type)
81 +{
82 + if (WARN_ON(intsize != 1))
83 + return -EINVAL;
84 +
85 + if (WARN_ON(intspec[0] >= BCM2835_AUXIRQ_NUM_IRQS))
86 + return -EINVAL;
87 +
88 + *out_hwirq = intspec[0];
89 + *out_type = IRQ_TYPE_NONE;
90 + return 0;
91 +}
92 +
93 +static void bcm2835_auxirq_mask(struct irq_data *data)
94 +{
95 + irq_hw_number_t hwirq = irqd_to_hwirq(data);
96 +
97 + auxirq.enables &= ~(1 << hwirq);
98 +}
99 +
100 +static void bcm2835_auxirq_unmask(struct irq_data *data)
101 +{
102 + irq_hw_number_t hwirq = irqd_to_hwirq(data);
103 +
104 + auxirq.enables |= (1 << hwirq);
105 +}
106 +
107 +static struct irq_chip bcm2835_auxirq_chip = {
108 + .name = "bcm2835-auxirq",
109 + .irq_mask = bcm2835_auxirq_mask,
110 + .irq_unmask = bcm2835_auxirq_unmask,
111 +};
112 +
113 +static const struct irq_domain_ops bcm2835_auxirq_ops = {
114 + .xlate = bcm2835_auxirq_xlate//irq_domain_xlate_onecell
115 +};
116 +
117 static int bcm2835_aux_clk_probe(struct platform_device *pdev)
118 {
119 struct device *dev = &pdev->dev;
120 + struct device_node *node = dev->of_node;
121 struct clk_hw_onecell_data *onecell;
122 const char *parent;
123 struct clk *parent_clk;
124 + int parent_irq;
125 struct resource *res;
126 void __iomem *reg, *gate;
127
128 @@ -41,6 +131,36 @@ static int bcm2835_aux_clk_probe(struct
129 if (IS_ERR(reg))
130 return PTR_ERR(reg);
131
132 + parent_irq = irq_of_parse_and_map(node, 0);
133 + if (parent_irq) {
134 + int ret;
135 + int i;
136 +
137 + /* Manage the AUX irq as well */
138 + auxirq.status = reg + BCM2835_AUXIRQ;
139 + auxirq.domain = irq_domain_add_linear(node,
140 + BCM2835_AUXIRQ_NUM_IRQS,
141 + &bcm2835_auxirq_ops,
142 + NULL);
143 + if (!auxirq.domain)
144 + return -ENXIO;
145 +
146 + for (i = 0; i < BCM2835_AUXIRQ_NUM_IRQS; i++) {
147 + unsigned int irq = irq_create_mapping(auxirq.domain, i);
148 +
149 + if (irq == 0)
150 + return -ENXIO;
151 +
152 + irq_set_chip_and_handler(irq, &bcm2835_auxirq_chip,
153 + handle_level_irq);
154 + }
155 +
156 + ret = devm_request_irq(dev, parent_irq, bcm2835_auxirq_handler,
157 + 0, "bcm2835-auxirq", NULL);
158 + if (ret)
159 + return ret;
160 + }
161 +
162 onecell = devm_kmalloc(dev, sizeof(*onecell) + sizeof(*onecell->hws) *
163 BCM2835_AUX_CLOCK_COUNT, GFP_KERNEL);
164 if (!onecell)