isa.c revision 46743
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 *	$Id: isa.c,v 1.124 1999/05/08 21:28:39 peter Exp $
27 */
28
29/*
30 * Modifications for Intel architecture by Garrett A. Wollman.
31 * Copyright 1998 Massachusetts Institute of Technology
32 *
33 * Permission to use, copy, modify, and distribute this software and
34 * its documentation for any purpose and without fee is hereby
35 * granted, provided that both the above copyright notice and this
36 * permission notice appear in all copies, that both the above
37 * copyright notice and this permission notice appear in all
38 * supporting documentation, and that the name of M.I.T. not be used
39 * in advertising or publicity pertaining to distribution of the
40 * software without specific, written prior permission.  M.I.T. makes
41 * no representations about the suitability of this software for any
42 * purpose.  It is provided "as is" without express or implied
43 * warranty.
44 *
45 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
46 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
47 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
48 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
49 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
51 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
52 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
53 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
54 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
55 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 */
58
59#include <sys/param.h>
60#include <sys/systm.h>
61#include <sys/kernel.h>
62#include <sys/bus.h>
63#include <sys/malloc.h>
64#include <sys/module.h>
65#include <machine/bus.h>
66#include <sys/rman.h>
67
68#include <machine/resource.h>
69
70#include <isa/isavar.h>
71
72MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device");
73
74/*
75 * The structure used to attach devices to the isa bus.
76 */
77struct isa_device {
78	int		id_port[ISA_NPORT_IVARS];
79	u_short		id_portsize[ISA_NPORT_IVARS];
80	vm_offset_t	id_maddr[ISA_NMEM_IVARS];
81	vm_size_t	id_msize[ISA_NMEM_IVARS];
82	int		id_irq[ISA_NIRQ_IVARS];
83	int		id_drq[ISA_NDRQ_IVARS];
84	int		id_flags;
85	struct resource	*id_portres[ISA_NPORT_IVARS];
86	struct resource	*id_memres[ISA_NMEM_IVARS];
87	struct resource	*id_irqres[ISA_NIRQ_IVARS];
88	struct resource	*id_drqres[ISA_NDRQ_IVARS];
89};
90
91#define DEVTOISA(dev)	((struct isa_device *) device_get_ivars(dev))
92
93static devclass_t isa_devclass;
94
95static void
96isa_add_device(device_t dev, const char *name, int unit)
97{
98	struct	isa_device *idev;
99	device_t	child;
100	int		sensitive, t;
101	static	device_t last_sensitive;
102
103	/* device-specific flag overrides any wildcard */
104	sensitive = 0;
105	if (resource_int_value(name, unit, "sensitive", &sensitive) != 0)
106		resource_int_value(name, -1, "sensitive", &sensitive);
107
108	idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT);
109	if (!idev)
110		return;
111	bzero(idev, sizeof *idev);
112
113	if (resource_int_value(name, unit, "port", &t) == 0)
114		idev->id_port[0] = t;
115	else
116		idev->id_port[0] = -1;
117	idev->id_port[1] = 0;
118
119	if (resource_int_value(name, unit, "portsize", &t) == 0)
120		idev->id_portsize[0] = t;
121	else
122		idev->id_portsize[0] = 0;
123	idev->id_portsize[1] = 0;
124
125	if (resource_int_value(name, unit, "maddr", &t) == 0)
126		idev->id_maddr[0] = t;
127	else
128		idev->id_maddr[0] = 0;
129	idev->id_maddr[1] = 0;
130
131	if (resource_int_value(name, unit, "msize", &t) == 0)
132		idev->id_msize[0] = t;
133	else
134		idev->id_msize[0] = 0;
135	idev->id_msize[1] = 0;
136
137	if (resource_int_value(name, unit, "flags", &t) == 0)
138		idev->id_flags = t;
139	else
140		idev->id_flags = 0;
141
142	if (resource_int_value(name, unit, "irq", &t) == 0)
143		idev->id_irq[0] = t;
144	else
145		idev->id_irq[0] = -1;
146	idev->id_irq[1] = -1;
147
148	if (resource_int_value(name, unit, "drq", &t) == 0)
149		idev->id_drq[0] = t;
150	else
151		idev->id_drq[0] = -1;
152	idev->id_drq[1] = -1;
153
154	if (sensitive)
155		child = device_add_child_after(dev, last_sensitive, name,
156					       unit, idev);
157	else
158		child = device_add_child(dev, name, unit, idev);
159	if (child == 0)
160		return;
161	else if (sensitive)
162		last_sensitive = child;
163
164	if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0)
165		device_disable(child);
166}
167
168/*
169 * At 'probe' time, we add all the devices which we know about to the
170 * bus.  The generic attach routine will probe and attach them if they
171 * are alive.
172 */
173static int
174isa_probe(device_t dev)
175{
176	int i;
177	static char buf[] = "isaXXX";
178
179	device_set_desc(dev, "ISA bus");
180
181	/*
182	 * Add all devices configured to be attached to isa0.
183	 */
184	sprintf(buf, "isa%d", device_get_unit(dev));
185	for (i = resource_query_string(-1, "at", buf);
186	     i != -1;
187	     i = resource_query_string(i, "at", buf)) {
188		if (strcmp(resource_query_name(i), "atkbd") == 0)
189			continue;	/* old GENERIC kludge */
190		isa_add_device(dev, resource_query_name(i),
191			       resource_query_unit(i));
192	}
193
194	/*
195	 * and isa?
196	 */
197	for (i = resource_query_string(-1, "at", "isa");
198	     i != -1;
199	     i = resource_query_string(i, "at", "isa")) {
200		if (strcmp(resource_query_name(i), "atkbd") == 0)
201			continue;	/* old GENERIC kludge */
202		isa_add_device(dev, resource_query_name(i),
203			       resource_query_unit(i));
204	}
205
206	isa_wrap_old_drivers();
207
208	return 0;
209}
210
211extern device_t isa_bus_device;
212
213static int
214isa_attach(device_t dev)
215{
216	/*
217	 * Arrange for bus_generic_attach(dev) to be called later.
218	 */
219	isa_bus_device = dev;
220	return 0;
221}
222
223static void
224isa_print_child(device_t bus, device_t dev)
225{
226	struct	isa_device *id = DEVTOISA(dev);
227
228	if (id->id_port[0] > 0 || id->id_port[1] > 0
229	    || id->id_maddr[0] > 0 || id->id_maddr[1] > 0
230	    || id->id_irq[0] >= 0 || id->id_irq[1] >= 0
231	    || id->id_drq[0] >= 0 || id->id_drq[1] >= 0)
232		printf(" at");
233	if (id->id_port[0] > 0 && id->id_port[1] > 0) {
234		printf(" ports %#x", (u_int)id->id_port[0]);
235		if (id->id_portsize[0] > 1)
236			printf("-%#x", (u_int)(id->id_port[0]
237					       + id->id_portsize[0] - 1));
238		printf(" and %#x", (u_int)id->id_port[1]);
239		if (id->id_portsize[1] > 1)
240			printf("-%#x", (u_int)(id->id_port[1]
241					       + id->id_portsize[1] - 1));
242	} else if (id->id_port[0] > 0) {
243		printf(" port %#x", (u_int)id->id_port[0]);
244		if (id->id_portsize[0] > 1)
245			printf("-%#x", (u_int)(id->id_port[0]
246					       + id->id_portsize[0] - 1));
247	} else if (id->id_port[1] > 0) {
248		printf(" port %#x", (u_int)id->id_port[1]);
249		if (id->id_portsize[1] > 1)
250			printf("-%#x", (u_int)(id->id_port[1]
251					       + id->id_portsize[1] - 1));
252	}
253	if (id->id_maddr[0] && id->id_maddr[1]) {
254		printf(" iomem %#x", (u_int)id->id_maddr[0]);
255		if (id->id_msize[0])
256			printf("-%#x", (u_int)(id->id_maddr[0]
257					       + id->id_msize[0] - 1));
258		printf(" and %#x", (u_int)id->id_maddr[1]);
259		if (id->id_msize[1])
260			printf("-%#x", (u_int)(id->id_maddr[1]
261					       + id->id_msize[1] - 1));
262	} else if (id->id_maddr[0]) {
263		printf(" iomem %#x", (u_int)id->id_maddr[0]);
264		if (id->id_msize[0])
265			printf("-%#x", (u_int)(id->id_maddr[0]
266					       + id->id_msize[0] - 1));
267	} else if (id->id_maddr[1]) {
268		printf(" iomem %#x", (u_int)id->id_maddr[1]);
269		if (id->id_msize[1])
270			printf("-%#x", (u_int)(id->id_maddr[1]
271					       + id->id_msize[1] - 1));
272	}
273	if (id->id_irq[0] >= 0 && id->id_irq[1] >= 0)
274		printf(" irqs %d and %d", id->id_irq[0], id->id_irq[1]);
275	else if (id->id_irq[0] >= 0)
276		printf(" irq %d", id->id_irq[0]);
277	else if (id->id_irq[1] >= 0)
278		printf(" irq %d", id->id_irq[1]);
279	if (id->id_drq[0] >= 0 && id->id_drq[1] >= 0)
280		printf(" drqs %d and %d", id->id_drq[0], id->id_drq[1]);
281	else if (id->id_drq[0] >= 0)
282		printf(" drq %d", id->id_drq[0]);
283	else if (id->id_drq[1] >= 0)
284		printf(" drq %d", id->id_drq[1]);
285
286	if (id->id_flags)
287		printf(" flags %#x", id->id_flags);
288
289	printf(" on %s%d",
290	       device_get_name(bus), device_get_unit(bus));
291}
292
293static int
294isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result)
295{
296	struct isa_device* idev = DEVTOISA(dev);
297
298	switch (index) {
299	case ISA_IVAR_PORT_0:
300		*result = idev->id_port[0];
301		break;
302	case ISA_IVAR_PORT_1:
303		*result = idev->id_port[1];
304		break;
305	case ISA_IVAR_PORTSIZE_0:
306		*result = idev->id_portsize[0];
307		break;
308	case ISA_IVAR_PORTSIZE_1:
309		*result = idev->id_portsize[1];
310		break;
311	case ISA_IVAR_MADDR_0:
312		*result = idev->id_maddr[0];
313		break;
314	case ISA_IVAR_MADDR_1:
315		*result = idev->id_maddr[1];
316		break;
317	case ISA_IVAR_MSIZE_0:
318		*result = idev->id_msize[0];
319		break;
320	case ISA_IVAR_MSIZE_1:
321		*result = idev->id_msize[1];
322		break;
323	case ISA_IVAR_IRQ_0:
324		*result = idev->id_irq[0];
325		break;
326	case ISA_IVAR_IRQ_1:
327		*result = idev->id_irq[1];
328		break;
329	case ISA_IVAR_DRQ_0:
330		*result = idev->id_drq[0];
331		break;
332	case ISA_IVAR_DRQ_1:
333		*result = idev->id_drq[1];
334		break;
335	case ISA_IVAR_FLAGS:
336		*result = idev->id_flags;
337		break;
338	}
339	return ENOENT;
340}
341
342/*
343 * XXX -- this interface is pretty much irrelevant in the presence of
344 * BUS_ALLOC_RESOURCE / BUS_RELEASE_RESOURCE (at least for the ivars which
345 * are defined at this point).
346 */
347static int
348isa_write_ivar(device_t bus, device_t dev,
349	       int index, uintptr_t value)
350{
351	struct isa_device* idev = DEVTOISA(dev);
352
353	switch (index) {
354	case ISA_IVAR_PORT_0:
355		idev->id_port[0] = value;
356		break;
357	case ISA_IVAR_PORT_1:
358		idev->id_port[1] = value;
359		break;
360	case ISA_IVAR_PORTSIZE_0:
361		idev->id_portsize[0] = value;
362		break;
363	case ISA_IVAR_PORTSIZE_1:
364		idev->id_portsize[1] = value;
365		break;
366	case ISA_IVAR_MADDR_0:
367		idev->id_maddr[0] = value;
368		break;
369	case ISA_IVAR_MADDR_1:
370		idev->id_maddr[1] = value;
371		break;
372	case ISA_IVAR_MSIZE_0:
373		idev->id_msize[0] = value;
374		break;
375	case ISA_IVAR_MSIZE_1:
376		idev->id_msize[1] = value;
377		break;
378	case ISA_IVAR_IRQ_0:
379		idev->id_irq[0] = value;
380		break;
381	case ISA_IVAR_IRQ_1:
382		idev->id_irq[1] = value;
383		break;
384	case ISA_IVAR_DRQ_0:
385		idev->id_drq[0] = value;
386		break;
387	case ISA_IVAR_DRQ_1:
388		idev->id_drq[1] = value;
389		break;
390	case ISA_IVAR_FLAGS:
391		idev->id_flags = value;
392		break;
393	default:
394		return (ENOENT);
395	}
396	return (0);
397}
398
399/*
400 * This implementation simply passes the request up to the parent
401 * bus, which in our case is the special i386 nexus, substituting any
402 * configured values if the caller defaulted.  We can get away with
403 * this because there is no special mapping for ISA resources on an Intel
404 * platform.  When porting this code to another architecture, it may be
405 * necessary to interpose a mapping layer here.
406 */
407static struct resource *
408isa_alloc_resource(device_t bus, device_t child, int type, int *rid,
409		   u_long start, u_long end, u_long count, u_int flags)
410{
411	int	isdefault;
412	struct	resource *rv, **rvp = 0;
413	struct	isa_device *id = DEVTOISA(child);
414
415	if (child) {
416		/*
417		 * If this is our child, then use the isa_device to find
418		 * defaults and to record results.
419		 */
420		if (device_get_devclass(device_get_parent(child)) == isa_devclass)
421			id = DEVTOISA(child);
422		else
423			id = NULL;
424	} else
425		id = NULL;
426	isdefault = (id != NULL && start == 0UL && end == ~0UL && *rid == 0);
427	if (*rid > 1)
428		return 0;
429
430	switch (type) {
431	case SYS_RES_IRQ:
432		if (isdefault && id->id_irq[0] >= 0) {
433			start = id->id_irq[0];
434			end = id->id_irq[0];
435			count = 1;
436		}
437		if (id)
438			rvp = &id->id_irqres[*rid];
439		break;
440
441	case SYS_RES_DRQ:
442		if (isdefault && id->id_drq[0] >= 0) {
443			start = id->id_drq[0];
444			end = id->id_drq[0];
445			count = 1;
446		}
447		if (id)
448			rvp = &id->id_drqres[*rid];
449		break;
450
451	case SYS_RES_MEMORY:
452		if (isdefault && id->id_maddr[0]) {
453			start = id->id_maddr[0];
454			count = max(count, (u_long)id->id_msize[0]);
455			end = id->id_maddr[0] + count;
456		}
457		if (id)
458			rvp = &id->id_memres[*rid];
459		break;
460
461	case SYS_RES_IOPORT:
462		if (isdefault && id->id_port[0]) {
463			start = id->id_port[0];
464			count = max(count, (u_long)id->id_portsize[0]);
465			end = id->id_port[0] + count;
466		}
467		if (id)
468			rvp = &id->id_portres[*rid];
469		break;
470
471	default:
472		return 0;
473	}
474
475	/*
476	 * If the client attempts to reallocate a resource without
477	 * releasing what was there previously, die horribly so that
478	 * he knows how he !@#$ed up.
479	 */
480	if (rvp && *rvp != 0)
481		panic("%s%d: (%d, %d) not free for %s%d\n",
482		      device_get_name(bus), device_get_unit(bus),
483		      type, *rid,
484		      device_get_name(child), device_get_unit(child));
485
486	/*
487	 * nexus_alloc_resource had better not change *rid...
488	 */
489	rv = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid,
490				start, end, count, flags);
491	if (rvp && (*rvp = rv) != 0) {
492		switch (type) {
493		case SYS_RES_MEMORY:
494			id->id_maddr[*rid] = rv->r_start;
495			id->id_msize[*rid] = count;
496			break;
497		case SYS_RES_IOPORT:
498			id->id_port[*rid] = rv->r_start;
499			id->id_portsize[*rid] = count;
500			break;
501		case SYS_RES_IRQ:
502			id->id_irq[*rid] = rv->r_start;
503			break;
504		case SYS_RES_DRQ:
505			id->id_drq[*rid] = rv->r_start;
506			break;
507		}
508	}
509	return rv;
510}
511
512static int
513isa_release_resource(device_t bus, device_t child, int type, int rid,
514		     struct resource *r)
515{
516	int	rv;
517	struct	isa_device *id = DEVTOISA(child);
518
519	if (rid > 1)
520		return EINVAL;
521
522	switch (type) {
523	case SYS_RES_IRQ:
524	case SYS_RES_DRQ:
525	case SYS_RES_IOPORT:
526	case SYS_RES_MEMORY:
527		break;
528	default:
529		return (ENOENT);
530	}
531
532	rv = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r);
533
534	if (rv == 0) {
535		switch (type) {
536		case SYS_RES_IRQ:
537			id->id_irqres[rid] = 0;
538			break;
539
540		case SYS_RES_DRQ:
541			id->id_drqres[rid] = 0;
542			break;
543
544		case SYS_RES_MEMORY:
545			id->id_memres[rid] = 0;
546			break;
547
548		case SYS_RES_IOPORT:
549			id->id_portres[rid] = 0;
550			break;
551
552		default:
553			return ENOENT;
554		}
555	}
556
557	return rv;
558}
559
560/*
561 * We can't use the bus_generic_* versions of these methods because those
562 * methods always pass the bus param as the requesting device, and we need
563 * to pass the child (the i386 nexus knows about this and is prepared to
564 * deal).
565 */
566static int
567isa_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
568	       void (*ihand)(void *), void *arg, void **cookiep)
569{
570	return (BUS_SETUP_INTR(device_get_parent(bus), child, r, flags,
571			       ihand, arg, cookiep));
572}
573
574static int
575isa_teardown_intr(device_t bus, device_t child, struct resource *r,
576		  void *cookie)
577{
578	return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, cookie));
579}
580
581static device_method_t isa_methods[] = {
582	/* Device interface */
583	DEVMETHOD(device_probe,		isa_probe),
584	DEVMETHOD(device_attach,	isa_attach),
585	DEVMETHOD(device_detach,	bus_generic_detach),
586	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
587	DEVMETHOD(device_suspend,	bus_generic_suspend),
588	DEVMETHOD(device_resume,	bus_generic_resume),
589
590	/* Bus interface */
591	DEVMETHOD(bus_print_child,	isa_print_child),
592	DEVMETHOD(bus_read_ivar,	isa_read_ivar),
593	DEVMETHOD(bus_write_ivar,	isa_write_ivar),
594	DEVMETHOD(bus_alloc_resource,	isa_alloc_resource),
595	DEVMETHOD(bus_release_resource,	isa_release_resource),
596	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
597	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
598	DEVMETHOD(bus_setup_intr,	isa_setup_intr),
599	DEVMETHOD(bus_teardown_intr,	isa_teardown_intr),
600
601	{ 0, 0 }
602};
603
604static driver_t isa_driver = {
605	"isa",
606	isa_methods,
607	1,			/* no softc */
608};
609
610/*
611 * ISA can be attached to a PCI-ISA bridge or directly to the nexus.
612 */
613DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0);
614DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0);
615