]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/ia64/ia64/sapic.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / ia64 / ia64 / sapic.c
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$
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
79 struct 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
87 struct 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
102 static MALLOC_DEFINE(M_SAPIC, "sapic", "I/O SAPIC devices");
103
104 struct sapic *ia64_sapics[16];          /* XXX make this resizable */
105 int ia64_sapic_count;
106
107 static int sysctl_machdep_apic(SYSCTL_HANDLER_ARGS);
108
109 SYSCTL_OID(_machdep, OID_AUTO, apic, CTLTYPE_STRING|CTLFLAG_RD,
110     NULL, 0, sysctl_machdep_apic, "A", "(x)APIC redirection table entries");
111
112 static __inline uint32_t
113 sapic_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
123 static __inline void
124 sapic_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
133 static __inline void
134 sapic_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
142 static __inline void
143 sapic_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
151 struct sapic *
152 sapic_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
175 int
176 sapic_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
195 int
196 sapic_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
224 struct sapic *
225 sapic_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
264 int
265 sapic_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
282 void
283 sapic_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. */
291 void
292 sapic_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. */
304 void
305 sapic_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
316 static int
317 sysctl_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
362 void
363 sapic_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