]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/dde/fbsd/lib/common/bsd/src/nexus.c
Inital import
[l4.git] / l4 / pkg / dde / fbsd / lib / common / bsd / src / nexus.c
1 /**
2  * Derived from i386/i386/nexus.c .
3  * \author Thomas Friebel <tf13@os.int.fu-dresden.de>
4  */
5 /*-
6  * Copyright 1998 Massachusetts Institute of Technology
7  *
8  * Permission to use, copy, modify, and distribute this software and
9  * its documentation for any purpose and without fee is hereby
10  * granted, provided that both the above copyright notice and this
11  * permission notice appear in all copies, that both the above
12  * copyright notice and this permission notice appear in all
13  * supporting documentation, and that the name of M.I.T. not be used
14  * in advertising or publicity pertaining to distribution of the
15  * software without specific, written prior permission.  M.I.T. makes
16  * no representations about the suitability of this software for any
17  * purpose.  It is provided "as is" without express or implied
18  * warranty.
19  * 
20  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
21  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
22  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
24  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD: src/sys/i386/i386/nexus.c,v 1.58.2.2 2004/11/07 22:35:36 njl Exp $");
36
37 /*
38  * This code implements a `root nexus' for Intel Architecture
39  * machines.  The function of the root nexus is to serve as an
40  * attachment point for both processors and buses, and to manage
41  * resources which are common to all of them.  In particular,
42  * this code implements the core resource managers for interrupt
43  * requests, DMA requests (which rightfully should be a part of the
44  * ISA code but it's easier to do it here for now), I/O port addresses,
45  * and I/O memory address space.
46  */
47
48 #include "opt_isa.h"
49
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/bus.h>
53 #include <sys/kernel.h>
54 #include <sys/malloc.h>
55 #include <sys/module.h>
56 #include <machine/bus.h>
57 #include <machine/intr_machdep.h>
58 #include <sys/rman.h>
59 #include <sys/interrupt.h>
60
61 #include <machine/vmparam.h>
62 #include <vm/vm.h>
63 #include <vm/pmap.h>
64 #include <machine/pmap.h>
65
66 #include <machine/resource.h>
67
68 #ifdef DEV_ISA
69 #include <isa/isavar.h>
70 #ifdef PC98
71 #include <pc98/pc98/pc98.h>
72 #else
73 #include <i386/isa/isa.h>
74 #endif
75 #endif
76 #include <sys/rtprio.h>
77
78 #include <dde_fbsd/interrupt.h>
79 #include <l4/dde/ddekit/panic.h>
80 #include <l4/dde/ddekit/resources.h>
81
82 #define dbg_this 0
83
84 static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
85 struct nexus_device {
86         struct resource_list    nx_resources;
87 };
88
89 #define DEVTONX(dev)    ((struct nexus_device *)device_get_ivars(dev))
90
91 static struct rman irq_rman, drq_rman, port_rman, mem_rman;
92
93 static  int nexus_probe(device_t);
94 static  int nexus_attach(device_t);
95 static  int nexus_print_all_resources(device_t dev);
96 static  int nexus_print_child(device_t, device_t);
97 static device_t nexus_add_child(device_t bus, int order, const char *name,
98                                 int unit);
99 static  struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
100                                               u_long, u_long, u_long, u_int);
101 static  int nexus_config_intr(device_t, int, enum intr_trigger,
102                               enum intr_polarity);
103 static  int nexus_activate_resource(device_t, device_t, int, int,
104                                     struct resource *);
105 static  int nexus_deactivate_resource(device_t, device_t, int, int,
106                                       struct resource *);
107 static  int nexus_release_resource(device_t, device_t, int, int,
108                                    struct resource *);
109 static  int nexus_setup_intr(device_t, device_t, struct resource *, int flags,
110                              void (*)(void *), void *, void **);
111 static  int nexus_teardown_intr(device_t, device_t, struct resource *,
112                                 void *);
113 static struct resource_list *nexus_get_reslist(device_t dev, device_t child);
114 static  int nexus_set_resource(device_t, device_t, int, int, u_long, u_long);
115 static  int nexus_get_resource(device_t, device_t, int, int, u_long *, u_long *);
116 static void nexus_delete_resource(device_t, device_t, int, int);
117
118 static device_method_t nexus_methods[] = {
119         /* Device interface */
120         DEVMETHOD(device_probe,         nexus_probe),
121         DEVMETHOD(device_attach,        nexus_attach),
122         DEVMETHOD(device_detach,        bus_generic_detach),
123         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
124         DEVMETHOD(device_suspend,       bus_generic_suspend),
125         DEVMETHOD(device_resume,        bus_generic_resume),
126
127         /* Bus interface */
128         DEVMETHOD(bus_print_child,      nexus_print_child),
129         DEVMETHOD(bus_add_child,        nexus_add_child),
130         DEVMETHOD(bus_alloc_resource,   nexus_alloc_resource),
131         DEVMETHOD(bus_release_resource, nexus_release_resource),
132         DEVMETHOD(bus_activate_resource, nexus_activate_resource),
133         DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
134         DEVMETHOD(bus_setup_intr,       nexus_setup_intr),
135         DEVMETHOD(bus_teardown_intr,    nexus_teardown_intr),
136         DEVMETHOD(bus_config_intr,      nexus_config_intr),
137         DEVMETHOD(bus_get_resource_list, nexus_get_reslist),
138         DEVMETHOD(bus_set_resource,     nexus_set_resource),
139         DEVMETHOD(bus_get_resource,     nexus_get_resource),
140         DEVMETHOD(bus_delete_resource,  nexus_delete_resource),
141
142         { 0, 0 }
143 };
144
145 static driver_t nexus_driver = {
146         "nexus",
147         nexus_methods,
148         1,                      /* no softc */
149 };
150 static devclass_t nexus_devclass;
151
152 DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
153
154 static int
155 nexus_probe(device_t dev)
156 {
157         device_quiet(dev);      /* suppress attach message for neatness */
158
159         // irq's
160         irq_rman.rm_start = 0;
161         irq_rman.rm_type = RMAN_ARRAY;
162         irq_rman.rm_descr = "Interrupt request lines";
163         irq_rman.rm_end = NUM_IO_INTS - 1;
164         if (rman_init(&irq_rman))
165                 panic("nexus_probe irq_rman");
166         if (rman_manage_region(&irq_rman, 0, NUM_IO_INTS - 1) != 0)
167                 panic("nexus_probe irq_rman add");
168
169         // no drqs
170         drq_rman.rm_start = 0;
171         drq_rman.rm_type = RMAN_ARRAY;
172         drq_rman.rm_descr = "DMA request lines";
173         irq_rman.rm_end = 3;
174         if (rman_init(&drq_rman))
175                 panic("nexus_probe drq_rman");
176         if (rman_manage_region(&drq_rman, drq_rman.rm_start, drq_rman.rm_end))
177                 panic("nexus_probe drq_rman");
178
179         port_rman.rm_start = 0;
180         port_rman.rm_end = 0xffff;
181         port_rman.rm_type = RMAN_ARRAY;
182         port_rman.rm_descr = "I/O ports";
183         if (rman_init(&port_rman)
184             || rman_manage_region(&port_rman, 0, 0xffff))
185                 panic("nexus_probe port_rman");
186
187         mem_rman.rm_start = 0;
188         mem_rman.rm_end = ~0u;
189         mem_rman.rm_type = RMAN_ARRAY;
190         mem_rman.rm_descr = "I/O memory addresses";
191         if (rman_init(&mem_rman)
192             || rman_manage_region(&mem_rman, 0, ~0))
193                 panic("nexus_probe mem_rman");
194
195         return 0;
196 }
197
198 static int
199 nexus_attach(device_t dev)
200 {
201
202         bus_generic_probe(dev);
203         bus_generic_attach(dev);
204         return 0;
205 }
206
207 static int
208 nexus_print_all_resources(device_t dev)
209 {
210         struct  nexus_device *ndev = DEVTONX(dev);
211         struct resource_list *rl = &ndev->nx_resources;
212         int retval = 0;
213
214         if (SLIST_FIRST(rl))
215                 retval += printf(" at");
216         
217         retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx");
218         retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx");
219         retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
220
221         return retval;
222 }
223
224 static int
225 nexus_print_child(device_t bus, device_t child)
226 {
227         int retval = 0;
228
229         retval += bus_print_child_header(bus, child);
230         retval += nexus_print_all_resources(child);
231         if (device_get_flags(child))
232                 retval += printf(" flags %#x", device_get_flags(child));
233         retval += printf(" on motherboard\n");  /* XXX "motherboard", ick */
234
235         return (retval);
236 }
237
238 static device_t
239 nexus_add_child(device_t bus, int order, const char *name, int unit)
240 {
241         device_t                child;
242         struct nexus_device     *ndev;
243
244         ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO);
245         if (!ndev)
246                 return(0);
247         resource_list_init(&ndev->nx_resources);
248
249         child = device_add_child_ordered(bus, order, name, unit); 
250
251         /* should we free this in nexus_child_detached? */
252         device_set_ivars(child, ndev);
253
254         return(child);
255 }
256
257 static char *get_restype_name(int type) {
258         char *stype="unknown";
259         switch (type) {
260                 case SYS_RES_IRQ:
261                         stype="IRQ";
262                         break;
263                 case SYS_RES_DRQ:
264                         stype="DRQ";
265                         break;
266                 case SYS_RES_MEMORY:
267                         stype="MEM";
268                         break;
269                 case SYS_RES_IOPORT:
270                         stype="I/O";
271                         break;
272         }
273         return stype;
274 }
275
276 /*
277  * Allocate a resource on behalf of child.  NB: child is usually going to be a
278  * child of one of our descendants, not a direct child of nexus0.
279  * (Exceptions include npx.)
280  */
281 static struct resource *
282 nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
283                      u_long start, u_long end, u_long count, u_int flags)
284 {
285         struct nexus_device *ndev = DEVTONX(child);
286         struct  resource *rv;
287         struct resource_list_entry *rle;
288         struct  rman *rm;
289         int needactivate = flags & RF_ACTIVE;
290
291         if (dbg_this)
292                 printf("nexus_alloc_resource(%s 0x%08lx-0x%08lx flags=0x%04x)\n", get_restype_name(type), start, end, flags);
293
294         /*
295          * If this is an allocation of the "default" range for a given RID, and
296          * we know what the resources for this device are (ie. they aren't maintained
297          * by a child bus), then work out the start/end values.
298          */
299         if ((start == 0UL) && (end == ~0UL) && (count == 1)) {
300                 if (ndev == NULL)
301                         return(NULL);
302                 rle = resource_list_find(&ndev->nx_resources, type, *rid);
303                 if (rle == NULL)
304                         return(NULL);
305                 start = rle->start;
306                 end = rle->end;
307                 count = rle->count;
308         }
309
310         flags &= ~RF_ACTIVE;
311
312         // use the responsible resource manager
313         switch (type) {
314         case SYS_RES_IRQ:
315                 rm = &irq_rman;
316                 break;
317         case SYS_RES_DRQ:
318                 rm = &drq_rman;
319                 break;
320         case SYS_RES_IOPORT:
321                 rm = &port_rman;
322                 break;
323         case SYS_RES_MEMORY:
324                 rm = &mem_rman;
325                 break;
326         default:
327                 return 0;
328         }
329         // reserve the resource local
330         rv = rman_reserve_resource(rm, start, end, count, flags, child);
331         if (rv == 0)
332                 return 0;
333
334         KASSERT(count==end-start+1, ("count (%d) != start (%d) - end (%d) + 1", count, start, end));
335
336         // ??? what is this for?
337         if (type == SYS_RES_MEMORY) {
338                 rman_set_bustag(rv, I386_BUS_SPACE_MEM);
339         } else if (type == SYS_RES_IOPORT) {
340                 rman_set_bustag(rv, I386_BUS_SPACE_IO);
341                 rman_set_bushandle(rv, rman_get_start(rv));
342         }
343
344         // activate if requested
345         if (needactivate) {
346                 if (bus_activate_resource(child, type, *rid, rv)) {
347                         rman_release_resource(rv);
348                         return 0;
349                 }
350         }
351         
352         return rv;
353 }
354
355 static int
356 nexus_activate_resource(device_t bus, device_t child, int type, int rid,
357                         struct resource *r)
358 {
359         u_long start = rman_get_start(r);
360         u_long end   = rman_get_end(r);
361         u_long count = rman_get_size(r);
362
363         if (dbg_this) printf("nexus_activate_resource(child=%s type=%s range=0x%08lux-0x%08lux)\n",
364                         device_get_nameunit(child), get_restype_name(type),
365                         rman_get_start(r), rman_get_end(r));
366         // do the reservation at the i/o server
367         if (type==SYS_RES_IRQ) {
368                 int err;
369
370                 KASSERT(count==1, ("trying to allocate more than one dma channel at once"));
371
372                 err = intr_prepare(start);
373                 if (err) {
374                         printf("nexus: error allocating irq\n");
375                         return EBUSY;
376                 }
377         } else if (type==SYS_RES_DRQ) {
378                 int err;
379
380                 KASSERT(count==1, ("trying to allocate more than one dma channel at once"));
381
382                 err = ddekit_request_dma(start);
383                 if (err) {
384                         printf("nexus: error allocating isa dma channels\n");
385                         return EBUSY;
386                 }
387         } else if (type==SYS_RES_IOPORT) {
388                 int err;
389
390                 err = ddekit_request_io(start, count);
391                 if (err) {
392                         printf("nexus: error allocating io ports\n");
393                         return EBUSY;
394                 }
395         } else if (type==SYS_RES_MEMORY) {
396                 int err;
397                 ddekit_addr_t virtual;
398
399                 if (end < 1024 * 1024) {
400 //                      /*
401 //                       * The first 1Mb is mapped at KERNBASE.
402 //                       */
403 //                      vaddr = (caddr_t)(uintptr_t)(KERNBASE + rman_get_start(r));
404                         ddekit_debug("special case? review (+implement)!");
405                 }
406
407                 err = ddekit_request_mem(start, count, &virtual);
408                 if (err) {
409                         printf("nexus: error allocating device memory\n");
410                         return EBUSY;
411                 }
412                 if (dbg_this) printf("nexus: mapped at virt=0x%08x\n", virtual);
413
414                 //XXXY: setup virtual->bus page mapping? (ddekit_set_pte)
415
416                 rman_set_virtual(r, (void*) virtual);
417                 rman_set_bushandle(r, (bus_space_handle_t) virtual);
418         }
419
420         return (rman_activate_resource(r));
421 }
422
423 static int
424 nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
425                           struct resource *r)
426 {
427         if (dbg_this) printf("nexus_deactivate_resource(child=%s type=%s range=0x%08lux-0x%08lux)\n",
428                         device_get_nameunit(child), get_restype_name(type),
429                         rman_get_start(r), rman_get_end(r));
430
431         // undo the reservation at the i/o server
432         if (type==SYS_RES_IRQ) {
433                 ddekit_debug("irq release not yet implemented");
434         } else if (type==SYS_RES_DRQ) {
435                 int err;
436
437                 err = ddekit_release_dma(rman_get_start(r));
438                 KASSERT(!err, ("error releasing dma channel"));
439         } else if (type==SYS_RES_IOPORT) {
440                 int err;
441
442                 err = ddekit_release_io(rman_get_start(r), rman_get_size(r));
443                 KASSERT(!err, ("error releasing io ports"));
444         } else if (type==SYS_RES_MEMORY) {
445                 int err;
446
447                 err = ddekit_release_mem(rman_get_start(r), rman_get_size(r));
448                 KASSERT(!err, ("error releasing device memory"));
449         }
450
451         return (rman_deactivate_resource(r));
452 }
453
454 static int
455 nexus_release_resource(device_t bus, device_t child, int type, int rid,
456                        struct resource *r)
457 {
458         if (dbg_this) printf("nexus_release_resource(child=%s type=%s range=0x%08lux-0x%08lux)\n",
459                         device_get_nameunit(child), get_restype_name(type),
460                         rman_get_start(r), rman_get_end(r));
461
462         // deactivate first if active
463         if (rman_get_flags(r) & RF_ACTIVE) {
464                 int error = bus_deactivate_resource(child, type, rid, r);
465                 if (error)
466                         return error;
467         }
468
469         return (rman_release_resource(r));
470 }
471
472 /*
473  * Currently this uses the really grody interface from kern/kern_intr.c
474  * (which really doesn't belong in kern/anything.c).  Eventually, all of
475  * the code in kern_intr.c and machdep_intr.c should get moved here, since
476  * this is going to be the official interface.
477  */
478 static int
479 nexus_setup_intr(device_t bus, device_t child, struct resource *irq,
480                  int flags, void (*ihand)(void *), void *arg, void **cookiep)
481 {
482         int             error;
483
484         /* somebody tried to setup an irq that failed to allocate! */
485         if (irq == NULL)
486                 panic("nexus_setup_intr: NULL irq resource!");
487
488         *cookiep = 0;
489         if ((rman_get_flags(irq) & RF_SHAREABLE) == 0)
490                 flags |= INTR_EXCL;
491
492         rman_activate_resource(irq);
493
494         error = intr_add_handler(device_get_nameunit(child),
495             rman_get_start(irq), ihand, arg, flags, cookiep);
496
497         return (error);
498 }
499
500 static int
501 nexus_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih)
502 {
503         return (intr_remove_handler(ih));;
504 }
505
506 static int
507 nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
508     enum intr_polarity pol)
509 {
510         return (intr_config_intr(irq, trig, pol));
511 }
512
513 static struct resource_list *
514 nexus_get_reslist(device_t dev, device_t child)
515 {
516         struct nexus_device *ndev = DEVTONX(child);
517
518         return (&ndev->nx_resources);
519 }
520
521 static int
522 nexus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count)
523 {
524         struct nexus_device     *ndev = DEVTONX(child);
525         struct resource_list    *rl = &ndev->nx_resources;
526
527         /* XXX this should return a success/failure indicator */
528         resource_list_add(rl, type, rid, start, start + count - 1, count);
529         return(0);
530 }
531
532 static int
533 nexus_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp)
534 {
535         struct nexus_device     *ndev = DEVTONX(child);
536         struct resource_list    *rl = &ndev->nx_resources;
537         struct resource_list_entry *rle;
538
539         rle = resource_list_find(rl, type, rid);
540         if (!rle)
541                 return(ENOENT);
542         if (startp)
543                 *startp = rle->start;
544         if (countp)
545                 *countp = rle->count;
546         return(0);
547 }
548
549 static void
550 nexus_delete_resource(device_t dev, device_t child, int type, int rid)
551 {
552         struct nexus_device     *ndev = DEVTONX(child);
553         struct resource_list    *rl = &ndev->nx_resources;
554
555         resource_list_delete(rl, type, rid);
556 }
557