1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2012, 2013 The FreeBSD Foundation
5 *
6 * This software was developed by Oleksandr Rybalko under sponsorship
7 * from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/rman.h>
40 #include <sys/proc.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46
47 #include <dev/ofw/openfirm.h>
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50
51 #include <arm/freescale/imx/imx51_tzicreg.h>
52
53 #include "pic_if.h"
54
55 #define TZIC_NIRQS 128
56
57 struct tzic_irqsrc {
58 struct intr_irqsrc isrc;
59 u_int irq;
60 };
61
62 struct tzic_softc {
63 device_t dev;
64 struct resource *tzicregs;
65 struct tzic_irqsrc isrcs[TZIC_NIRQS];
66 };
67
68 static struct tzic_softc *tzic_sc;
69
70 static inline uint32_t
71 tzic_read_4(struct tzic_softc *sc, int reg)
72 {
73
74 return (bus_read_4(sc->tzicregs, reg));
75 }
76
77 static inline void
78 tzic_write_4(struct tzic_softc *sc, int reg, uint32_t val)
79 {
80
81 bus_write_4(sc->tzicregs, reg, val);
82 }
83
84 static inline void
85 tzic_irq_eoi(struct tzic_softc *sc)
86 {
87
88 tzic_write_4(sc, TZIC_PRIOMASK, 0xff);
89 }
90
91 static inline void
92 tzic_irq_mask(struct tzic_softc *sc, u_int irq)
93 {
94
95 tzic_write_4(sc, TZIC_ENCLEAR(irq >> 5), (1u << (irq & 0x1f)));
96 }
97
98 static inline void
99 tzic_irq_unmask(struct tzic_softc *sc, u_int irq)
100 {
101
102 tzic_write_4(sc, TZIC_ENSET(irq >> 5), (1u << (irq & 0x1f)));
103 }
104
105 static int
106 tzic_intr(void *arg)
107 {
108 struct tzic_softc *sc = arg;
109 int b, i, irq;
110 uint32_t pending;
111
112 /* Get active interrupt */
113 for (i = 0; i < TZIC_NIRQS / 32; ++i) {
114 pending = tzic_read_4(sc, TZIC_PND(i));
115 if ((b = 31 - __builtin_clz(pending)) < 0)
116 continue;
117 irq = i * 32 + b;
118 tzic_write_4(sc, TZIC_PRIOMASK, 0);
119 if (intr_isrc_dispatch(&sc->isrcs[irq].isrc,
120 curthread->td_intr_frame) != 0) {
121 tzic_irq_mask(sc, irq);
122 tzic_irq_eoi(sc);
123 arm_irq_memory_barrier(irq);
124 if (bootverbose) {
125 device_printf(sc->dev,
126 "Stray irq %u disabled\n", irq);
127 }
128 }
129 return (FILTER_HANDLED);
130 }
131
132 if (bootverbose)
133 device_printf(sc->dev, "Spurious interrupt detected\n");
134
135 return (FILTER_HANDLED);
136 }
137
138 static void
139 tzic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
140 {
141 u_int irq = ((struct tzic_irqsrc *)isrc)->irq;
142 struct tzic_softc *sc = device_get_softc(dev);
143
144 arm_irq_memory_barrier(irq);
145 tzic_irq_unmask(sc, irq);
146 }
147
148 static void
149 tzic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
150 {
151 u_int irq = ((struct tzic_irqsrc *)isrc)->irq;
152 struct tzic_softc *sc = device_get_softc(dev);
153
154 tzic_irq_mask(sc, irq);
155 }
156
157 static int
158 tzic_map_intr(device_t dev, struct intr_map_data *data,
159 struct intr_irqsrc **isrcp)
160 {
161 struct intr_map_data_fdt *daf;
162 struct tzic_softc *sc;
163
164 if (data->type != INTR_MAP_DATA_FDT)
165 return (ENOTSUP);
166
167 daf = (struct intr_map_data_fdt *)data;
168 if (daf->ncells != 1 || daf->cells[0] >= TZIC_NIRQS)
169 return (EINVAL);
170
171 sc = device_get_softc(dev);
172 *isrcp = &sc->isrcs[daf->cells[0]].isrc;
173
174 return (0);
175 }
176
177 static void
178 tzic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
179 {
180 struct tzic_softc *sc = device_get_softc(dev);
181
182 tzic_irq_mask(sc, ((struct tzic_irqsrc *)isrc)->irq);
183 tzic_irq_eoi(sc);
184 }
185
186 static void
187 tzic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
188 {
189
190 tzic_enable_intr(dev, isrc);
191 }
192
193 static void
194 tzic_post_filter(device_t dev, struct intr_irqsrc *isrc)
195 {
196
197 tzic_irq_eoi(device_get_softc(dev));
198 }
199
200 static int
201 tzic_pic_attach(struct tzic_softc *sc)
202 {
203 struct intr_pic *pic;
204 const char *name;
205 intptr_t xref;
206 int error;
207 u_int irq;
208
209 name = device_get_nameunit(sc->dev);
210 for (irq = 0; irq < TZIC_NIRQS; irq++) {
211 sc->isrcs[irq].irq = irq;
212 error = intr_isrc_register(&sc->isrcs[irq].isrc,
213 sc->dev, 0, "%s,%u", name, irq);
214 if (error != 0)
215 return (error);
216 }
217
218 xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
219 pic = intr_pic_register(sc->dev, xref);
220 if (pic == NULL)
221 return (ENXIO);
222
223 return (intr_pic_claim_root(sc->dev, xref, tzic_intr, sc, 0));
224 }
225
226 static int
227 tzic_probe(device_t dev)
228 {
229
230 if (!ofw_bus_status_okay(dev))
231 return (ENXIO);
232
233 if (ofw_bus_is_compatible(dev, "fsl,tzic")) {
234 device_set_desc(dev, "TrustZone Interrupt Controller");
235 return (BUS_PROBE_DEFAULT);
236 }
237 return (ENXIO);
238 }
239
240 static int
241 tzic_attach(device_t dev)
242 {
243 struct tzic_softc *sc = device_get_softc(dev);
244 int i;
245
246 if (tzic_sc)
247 return (ENXIO);
248 tzic_sc = sc;
249 sc->dev = dev;
250
251 i = 0;
252 sc->tzicregs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i,
253 RF_ACTIVE);
254 if (sc->tzicregs == NULL) {
255 device_printf(dev, "could not allocate resources\n");
256 return (ENXIO);
257 }
258
259 /* route all interrupts to IRQ. secure interrupts are for FIQ */
260 for (i = 0; i < 4; i++)
261 tzic_write_4(sc, TZIC_INTSEC(i), 0xffffffff);
262
263 /* disable all interrupts */
264 for (i = 0; i < 4; i++)
265 tzic_write_4(sc, TZIC_ENCLEAR(i), 0xffffffff);
266
267 /* Set all interrupts to priority 0 (max). */
268 for (i = 0; i < 128 / 4; ++i)
269 tzic_write_4(sc, TZIC_PRIORITY(i), 0);
270
271 /*
272 * Set priority mask to lowest (unmasked) prio, set synchronizer to
273 * low-latency mode (as opposed to low-power), enable the controller.
274 */
275 tzic_write_4(sc, TZIC_PRIOMASK, 0xff);
276 tzic_write_4(sc, TZIC_SYNCCTRL, 0);
277 tzic_write_4(sc, TZIC_INTCNTL, INTCNTL_NSEN_MASK|INTCNTL_NSEN|INTCNTL_EN);
278
279 /* Register as a root pic. */
280 if (tzic_pic_attach(sc) != 0) {
281 device_printf(dev, "could not attach PIC\n");
282 return (ENXIO);
283 }
284
285 return (0);
286 }
287
288 static device_method_t tzic_methods[] = {
289 DEVMETHOD(device_probe, tzic_probe),
290 DEVMETHOD(device_attach, tzic_attach),
291
292 DEVMETHOD(pic_disable_intr, tzic_disable_intr),
293 DEVMETHOD(pic_enable_intr, tzic_enable_intr),
294 DEVMETHOD(pic_map_intr, tzic_map_intr),
295 DEVMETHOD(pic_post_filter, tzic_post_filter),
296 DEVMETHOD(pic_post_ithread, tzic_post_ithread),
297 DEVMETHOD(pic_pre_ithread, tzic_pre_ithread),
298
299 DEVMETHOD_END
300 };
301
302 static driver_t tzic_driver = {
303 "tzic",
304 tzic_methods,
305 sizeof(struct tzic_softc),
306 };
307
308 EARLY_DRIVER_MODULE(tzic, ofwbus, tzic_driver, 0, 0, BUS_PASS_INTERRUPT);
Cache object: 0e378220a68ab5b22807d75e230d1bd9
|