alpm.c revision 113506
143973Snsouch/*-
270606Snsouch * Copyright (c) 1998, 1999, 2001 Nicolas Souchu
343973Snsouch * All rights reserved.
443973Snsouch *
543973Snsouch * Redistribution and use in source and binary forms, with or without
643973Snsouch * modification, are permitted provided that the following conditions
743973Snsouch * are met:
843973Snsouch * 1. Redistributions of source code must retain the above copyright
943973Snsouch *    notice, this list of conditions and the following disclaimer.
1043973Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1143973Snsouch *    notice, this list of conditions and the following disclaimer in the
1243973Snsouch *    documentation and/or other materials provided with the distribution.
1343973Snsouch *
1443973Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1543973Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1643973Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1743973Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1843973Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1943973Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2043973Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2143973Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2243973Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2343973Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2443973Snsouch * SUCH DAMAGE.
2543973Snsouch *
2650477Speter * $FreeBSD: head/sys/pci/alpm.c 113506 2003-04-15 06:37:30Z mdodd $
2743973Snsouch *
2843973Snsouch */
2943973Snsouch
3043973Snsouch/*
3143973Snsouch * Power Management support for the Acer M15x3 chipsets
3243973Snsouch */
3343973Snsouch#include <sys/param.h>
3443973Snsouch#include <sys/kernel.h>
3543973Snsouch#include <sys/systm.h>
3643973Snsouch#include <sys/module.h>
3743973Snsouch#include <sys/bus.h>
3843973Snsouch#include <sys/uio.h>
3943973Snsouch
4043973Snsouch#include <machine/bus_pio.h>
4143973Snsouch#include <machine/bus_memio.h>
4243973Snsouch#include <machine/bus.h>
4370606Snsouch#include <machine/resource.h>
4470606Snsouch#include <sys/rman.h>
4543973Snsouch
4643973Snsouch#include <pci/pcivar.h>
4743973Snsouch#include <pci/pcireg.h>
4843973Snsouch
4943973Snsouch#include <dev/iicbus/iiconf.h>
5043973Snsouch#include <dev/smbus/smbconf.h>
5143973Snsouch#include "smbus_if.h"
5243973Snsouch
5343973Snsouch#define ALPM_DEBUG(x)	if (alpm_debug) (x)
5443973Snsouch
5543973Snsouch#ifdef DEBUG
5643973Snsouchstatic int alpm_debug = 1;
5743973Snsouch#else
5843973Snsouchstatic int alpm_debug = 0;
5943973Snsouch#endif
6043973Snsouch
6143973Snsouch#define ACER_M1543_PMU_ID	0x710110b9
6243973Snsouch
6343973Snsouch/* Uncomment this line to force another I/O base address for SMB */
6443973Snsouch/* #define ALPM_SMBIO_BASE_ADDR	0x3a80 */
6543973Snsouch
6643973Snsouch/* I/O registers offsets - the base address is programmed via the
6743973Snsouch * SMBBA PCI configuration register
6843973Snsouch */
6943973Snsouch#define SMBSTS		0x0	/* SMBus host/slave status register */
7043973Snsouch#define SMBCMD		0x1	/* SMBus host/slave command register */
7143973Snsouch#define SMBSTART	0x2	/* start to generate programmed cycle */
7243973Snsouch#define SMBHADDR	0x3	/* host address register */
7343973Snsouch#define SMBHDATA	0x4	/* data A register for host controller */
7443973Snsouch#define SMBHDATB	0x5	/* data B register for host controller */
7543973Snsouch#define SMBHBLOCK	0x6	/* block register for host controller */
7643973Snsouch#define SMBHCMD		0x7	/* command register for host controller */
7743973Snsouch
7843973Snsouch/* SMBSTS masks */
7943973Snsouch#define TERMINATE	0x80
8043973Snsouch#define BUS_COLLI	0x40
8143973Snsouch#define DEVICE_ERR	0x20
8243973Snsouch#define SMI_I_STS	0x10
8343973Snsouch#define HST_BSY		0x08
8443973Snsouch#define IDL_STS		0x04
8543973Snsouch#define HSTSLV_STS	0x02
8643973Snsouch#define HSTSLV_BSY	0x01
8743973Snsouch
8843973Snsouch/* SMBCMD masks */
8943973Snsouch#define SMB_BLK_CLR	0x80
9043973Snsouch#define T_OUT_CMD	0x08
9143973Snsouch#define ABORT_HOST	0x04
9243973Snsouch
9343973Snsouch/* SMBus commands */
9443973Snsouch#define SMBQUICK	0x00
9543973Snsouch#define SMBSRBYTE	0x10		/* send/receive byte */
9643973Snsouch#define SMBWRBYTE	0x20		/* write/read byte */
9743973Snsouch#define SMBWRWORD	0x30		/* write/read word */
9843973Snsouch#define SMBWRBLOCK	0x40		/* write/read block */
9943973Snsouch
10043973Snsouch/* PCI configuration registers and masks
10143973Snsouch */
10243973Snsouch#define COM		0x4
10343973Snsouch#define COM_ENABLE_IO	0x1
10443973Snsouch
10543973Snsouch#define SMBBA		0x14
10643973Snsouch
10743973Snsouch#define ATPC		0x5b
10870606Snsouch#define ATPC_SMBCTRL	0x04 		/* XX linux has this as 0x6 */
10943973Snsouch
11043973Snsouch#define SMBHSI		0xe0
11143973Snsouch#define SMBHSI_SLAVE	0x2
11243973Snsouch#define SMBHSI_HOST	0x1
11343973Snsouch
11443973Snsouch#define SMBHCBC		0xe2
11543973Snsouch#define SMBHCBC_CLOCK	0x70
11643973Snsouch
11743973Snsouch#define SMBCLOCK_149K	0x0
11843973Snsouch#define SMBCLOCK_74K	0x20
11943973Snsouch#define SMBCLOCK_37K	0x40
12043973Snsouch#define SMBCLOCK_223K	0x80
12143973Snsouch#define SMBCLOCK_111K	0xa0
12243973Snsouch#define SMBCLOCK_55K	0xc0
12343973Snsouch
12493023Snsouchstruct alpm_softc {
12543973Snsouch	int base;
12693023Snsouch	struct resource *res;
12743973Snsouch        bus_space_tag_t smbst;
12843973Snsouch        bus_space_handle_t smbsh;
12943973Snsouch	device_t smbus;
13043973Snsouch};
13143973Snsouch
13293023Snsouch#define ALPM_SMBINB(alpm,register) \
13393023Snsouch	(bus_space_read_1(alpm->smbst, alpm->smbsh, register))
13493023Snsouch#define ALPM_SMBOUTB(alpm,register,value) \
13593023Snsouch	(bus_space_write_1(alpm->smbst, alpm->smbsh, register, value))
13643973Snsouch
13793023Snsouchstatic int
13893023Snsouchalpm_probe(device_t dev)
13993023Snsouch{
14093023Snsouch#ifdef ALPM_SMBIO_BASE_ADDR
14193023Snsouch	u_int32_t l;
14293023Snsouch#endif
14343973Snsouch
14493023Snsouch	if (pci_get_devid(dev) == ACER_M1543_PMU_ID) {
14593023Snsouch		device_set_desc(dev, "AcerLabs M15x3 Power Management Unit");
14643973Snsouch
14793023Snsouch#ifdef ALPM_SMBIO_BASE_ADDR
14893023Snsouch		if (bootverbose || alpm_debug)
14993023Snsouch			device_printf(dev, "forcing base I/O at 0x%x\n",
15093023Snsouch					ALPM_SMBIO_BASE_ADDR);
15143973Snsouch
15293023Snsouch		/* disable I/O */
15393023Snsouch		l = pci_read_config(dev, COM, 2);
15493023Snsouch		pci_write_config(dev, COM, l & ~COM_ENABLE_IO, 2);
15543973Snsouch
15693023Snsouch		/* set the I/O base address */
15793023Snsouch		pci_write_config(dev, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4);
15843973Snsouch
15993023Snsouch		/* enable I/O */
16093023Snsouch		pci_write_config(dev, COM, l | COM_ENABLE_IO, 2);
16143973Snsouch
16293023Snsouch		if (bus_set_resource(dev, SYS_RES_IOPORT, SMBBA,
16393023Snsouch					ALPM_SMBIO_BASE_ADDR, 256)) {
16493023Snsouch			device_printf(dev, "could not set bus resource\n");
16593023Snsouch			return (ENXIO);
16693023Snsouch		}
16793023Snsouch#endif
16893023Snsouch		return (0);
16993023Snsouch	}
17043973Snsouch
17193023Snsouch	return (ENXIO);
17243973Snsouch}
17343973Snsouch
17470606Snsouchstatic int
17593023Snsouchalpm_attach(device_t dev)
17643973Snsouch{
17770606Snsouch	int rid, unit;
17870606Snsouch	u_int32_t l;
17993023Snsouch	struct alpm_softc *alpm;
18043973Snsouch
18170606Snsouch	alpm = device_get_softc(dev);
18270606Snsouch	unit = device_get_unit(dev);
18343973Snsouch
18443973Snsouch	/* Unlock SMBIO base register access */
18570606Snsouch	l = pci_read_config(dev, ATPC, 1);
18670606Snsouch	pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1);
18743973Snsouch
18870606Snsouch	/*
18970606Snsouch	 * XX linux sets clock to 74k, should we?
19070606Snsouch	l = pci_read_config(dev, SMBHCBC, 1);
19170606Snsouch	l &= 0x1f;
19270606Snsouch	l |= SMBCLOCK_74K;
19370606Snsouch	pci_write_config(dev, SMBHCBC, l, 1);
19470606Snsouch	 */
19570606Snsouch
19693023Snsouch	if (bootverbose || alpm_debug) {
19770606Snsouch		l = pci_read_config(dev, SMBHSI, 1);
19893023Snsouch		device_printf(dev, "%s/%s",
19943973Snsouch			(l & SMBHSI_HOST) ? "host":"nohost",
20043973Snsouch			(l & SMBHSI_SLAVE) ? "slave":"noslave");
20143973Snsouch
20270606Snsouch		l = pci_read_config(dev, SMBHCBC, 1);
20343973Snsouch		switch (l & SMBHCBC_CLOCK) {
20443973Snsouch		case SMBCLOCK_149K:
20543973Snsouch			printf(" 149K");
20643973Snsouch			break;
20743973Snsouch		case SMBCLOCK_74K:
20843973Snsouch			printf(" 74K");
20943973Snsouch			break;
21043973Snsouch		case SMBCLOCK_37K:
21143973Snsouch			printf(" 37K");
21243973Snsouch			break;
21343973Snsouch		case SMBCLOCK_223K:
21443973Snsouch			printf(" 223K");
21543973Snsouch			break;
21643973Snsouch		case SMBCLOCK_111K:
21743973Snsouch			printf(" 111K");
21843973Snsouch			break;
21943973Snsouch		case SMBCLOCK_55K:
22043973Snsouch			printf(" 55K");
22143973Snsouch			break;
22293023Snsouch		default:
22393023Snsouch			printf("unkown");
22493023Snsouch			break;
22543973Snsouch		}
22693023Snsouch		printf("\n");
22743973Snsouch	}
22843973Snsouch
22993023Snsouch	rid = SMBBA;
23093023Snsouch	alpm->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
23193023Snsouch	    0, ~0, 1, RF_ACTIVE);
23243973Snsouch
23393023Snsouch	if (alpm->res == NULL) {
23470606Snsouch		device_printf(dev,"Could not allocate Bus space\n");
23593023Snsouch		return (ENXIO);
23670606Snsouch	}
23793023Snsouch	alpm->smbst = rman_get_bustag(alpm->res);
23893023Snsouch	alpm->smbsh = rman_get_bushandle(alpm->res);
23970606Snsouch
24093023Snsouch	/* attach the smbus */
24193023Snsouch	alpm->smbus = device_add_child(dev, "smbus", -1);
24293023Snsouch	bus_generic_attach(dev);
24343973Snsouch
24443973Snsouch	return (0);
24543973Snsouch}
24643973Snsouch
24743973Snsouchstatic int
24893023Snsouchalpm_detach(device_t dev)
24943973Snsouch{
25093023Snsouch	struct alpm_softc *alpm = device_get_softc(dev);
25143973Snsouch
25293023Snsouch	if (alpm->smbus) {
25393023Snsouch		device_delete_child(dev, alpm->smbus);
25493023Snsouch		alpm->smbus = NULL;
25593023Snsouch	}
25643973Snsouch
25793023Snsouch	if (alpm->res)
25893023Snsouch		bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res);
25943973Snsouch
26043973Snsouch	return (0);
26143973Snsouch}
26243973Snsouch
26343973Snsouchstatic int
26493023Snsouchalpm_callback(device_t dev, int index, caddr_t *data)
26543973Snsouch{
26643973Snsouch	int error = 0;
26743973Snsouch
26843973Snsouch	switch (index) {
26943973Snsouch	case SMB_REQUEST_BUS:
27043973Snsouch	case SMB_RELEASE_BUS:
27143973Snsouch		/* ok, bus allocation accepted */
27243973Snsouch		break;
27343973Snsouch	default:
27443973Snsouch		error = EINVAL;
27543973Snsouch	}
27643973Snsouch
27743973Snsouch	return (error);
27843973Snsouch}
27943973Snsouch
28043973Snsouchstatic int
28193023Snsouchalpm_clear(struct alpm_softc *sc)
28243973Snsouch{
28343973Snsouch	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
28443973Snsouch	DELAY(10);
28543973Snsouch
28643973Snsouch	return (0);
28743973Snsouch}
28843973Snsouch
28943973Snsouch#if 0
29043973Snsouchstatic int
29193023Snsouchalpm_abort(struct alpm_softc *sc)
29243973Snsouch{
29343973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
29443973Snsouch
29543973Snsouch	return (0);
29643973Snsouch}
29743973Snsouch#endif
29843973Snsouch
29943973Snsouchstatic int
30093023Snsouchalpm_idle(struct alpm_softc *sc)
30143973Snsouch{
30243973Snsouch	u_char sts;
30343973Snsouch
30443973Snsouch	sts = ALPM_SMBINB(sc, SMBSTS);
30543973Snsouch
30643973Snsouch	ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts));
30743973Snsouch
30843973Snsouch	return (sts & IDL_STS);
30943973Snsouch}
31043973Snsouch
31143973Snsouch/*
31243973Snsouch * Poll the SMBus controller
31343973Snsouch */
31443973Snsouchstatic int
31593023Snsouchalpm_wait(struct alpm_softc *sc)
31643973Snsouch{
31743973Snsouch	int count = 10000;
31870606Snsouch	u_char sts = 0;
31943973Snsouch	int error;
32043973Snsouch
32143973Snsouch	/* wait for command to complete and SMBus controller is idle */
32243973Snsouch	while(count--) {
32343973Snsouch		DELAY(10);
32443973Snsouch		sts = ALPM_SMBINB(sc, SMBSTS);
32543973Snsouch		if (sts & SMI_I_STS)
32643973Snsouch			break;
32743973Snsouch	}
32843973Snsouch
32943973Snsouch	ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts));
33043973Snsouch
33143973Snsouch	error = SMB_ENOERR;
33243973Snsouch
33343973Snsouch	if (!count)
33443973Snsouch		error |= SMB_ETIMEOUT;
33543973Snsouch
33643973Snsouch	if (sts & TERMINATE)
33743973Snsouch		error |= SMB_EABORT;
33843973Snsouch
33943973Snsouch	if (sts & BUS_COLLI)
34043973Snsouch		error |= SMB_ENOACK;
34143973Snsouch
34243973Snsouch	if (sts & DEVICE_ERR)
34343973Snsouch		error |= SMB_EBUSERR;
34443973Snsouch
34543973Snsouch	if (error != SMB_ENOERR)
34693023Snsouch		alpm_clear(sc);
34743973Snsouch
34843973Snsouch	return (error);
34943973Snsouch}
35043973Snsouch
35143973Snsouchstatic int
35293023Snsouchalpm_quick(device_t dev, u_char slave, int how)
35343973Snsouch{
35493023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
35543973Snsouch	int error;
35643973Snsouch
35793023Snsouch	alpm_clear(sc);
35893023Snsouch	if (!alpm_idle(sc))
35943973Snsouch		return (EBUSY);
36043973Snsouch
36143973Snsouch	switch (how) {
36243973Snsouch	case SMB_QWRITE:
36343973Snsouch		ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave));
36443973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
36543973Snsouch		break;
36643973Snsouch	case SMB_QREAD:
36743973Snsouch		ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave));
36843973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
36943973Snsouch		break;
37043973Snsouch	default:
37187599Sobrien		panic("%s: unknown QUICK command (%x)!", __func__,
37243973Snsouch			how);
37343973Snsouch	}
37443973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
37543973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
37643973Snsouch
37793023Snsouch	error = alpm_wait(sc);
37843973Snsouch
37943973Snsouch	ALPM_DEBUG(printf(", error=0x%x\n", error));
38043973Snsouch
38143973Snsouch	return (error);
38243973Snsouch}
38343973Snsouch
38443973Snsouchstatic int
38593023Snsouchalpm_sendb(device_t dev, u_char slave, char byte)
38643973Snsouch{
38793023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
38843973Snsouch	int error;
38943973Snsouch
39093023Snsouch	alpm_clear(sc);
39193023Snsouch	if (!alpm_idle(sc))
39243973Snsouch		return (SMB_EBUSY);
39343973Snsouch
39443973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
39543973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
39643973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
39743973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
39843973Snsouch
39993023Snsouch	error = alpm_wait(sc);
40043973Snsouch
40143973Snsouch	ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
40243973Snsouch
40343973Snsouch	return (error);
40443973Snsouch}
40543973Snsouch
40643973Snsouchstatic int
40793023Snsouchalpm_recvb(device_t dev, u_char slave, char *byte)
40843973Snsouch{
40993023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
41043973Snsouch	int error;
41143973Snsouch
41293023Snsouch	alpm_clear(sc);
41393023Snsouch	if (!alpm_idle(sc))
41443973Snsouch		return (SMB_EBUSY);
41543973Snsouch
41643973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
41743973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
41843973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
41943973Snsouch
42093023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR)
42143973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
42243973Snsouch
42343973Snsouch	ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
42443973Snsouch
42543973Snsouch	return (error);
42643973Snsouch}
42743973Snsouch
42843973Snsouchstatic int
42993023Snsouchalpm_writeb(device_t dev, u_char slave, char cmd, char byte)
43043973Snsouch{
43193023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
43243973Snsouch	int error;
43343973Snsouch
43493023Snsouch	alpm_clear(sc);
43593023Snsouch	if (!alpm_idle(sc))
43643973Snsouch		return (SMB_EBUSY);
43743973Snsouch
43843973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
43943973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
44043973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
44143973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
44243973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
44343973Snsouch
44493023Snsouch	error = alpm_wait(sc);
44543973Snsouch
44643973Snsouch	ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
44743973Snsouch
44843973Snsouch	return (error);
44943973Snsouch}
45043973Snsouch
45143973Snsouchstatic int
45293023Snsouchalpm_readb(device_t dev, u_char slave, char cmd, char *byte)
45343973Snsouch{
45493023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
45543973Snsouch	int error;
45643973Snsouch
45793023Snsouch	alpm_clear(sc);
45893023Snsouch	if (!alpm_idle(sc))
45943973Snsouch		return (SMB_EBUSY);
46043973Snsouch
46143973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
46243973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
46343973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
46443973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
46543973Snsouch
46693023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR)
46743973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
46843973Snsouch
46943973Snsouch	ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
47043973Snsouch
47143973Snsouch	return (error);
47243973Snsouch}
47343973Snsouch
47443973Snsouchstatic int
47593023Snsouchalpm_writew(device_t dev, u_char slave, char cmd, short word)
47643973Snsouch{
47793023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
47843973Snsouch	int error;
47943973Snsouch
48093023Snsouch	alpm_clear(sc);
48193023Snsouch	if (!alpm_idle(sc))
48243973Snsouch		return (SMB_EBUSY);
48343973Snsouch
48443973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
48543973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
48643973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
48743973Snsouch	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
48843973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
48943973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
49043973Snsouch
49193023Snsouch	error = alpm_wait(sc);
49243973Snsouch
49343973Snsouch	ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
49443973Snsouch
49543973Snsouch	return (error);
49643973Snsouch}
49743973Snsouch
49843973Snsouchstatic int
49993023Snsouchalpm_readw(device_t dev, u_char slave, char cmd, short *word)
50043973Snsouch{
50193023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
50243973Snsouch	int error;
50343973Snsouch	u_char high, low;
50443973Snsouch
50593023Snsouch	alpm_clear(sc);
50693023Snsouch	if (!alpm_idle(sc))
50743973Snsouch		return (SMB_EBUSY);
50843973Snsouch
50943973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
51043973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
51143973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
51243973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
51343973Snsouch
51493023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR) {
51543973Snsouch		low = ALPM_SMBINB(sc, SMBHDATA);
51643973Snsouch		high = ALPM_SMBINB(sc, SMBHDATB);
51743973Snsouch
51843973Snsouch		*word = ((high & 0xff) << 8) | (low & 0xff);
51943973Snsouch	}
52043973Snsouch
52143973Snsouch	ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
52243973Snsouch
52343973Snsouch	return (error);
52443973Snsouch}
52543973Snsouch
52643973Snsouchstatic int
52793023Snsouchalpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
52843973Snsouch{
52993023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
53043973Snsouch	u_char remain, len, i;
53143973Snsouch	int error = SMB_ENOERR;
53243973Snsouch
53393023Snsouch	alpm_clear(sc);
53493023Snsouch	if(!alpm_idle(sc))
53543973Snsouch		return (SMB_EBUSY);
53643973Snsouch
53743973Snsouch	remain = count;
53843973Snsouch	while (remain) {
53943973Snsouch		len = min(remain, 32);
54043973Snsouch
54143973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
54243973Snsouch
54343973Snsouch		/* set the cmd and reset the
54443973Snsouch		 * 32-byte long internal buffer */
54543973Snsouch		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
54643973Snsouch
54743973Snsouch		ALPM_SMBOUTB(sc, SMBHDATA, len);
54843973Snsouch
54943973Snsouch		/* fill the 32-byte internal buffer */
55043973Snsouch		for (i=0; i<len; i++) {
55143973Snsouch			ALPM_SMBOUTB(sc, SMBHBLOCK, buf[count-remain+i]);
55243973Snsouch			DELAY(2);
55343973Snsouch		}
55443973Snsouch		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
55543973Snsouch		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
55643973Snsouch
55793023Snsouch		if ((error = alpm_wait(sc)) != SMB_ENOERR)
55843973Snsouch			goto error;
55943973Snsouch
56043973Snsouch		remain -= len;
56143973Snsouch	}
56243973Snsouch
56343973Snsoucherror:
56443973Snsouch	ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
56543973Snsouch
56643973Snsouch	return (error);
56743973Snsouch}
56843973Snsouch
56943973Snsouchstatic int
57093023Snsouchalpm_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
57143973Snsouch{
57293023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
57343973Snsouch	u_char remain, len, i;
57443973Snsouch	int error = SMB_ENOERR;
57543973Snsouch
57693023Snsouch	alpm_clear(sc);
57793023Snsouch	if (!alpm_idle(sc))
57843973Snsouch		return (SMB_EBUSY);
57943973Snsouch
58043973Snsouch	remain = count;
58143973Snsouch	while (remain) {
58243973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
58343973Snsouch
58443973Snsouch		/* set the cmd and reset the
58543973Snsouch		 * 32-byte long internal buffer */
58643973Snsouch		ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
58743973Snsouch
58843973Snsouch		ALPM_SMBOUTB(sc, SMBHCMD, cmd);
58943973Snsouch		ALPM_SMBOUTB(sc, SMBSTART, 0xff);
59043973Snsouch
59193023Snsouch		if ((error = alpm_wait(sc)) != SMB_ENOERR)
59243973Snsouch			goto error;
59343973Snsouch
59443973Snsouch		len = ALPM_SMBINB(sc, SMBHDATA);
59543973Snsouch
59643973Snsouch		/* read the 32-byte internal buffer */
59743973Snsouch		for (i=0; i<len; i++) {
59843973Snsouch			buf[count-remain+i] = ALPM_SMBINB(sc, SMBHBLOCK);
59943973Snsouch			DELAY(2);
60043973Snsouch		}
60143973Snsouch
60243973Snsouch		remain -= len;
60343973Snsouch	}
60443973Snsoucherror:
60543973Snsouch	ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
60643973Snsouch
60743973Snsouch	return (error);
60843973Snsouch}
60943973Snsouch
61093023Snsouchstatic devclass_t alpm_devclass;
61193023Snsouch
61293023Snsouchstatic device_method_t alpm_methods[] = {
61393023Snsouch	/* device interface */
61493023Snsouch	DEVMETHOD(device_probe,		alpm_probe),
61593023Snsouch	DEVMETHOD(device_attach,	alpm_attach),
61693023Snsouch	DEVMETHOD(device_detach,	alpm_detach),
61793023Snsouch
61893023Snsouch	/* smbus interface */
61993023Snsouch	DEVMETHOD(smbus_callback,	alpm_callback),
62093023Snsouch	DEVMETHOD(smbus_quick,		alpm_quick),
62193023Snsouch	DEVMETHOD(smbus_sendb,		alpm_sendb),
62293023Snsouch	DEVMETHOD(smbus_recvb,		alpm_recvb),
62393023Snsouch	DEVMETHOD(smbus_writeb,		alpm_writeb),
62493023Snsouch	DEVMETHOD(smbus_readb,		alpm_readb),
62593023Snsouch	DEVMETHOD(smbus_writew,		alpm_writew),
62693023Snsouch	DEVMETHOD(smbus_readw,		alpm_readw),
62793023Snsouch	DEVMETHOD(smbus_bwrite,		alpm_bwrite),
62893023Snsouch	DEVMETHOD(smbus_bread,		alpm_bread),
62993023Snsouch
63093023Snsouch	{ 0, 0 }
63193023Snsouch};
63293023Snsouch
63393023Snsouchstatic driver_t alpm_driver = {
63493023Snsouch	"alpm",
63593023Snsouch	alpm_methods,
63693023Snsouch	sizeof(struct alpm_softc)
63793023Snsouch};
63893023Snsouch
63993023SnsouchDRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0);
640113506SmdoddMODULE_DEPEND(alpm, pci, 1, 1, 1);
64193023SnsouchMODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
64293023SnsouchMODULE_VERSION(alpm, 1);
643