xref: /openbsd-src/sys/arch/octeon/dev/octcib.c (revision 5e3c7963eb248119b7dfd4b0defad58a7d9cd306)
1 /*	$OpenBSD: octcib.c,v 1.2 2017/07/31 14:46:14 visa Exp $	*/
2 
3 /*
4  * Copyright (c) 2017 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/malloc.h>
27 #include <sys/queue.h>
28 
29 #include <dev/ofw/fdt.h>
30 #include <dev/ofw/openfirm.h>
31 
32 #include <machine/fdt.h>
33 
34 #define CIB_HIGHIPL		IPL_BIO
35 #define CIB_MAXBITS		64
36 #define CIB_IRQNUM(sc, bit)	(256 + (sc)->sc_dev.dv_unit * CIB_MAXBITS + \
37 				    (bit))
38 
39 #define CIB_EN_RD(sc) \
40 	bus_space_read_8((sc)->sc_iot, (sc)->sc_en_ioh, 0)
41 #define CIB_EN_WR(sc, val) \
42 	bus_space_write_8((sc)->sc_iot, (sc)->sc_en_ioh, 0, (val))
43 #define CIB_RAW_RD(sc) \
44 	bus_space_read_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0)
45 #define CIB_RAW_WR(sc, val) \
46 	bus_space_write_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0, (val))
47 
48 struct octcib_softc;
49 
50 struct octcib_intrhand {
51 	LIST_ENTRY(octcib_intrhand) cih_list;
52 	int			(*cih_func)(void *);
53 	void			*cih_arg;
54 	uint32_t		 cih_bit;
55 	uint32_t		 cih_flags;
56 #define CIH_MPSAFE			0x01
57 #define CIH_EDGE			0x02	/* edge-triggered */
58 	struct evcount		 cih_count;
59 	unsigned int		 cih_irq;	/* for cih_count */
60 	struct octcib_softc	*cih_sc;
61 };
62 
63 struct octcib_softc {
64 	struct device		 sc_dev;
65 	void			*sc_ih;
66 	bus_space_tag_t		 sc_iot;
67 	bus_space_handle_t	 sc_en_ioh;
68 	bus_space_handle_t	 sc_raw_ioh;
69 
70 	LIST_HEAD(, octcib_intrhand) sc_bits[CIB_MAXBITS];
71 	uint32_t		 sc_maxbits;
72 
73 	struct intr_controller	 sc_ic;
74 };
75 
76 int	 octcib_match(struct device *, void *, void *);
77 void	 octcib_attach(struct device *, struct device *, void *);
78 
79 void	*octcib_establish(void *, int, int, int, int (*func)(void *),
80 	    void *, const char *);
81 void	 octcib_disestablish(void *);
82 int	 octcib_intr(void *);
83 
84 const struct cfattach octcib_ca = {
85 	sizeof(struct octcib_softc), octcib_match, octcib_attach
86 };
87 
88 struct cfdriver octcib_cd = {
89 	NULL, "octcib", DV_DULL
90 };
91 
92 int
93 octcib_match(struct device *parent, void *match, void *aux)
94 {
95 	struct fdt_attach_args *faa = aux;
96 
97 	return OF_is_compatible(faa->fa_node, "cavium,octeon-7130-cib");
98 }
99 
100 void
101 octcib_attach(struct device *parent, struct device *self, void *aux)
102 {
103 	struct fdt_attach_args *faa = aux;
104 	struct octcib_softc *sc = (struct octcib_softc *)self;
105 	unsigned int i;
106 
107 	if (faa->fa_nreg != 2) {
108 		printf(": expected 2 IO spaces, got %d\n", faa->fa_nreg);
109 		return;
110 	}
111 
112 	sc->sc_iot = faa->fa_iot;
113 	sc->sc_maxbits = OF_getpropint(faa->fa_node, "cavium,max-bits", 0);
114 
115 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
116 	    0, &sc->sc_raw_ioh)) {
117 		printf(": could not map RAW\n");
118 		goto error;
119 	}
120 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, faa->fa_reg[1].size,
121 	    0, &sc->sc_en_ioh)) {
122 		printf(": could not map EN\n");
123 		goto error;
124 	}
125 
126 	/* Disable all interrupts. */
127 	CIB_EN_WR(sc, 0);
128 	/* Acknowledge any pending interrupts. */
129 	CIB_RAW_WR(sc, ~0ul);
130 
131 	sc->sc_ih = octeon_intr_establish_fdt(faa->fa_node,
132 	    CIB_HIGHIPL | IPL_MPSAFE, octcib_intr, sc, sc->sc_dev.dv_xname);
133 	if (sc->sc_ih == NULL) {
134 		printf(": failed to register interrupt\n");
135 		goto error;
136 	}
137 
138 	printf(": max-bits %u\n", sc->sc_maxbits);
139 
140 	for (i = 0; i < CIB_MAXBITS; i++)
141 		LIST_INIT(&sc->sc_bits[i]);
142 
143 	sc->sc_ic.ic_cookie = sc;
144 	sc->sc_ic.ic_node = faa->fa_node;
145 	sc->sc_ic.ic_establish_fdt_idx = octcib_establish;
146 	sc->sc_ic.ic_disestablish = octcib_disestablish;
147 	octeon_intr_register(&sc->sc_ic);
148 	return;
149 
150 error:
151 	if (sc->sc_en_ioh != 0)
152 		bus_space_unmap(sc->sc_iot, sc->sc_en_ioh,
153 		    faa->fa_reg[1].size);
154 	if (sc->sc_raw_ioh != 0)
155 		bus_space_unmap(sc->sc_iot, sc->sc_raw_ioh,
156 		    faa->fa_reg[0].size);
157 }
158 
159 void *
160 octcib_establish(void *cookie, int node, int idx, int level,
161     int (*func)(void *), void *arg, const char *name)
162 {
163 	struct octcib_intrhand *cih;
164 	struct octcib_softc *sc = cookie;
165 	uint64_t en;
166 	uint32_t *cells;
167 	uint32_t bit, type;
168 	int flags, len, s;
169 
170 	flags = (level & IPL_MPSAFE) ? CIH_MPSAFE : 0;
171 	level &= ~IPL_MPSAFE;
172 
173 	if (level > CIB_HIGHIPL)
174 		return NULL;
175 
176 	len = OF_getproplen(node, "interrupts");
177 	if (len / (sizeof(uint32_t) * 2) <= idx ||
178 	    len % (sizeof(uint32_t) * 2) != 0)
179 		return NULL;
180 
181 	cells = malloc(len, M_TEMP, M_NOWAIT);
182 	if (cells == NULL)
183 		return NULL;
184 	OF_getpropintarray(node, "interrupts", cells, len);
185 	bit = cells[idx * 2];
186 	type = cells[idx * 2 + 1];
187 	free(cells, M_TEMP, len);
188 
189 	if (bit >= sc->sc_maxbits)
190 		return NULL;
191 	if (type != 4)
192 		flags |= CIH_EDGE;
193 
194 	cih = malloc(sizeof(*cih), M_DEVBUF, M_NOWAIT);
195 	if (cih == NULL)
196 		return NULL;
197 	cih->cih_func = func;
198 	cih->cih_arg = arg;
199 	cih->cih_bit = bit;
200 	cih->cih_flags = flags;
201 	cih->cih_irq = CIB_IRQNUM(sc, bit);
202 	cih->cih_sc = sc;
203 
204 	s = splhigh();
205 
206 	evcount_attach(&cih->cih_count, name, &cih->cih_irq);
207 	LIST_INSERT_HEAD(&sc->sc_bits[bit], cih, cih_list);
208 
209 	/* Enable the interrupt. */
210 	en = CIB_EN_RD(sc);
211 	en |= 1ul << bit;
212 	CIB_EN_WR(sc, en);
213 
214 	splx(s);
215 
216 	return cih;
217 }
218 
219 void
220 octcib_disestablish(void *cookie)
221 {
222 	struct octcib_intrhand *cih = cookie;
223 	struct octcib_softc *sc = cih->cih_sc;
224 	uint64_t val;
225 	uint32_t bit = cih->cih_bit;
226 	int s;
227 #ifdef DIAGNOSTIC
228 	struct octcib_intrhand *tmp;
229 	int found;
230 #endif
231 
232 	s = splhigh();
233 
234 #ifdef DIAGNOSTIC
235 	found = 0;
236 	LIST_FOREACH(tmp, &sc->sc_bits[bit], cih_list) {
237 		if (tmp == cih) {
238 			found = 1;
239 			break;
240 		}
241 	}
242 	if (found == 0)
243 		panic("%s: intrhand %p not registered", __func__, cih);
244 #endif
245 
246 	LIST_REMOVE(cih, cih_list);
247 	evcount_detach(&cih->cih_count);
248 
249 	if (LIST_EMPTY(&sc->sc_bits[bit])) {
250 		/* Disable the interrupt. */
251 		val = CIB_EN_RD(sc);
252 		val &= ~(1ul << bit);
253 		CIB_EN_WR(sc, val);
254 	}
255 
256 	splx(s);
257 
258 	free(cih, M_DEVBUF, sizeof(*cih));
259 }
260 
261 int
262 octcib_intr(void *arg)
263 {
264 	struct octcib_intrhand *cih;
265 	struct octcib_softc *sc = arg;
266 	uint64_t en, isr, mask;
267 	uint32_t bit;
268 	int handled = 0;
269 #ifdef MULTIPROCESSOR
270 	int need_lock;
271 #endif
272 
273 	en = CIB_EN_RD(sc);
274 	isr = CIB_RAW_RD(sc);
275 	isr &= en;
276 
277 	for (bit = 0; isr != 0 && bit < sc->sc_maxbits; bit++) {
278 		mask = 1ul << bit;
279 
280 		if ((isr & mask) == 0)
281 			continue;
282 		isr &= ~mask;
283 
284 		handled = 0;
285 		LIST_FOREACH(cih, &sc->sc_bits[bit], cih_list) {
286 			/* Acknowledge the interrupt. */
287 			if (ISSET(cih->cih_flags, CIH_EDGE))
288 				CIB_RAW_WR(sc, mask);
289 
290 #ifdef MULTIPROCESSOR
291 			if (!ISSET(cih->cih_flags, CIH_MPSAFE))
292 				need_lock = 1;
293 			else
294 				need_lock = 0;
295 			if (need_lock)
296 				__mp_lock(&kernel_lock);
297 #endif
298 			if (cih->cih_func(cih->cih_arg)) {
299 				handled = 1;
300 				cih->cih_count.ec_count++;
301 			}
302 #ifdef MULTIPROCESSOR
303 			if (need_lock)
304 				__mp_unlock(&kernel_lock);
305 #endif
306 		}
307 
308 		if (handled == 0)
309 			printf("%s: spurious interrupt %u (bit %u)\n",
310 			    sc->sc_dev.dv_xname, CIB_IRQNUM(sc, bit), bit);
311 	}
312 
313 	return 1;
314 }
315