alpm.c revision 48528
1/*-
2 * Copyright (c) 1998, 1999 Nicolas Souchu
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: alpm.c,v 1.4 1999/05/09 17:06:38 peter Exp $
27 *
28 */
29
30/*
31 * Power Management support for the Acer M15x3 chipsets
32 */
33#include <sys/param.h>
34#include <sys/kernel.h>
35#include <sys/systm.h>
36#include <sys/module.h>
37#include <sys/bus.h>
38#include <sys/conf.h>
39#include <sys/buf.h>
40#include <sys/uio.h>
41#include <sys/malloc.h>
42
43#include <machine/clock.h>
44
45#include <machine/bus_pio.h>
46#include <machine/bus_memio.h>
47#include <machine/bus.h>
48
49#include <pci/pcivar.h>
50#include <pci/pcireg.h>
51
52#include <dev/iicbus/iiconf.h>
53#include <dev/smbus/smbconf.h>
54#include "smbus_if.h"
55
56#include "alpm.h"
57
58#define ALPM_DEBUG(x)	if (alpm_debug) (x)
59
60#ifdef DEBUG
61static int alpm_debug = 1;
62#else
63static int alpm_debug = 0;
64#endif
65
66#define ACER_M1543_PMU_ID	0x710110b9
67
68/* Uncomment this line to force another I/O base address for SMB */
69/* #define ALPM_SMBIO_BASE_ADDR	0x3a80 */
70
71/* I/O registers offsets - the base address is programmed via the
72 * SMBBA PCI configuration register
73 */
74#define SMBSTS		0x0	/* SMBus host/slave status register */
75#define SMBCMD		0x1	/* SMBus host/slave command register */
76#define SMBSTART	0x2	/* start to generate programmed cycle */
77#define SMBHADDR	0x3	/* host address register */
78#define SMBHDATA	0x4	/* data A register for host controller */
79#define SMBHDATB	0x5	/* data B register for host controller */
80#define SMBHBLOCK	0x6	/* block register for host controller */
81#define SMBHCMD		0x7	/* command register for host controller */
82
83/* SMBSTS masks */
84#define TERMINATE	0x80
85#define BUS_COLLI	0x40
86#define DEVICE_ERR	0x20
87#define SMI_I_STS	0x10
88#define HST_BSY		0x08
89#define IDL_STS		0x04
90#define HSTSLV_STS	0x02
91#define HSTSLV_BSY	0x01
92
93/* SMBCMD masks */
94#define SMB_BLK_CLR	0x80
95#define T_OUT_CMD	0x08
96#define ABORT_HOST	0x04
97
98/* SMBus commands */
99#define SMBQUICK	0x00
100#define SMBSRBYTE	0x10		/* send/receive byte */
101#define SMBWRBYTE	0x20		/* write/read byte */
102#define SMBWRWORD	0x30		/* write/read word */
103#define SMBWRBLOCK	0x40		/* write/read block */
104
105/* PCI configuration registers and masks
106 */
107#define COM		0x4
108#define COM_ENABLE_IO	0x1
109
110#define SMBBA		0x14
111
112#define ATPC		0x5b
113#define ATPC_SMBCTRL	0x04
114
115#define SMBHSI		0xe0
116#define SMBHSI_SLAVE	0x2
117#define SMBHSI_HOST	0x1
118
119#define SMBHCBC		0xe2
120#define SMBHCBC_CLOCK	0x70
121
122#define SMBCLOCK_149K	0x0
123#define SMBCLOCK_74K	0x20
124#define SMBCLOCK_37K	0x40
125#define SMBCLOCK_223K	0x80
126#define SMBCLOCK_111K	0xa0
127#define SMBCLOCK_55K	0xc0
128
129struct alpm_data {
130	int base;
131        bus_space_tag_t smbst;
132        bus_space_handle_t smbsh;
133	pcici_t tag;
134};
135struct alpm_data alpmdata[NALPM];
136
137struct alsmb_softc {
138	int base;
139	device_t smbus;
140	struct alpm_data *alpm;
141};
142
143#define ALPM_SMBINB(alsmb,register) \
144	(bus_space_read_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register))
145#define ALPM_SMBOUTB(alsmb,register,value) \
146	(bus_space_write_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register, value))
147
148static int alsmb_probe(device_t);
149static int alsmb_attach(device_t);
150static void alsmb_print_child(device_t, device_t);
151static int alsmb_smb_callback(device_t, int, caddr_t *);
152static int alsmb_smb_quick(device_t dev, u_char slave, int how);
153static int alsmb_smb_sendb(device_t dev, u_char slave, char byte);
154static int alsmb_smb_recvb(device_t dev, u_char slave, char *byte);
155static int alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte);
156static int alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte);
157static int alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word);
158static int alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word);
159static int alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
160static int alsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *byte);
161
162static devclass_t alsmb_devclass;
163
164static device_method_t alsmb_methods[] = {
165	/* device interface */
166	DEVMETHOD(device_probe,		alsmb_probe),
167	DEVMETHOD(device_attach,	alsmb_attach),
168
169	/* bus interface */
170	DEVMETHOD(bus_print_child,	alsmb_print_child),
171
172	/* smbus interface */
173	DEVMETHOD(smbus_callback,	alsmb_smb_callback),
174	DEVMETHOD(smbus_quick,		alsmb_smb_quick),
175	DEVMETHOD(smbus_sendb,		alsmb_smb_sendb),
176	DEVMETHOD(smbus_recvb,		alsmb_smb_recvb),
177	DEVMETHOD(smbus_writeb,		alsmb_smb_writeb),
178	DEVMETHOD(smbus_readb,		alsmb_smb_readb),
179	DEVMETHOD(smbus_writew,		alsmb_smb_writew),
180	DEVMETHOD(smbus_readw,		alsmb_smb_readw),
181	DEVMETHOD(smbus_bwrite,		alsmb_smb_bwrite),
182	DEVMETHOD(smbus_bread,		alsmb_smb_bread),
183
184	{ 0, 0 }
185};
186
187static driver_t alsmb_driver = {
188	"alsmb",
189	alsmb_methods,
190	sizeof(struct alsmb_softc),
191};
192
193static const char* alpm_pci_probe(pcici_t tag, pcidi_t type);
194static void alpm_pci_attach(pcici_t tag, int unit);
195
196static u_long	alpm_count;
197
198static struct	pci_device alpm_device = {
199	"alpm",
200	alpm_pci_probe,
201	alpm_pci_attach,
202	&alpm_count
203};
204
205COMPAT_PCI_DRIVER (alpm, alpm_device);
206
207static const char*
208alpm_pci_probe(pcici_t tag, pcidi_t type)
209{
210	if (type == ACER_M1543_PMU_ID)
211		return ("AcerLabs M15x3 Power Management Unit");
212
213	return ((char *)0);
214}
215
216static void
217alpm_pci_attach(pcici_t tag, int unit)
218{
219	struct alpm_data *alpm;
220	u_long l;
221
222	if (unit >= NALPM) {
223		printf("alpm%d: attach: only %d units configured.\n",
224		        unit, NALPM);
225		return;
226	}
227	alpm = &alpmdata[unit];
228
229	alpm->tag = tag;
230
231	/* Unlock SMBIO base register access */
232	l = pci_cfgread(tag, ATPC, 1);
233	pci_cfgwrite(tag, ATPC, l & ~ATPC_SMBCTRL, 1);
234
235	if (bootverbose) {
236		l = pci_cfgread(tag, SMBHSI, 1);
237		printf("alsmb%d: %s/%s", unit,
238			(l & SMBHSI_HOST) ? "host":"nohost",
239			(l & SMBHSI_SLAVE) ? "slave":"noslave");
240
241		l = pci_cfgread(tag, SMBHCBC, 1);
242		switch (l & SMBHCBC_CLOCK) {
243		case SMBCLOCK_149K:
244			printf(" 149K");
245			break;
246		case SMBCLOCK_74K:
247			printf(" 74K");
248			break;
249		case SMBCLOCK_37K:
250			printf(" 37K");
251			break;
252		case SMBCLOCK_223K:
253			printf(" 223K");
254			break;
255		case SMBCLOCK_111K:
256			printf(" 111K");
257			break;
258		case SMBCLOCK_55K:
259			printf(" 55K");
260			break;
261		}
262	}
263
264	alpm->smbst = I386_BUS_SPACE_IO;
265
266#ifdef ALPM_SMBIO_BASE_ADDR
267	/* disable I/O */
268	l = pci_cfgread(tag, COM, 2);
269	pci_cfgwrite(tag, COM, l & ~COM_ENABLE_IO, 2);
270
271	/* set the I/O base address */
272	pci_cfgwrite(tag, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4);
273
274	/* enable I/O */
275	pci_cfgwrite(tag, COM, l | COM_ENABLE_IO, 2);
276
277	alpm->smbsh = ALPM_SMBIO_BASE_ADDR;
278#else
279	alpm->smbsh = pci_cfgread(tag, SMBBA, 4) & ~0x1;
280#endif
281	if (bootverbose)
282		printf(" at 0x%x\n", alpm->smbsh);
283
284	/* XXX add the I2C interface to the root_bus until pcibus is ready */
285	device_add_child(root_bus, "alsmb", unit, NULL);
286
287	return;
288}
289
290/*
291 * Not a real probe, we know the device exists since the device has
292 * been added after the successfull pci probe.
293 */
294static int
295alsmb_probe(device_t dev)
296{
297	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
298
299	sc->alpm = &alpmdata[device_get_unit(dev)];
300
301	device_set_desc(dev, "Aladdin IV/V/Pro2 SMBus controller");
302
303	return (0);
304}
305
306static int
307alsmb_attach(device_t dev)
308{
309	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
310
311	/* allocate a new smbus device */
312	sc->smbus = smbus_alloc_bus(dev);
313
314	/* probe and attach the smbus */
315	device_probe_and_attach(sc->smbus);
316
317	return (0);
318}
319
320static void
321alsmb_print_child(device_t bus, device_t dev)
322{
323	printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
324
325	return;
326}
327
328static int
329alsmb_smb_callback(device_t dev, int index, caddr_t *data)
330{
331	int error = 0;
332
333	switch (index) {
334	case SMB_REQUEST_BUS:
335	case SMB_RELEASE_BUS:
336		/* ok, bus allocation accepted */
337		break;
338	default:
339		error = EINVAL;
340	}
341
342	return (error);
343}
344
345static int
346alsmb_clear(struct alsmb_softc *sc)
347{
348	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
349	DELAY(10);
350
351	return (0);
352}
353
354#if 0
355static int
356alsmb_abort(struct alsmb_softc *sc)
357{
358	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
359
360	return (0);
361}
362#endif
363
364static int
365alsmb_idle(struct alsmb_softc *sc)
366{
367	u_char sts;
368
369	sts = ALPM_SMBINB(sc, SMBSTS);
370
371	ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts));
372
373	return (sts & IDL_STS);
374}
375
376/*
377 * Poll the SMBus controller
378 */
379static int
380alsmb_wait(struct alsmb_softc *sc)
381{
382	int count = 10000;
383	u_char sts;
384	int error;
385
386	/* wait for command to complete and SMBus controller is idle */
387	while(count--) {
388		DELAY(10);
389		sts = ALPM_SMBINB(sc, SMBSTS);
390		if (sts & SMI_I_STS)
391			break;
392	}
393
394	ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts));
395
396	error = SMB_ENOERR;
397
398	if (!count)
399		error |= SMB_ETIMEOUT;
400
401	if (sts & TERMINATE)
402		error |= SMB_EABORT;
403
404	if (sts & BUS_COLLI)
405		error |= SMB_ENOACK;
406
407	if (sts & DEVICE_ERR)
408		error |= SMB_EBUSERR;
409
410	if (error != SMB_ENOERR)
411		alsmb_clear(sc);
412
413	return (error);
414}
415
416static int
417alsmb_smb_quick(device_t dev, u_char slave, int how)
418{
419	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
420	int error;
421
422	alsmb_clear(sc);
423	if (!alsmb_idle(sc))
424		return (EBUSY);
425
426	switch (how) {
427	case SMB_QWRITE:
428		ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave));
429		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
430		break;
431	case SMB_QREAD:
432		ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave));
433		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
434		break;
435	default:
436		panic("%s: unknown QUICK command (%x)!", __FUNCTION__,
437			how);
438	}
439	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
440	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
441
442	error = alsmb_wait(sc);
443
444	ALPM_DEBUG(printf(", error=0x%x\n", error));
445
446	return (error);
447}
448
449static int
450alsmb_smb_sendb(device_t dev, u_char slave, char byte)
451{
452	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
453	int error;
454
455	alsmb_clear(sc);
456	if (!alsmb_idle(sc))
457		return (SMB_EBUSY);
458
459	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
460	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
461	ALPM_SMBOUTB(sc, SMBHDATA, byte);
462	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
463
464	error = alsmb_wait(sc);
465
466	ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
467
468	return (error);
469}
470
471static int
472alsmb_smb_recvb(device_t dev, u_char slave, char *byte)
473{
474	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
475	int error;
476
477	alsmb_clear(sc);
478	if (!alsmb_idle(sc))
479		return (SMB_EBUSY);
480
481	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
482	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
483	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
484
485	if ((error = alsmb_wait(sc)) == SMB_ENOERR)
486		*byte = ALPM_SMBINB(sc, SMBHDATA);
487
488	ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
489
490	return (error);
491}
492
493static int
494alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte)
495{
496	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
497	int error;
498
499	alsmb_clear(sc);
500	if (!alsmb_idle(sc))
501		return (SMB_EBUSY);
502
503	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
504	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
505	ALPM_SMBOUTB(sc, SMBHDATA, byte);
506	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
507	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
508
509	error = alsmb_wait(sc);
510
511	ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
512
513	return (error);
514}
515
516static int
517alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte)
518{
519	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
520	int error;
521
522	alsmb_clear(sc);
523	if (!alsmb_idle(sc))
524		return (SMB_EBUSY);
525
526	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
527	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
528	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
529	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
530
531	if ((error = alsmb_wait(sc)) == SMB_ENOERR)
532		*byte = ALPM_SMBINB(sc, SMBHDATA);
533
534	ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
535
536	return (error);
537}
538
539static int
540alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word)
541{
542	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
543	int error;
544
545	alsmb_clear(sc);
546	if (!alsmb_idle(sc))
547		return (SMB_EBUSY);
548
549	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
550	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
551	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
552	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
553	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
554	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
555
556	error = alsmb_wait(sc);
557
558	ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
559
560	return (error);
561}
562
563static int
564alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word)
565{
566	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
567	int error;
568	u_char high, low;
569
570	alsmb_clear(sc);
571	if (!alsmb_idle(sc))
572		return (SMB_EBUSY);
573
574	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
575	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
576	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
577	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
578
579	if ((error = alsmb_wait(sc)) == SMB_ENOERR) {
580		low = ALPM_SMBINB(sc, SMBHDATA);
581		high = ALPM_SMBINB(sc, SMBHDATB);
582
583		*word = ((high & 0xff) << 8) | (low & 0xff);
584	}
585
586	ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
587
588	return (error);
589}
590
591static int
592alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
593{
594	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
595	u_char remain, len, i;
596	int error = SMB_ENOERR;
597
598	alsmb_clear(sc);
599	if(!alsmb_idle(sc))
600		return (SMB_EBUSY);
601
602	remain = count;
603	while (remain) {
604		len = min(remain, 32);
605
606		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
607
608		/* set the cmd and reset the
609		 * 32-byte long internal buffer */
610		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
611
612		ALPM_SMBOUTB(sc, SMBHDATA, len);
613
614		/* fill the 32-byte internal buffer */
615		for (i=0; i<len; i++) {
616			ALPM_SMBOUTB(sc, SMBHBLOCK, buf[count-remain+i]);
617			DELAY(2);
618		}
619		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
620		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
621
622		if ((error = alsmb_wait(sc)) != SMB_ENOERR)
623			goto error;
624
625		remain -= len;
626	}
627
628error:
629	ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
630
631	return (error);
632}
633
634static int
635alsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
636{
637	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
638	u_char remain, len, i;
639	int error = SMB_ENOERR;
640
641	alsmb_clear(sc);
642	if (!alsmb_idle(sc))
643		return (SMB_EBUSY);
644
645	remain = count;
646	while (remain) {
647		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
648
649		/* set the cmd and reset the
650		 * 32-byte long internal buffer */
651		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
652
653		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
654		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
655
656		if ((error = alsmb_wait(sc)) != SMB_ENOERR)
657			goto error;
658
659		len = ALPM_SMBINB(sc, SMBHDATA);
660
661		/* read the 32-byte internal buffer */
662		for (i=0; i<len; i++) {
663			buf[count-remain+i] = ALPM_SMBINB(sc, SMBHBLOCK);
664			DELAY(2);
665		}
666
667		remain -= len;
668	}
669error:
670	ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
671
672	return (error);
673}
674
675DRIVER_MODULE(alsmb, root, alsmb_driver, alsmb_devclass, 0, 0);
676