]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/amlogic/aml8726/aml8726_pic.c
Upgrade to OpenSSH 7.6p1. This will be followed shortly by 7.7p1.
[FreeBSD/FreeBSD.git] / sys / arm / amlogic / aml8726 / aml8726_pic.c
1 /*-
2  * Copyright 2013-2015 John Wehle <john@feith.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 /*
28  * Amlogic aml8726 PIC driver.
29  *
30  * The current implementation doesn't include support for FIQ.
31  *
32  * There is a set of four interrupt controllers per cpu located in adjacent
33  * memory addresses (the set for cpu 1 starts right after the set for cpu 0)
34  * ... this allows for interrupt handling to be spread across the cpus.
35  *
36  * The multicore chips also have a GIC ... typically they run SMP kernels
37  * which include the GIC driver in which case this driver is simply used
38  * to disable the PIC.
39  */
40
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD$");
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/conf.h>
47 #include <sys/bus.h>
48 #include <sys/kernel.h>
49 #include <sys/module.h>
50 #include <sys/resource.h>
51 #include <sys/rman.h>
52
53 #include <machine/bus.h>
54 #include <machine/intr.h>
55
56 #include <dev/ofw/ofw_bus.h>
57 #include <dev/ofw/ofw_bus_subr.h>
58
59 struct aml8726_pic_softc {
60         device_t                dev;
61         struct resource *       res[1];
62 };
63
64 static struct resource_spec aml8726_pic_spec[] = {
65         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
66         { -1, 0 }
67 };
68
69 /*
70  * devclass_get_device / device_get_softc could be used
71  * to dynamically locate this, however the pic is a
72  * required device which can't be unloaded so there's
73  * no need for the overhead.
74  */
75 static struct aml8726_pic_softc *aml8726_pic_sc = NULL;
76
77 #define AML_PIC_NCNTRLS         4
78 #define AML_PIC_IRQS_PER_CNTRL  32
79
80 #define AML_PIC_NIRQS           (AML_PIC_NCNTRLS * AML_PIC_IRQS_PER_CNTRL)
81
82 #define AML_PIC_0_STAT_REG      0
83 #define AML_PIC_0_STAT_CLR_REG  4
84 #define AML_PIC_0_MASK_REG      8
85 #define AML_PIC_0_FIRQ_SEL      12
86
87 #define AML_PIC_1_STAT_REG      16
88 #define AML_PIC_1_STAT_CLR_REG  20
89 #define AML_PIC_1_MASK_REG      24
90 #define AML_PIC_1_FIRQ_SEL      28
91
92 #define AML_PIC_2_STAT_REG      32
93 #define AML_PIC_2_STAT_CLR_REG  36
94 #define AML_PIC_2_MASK_REG      40
95 #define AML_PIC_2_FIRQ_SEL      44
96
97 #define AML_PIC_3_STAT_REG      48
98 #define AML_PIC_3_STAT_CLR_REG  52
99 #define AML_PIC_3_MASK_REG      56
100 #define AML_PIC_3_FIRQ_SEL      60
101
102 #define AML_PIC_CTRL(x)         ((x) >> 5)
103 #define AML_PIC_BIT(x)          (1 << ((x) & 0x1f))
104
105 #define AML_PIC_STAT_REG(x)     (AML_PIC_0_STAT_REG + AML_PIC_CTRL(x) * 16)
106 #define AML_PIC_STAT_CLR_REG(x) (AML_PIC_0_STAT_CLR_REG + AML_PIC_CTRL(x) * 16)
107 #define AML_PIC_MASK_REG(x)     (AML_PIC_0_MASK_REG + AML_PIC_CTRL(x) * 16)
108 #define AML_PIC_FIRQ_SEL(x)     (AML_PIC_0_FIRQ_REG + AML_PIC_CTRL(x) * 16)
109
110 #define CSR_WRITE_4(sc, reg, val)       bus_write_4((sc)->res[0], reg, (val))
111 #define CSR_READ_4(sc, reg)             bus_read_4((sc)->res[0], reg)
112 #define CSR_BARRIER(sc, reg)            bus_barrier((sc)->res[0], reg, 4, \
113     (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
114
115 static void
116 aml8726_pic_eoi(void *arg)
117 {
118         uintptr_t nb = (uintptr_t) arg;
119
120         if (nb >= AML_PIC_NIRQS)
121                 return;
122
123         arm_irq_memory_barrier(nb);
124
125         CSR_WRITE_4(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb), AML_PIC_BIT(nb));
126
127         CSR_BARRIER(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb));
128 }
129
130 static int
131 aml8726_pic_probe(device_t dev)
132 {
133
134         if (!ofw_bus_status_okay(dev))
135                 return (ENXIO);
136
137         if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-pic"))
138                 return (ENXIO);
139
140         device_set_desc(dev, "Amlogic aml8726 PIC");
141
142         return (BUS_PROBE_DEFAULT);
143 }
144
145 static int
146 aml8726_pic_attach(device_t dev)
147 {
148         struct aml8726_pic_softc *sc = device_get_softc(dev);
149         int i;
150
151         /* There should be exactly one instance. */
152         if (aml8726_pic_sc != NULL)
153                 return (ENXIO);
154
155         sc->dev = dev;
156
157         if (bus_alloc_resources(dev, aml8726_pic_spec, sc->res)) {
158                 device_printf(dev, "could not allocate resources for device\n");
159                 return (ENXIO);
160         }
161
162         /*
163          * Disable, clear, and set the interrupts to normal mode.
164          */
165         for (i = 0; i < AML_PIC_NCNTRLS; i++) {
166                 CSR_WRITE_4(sc, AML_PIC_0_MASK_REG + i * 16, 0);
167                 CSR_WRITE_4(sc, AML_PIC_0_STAT_CLR_REG + i * 16, ~0u);
168                 CSR_WRITE_4(sc, AML_PIC_0_FIRQ_SEL + i * 16, 0);
169         }
170
171 #ifndef DEV_GIC
172         arm_post_filter = aml8726_pic_eoi;
173 #else
174         device_printf(dev, "disabled in favor of gic\n");
175 #endif
176
177         aml8726_pic_sc = sc;
178
179         return (0);
180 }
181
182 static int
183 aml8726_pic_detach(device_t dev)
184 {
185
186         return (EBUSY);
187 }
188
189 static device_method_t aml8726_pic_methods[] = {
190         /* Device interface */
191         DEVMETHOD(device_probe,         aml8726_pic_probe),
192         DEVMETHOD(device_attach,        aml8726_pic_attach),
193         DEVMETHOD(device_detach,        aml8726_pic_detach),
194
195         DEVMETHOD_END
196 };
197
198 static driver_t aml8726_pic_driver = {
199         "pic",
200         aml8726_pic_methods,
201         sizeof(struct aml8726_pic_softc),
202 };
203
204 static devclass_t aml8726_pic_devclass;
205
206 EARLY_DRIVER_MODULE(pic, simplebus, aml8726_pic_driver, aml8726_pic_devclass,
207     0, 0, BUS_PASS_INTERRUPT);
208
209 #ifndef DEV_GIC
210 int
211 arm_get_next_irq(int last)
212 {
213         uint32_t value;
214         int irq;
215         int start;
216
217         /*
218          * The extra complexity is simply so that all IRQs are checked
219          * round robin so a particularly busy interrupt can't prevent
220          * other interrupts from being serviced.
221          */
222
223         start = (last + 1) % AML_PIC_NIRQS;
224         irq = start;
225
226         for ( ; ; ) {
227                 value = CSR_READ_4(aml8726_pic_sc, AML_PIC_STAT_REG(irq));
228
229                 for ( ; ; ) {
230                         if ((value & AML_PIC_BIT(irq)) != 0)
231                                 return (irq);
232
233                         irq = (irq + 1) % AML_PIC_NIRQS;
234
235                         if (irq == start)
236                                 return (-1);
237
238                         if ((irq % AML_PIC_IRQS_PER_CNTRL) == 0)
239                                 break;
240                 }
241         }
242 }
243
244 void
245 arm_mask_irq(uintptr_t nb)
246 {
247         uint32_t mask;
248
249         if (nb >= AML_PIC_NIRQS)
250                 return;
251
252         mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
253         mask &= ~AML_PIC_BIT(nb);
254         CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask);
255
256         CSR_BARRIER(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
257
258         aml8726_pic_eoi((void *)nb);
259 }
260
261 void
262 arm_unmask_irq(uintptr_t nb)
263 {
264         uint32_t mask;
265
266         if (nb >= AML_PIC_NIRQS)
267                 return;
268
269         arm_irq_memory_barrier(nb);
270
271         mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
272         mask |= AML_PIC_BIT(nb);
273         CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask);
274
275         CSR_BARRIER(aml8726_pic_sc, AML_PIC_MASK_REG(nb));
276 }
277 #endif