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