alpm.c revision 298955
12061Sjkh/*-
210760Sache * Copyright (c) 1998, 1999, 2001 Nicolas Souchu
32061Sjkh * All rights reserved.
42061Sjkh *
58854Srgrimes * Redistribution and use in source and binary forms, with or without
62061Sjkh * modification, are permitted provided that the following conditions
72061Sjkh * are met:
83197Scsgr * 1. Redistributions of source code must retain the above copyright
93197Scsgr *    notice, this list of conditions and the following disclaimer.
102061Sjkh * 2. Redistributions in binary form must reproduce the above copyright
112160Scsgr *    notice, this list of conditions and the following disclaimer in the
122834Swollman *    documentation and/or other materials provided with the distribution.
132061Sjkh *
142061Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152160Scsgr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161594Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172061Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182061Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191594Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
207407Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
217407Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
227108Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
237108Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
247108Sphk * SUCH DAMAGE.
257407Srgrimes */
267407Srgrimes
277407Srgrimes/*
287108Sphk * Power Management support for the Acer M15x3 chipsets
292061Sjkh */
302061Sjkh
312061Sjkh#include <sys/cdefs.h>
322061Sjkh__FBSDID("$FreeBSD: head/sys/dev/alpm/alpm.c 298955 2016-05-03 03:41:25Z pfg $");
332061Sjkh
342061Sjkh#include <sys/param.h>
352061Sjkh#include <sys/bus.h>
362061Sjkh#include <sys/kernel.h>
372061Sjkh#include <sys/lock.h>
382061Sjkh#include <sys/module.h>
392061Sjkh#include <sys/mutex.h>
402061Sjkh#include <sys/systm.h>
413197Scsgr
422626Scsgr#include <machine/bus.h>
432626Scsgr#include <machine/resource.h>
442061Sjkh#include <sys/rman.h>
452061Sjkh
462061Sjkh#include <dev/pci/pcivar.h>
472061Sjkh#include <dev/pci/pcireg.h>
482061Sjkh
492061Sjkh#include <dev/smbus/smbconf.h>
502061Sjkh#include "smbus_if.h"
512061Sjkh
522061Sjkh#define ALPM_DEBUG(x)	if (alpm_debug) (x)
532061Sjkh
542061Sjkh#ifdef DEBUG
552061Sjkhstatic int alpm_debug = 1;
562061Sjkh#else
572061Sjkhstatic int alpm_debug = 0;
582061Sjkh#endif
592061Sjkh
602061Sjkh#define ACER_M1543_PMU_ID	0x710110b9
612061Sjkh
622834Swollman/*
632834Swollman * I/O registers offsets - the base address is programmed via the
642834Swollman * SMBBA PCI configuration register
652834Swollman */
662834Swollman#define SMBSTS		0x0	/* SMBus host/slave status register */
672834Swollman#define SMBCMD		0x1	/* SMBus host/slave command register */
681594Srgrimes#define SMBSTART	0x2	/* start to generate programmed cycle */
694486Sphk#define SMBHADDR	0x3	/* host address register */
704486Sphk#define SMBHDATA	0x4	/* data A register for host controller */
714486Sphk#define SMBHDATB	0x5	/* data B register for host controller */
724486Sphk#define SMBHBLOCK	0x6	/* block register for host controller */
734486Sphk#define SMBHCMD		0x7	/* command register for host controller */
742061Sjkh
752061Sjkh/* SMBHADDR mask. */
762061Sjkh#define	LSB		0x1	/* XXX: Better name: Read/Write? */
772061Sjkh
782061Sjkh/* SMBSTS masks */
792061Sjkh#define TERMINATE	0x80
802061Sjkh#define BUS_COLLI	0x40
812061Sjkh#define DEVICE_ERR	0x20
822061Sjkh#define SMI_I_STS	0x10
832061Sjkh#define HST_BSY		0x08
842061Sjkh#define IDL_STS		0x04
852061Sjkh#define HSTSLV_STS	0x02
862061Sjkh#define HSTSLV_BSY	0x01
872061Sjkh
882061Sjkh/* SMBCMD masks */
892061Sjkh#define SMB_BLK_CLR	0x80
902061Sjkh#define T_OUT_CMD	0x08
918854Srgrimes#define ABORT_HOST	0x04
922061Sjkh
932061Sjkh/* SMBus commands */
942061Sjkh#define SMBQUICK	0x00
955511Sjkh#define SMBSRBYTE	0x10		/* send/receive byte */
962061Sjkh#define SMBWRBYTE	0x20		/* write/read byte */
972061Sjkh#define SMBWRWORD	0x30		/* write/read word */
982061Sjkh#define SMBWRBLOCK	0x40		/* write/read block */
992061Sjkh
1002061Sjkh/* PCI configuration registers and masks
1012061Sjkh */
1022061Sjkh#define COM		0x4
1033030Srgrimes#define COM_ENABLE_IO	0x1
1042061Sjkh
1053030Srgrimes#define SMBBA		PCIR_BAR(1)
1062061Sjkh
1076722Sphk#define ATPC		0x5b
1082061Sjkh#define ATPC_SMBCTRL	0x04 		/* XX linux has this as 0x6 */
1092302Spaul
1102302Spaul#define SMBHSI		0xe0
1112302Spaul#define SMBHSI_SLAVE	0x2
1122302Spaul#define SMBHSI_HOST	0x1
1132302Spaul
1142302Spaul#define SMBHCBC		0xe2
11510760Sache#define SMBHCBC_CLOCK	0x70
11610760Sache
1172302Spaul#define SMBCLOCK_149K	0x0
11810760Sache#define SMBCLOCK_74K	0x20
11910760Sache#define SMBCLOCK_37K	0x40
12010760Sache#define SMBCLOCK_223K	0x80
12110760Sache#define SMBCLOCK_111K	0xa0
1222302Spaul#define SMBCLOCK_55K	0xc0
1232302Spaul
1242302Spaulstruct alpm_softc {
1252302Spaul	int base;
1262302Spaul	struct resource *res;
1272302Spaul        bus_space_tag_t smbst;
1282302Spaul        bus_space_handle_t smbsh;
1292061Sjkh	device_t smbus;
1302061Sjkh	struct mtx lock;
1312061Sjkh};
1322061Sjkh
1332061Sjkh#define	ALPM_LOCK(alpm)		mtx_lock(&(alpm)->lock)
1342061Sjkh#define	ALPM_UNLOCK(alpm)	mtx_unlock(&(alpm)->lock)
1352061Sjkh#define	ALPM_LOCK_ASSERT(alpm)	mtx_assert(&(alpm)->lock, MA_OWNED)
1362061Sjkh
1372061Sjkh#define ALPM_SMBINB(alpm,register) \
1382061Sjkh	(bus_space_read_1(alpm->smbst, alpm->smbsh, register))
1392061Sjkh#define ALPM_SMBOUTB(alpm,register,value) \
1402061Sjkh	(bus_space_write_1(alpm->smbst, alpm->smbsh, register, value))
1412061Sjkh
1422061Sjkhstatic int	alpm_detach(device_t dev);
1432061Sjkh
1442061Sjkhstatic int
1452061Sjkhalpm_probe(device_t dev)
1462061Sjkh{
1472061Sjkh
1482061Sjkh	if (pci_get_devid(dev) == ACER_M1543_PMU_ID) {
1492061Sjkh		device_set_desc(dev, "AcerLabs M15x3 Power Management Unit");
1502061Sjkh
1512061Sjkh		return (BUS_PROBE_DEFAULT);
1522061Sjkh	}
1532061Sjkh
1542061Sjkh	return (ENXIO);
1553626Swollman}
1563626Swollman
1573626Swollmanstatic int
1583626Swollmanalpm_attach(device_t dev)
1593626Swollman{
1603626Swollman	int rid;
1613626Swollman	u_int32_t l;
1623626Swollman	struct alpm_softc *alpm;
1633626Swollman
1643626Swollman	alpm = device_get_softc(dev);
1653626Swollman
1667059Sroberto	/* Unlock SMBIO base register access */
1673626Swollman	l = pci_read_config(dev, ATPC, 1);
1683626Swollman	pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1);
1693626Swollman
1703626Swollman	/*
1713626Swollman	 * XX linux sets clock to 74k, should we?
1723626Swollman	l = pci_read_config(dev, SMBHCBC, 1);
1733626Swollman	l &= 0x1f;
1743626Swollman	l |= SMBCLOCK_74K;
1753626Swollman	pci_write_config(dev, SMBHCBC, l, 1);
1763626Swollman	 */
1773626Swollman
1783626Swollman	if (bootverbose || alpm_debug) {
1793626Swollman		l = pci_read_config(dev, SMBHSI, 1);
1803626Swollman		device_printf(dev, "%s/%s",
1813626Swollman			(l & SMBHSI_HOST) ? "host":"nohost",
1823626Swollman			(l & SMBHSI_SLAVE) ? "slave":"noslave");
1833626Swollman
1843626Swollman		l = pci_read_config(dev, SMBHCBC, 1);
1857446Ssos		switch (l & SMBHCBC_CLOCK) {
1863626Swollman		case SMBCLOCK_149K:
1873626Swollman			printf(" 149K");
1883626Swollman			break;
1893626Swollman		case SMBCLOCK_74K:
1903626Swollman			printf(" 74K");
1913626Swollman			break;
1923626Swollman		case SMBCLOCK_37K:
1932061Sjkh			printf(" 37K");
1942061Sjkh			break;
1952061Sjkh		case SMBCLOCK_223K:
1962061Sjkh			printf(" 223K");
1972061Sjkh			break;
1982061Sjkh		case SMBCLOCK_111K:
1992061Sjkh			printf(" 111K");
2002061Sjkh			break;
2012061Sjkh		case SMBCLOCK_55K:
2022061Sjkh			printf(" 55K");
2032061Sjkh			break;
2042061Sjkh		default:
2057130Srgrimes			printf("unknown");
2067130Srgrimes			break;
2077130Srgrimes		}
2082061Sjkh		printf("\n");
2092061Sjkh	}
2104249Sache
2112685Srgrimes	rid = SMBBA;
2126927Snate	alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
2132685Srgrimes	    RF_ACTIVE);
2143518Sache
2153197Scsgr	if (alpm->res == NULL) {
2163197Scsgr		device_printf(dev,"Could not allocate Bus space\n");
2173197Scsgr		return (ENXIO);
2182061Sjkh	}
2192061Sjkh	alpm->smbst = rman_get_bustag(alpm->res);
2202061Sjkh	alpm->smbsh = rman_get_bushandle(alpm->res);
2212883Sphk	mtx_init(&alpm->lock, device_get_nameunit(dev), "alpm", MTX_DEF);
2223429Sache
2233429Sache	/* attach the smbus */
2247281Srgrimes	alpm->smbus = device_add_child(dev, "smbus", -1);
2253242Spaul	if (alpm->smbus == NULL) {
2263242Spaul		alpm_detach(dev);
2277171Sats		return (EINVAL);
2282061Sjkh	}
2293213Spst	bus_generic_attach(dev);
2304942Sache
2315749Swollman	return (0);
2325772Swollman}
2335865Sache
2345866Sachestatic int
2352061Sjkhalpm_detach(device_t dev)
2365366Snate{
2375366Snate	struct alpm_softc *alpm = device_get_softc(dev);
2386934Sse
2395366Snate	if (alpm->smbus) {
2405366Snate		device_delete_child(dev, alpm->smbus);
2417292Srgrimes		alpm->smbus = NULL;
2427292Srgrimes	}
2435366Snate	mtx_destroy(&alpm->lock);
2445366Snate
2455366Snate	if (alpm->res)
2465366Snate		bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res);
2475366Snate
2485366Snate	return (0);
2495772Swollman}
2505772Swollman
2515728Swollmanstatic int
2525728Swollmanalpm_callback(device_t dev, int index, void *data)
2535728Swollman{
2545728Swollman	int error = 0;
2555728Swollman
2565366Snate	switch (index) {
2572061Sjkh	case SMB_REQUEST_BUS:
2582061Sjkh	case SMB_RELEASE_BUS:
2592061Sjkh		/* ok, bus allocation accepted */
2602061Sjkh		break;
2612061Sjkh	default:
2622061Sjkh		error = EINVAL;
2632061Sjkh	}
2642061Sjkh
2652061Sjkh	return (error);
2668295Srgrimes}
2678295Srgrimes
2688295Srgrimesstatic int
2698295Srgrimesalpm_clear(struct alpm_softc *sc)
2708489Srgrimes{
2718489Srgrimes	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
2728489Srgrimes	DELAY(10);
2738489Srgrimes
2748489Srgrimes	return (0);
2758489Srgrimes}
2768489Srgrimes
2778489Srgrimes#if 0
2788295Srgrimesstatic int
2792468Spaulalpm_abort(struct alpm_softc *sc)
2802061Sjkh{
2812273Spaul	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
2822061Sjkh
2838295Srgrimes	return (0);
2842160Scsgr}
2852160Scsgr#endif
2862160Scsgr
2872160Scsgrstatic int
2882279Spaulalpm_idle(struct alpm_softc *sc)
2894054Spst{
2904054Spst	u_char sts;
2912061Sjkh
2922061Sjkh	sts = ALPM_SMBINB(sc, SMBSTS);
2932279Spaul
2942468Spaul	ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts));
2952468Spaul
2963197Scsgr	return (sts & IDL_STS);
2972626Scsgr}
2982626Scsgr
2992626Scsgr/*
3002626Scsgr * Poll the SMBus controller
3012626Scsgr */
3022626Scsgrstatic int
3032626Scsgralpm_wait(struct alpm_softc *sc)
3042626Scsgr{
3052626Scsgr	int count = 10000;
3068304Srgrimes	u_char sts = 0;
3078304Srgrimes	int error;
3088304Srgrimes
3098304Srgrimes	/* wait for command to complete and SMBus controller is idle */
3102061Sjkh	while (count--) {
3112061Sjkh		DELAY(10);
3122061Sjkh		sts = ALPM_SMBINB(sc, SMBSTS);
31310479Sdg		if (sts & SMI_I_STS)
3142061Sjkh			break;
3152061Sjkh	}
3162273Spaul
3172061Sjkh	ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts));
3182061Sjkh
3192061Sjkh	error = SMB_ENOERR;
32010479Sdg
32110479Sdg	if (!count)
3222061Sjkh		error |= SMB_ETIMEOUT;
3231594Srgrimes
324	if (sts & TERMINATE)
325		error |= SMB_EABORT;
326
327	if (sts & BUS_COLLI)
328		error |= SMB_ENOACK;
329
330	if (sts & DEVICE_ERR)
331		error |= SMB_EBUSERR;
332
333	if (error != SMB_ENOERR)
334		alpm_clear(sc);
335
336	return (error);
337}
338
339static int
340alpm_quick(device_t dev, u_char slave, int how)
341{
342	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
343	int error;
344
345	ALPM_LOCK(sc);
346	alpm_clear(sc);
347	if (!alpm_idle(sc)) {
348		ALPM_UNLOCK(sc);
349		return (EBUSY);
350	}
351
352	switch (how) {
353	case SMB_QWRITE:
354		ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave));
355		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
356		break;
357	case SMB_QREAD:
358		ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave));
359		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
360		break;
361	default:
362		panic("%s: unknown QUICK command (%x)!", __func__,
363			how);
364	}
365	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
366	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
367
368	error = alpm_wait(sc);
369
370	ALPM_DEBUG(printf(", error=0x%x\n", error));
371	ALPM_UNLOCK(sc);
372
373	return (error);
374}
375
376static int
377alpm_sendb(device_t dev, u_char slave, char byte)
378{
379	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
380	int error;
381
382	ALPM_LOCK(sc);
383	alpm_clear(sc);
384	if (!alpm_idle(sc)) {
385		ALPM_UNLOCK(sc);
386		return (SMB_EBUSY);
387	}
388
389	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
390	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
391	ALPM_SMBOUTB(sc, SMBHDATA, byte);
392	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
393
394	error = alpm_wait(sc);
395
396	ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
397	ALPM_UNLOCK(sc);
398
399	return (error);
400}
401
402static int
403alpm_recvb(device_t dev, u_char slave, char *byte)
404{
405	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
406	int error;
407
408	ALPM_LOCK(sc);
409	alpm_clear(sc);
410	if (!alpm_idle(sc)) {
411		ALPM_UNLOCK(sc);
412		return (SMB_EBUSY);
413	}
414
415	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
416	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
417	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
418
419	if ((error = alpm_wait(sc)) == SMB_ENOERR)
420		*byte = ALPM_SMBINB(sc, SMBHDATA);
421
422	ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
423	ALPM_UNLOCK(sc);
424
425	return (error);
426}
427
428static int
429alpm_writeb(device_t dev, u_char slave, char cmd, char byte)
430{
431	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
432	int error;
433
434	ALPM_LOCK(sc);
435	alpm_clear(sc);
436	if (!alpm_idle(sc)) {
437		ALPM_UNLOCK(sc);
438		return (SMB_EBUSY);
439	}
440
441	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
442	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
443	ALPM_SMBOUTB(sc, SMBHDATA, byte);
444	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
445	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
446
447	error = alpm_wait(sc);
448
449	ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
450	ALPM_UNLOCK(sc);
451
452	return (error);
453}
454
455static int
456alpm_readb(device_t dev, u_char slave, char cmd, char *byte)
457{
458	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
459	int error;
460
461	ALPM_LOCK(sc);
462	alpm_clear(sc);
463	if (!alpm_idle(sc)) {
464		ALPM_UNLOCK(sc);
465		return (SMB_EBUSY);
466	}
467
468	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
469	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
470	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
471	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
472
473	if ((error = alpm_wait(sc)) == SMB_ENOERR)
474		*byte = ALPM_SMBINB(sc, SMBHDATA);
475
476	ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
477	ALPM_UNLOCK(sc);
478
479	return (error);
480}
481
482static int
483alpm_writew(device_t dev, u_char slave, char cmd, short word)
484{
485	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
486	int error;
487
488	ALPM_LOCK(sc);
489	alpm_clear(sc);
490	if (!alpm_idle(sc)) {
491		ALPM_UNLOCK(sc);
492		return (SMB_EBUSY);
493	}
494
495	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
496	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
497	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
498	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
499	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
500	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
501
502	error = alpm_wait(sc);
503
504	ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
505	ALPM_UNLOCK(sc);
506
507	return (error);
508}
509
510static int
511alpm_readw(device_t dev, u_char slave, char cmd, short *word)
512{
513	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
514	int error;
515	u_char high, low;
516
517	ALPM_LOCK(sc);
518	alpm_clear(sc);
519	if (!alpm_idle(sc)) {
520		ALPM_UNLOCK(sc);
521		return (SMB_EBUSY);
522	}
523
524	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
525	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
526	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
527	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
528
529	if ((error = alpm_wait(sc)) == SMB_ENOERR) {
530		low = ALPM_SMBINB(sc, SMBHDATA);
531		high = ALPM_SMBINB(sc, SMBHDATB);
532
533		*word = ((high & 0xff) << 8) | (low & 0xff);
534	}
535
536	ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
537	ALPM_UNLOCK(sc);
538
539	return (error);
540}
541
542static int
543alpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
544{
545	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
546	u_char i;
547	int error;
548
549	if (count < 1 || count > 32)
550		return (SMB_EINVAL);
551
552	ALPM_LOCK(sc);
553	alpm_clear(sc);
554	if(!alpm_idle(sc)) {
555		ALPM_UNLOCK(sc);
556		return (SMB_EBUSY);
557	}
558
559	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
560
561	/* set the cmd and reset the
562	 * 32-byte long internal buffer */
563	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
564
565	ALPM_SMBOUTB(sc, SMBHDATA, count);
566
567	/* fill the 32-byte internal buffer */
568	for (i = 0; i < count; i++) {
569		ALPM_SMBOUTB(sc, SMBHBLOCK, buf[i]);
570		DELAY(2);
571	}
572	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
573	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
574
575	error = alpm_wait(sc);
576
577	ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
578	ALPM_UNLOCK(sc);
579
580	return (error);
581}
582
583static int
584alpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
585{
586	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
587	u_char data, len, i;
588	int error;
589
590	if (*count < 1 || *count > 32)
591		return (SMB_EINVAL);
592
593	ALPM_LOCK(sc);
594	alpm_clear(sc);
595	if (!alpm_idle(sc)) {
596		ALPM_UNLOCK(sc);
597		return (SMB_EBUSY);
598	}
599
600	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
601
602	/* set the cmd and reset the
603	 * 32-byte long internal buffer */
604	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
605
606	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
607	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
608
609	if ((error = alpm_wait(sc)) != SMB_ENOERR)
610			goto error;
611
612	len = ALPM_SMBINB(sc, SMBHDATA);
613
614	/* read the 32-byte internal buffer */
615	for (i = 0; i < len; i++) {
616		data = ALPM_SMBINB(sc, SMBHBLOCK);
617		if (i < *count)
618			buf[i] = data;
619		DELAY(2);
620	}
621	*count = len;
622
623error:
624	ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
625	ALPM_UNLOCK(sc);
626
627	return (error);
628}
629
630static devclass_t alpm_devclass;
631
632static device_method_t alpm_methods[] = {
633	/* device interface */
634	DEVMETHOD(device_probe,		alpm_probe),
635	DEVMETHOD(device_attach,	alpm_attach),
636	DEVMETHOD(device_detach,	alpm_detach),
637
638	/* smbus interface */
639	DEVMETHOD(smbus_callback,	alpm_callback),
640	DEVMETHOD(smbus_quick,		alpm_quick),
641	DEVMETHOD(smbus_sendb,		alpm_sendb),
642	DEVMETHOD(smbus_recvb,		alpm_recvb),
643	DEVMETHOD(smbus_writeb,		alpm_writeb),
644	DEVMETHOD(smbus_readb,		alpm_readb),
645	DEVMETHOD(smbus_writew,		alpm_writew),
646	DEVMETHOD(smbus_readw,		alpm_readw),
647	DEVMETHOD(smbus_bwrite,		alpm_bwrite),
648	DEVMETHOD(smbus_bread,		alpm_bread),
649
650	{ 0, 0 }
651};
652
653static driver_t alpm_driver = {
654	"alpm",
655	alpm_methods,
656	sizeof(struct alpm_softc)
657};
658
659DRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0);
660DRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, 0, 0);
661MODULE_DEPEND(alpm, pci, 1, 1, 1);
662MODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
663MODULE_VERSION(alpm, 1);
664