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