1 /* $NetBSD: apic.c,v 1.5 2009/12/03 11:54:09 skrll Exp $ */
3 /* $OpenBSD: apic.c,v 1.7 2007/10/06 23:50:54 krw Exp $ */
6 * Copyright (c) 2005 Michael Shalayeff
7 * Copyright (c) 2007 Mark Kettenis
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.
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. */
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/device.h>
25 #include <sys/malloc.h>
27 #include <machine/autoconf.h>
28 #include <machine/pdc.h>
29 #include <machine/intr.h>
31 #include <hp700/hp700/intr.h>
33 #include <dev/pci/pcireg.h>
34 #include <dev/pci/pcivar.h>
35 #include <dev/pci/pcidevs.h>
37 #include <hp700/dev/elroyreg.h>
38 #include <hp700/dev/elroyvar.h>
40 #define APIC_INT_LINE_MASK 0x0000ff00
41 #define APIC_INT_LINE_SHIFT 8
42 #define APIC_INT_IRQ_MASK 0x0000001f
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)
48 * Interrupt types match the Intel MP Specification.
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
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
63 #define MPS_INT(p,t) \
64 ((((p) & MPS_INTPO_MASK) << MPS_INTPO_SHIFT) | \
65 (((t) & MPS_INTTR_MASK) << MPS_INTTR_SHIFT))
68 struct elroy_softc
*sc
;
70 int (*handler
)(void *);
76 struct apic_iv
*apic_intr_list
[CPU_NINTS
];
78 void apic_write(volatile struct elroy_regs
*, uint32_t, uint32_t);
79 uint32_t apic_read(volatile struct elroy_regs
*, uint32_t reg
);
81 void apic_get_int_tbl(struct elroy_softc
*);
82 uint32_t apic_get_int_ent0(struct elroy_softc
*, int);
84 void apic_dump(struct elroy_softc
*);
88 apic_write(volatile struct elroy_regs
*r
, uint32_t reg
, uint32_t val
)
90 elroy_write32(&r
->apic_addr
, htole32(reg
));
91 elroy_write32(&r
->apic_data
, htole32(val
));
92 elroy_read32(&r
->apic_data
);
96 apic_read(volatile struct elroy_regs
*r
, uint32_t reg
)
98 elroy_write32(&r
->apic_addr
, htole32(reg
));
99 return le32toh(elroy_read32(&r
->apic_data
));
103 apic_attach(struct elroy_softc
*sc
)
105 volatile struct elroy_regs
*r
= sc
->sc_regs
;
108 data
= apic_read(r
, APIC_VERSION
);
109 sc
->sc_nints
= (data
& APIC_VERSION_NENT
) >> APIC_VERSION_NENT_SHIFT
;
110 aprint_normal(" APIC ver %x, %d pins",
111 data
& APIC_VERSION_MASK
, sc
->sc_nints
);
113 sc
->sc_irq
= malloc(sc
->sc_nints
* sizeof(int), M_DEVBUF
,
115 if (sc
->sc_irq
== NULL
)
116 panic("apic_attach: can't allocate irq table\n");
118 apic_get_int_tbl(sc
);
126 apic_intr_map(struct pci_attach_args
*pa
, pci_intr_handle_t
*ihp
)
128 struct elroy_softc
*sc
= pa
->pa_pc
->_cookie
;
129 pci_chipset_tag_t pc
= pa
->pa_pc
;
130 pcitag_t tag
= pa
->pa_tag
;
134 reg
= pci_conf_read(pc
, tag
, PCI_INTERRUPT_REG
);
136 printf(" pin=%d line=%d ", PCI_INTERRUPT_PIN(reg
),
137 PCI_INTERRUPT_LINE(reg
));
139 line
= PCI_INTERRUPT_LINE(reg
);
140 if (sc
->sc_irq
[line
] == 0)
141 sc
->sc_irq
[line
] = hp700_intr_allocate_bit(&int_reg_cpu
);
142 *ihp
= (line
<< APIC_INT_LINE_SHIFT
) | sc
->sc_irq
[line
];
143 return (APIC_INT_IRQ(*ihp
) == 0);
147 apic_intr_string(void *v
, pci_intr_handle_t ih
)
151 snprintf(buf
, sizeof(buf
), "line %ld irq %ld",
152 APIC_INT_LINE(ih
), APIC_INT_IRQ(ih
));
158 apic_intr_establish(void *v
, pci_intr_handle_t ih
,
159 int pri
, int (*handler
)(void *), void *arg
)
161 struct elroy_softc
*sc
= v
;
162 volatile struct elroy_regs
*r
= sc
->sc_regs
;
163 hppa_hpa_t hpa
= cpu_gethpa(0);
165 struct apic_iv
*aiv
, *biv
;
167 int irq
= APIC_INT_IRQ(ih
);
168 int line
= APIC_INT_LINE(ih
);
171 /* no mapping or bogus */
172 if (irq
<= 0 || irq
> 31)
175 aiv
= malloc(sizeof(struct apic_iv
), M_DEVBUF
, M_NOWAIT
);
181 aiv
->handler
= handler
;
185 if (apic_intr_list
[irq
]) {
186 cnt
= malloc(sizeof(struct evcnt
), M_DEVBUF
, M_NOWAIT
);
192 evcnt_attach_dynamic(cnt
, EVCNT_TYPE_INTR
, NULL
,
193 device_xname(sc
->sc_dv
), "irq" /* XXXNH */);
194 biv
= apic_intr_list
[irq
];
202 if ((iv
= hp700_intr_establish(sc
->sc_dv
, pri
, apic_intr
,
203 aiv
, &int_reg_cpu
, irq
))) {
204 ent0
= (31 - irq
) & APIC_ENT0_VEC
;
205 ent0
|= apic_get_int_ent0(sc
, line
);
208 sc
->sc_imr
|= (1 << irq
);
209 ent0
|= APIC_ENT0_MASK
;
212 apic_write(sc
->sc_regs
, APIC_ENT0(line
), APIC_ENT0_MASK
);
213 apic_write(sc
->sc_regs
, APIC_ENT1(line
),
214 ((hpa
& 0x0ff00000) >> 4) | ((hpa
& 0x000ff000) << 12));
215 apic_write(sc
->sc_regs
, APIC_ENT0(line
), ent0
);
218 elroy_write32(&r
->apic_eoi
,
219 htole32((31 - irq
) & APIC_ENT0_VEC
));
221 apic_intr_list
[irq
] = aiv
;
228 apic_intr_disestablish(void *v
, void *cookie
)
235 struct apic_iv
*iv
= v
;
236 struct elroy_softc
*sc
= iv
->sc
;
237 volatile struct elroy_regs
*r
= sc
->sc_regs
;
241 if (iv
->handler(iv
->arg
)) {
245 elroy_write32(&r
->apic_eoi
,
246 htole32((31 - APIC_INT_IRQ(iv
->ih
)) & APIC_ENT0_VEC
));
255 /* Maximum number of supported interrupt routing entries. */
256 #define MAX_INT_TBL_SZ 16
259 apic_get_int_tbl(struct elroy_softc
*sc
)
261 static struct pdc_pat_io_num int_tbl_sz PDC_ALIGNMENT
;
262 static struct pdc_pat_pci_rt int_tbl
[MAX_INT_TBL_SZ
] PDC_ALIGNMENT
;
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))
269 if (int_tbl_sz
.num
> MAX_INT_TBL_SZ
)
270 panic("interrupt routing table too big (%d entries)",
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
)
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))
283 memcpy(sc
->sc_int_tbl
, int_tbl
, size
);
287 apic_get_int_ent0(struct elroy_softc
*sc
, int line
)
289 volatile struct elroy_regs
*r
= sc
->sc_regs
;
290 int trigger
= MPS_INT(MPS_INTPO_DEF
, MPS_INTTR_DEF
);
291 uint32_t ent0
= APIC_ENT0_LOW
| APIC_ENT0_LEV
;
292 int bus
, mpspo
, mpstr
;
295 bus
= le32toh(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
;
302 mpspo
= (trigger
>> MPS_INTPO_SHIFT
) & MPS_INTPO_MASK
;
303 mpstr
= (trigger
>> MPS_INTTR_SHIFT
) & MPS_INTTR_MASK
;
308 case MPS_INTPO_ACTHI
:
309 ent0
&= ~APIC_ENT0_LOW
;
311 case MPS_INTPO_ACTLO
:
312 ent0
|= APIC_ENT0_LOW
;
315 panic("unknown MPS interrupt polarity %d", mpspo
);
321 case MPS_INTTR_LEVEL
:
322 ent0
|= APIC_ENT0_LEV
;
325 ent0
&= ~APIC_ENT0_LEV
;
328 panic("unknown MPS interrupt trigger %d", mpstr
);
336 apic_dump(struct elroy_softc
*sc
)
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
)));
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=%llx\n", sc
->sc_int_tbl
[i
].addr
);