xref: /openbsd-src/sys/arch/hppa/dev/apic.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: apic.c,v 1.7 2007/10/06 23:50:54 krw 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
81 apic_write(volatile struct elroy_regs *r, u_int32_t reg, u_int32_t val)
82 {
83 	elroy_write32(&r->apic_addr, htole32(reg));
84 	elroy_write32(&r->apic_data, htole32(val));
85 	elroy_read32(&r->apic_data);
86 }
87 
88 u_int32_t
89 apic_read(volatile struct elroy_regs *r, u_int32_t reg)
90 {
91 	elroy_write32(&r->apic_addr, htole32(reg));
92 	return letoh32(elroy_read32(&r->apic_data));
93 }
94 
95 void
96 apic_attach(struct elroy_softc *sc)
97 {
98 	volatile struct elroy_regs *r = sc->sc_regs;
99 	u_int32_t data;
100 
101 	data = apic_read(r, APIC_VERSION);
102 	sc->sc_nints = (data & APIC_VERSION_NENT) >> APIC_VERSION_NENT_SHIFT;
103 	printf(" APIC ver %x, %d pins",
104 	    data & APIC_VERSION_MASK, sc->sc_nints);
105 
106 	sc->sc_irq = malloc(sc->sc_nints * sizeof(int), M_DEVBUF,
107 	    M_NOWAIT | M_ZERO);
108 	if (sc->sc_irq == NULL)
109 		panic("apic_attach: cannot allocate irq table\n");
110 
111 	apic_get_int_tbl(sc);
112 
113 #ifdef DEBUG
114 	apic_dump(sc);
115 #endif
116 }
117 
118 int
119 apic_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
120 {
121 	struct elroy_softc *sc = pa->pa_pc->_cookie;
122 	pci_chipset_tag_t pc = pa->pa_pc;
123 	pcitag_t tag = pa->pa_tag;
124 	pcireg_t reg;
125 	int line;
126 
127 	reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
128 #ifdef DEBUG
129 	printf(" pin=%d line=%d ", PCI_INTERRUPT_PIN(reg),
130 	    PCI_INTERRUPT_LINE(reg));
131 #endif
132 	line = PCI_INTERRUPT_LINE(reg);
133 	if (sc->sc_irq[line] == 0)
134 		sc->sc_irq[line] = cpu_intr_findirq();
135 	*ihp = (line << APIC_INT_LINE_SHIFT) | sc->sc_irq[line];
136 	return (APIC_INT_IRQ(*ihp) == 0);
137 }
138 
139 const char *
140 apic_intr_string(void *v, pci_intr_handle_t ih)
141 {
142 	static char buf[32];
143 
144 	snprintf(buf, 32, "line %ld irq %ld",
145 	    APIC_INT_LINE(ih), APIC_INT_IRQ(ih));
146 
147 	return (buf);
148 }
149 
150 void *
151 apic_intr_establish(void *v, pci_intr_handle_t ih,
152     int pri, int (*handler)(void *), void *arg, char *name)
153 {
154 	struct elroy_softc *sc = v;
155 	volatile struct elroy_regs *r = sc->sc_regs;
156 	hppa_hpa_t hpa = cpu_gethpa(0);
157 	struct evcount *cnt;
158 	struct apic_iv *aiv, *biv;
159 	void *iv;
160 	int irq = APIC_INT_IRQ(ih);
161 	int line = APIC_INT_LINE(ih);
162 	u_int32_t ent0;
163 
164 	/* no mapping or bogus */
165 	if (irq <= 0 || irq > 31)
166 		return (NULL);
167 
168 	aiv = malloc(sizeof(struct apic_iv), M_DEVBUF, M_NOWAIT);
169 	if (aiv == NULL) {
170 		free(cnt, M_DEVBUF);
171 		return NULL;
172 	}
173 
174 	aiv->sc = sc;
175 	aiv->ih = ih;
176 	aiv->handler = handler;
177 	aiv->arg = arg;
178 	aiv->next = NULL;
179 	aiv->cnt = NULL;
180 	if (apic_intr_list[irq]) {
181 		cnt = malloc(sizeof(struct evcount), M_DEVBUF, M_NOWAIT);
182 		if (!cnt) {
183 			free(aiv, M_DEVBUF);
184 			return (NULL);
185 		}
186 
187 		evcount_attach(cnt, name, NULL, &evcount_intr);
188 		biv = apic_intr_list[irq];
189 		while (biv->next)
190 			biv = biv->next;
191 		biv->next = aiv;
192 		aiv->cnt = cnt;
193 		return (arg);
194 	}
195 
196 	if ((iv = cpu_intr_establish(pri, irq, apic_intr, aiv, name))) {
197 		ent0 = (31 - irq) & APIC_ENT0_VEC;
198 		ent0 |= apic_get_int_ent0(sc, line);
199 #if 0
200 		if (cold) {
201 			sc->sc_imr |= (1 << irq);
202 			ent0 |= APIC_ENT0_MASK;
203 		}
204 #endif
205 		apic_write(sc->sc_regs, APIC_ENT0(line), APIC_ENT0_MASK);
206 		apic_write(sc->sc_regs, APIC_ENT1(line),
207 		    ((hpa & 0x0ff00000) >> 4) | ((hpa & 0x000ff000) << 12));
208 		apic_write(sc->sc_regs, APIC_ENT0(line), ent0);
209 
210 		/* Signal EOI. */
211 		elroy_write32(&r->apic_eoi,
212 		    htole32((31 - irq) & APIC_ENT0_VEC));
213 
214 		apic_intr_list[irq] = aiv;
215 	}
216 
217 	return (arg);
218 }
219 
220 void
221 apic_intr_disestablish(void *v, void *cookie)
222 {
223 }
224 
225 int
226 apic_intr(void *v)
227 {
228 	struct apic_iv *iv = v;
229 	struct elroy_softc *sc = iv->sc;
230 	volatile struct elroy_regs *r = sc->sc_regs;
231 	int claimed = 0;
232 
233 	while (iv) {
234 		if (iv->handler(iv->arg)) {
235 			if (iv->cnt)
236 				iv->cnt->ec_count++;
237 			else
238 				claimed = 1;
239 		}
240 		iv = iv->next;
241 	}
242 
243 	/* Signal EOI. */
244 	elroy_write32(&r->apic_eoi,
245 	    htole32((31 - APIC_INT_IRQ(iv->ih)) & APIC_ENT0_VEC));
246 
247 	return (claimed);
248 }
249 
250 /* Maximum number of supported interrupt routing entries. */
251 #define MAX_INT_TBL_SZ	16
252 
253 void
254 apic_get_int_tbl(struct elroy_softc *sc)
255 {
256 	struct pdc_pat_io_num int_tbl_sz PDC_ALIGNMENT;
257 	struct pdc_pat_pci_rt int_tbl[MAX_INT_TBL_SZ] PDC_ALIGNMENT;
258 	size_t size;
259 
260 	/*
261 	 * XXX int_tbl should not be allocated on the stack, but we need a
262 	 * 1:1 mapping, and malloc doesn't provide that.
263 	 */
264 
265 	if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL_SZ,
266 	    &int_tbl_sz, 0, 0, 0, 0, 0))
267 		return;
268 
269 	if (int_tbl_sz.num > MAX_INT_TBL_SZ)
270 		panic("interrupt routing table too big (%d entries)",
271 		    int_tbl_sz.num);
272 
273 	size = int_tbl_sz.num * sizeof(struct pdc_pat_pci_rt);
274 	sc->sc_int_tbl_sz = int_tbl_sz.num;
275 	sc->sc_int_tbl = malloc(size, M_DEVBUF, M_NOWAIT);
276 	if (sc->sc_int_tbl == NULL)
277 		return;
278 
279 	if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL,
280 	    &int_tbl_sz, 0, &int_tbl, 0, 0, 0))
281 		return;
282 
283 	memcpy(sc->sc_int_tbl, int_tbl, size);
284 }
285 
286 u_int32_t
287 apic_get_int_ent0(struct elroy_softc *sc, int line)
288 {
289 	volatile struct elroy_regs *r = sc->sc_regs;
290 	int trigger = MPS_INT(MPS_INTPO_DEF, MPS_INTTR_DEF);
291 	u_int32_t ent0 = APIC_ENT0_LOW | APIC_ENT0_LEV;
292 	int bus, mpspo, mpstr;
293 	int i;
294 
295 	bus = letoh32(elroy_read32(&r->busnum)) & 0xff;
296 	for (i = 0; i < sc->sc_int_tbl_sz; i++) {
297 		if (bus == sc->sc_int_tbl[i].bus &&
298 		    line == sc->sc_int_tbl[i].line)
299 			trigger = sc->sc_int_tbl[i].trigger;
300 	}
301 
302 	mpspo = (trigger >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
303 	mpstr = (trigger >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;
304 
305 	switch (mpspo) {
306 	case MPS_INTPO_DEF:
307 		break;
308 	case MPS_INTPO_ACTHI:
309 		ent0 &= ~APIC_ENT0_LOW;
310 		break;
311 	case MPS_INTPO_ACTLO:
312 		ent0 |= APIC_ENT0_LOW;
313 		break;
314 	default:
315 		panic("unknown MPS interrupt polarity %d", mpspo);
316 	}
317 
318 	switch(mpstr) {
319 	case MPS_INTTR_DEF:
320 		break;
321 	case MPS_INTTR_LEVEL:
322 		ent0 |= APIC_ENT0_LEV;
323 		break;
324 	case MPS_INTTR_EDGE:
325 		ent0 &= ~APIC_ENT0_LEV;
326 		break;
327 	default:
328 		panic("unknown MPS interrupt trigger %d", mpstr);
329 	}
330 
331 	return ent0;
332 }
333 
334 #ifdef DEBUG
335 void
336 apic_dump(struct elroy_softc *sc)
337 {
338 	int i;
339 
340 	for (i = 0; i < sc->sc_nints; i++)
341 		printf("0x%04x 0x%04x\n", apic_read(sc->sc_regs, APIC_ENT0(i)),
342 		    apic_read(sc->sc_regs, APIC_ENT1(i)));
343 
344 	for (i = 0; i < sc->sc_int_tbl_sz; i++) {
345 		printf("type=%x ", sc->sc_int_tbl[i].type);
346 		printf("len=%d ", sc->sc_int_tbl[i].len);
347 		printf("itype=%d ", sc->sc_int_tbl[i].itype);
348 		printf("trigger=%x ", sc->sc_int_tbl[i].trigger);
349 		printf("pin=%x ", sc->sc_int_tbl[i].pin);
350 		printf("bus=%d ", sc->sc_int_tbl[i].bus);
351 		printf("line=%d ", sc->sc_int_tbl[i].line);
352 		printf("addr=%x\n", sc->sc_int_tbl[i].addr);
353 	}
354 }
355 #endif
356