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