xref: /openbsd/sys/arch/hppa/dev/apic.c (revision 5af055cd)
1 /*	$OpenBSD: apic.c,v 1.18 2015/09/08 07:14:04 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2005 Michael Shalayeff
5  * Copyright (c) 2007 Mark Kettenis
6  * All rights reserved.
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
17  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
18  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 #include <sys/evcount.h>
24 #include <sys/malloc.h>
25 
26 #include <machine/autoconf.h>
27 #include <machine/pdc.h>
28 
29 #include <dev/pci/pcireg.h>
30 #include <dev/pci/pcivar.h>
31 #include <dev/pci/pcidevs.h>
32 
33 #include <hppa/dev/elroyreg.h>
34 #include <hppa/dev/elroyvar.h>
35 
36 #define APIC_INT_LINE_MASK	0x0000ff00
37 #define APIC_INT_LINE_SHIFT	8
38 #define APIC_INT_IRQ_MASK	0x0000001f
39 
40 #define APIC_INT_LINE(x) (((x) & APIC_INT_LINE_MASK) >> APIC_INT_LINE_SHIFT)
41 #define APIC_INT_IRQ(x) ((x) & APIC_INT_IRQ_MASK)
42 
43 /*
44  * Interrupt types match the Intel MP Specification.
45  */
46 
47 #define MPS_INTPO_DEF		0
48 #define MPS_INTPO_ACTHI		1
49 #define MPS_INTPO_ACTLO		3
50 #define MPS_INTPO_SHIFT		0
51 #define MPS_INTPO_MASK		3
52 
53 #define MPS_INTTR_DEF		0
54 #define MPS_INTTR_EDGE		1
55 #define MPS_INTTR_LEVEL		3
56 #define MPS_INTTR_SHIFT		2
57 #define MPS_INTTR_MASK		3
58 
59 #define MPS_INT(p,t) \
60     ((((p) & MPS_INTPO_MASK) << MPS_INTPO_SHIFT) | \
61      (((t) & MPS_INTTR_MASK) << MPS_INTTR_SHIFT))
62 
63 struct apic_iv {
64 	struct elroy_softc *sc;
65 	pci_intr_handle_t ih;
66 	int (*handler)(void *);
67 	void *arg;
68 	struct apic_iv *next;
69 	struct evcount *cnt;
70 };
71 
72 struct apic_iv *apic_intr_list[CPU_NINTS];
73 
74 void	apic_get_int_tbl(struct elroy_softc *);
75 u_int32_t apic_get_int_ent0(struct elroy_softc *, int);
76 #ifdef DEBUG
77 void	apic_dump(struct elroy_softc *);
78 #endif
79 
80 void		apic_write(volatile struct elroy_regs *r, u_int32_t reg,
81 		    u_int32_t val);
82 u_int32_t	apic_read(volatile struct elroy_regs *r, u_int32_t reg);
83 
84 void
85 apic_write(volatile struct elroy_regs *r, u_int32_t reg, u_int32_t val)
86 {
87 	elroy_write32(&r->apic_addr, htole32(reg));
88 	elroy_write32(&r->apic_data, htole32(val));
89 	elroy_read32(&r->apic_data);
90 }
91 
92 u_int32_t
93 apic_read(volatile struct elroy_regs *r, u_int32_t reg)
94 {
95 	elroy_write32(&r->apic_addr, htole32(reg));
96 	return letoh32(elroy_read32(&r->apic_data));
97 }
98 
99 void
100 apic_attach(struct elroy_softc *sc)
101 {
102 	volatile struct elroy_regs *r = sc->sc_regs;
103 	u_int32_t data;
104 
105 	data = apic_read(r, APIC_VERSION);
106 	sc->sc_nints = (data & APIC_VERSION_NENT) >> APIC_VERSION_NENT_SHIFT;
107 	printf(" APIC ver %x, %d pins",
108 	    data & APIC_VERSION_MASK, sc->sc_nints);
109 
110 	sc->sc_irq = mallocarray(sc->sc_nints, sizeof(int), M_DEVBUF,
111 	    M_NOWAIT | M_ZERO);
112 	if (sc->sc_irq == NULL)
113 		panic("apic_attach: cannot allocate irq table");
114 
115 	apic_get_int_tbl(sc);
116 
117 #ifdef DEBUG
118 	apic_dump(sc);
119 #endif
120 }
121 
122 int
123 apic_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
124 {
125 	struct elroy_softc *sc = pa->pa_pc->_cookie;
126 	pci_chipset_tag_t pc = pa->pa_pc;
127 	pcitag_t tag = pa->pa_tag;
128 	pcireg_t reg;
129 	int line;
130 
131 	reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
132 #ifdef DEBUG
133 	printf(" pin=%d line=%d ", PCI_INTERRUPT_PIN(reg),
134 	    PCI_INTERRUPT_LINE(reg));
135 #endif
136 	line = PCI_INTERRUPT_LINE(reg);
137 	if (sc->sc_irq[line] == 0)
138 		sc->sc_irq[line] = cpu_intr_findirq();
139 	*ihp = (line << APIC_INT_LINE_SHIFT) | sc->sc_irq[line];
140 	return (APIC_INT_IRQ(*ihp) == 0);
141 }
142 
143 const char *
144 apic_intr_string(void *v, pci_intr_handle_t ih)
145 {
146 	static char buf[32];
147 
148 	snprintf(buf, 32, "line %ld irq %ld",
149 	    APIC_INT_LINE(ih), APIC_INT_IRQ(ih));
150 
151 	return (buf);
152 }
153 
154 void *
155 apic_intr_establish(void *v, pci_intr_handle_t ih,
156     int pri, int (*handler)(void *), void *arg, const char *name)
157 {
158 	struct elroy_softc *sc = v;
159 	volatile struct elroy_regs *r = sc->sc_regs;
160 	hppa_hpa_t hpa = cpu_gethpa(0);
161 	struct evcount *cnt;
162 	struct apic_iv *aiv, *biv;
163 	void *iv;
164 	int irq = APIC_INT_IRQ(ih);
165 	int line = APIC_INT_LINE(ih);
166 	u_int32_t ent0;
167 
168 	/* no mapping or bogus */
169 	if (irq <= 0 || irq > 31)
170 		return (NULL);
171 
172 	aiv = malloc(sizeof(struct apic_iv), M_DEVBUF, M_NOWAIT);
173 	if (aiv == NULL)
174 		return (NULL);
175 
176 	cnt = malloc(sizeof(struct evcount), M_DEVBUF, M_NOWAIT);
177 	if (!cnt) {
178 		free(aiv, M_DEVBUF, sizeof *aiv);
179 		return (NULL);
180 	}
181 
182 	aiv->sc = sc;
183 	aiv->ih = ih;
184 	aiv->handler = handler;
185 	aiv->arg = arg;
186 	aiv->next = NULL;
187 	aiv->cnt = cnt;
188 
189 	evcount_attach(cnt, name, NULL);
190 
191 	if (apic_intr_list[irq]) {
192 		biv = apic_intr_list[irq];
193 		while (biv->next)
194 			biv = biv->next;
195 		biv->next = aiv;
196 		return (arg);
197 	}
198 
199 	if ((iv = cpu_intr_establish(pri, irq, apic_intr, aiv, NULL))) {
200 		ent0 = (31 - irq) & APIC_ENT0_VEC;
201 		ent0 |= apic_get_int_ent0(sc, line);
202 #if 0
203 		if (cold) {
204 			sc->sc_imr |= (1 << irq);
205 			ent0 |= APIC_ENT0_MASK;
206 		}
207 #endif
208 		apic_write(sc->sc_regs, APIC_ENT0(line), APIC_ENT0_MASK);
209 		apic_write(sc->sc_regs, APIC_ENT1(line),
210 		    ((hpa & 0x0ff00000) >> 4) | ((hpa & 0x000ff000) << 12));
211 		apic_write(sc->sc_regs, APIC_ENT0(line), ent0);
212 
213 		/* Signal EOI. */
214 		elroy_write32(&r->apic_eoi,
215 		    htole32((31 - irq) & APIC_ENT0_VEC));
216 
217 		apic_intr_list[irq] = aiv;
218 	}
219 
220 	return (arg);
221 }
222 
223 void
224 apic_intr_disestablish(void *v, void *cookie)
225 {
226 }
227 
228 int
229 apic_intr(void *v)
230 {
231 	struct apic_iv *iv = v;
232 	struct elroy_softc *sc = iv->sc;
233 	volatile struct elroy_regs *r = sc->sc_regs;
234 	pci_intr_handle_t ih = iv->ih;
235 	int claimed = 0;
236 
237 	while (iv) {
238 		claimed = iv->handler(iv->arg);
239 		if (claimed != 0 && iv->cnt)
240 			iv->cnt->ec_count++;
241 		if (claimed == 1)
242 			break;
243 		iv = iv->next;
244 	}
245 
246 	/* Signal EOI. */
247 	elroy_write32(&r->apic_eoi,
248 	    htole32((31 - APIC_INT_IRQ(ih)) & APIC_ENT0_VEC));
249 
250 	return (claimed);
251 }
252 
253 /* Maximum number of supported interrupt routing entries. */
254 #define MAX_INT_TBL_SZ	16
255 
256 void
257 apic_get_int_tbl(struct elroy_softc *sc)
258 {
259 	struct pdc_pat_io_num int_tbl_sz PDC_ALIGNMENT;
260 	struct pdc_pat_pci_rt int_tbl[MAX_INT_TBL_SZ] PDC_ALIGNMENT;
261 	size_t size;
262 
263 	/*
264 	 * XXX int_tbl should not be allocated on the stack, but we need a
265 	 * 1:1 mapping, and malloc doesn't provide that.
266 	 */
267 
268 	if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL_SZ,
269 	    &int_tbl_sz, 0, 0, 0, 0, 0))
270 		return;
271 
272 	if (int_tbl_sz.num > MAX_INT_TBL_SZ)
273 		panic("interrupt routing table too big (%d entries)",
274 		    int_tbl_sz.num);
275 
276 	size = int_tbl_sz.num * sizeof(struct pdc_pat_pci_rt);
277 	sc->sc_int_tbl_sz = int_tbl_sz.num;
278 	sc->sc_int_tbl = malloc(size, M_DEVBUF, M_NOWAIT);
279 	if (sc->sc_int_tbl == NULL)
280 		return;
281 
282 	if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL,
283 	    &int_tbl_sz, 0, &int_tbl, 0, 0, 0))
284 		return;
285 
286 	memcpy(sc->sc_int_tbl, int_tbl, size);
287 }
288 
289 u_int32_t
290 apic_get_int_ent0(struct elroy_softc *sc, int line)
291 {
292 	volatile struct elroy_regs *r = sc->sc_regs;
293 	int trigger = MPS_INT(MPS_INTPO_DEF, MPS_INTTR_DEF);
294 	u_int32_t ent0 = APIC_ENT0_LOW | APIC_ENT0_LEV;
295 	int bus, mpspo, mpstr;
296 	int i;
297 
298 	bus = letoh32(elroy_read32(&r->busnum)) & 0xff;
299 	for (i = 0; i < sc->sc_int_tbl_sz; i++) {
300 		if (bus == sc->sc_int_tbl[i].bus &&
301 		    line == sc->sc_int_tbl[i].line)
302 			trigger = sc->sc_int_tbl[i].trigger;
303 	}
304 
305 	mpspo = (trigger >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
306 	mpstr = (trigger >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;
307 
308 	switch (mpspo) {
309 	case MPS_INTPO_DEF:
310 		break;
311 	case MPS_INTPO_ACTHI:
312 		ent0 &= ~APIC_ENT0_LOW;
313 		break;
314 	case MPS_INTPO_ACTLO:
315 		ent0 |= APIC_ENT0_LOW;
316 		break;
317 	default:
318 		panic("unknown MPS interrupt polarity %d", mpspo);
319 	}
320 
321 	switch(mpstr) {
322 	case MPS_INTTR_DEF:
323 		break;
324 	case MPS_INTTR_LEVEL:
325 		ent0 |= APIC_ENT0_LEV;
326 		break;
327 	case MPS_INTTR_EDGE:
328 		ent0 &= ~APIC_ENT0_LEV;
329 		break;
330 	default:
331 		panic("unknown MPS interrupt trigger %d", mpstr);
332 	}
333 
334 	return ent0;
335 }
336 
337 #ifdef DEBUG
338 void
339 apic_dump(struct elroy_softc *sc)
340 {
341 	int i;
342 
343 	for (i = 0; i < sc->sc_nints; i++)
344 		printf("0x%04x 0x%04x\n", apic_read(sc->sc_regs, APIC_ENT0(i)),
345 		    apic_read(sc->sc_regs, APIC_ENT1(i)));
346 
347 	for (i = 0; i < sc->sc_int_tbl_sz; i++) {
348 		printf("type=%x ", sc->sc_int_tbl[i].type);
349 		printf("len=%d ", sc->sc_int_tbl[i].len);
350 		printf("itype=%d ", sc->sc_int_tbl[i].itype);
351 		printf("trigger=%x ", sc->sc_int_tbl[i].trigger);
352 		printf("pin=%x ", sc->sc_int_tbl[i].pin);
353 		printf("bus=%d ", sc->sc_int_tbl[i].bus);
354 		printf("line=%d ", sc->sc_int_tbl[i].line);
355 		printf("addr=%x\n", sc->sc_int_tbl[i].addr);
356 	}
357 }
358 #endif
359