legacy.c revision 66416
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: head/sys/i386/i386/legacy.c 66416 2000-09-28 00:37:32Z peter $
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 "mca.h"
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/bus.h>
48#include <sys/kernel.h>
49#include <sys/malloc.h>
50#include <sys/module.h>
51#include <machine/bus.h>
52#include <sys/rman.h>
53#include <sys/interrupt.h>
54
55#include <machine/vmparam.h>
56#include <vm/vm.h>
57#include <vm/pmap.h>
58#include <machine/pmap.h>
59
60#include <machine/nexusvar.h>
61#include <machine/resource.h>
62#ifdef APIC_IO
63#include <machine/smp.h>
64#include <machine/mpapic.h>
65#endif
66
67#include <isa/isavar.h>
68#ifdef PC98
69#include <pc98/pc98/pc98.h>
70#else
71#include <i386/isa/isa.h>
72#endif
73#include <sys/proc.h>
74#include <i386/isa/icu.h>
75#include <i386/isa/intr_machdep.h>
76#include <sys/rtprio.h>
77
78MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device");
79
80struct nexus_device {
81	struct resource_list	nx_resources;
82	int			nx_pcibus;
83};
84
85#define DEVTONX(dev)	((struct nexus_device *)device_get_ivars(dev))
86
87static struct rman irq_rman, drq_rman, port_rman, mem_rman;
88
89static	int nexus_probe(device_t);
90static	int nexus_attach(device_t);
91static	int nexus_print_resources(struct resource_list *rl, const char *name, int type,
92				  const char *format);
93static	int nexus_print_all_resources(device_t dev);
94static	int nexus_print_child(device_t, device_t);
95static device_t nexus_add_child(device_t bus, int order, const char *name,
96				int unit);
97static	struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
98					      u_long, u_long, u_long, u_int);
99static	int nexus_read_ivar(device_t, device_t, int, uintptr_t *);
100static	int nexus_write_ivar(device_t, device_t, int, uintptr_t);
101static	int nexus_activate_resource(device_t, device_t, int, int,
102				    struct resource *);
103static	int nexus_deactivate_resource(device_t, device_t, int, int,
104				      struct resource *);
105static	int nexus_release_resource(device_t, device_t, int, int,
106				   struct resource *);
107static	int nexus_setup_intr(device_t, device_t, struct resource *, int flags,
108			     void (*)(void *), void *, void **);
109static	int nexus_teardown_intr(device_t, device_t, struct resource *,
110				void *);
111static	int nexus_set_resource(device_t, device_t, int, int, u_long, u_long);
112static	int nexus_get_resource(device_t, device_t, int, int, u_long *, u_long *);
113static void nexus_delete_resource(device_t, device_t, int, int);
114
115static device_method_t nexus_methods[] = {
116	/* Device interface */
117	DEVMETHOD(device_probe,		nexus_probe),
118	DEVMETHOD(device_attach,	nexus_attach),
119	DEVMETHOD(device_detach,	bus_generic_detach),
120	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
121	DEVMETHOD(device_suspend,	bus_generic_suspend),
122	DEVMETHOD(device_resume,	bus_generic_resume),
123
124	/* Bus interface */
125	DEVMETHOD(bus_print_child,	nexus_print_child),
126	DEVMETHOD(bus_add_child,	nexus_add_child),
127	DEVMETHOD(bus_read_ivar,	nexus_read_ivar),
128	DEVMETHOD(bus_write_ivar,	nexus_write_ivar),
129	DEVMETHOD(bus_alloc_resource,	nexus_alloc_resource),
130	DEVMETHOD(bus_release_resource,	nexus_release_resource),
131	DEVMETHOD(bus_activate_resource, nexus_activate_resource),
132	DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
133	DEVMETHOD(bus_setup_intr,	nexus_setup_intr),
134	DEVMETHOD(bus_teardown_intr,	nexus_teardown_intr),
135	DEVMETHOD(bus_set_resource,	nexus_set_resource),
136	DEVMETHOD(bus_get_resource,	nexus_get_resource),
137	DEVMETHOD(bus_delete_resource,	nexus_delete_resource),
138
139	{ 0, 0 }
140};
141
142static driver_t nexus_driver = {
143	"nexus",
144	nexus_methods,
145	1,			/* no softc */
146};
147static devclass_t nexus_devclass;
148
149DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
150
151static int
152nexus_probe(device_t dev)
153{
154
155	device_quiet(dev);	/* suppress attach message for neatness */
156
157	/*
158	 * XXX working notes:
159	 *
160	 * - IRQ resource creation should be moved to the PIC/APIC driver.
161	 * - DRQ resource creation should be moved to the DMAC driver.
162	 * - The above should be sorted to probe earlier than any child busses.
163	 *
164	 * - Leave I/O and memory creation here, as child probes may need them.
165	 *   (especially eg. ACPI)
166	 */
167
168	/*
169	 * IRQ's are on the mainboard on old systems, but on the ISA part
170	 * of PCI->ISA bridges.  There would be multiple sets of IRQs on
171	 * multi-ISA-bus systems.  PCI interrupts are routed to the ISA
172	 * component, so in a way, PCI can be a partial child of an ISA bus(!).
173	 * APIC interrupts are global though.
174	 *
175	 * XXX We depend on the AT PIC driver correctly claiming IRQ 2
176	 *     to prevent its reuse elsewhere in the !APIC_IO case.
177	 */
178	irq_rman.rm_start = 0;
179	irq_rman.rm_type = RMAN_ARRAY;
180	irq_rman.rm_descr = "Interrupt request lines";
181#ifdef APIC_IO
182	irq_rman.rm_end = APIC_INTMAPSIZE - 1;
183#else
184	irq_rman.rm_end = 15;
185#endif
186	if (rman_init(&irq_rman)
187	    || rman_manage_region(&irq_rman,
188				  irq_rman.rm_start, irq_rman.rm_end))
189		panic("nexus_probe irq_rman");
190
191	/*
192	 * ISA DMA on PCI systems is implemented in the ISA part of each
193	 * PCI->ISA bridge and the channels can be duplicated if there are
194	 * multiple bridges.  (eg: laptops with docking stations)
195	 */
196	drq_rman.rm_start = 0;
197#ifdef PC98
198	drq_rman.rm_end = 3;
199#else
200	drq_rman.rm_end = 7;
201#endif
202	drq_rman.rm_type = RMAN_ARRAY;
203	drq_rman.rm_descr = "DMA request lines";
204	/* XXX drq 0 not available on some machines */
205	if (rman_init(&drq_rman)
206	    || rman_manage_region(&drq_rman,
207				  drq_rman.rm_start, drq_rman.rm_end))
208		panic("nexus_probe drq_rman");
209
210	/*
211	 * However, IO ports and Memory truely are global at this level,
212	 * as are APIC interrupts (however many IO APICS there turn out
213	 * to be on large systems..)
214	 */
215	port_rman.rm_start = 0;
216	port_rman.rm_end = 0xffff;
217	port_rman.rm_type = RMAN_ARRAY;
218	port_rman.rm_descr = "I/O ports";
219	if (rman_init(&port_rman)
220	    || rman_manage_region(&port_rman, 0, 0xffff))
221		panic("nexus_probe port_rman");
222
223	mem_rman.rm_start = 0;
224	mem_rman.rm_end = ~0u;
225	mem_rman.rm_type = RMAN_ARRAY;
226	mem_rman.rm_descr = "I/O memory addresses";
227	if (rman_init(&mem_rman)
228	    || rman_manage_region(&mem_rman, 0, ~0))
229		panic("nexus_probe mem_rman");
230
231	return bus_generic_probe(dev);
232}
233
234static int
235nexus_attach(device_t dev)
236{
237	device_t	child;
238
239	/*
240	 * First, deal with the children we know about already
241	 */
242	bus_generic_attach(dev);
243	/*
244	 * And if we didn't see EISA or ISA on a pci bridge, create some
245	 * connection points now so they show up "on motherboard".
246	 */
247	if (!devclass_get_device(devclass_find("eisa"), 0)) {
248		child = device_add_child(dev, "eisa", 0);
249		if (child == NULL)
250			panic("nexus_attach eisa");
251		device_probe_and_attach(child);
252	}
253#if NMCA > 0
254	if (!devclass_get_device(devclass_find("mca"), 0)) {
255        	child = device_add_child(dev, "mca", 0);
256        	if (child == 0)
257                	panic("nexus_probe mca");
258		device_probe_and_attach(child);
259	}
260#endif
261	if (!devclass_get_device(devclass_find("isa"), 0)) {
262		child = device_add_child(dev, "isa", 0);
263		if (child == NULL)
264			panic("nexus_attach isa");
265		device_probe_and_attach(child);
266	}
267
268	return 0;
269}
270
271static int
272nexus_print_resources(struct resource_list *rl, const char *name, int type,
273		      const char *format)
274{
275	struct resource_list_entry *rle;
276	int printed, retval;
277
278	printed = 0;
279	retval = 0;
280	/* Yes, this is kinda cheating */
281	SLIST_FOREACH(rle, rl, link) {
282		if (rle->type == type) {
283			if (printed == 0)
284				retval += printf(" %s ", name);
285			else if (printed > 0)
286				retval += printf(",");
287			printed++;
288			retval += printf(format, rle->start);
289			if (rle->count > 1) {
290				retval += printf("-");
291				retval += printf(format, rle->start +
292						 rle->count - 1);
293			}
294		}
295	}
296	return retval;
297}
298
299static int
300nexus_print_all_resources(device_t dev)
301{
302	struct	nexus_device *ndev = DEVTONX(dev);
303	struct resource_list *rl = &ndev->nx_resources;
304	int retval = 0;
305
306	if (SLIST_FIRST(rl) || ndev->nx_pcibus != -1)
307		retval += printf(" at");
308
309	retval += nexus_print_resources(rl, "port", SYS_RES_IOPORT, "%#lx");
310	retval += nexus_print_resources(rl, "iomem", SYS_RES_MEMORY, "%#lx");
311	retval += nexus_print_resources(rl, "irq", SYS_RES_IRQ, "%ld");
312
313	return retval;
314}
315
316static int
317nexus_print_child(device_t bus, device_t child)
318{
319	struct	nexus_device *ndev = DEVTONX(child);
320	int retval = 0;
321
322	retval += bus_print_child_header(bus, child);
323	retval += nexus_print_all_resources(child);
324	if (ndev->nx_pcibus != -1)
325		retval += printf(" pcibus %d", ndev->nx_pcibus);
326	retval += printf(" on motherboard\n");	/* XXX "motherboard", ick */
327
328	return (retval);
329}
330
331static device_t
332nexus_add_child(device_t bus, int order, const char *name, int unit)
333{
334	device_t		child;
335	struct nexus_device	*ndev;
336
337	ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT);
338	if (!ndev)
339		return(0);
340	bzero(ndev, sizeof(struct nexus_device));
341	resource_list_init(&ndev->nx_resources);
342	ndev->nx_pcibus = -1;
343
344	child = device_add_child_ordered(bus, order, name, unit);
345
346	/* should we free this in nexus_child_detached? */
347	device_set_ivars(child, ndev);
348
349	return(child);
350}
351
352static int
353nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
354{
355	struct	nexus_device *ndev = DEVTONX(child);
356
357	switch (which) {
358	case NEXUS_IVAR_PCIBUS:
359		*result = ndev->nx_pcibus;
360		break;
361	default:
362		return ENOENT;
363	}
364	return 0;
365}
366
367
368static int
369nexus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
370{
371	struct	nexus_device *ndev = DEVTONX(child);
372
373	switch (which) {
374	case NEXUS_IVAR_PCIBUS:
375		ndev->nx_pcibus = value;
376		break;
377	default:
378		return ENOENT;
379	}
380	return 0;
381}
382
383
384/*
385 * Allocate a resource on behalf of child.  NB: child is usually going to be a
386 * child of one of our descendants, not a direct child of nexus0.
387 * (Exceptions include npx.)
388 */
389static struct resource *
390nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
391		     u_long start, u_long end, u_long count, u_int flags)
392{
393	struct nexus_device *ndev = DEVTONX(child);
394	struct	resource *rv;
395	struct resource_list_entry *rle;
396	struct	rman *rm;
397	int needactivate = flags & RF_ACTIVE;
398
399	/*
400	 * If this is an allocation of the "default" range for a given RID, and
401	 * we know what the resources for this device are (ie. they aren't maintained
402	 * by a child bus), then work out the start/end values.
403	 */
404	if ((start == 0UL) && (end == ~0UL) && (count == 1)) {
405		if (ndev == NULL)
406			return(NULL);
407		rle = resource_list_find(&ndev->nx_resources, type, *rid);
408		if (rle == NULL)
409			return(NULL);
410		start = rle->start;
411		end = rle->end;
412		count = rle->count;
413	}
414
415	flags &= ~RF_ACTIVE;
416
417	switch (type) {
418	case SYS_RES_IRQ:
419		rm = &irq_rman;
420		break;
421
422	case SYS_RES_DRQ:
423		rm = &drq_rman;
424		break;
425
426	case SYS_RES_IOPORT:
427		rm = &port_rman;
428		break;
429
430	case SYS_RES_MEMORY:
431		rm = &mem_rman;
432		break;
433
434	default:
435		return 0;
436	}
437
438	rv = rman_reserve_resource(rm, start, end, count, flags, child);
439	if (rv == 0)
440		return 0;
441
442	if (type == SYS_RES_MEMORY) {
443		rman_set_bustag(rv, I386_BUS_SPACE_MEM);
444	} else if (type == SYS_RES_IOPORT) {
445		rman_set_bustag(rv, I386_BUS_SPACE_IO);
446#ifdef PC98
447		/* PC-98: the type of bus_space_handle_t is the structure. */
448		rv->r_bushandle.bsh_base = rv->r_start;
449		rv->r_bushandle.bsh_iat = NULL;
450		rv->r_bushandle.bsh_iatsz = 0;
451		rv->r_bushandle.bsh_res = NULL;
452		rv->r_bushandle.bsh_ressz = 0;
453#else
454		/* IBM-PC: the type of bus_space_handle_t is u_int */
455		rman_set_bushandle(rv, rv->r_start);
456#endif
457	}
458
459	if (needactivate) {
460		if (bus_activate_resource(child, type, *rid, rv)) {
461			rman_release_resource(rv);
462			return 0;
463		}
464	}
465
466	return rv;
467}
468
469static int
470nexus_activate_resource(device_t bus, device_t child, int type, int rid,
471			struct resource *r)
472{
473	/*
474	 * If this is a memory resource, map it into the kernel.
475	 */
476	if (rman_get_bustag(r) == I386_BUS_SPACE_MEM) {
477		caddr_t vaddr = 0;
478
479		if (r->r_end < 1024 * 1024) {
480			/*
481			 * The first 1Mb is mapped at KERNBASE.
482			 */
483			vaddr = (caddr_t)(uintptr_t)(KERNBASE + r->r_start);
484		} else {
485			u_int32_t paddr;
486			u_int32_t psize;
487			u_int32_t poffs;
488
489			paddr = r->r_start;
490			psize = r->r_end - r->r_start;
491
492			poffs = paddr - trunc_page(paddr);
493			vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs;
494		}
495		rman_set_virtual(r, vaddr);
496#ifdef PC98
497		/* PC-98: the type of bus_space_handle_t is the structure. */
498		r->r_bushandle.bsh_base = (bus_addr_t) vaddr;
499		r->r_bushandle.bsh_iat = NULL;
500		r->r_bushandle.bsh_iatsz = 0;
501		r->r_bushandle.bsh_res = NULL;
502		r->r_bushandle.bsh_ressz = 0;
503#else
504		/* IBM-PC: the type of bus_space_handle_t is u_int */
505		rman_set_bushandle(r, (bus_space_handle_t) vaddr);
506#endif
507	}
508	return (rman_activate_resource(r));
509}
510
511static int
512nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
513			  struct resource *r)
514{
515	/*
516	 * If this is a memory resource, unmap it.
517	 */
518	if ((rman_get_bustag(r) == I386_BUS_SPACE_MEM) && (r->r_end >= 1024 * 1024)) {
519		u_int32_t psize;
520
521		psize = r->r_end - r->r_start;
522		pmap_unmapdev((vm_offset_t)rman_get_virtual(r), psize);
523	}
524
525	return (rman_deactivate_resource(r));
526}
527
528static int
529nexus_release_resource(device_t bus, device_t child, int type, int rid,
530		       struct resource *r)
531{
532	if (r->r_flags & RF_ACTIVE) {
533		int error = bus_deactivate_resource(child, type, rid, r);
534		if (error)
535			return error;
536	}
537	return (rman_release_resource(r));
538}
539
540/*
541 * Currently this uses the really grody interface from kern/kern_intr.c
542 * (which really doesn't belong in kern/anything.c).  Eventually, all of
543 * the code in kern_intr.c and machdep_intr.c should get moved here, since
544 * this is going to be the official interface.
545 */
546static int
547nexus_setup_intr(device_t bus, device_t child, struct resource *irq,
548		 int flags, void (*ihand)(void *), void *arg, void **cookiep)
549{
550	driver_t	*driver;
551	int		error, icflags;
552	int 		pri;		/* interrupt thread priority */
553
554	/* somebody tried to setup an irq that failed to allocate! */
555	if (irq == NULL)
556		panic("nexus_setup_intr: NULL irq resource!");
557
558	*cookiep = 0;
559	if (irq->r_flags & RF_SHAREABLE)
560		icflags = 0;
561	else
562		icflags = INTR_EXCL;
563
564	driver = device_get_driver(child);
565	pri = ithread_priority(flags);
566	if (flags & INTR_FAST)
567		icflags |= INTR_FAST;
568
569	/*
570	 * We depend here on rman_activate_resource() being idempotent.
571	 */
572	error = rman_activate_resource(irq);
573	if (error)
574		return (error);
575
576	*cookiep = inthand_add(device_get_nameunit(child), irq->r_start,
577	    ihand, arg, pri, icflags);
578	if (*cookiep == NULL)
579		error = EINVAL;	/* XXX ??? */
580
581	return (error);
582}
583
584static int
585nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
586{
587	return (inthand_remove(ih));
588}
589
590static int
591nexus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count)
592{
593	struct nexus_device	*ndev = DEVTONX(child);
594	struct resource_list	*rl = &ndev->nx_resources;
595
596	/* XXX this should return a success/failure indicator */
597	resource_list_add(rl, type, rid, start, start + count - 1, count);
598	return(0);
599}
600
601static int
602nexus_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp)
603{
604	struct nexus_device	*ndev = DEVTONX(child);
605	struct resource_list	*rl = &ndev->nx_resources;
606	struct resource_list_entry *rle;
607
608	rle = resource_list_find(rl, type, rid);
609	device_printf(child, "type %d  rid %d  startp %p  countp %p - got %p\n",
610		      type, rid, startp, countp, rle);
611	if (!rle)
612		return(ENOENT);
613	if (startp)
614		*startp = rle->start;
615	if (countp)
616		*countp = rle->count;
617	return(0);
618}
619
620static void
621nexus_delete_resource(device_t dev, device_t child, int type, int rid)
622{
623	struct nexus_device	*ndev = DEVTONX(child);
624	struct resource_list	*rl = &ndev->nx_resources;
625
626	resource_list_delete(rl, type, rid);
627}
628
629/*
630 * Placeholder which claims PnP 'devices' which describe system
631 * resources.
632 */
633static struct isa_pnp_id sysresource_ids[] = {
634	{ 0x010cd041 /* PNP0c01 */, "System Memory" },
635	{ 0x020cd041 /* PNP0c02 */, "System Resource" },
636	{ 0 }
637};
638
639static int
640sysresource_probe(device_t dev)
641{
642	int	result;
643
644	if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, sysresource_ids)) <= 0) {
645		device_quiet(dev);
646	}
647	return(result);
648}
649
650static int
651sysresource_attach(device_t dev)
652{
653	return(0);
654}
655
656static device_method_t sysresource_methods[] = {
657	/* Device interface */
658	DEVMETHOD(device_probe,		sysresource_probe),
659	DEVMETHOD(device_attach,	sysresource_attach),
660	DEVMETHOD(device_detach,	bus_generic_detach),
661	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
662	DEVMETHOD(device_suspend,	bus_generic_suspend),
663	DEVMETHOD(device_resume,	bus_generic_resume),
664	{ 0, 0 }
665};
666
667static driver_t sysresource_driver = {
668	"sysresource",
669	sysresource_methods,
670	1,		/* no softc */
671};
672
673static devclass_t sysresource_devclass;
674
675DRIVER_MODULE(sysresource, isa, sysresource_driver, sysresource_devclass, 0, 0);
676