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