1 /* $OpenBSD: octcib.c,v 1.5 2019/09/01 12:16:01 visa Exp $ */
2
3 /*
4 * Copyright (c) 2017, 2019 Visa Hankala
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Driver for Cavium Interrupt Bus (CIB) widget.
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <sys/evcount.h>
27 #include <sys/malloc.h>
28 #include <sys/queue.h>
29
30 #include <dev/ofw/fdt.h>
31 #include <dev/ofw/openfirm.h>
32
33 #include <machine/fdt.h>
34
35 #define CIB_HIGHIPL IPL_BIO
36 #define CIB_MAXBITS 64
37 #define CIB_IRQNUM(sc, bit) (256 + (sc)->sc_dev.dv_unit * CIB_MAXBITS + \
38 (bit))
39
40 #define CIB_EN_RD(sc) \
41 bus_space_read_8((sc)->sc_iot, (sc)->sc_en_ioh, 0)
42 #define CIB_EN_WR(sc, val) \
43 bus_space_write_8((sc)->sc_iot, (sc)->sc_en_ioh, 0, (val))
44 #define CIB_RAW_RD(sc) \
45 bus_space_read_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0)
46 #define CIB_RAW_WR(sc, val) \
47 bus_space_write_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0, (val))
48
49 struct octcib_softc;
50
51 struct octcib_intrhand {
52 LIST_ENTRY(octcib_intrhand) cih_list;
53 int (*cih_func)(void *);
54 void *cih_arg;
55 uint32_t cih_bit;
56 uint32_t cih_flags;
57 #define CIH_MPSAFE 0x01
58 #define CIH_EDGE 0x02 /* edge-triggered */
59 struct evcount cih_count;
60 unsigned int cih_irq; /* for cih_count */
61 struct octcib_softc *cih_sc;
62 };
63
64 struct octcib_softc {
65 struct device sc_dev;
66 void *sc_ih;
67 bus_space_tag_t sc_iot;
68 bus_space_handle_t sc_en_ioh;
69 bus_space_handle_t sc_raw_ioh;
70
71 LIST_HEAD(, octcib_intrhand) sc_bits[CIB_MAXBITS];
72 uint32_t sc_maxbits;
73
74 struct intr_controller sc_ic;
75 };
76
77 int octcib_match(struct device *, void *, void *);
78 void octcib_attach(struct device *, struct device *, void *);
79
80 void *octcib_establish(void *, int, int, int, int (*func)(void *),
81 void *, const char *);
82 void octcib_disestablish(void *);
83 void octcib_intr_barrier(void *);
84 int octcib_intr(void *);
85
86 const struct cfattach octcib_ca = {
87 sizeof(struct octcib_softc), octcib_match, octcib_attach
88 };
89
90 struct cfdriver octcib_cd = {
91 NULL, "octcib", DV_DULL
92 };
93
94 int
octcib_match(struct device * parent,void * match,void * aux)95 octcib_match(struct device *parent, void *match, void *aux)
96 {
97 struct fdt_attach_args *faa = aux;
98
99 return OF_is_compatible(faa->fa_node, "cavium,octeon-7130-cib");
100 }
101
102 void
octcib_attach(struct device * parent,struct device * self,void * aux)103 octcib_attach(struct device *parent, struct device *self, void *aux)
104 {
105 struct fdt_attach_args *faa = aux;
106 struct octcib_softc *sc = (struct octcib_softc *)self;
107 unsigned int i;
108
109 if (faa->fa_nreg != 2) {
110 printf(": expected 2 IO spaces, got %d\n", faa->fa_nreg);
111 return;
112 }
113
114 sc->sc_iot = faa->fa_iot;
115 sc->sc_maxbits = OF_getpropint(faa->fa_node, "cavium,max-bits", 0);
116
117 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
118 0, &sc->sc_raw_ioh)) {
119 printf(": could not map RAW\n");
120 goto error;
121 }
122 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, faa->fa_reg[1].size,
123 0, &sc->sc_en_ioh)) {
124 printf(": could not map EN\n");
125 goto error;
126 }
127
128 /* Disable all interrupts. */
129 CIB_EN_WR(sc, 0);
130 /* Acknowledge any pending interrupts. */
131 CIB_RAW_WR(sc, ~0ul);
132
133 sc->sc_ih = octeon_intr_establish_fdt(faa->fa_node,
134 CIB_HIGHIPL | IPL_MPSAFE, octcib_intr, sc, sc->sc_dev.dv_xname);
135 if (sc->sc_ih == NULL) {
136 printf(": failed to register interrupt\n");
137 goto error;
138 }
139
140 printf(": max-bits %u\n", sc->sc_maxbits);
141
142 for (i = 0; i < CIB_MAXBITS; i++)
143 LIST_INIT(&sc->sc_bits[i]);
144
145 sc->sc_ic.ic_cookie = sc;
146 sc->sc_ic.ic_node = faa->fa_node;
147 sc->sc_ic.ic_establish_fdt_idx = octcib_establish;
148 sc->sc_ic.ic_disestablish = octcib_disestablish;
149 sc->sc_ic.ic_intr_barrier = octcib_intr_barrier;
150 octeon_intr_register(&sc->sc_ic);
151 return;
152
153 error:
154 if (sc->sc_en_ioh != 0)
155 bus_space_unmap(sc->sc_iot, sc->sc_en_ioh,
156 faa->fa_reg[1].size);
157 if (sc->sc_raw_ioh != 0)
158 bus_space_unmap(sc->sc_iot, sc->sc_raw_ioh,
159 faa->fa_reg[0].size);
160 }
161
162 void *
octcib_establish(void * cookie,int node,int idx,int level,int (* func)(void *),void * arg,const char * name)163 octcib_establish(void *cookie, int node, int idx, int level,
164 int (*func)(void *), void *arg, const char *name)
165 {
166 struct octcib_intrhand *cih;
167 struct octcib_softc *sc = cookie;
168 uint64_t en;
169 uint32_t *cells;
170 uint32_t bit, type;
171 int flags, len, s;
172
173 flags = (level & IPL_MPSAFE) ? CIH_MPSAFE : 0;
174 level &= ~IPL_MPSAFE;
175
176 if (level > CIB_HIGHIPL)
177 return NULL;
178
179 len = OF_getproplen(node, "interrupts");
180 if (len / (sizeof(uint32_t) * 2) <= idx ||
181 len % (sizeof(uint32_t) * 2) != 0)
182 return NULL;
183
184 cells = malloc(len, M_TEMP, M_NOWAIT);
185 if (cells == NULL)
186 return NULL;
187 OF_getpropintarray(node, "interrupts", cells, len);
188 bit = cells[idx * 2];
189 type = cells[idx * 2 + 1];
190 free(cells, M_TEMP, len);
191
192 if (bit >= sc->sc_maxbits)
193 return NULL;
194 if (type != 4)
195 flags |= CIH_EDGE;
196
197 cih = malloc(sizeof(*cih), M_DEVBUF, M_NOWAIT);
198 if (cih == NULL)
199 return NULL;
200 cih->cih_func = func;
201 cih->cih_arg = arg;
202 cih->cih_bit = bit;
203 cih->cih_flags = flags;
204 cih->cih_irq = CIB_IRQNUM(sc, bit);
205 cih->cih_sc = sc;
206
207 s = splhigh();
208
209 evcount_attach(&cih->cih_count, name, &cih->cih_irq);
210 LIST_INSERT_HEAD(&sc->sc_bits[bit], cih, cih_list);
211
212 /* Enable the interrupt. */
213 en = CIB_EN_RD(sc);
214 en |= 1ul << bit;
215 CIB_EN_WR(sc, en);
216
217 splx(s);
218
219 return cih;
220 }
221
222 void
octcib_disestablish(void * cookie)223 octcib_disestablish(void *cookie)
224 {
225 struct octcib_intrhand *cih = cookie;
226 struct octcib_softc *sc = cih->cih_sc;
227 uint64_t val;
228 uint32_t bit = cih->cih_bit;
229 int s;
230 #ifdef DIAGNOSTIC
231 struct octcib_intrhand *tmp;
232 int found;
233 #endif
234
235 s = splhigh();
236
237 #ifdef DIAGNOSTIC
238 found = 0;
239 LIST_FOREACH(tmp, &sc->sc_bits[bit], cih_list) {
240 if (tmp == cih) {
241 found = 1;
242 break;
243 }
244 }
245 if (found == 0)
246 panic("%s: intrhand %p not registered", __func__, cih);
247 #endif
248
249 LIST_REMOVE(cih, cih_list);
250 evcount_detach(&cih->cih_count);
251
252 if (LIST_EMPTY(&sc->sc_bits[bit])) {
253 /* Disable the interrupt. */
254 val = CIB_EN_RD(sc);
255 val &= ~(1ul << bit);
256 CIB_EN_WR(sc, val);
257 }
258
259 splx(s);
260
261 free(cih, M_DEVBUF, sizeof(*cih));
262 }
263
264 void
octcib_intr_barrier(void * cookie)265 octcib_intr_barrier(void *cookie)
266 {
267 struct octcib_intrhand *cih = cookie;
268 struct octcib_softc *sc = cih->cih_sc;
269
270 intr_barrier(sc->sc_ih);
271 }
272
273 int
octcib_intr(void * arg)274 octcib_intr(void *arg)
275 {
276 struct octcib_intrhand *cih;
277 struct octcib_softc *sc = arg;
278 uint64_t en, isr, mask;
279 uint32_t bit;
280 int handled = 0;
281 #ifdef MULTIPROCESSOR
282 int need_lock;
283 #endif
284
285 en = CIB_EN_RD(sc);
286 isr = CIB_RAW_RD(sc);
287 isr &= en;
288
289 for (bit = 0; isr != 0 && bit < sc->sc_maxbits; bit++) {
290 mask = 1ul << bit;
291
292 if ((isr & mask) == 0)
293 continue;
294 isr &= ~mask;
295
296 handled = 0;
297 LIST_FOREACH(cih, &sc->sc_bits[bit], cih_list) {
298 /* Acknowledge the interrupt. */
299 if (ISSET(cih->cih_flags, CIH_EDGE))
300 CIB_RAW_WR(sc, mask);
301
302 #ifdef MULTIPROCESSOR
303 if (!ISSET(cih->cih_flags, CIH_MPSAFE))
304 need_lock = 1;
305 else
306 need_lock = 0;
307 if (need_lock)
308 __mp_lock(&kernel_lock);
309 #endif
310 if (cih->cih_func(cih->cih_arg)) {
311 handled = 1;
312 cih->cih_count.ec_count++;
313 }
314 #ifdef MULTIPROCESSOR
315 if (need_lock)
316 __mp_unlock(&kernel_lock);
317 #endif
318 }
319
320 if (handled == 0)
321 printf("%s: spurious interrupt %u (bit %u) "
322 "on cpu %lu\n",
323 sc->sc_dev.dv_xname, CIB_IRQNUM(sc, bit), bit,
324 cpu_number());
325 }
326
327 return 1;
328 }
329