alpm.c revision 58695
150276Speter/*-
2184989Srafan * Copyright (c) 1998, 1999 Nicolas Souchu
350276Speter * All rights reserved.
450276Speter *
550276Speter * Redistribution and use in source and binary forms, with or without
650276Speter * modification, are permitted provided that the following conditions
750276Speter * are met:
850276Speter * 1. Redistributions of source code must retain the above copyright
950276Speter *    notice, this list of conditions and the following disclaimer.
1050276Speter * 2. Redistributions in binary form must reproduce the above copyright
1150276Speter *    notice, this list of conditions and the following disclaimer in the
1250276Speter *    documentation and/or other materials provided with the distribution.
1350276Speter *
1450276Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550276Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650276Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750276Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850276Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950276Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050276Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150276Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250276Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350276Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450276Speter * SUCH DAMAGE.
2550276Speter *
2650276Speter * $FreeBSD: head/sys/pci/alpm.c 58695 2000-03-27 18:32:45Z imp $
2750276Speter *
2850276Speter */
2950276Speter
3050276Speter#ifndef COMPAT_OLDPCI
3150276Speter#error "The alpm device requires the old pci compatibility shims"
3250276Speter#endif
3350276Speter
3450276Speter/*
3550276Speter * Power Management support for the Acer M15x3 chipsets
3650276Speter */
37184989Srafan#include <sys/param.h>
3850276Speter#include <sys/kernel.h>
3950276Speter#include <sys/systm.h>
4050276Speter#include <sys/module.h>
4150276Speter#include <sys/bus.h>
4250276Speter#include <sys/conf.h>
43178866Srafan#include <sys/buf.h>
44178866Srafan#include <sys/uio.h>
4550276Speter#include <sys/malloc.h>
46178866Srafan
4750276Speter#include <machine/clock.h>
4850276Speter
4997049Speter#include <machine/bus_pio.h>
5050276Speter#include <machine/bus_memio.h>
51178866Srafan#include <machine/bus.h>
5250276Speter
5350276Speter#include <pci/pcivar.h>
5497049Speter#include <pci/pcireg.h>
5597049Speter
5650276Speter#include <dev/iicbus/iiconf.h>
5750276Speter#include <dev/smbus/smbconf.h>
5850276Speter#include "smbus_if.h"
5950276Speter
60#include "alpm.h"
61
62#define ALPM_DEBUG(x)	if (alpm_debug) (x)
63
64#ifdef DEBUG
65static int alpm_debug = 1;
66#else
67static int alpm_debug = 0;
68#endif
69
70#define ACER_M1543_PMU_ID	0x710110b9
71
72/* Uncomment this line to force another I/O base address for SMB */
73/* #define ALPM_SMBIO_BASE_ADDR	0x3a80 */
74
75/* I/O registers offsets - the base address is programmed via the
76 * SMBBA PCI configuration register
77 */
78#define SMBSTS		0x0	/* SMBus host/slave status register */
79#define SMBCMD		0x1	/* SMBus host/slave command register */
80#define SMBSTART	0x2	/* start to generate programmed cycle */
81#define SMBHADDR	0x3	/* host address register */
82#define SMBHDATA	0x4	/* data A register for host controller */
83#define SMBHDATB	0x5	/* data B register for host controller */
84#define SMBHBLOCK	0x6	/* block register for host controller */
85#define SMBHCMD		0x7	/* command register for host controller */
86
87/* SMBSTS masks */
88#define TERMINATE	0x80
89#define BUS_COLLI	0x40
90#define DEVICE_ERR	0x20
91#define SMI_I_STS	0x10
92#define HST_BSY		0x08
93#define IDL_STS		0x04
94#define HSTSLV_STS	0x02
95#define HSTSLV_BSY	0x01
96
97/* SMBCMD masks */
98#define SMB_BLK_CLR	0x80
99#define T_OUT_CMD	0x08
100#define ABORT_HOST	0x04
101
102/* SMBus commands */
103#define SMBQUICK	0x00
104#define SMBSRBYTE	0x10		/* send/receive byte */
105#define SMBWRBYTE	0x20		/* write/read byte */
106#define SMBWRWORD	0x30		/* write/read word */
107#define SMBWRBLOCK	0x40		/* write/read block */
108
109/* PCI configuration registers and masks
110 */
111#define COM		0x4
112#define COM_ENABLE_IO	0x1
113
114#define SMBBA		0x14
115
116#define ATPC		0x5b
117#define ATPC_SMBCTRL	0x04
118
119#define SMBHSI		0xe0
120#define SMBHSI_SLAVE	0x2
121#define SMBHSI_HOST	0x1
122
123#define SMBHCBC		0xe2
124#define SMBHCBC_CLOCK	0x70
125
126#define SMBCLOCK_149K	0x0
127#define SMBCLOCK_74K	0x20
128#define SMBCLOCK_37K	0x40
129#define SMBCLOCK_223K	0x80
130#define SMBCLOCK_111K	0xa0
131#define SMBCLOCK_55K	0xc0
132
133struct alpm_data {
134	int base;
135        bus_space_tag_t smbst;
136        bus_space_handle_t smbsh;
137	pcici_t tag;
138};
139struct alpm_data alpmdata[NALPM];
140
141struct alsmb_softc {
142	int base;
143	device_t smbus;
144	struct alpm_data *alpm;
145};
146
147#define ALPM_SMBINB(alsmb,register) \
148	(bus_space_read_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register))
149#define ALPM_SMBOUTB(alsmb,register,value) \
150	(bus_space_write_1(alsmb->alpm->smbst, alsmb->alpm->smbsh, register, value))
151
152static int alsmb_probe(device_t);
153static int alsmb_attach(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,	bus_generic_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
208COMPAT_PCI_DRIVER (alpm, alpm_device);
209
210static const char*
211alpm_pci_probe(pcici_t tag, pcidi_t type)
212{
213	if (type == ACER_M1543_PMU_ID)
214		return ("AcerLabs M15x3 Power Management Unit");
215
216	return ((char *)0);
217}
218
219static void
220alpm_pci_attach(pcici_t tag, int unit)
221{
222	struct alpm_data *alpm;
223	u_long l;
224
225	if (unit >= NALPM) {
226		printf("alpm%d: attach: only %d units configured.\n",
227		        unit, NALPM);
228		return;
229	}
230	alpm = &alpmdata[unit];
231
232	alpm->tag = tag;
233
234	/* Unlock SMBIO base register access */
235	l = pci_cfgread(tag, ATPC, 1);
236	pci_cfgwrite(tag, ATPC, l & ~ATPC_SMBCTRL, 1);
237
238	if (bootverbose) {
239		l = pci_cfgread(tag, SMBHSI, 1);
240		printf("alsmb%d: %s/%s", unit,
241			(l & SMBHSI_HOST) ? "host":"nohost",
242			(l & SMBHSI_SLAVE) ? "slave":"noslave");
243
244		l = pci_cfgread(tag, SMBHCBC, 1);
245		switch (l & SMBHCBC_CLOCK) {
246		case SMBCLOCK_149K:
247			printf(" 149K");
248			break;
249		case SMBCLOCK_74K:
250			printf(" 74K");
251			break;
252		case SMBCLOCK_37K:
253			printf(" 37K");
254			break;
255		case SMBCLOCK_223K:
256			printf(" 223K");
257			break;
258		case SMBCLOCK_111K:
259			printf(" 111K");
260			break;
261		case SMBCLOCK_55K:
262			printf(" 55K");
263			break;
264		}
265	}
266
267	alpm->smbst = I386_BUS_SPACE_IO;
268
269#ifdef ALPM_SMBIO_BASE_ADDR
270	/* disable I/O */
271	l = pci_cfgread(tag, COM, 2);
272	pci_cfgwrite(tag, COM, l & ~COM_ENABLE_IO, 2);
273
274	/* set the I/O base address */
275	pci_cfgwrite(tag, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4);
276
277	/* enable I/O */
278	pci_cfgwrite(tag, COM, l | COM_ENABLE_IO, 2);
279
280	alpm->smbsh = ALPM_SMBIO_BASE_ADDR;
281#else
282	alpm->smbsh = pci_cfgread(tag, SMBBA, 4) & ~0x1;
283#endif
284	if (bootverbose)
285		printf(" at 0x%x\n", alpm->smbsh);
286
287	/* XXX add the I2C interface to the root_bus until pcibus is ready */
288	device_add_child(root_bus, "alsmb", unit);
289
290	return;
291}
292
293/*
294 * Not a real probe, we know the device exists since the device has
295 * been added after the successfull pci probe.
296 */
297static int
298alsmb_probe(device_t dev)
299{
300	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
301
302	sc->alpm = &alpmdata[device_get_unit(dev)];
303
304	device_set_desc(dev, "Aladdin IV/V/Pro2 SMBus controller");
305
306	return (0);
307}
308
309static int
310alsmb_attach(device_t dev)
311{
312	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
313
314	/* allocate a new smbus device */
315	sc->smbus = smbus_alloc_bus(dev);
316
317	/* probe and attach the smbus */
318	device_probe_and_attach(sc->smbus);
319
320	return (0);
321}
322
323static int
324alsmb_smb_callback(device_t dev, int index, caddr_t *data)
325{
326	int error = 0;
327
328	switch (index) {
329	case SMB_REQUEST_BUS:
330	case SMB_RELEASE_BUS:
331		/* ok, bus allocation accepted */
332		break;
333	default:
334		error = EINVAL;
335	}
336
337	return (error);
338}
339
340static int
341alsmb_clear(struct alsmb_softc *sc)
342{
343	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
344	DELAY(10);
345
346	return (0);
347}
348
349#if 0
350static int
351alsmb_abort(struct alsmb_softc *sc)
352{
353	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
354
355	return (0);
356}
357#endif
358
359static int
360alsmb_idle(struct alsmb_softc *sc)
361{
362	u_char sts;
363
364	sts = ALPM_SMBINB(sc, SMBSTS);
365
366	ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts));
367
368	return (sts & IDL_STS);
369}
370
371/*
372 * Poll the SMBus controller
373 */
374static int
375alsmb_wait(struct alsmb_softc *sc)
376{
377	int count = 10000;
378	u_char sts;
379	int error;
380
381	/* wait for command to complete and SMBus controller is idle */
382	while(count--) {
383		DELAY(10);
384		sts = ALPM_SMBINB(sc, SMBSTS);
385		if (sts & SMI_I_STS)
386			break;
387	}
388
389	ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts));
390
391	error = SMB_ENOERR;
392
393	if (!count)
394		error |= SMB_ETIMEOUT;
395
396	if (sts & TERMINATE)
397		error |= SMB_EABORT;
398
399	if (sts & BUS_COLLI)
400		error |= SMB_ENOACK;
401
402	if (sts & DEVICE_ERR)
403		error |= SMB_EBUSERR;
404
405	if (error != SMB_ENOERR)
406		alsmb_clear(sc);
407
408	return (error);
409}
410
411static int
412alsmb_smb_quick(device_t dev, u_char slave, int how)
413{
414	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
415	int error;
416
417	alsmb_clear(sc);
418	if (!alsmb_idle(sc))
419		return (EBUSY);
420
421	switch (how) {
422	case SMB_QWRITE:
423		ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave));
424		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
425		break;
426	case SMB_QREAD:
427		ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave));
428		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
429		break;
430	default:
431		panic("%s: unknown QUICK command (%x)!", __FUNCTION__,
432			how);
433	}
434	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
435	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
436
437	error = alsmb_wait(sc);
438
439	ALPM_DEBUG(printf(", error=0x%x\n", error));
440
441	return (error);
442}
443
444static int
445alsmb_smb_sendb(device_t dev, u_char slave, char byte)
446{
447	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
448	int error;
449
450	alsmb_clear(sc);
451	if (!alsmb_idle(sc))
452		return (SMB_EBUSY);
453
454	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
455	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
456	ALPM_SMBOUTB(sc, SMBHDATA, byte);
457	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
458
459	error = alsmb_wait(sc);
460
461	ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
462
463	return (error);
464}
465
466static int
467alsmb_smb_recvb(device_t dev, u_char slave, char *byte)
468{
469	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
470	int error;
471
472	alsmb_clear(sc);
473	if (!alsmb_idle(sc))
474		return (SMB_EBUSY);
475
476	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
477	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
478	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
479
480	if ((error = alsmb_wait(sc)) == SMB_ENOERR)
481		*byte = ALPM_SMBINB(sc, SMBHDATA);
482
483	ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
484
485	return (error);
486}
487
488static int
489alsmb_smb_writeb(device_t dev, u_char slave, char cmd, char byte)
490{
491	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
492	int error;
493
494	alsmb_clear(sc);
495	if (!alsmb_idle(sc))
496		return (SMB_EBUSY);
497
498	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
499	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
500	ALPM_SMBOUTB(sc, SMBHDATA, byte);
501	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
502	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
503
504	error = alsmb_wait(sc);
505
506	ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
507
508	return (error);
509}
510
511static int
512alsmb_smb_readb(device_t dev, u_char slave, char cmd, char *byte)
513{
514	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
515	int error;
516
517	alsmb_clear(sc);
518	if (!alsmb_idle(sc))
519		return (SMB_EBUSY);
520
521	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
522	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
523	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
524	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
525
526	if ((error = alsmb_wait(sc)) == SMB_ENOERR)
527		*byte = ALPM_SMBINB(sc, SMBHDATA);
528
529	ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
530
531	return (error);
532}
533
534static int
535alsmb_smb_writew(device_t dev, u_char slave, char cmd, short word)
536{
537	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
538	int error;
539
540	alsmb_clear(sc);
541	if (!alsmb_idle(sc))
542		return (SMB_EBUSY);
543
544	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
545	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
546	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
547	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
548	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
549	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
550
551	error = alsmb_wait(sc);
552
553	ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
554
555	return (error);
556}
557
558static int
559alsmb_smb_readw(device_t dev, u_char slave, char cmd, short *word)
560{
561	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
562	int error;
563	u_char high, low;
564
565	alsmb_clear(sc);
566	if (!alsmb_idle(sc))
567		return (SMB_EBUSY);
568
569	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
570	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
571	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
572	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
573
574	if ((error = alsmb_wait(sc)) == SMB_ENOERR) {
575		low = ALPM_SMBINB(sc, SMBHDATA);
576		high = ALPM_SMBINB(sc, SMBHDATB);
577
578		*word = ((high & 0xff) << 8) | (low & 0xff);
579	}
580
581	ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
582
583	return (error);
584}
585
586static int
587alsmb_smb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
588{
589	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
590	u_char remain, len, i;
591	int error = SMB_ENOERR;
592
593	alsmb_clear(sc);
594	if(!alsmb_idle(sc))
595		return (SMB_EBUSY);
596
597	remain = count;
598	while (remain) {
599		len = min(remain, 32);
600
601		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
602
603		/* set the cmd and reset the
604		 * 32-byte long internal buffer */
605		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
606
607		ALPM_SMBOUTB(sc, SMBHDATA, len);
608
609		/* fill the 32-byte internal buffer */
610		for (i=0; i<len; i++) {
611			ALPM_SMBOUTB(sc, SMBHBLOCK, buf[count-remain+i]);
612			DELAY(2);
613		}
614		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
615		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
616
617		if ((error = alsmb_wait(sc)) != SMB_ENOERR)
618			goto error;
619
620		remain -= len;
621	}
622
623error:
624	ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
625
626	return (error);
627}
628
629static int
630alsmb_smb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
631{
632	struct alsmb_softc *sc = (struct alsmb_softc *)device_get_softc(dev);
633	u_char remain, len, i;
634	int error = SMB_ENOERR;
635
636	alsmb_clear(sc);
637	if (!alsmb_idle(sc))
638		return (SMB_EBUSY);
639
640	remain = count;
641	while (remain) {
642		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
643
644		/* set the cmd and reset the
645		 * 32-byte long internal buffer */
646		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
647
648		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
649		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
650
651		if ((error = alsmb_wait(sc)) != SMB_ENOERR)
652			goto error;
653
654		len = ALPM_SMBINB(sc, SMBHDATA);
655
656		/* read the 32-byte internal buffer */
657		for (i=0; i<len; i++) {
658			buf[count-remain+i] = ALPM_SMBINB(sc, SMBHBLOCK);
659			DELAY(2);
660		}
661
662		remain -= len;
663	}
664error:
665	ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
666
667	return (error);
668}
669
670DRIVER_MODULE(alsmb, root, alsmb_driver, alsmb_devclass, 0, 0);
671