184543Sdfr/*-
284543Sdfr * Copyright (c) 2001 Doug Rabson
384543Sdfr * All rights reserved.
484543Sdfr *
584543Sdfr * Redistribution and use in source and binary forms, with or without
684543Sdfr * modification, are permitted provided that the following conditions
784543Sdfr * are met:
884543Sdfr * 1. Redistributions of source code must retain the above copyright
984543Sdfr *    notice, this list of conditions and the following disclaimer.
1084543Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1184543Sdfr *    notice, this list of conditions and the following disclaimer in the
1284543Sdfr *    documentation and/or other materials provided with the distribution.
1384543Sdfr *
1484543Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1584543Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1684543Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1784543Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1884543Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1984543Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2084543Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2184543Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2284543Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2384543Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2484543Sdfr * SUCH DAMAGE.
2584543Sdfr *
2684543Sdfr * $FreeBSD$
2784543Sdfr */
2884543Sdfr
2992267Sdfr#include "opt_ddb.h"
3092267Sdfr
3184543Sdfr#include <sys/param.h>
3284543Sdfr#include <sys/malloc.h>
3384543Sdfr#include <sys/kernel.h>
3484543Sdfr#include <sys/systm.h>
3592668Speter#include <sys/bus.h>
36171664Smarcel#include <sys/lock.h>
37171664Smarcel#include <sys/mutex.h>
38205726Smarcel#include <sys/pcpu.h>
39119970Smarcel#include <sys/sysctl.h>
40119970Smarcel
4192668Speter#include <machine/intr.h>
42108737Smarcel#include <machine/pal.h>
4384543Sdfr
44203883Smarcel#include <vm/vm.h>
45203883Smarcel#include <vm/pmap.h>
46203883Smarcel
47204425Smarcel/*
48204425Smarcel * Offsets from the SAPIC base in memory. Most registers are accessed
49204425Smarcel * by indexing using the SAPIC_IO_SELECT register.
50204425Smarcel */
51204425Smarcel#define	SAPIC_IO_SELECT		0x00
52204425Smarcel#define	SAPIC_IO_WINDOW		0x10
53204425Smarcel#define	SAPIC_APIC_EOI		0x40
5484543Sdfr
55204425Smarcel/*
56204425Smarcel * Indexed registers.
57204425Smarcel */
58204425Smarcel#define SAPIC_ID		0x00
59204425Smarcel#define SAPIC_VERSION		0x01
60204425Smarcel#define SAPIC_ARBITRATION_ID	0x02
61204425Smarcel#define SAPIC_RTE_BASE		0x10
62108759Smarcel
63204425Smarcel/* Interrupt polarity. */
64204425Smarcel#define	SAPIC_POLARITY_HIGH	0
65204425Smarcel#define	SAPIC_POLARITY_LOW	1
66108759Smarcel
67204425Smarcel/* Interrupt trigger. */
68204425Smarcel#define	SAPIC_TRIGGER_EDGE	0
69204425Smarcel#define	SAPIC_TRIGGER_LEVEL	1
70108759Smarcel
71204425Smarcel/* Interrupt delivery mode. */
72204425Smarcel#define	SAPIC_DELMODE_FIXED	0
73204425Smarcel#define	SAPIC_DELMODE_LOWPRI	1
74204425Smarcel#define	SAPIC_DELMODE_PMI	2
75204425Smarcel#define	SAPIC_DELMODE_NMI	4
76204425Smarcel#define	SAPIC_DELMODE_INIT	5
77204425Smarcel#define	SAPIC_DELMODE_EXTINT	7
78204425Smarcel
79204425Smarcelstruct sapic {
80204425Smarcel	struct mtx	sa_mtx;
81204425Smarcel	uint64_t	sa_registers;	/* virtual address of sapic */
82204425Smarcel	u_int		sa_id;		/* I/O SAPIC Id */
83204425Smarcel	u_int		sa_base;	/* ACPI vector base */
84204425Smarcel	u_int		sa_limit;	/* last ACPI vector handled here */
85204425Smarcel};
86204425Smarcel
8784543Sdfrstruct sapic_rte {
88204425Smarcel	uint64_t	rte_vector		:8;
89204425Smarcel	uint64_t	rte_delivery_mode	:3;
90204425Smarcel	uint64_t	rte_destination_mode	:1;
91204425Smarcel	uint64_t	rte_delivery_status	:1;
92204425Smarcel	uint64_t	rte_polarity		:1;
93204425Smarcel	uint64_t	rte_rirr		:1;
94204425Smarcel	uint64_t	rte_trigger_mode	:1;
95204425Smarcel	uint64_t	rte_mask		:1;
96204425Smarcel	uint64_t	rte_flushen		:1;
97204425Smarcel	uint64_t	rte_reserved		:30;
98204425Smarcel	uint64_t	rte_destination_eid	:8;
99204425Smarcel	uint64_t	rte_destination_id	:8;
10084543Sdfr};
10184543Sdfr
102249132Smavstatic MALLOC_DEFINE(M_SAPIC, "sapic", "I/O SAPIC devices");
103171664Smarcel
104204425Smarcelstruct sapic *ia64_sapics[16];		/* XXX make this resizable */
105204425Smarcelint ia64_sapic_count;
106171664Smarcel
107204425Smarcelstatic int sysctl_machdep_apic(SYSCTL_HANDLER_ARGS);
108171664Smarcel
109204425SmarcelSYSCTL_OID(_machdep, OID_AUTO, apic, CTLTYPE_STRING|CTLFLAG_RD,
110204425Smarcel    NULL, 0, sysctl_machdep_apic, "A", "(x)APIC redirection table entries");
111204425Smarcel
112204425Smarcelstatic __inline uint32_t
11384543Sdfrsapic_read(struct sapic *sa, int which)
11484543Sdfr{
115204425Smarcel	uint32_t value;
11684543Sdfr
117204425Smarcel	ia64_st4((void *)(sa->sa_registers + SAPIC_IO_SELECT), which);
118204425Smarcel	ia64_mf_a();
119204425Smarcel	value = ia64_ld4((void *)(sa->sa_registers + SAPIC_IO_WINDOW));
120204425Smarcel	return (value);
12184543Sdfr}
12284543Sdfr
123171664Smarcelstatic __inline void
124204425Smarcelsapic_write(struct sapic *sa, int which, uint32_t value)
12584543Sdfr{
12684543Sdfr
127204425Smarcel	ia64_st4((void *)(sa->sa_registers + SAPIC_IO_SELECT), which);
128204425Smarcel	ia64_mf_a();
129204425Smarcel	ia64_st4((void *)(sa->sa_registers + SAPIC_IO_WINDOW), value);
130204425Smarcel	ia64_mf_a();
13184543Sdfr}
13284543Sdfr
133171664Smarcelstatic __inline void
134108759Smarcelsapic_read_rte(struct sapic *sa, int which, struct sapic_rte *rte)
13584543Sdfr{
136204425Smarcel	uint32_t *p = (uint32_t *) rte;
13792782Sdfr
138171664Smarcel	p[0] = sapic_read(sa, SAPIC_RTE_BASE + 2 * which);
139171664Smarcel	p[1] = sapic_read(sa, SAPIC_RTE_BASE + 2 * which + 1);
14084543Sdfr}
14184543Sdfr
142171664Smarcelstatic __inline void
143119970Smarcelsapic_write_rte(struct sapic *sa, int which, struct sapic_rte *rte)
14484543Sdfr{
145204425Smarcel	uint32_t *p = (uint32_t *) rte;
14692782Sdfr
147204425Smarcel	sapic_write(sa, SAPIC_RTE_BASE + 2 * which, p[0]);
148204425Smarcel	sapic_write(sa, SAPIC_RTE_BASE + 2 * which + 1, p[1]);
14984543Sdfr}
15084543Sdfr
151204425Smarcelstruct sapic *
152204425Smarcelsapic_lookup(u_int irq, u_int *vecp)
153204425Smarcel{
154204425Smarcel	struct sapic_rte rte;
155204425Smarcel	struct sapic *sa;
156204425Smarcel	int i;
157204425Smarcel
158204425Smarcel	for (i = 0; i < ia64_sapic_count; i++) {
159204425Smarcel		sa = ia64_sapics[i];
160204425Smarcel		if (irq >= sa->sa_base && irq <= sa->sa_limit) {
161204425Smarcel			if (vecp != NULL) {
162204425Smarcel				mtx_lock_spin(&sa->sa_mtx);
163204425Smarcel				sapic_read_rte(sa, irq - sa->sa_base, &rte);
164204425Smarcel				mtx_unlock_spin(&sa->sa_mtx);
165204425Smarcel				*vecp = rte.rte_vector;
166204425Smarcel			}
167204425Smarcel			return (sa);
168204425Smarcel		}
169204425Smarcel	}
170204425Smarcel
171204425Smarcel	return (NULL);
172204425Smarcel}
173204425Smarcel
174204425Smarcel
175119970Smarcelint
176205726Smarcelsapic_bind_intr(u_int irq, struct pcpu *pc)
177205726Smarcel{
178205726Smarcel	struct sapic_rte rte;
179205726Smarcel	struct sapic *sa;
180205726Smarcel
181205726Smarcel	sa = sapic_lookup(irq, NULL);
182205726Smarcel	if (sa == NULL)
183205726Smarcel		return (EINVAL);
184205726Smarcel
185205726Smarcel	mtx_lock_spin(&sa->sa_mtx);
186205726Smarcel	sapic_read_rte(sa, irq - sa->sa_base, &rte);
187205726Smarcel	rte.rte_destination_id = (pc->pc_md.lid >> 24) & 255;
188205726Smarcel	rte.rte_destination_eid = (pc->pc_md.lid >> 16) & 255;
189205726Smarcel	rte.rte_delivery_mode = SAPIC_DELMODE_FIXED;
190205726Smarcel	sapic_write_rte(sa, irq - sa->sa_base, &rte);
191205726Smarcel	mtx_unlock_spin(&sa->sa_mtx);
192205726Smarcel	return (0);
193205726Smarcel}
194205726Smarcel
195205726Smarcelint
196171664Smarcelsapic_config_intr(u_int irq, enum intr_trigger trig, enum intr_polarity pol)
197119970Smarcel{
198119970Smarcel	struct sapic_rte rte;
199119970Smarcel	struct sapic *sa;
200119970Smarcel
201204425Smarcel	sa = sapic_lookup(irq, NULL);
202171664Smarcel	if (sa == NULL)
203171664Smarcel		return (EINVAL);
204119970Smarcel
205171664Smarcel	mtx_lock_spin(&sa->sa_mtx);
206171664Smarcel	sapic_read_rte(sa, irq - sa->sa_base, &rte);
207171664Smarcel	if (trig != INTR_TRIGGER_CONFORM)
208171664Smarcel		rte.rte_trigger_mode = (trig == INTR_TRIGGER_EDGE) ?
209171664Smarcel		    SAPIC_TRIGGER_EDGE : SAPIC_TRIGGER_LEVEL;
210171664Smarcel	else
211171664Smarcel		rte.rte_trigger_mode = (irq < 16) ? SAPIC_TRIGGER_EDGE :
212171664Smarcel		    SAPIC_TRIGGER_LEVEL;
213171664Smarcel	if (pol != INTR_POLARITY_CONFORM)
214171664Smarcel		rte.rte_polarity = (pol == INTR_POLARITY_HIGH) ?
215171664Smarcel		    SAPIC_POLARITY_HIGH : SAPIC_POLARITY_LOW;
216171664Smarcel	else
217171664Smarcel		rte.rte_polarity = (irq < 16) ? SAPIC_POLARITY_HIGH :
218171664Smarcel		    SAPIC_POLARITY_LOW;
219171664Smarcel	sapic_write_rte(sa, irq - sa->sa_base, &rte);
220171664Smarcel	mtx_unlock_spin(&sa->sa_mtx);
221171664Smarcel	return (0);
222119970Smarcel}
223119970Smarcel
22484543Sdfrstruct sapic *
225204425Smarcelsapic_create(u_int id, u_int base, uint64_t address)
22684543Sdfr{
227119970Smarcel	struct sapic_rte rte;
22884543Sdfr	struct sapic *sa;
229171664Smarcel	u_int i, max;
23084543Sdfr
231171664Smarcel	sa = malloc(sizeof(struct sapic), M_SAPIC, M_ZERO | M_NOWAIT);
232171664Smarcel	if (sa == NULL)
233171664Smarcel		return (NULL);
23484543Sdfr
23584543Sdfr	sa->sa_id = id;
23684543Sdfr	sa->sa_base = base;
237203883Smarcel	sa->sa_registers = (uintptr_t)pmap_mapdev(address, 1048576);
23884543Sdfr
239171664Smarcel	mtx_init(&sa->sa_mtx, "I/O SAPIC lock", NULL, MTX_SPIN);
240171664Smarcel
24184543Sdfr	max = (sapic_read(sa, SAPIC_VERSION) >> 16) & 0xff;
24284543Sdfr	sa->sa_limit = base + max;
24384543Sdfr
244108759Smarcel	ia64_sapics[ia64_sapic_count++] = sa;
24584543Sdfr
246119970Smarcel	/*
247119970Smarcel	 * Initialize all RTEs with a default trigger mode and polarity.
248119970Smarcel	 * This may be changed later by calling sapic_config_intr(). We
249119970Smarcel	 * mask all interrupts by default.
250119970Smarcel	 */
251119970Smarcel	bzero(&rte, sizeof(rte));
252119970Smarcel	rte.rte_mask = 1;
253119970Smarcel	for (i = base; i <= sa->sa_limit; i++) {
254119970Smarcel		rte.rte_trigger_mode = (i < 16) ? SAPIC_TRIGGER_EDGE :
255119970Smarcel		    SAPIC_TRIGGER_LEVEL;
256119970Smarcel		rte.rte_polarity = (i < 16) ? SAPIC_POLARITY_HIGH :
257119970Smarcel		    SAPIC_POLARITY_LOW;
258119970Smarcel		sapic_write_rte(sa, i - base, &rte);
259119970Smarcel	}
260119970Smarcel
261119970Smarcel	return (sa);
26284543Sdfr}
26384543Sdfr
264119970Smarcelint
265171664Smarcelsapic_enable(struct sapic *sa, u_int irq, u_int vector)
26684543Sdfr{
26784543Sdfr	struct sapic_rte rte;
268119970Smarcel	uint64_t lid = ia64_get_lid();
26984543Sdfr
270171664Smarcel	mtx_lock_spin(&sa->sa_mtx);
271171664Smarcel	sapic_read_rte(sa, irq - sa->sa_base, &rte);
272171664Smarcel	rte.rte_destination_id = (lid >> 24) & 255;
273171664Smarcel	rte.rte_destination_eid = (lid >> 16) & 255;
274178206Smarcel	rte.rte_delivery_mode = SAPIC_DELMODE_FIXED;
275171664Smarcel	rte.rte_vector = vector;
276171664Smarcel	rte.rte_mask = 0;
277171664Smarcel	sapic_write_rte(sa, irq - sa->sa_base, &rte);
278171664Smarcel	mtx_unlock_spin(&sa->sa_mtx);
279171664Smarcel	return (0);
28084543Sdfr}
28184543Sdfr
28284543Sdfrvoid
283171664Smarcelsapic_eoi(struct sapic *sa, u_int vector)
28484543Sdfr{
28584543Sdfr
286204425Smarcel	ia64_st4((void *)(sa->sa_registers + SAPIC_APIC_EOI), vector);
287204425Smarcel	ia64_mf_a();
28884543Sdfr}
28984543Sdfr
290171664Smarcel/* Expected to be called with interrupts disabled. */
291171664Smarcelvoid
292171664Smarcelsapic_mask(struct sapic *sa, u_int irq)
293171664Smarcel{
294171664Smarcel	struct sapic_rte rte;
295171664Smarcel
296171664Smarcel	mtx_lock_spin(&sa->sa_mtx);
297171664Smarcel	sapic_read_rte(sa, irq - sa->sa_base, &rte);
298171664Smarcel	rte.rte_mask = 1;
299171664Smarcel	sapic_write_rte(sa, irq - sa->sa_base, &rte);
300171664Smarcel	mtx_unlock_spin(&sa->sa_mtx);
301171664Smarcel}
302171664Smarcel
303171664Smarcel/* Expected to be called with interrupts disabled. */
304171664Smarcelvoid
305171664Smarcelsapic_unmask(struct sapic *sa, u_int irq)
306171664Smarcel{
307171664Smarcel	struct sapic_rte rte;
308171664Smarcel
309171664Smarcel	mtx_lock_spin(&sa->sa_mtx);
310171664Smarcel	sapic_read_rte(sa, irq - sa->sa_base, &rte);
311171664Smarcel	rte.rte_mask = 0;
312171664Smarcel	sapic_write_rte(sa, irq - sa->sa_base, &rte);
313171664Smarcel	mtx_unlock_spin(&sa->sa_mtx);
314171664Smarcel}
315171664Smarcel
316108759Smarcelstatic int
317108759Smarcelsysctl_machdep_apic(SYSCTL_HANDLER_ARGS)
318108759Smarcel{
319108759Smarcel	char buf[80];
320108759Smarcel	struct sapic_rte rte;
321108759Smarcel	struct sapic *sa;
322108759Smarcel	int apic, count, error, index, len;
323108759Smarcel
324108759Smarcel	len = sprintf(buf, "\n    APIC Idx: Id,EId : RTE\n");
325108759Smarcel	error = SYSCTL_OUT(req, buf, len);
326108759Smarcel	if (error)
327108759Smarcel		return (error);
328108759Smarcel
329108759Smarcel	for (apic = 0; apic < ia64_sapic_count; apic++) {
330108759Smarcel		sa = ia64_sapics[apic];
331108759Smarcel		count = sa->sa_limit - sa->sa_base + 1;
332108759Smarcel		for (index = 0; index < count; index++) {
333171664Smarcel			mtx_lock_spin(&sa->sa_mtx);
334108759Smarcel			sapic_read_rte(sa, index, &rte);
335171664Smarcel			mtx_unlock_spin(&sa->sa_mtx);
336171664Smarcel			if (rte.rte_vector == 0)
337108759Smarcel				continue;
338108759Smarcel			len = sprintf(buf,
339108759Smarcel    "    0x%02x %3d: (%02x,%02x): %3d %d %d %s %s %s %s %s\n",
340108759Smarcel			    sa->sa_id, index,
341108759Smarcel			    rte.rte_destination_id, rte.rte_destination_eid,
342108759Smarcel			    rte.rte_vector, rte.rte_delivery_mode,
343108759Smarcel			    rte.rte_destination_mode,
344108759Smarcel			    rte.rte_delivery_status ? "DS" : "  ",
345108759Smarcel			    rte.rte_polarity ? "low-active " : "high-active",
346108759Smarcel			    rte.rte_rirr ? "RIRR" : "    ",
347108759Smarcel			    rte.rte_trigger_mode ? "level" : "edge ",
348108759Smarcel			    rte.rte_flushen ? "F" : " ");
349108759Smarcel			error = SYSCTL_OUT(req, buf, len);
350108759Smarcel			if (error)
351108759Smarcel				return (error);
352108759Smarcel		}
353108759Smarcel	}
354108759Smarcel
355108759Smarcel	return (0);
356108759Smarcel}
357108759Smarcel
35892267Sdfr#ifdef DDB
35992267Sdfr
36092267Sdfr#include <ddb/ddb.h>
36192267Sdfr
36292267Sdfrvoid
363171664Smarcelsapic_print(struct sapic *sa, u_int irq)
36492267Sdfr{
36592267Sdfr	struct sapic_rte rte;
36692267Sdfr
367171664Smarcel	db_printf("sapic=%u, irq=%u: ", sa->sa_id, irq);
368171664Smarcel	sapic_read_rte(sa, irq - sa->sa_base, &rte);
369171664Smarcel	db_printf("%3d %x->%x:%x %d %s %s %s %s %s %s\n", rte.rte_vector,
370171664Smarcel	    rte.rte_delivery_mode,
371171664Smarcel	    rte.rte_destination_id, rte.rte_destination_eid,
372171664Smarcel	    rte.rte_destination_mode,
373171664Smarcel	    rte.rte_delivery_status ? "DS" : "  ",
374171664Smarcel	    rte.rte_polarity ? "low-active " : "high-active",
375171664Smarcel	    rte.rte_rirr ? "RIRR" : "    ",
376171664Smarcel	    rte.rte_trigger_mode ? "level" : "edge ",
377171664Smarcel	    rte.rte_flushen ? "F" : " ",
378171664Smarcel	    rte.rte_mask ? "(masked)" : "");
37992267Sdfr}
38092267Sdfr
38192267Sdfr#endif
382