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