sbc.c revision 61061
1/*-
2 * Copyright (c) 1999 Seigo Tanimura
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 * $FreeBSD: head/sys/dev/sound/isa/sbc.c 61061 2000-05-29 07:43:51Z kuriyama $
27 */
28
29#include "isa.h"
30
31#include <dev/sound/chip.h>
32#include <dev/sound/pcm/sound.h>
33#include  <dev/sound/isa/sb.h>
34
35#define IO_MAX	3
36#define IRQ_MAX	1
37#define DRQ_MAX	2
38#define INTR_MAX	2
39
40struct sbc_ihl {
41	driver_intr_t *intr[INTR_MAX];
42	void *intr_arg[INTR_MAX];
43};
44
45/* Here is the parameter structure per a device. */
46struct sbc_softc {
47	device_t dev; /* device */
48
49	int io_rid[IO_MAX]; /* io port rids */
50	struct resource *io[IO_MAX]; /* io port resources */
51	int io_alloced[IO_MAX]; /* io port alloc flag */
52
53	int irq_rid[IRQ_MAX]; /* irq rids */
54	struct resource *irq[IRQ_MAX]; /* irq resources */
55	int irq_alloced[IRQ_MAX]; /* irq alloc flag */
56
57	int drq_rid[DRQ_MAX]; /* drq rids */
58	struct resource *drq[DRQ_MAX]; /* drq resources */
59	int drq_alloced[DRQ_MAX]; /* drq alloc flag */
60
61	struct sbc_ihl ihl[IRQ_MAX];
62
63	void *ih;
64
65	u_int32_t bd_ver;
66};
67
68static int sbc_probe(device_t dev);
69static int sbc_attach(device_t dev);
70static void sbc_intr(void *p);
71
72static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
73					   u_long start, u_long end, u_long count, u_int flags);
74static int sbc_release_resource(device_t bus, device_t child, int type, int rid,
75				struct resource *r);
76static int sbc_setup_intr(device_t dev, device_t child, struct resource *irq,
77   	       int flags, driver_intr_t *intr, void *arg,
78   	       void **cookiep);
79static int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq,
80  		  void *cookie);
81
82static int alloc_resource(struct sbc_softc *scp);
83static int release_resource(struct sbc_softc *scp);
84
85static devclass_t sbc_devclass;
86
87static int io_range[3] = {0x10, 0x2, 0x4};
88
89#ifdef PC98 /* I/O address table for PC98 */
90static bus_addr_t pcm_iat[] = {
91	0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700,
92	0x800, 0x900, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00
93};
94static bus_addr_t midi_iat[] = {
95	0x000, 0x100
96};
97static bus_addr_t opl_iat[] = {
98	0x000, 0x100, 0x200, 0x300
99};
100static bus_addr_t *sb_iat[] = { pcm_iat, midi_iat, opl_iat };
101#endif
102
103static int sb_rd(struct resource *io, int reg);
104static void sb_wr(struct resource *io, int reg, u_int8_t val);
105static int sb_dspready(struct resource *io);
106static int sb_cmd(struct resource *io, u_char val);
107static u_int sb_get_byte(struct resource *io);
108static void sb_setmixer(struct resource *io, u_int port, u_int value);
109
110static int
111sb_rd(struct resource *io, int reg)
112{
113	return bus_space_read_1(rman_get_bustag(io),
114				rman_get_bushandle(io),
115				reg);
116}
117
118static void
119sb_wr(struct resource *io, int reg, u_int8_t val)
120{
121	return bus_space_write_1(rman_get_bustag(io),
122				 rman_get_bushandle(io),
123				 reg, val);
124}
125
126static int
127sb_dspready(struct resource *io)
128{
129	return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0);
130}
131
132static int
133sb_dspwr(struct resource *io, u_char val)
134{
135    	int  i;
136
137    	for (i = 0; i < 1000; i++) {
138		if (sb_dspready(io)) {
139	    		sb_wr(io, SBDSP_CMD, val);
140	    		return 1;
141		}
142		if (i > 10) DELAY((i > 100)? 1000 : 10);
143    	}
144    	printf("sb_dspwr(0x%02x) timed out.\n", val);
145    	return 0;
146}
147
148static int
149sb_cmd(struct resource *io, u_char val)
150{
151    	return sb_dspwr(io, val);
152}
153
154static void
155sb_setmixer(struct resource *io, u_int port, u_int value)
156{
157    	u_long   flags;
158
159    	flags = spltty();
160    	sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
161    	DELAY(10);
162    	sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff));
163    	DELAY(10);
164    	splx(flags);
165}
166
167static u_int
168sb_get_byte(struct resource *io)
169{
170    	int i;
171
172    	for (i = 1000; i > 0; i--) {
173		if (sb_rd(io, DSP_DATA_AVAIL) & 0x80)
174			return sb_rd(io, DSP_READ);
175		else
176			DELAY(20);
177    	}
178    	return 0xffff;
179}
180
181static int
182sb_reset_dsp(struct resource *io)
183{
184    	sb_wr(io, SBDSP_RST, 3);
185    	DELAY(100);
186    	sb_wr(io, SBDSP_RST, 0);
187    	return (sb_get_byte(io) == 0xAA)? 0 : ENXIO;
188}
189
190static int
191sb_identify_board(struct resource *io)
192{
193	int ver, essver, rev;
194
195    	sb_cmd(io, DSP_CMD_GETVER);	/* Get version */
196    	ver = (sb_get_byte(io) << 8) | sb_get_byte(io);
197	if (ver < 0x100 || ver > 0x4ff) return 0;
198    	if (ver == 0x0301) {
199	    	/* Try to detect ESS chips. */
200	    	sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */
201	    	essver = (sb_get_byte(io) << 8) | sb_get_byte(io);
202	    	rev = essver & 0x000f;
203	    	essver &= 0xfff0;
204	    	if (essver == 0x4880) ver |= 0x1000;
205	    	else if (essver == 0x6880) ver = 0x0500 | rev;
206	}
207	return ver;
208}
209
210static struct isa_pnp_id sbc_ids[] = {
211	{0x01008c0e, "Creative ViBRA16C"},		/* CTL0001 */
212	{0x31008c0e, "Creative SB16/SB32"},		/* CTL0031 */
213	{0x41008c0e, "Creative SB16/SB32"},		/* CTL0041 */
214	{0x42008c0e, "Creative SB AWE64"},		/* CTL0042 */
215	{0x43008c0e, "Creative ViBRA16X"},		/* CTL0043 */
216	{0x44008c0e, "Creative SB AWE64 Gold"},		/* CTL0044 */
217	{0x45008c0e, "Creative SB AWE64"},		/* CTL0045 */
218
219	{0x01000000, "Avance Logic ALS100+"},		/* @@@0001 - ViBRA16X clone */
220	{0x01100000, "Avance Asound 110"},		/* @@@1001 */
221	{0x01200000, "Avance Logic ALS120"},		/* @@@2001 - ViBRA16X clone */
222
223	{0x02017316, "ESS ES1688"},			/* ESS1688 */
224	{0x68187316, "ESS ES1868"},			/* ESS1868 */
225	{0x03007316, "ESS ES1869"},			/* ESS1869 */
226	{0x69187316, "ESS ES1869"},			/* ESS1869 */
227	{0xacb0110e, "ESS ES1869 (Compaq OEM)"},	/* CPQb0ac */
228	{0x78187316, "ESS ES1878"},			/* ESS1878 */
229	{0x79187316, "ESS ES1879"},			/* ESS1879 */
230	{0x88187316, "ESS ES1888"},			/* ESS1888 */
231	{0x07017316, "ESS ES1888 (DEC OEM)"},		/* ESS0107 */
232	{0}
233};
234
235static int
236sbc_probe(device_t dev)
237{
238	char *s = NULL;
239	u_int32_t lid, vid;
240
241	lid = isa_get_logicalid(dev);
242	vid = isa_get_vendorid(dev);
243	if (lid) {
244		if (lid == 0x01000000 && vid != 0x01009305) /* ALS0001 */
245			return ENXIO;
246		/* Check pnp ids */
247		return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids);
248	} else {
249		int rid = 0, ver;
250	    	struct resource *io;
251
252#ifdef PC98
253		io = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid,
254					 pcm_iat, 16, RF_ACTIVE);
255#else
256		io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
257		  		    	0, ~0, 16, RF_ACTIVE);
258#endif
259		if (!io) goto bad;
260#ifdef PC98
261		isa_load_resourcev(io, pcm_iat, 16);
262#endif
263    		if (sb_reset_dsp(io)) goto bad2;
264		ver = sb_identify_board(io);
265		if (ver == 0) goto bad2;
266		switch ((ver & 0x00000f00) >> 8) {
267		case 1:
268		case 2:
269			s = "Soundblaster";
270			break;
271
272		case 3:
273			s = (ver & 0x0000f000)? "ESS 488" : "Soundblaster Pro";
274			break;
275
276		case 4:
277			s = "Soundblaster 16";
278			break;
279
280		case 5:
281			s = (ver & 0x00000008)? "ESS 688" : "ESS 1688";
282			break;
283	     	}
284		if (s) device_set_desc(dev, s);
285bad2:		bus_release_resource(dev, SYS_RES_IOPORT, rid, io);
286bad:		return s? 0 : ENXIO;
287	}
288}
289
290static int
291sbc_attach(device_t dev)
292{
293	char *err = NULL;
294	struct sbc_softc *scp;
295	struct sndcard_func *func;
296	device_t child;
297	u_int32_t logical_id = isa_get_logicalid(dev);
298    	int flags = device_get_flags(dev);
299	int f, dh, dl, x, irq, i;
300
301    	if (!logical_id && (flags & DV_F_DUAL_DMA)) {
302        	bus_set_resource(dev, SYS_RES_DRQ, 1,
303				 flags & DV_F_DRQ_MASK, 1);
304    	}
305
306	scp = device_get_softc(dev);
307	bzero(scp, sizeof(*scp));
308	scp->dev = dev;
309	err = "alloc_resource";
310	if (alloc_resource(scp)) goto bad;
311
312	err = "sb_reset_dsp";
313	if (sb_reset_dsp(scp->io[0])) goto bad;
314	err = "sb_identify_board";
315	scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff;
316	if (scp->bd_ver == 0) goto bad;
317	f = 0;
318	if (logical_id == 0x01200000 && scp->bd_ver < 0x0400) scp->bd_ver = 0x0499;
319	switch ((scp->bd_ver & 0x0f00) >> 8) {
320    	case 1: /* old sound blaster has nothing... */
321		break;
322
323    	case 2:
324		f |= BD_F_DUP_MIDI;
325		if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335;
326		break;
327
328	case 5:
329		f |= BD_F_ESS;
330		scp->bd_ver = 0x0301;
331    	case 3:
332		f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345;
333		break;
334
335    	case 4:
336    		f |= BD_F_SB16 | BD_F_MIX_CT1745;
337		if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); else dl = -1;
338		if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl;
339		if (!logical_id && (dh < dl)) {
340			struct resource *r;
341			r = scp->drq[0];
342			scp->drq[0] = scp->drq[1];
343			scp->drq[1] = r;
344			dl = rman_get_start(scp->drq[0]);
345			dh = rman_get_start(scp->drq[1]);
346		}
347		/* soft irq/dma configuration */
348		x = -1;
349		irq = rman_get_start(scp->irq[0]);
350#ifdef PC98
351		/* SB16 in PC98 use different IRQ table */
352		if	(irq == 3) x = 1;
353		else if (irq == 5) x = 8;
354		else if (irq == 10) x = 2;
355		else if (irq == 12) x = 4;
356		if (x == -1) {
357			err = "bad irq (3/5/10/12 valid)";
358			goto bad;
359		}
360		else sb_setmixer(scp->io[0], IRQ_NR, x);
361		/* SB16 in PC98 use different dma setting */
362		sb_setmixer(scp->io[0], DMA_NR, dh == 0 ? 1 : 2);
363#else
364		if      (irq == 5) x = 2;
365		else if (irq == 7) x = 4;
366		else if (irq == 9) x = 1;
367		else if (irq == 10) x = 8;
368		if (x == -1) {
369			err = "bad irq (5/7/9/10 valid)";
370			goto bad;
371		}
372		else sb_setmixer(scp->io[0], IRQ_NR, x);
373		sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl));
374#endif
375		device_printf(dev, "setting card to irq %d, drq %d", irq, dl);
376		if (dl != dh) printf(", %d", dh);
377		printf("\n");
378		break;
379    	}
380
381	switch (logical_id) {
382    	case 0x43008c0e:	/* CTL0043 */
383	case 0x01200000:
384	case 0x01000000:
385		f |= BD_F_SB16X;
386		break;
387	}
388	scp->bd_ver |= f << 16;
389
390	err = "setup_intr";
391	for (i = 0; i < IRQ_MAX; i++) {
392		if (bus_setup_intr(dev, scp->irq[i], INTR_TYPE_TTY, sbc_intr, &scp->ihl[i], &scp->ih))
393			goto bad;
394	}
395
396	/* PCM Audio */
397	func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
398	if (func == NULL) goto bad;
399	bzero(func, sizeof(*func));
400	func->func = SCF_PCM;
401	child = device_add_child(dev, "pcm", -1);
402	device_set_ivars(child, func);
403
404#if notyet
405	/* Midi Interface */
406	func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
407	if (func == NULL) goto bad;
408	bzero(func, sizeof(*func));
409	func->func = SCF_MIDI;
410	child = device_add_child(dev, "midi", -1);
411	device_set_ivars(child, func);
412
413	/* OPL FM Synthesizer */
414	func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT);
415	if (func == NULL) goto bad;
416	bzero(func, sizeof(*func));
417	func->func = SCF_SYNTH;
418	child = device_add_child(dev, "midi", -1);
419	device_set_ivars(child, func);
420#endif /* notyet */
421
422	/* probe/attach kids */
423	bus_generic_attach(dev);
424
425	return (0);
426
427bad:	if (err) device_printf(dev, "%s\n", err);
428	release_resource(scp);
429	return (ENXIO);
430}
431
432static void
433sbc_intr(void *p)
434{
435	struct sbc_ihl *ihl = p;
436	int i;
437
438	i = 0;
439	while (i < INTR_MAX) {
440		if (ihl->intr[i] != NULL) ihl->intr[i](ihl->intr_arg[i]);
441		i++;
442	}
443}
444
445static int
446sbc_setup_intr(device_t dev, device_t child, struct resource *irq,
447   	       int flags, driver_intr_t *intr, void *arg,
448   	       void **cookiep)
449{
450	struct sbc_softc *scp = device_get_softc(dev);
451	struct sbc_ihl *ihl = NULL;
452	int i;
453
454	i = 0;
455	while (i < IRQ_MAX) {
456		if (irq == scp->irq[i]) ihl = &scp->ihl[i];
457		i++;
458	}
459	if (ihl == NULL) return (EINVAL);
460	i = 0;
461	while (i < INTR_MAX) {
462		if (ihl->intr[i] == NULL) {
463			ihl->intr[i] = intr;
464			ihl->intr_arg[i] = arg;
465			*cookiep = &ihl->intr[i];
466			return 0;
467		} else i++;
468	}
469	return (EINVAL);
470}
471
472static int
473sbc_teardown_intr(device_t dev, device_t child, struct resource *irq,
474  		  void *cookie)
475{
476	struct sbc_softc *scp = device_get_softc(dev);
477	struct sbc_ihl *ihl = NULL;
478	int i;
479
480	i = 0;
481	while (i < IRQ_MAX) {
482		if (irq == scp->irq[i]) ihl = &scp->ihl[i];
483		i++;
484	}
485	if (ihl == NULL) return (EINVAL);
486	i = 0;
487	while (i < INTR_MAX) {
488		if (cookie == &ihl->intr[i]) {
489			ihl->intr[i] = NULL;
490			ihl->intr_arg[i] = NULL;
491			return 0;
492		} else i++;
493	}
494	return (EINVAL);
495}
496
497static struct resource *
498sbc_alloc_resource(device_t bus, device_t child, int type, int *rid,
499		      u_long start, u_long end, u_long count, u_int flags)
500{
501	struct sbc_softc *scp;
502	int *alloced, rid_max, alloced_max;
503	struct resource **res;
504#ifdef PC98
505	int i;
506#endif
507
508	scp = device_get_softc(bus);
509	switch (type) {
510	case SYS_RES_IOPORT:
511		alloced = scp->io_alloced;
512		res = scp->io;
513#ifdef PC98
514		rid_max = 0;
515		for (i = 0; i < IO_MAX; i++)
516			rid_max += io_range[i];
517#else
518		rid_max = IO_MAX - 1;
519#endif
520		alloced_max = 1;
521		break;
522	case SYS_RES_DRQ:
523		alloced = scp->drq_alloced;
524		res = scp->drq;
525		rid_max = DRQ_MAX - 1;
526		alloced_max = 1;
527		break;
528	case SYS_RES_IRQ:
529		alloced = scp->irq_alloced;
530		res = scp->irq;
531		rid_max = IRQ_MAX - 1;
532		alloced_max = INTR_MAX; /* pcm and mpu may share the irq. */
533		break;
534	default:
535		return (NULL);
536	}
537
538	if (*rid > rid_max || alloced[*rid] == alloced_max)
539		return (NULL);
540
541	alloced[*rid]++;
542	return (res[*rid]);
543}
544
545static int
546sbc_release_resource(device_t bus, device_t child, int type, int rid,
547			struct resource *r)
548{
549	struct sbc_softc *scp;
550	int *alloced, rid_max;
551
552	scp = device_get_softc(bus);
553	switch (type) {
554	case SYS_RES_IOPORT:
555		alloced = scp->io_alloced;
556		rid_max = IO_MAX - 1;
557		break;
558	case SYS_RES_DRQ:
559		alloced = scp->drq_alloced;
560		rid_max = DRQ_MAX - 1;
561		break;
562	case SYS_RES_IRQ:
563		alloced = scp->irq_alloced;
564		rid_max = IRQ_MAX - 1;
565		break;
566	default:
567		return (1);
568	}
569
570	if (rid > rid_max || alloced[rid] == 0)
571		return (1);
572
573	alloced[rid]--;
574	return (0);
575}
576
577static int
578sbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result)
579{
580	struct sbc_softc *scp = device_get_softc(bus);
581	struct sndcard_func *func = device_get_ivars(dev);
582
583	switch (index) {
584	case 0:
585		*result = func->func;
586		break;
587
588	case 1:
589		*result = scp->bd_ver;
590	      	break;
591
592	default:
593		return ENOENT;
594	}
595
596	return 0;
597}
598
599static int
600sbc_write_ivar(device_t bus, device_t dev,
601	       int index, uintptr_t value)
602{
603	switch (index) {
604	case 0:
605	case 1:
606  		return EINVAL;
607
608	default:
609		return (ENOENT);
610	}
611}
612
613static int
614alloc_resource(struct sbc_softc *scp)
615{
616	int i;
617
618	for (i = 0 ; i < IO_MAX ; i++) {
619		if (scp->io[i] == NULL) {
620#ifdef PC98
621			scp->io_rid[i] = i > 0 ?
622				scp->io_rid[i - 1] + io_range[i - 1] : 0;
623			scp->io[i] = isa_alloc_resourcev(scp->dev,
624							 SYS_RES_IOPORT,
625							 &scp->io_rid[i],
626							 sb_iat[i],
627							 io_range[i],
628							 RF_ACTIVE);
629			if (scp->io[i] != NULL)
630				isa_load_resourcev(scp->io[i], sb_iat[i],
631						   io_range[i]);
632#else
633			scp->io_rid[i] = i;
634			scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i],
635							0, ~0, io_range[i], RF_ACTIVE);
636#endif
637			if (i == 0 && scp->io[i] == NULL)
638				return (1);
639			scp->io_alloced[i] = 0;
640		}
641	}
642	for (i = 0 ; i < DRQ_MAX ; i++) {
643		if (scp->drq[i] == NULL) {
644			scp->drq_rid[i] = i;
645			scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i],
646							 0, ~0, 1, RF_ACTIVE);
647			if (i == 0 && scp->drq[i] == NULL)
648				return (1);
649			scp->drq_alloced[i] = 0;
650		}
651	}
652	for (i = 0 ; i < IRQ_MAX ; i++) {
653		if (scp->irq[i] == NULL) {
654			scp->irq_rid[i] = i;
655			scp->irq[i] = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid[i],
656							 0, ~0, 1, RF_ACTIVE);
657			if (i == 0 && scp->irq[i] == NULL)
658				return (1);
659			scp->irq_alloced[i] = 0;
660		}
661	}
662	return (0);
663}
664
665static int
666release_resource(struct sbc_softc *scp)
667{
668	int i;
669
670	for (i = 0 ; i < IO_MAX ; i++) {
671		if (scp->io[i] != NULL) {
672			bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]);
673			scp->io[i] = NULL;
674		}
675	}
676	for (i = 0 ; i < DRQ_MAX ; i++) {
677		if (scp->drq[i] != NULL) {
678			bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]);
679			scp->drq[i] = NULL;
680		}
681	}
682	for (i = 0 ; i < IRQ_MAX ; i++) {
683		if (scp->irq[i] != NULL) {
684			bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid[i], scp->irq[i]);
685			scp->irq[i] = NULL;
686		}
687	}
688	return (0);
689}
690
691static device_method_t sbc_methods[] = {
692	/* Device interface */
693	DEVMETHOD(device_probe,		sbc_probe),
694	DEVMETHOD(device_attach,	sbc_attach),
695	DEVMETHOD(device_detach,	bus_generic_detach),
696	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
697	DEVMETHOD(device_suspend,	bus_generic_suspend),
698	DEVMETHOD(device_resume,	bus_generic_resume),
699
700	/* Bus interface */
701	DEVMETHOD(bus_read_ivar,	sbc_read_ivar),
702	DEVMETHOD(bus_write_ivar,	sbc_write_ivar),
703	DEVMETHOD(bus_print_child,	bus_generic_print_child),
704	DEVMETHOD(bus_alloc_resource,	sbc_alloc_resource),
705	DEVMETHOD(bus_release_resource,	sbc_release_resource),
706	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
707	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
708	DEVMETHOD(bus_setup_intr,	sbc_setup_intr),
709	DEVMETHOD(bus_teardown_intr,	sbc_teardown_intr),
710
711	{ 0, 0 }
712};
713
714static driver_t sbc_driver = {
715	"sbc",
716	sbc_methods,
717	sizeof(struct sbc_softc),
718};
719
720/* sbc can be attached to an isa bus. */
721DRIVER_MODULE(sbc, isa, sbc_driver, sbc_devclass, 0, 0);
722