xref: /openbsd-src/sys/arch/hppa/dev/apic.c (revision 51f66ac76fe2119d6ac8316b4a09dbb260bff705)
1*51f66ac7Skettenis /*	$OpenBSD: apic.c,v 1.19 2018/05/14 13:54:39 kettenis Exp $	*/
2b95ee612Skettenis 
3b95ee612Skettenis /*
4b95ee612Skettenis  * Copyright (c) 2005 Michael Shalayeff
5b95ee612Skettenis  * Copyright (c) 2007 Mark Kettenis
6b95ee612Skettenis  * All rights reserved.
7b95ee612Skettenis  *
8b95ee612Skettenis  * Permission to use, copy, modify, and distribute this software for any
9b95ee612Skettenis  * purpose with or without fee is hereby granted, provided that the above
10b95ee612Skettenis  * copyright notice and this permission notice appear in all copies.
11b95ee612Skettenis  *
12b95ee612Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13b95ee612Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14b95ee612Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15b95ee612Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16b95ee612Skettenis  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
17b95ee612Skettenis  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
18b95ee612Skettenis  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
19b95ee612Skettenis 
20b95ee612Skettenis #include <sys/param.h>
21b95ee612Skettenis #include <sys/systm.h>
22b95ee612Skettenis #include <sys/device.h>
23804c1233Skettenis #include <sys/evcount.h>
24b95ee612Skettenis #include <sys/malloc.h>
25b95ee612Skettenis 
26b95ee612Skettenis #include <machine/autoconf.h>
27b95ee612Skettenis #include <machine/pdc.h>
28b95ee612Skettenis 
29b95ee612Skettenis #include <dev/pci/pcireg.h>
30b95ee612Skettenis #include <dev/pci/pcivar.h>
31b95ee612Skettenis #include <dev/pci/pcidevs.h>
32b95ee612Skettenis 
33b95ee612Skettenis #include <hppa/dev/elroyreg.h>
34b95ee612Skettenis #include <hppa/dev/elroyvar.h>
35b95ee612Skettenis 
3615fe2033Skettenis #define APIC_INT_LINE_MASK	0x0000ff00
3715fe2033Skettenis #define APIC_INT_LINE_SHIFT	8
3815fe2033Skettenis #define APIC_INT_IRQ_MASK	0x0000001f
3915fe2033Skettenis 
4015fe2033Skettenis #define APIC_INT_LINE(x) (((x) & APIC_INT_LINE_MASK) >> APIC_INT_LINE_SHIFT)
4115fe2033Skettenis #define APIC_INT_IRQ(x) ((x) & APIC_INT_IRQ_MASK)
4215fe2033Skettenis 
4389a56fa7Skettenis /*
4489a56fa7Skettenis  * Interrupt types match the Intel MP Specification.
4589a56fa7Skettenis  */
4689a56fa7Skettenis 
4789a56fa7Skettenis #define MPS_INTPO_DEF		0
4889a56fa7Skettenis #define MPS_INTPO_ACTHI		1
4989a56fa7Skettenis #define MPS_INTPO_ACTLO		3
5089a56fa7Skettenis #define MPS_INTPO_SHIFT		0
5189a56fa7Skettenis #define MPS_INTPO_MASK		3
5289a56fa7Skettenis 
5389a56fa7Skettenis #define MPS_INTTR_DEF		0
5489a56fa7Skettenis #define MPS_INTTR_EDGE		1
5589a56fa7Skettenis #define MPS_INTTR_LEVEL		3
5689a56fa7Skettenis #define MPS_INTTR_SHIFT		2
5789a56fa7Skettenis #define MPS_INTTR_MASK		3
5889a56fa7Skettenis 
5989a56fa7Skettenis #define MPS_INT(p,t) \
6089a56fa7Skettenis     ((((p) & MPS_INTPO_MASK) << MPS_INTPO_SHIFT) | \
6189a56fa7Skettenis      (((t) & MPS_INTTR_MASK) << MPS_INTTR_SHIFT))
6289a56fa7Skettenis 
63b95ee612Skettenis struct apic_iv {
64b95ee612Skettenis 	struct elroy_softc *sc;
65b95ee612Skettenis 	pci_intr_handle_t ih;
66b95ee612Skettenis 	int (*handler)(void *);
67b95ee612Skettenis 	void *arg;
68b95ee612Skettenis 	struct apic_iv *next;
69804c1233Skettenis 	struct evcount *cnt;
70b95ee612Skettenis };
71b95ee612Skettenis 
72b95ee612Skettenis struct apic_iv *apic_intr_list[CPU_NINTS];
73b95ee612Skettenis 
7489a56fa7Skettenis void	apic_get_int_tbl(struct elroy_softc *);
7589a56fa7Skettenis u_int32_t apic_get_int_ent0(struct elroy_softc *, int);
76b95ee612Skettenis #ifdef DEBUG
77b95ee612Skettenis void	apic_dump(struct elroy_softc *);
78b95ee612Skettenis #endif
79b95ee612Skettenis 
80dd6e4488Sderaadt void		apic_write(volatile struct elroy_regs *r, u_int32_t reg,
81dd6e4488Sderaadt 		    u_int32_t val);
82dd6e4488Sderaadt u_int32_t	apic_read(volatile struct elroy_regs *r, u_int32_t reg);
83dd6e4488Sderaadt 
84b95ee612Skettenis void
apic_write(volatile struct elroy_regs * r,u_int32_t reg,u_int32_t val)85b95ee612Skettenis apic_write(volatile struct elroy_regs *r, u_int32_t reg, u_int32_t val)
86b95ee612Skettenis {
87b95ee612Skettenis 	elroy_write32(&r->apic_addr, htole32(reg));
88b95ee612Skettenis 	elroy_write32(&r->apic_data, htole32(val));
89b95ee612Skettenis 	elroy_read32(&r->apic_data);
90b95ee612Skettenis }
91b95ee612Skettenis 
92b95ee612Skettenis u_int32_t
apic_read(volatile struct elroy_regs * r,u_int32_t reg)93b95ee612Skettenis apic_read(volatile struct elroy_regs *r, u_int32_t reg)
94b95ee612Skettenis {
95b95ee612Skettenis 	elroy_write32(&r->apic_addr, htole32(reg));
96b95ee612Skettenis 	return letoh32(elroy_read32(&r->apic_data));
97b95ee612Skettenis }
98b95ee612Skettenis 
99b95ee612Skettenis void
apic_attach(struct elroy_softc * sc)100b95ee612Skettenis apic_attach(struct elroy_softc *sc)
101b95ee612Skettenis {
102b95ee612Skettenis 	volatile struct elroy_regs *r = sc->sc_regs;
103b95ee612Skettenis 	u_int32_t data;
104b95ee612Skettenis 
105b95ee612Skettenis 	data = apic_read(r, APIC_VERSION);
106b95ee612Skettenis 	sc->sc_nints = (data & APIC_VERSION_NENT) >> APIC_VERSION_NENT_SHIFT;
107b95ee612Skettenis 	printf(" APIC ver %x, %d pins",
108b95ee612Skettenis 	    data & APIC_VERSION_MASK, sc->sc_nints);
109b95ee612Skettenis 
1105714159aSdoug 	sc->sc_irq = mallocarray(sc->sc_nints, sizeof(int), M_DEVBUF,
111184c5478Skrw 	    M_NOWAIT | M_ZERO);
11215fe2033Skettenis 	if (sc->sc_irq == NULL)
113859d5ed4Skrw 		panic("apic_attach: cannot allocate irq table");
11415fe2033Skettenis 
11589a56fa7Skettenis 	apic_get_int_tbl(sc);
11689a56fa7Skettenis 
117b95ee612Skettenis #ifdef DEBUG
118b95ee612Skettenis 	apic_dump(sc);
119b95ee612Skettenis #endif
120b95ee612Skettenis }
121b95ee612Skettenis 
122b95ee612Skettenis int
apic_intr_map(struct pci_attach_args * pa,pci_intr_handle_t * ihp)123b95ee612Skettenis apic_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
124b95ee612Skettenis {
12515fe2033Skettenis 	struct elroy_softc *sc = pa->pa_pc->_cookie;
126b95ee612Skettenis 	pci_chipset_tag_t pc = pa->pa_pc;
127b95ee612Skettenis 	pcitag_t tag = pa->pa_tag;
128b95ee612Skettenis 	pcireg_t reg;
12915fe2033Skettenis 	int line;
130b95ee612Skettenis 
131b95ee612Skettenis 	reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
132b95ee612Skettenis #ifdef DEBUG
133b95ee612Skettenis 	printf(" pin=%d line=%d ", PCI_INTERRUPT_PIN(reg),
134b95ee612Skettenis 	    PCI_INTERRUPT_LINE(reg));
135b95ee612Skettenis #endif
13615fe2033Skettenis 	line = PCI_INTERRUPT_LINE(reg);
137*51f66ac7Skettenis 	if (sc->sc_irq[line] <= 0) {
138*51f66ac7Skettenis 		if ((sc->sc_irq[line] = cpu_intr_findirq()) == -1)
139*51f66ac7Skettenis 			return 1;
140*51f66ac7Skettenis 	}
14115fe2033Skettenis 	*ihp = (line << APIC_INT_LINE_SHIFT) | sc->sc_irq[line];
14215fe2033Skettenis 	return (APIC_INT_IRQ(*ihp) == 0);
143b95ee612Skettenis }
144b95ee612Skettenis 
145b95ee612Skettenis const char *
apic_intr_string(void * v,pci_intr_handle_t ih)146b95ee612Skettenis apic_intr_string(void *v, pci_intr_handle_t ih)
147b95ee612Skettenis {
148b95ee612Skettenis 	static char buf[32];
149b95ee612Skettenis 
15015fe2033Skettenis 	snprintf(buf, 32, "line %ld irq %ld",
15115fe2033Skettenis 	    APIC_INT_LINE(ih), APIC_INT_IRQ(ih));
152b95ee612Skettenis 
153b95ee612Skettenis 	return (buf);
154b95ee612Skettenis }
155b95ee612Skettenis 
156b95ee612Skettenis void *
apic_intr_establish(void * v,pci_intr_handle_t ih,int pri,int (* handler)(void *),void * arg,const char * name)157b95ee612Skettenis apic_intr_establish(void *v, pci_intr_handle_t ih,
158c03b1b92Smk     int pri, int (*handler)(void *), void *arg, const char *name)
159b95ee612Skettenis {
160b95ee612Skettenis 	struct elroy_softc *sc = v;
161b95ee612Skettenis 	volatile struct elroy_regs *r = sc->sc_regs;
162b95ee612Skettenis 	hppa_hpa_t hpa = cpu_gethpa(0);
163804c1233Skettenis 	struct evcount *cnt;
164b95ee612Skettenis 	struct apic_iv *aiv, *biv;
165b95ee612Skettenis 	void *iv;
16615fe2033Skettenis 	int irq = APIC_INT_IRQ(ih);
16715fe2033Skettenis 	int line = APIC_INT_LINE(ih);
16815fe2033Skettenis 	u_int32_t ent0;
169b95ee612Skettenis 
170b95ee612Skettenis 	/* no mapping or bogus */
17115fe2033Skettenis 	if (irq <= 0 || irq > 31)
172b95ee612Skettenis 		return (NULL);
173b95ee612Skettenis 
174b95ee612Skettenis 	aiv = malloc(sizeof(struct apic_iv), M_DEVBUF, M_NOWAIT);
17554b0a103Smlarkin 	if (aiv == NULL)
17654b0a103Smlarkin 		return (NULL);
177b95ee612Skettenis 
178804c1233Skettenis 	cnt = malloc(sizeof(struct evcount), M_DEVBUF, M_NOWAIT);
179804c1233Skettenis 	if (!cnt) {
180367b04a3Sderaadt 		free(aiv, M_DEVBUF, sizeof *aiv);
181804c1233Skettenis 		return (NULL);
182804c1233Skettenis 	}
183804c1233Skettenis 
184b83c2504Skettenis 	aiv->sc = sc;
185b83c2504Skettenis 	aiv->ih = ih;
186b83c2504Skettenis 	aiv->handler = handler;
187b83c2504Skettenis 	aiv->arg = arg;
188b83c2504Skettenis 	aiv->next = NULL;
189b83c2504Skettenis 	aiv->cnt = cnt;
190b83c2504Skettenis 
1914667bebbSmatthew 	evcount_attach(cnt, name, NULL);
192b83c2504Skettenis 
193b83c2504Skettenis 	if (apic_intr_list[irq]) {
19415fe2033Skettenis 		biv = apic_intr_list[irq];
195b95ee612Skettenis 		while (biv->next)
196b95ee612Skettenis 			biv = biv->next;
197b95ee612Skettenis 		biv->next = aiv;
198b95ee612Skettenis 		return (arg);
199b95ee612Skettenis 	}
200b95ee612Skettenis 
201b83c2504Skettenis 	if ((iv = cpu_intr_establish(pri, irq, apic_intr, aiv, NULL))) {
20215fe2033Skettenis 		ent0 = (31 - irq) & APIC_ENT0_VEC;
20389a56fa7Skettenis 		ent0 |= apic_get_int_ent0(sc, line);
204b95ee612Skettenis #if 0
205b95ee612Skettenis 		if (cold) {
20615fe2033Skettenis 			sc->sc_imr |= (1 << irq);
207b95ee612Skettenis 			ent0 |= APIC_ENT0_MASK;
208b95ee612Skettenis 		}
209b95ee612Skettenis #endif
21015fe2033Skettenis 		apic_write(sc->sc_regs, APIC_ENT0(line), APIC_ENT0_MASK);
21115fe2033Skettenis 		apic_write(sc->sc_regs, APIC_ENT1(line),
212b95ee612Skettenis 		    ((hpa & 0x0ff00000) >> 4) | ((hpa & 0x000ff000) << 12));
21315fe2033Skettenis 		apic_write(sc->sc_regs, APIC_ENT0(line), ent0);
214b95ee612Skettenis 
21515fe2033Skettenis 		/* Signal EOI. */
21615fe2033Skettenis 		elroy_write32(&r->apic_eoi,
21715fe2033Skettenis 		    htole32((31 - irq) & APIC_ENT0_VEC));
218b95ee612Skettenis 
21915fe2033Skettenis 		apic_intr_list[irq] = aiv;
220b95ee612Skettenis 	}
221b95ee612Skettenis 
222b95ee612Skettenis 	return (arg);
223b95ee612Skettenis }
224b95ee612Skettenis 
225b95ee612Skettenis void
apic_intr_disestablish(void * v,void * cookie)226b95ee612Skettenis apic_intr_disestablish(void *v, void *cookie)
227b95ee612Skettenis {
228b95ee612Skettenis }
229b95ee612Skettenis 
230b95ee612Skettenis int
apic_intr(void * v)231b95ee612Skettenis apic_intr(void *v)
232b95ee612Skettenis {
233b95ee612Skettenis 	struct apic_iv *iv = v;
234b95ee612Skettenis 	struct elroy_softc *sc = iv->sc;
235b95ee612Skettenis 	volatile struct elroy_regs *r = sc->sc_regs;
2360b8b2ce6Skettenis 	pci_intr_handle_t ih = iv->ih;
237b95ee612Skettenis 	int claimed = 0;
238b95ee612Skettenis 
239b95ee612Skettenis 	while (iv) {
240f84a0763Skettenis 		claimed = iv->handler(iv->arg);
241f84a0763Skettenis 		if (claimed != 0 && iv->cnt)
242804c1233Skettenis 			iv->cnt->ec_count++;
243f84a0763Skettenis 		if (claimed == 1)
244f84a0763Skettenis 			break;
245b95ee612Skettenis 		iv = iv->next;
246b95ee612Skettenis 	}
247b95ee612Skettenis 
24815fe2033Skettenis 	/* Signal EOI. */
24915fe2033Skettenis 	elroy_write32(&r->apic_eoi,
2500b8b2ce6Skettenis 	    htole32((31 - APIC_INT_IRQ(ih)) & APIC_ENT0_VEC));
251b95ee612Skettenis 
252b95ee612Skettenis 	return (claimed);
253b95ee612Skettenis }
254b95ee612Skettenis 
25589a56fa7Skettenis /* Maximum number of supported interrupt routing entries. */
256a97e6b04Skettenis #define MAX_INT_TBL_SZ	16
25789a56fa7Skettenis 
25889a56fa7Skettenis void
apic_get_int_tbl(struct elroy_softc * sc)25989a56fa7Skettenis apic_get_int_tbl(struct elroy_softc *sc)
26089a56fa7Skettenis {
26189a56fa7Skettenis 	struct pdc_pat_io_num int_tbl_sz PDC_ALIGNMENT;
26289a56fa7Skettenis 	struct pdc_pat_pci_rt int_tbl[MAX_INT_TBL_SZ] PDC_ALIGNMENT;
26389a56fa7Skettenis 	size_t size;
26489a56fa7Skettenis 
26589a56fa7Skettenis 	/*
26689a56fa7Skettenis 	 * XXX int_tbl should not be allocated on the stack, but we need a
26789a56fa7Skettenis 	 * 1:1 mapping, and malloc doesn't provide that.
26889a56fa7Skettenis 	 */
26989a56fa7Skettenis 
27089a56fa7Skettenis 	if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL_SZ,
27189a56fa7Skettenis 	    &int_tbl_sz, 0, 0, 0, 0, 0))
27289a56fa7Skettenis 		return;
27389a56fa7Skettenis 
27489a56fa7Skettenis 	if (int_tbl_sz.num > MAX_INT_TBL_SZ)
27589a56fa7Skettenis 		panic("interrupt routing table too big (%d entries)",
27689a56fa7Skettenis 		    int_tbl_sz.num);
27789a56fa7Skettenis 
27889a56fa7Skettenis 	size = int_tbl_sz.num * sizeof(struct pdc_pat_pci_rt);
27989a56fa7Skettenis 	sc->sc_int_tbl_sz = int_tbl_sz.num;
28089a56fa7Skettenis 	sc->sc_int_tbl = malloc(size, M_DEVBUF, M_NOWAIT);
28189a56fa7Skettenis 	if (sc->sc_int_tbl == NULL)
28289a56fa7Skettenis 		return;
28389a56fa7Skettenis 
28489a56fa7Skettenis 	if (pdc_call((iodcio_t)pdc, 0, PDC_PCI_INDEX, PDC_PCI_GET_INT_TBL,
28589a56fa7Skettenis 	    &int_tbl_sz, 0, &int_tbl, 0, 0, 0))
28689a56fa7Skettenis 		return;
28789a56fa7Skettenis 
28889a56fa7Skettenis 	memcpy(sc->sc_int_tbl, int_tbl, size);
28989a56fa7Skettenis }
29089a56fa7Skettenis 
29189a56fa7Skettenis u_int32_t
apic_get_int_ent0(struct elroy_softc * sc,int line)29289a56fa7Skettenis apic_get_int_ent0(struct elroy_softc *sc, int line)
29389a56fa7Skettenis {
2941a79cf7eSkettenis 	volatile struct elroy_regs *r = sc->sc_regs;
29589a56fa7Skettenis 	int trigger = MPS_INT(MPS_INTPO_DEF, MPS_INTTR_DEF);
29689a56fa7Skettenis 	u_int32_t ent0 = APIC_ENT0_LOW | APIC_ENT0_LEV;
2971a79cf7eSkettenis 	int bus, mpspo, mpstr;
29889a56fa7Skettenis 	int i;
29989a56fa7Skettenis 
3001a79cf7eSkettenis 	bus = letoh32(elroy_read32(&r->busnum)) & 0xff;
30189a56fa7Skettenis 	for (i = 0; i < sc->sc_int_tbl_sz; i++) {
3021a79cf7eSkettenis 		if (bus == sc->sc_int_tbl[i].bus &&
3031a79cf7eSkettenis 		    line == sc->sc_int_tbl[i].line)
30489a56fa7Skettenis 			trigger = sc->sc_int_tbl[i].trigger;
30589a56fa7Skettenis 	}
30689a56fa7Skettenis 
30789a56fa7Skettenis 	mpspo = (trigger >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK;
30889a56fa7Skettenis 	mpstr = (trigger >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK;
30989a56fa7Skettenis 
31089a56fa7Skettenis 	switch (mpspo) {
31189a56fa7Skettenis 	case MPS_INTPO_DEF:
31289a56fa7Skettenis 		break;
31389a56fa7Skettenis 	case MPS_INTPO_ACTHI:
31489a56fa7Skettenis 		ent0 &= ~APIC_ENT0_LOW;
31589a56fa7Skettenis 		break;
31689a56fa7Skettenis 	case MPS_INTPO_ACTLO:
31789a56fa7Skettenis 		ent0 |= APIC_ENT0_LOW;
31889a56fa7Skettenis 		break;
31989a56fa7Skettenis 	default:
32089a56fa7Skettenis 		panic("unknown MPS interrupt polarity %d", mpspo);
32189a56fa7Skettenis 	}
32289a56fa7Skettenis 
32389a56fa7Skettenis 	switch(mpstr) {
32489a56fa7Skettenis 	case MPS_INTTR_DEF:
32589a56fa7Skettenis 		break;
32689a56fa7Skettenis 	case MPS_INTTR_LEVEL:
32789a56fa7Skettenis 		ent0 |= APIC_ENT0_LEV;
32889a56fa7Skettenis 		break;
32989a56fa7Skettenis 	case MPS_INTTR_EDGE:
33089a56fa7Skettenis 		ent0 &= ~APIC_ENT0_LEV;
33189a56fa7Skettenis 		break;
33289a56fa7Skettenis 	default:
33389a56fa7Skettenis 		panic("unknown MPS interrupt trigger %d", mpstr);
33489a56fa7Skettenis 	}
33589a56fa7Skettenis 
33689a56fa7Skettenis 	return ent0;
33789a56fa7Skettenis }
33889a56fa7Skettenis 
339b95ee612Skettenis #ifdef DEBUG
340b95ee612Skettenis void
apic_dump(struct elroy_softc * sc)341b95ee612Skettenis apic_dump(struct elroy_softc *sc)
342b95ee612Skettenis {
343b95ee612Skettenis 	int i;
344b95ee612Skettenis 
345b95ee612Skettenis 	for (i = 0; i < sc->sc_nints; i++)
346b95ee612Skettenis 		printf("0x%04x 0x%04x\n", apic_read(sc->sc_regs, APIC_ENT0(i)),
347b95ee612Skettenis 		    apic_read(sc->sc_regs, APIC_ENT1(i)));
348b95ee612Skettenis 
3491a79cf7eSkettenis 	for (i = 0; i < sc->sc_int_tbl_sz; i++) {
3501a79cf7eSkettenis 		printf("type=%x ", sc->sc_int_tbl[i].type);
3511a79cf7eSkettenis 		printf("len=%d ", sc->sc_int_tbl[i].len);
3521a79cf7eSkettenis 		printf("itype=%d ", sc->sc_int_tbl[i].itype);
3531a79cf7eSkettenis 		printf("trigger=%x ", sc->sc_int_tbl[i].trigger);
3541a79cf7eSkettenis 		printf("pin=%x ", sc->sc_int_tbl[i].pin);
3551a79cf7eSkettenis 		printf("bus=%d ", sc->sc_int_tbl[i].bus);
3561a79cf7eSkettenis 		printf("line=%d ", sc->sc_int_tbl[i].line);
3571a79cf7eSkettenis 		printf("addr=%x\n", sc->sc_int_tbl[i].addr);
358b95ee612Skettenis 	}
359b95ee612Skettenis }
360b95ee612Skettenis #endif
361