1/*-
2 * Copyright (c) 2001 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: releng/10.3/sys/ia64/ia64/sapic.c 227293 2011-11-07 06:44:47Z ed $
27 */
28
29#include "opt_ddb.h"
30
31#include <sys/param.h>
32#include <sys/malloc.h>
33#include <sys/kernel.h>
34#include <sys/systm.h>
35#include <sys/bus.h>
36#include <sys/lock.h>
37#include <sys/mutex.h>
38#include <sys/pcpu.h>
39#include <sys/sysctl.h>
40
41#include <machine/intr.h>
42#include <machine/pal.h>
43
44#include <vm/vm.h>
45#include <vm/pmap.h>
46
47/*
48 * Offsets from the SAPIC base in memory. Most registers are accessed
49 * by indexing using the SAPIC_IO_SELECT register.
50 */
51#define	SAPIC_IO_SELECT		0x00
52#define	SAPIC_IO_WINDOW		0x10
53#define	SAPIC_APIC_EOI		0x40
54
55/*
56 * Indexed registers.
57 */
58#define SAPIC_ID		0x00
59#define SAPIC_VERSION		0x01
60#define SAPIC_ARBITRATION_ID	0x02
61#define SAPIC_RTE_BASE		0x10
62
63/* Interrupt polarity. */
64#define	SAPIC_POLARITY_HIGH	0
65#define	SAPIC_POLARITY_LOW	1
66
67/* Interrupt trigger. */
68#define	SAPIC_TRIGGER_EDGE	0
69#define	SAPIC_TRIGGER_LEVEL	1
70
71/* Interrupt delivery mode. */
72#define	SAPIC_DELMODE_FIXED	0
73#define	SAPIC_DELMODE_LOWPRI	1
74#define	SAPIC_DELMODE_PMI	2
75#define	SAPIC_DELMODE_NMI	4
76#define	SAPIC_DELMODE_INIT	5
77#define	SAPIC_DELMODE_EXTINT	7
78
79struct sapic {
80	struct mtx	sa_mtx;
81	uint64_t	sa_registers;	/* virtual address of sapic */
82	u_int		sa_id;		/* I/O SAPIC Id */
83	u_int		sa_base;	/* ACPI vector base */
84	u_int		sa_limit;	/* last ACPI vector handled here */
85};
86
87struct sapic_rte {
88	uint64_t	rte_vector		:8;
89	uint64_t	rte_delivery_mode	:3;
90	uint64_t	rte_destination_mode	:1;
91	uint64_t	rte_delivery_status	:1;
92	uint64_t	rte_polarity		:1;
93	uint64_t	rte_rirr		:1;
94	uint64_t	rte_trigger_mode	:1;
95	uint64_t	rte_mask		:1;
96	uint64_t	rte_flushen		:1;
97	uint64_t	rte_reserved		:30;
98	uint64_t	rte_destination_eid	:8;
99	uint64_t	rte_destination_id	:8;
100};
101
102static MALLOC_DEFINE(M_SAPIC, "sapic", "I/O SAPIC devices");
103
104struct sapic *ia64_sapics[16];		/* XXX make this resizable */
105int ia64_sapic_count;
106
107static int sysctl_machdep_apic(SYSCTL_HANDLER_ARGS);
108
109SYSCTL_OID(_machdep, OID_AUTO, apic, CTLTYPE_STRING|CTLFLAG_RD,
110    NULL, 0, sysctl_machdep_apic, "A", "(x)APIC redirection table entries");
111
112static __inline uint32_t
113sapic_read(struct sapic *sa, int which)
114{
115	uint32_t value;
116
117	ia64_st4((void *)(sa->sa_registers + SAPIC_IO_SELECT), which);
118	ia64_mf_a();
119	value = ia64_ld4((void *)(sa->sa_registers + SAPIC_IO_WINDOW));
120	return (value);
121}
122
123static __inline void
124sapic_write(struct sapic *sa, int which, uint32_t value)
125{
126
127	ia64_st4((void *)(sa->sa_registers + SAPIC_IO_SELECT), which);
128	ia64_mf_a();
129	ia64_st4((void *)(sa->sa_registers + SAPIC_IO_WINDOW), value);
130	ia64_mf_a();
131}
132
133static __inline void
134sapic_read_rte(struct sapic *sa, int which, struct sapic_rte *rte)
135{
136	uint32_t *p = (uint32_t *) rte;
137
138	p[0] = sapic_read(sa, SAPIC_RTE_BASE + 2 * which);
139	p[1] = sapic_read(sa, SAPIC_RTE_BASE + 2 * which + 1);
140}
141
142static __inline void
143sapic_write_rte(struct sapic *sa, int which, struct sapic_rte *rte)
144{
145	uint32_t *p = (uint32_t *) rte;
146
147	sapic_write(sa, SAPIC_RTE_BASE + 2 * which, p[0]);
148	sapic_write(sa, SAPIC_RTE_BASE + 2 * which + 1, p[1]);
149}
150
151struct sapic *
152sapic_lookup(u_int irq, u_int *vecp)
153{
154	struct sapic_rte rte;
155	struct sapic *sa;
156	int i;
157
158	for (i = 0; i < ia64_sapic_count; i++) {
159		sa = ia64_sapics[i];
160		if (irq >= sa->sa_base && irq <= sa->sa_limit) {
161			if (vecp != NULL) {
162				mtx_lock_spin(&sa->sa_mtx);
163				sapic_read_rte(sa, irq - sa->sa_base, &rte);
164				mtx_unlock_spin(&sa->sa_mtx);
165				*vecp = rte.rte_vector;
166			}
167			return (sa);
168		}
169	}
170
171	return (NULL);
172}
173
174
175int
176sapic_bind_intr(u_int irq, struct pcpu *pc)
177{
178	struct sapic_rte rte;
179	struct sapic *sa;
180
181	sa = sapic_lookup(irq, NULL);
182	if (sa == NULL)
183		return (EINVAL);
184
185	mtx_lock_spin(&sa->sa_mtx);
186	sapic_read_rte(sa, irq - sa->sa_base, &rte);
187	rte.rte_destination_id = (pc->pc_md.lid >> 24) & 255;
188	rte.rte_destination_eid = (pc->pc_md.lid >> 16) & 255;
189	rte.rte_delivery_mode = SAPIC_DELMODE_FIXED;
190	sapic_write_rte(sa, irq - sa->sa_base, &rte);
191	mtx_unlock_spin(&sa->sa_mtx);
192	return (0);
193}
194
195int
196sapic_config_intr(u_int irq, enum intr_trigger trig, enum intr_polarity pol)
197{
198	struct sapic_rte rte;
199	struct sapic *sa;
200
201	sa = sapic_lookup(irq, NULL);
202	if (sa == NULL)
203		return (EINVAL);
204
205	mtx_lock_spin(&sa->sa_mtx);
206	sapic_read_rte(sa, irq - sa->sa_base, &rte);
207	if (trig != INTR_TRIGGER_CONFORM)
208		rte.rte_trigger_mode = (trig == INTR_TRIGGER_EDGE) ?
209		    SAPIC_TRIGGER_EDGE : SAPIC_TRIGGER_LEVEL;
210	else
211		rte.rte_trigger_mode = (irq < 16) ? SAPIC_TRIGGER_EDGE :
212		    SAPIC_TRIGGER_LEVEL;
213	if (pol != INTR_POLARITY_CONFORM)
214		rte.rte_polarity = (pol == INTR_POLARITY_HIGH) ?
215		    SAPIC_POLARITY_HIGH : SAPIC_POLARITY_LOW;
216	else
217		rte.rte_polarity = (irq < 16) ? SAPIC_POLARITY_HIGH :
218		    SAPIC_POLARITY_LOW;
219	sapic_write_rte(sa, irq - sa->sa_base, &rte);
220	mtx_unlock_spin(&sa->sa_mtx);
221	return (0);
222}
223
224struct sapic *
225sapic_create(u_int id, u_int base, uint64_t address)
226{
227	struct sapic_rte rte;
228	struct sapic *sa;
229	u_int i, max;
230
231	sa = malloc(sizeof(struct sapic), M_SAPIC, M_ZERO | M_NOWAIT);
232	if (sa == NULL)
233		return (NULL);
234
235	sa->sa_id = id;
236	sa->sa_base = base;
237	sa->sa_registers = (uintptr_t)pmap_mapdev(address, 1048576);
238
239	mtx_init(&sa->sa_mtx, "I/O SAPIC lock", NULL, MTX_SPIN);
240
241	max = (sapic_read(sa, SAPIC_VERSION) >> 16) & 0xff;
242	sa->sa_limit = base + max;
243
244	ia64_sapics[ia64_sapic_count++] = sa;
245
246	/*
247	 * Initialize all RTEs with a default trigger mode and polarity.
248	 * This may be changed later by calling sapic_config_intr(). We
249	 * mask all interrupts by default.
250	 */
251	bzero(&rte, sizeof(rte));
252	rte.rte_mask = 1;
253	for (i = base; i <= sa->sa_limit; i++) {
254		rte.rte_trigger_mode = (i < 16) ? SAPIC_TRIGGER_EDGE :
255		    SAPIC_TRIGGER_LEVEL;
256		rte.rte_polarity = (i < 16) ? SAPIC_POLARITY_HIGH :
257		    SAPIC_POLARITY_LOW;
258		sapic_write_rte(sa, i - base, &rte);
259	}
260
261	return (sa);
262}
263
264int
265sapic_enable(struct sapic *sa, u_int irq, u_int vector)
266{
267	struct sapic_rte rte;
268	uint64_t lid = ia64_get_lid();
269
270	mtx_lock_spin(&sa->sa_mtx);
271	sapic_read_rte(sa, irq - sa->sa_base, &rte);
272	rte.rte_destination_id = (lid >> 24) & 255;
273	rte.rte_destination_eid = (lid >> 16) & 255;
274	rte.rte_delivery_mode = SAPIC_DELMODE_FIXED;
275	rte.rte_vector = vector;
276	rte.rte_mask = 0;
277	sapic_write_rte(sa, irq - sa->sa_base, &rte);
278	mtx_unlock_spin(&sa->sa_mtx);
279	return (0);
280}
281
282void
283sapic_eoi(struct sapic *sa, u_int vector)
284{
285
286	ia64_st4((void *)(sa->sa_registers + SAPIC_APIC_EOI), vector);
287	ia64_mf_a();
288}
289
290/* Expected to be called with interrupts disabled. */
291void
292sapic_mask(struct sapic *sa, u_int irq)
293{
294	struct sapic_rte rte;
295
296	mtx_lock_spin(&sa->sa_mtx);
297	sapic_read_rte(sa, irq - sa->sa_base, &rte);
298	rte.rte_mask = 1;
299	sapic_write_rte(sa, irq - sa->sa_base, &rte);
300	mtx_unlock_spin(&sa->sa_mtx);
301}
302
303/* Expected to be called with interrupts disabled. */
304void
305sapic_unmask(struct sapic *sa, u_int irq)
306{
307	struct sapic_rte rte;
308
309	mtx_lock_spin(&sa->sa_mtx);
310	sapic_read_rte(sa, irq - sa->sa_base, &rte);
311	rte.rte_mask = 0;
312	sapic_write_rte(sa, irq - sa->sa_base, &rte);
313	mtx_unlock_spin(&sa->sa_mtx);
314}
315
316static int
317sysctl_machdep_apic(SYSCTL_HANDLER_ARGS)
318{
319	char buf[80];
320	struct sapic_rte rte;
321	struct sapic *sa;
322	int apic, count, error, index, len;
323
324	len = sprintf(buf, "\n    APIC Idx: Id,EId : RTE\n");
325	error = SYSCTL_OUT(req, buf, len);
326	if (error)
327		return (error);
328
329	for (apic = 0; apic < ia64_sapic_count; apic++) {
330		sa = ia64_sapics[apic];
331		count = sa->sa_limit - sa->sa_base + 1;
332		for (index = 0; index < count; index++) {
333			mtx_lock_spin(&sa->sa_mtx);
334			sapic_read_rte(sa, index, &rte);
335			mtx_unlock_spin(&sa->sa_mtx);
336			if (rte.rte_vector == 0)
337				continue;
338			len = sprintf(buf,
339    "    0x%02x %3d: (%02x,%02x): %3d %d %d %s %s %s %s %s\n",
340			    sa->sa_id, index,
341			    rte.rte_destination_id, rte.rte_destination_eid,
342			    rte.rte_vector, rte.rte_delivery_mode,
343			    rte.rte_destination_mode,
344			    rte.rte_delivery_status ? "DS" : "  ",
345			    rte.rte_polarity ? "low-active " : "high-active",
346			    rte.rte_rirr ? "RIRR" : "    ",
347			    rte.rte_trigger_mode ? "level" : "edge ",
348			    rte.rte_flushen ? "F" : " ");
349			error = SYSCTL_OUT(req, buf, len);
350			if (error)
351				return (error);
352		}
353	}
354
355	return (0);
356}
357
358#ifdef DDB
359
360#include <ddb/ddb.h>
361
362void
363sapic_print(struct sapic *sa, u_int irq)
364{
365	struct sapic_rte rte;
366
367	db_printf("sapic=%u, irq=%u: ", sa->sa_id, irq);
368	sapic_read_rte(sa, irq - sa->sa_base, &rte);
369	db_printf("%3d %x->%x:%x %d %s %s %s %s %s %s\n", rte.rte_vector,
370	    rte.rte_delivery_mode,
371	    rte.rte_destination_id, rte.rte_destination_eid,
372	    rte.rte_destination_mode,
373	    rte.rte_delivery_status ? "DS" : "  ",
374	    rte.rte_polarity ? "low-active " : "high-active",
375	    rte.rte_rirr ? "RIRR" : "    ",
376	    rte.rte_trigger_mode ? "level" : "edge ",
377	    rte.rte_flushen ? "F" : " ",
378	    rte.rte_mask ? "(masked)" : "");
379}
380
381#endif
382