isa.c revision 45985
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.120 1999/04/16 23:39:15 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	short		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	if (resource_int_value(name, unit, "sensitive", &sensitive) != 0)
104		sensitive = 0;
105
106	idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT);
107	if (!idev)
108		return;
109	bzero(idev, sizeof *idev);
110
111	if (resource_int_value(name, unit, "port", &t) == 0)
112		idev->id_port[0] = t;
113	else
114		idev->id_port[0] = -1;
115	idev->id_port[1] = 0;
116
117	if (resource_int_value(name, unit, "portsize", &t) == 0)
118		idev->id_portsize[0] = t;
119	else
120		idev->id_portsize[0] = 0;
121	idev->id_portsize[1] = 0;
122
123	if (resource_int_value(name, unit, "maddr", &t) == 0)
124		idev->id_maddr[0] = t;
125	else
126		idev->id_maddr[0] = 0;
127	idev->id_maddr[1] = 0;
128
129	if (resource_int_value(name, unit, "msize", &t) == 0)
130		idev->id_msize[0] = t;
131	else
132		idev->id_msize[0] = 0;
133	idev->id_msize[1] = 0;
134
135	if (resource_int_value(name, unit, "flags", &t) == 0)
136		idev->id_flags = t;
137	else
138		idev->id_flags = 0;
139
140	if (resource_int_value(name, unit, "irq", &t) == 0)
141		idev->id_irq[0] = t;
142	else
143		idev->id_irq[0] = -1;
144	idev->id_irq[1] = -1;
145
146	if (resource_int_value(name, unit, "drq", &t) == 0)
147		idev->id_drq[0] = t;
148	else
149		idev->id_drq[0] = -1;
150	idev->id_drq[1] = -1;
151
152	if (sensitive)
153		child = device_add_child_after(dev, last_sensitive, name,
154					       unit, idev);
155	else
156		child = device_add_child(dev, name, unit, idev);
157	if (child == 0)
158		return;
159	else if (sensitive)
160		last_sensitive = child;
161
162	if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0)
163		device_disable(child);
164}
165
166/*
167 * At 'probe' time, we add all the devices which we know about to the
168 * bus.  The generic attach routine will probe and attach them if they
169 * are alive.
170 */
171static int
172isa_probe(device_t dev)
173{
174	int i;
175	static char buf[] = "isaXXX";
176
177	device_set_desc(dev, "ISA bus");
178
179	/*
180	 * Add all devices configured to be attached to isa0.
181	 */
182	sprintf(buf, "isa%d", device_get_unit(dev));
183	for (i = resource_query_string(-1, "at", buf);
184	     i != -1;
185	     i = resource_query_string(i, "at", buf)) {
186		if (strcmp(resource_query_name(i), "atkbd") == 0)
187			continue;	/* old GENERIC kludge */
188		isa_add_device(dev, resource_query_name(i),
189			       resource_query_unit(i));
190	}
191
192	/*
193	 * and isa?
194	 */
195	for (i = resource_query_string(-1, "at", "isa");
196	     i != -1;
197	     i = resource_query_string(i, "at", "isa")) {
198		if (strcmp(resource_query_name(i), "atkbd") == 0)
199			continue;	/* old GENERIC kludge */
200		isa_add_device(dev, resource_query_name(i),
201			       resource_query_unit(i));
202	}
203
204	isa_wrap_old_drivers();
205
206	return 0;
207}
208
209extern device_t isa_bus_device;
210
211static int
212isa_attach(device_t dev)
213{
214	/*
215	 * Arrange for bus_generic_attach(dev) to be called later.
216	 */
217	isa_bus_device = dev;
218	return 0;
219}
220
221static void
222isa_print_child(device_t bus, device_t dev)
223{
224	struct	isa_device *id = DEVTOISA(dev);
225
226	if (id->id_port[0] > 0 || id->id_port[1] > 0
227	    || id->id_maddr[0] > 0 || id->id_maddr[1] > 0
228	    || id->id_irq[0] >= 0 || id->id_irq[1] >= 0
229	    || id->id_drq[0] >= 0 || id->id_drq[1] >= 0)
230		printf(" at");
231	if (id->id_port[0] > 0 && id->id_port[1] > 0) {
232		printf(" ports %#x", (u_int)id->id_port[0]);
233		if (id->id_portsize[0] > 1)
234			printf("-%#x", (u_int)(id->id_port[0]
235					       + id->id_portsize[0] - 1));
236		printf(" and %#x", (u_int)id->id_port[1]);
237		if (id->id_portsize[1] > 1)
238			printf("-%#x", (u_int)(id->id_port[1]
239					       + id->id_portsize[1] - 1));
240	} else if (id->id_port[0] > 0) {
241		printf(" port %#x", (u_int)id->id_port[0]);
242		if (id->id_portsize[0] > 1)
243			printf("-%#x", (u_int)(id->id_port[0]
244					       + id->id_portsize[0] - 1));
245	} else if (id->id_port[1] > 0) {
246		printf(" port %#x", (u_int)id->id_port[1]);
247		if (id->id_portsize[1] > 1)
248			printf("-%#x", (u_int)(id->id_port[1]
249					       + id->id_portsize[1] - 1));
250	}
251	if (id->id_maddr[0] && id->id_maddr[1]) {
252		printf(" iomem %#x", (u_int)id->id_maddr[0]);
253		if (id->id_msize[0])
254			printf("-%#x", (u_int)(id->id_maddr[0]
255					       + id->id_msize[0] - 1));
256		printf(" and %#x", (u_int)id->id_maddr[1]);
257		if (id->id_msize[1])
258			printf("-%#x", (u_int)(id->id_maddr[1]
259					       + id->id_msize[1] - 1));
260	} else if (id->id_maddr[0]) {
261		printf(" iomem %#x", (u_int)id->id_maddr[0]);
262		if (id->id_msize[0])
263			printf("-%#x", (u_int)(id->id_maddr[0]
264					       + id->id_msize[0] - 1));
265	} else if (id->id_maddr[1]) {
266		printf(" iomem %#x", (u_int)id->id_maddr[1]);
267		if (id->id_msize[1])
268			printf("-%#x", (u_int)(id->id_maddr[1]
269					       + id->id_msize[1] - 1));
270	}
271	if (id->id_irq[0] >= 0 && id->id_irq[1] >= 0)
272		printf(" irqs %d and %d", id->id_irq[0], id->id_irq[1]);
273	else if (id->id_irq[0] >= 0)
274		printf(" irq %d", id->id_irq[0]);
275	else if (id->id_irq[1] >= 0)
276		printf(" irq %d", id->id_irq[1]);
277	if (id->id_drq[0] >= 0 && id->id_drq[1] >= 0)
278		printf(" drqs %d and %d", id->id_drq[0], id->id_drq[1]);
279	else if (id->id_drq[0] >= 0)
280		printf(" drq %d", id->id_drq[0]);
281	else if (id->id_drq[1] >= 0)
282		printf(" drq %d", id->id_drq[1]);
283
284	if (id->id_flags)
285		printf(" flags %#x", id->id_flags);
286
287	printf(" on %s%d",
288	       device_get_name(bus), device_get_unit(bus));
289}
290
291static int
292isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result)
293{
294	struct isa_device* idev = DEVTOISA(dev);
295
296	switch (index) {
297	case ISA_IVAR_PORT_0:
298		*result = idev->id_port[0];
299		break;
300	case ISA_IVAR_PORT_1:
301		*result = idev->id_port[1];
302		break;
303	case ISA_IVAR_PORTSIZE_0:
304		*result = idev->id_portsize[0];
305		break;
306	case ISA_IVAR_PORTSIZE_1:
307		*result = idev->id_portsize[1];
308		break;
309	case ISA_IVAR_MADDR_0:
310		*result = idev->id_maddr[0];
311		break;
312	case ISA_IVAR_MADDR_1:
313		*result = idev->id_maddr[1];
314		break;
315	case ISA_IVAR_MSIZE_0:
316		*result = idev->id_msize[0];
317		break;
318	case ISA_IVAR_MSIZE_1:
319		*result = idev->id_msize[1];
320		break;
321	case ISA_IVAR_IRQ_0:
322		*result = idev->id_irq[0];
323		break;
324	case ISA_IVAR_IRQ_1:
325		*result = idev->id_irq[1];
326		break;
327	case ISA_IVAR_DRQ_0:
328		*result = idev->id_drq[0];
329		break;
330	case ISA_IVAR_DRQ_1:
331		*result = idev->id_drq[1];
332		break;
333	case ISA_IVAR_FLAGS:
334		*result = idev->id_flags;
335		break;
336	}
337	return ENOENT;
338}
339
340/*
341 * XXX -- this interface is pretty much irrelevant in the presence of
342 * BUS_ALLOC_RESOURCE / BUS_RELEASE_RESOURCE (at least for the ivars which
343 * are defined at this point).
344 */
345static int
346isa_write_ivar(device_t bus, device_t dev,
347	       int index, uintptr_t value)
348{
349	struct isa_device* idev = DEVTOISA(dev);
350
351	switch (index) {
352	case ISA_IVAR_PORT_0:
353		idev->id_port[0] = value;
354		break;
355	case ISA_IVAR_PORT_1:
356		idev->id_port[1] = value;
357		break;
358	case ISA_IVAR_PORTSIZE_0:
359		idev->id_portsize[0] = value;
360		break;
361	case ISA_IVAR_PORTSIZE_1:
362		idev->id_portsize[1] = value;
363		break;
364	case ISA_IVAR_MADDR_0:
365		idev->id_maddr[0] = value;
366		break;
367	case ISA_IVAR_MADDR_1:
368		idev->id_maddr[1] = value;
369		break;
370	case ISA_IVAR_MSIZE_0:
371		idev->id_msize[0] = value;
372		break;
373	case ISA_IVAR_MSIZE_1:
374		idev->id_msize[1] = value;
375		break;
376	case ISA_IVAR_IRQ_0:
377		idev->id_irq[0] = value;
378		break;
379	case ISA_IVAR_IRQ_1:
380		idev->id_irq[1] = value;
381		break;
382	case ISA_IVAR_DRQ_0:
383		idev->id_drq[0] = value;
384		break;
385	case ISA_IVAR_DRQ_1:
386		idev->id_drq[1] = value;
387		break;
388	case ISA_IVAR_FLAGS:
389		idev->id_flags = value;
390		break;
391	default:
392		return (ENOENT);
393	}
394	return (0);
395}
396
397/*
398 * This implementation simply passes the request up to the parent
399 * bus, which in our case is the special i386 nexus, substituting any
400 * configured values if the caller defaulted.  We can get away with
401 * this because there is no special mapping for ISA resources on an Intel
402 * platform.  When porting this code to another architecture, it may be
403 * necessary to interpose a mapping layer here.
404 */
405static struct resource *
406isa_alloc_resource(device_t bus, device_t child, int type, int *rid,
407		   u_long start, u_long end, u_long count, u_int flags)
408{
409	int	isdefault;
410	struct	resource *rv, **rvp = 0;
411	struct	isa_device *id = DEVTOISA(child);
412
413	if (child) {
414		/*
415		 * If this is our child, then use the isa_device to find
416		 * defaults and to record results.
417		 */
418		if (device_get_devclass(device_get_parent(child)) == isa_devclass)
419			id = DEVTOISA(child);
420		else
421			id = NULL;
422	} else
423		id = NULL;
424	isdefault = (id != NULL && start == 0UL && end == ~0UL && *rid == 0);
425	if (*rid > 1)
426		return 0;
427
428	switch (type) {
429	case SYS_RES_IRQ:
430		if (isdefault && id->id_irq[0] >= 0) {
431			start = id->id_irq[0];
432			end = id->id_irq[0];
433			count = 1;
434		}
435		if (id)
436			rvp = &id->id_irqres[*rid];
437		break;
438
439	case SYS_RES_DRQ:
440		if (isdefault && id->id_drq[0] >= 0) {
441			start = id->id_drq[0];
442			end = id->id_drq[0];
443			count = 1;
444		}
445		if (id)
446			rvp = &id->id_drqres[*rid];
447		break;
448
449	case SYS_RES_MEMORY:
450		if (isdefault && id->id_maddr[0]) {
451			start = id->id_maddr[0];
452			count = max(count, (u_long)id->id_msize[0]);
453			end = id->id_maddr[0] + count;
454		}
455		if (id)
456			rvp = &id->id_memres[*rid];
457		break;
458
459	case SYS_RES_IOPORT:
460		if (isdefault && id->id_port[0]) {
461			start = id->id_port[0];
462			count = max(count, (u_long)id->id_portsize[0]);
463			end = id->id_port[0] + count;
464		}
465		if (id)
466			rvp = &id->id_portres[*rid];
467		break;
468
469	default:
470		return 0;
471	}
472
473	/*
474	 * If the client attempts to reallocate a resource without
475	 * releasing what was there previously, die horribly so that
476	 * he knows how he !@#$ed up.
477	 */
478	if (rvp && *rvp != 0)
479		panic("%s%d: (%d, %d) not free for %s%d\n",
480		      device_get_name(bus), device_get_unit(bus),
481		      type, *rid,
482		      device_get_name(child), device_get_unit(child));
483
484	/*
485	 * nexus_alloc_resource had better not change *rid...
486	 */
487	rv = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid,
488				start, end, count, flags);
489	if (rvp && (*rvp = rv) != 0) {
490		switch (type) {
491		case SYS_RES_MEMORY:
492			id->id_maddr[*rid] = rv->r_start;
493			id->id_msize[*rid] = count;
494			break;
495		case SYS_RES_IOPORT:
496			id->id_port[*rid] = rv->r_start;
497			id->id_portsize[*rid] = count;
498			break;
499		case SYS_RES_IRQ:
500			id->id_irq[*rid] = rv->r_start;
501			break;
502		case SYS_RES_DRQ:
503			id->id_drq[*rid] = rv->r_start;
504			break;
505		}
506	}
507	return rv;
508}
509
510static int
511isa_release_resource(device_t bus, device_t child, int type, int rid,
512		     struct resource *r)
513{
514	int	rv;
515	struct	isa_device *id = DEVTOISA(child);
516
517	if (rid > 1)
518		return EINVAL;
519
520	switch (type) {
521	case SYS_RES_IRQ:
522	case SYS_RES_DRQ:
523	case SYS_RES_IOPORT:
524	case SYS_RES_MEMORY:
525		break;
526	default:
527		return (ENOENT);
528	}
529
530	rv = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r);
531
532#if 0
533	if (rv) {
534		/* Kludge, isa as a child of pci doesn't have mapping regs */
535		printf("WARNING: isa_release_resource: BUS_RELEASE_RESOURCE() failed: %d\n", rv);
536		rv = 0;
537	}
538#endif
539
540	if (rv == 0) {
541		switch (type) {
542		case SYS_RES_IRQ:
543			id->id_irqres[rid] = 0;
544			break;
545
546		case SYS_RES_DRQ:
547			id->id_drqres[rid] = 0;
548			break;
549
550		case SYS_RES_MEMORY:
551			id->id_memres[rid] = 0;
552			break;
553
554		case SYS_RES_IOPORT:
555			id->id_portres[rid] = 0;
556			break;
557
558		default:
559			return ENOENT;
560		}
561	}
562
563	return rv;
564}
565
566/*
567 * We can't use the bus_generic_* versions of these methods because those
568 * methods always pass the bus param as the requesting device, and we need
569 * to pass the child (the i386 nexus knows about this and is prepared to
570 * deal).
571 */
572static int
573isa_setup_intr(device_t bus, device_t child, struct resource *r,
574	       void (*ihand)(void *), void *arg, void **cookiep)
575{
576	return (BUS_SETUP_INTR(device_get_parent(bus), child, r, ihand, arg,
577			       cookiep));
578}
579
580static int
581isa_teardown_intr(device_t bus, device_t child, struct resource *r,
582		  void *cookie)
583{
584	return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, cookie));
585}
586
587static device_method_t isa_methods[] = {
588	/* Device interface */
589	DEVMETHOD(device_probe,		isa_probe),
590	DEVMETHOD(device_attach,	isa_attach),
591	DEVMETHOD(device_detach,	bus_generic_detach),
592	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
593	DEVMETHOD(device_suspend,	bus_generic_suspend),
594	DEVMETHOD(device_resume,	bus_generic_resume),
595
596	/* Bus interface */
597	DEVMETHOD(bus_print_child,	isa_print_child),
598	DEVMETHOD(bus_read_ivar,	isa_read_ivar),
599	DEVMETHOD(bus_write_ivar,	isa_write_ivar),
600	DEVMETHOD(bus_alloc_resource,	isa_alloc_resource),
601	DEVMETHOD(bus_release_resource,	isa_release_resource),
602	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
603	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
604	DEVMETHOD(bus_setup_intr,	isa_setup_intr),
605	DEVMETHOD(bus_teardown_intr,	isa_teardown_intr),
606
607	{ 0, 0 }
608};
609
610static driver_t isa_driver = {
611	"isa",
612	isa_methods,
613	DRIVER_TYPE_MISC,
614	1,			/* no softc */
615};
616
617/*
618 * ISA can be attached to a PCI-ISA bridge or directly to the nexus.
619 */
620DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0);
621DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0);
622