]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/alpha/isa/isa.c
- Heavyweight interrupt threads on the alpha for device I/O interrupts.
[FreeBSD/FreeBSD.git] / sys / alpha / isa / isa.c
1 /*-
2  * Copyright (c) 1998 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 <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
33 #include <sys/bus.h>
34 #include <machine/bus.h>
35 #include <sys/malloc.h>
36 #include <sys/proc.h>
37 #include <sys/rman.h>
38 #include <sys/interrupt.h>
39
40 #include <isa/isareg.h>
41 #include <isa/isavar.h>
42 #include <isa/isa_common.h>
43 #include <alpha/isa/isavar.h>
44 #include <machine/intr.h>
45 #include <machine/intrcnt.h>
46 #include <machine/resource.h>
47 #include <machine/cpuconf.h>
48
49 static struct rman isa_irq_rman;
50 static struct rman isa_drq_rman;
51
52 static void
53 isa_intr_enable(int irq)
54 {
55         int s = splhigh();
56         if (irq < 8)
57                 outb(IO_ICU1+1, inb(IO_ICU1+1) & ~(1 << irq));
58         else
59                 outb(IO_ICU2+1, inb(IO_ICU2+1) & ~(1 << (irq - 8)));
60         splx(s);
61 }
62
63 static void
64 isa_intr_disable(int irq)
65 {
66         int s = splhigh();
67         if (irq < 8)
68                 outb(IO_ICU1+1, inb(IO_ICU1+1) | (1 << irq));
69         else
70                 outb(IO_ICU2+1, inb(IO_ICU2+1) | (1 << (irq - 8)));
71         splx(s);
72 }
73
74 intrmask_t
75 isa_irq_pending(void)
76 {
77         u_char irr1;
78         u_char irr2;
79
80         irr1 = inb(IO_ICU1);
81         irr2 = inb(IO_ICU2);
82         return ((irr2 << 8) | irr1);
83 }
84
85 intrmask_t
86 isa_irq_mask(void)
87 {
88         u_char irr1;
89         u_char irr2;
90
91         irr1 = inb(IO_ICU1+1);
92         irr2 = inb(IO_ICU2+1);
93         return ((irr2 << 8) | irr1);
94 }
95
96 void
97 isa_init(void)
98 {
99         isa_init_intr();
100 }
101
102 void
103 isa_init_intr(void)
104 {
105         static int initted = 0;
106
107         if (initted) return;
108         initted = 1;
109                 
110         isa_irq_rman.rm_start = 0;
111         isa_irq_rman.rm_end = 15;
112         isa_irq_rman.rm_type = RMAN_ARRAY;
113         isa_irq_rman.rm_descr = "ISA Interrupt request lines";
114         if (rman_init(&isa_irq_rman)
115             || rman_manage_region(&isa_irq_rman, 0, 1)
116             || rman_manage_region(&isa_irq_rman, 3, 15))
117                 panic("isa_probe isa_irq_rman");
118
119         isa_drq_rman.rm_start = 0;
120         isa_drq_rman.rm_end = 7;
121         isa_drq_rman.rm_type = RMAN_ARRAY;
122         isa_drq_rman.rm_descr = "ISA DMA request lines";
123         if (rman_init(&isa_drq_rman)
124             || rman_manage_region(&isa_drq_rman, 0, 7))
125                 panic("isa_probe isa_drq_rman");
126
127         /* mask all isa interrupts */
128         outb(IO_ICU1+1, 0xff);
129         outb(IO_ICU2+1, 0xff);
130
131         /* make sure chaining irq is enabled */
132         isa_intr_enable(2);
133 }
134
135 struct resource *
136 isa_alloc_intr(device_t bus, device_t child, int irq)
137 {
138         return rman_reserve_resource(&isa_irq_rman, irq, irq, 1,
139                                      0, child);
140 }
141
142 struct resource *
143 isa_alloc_intrs(device_t bus, device_t child, u_long start, u_long end)
144 {
145         return rman_reserve_resource(&isa_irq_rman, start, end,
146                                      end - start + 1, 0, child);
147 }
148
149 int
150 isa_release_intr(device_t bus, device_t child, struct resource *r)
151 {
152         return rman_release_resource(r);
153 }
154
155 /*
156  * This implementation simply passes the request up to the parent
157  * bus, which in our case is the pci chipset device, substituting any
158  * configured values if the caller defaulted.  We can get away with
159  * this because there is no special mapping for ISA resources on this
160  * platform.  When porting this code to another architecture, it may be
161  * necessary to interpose a mapping layer here.
162  *
163  * We manage our own interrupt resources since ISA interrupts go through
164  * the ISA PIC, not the PCI interrupt controller.
165  */
166 struct resource *
167 isa_alloc_resource(device_t bus, device_t child, int type, int *rid,
168                    u_long start, u_long end, u_long count, u_int flags)
169 {
170         /*
171          * Consider adding a resource definition. We allow rid 0-1 for
172          * irq and drq, 0-3 for memory and 0-7 for ports which is
173          * sufficient for isapnp.
174          */
175         int passthrough = (device_get_parent(child) != bus);
176         int isdefault = (start == 0UL && end == ~0UL);
177         struct isa_device* idev = DEVTOISA(child);
178         struct resource_list *rl = &idev->id_resources;
179         struct resource_list_entry *rle;
180         struct resource *res;
181         
182         if (!passthrough && !isdefault) {
183                 rle = resource_list_find(rl, type, *rid);
184                 if (!rle) {
185                         if (*rid < 0)
186                                 return 0;
187                         switch (type) {
188                         case SYS_RES_IRQ:
189                                 if (*rid >= ISA_NIRQ)
190                                         return 0;
191                                 break;
192                         case SYS_RES_DRQ:
193                                 if (*rid >= ISA_NDRQ)
194                                         return 0;
195                                 break;
196                         case SYS_RES_MEMORY:
197                                 if (*rid >= ISA_NMEM)
198                                         return 0;
199                                 break;
200                         case SYS_RES_IOPORT:
201                                 if (*rid >= ISA_NPORT)
202                                         return 0;
203                                 break;
204                         default:
205                                 return 0;
206                         }
207                         resource_list_add(rl, type, *rid, start, end, count);
208                 }
209         }
210
211         if (type != SYS_RES_IRQ && type != SYS_RES_DRQ)
212                 return resource_list_alloc(rl, bus, child, type, rid,
213                                            start, end, count, flags);
214
215         if (!passthrough) {
216                 rl = device_get_ivars(child);
217                 rle = resource_list_find(rl, type, *rid);
218                 if (!rle)
219                         return 0;
220                 if (rle->res)
221                         panic("isa_alloc_resource: resource entry is busy");
222                 if (isdefault) {
223                         start = end = rle->start;
224                         count = 1;
225                 }
226         }
227
228         if (type == SYS_RES_IRQ)
229             res = rman_reserve_resource(&isa_irq_rman, start, start, 1,
230                                         0, child);
231         else
232             res = rman_reserve_resource(&isa_drq_rman, start, start, 1,
233                                         0, child);
234             
235         if (res && !passthrough) {
236                 rle = resource_list_find(rl, type, *rid);
237                 rle->start = rman_get_start(res);
238                 rle->end = rman_get_end(res);
239                 rle->count = 1;
240                 rle->res = res;
241         }
242
243         return res;
244 }
245
246 int
247 isa_release_resource(device_t bus, device_t child, int type, int rid,
248                      struct resource *res)
249 {
250         int passthrough = (device_get_parent(child) != bus);
251         struct isa_device* idev = DEVTOISA(child);
252         struct resource_list *rl = &idev->id_resources;
253         struct resource_list_entry *rle;
254         int error;
255
256         if (type != SYS_RES_IRQ)
257                 return resource_list_release(rl, bus, child, type, rid, res);
258
259         error = rman_release_resource(res);
260
261         if (!passthrough && !error) {
262                 rle = resource_list_find(rl, SYS_RES_IRQ, rid);
263                 if (rle)
264                         rle->res = NULL;
265                 else
266                         error = ENOENT;
267         }
268
269         return error;
270 }
271
272 struct isa_intr {
273         void *ih;
274         driver_intr_t *intr;
275         void *arg;
276         int irq;
277 };
278
279 /*
280  * Wrap ISA interrupt routines so that we can feed non-specific
281  * EOI to the PICs.
282  */
283
284 static void
285 isa_handle_intr(void *arg)
286 {
287         struct isa_intr *ii = arg;
288         int irq = ii->irq;
289
290         ii->intr(ii->arg);
291
292         if (ii->irq > 7)
293                 outb(IO_ICU2, 0x20 | (irq & 7));
294         outb(IO_ICU1, 0x20 | (irq > 7 ? 2 : irq));
295 }
296
297 int
298 isa_setup_intr(device_t dev, device_t child,
299                struct resource *irq, int flags,
300                driver_intr_t *intr, void *arg, void **cookiep)
301 {
302         struct isa_intr *ii;
303         int error;
304
305         if (platform.isa_setup_intr)
306                 return platform.isa_setup_intr(dev, child, irq, flags, 
307                         intr, arg, cookiep);    
308
309         error = rman_activate_resource(irq);
310         if (error)
311                 return error;
312
313         ii = malloc(sizeof(struct isa_intr), M_DEVBUF, M_NOWAIT);
314         if (!ii)
315                 return ENOMEM;
316         ii->intr = intr;
317         ii->arg = arg;
318         ii->irq = irq->r_start;
319
320         error = alpha_setup_intr(
321                          device_get_nameunit(child ? child : dev),
322                          0x800 + (irq->r_start << 4), isa_handle_intr, ii,
323                          ithread_priority(flags), &ii->ih,
324                          &intrcnt[INTRCNT_ISA_IRQ + irq->r_start],
325                          NULL, NULL);
326         if (error) {
327                 free(ii, M_DEVBUF);
328                 return error;
329         }
330         isa_intr_enable(irq->r_start);
331
332         *cookiep = ii;
333
334         if (child)
335                 device_printf(child, "interrupting at ISA irq %d\n",
336                               (int)irq->r_start);
337
338         return 0;
339 }
340
341 int
342 isa_teardown_intr(device_t dev, device_t child,
343                   struct resource *irq, void *cookie)
344 {
345         struct isa_intr *ii = cookie;
346
347         if (platform.isa_teardown_intr) {
348                 platform.isa_teardown_intr(dev, child, irq, cookie);    
349                 return 0;
350         }
351
352         alpha_teardown_intr(ii->ih);
353         isa_intr_disable(irq->r_start);
354
355         return 0;
356 }