]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/i386/legacy.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / sys / i386 / i386 / legacy.c
1 /*
2  * Copyright 1998 Massachusetts Institute of Technology
3  *
4  * Permission to use, copy, modify, and distribute this software and
5  * its documentation for any purpose and without fee is hereby
6  * granted, provided that both the above copyright notice and this
7  * permission notice appear in all copies, that both the above
8  * copyright notice and this permission notice appear in all
9  * supporting documentation, and that the name of M.I.T. not be used
10  * in advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.  M.I.T. makes
12  * no representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied
14  * warranty.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 /*
33  * This code implements a `root nexus' for Intel Architecture
34  * machines.  The function of the root nexus is to serve as an
35  * attachment point for both processors and buses, and to manage
36  * resources which are common to all of them.  In particular,
37  * this code implements the core resource managers for interrupt
38  * requests, DMA requests (which rightfully should be a part of the
39  * ISA code but it's easier to do it here for now), I/O port addresses,
40  * and I/O memory address space.
41  */
42
43 #include "opt_smp.h"
44 #include "mca.h"
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/bus.h>
49 #include <sys/kernel.h>
50 #include <sys/module.h>
51 #include <machine/bus.h>
52 #include <sys/rman.h>
53
54 #include <machine/vmparam.h>
55 #include <vm/vm.h>
56 #include <vm/pmap.h>
57 #include <machine/pmap.h>
58
59 #include <machine/resource.h>
60 #ifdef APIC_IO
61 #include <machine/smp.h>
62 #include <machine/mpapic.h>
63 #endif
64
65 #ifdef PC98
66 #include <pc98/pc98/pc98.h>
67 #else
68 #include <i386/isa/isa.h>
69 #endif
70 #include <i386/isa/intr_machdep.h>
71
72 static struct rman irq_rman, drq_rman, port_rman, mem_rman;
73
74 static  int nexus_probe(device_t);
75 static  int nexus_attach(device_t);
76 static  int nexus_print_child(device_t, device_t);
77 static device_t nexus_add_child(device_t bus, int order, const char *name,
78                                 int unit);
79 static  struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
80                                               u_long, u_long, u_long, u_int);
81 static  int nexus_activate_resource(device_t, device_t, int, int,
82                                     struct resource *);
83 static  int nexus_deactivate_resource(device_t, device_t, int, int,
84                                       struct resource *);
85 static  int nexus_release_resource(device_t, device_t, int, int,
86                                    struct resource *);
87 static  int nexus_setup_intr(device_t, device_t, struct resource *, int flags,
88                              void (*)(void *), void *, void **);
89 static  int nexus_teardown_intr(device_t, device_t, struct resource *,
90                                 void *);
91
92 static device_method_t nexus_methods[] = {
93         /* Device interface */
94         DEVMETHOD(device_probe,         nexus_probe),
95         DEVMETHOD(device_attach,        nexus_attach),
96         DEVMETHOD(device_detach,        bus_generic_detach),
97         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
98         DEVMETHOD(device_suspend,       bus_generic_suspend),
99         DEVMETHOD(device_resume,        bus_generic_resume),
100
101         /* Bus interface */
102         DEVMETHOD(bus_print_child,      nexus_print_child),
103         DEVMETHOD(bus_add_child,        nexus_add_child),
104         DEVMETHOD(bus_read_ivar,        bus_generic_read_ivar),
105         DEVMETHOD(bus_write_ivar,       bus_generic_write_ivar),
106         DEVMETHOD(bus_alloc_resource,   nexus_alloc_resource),
107         DEVMETHOD(bus_release_resource, nexus_release_resource),
108         DEVMETHOD(bus_activate_resource, nexus_activate_resource),
109         DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
110         DEVMETHOD(bus_setup_intr,       nexus_setup_intr),
111         DEVMETHOD(bus_teardown_intr,    nexus_teardown_intr),
112
113         { 0, 0 }
114 };
115
116 static driver_t nexus_driver = {
117         "nexus",
118         nexus_methods,
119         1,                      /* no softc */
120 };
121 static devclass_t nexus_devclass;
122
123 DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
124
125 static int
126 nexus_probe(device_t dev)
127 {
128
129         device_quiet(dev);      /* suppress attach message for neatness */
130
131         /*
132          * IRQ's are on the mainboard on old systems, but on the ISA part
133          * of PCI->ISA bridges.  There would be multiple sets of IRQs on
134          * multi-ISA-bus systems.  PCI interrupts are routed to the ISA
135          * component, so in a way, PCI can be a partial child of an ISA bus(!).
136          * APIC interrupts are global though.
137          * In the non-APIC case, disallow the use of IRQ 2.
138          */
139         irq_rman.rm_start = 0;
140         irq_rman.rm_type = RMAN_ARRAY;
141         irq_rman.rm_descr = "Interrupt request lines";
142 #ifdef APIC_IO
143         irq_rman.rm_end = APIC_INTMAPSIZE - 1;
144         if (rman_init(&irq_rman)
145             || rman_manage_region(&irq_rman,
146                                   irq_rman.rm_start, irq_rman.rm_end))
147                 panic("nexus_probe irq_rman");
148 #else
149         irq_rman.rm_end = 15;
150         if (rman_init(&irq_rman)
151             || rman_manage_region(&irq_rman, irq_rman.rm_start, 1)
152             || rman_manage_region(&irq_rman, 3, irq_rman.rm_end))
153                 panic("nexus_probe irq_rman");
154 #endif
155
156         /*
157          * ISA DMA on PCI systems is implemented in the ISA part of each
158          * PCI->ISA bridge and the channels can be duplicated if there are
159          * multiple bridges.  (eg: laptops with docking stations)
160          */
161         drq_rman.rm_start = 0;
162         drq_rman.rm_end = 7;
163         drq_rman.rm_type = RMAN_ARRAY;
164         drq_rman.rm_descr = "DMA request lines";
165         /* XXX drq 0 not available on some machines */
166         if (rman_init(&drq_rman)
167             || rman_manage_region(&drq_rman, 0, 7))
168                 panic("nexus_probe drq_rman");
169
170         /*
171          * However, IO ports and Memory truely are global at this level,
172          * as are APIC interrupts (however many IO APICS there turn out
173          * to be on large systems..)
174          */
175         port_rman.rm_start = 0;
176         port_rman.rm_end = 0xffff;
177         port_rman.rm_type = RMAN_ARRAY;
178         port_rman.rm_descr = "I/O ports";
179         if (rman_init(&port_rman)
180             || rman_manage_region(&port_rman, 0, 0xffff))
181                 panic("nexus_probe port_rman");
182
183         mem_rman.rm_start = 0;
184         mem_rman.rm_end = ~0u;
185         mem_rman.rm_type = RMAN_ARRAY;
186         mem_rman.rm_descr = "I/O memory addresses";
187         if (rman_init(&mem_rman)
188             || rman_manage_region(&mem_rman, 0, ~0))
189                 panic("nexus_probe mem_rman");
190
191         return bus_generic_probe(dev);
192 }
193
194 static int
195 nexus_attach(device_t dev)
196 {
197         device_t        child;
198
199         /*
200          * First, deal with the children we know about already
201          */
202         bus_generic_attach(dev);
203         /*
204          * And if we didn't see EISA or ISA on a pci bridge, create some
205          * connection points now so they show up "on motherboard".
206          */
207         if (!devclass_get_device(devclass_find("eisa"), 0)) {
208                 child = device_add_child(dev, "eisa", 0);
209                 if (child == NULL)
210                         panic("nexus_attach eisa");
211                 device_probe_and_attach(child);
212         }
213 #if NMCA > 0
214         if (!devclass_get_device(devclass_find("mca"), 0)) {
215                 child = device_add_child(dev, "mca", 0);
216                 if (child == 0)
217                         panic("nexus_probe mca");
218                 device_probe_and_attach(child);
219         }
220 #endif
221         if (!devclass_get_device(devclass_find("isa"), 0)) {
222                 child = device_add_child(dev, "isa", 0);
223                 if (child == NULL)
224                         panic("nexus_attach isa");
225                 device_probe_and_attach(child);
226         }
227
228         return 0;
229 }
230
231 static int
232 nexus_print_child(device_t bus, device_t child)
233 {
234         int retval = 0;
235
236         retval += bus_print_child_header(bus, child);
237         retval += printf(" on motherboard\n");
238
239         return (retval);
240 }
241
242 static device_t
243 nexus_add_child(device_t bus, int order, const char *name, int unit)
244 {
245         return device_add_child_ordered(bus, order, name, unit);
246 }
247
248 /*
249  * Allocate a resource on behalf of child.  NB: child is usually going to be a
250  * child of one of our descendants, not a direct child of nexus0.
251  * (Exceptions include npx.)
252  */
253 static struct resource *
254 nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
255                      u_long start, u_long end, u_long count, u_int flags)
256 {
257         struct  resource *rv;
258         struct  rman *rm;
259         int needactivate = flags & RF_ACTIVE;
260
261         flags &= ~RF_ACTIVE;
262
263         switch (type) {
264         case SYS_RES_IRQ:
265                 rm = &irq_rman;
266                 break;
267
268         case SYS_RES_DRQ:
269                 rm = &drq_rman;
270                 break;
271
272         case SYS_RES_IOPORT:
273                 rm = &port_rman;
274                 break;
275
276         case SYS_RES_MEMORY:
277                 rm = &mem_rman;
278                 break;
279
280         default:
281                 return 0;
282         }
283
284         rv = rman_reserve_resource(rm, start, end, count, flags, child);
285         if (rv == 0)
286                 return 0;
287
288         if (type == SYS_RES_MEMORY) {
289                 rman_set_bustag(rv, I386_BUS_SPACE_MEM);
290         } else if (type == SYS_RES_IOPORT) {
291                 rman_set_bustag(rv, I386_BUS_SPACE_IO);
292 #ifdef PC98
293                 /* PC-98: the type of bus_space_handle_t is the structure. */
294                 rv->r_bushandle.bsh_base = rv->r_start;
295                 rv->r_bushandle.bsh_iat = NULL;
296                 rv->r_bushandle.bsh_iatsz = 0;
297                 rv->r_bushandle.bsh_res = NULL;
298                 rv->r_bushandle.bsh_ressz = 0;
299 #else
300                 /* IBM-PC: the type of bus_space_handle_t is u_int */
301                 rman_set_bushandle(rv, rv->r_start);
302 #endif
303         }
304
305         if (needactivate) {
306                 if (bus_activate_resource(child, type, *rid, rv)) {
307                         rman_release_resource(rv);
308                         return 0;
309                 }
310         }
311         
312         return rv;
313 }
314
315 static int
316 nexus_activate_resource(device_t bus, device_t child, int type, int rid,
317                         struct resource *r)
318 {
319         /*
320          * If this is a memory resource, map it into the kernel.
321          */
322         if (rman_get_bustag(r) == I386_BUS_SPACE_MEM) {
323                 caddr_t vaddr = 0;
324
325                 if (r->r_end < 1024 * 1024) {
326                         /*
327                          * The first 1Mb is mapped at KERNBASE.
328                          */
329                         vaddr = (caddr_t)(uintptr_t)(KERNBASE + r->r_start);
330                 } else {
331                         u_int32_t paddr;
332                         u_int32_t psize;
333                         u_int32_t poffs;
334
335                         paddr = r->r_start;
336                         psize = r->r_end - r->r_start;
337
338                         poffs = paddr - trunc_page(paddr);
339                         vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs;
340                 }
341                 rman_set_virtual(r, vaddr);
342 #ifdef PC98
343                 /* PC-98: the type of bus_space_handle_t is the structure. */
344                 r->r_bushandle.bsh_base = (bus_addr_t) vaddr;
345                 r->r_bushandle.bsh_iat = NULL;
346                 r->r_bushandle.bsh_iatsz = 0;
347                 r->r_bushandle.bsh_res = NULL;
348                 r->r_bushandle.bsh_ressz = 0;
349 #else
350                 /* IBM-PC: the type of bus_space_handle_t is u_int */
351                 rman_set_bushandle(r, (bus_space_handle_t) vaddr);
352 #endif
353         }
354         return (rman_activate_resource(r));
355 }
356
357 static int
358 nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
359                           struct resource *r)
360 {
361         /*
362          * If this is a memory resource, unmap it.
363          */
364         if ((rman_get_bustag(r) == I386_BUS_SPACE_MEM) && (r->r_end >= 1024 * 1024)) {
365                 u_int32_t psize;
366
367                 psize = r->r_end - r->r_start;
368                 pmap_unmapdev((vm_offset_t)rman_get_virtual(r), psize);
369         }
370                 
371         return (rman_deactivate_resource(r));
372 }
373
374 static int
375 nexus_release_resource(device_t bus, device_t child, int type, int rid,
376                        struct resource *r)
377 {
378         if (r->r_flags & RF_ACTIVE) {
379                 int error = bus_deactivate_resource(child, type, rid, r);
380                 if (error)
381                         return error;
382         }
383         return (rman_release_resource(r));
384 }
385
386 /*
387  * Currently this uses the really grody interface from kern/kern_intr.c
388  * (which really doesn't belong in kern/anything.c).  Eventually, all of
389  * the code in kern_intr.c and machdep_intr.c should get moved here, since
390  * this is going to be the official interface.
391  */
392 static int
393 nexus_setup_intr(device_t bus, device_t child, struct resource *irq,
394                  int flags, void (*ihand)(void *), void *arg, void **cookiep)
395 {
396         intrmask_t      *mask;
397         driver_t        *driver;
398         int     error, icflags;
399
400         /* somebody tried to setup an irq that failed to allocate! */
401         if (irq == NULL)
402                 panic("nexus_setup_intr: NULL irq resource!");
403
404         *cookiep = 0;
405         if (irq->r_flags & RF_SHAREABLE)
406                 icflags = 0;
407         else
408                 icflags = INTR_EXCL;
409
410         driver = device_get_driver(child);
411         switch (flags) {
412         case INTR_TYPE_TTY:
413                 mask = &tty_imask;
414                 break;
415         case (INTR_TYPE_TTY | INTR_TYPE_FAST):
416                 mask = &tty_imask;
417                 icflags |= INTR_FAST;
418                 break;
419         case INTR_TYPE_BIO:
420                 mask = &bio_imask;
421                 break;
422         case INTR_TYPE_NET:
423                 mask = &net_imask;
424                 break;
425         case INTR_TYPE_CAM:
426                 mask = &cam_imask;
427                 break;
428         case INTR_TYPE_MISC:
429                 mask = 0;
430                 break;
431         default:
432                 panic("still using grody create_intr interface");
433         }
434
435         /*
436          * We depend here on rman_activate_resource() being idempotent.
437          */
438         error = rman_activate_resource(irq);
439         if (error)
440                 return (error);
441
442         *cookiep = inthand_add(device_get_nameunit(child), irq->r_start,
443             ihand, arg, mask, icflags);
444         if (*cookiep == NULL)
445                 error = EINVAL; /* XXX ??? */
446
447         return (error);
448 }
449
450 static int
451 nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
452 {
453         return (inthand_remove(ih));
454 }