xref: /openbsd/sys/arch/octeon/dev/octcib.c (revision 76d0caae)
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
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
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 *
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
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
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
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