alpm.c revision 162234
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 */
2643973Snsouch
2743973Snsouch/*
2843973Snsouch * Power Management support for the Acer M15x3 chipsets
2943973Snsouch */
30116192Sobrien
31116192Sobrien#include <sys/cdefs.h>
32116192Sobrien__FBSDID("$FreeBSD: head/sys/pci/alpm.c 162234 2006-09-11 20:52:41Z jhb $");
33116192Sobrien
3443973Snsouch#include <sys/param.h>
3543973Snsouch#include <sys/kernel.h>
3643973Snsouch#include <sys/systm.h>
3743973Snsouch#include <sys/module.h>
3843973Snsouch#include <sys/bus.h>
3943973Snsouch#include <sys/uio.h>
4043973Snsouch
4143973Snsouch#include <machine/bus.h>
4270606Snsouch#include <machine/resource.h>
4370606Snsouch#include <sys/rman.h>
4443973Snsouch
45119288Simp#include <dev/pci/pcivar.h>
46119288Simp#include <dev/pci/pcireg.h>
4743973Snsouch
4843973Snsouch#include <dev/iicbus/iiconf.h>
4943973Snsouch#include <dev/smbus/smbconf.h>
5043973Snsouch#include "smbus_if.h"
5143973Snsouch
5243973Snsouch#define ALPM_DEBUG(x)	if (alpm_debug) (x)
5343973Snsouch
5443973Snsouch#ifdef DEBUG
5543973Snsouchstatic int alpm_debug = 1;
5643973Snsouch#else
5743973Snsouchstatic int alpm_debug = 0;
5843973Snsouch#endif
5943973Snsouch
6043973Snsouch#define ACER_M1543_PMU_ID	0x710110b9
6143973Snsouch
6243973Snsouch/* Uncomment this line to force another I/O base address for SMB */
6343973Snsouch/* #define ALPM_SMBIO_BASE_ADDR	0x3a80 */
6443973Snsouch
6543973Snsouch/* I/O registers offsets - the base address is programmed via the
6643973Snsouch * SMBBA PCI configuration register
6743973Snsouch */
6843973Snsouch#define SMBSTS		0x0	/* SMBus host/slave status register */
6943973Snsouch#define SMBCMD		0x1	/* SMBus host/slave command register */
7043973Snsouch#define SMBSTART	0x2	/* start to generate programmed cycle */
7143973Snsouch#define SMBHADDR	0x3	/* host address register */
7243973Snsouch#define SMBHDATA	0x4	/* data A register for host controller */
7343973Snsouch#define SMBHDATB	0x5	/* data B register for host controller */
7443973Snsouch#define SMBHBLOCK	0x6	/* block register for host controller */
7543973Snsouch#define SMBHCMD		0x7	/* command register for host controller */
7643973Snsouch
7743973Snsouch/* SMBSTS masks */
7843973Snsouch#define TERMINATE	0x80
7943973Snsouch#define BUS_COLLI	0x40
8043973Snsouch#define DEVICE_ERR	0x20
8143973Snsouch#define SMI_I_STS	0x10
8243973Snsouch#define HST_BSY		0x08
8343973Snsouch#define IDL_STS		0x04
8443973Snsouch#define HSTSLV_STS	0x02
8543973Snsouch#define HSTSLV_BSY	0x01
8643973Snsouch
8743973Snsouch/* SMBCMD masks */
8843973Snsouch#define SMB_BLK_CLR	0x80
8943973Snsouch#define T_OUT_CMD	0x08
9043973Snsouch#define ABORT_HOST	0x04
9143973Snsouch
9243973Snsouch/* SMBus commands */
9343973Snsouch#define SMBQUICK	0x00
9443973Snsouch#define SMBSRBYTE	0x10		/* send/receive byte */
9543973Snsouch#define SMBWRBYTE	0x20		/* write/read byte */
9643973Snsouch#define SMBWRWORD	0x30		/* write/read word */
9743973Snsouch#define SMBWRBLOCK	0x40		/* write/read block */
9843973Snsouch
9943973Snsouch/* PCI configuration registers and masks
10043973Snsouch */
10143973Snsouch#define COM		0x4
10243973Snsouch#define COM_ENABLE_IO	0x1
10343973Snsouch
10443973Snsouch#define SMBBA		0x14
10543973Snsouch
10643973Snsouch#define ATPC		0x5b
10770606Snsouch#define ATPC_SMBCTRL	0x04 		/* XX linux has this as 0x6 */
10843973Snsouch
10943973Snsouch#define SMBHSI		0xe0
11043973Snsouch#define SMBHSI_SLAVE	0x2
11143973Snsouch#define SMBHSI_HOST	0x1
11243973Snsouch
11343973Snsouch#define SMBHCBC		0xe2
11443973Snsouch#define SMBHCBC_CLOCK	0x70
11543973Snsouch
11643973Snsouch#define SMBCLOCK_149K	0x0
11743973Snsouch#define SMBCLOCK_74K	0x20
11843973Snsouch#define SMBCLOCK_37K	0x40
11943973Snsouch#define SMBCLOCK_223K	0x80
12043973Snsouch#define SMBCLOCK_111K	0xa0
12143973Snsouch#define SMBCLOCK_55K	0xc0
12243973Snsouch
12393023Snsouchstruct alpm_softc {
12443973Snsouch	int base;
12593023Snsouch	struct resource *res;
12643973Snsouch        bus_space_tag_t smbst;
12743973Snsouch        bus_space_handle_t smbsh;
12843973Snsouch	device_t smbus;
12943973Snsouch};
13043973Snsouch
13193023Snsouch#define ALPM_SMBINB(alpm,register) \
13293023Snsouch	(bus_space_read_1(alpm->smbst, alpm->smbsh, register))
13393023Snsouch#define ALPM_SMBOUTB(alpm,register,value) \
13493023Snsouch	(bus_space_write_1(alpm->smbst, alpm->smbsh, register, value))
13543973Snsouch
13693023Snsouchstatic int
13793023Snsouchalpm_probe(device_t dev)
13893023Snsouch{
13993023Snsouch#ifdef ALPM_SMBIO_BASE_ADDR
14093023Snsouch	u_int32_t l;
14193023Snsouch#endif
14243973Snsouch
14393023Snsouch	if (pci_get_devid(dev) == ACER_M1543_PMU_ID) {
14493023Snsouch		device_set_desc(dev, "AcerLabs M15x3 Power Management Unit");
14543973Snsouch
14693023Snsouch#ifdef ALPM_SMBIO_BASE_ADDR
14793023Snsouch		if (bootverbose || alpm_debug)
14893023Snsouch			device_printf(dev, "forcing base I/O at 0x%x\n",
14993023Snsouch					ALPM_SMBIO_BASE_ADDR);
15043973Snsouch
15193023Snsouch		/* disable I/O */
15293023Snsouch		l = pci_read_config(dev, COM, 2);
15393023Snsouch		pci_write_config(dev, COM, l & ~COM_ENABLE_IO, 2);
15443973Snsouch
15593023Snsouch		/* set the I/O base address */
15693023Snsouch		pci_write_config(dev, SMBBA, ALPM_SMBIO_BASE_ADDR | 0x1, 4);
15743973Snsouch
15893023Snsouch		/* enable I/O */
15993023Snsouch		pci_write_config(dev, COM, l | COM_ENABLE_IO, 2);
16043973Snsouch
16193023Snsouch		if (bus_set_resource(dev, SYS_RES_IOPORT, SMBBA,
16293023Snsouch					ALPM_SMBIO_BASE_ADDR, 256)) {
16393023Snsouch			device_printf(dev, "could not set bus resource\n");
16493023Snsouch			return (ENXIO);
16593023Snsouch		}
16693023Snsouch#endif
167142398Simp		return (BUS_PROBE_DEFAULT);
16893023Snsouch	}
16943973Snsouch
17093023Snsouch	return (ENXIO);
17143973Snsouch}
17243973Snsouch
17370606Snsouchstatic int
17493023Snsouchalpm_attach(device_t dev)
17543973Snsouch{
176115532Sphk	int rid;
17770606Snsouch	u_int32_t l;
17893023Snsouch	struct alpm_softc *alpm;
17943973Snsouch
18070606Snsouch	alpm = device_get_softc(dev);
18143973Snsouch
18243973Snsouch	/* Unlock SMBIO base register access */
18370606Snsouch	l = pci_read_config(dev, ATPC, 1);
18470606Snsouch	pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1);
18543973Snsouch
18670606Snsouch	/*
18770606Snsouch	 * XX linux sets clock to 74k, should we?
18870606Snsouch	l = pci_read_config(dev, SMBHCBC, 1);
18970606Snsouch	l &= 0x1f;
19070606Snsouch	l |= SMBCLOCK_74K;
19170606Snsouch	pci_write_config(dev, SMBHCBC, l, 1);
19270606Snsouch	 */
19370606Snsouch
19493023Snsouch	if (bootverbose || alpm_debug) {
19570606Snsouch		l = pci_read_config(dev, SMBHSI, 1);
19693023Snsouch		device_printf(dev, "%s/%s",
19743973Snsouch			(l & SMBHSI_HOST) ? "host":"nohost",
19843973Snsouch			(l & SMBHSI_SLAVE) ? "slave":"noslave");
19943973Snsouch
20070606Snsouch		l = pci_read_config(dev, SMBHCBC, 1);
20143973Snsouch		switch (l & SMBHCBC_CLOCK) {
20243973Snsouch		case SMBCLOCK_149K:
20343973Snsouch			printf(" 149K");
20443973Snsouch			break;
20543973Snsouch		case SMBCLOCK_74K:
20643973Snsouch			printf(" 74K");
20743973Snsouch			break;
20843973Snsouch		case SMBCLOCK_37K:
20943973Snsouch			printf(" 37K");
21043973Snsouch			break;
21143973Snsouch		case SMBCLOCK_223K:
21243973Snsouch			printf(" 223K");
21343973Snsouch			break;
21443973Snsouch		case SMBCLOCK_111K:
21543973Snsouch			printf(" 111K");
21643973Snsouch			break;
21743973Snsouch		case SMBCLOCK_55K:
21843973Snsouch			printf(" 55K");
21943973Snsouch			break;
22093023Snsouch		default:
22193023Snsouch			printf("unkown");
22293023Snsouch			break;
22343973Snsouch		}
22493023Snsouch		printf("\n");
22543973Snsouch	}
22643973Snsouch
22793023Snsouch	rid = SMBBA;
228127135Snjl	alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
229127135Snjl	    RF_ACTIVE);
23043973Snsouch
23193023Snsouch	if (alpm->res == NULL) {
23270606Snsouch		device_printf(dev,"Could not allocate Bus space\n");
23393023Snsouch		return (ENXIO);
23470606Snsouch	}
23593023Snsouch	alpm->smbst = rman_get_bustag(alpm->res);
23693023Snsouch	alpm->smbsh = rman_get_bushandle(alpm->res);
23770606Snsouch
23893023Snsouch	/* attach the smbus */
23993023Snsouch	alpm->smbus = device_add_child(dev, "smbus", -1);
24093023Snsouch	bus_generic_attach(dev);
24143973Snsouch
24243973Snsouch	return (0);
24343973Snsouch}
24443973Snsouch
24543973Snsouchstatic int
24693023Snsouchalpm_detach(device_t dev)
24743973Snsouch{
24893023Snsouch	struct alpm_softc *alpm = device_get_softc(dev);
24943973Snsouch
25093023Snsouch	if (alpm->smbus) {
25193023Snsouch		device_delete_child(dev, alpm->smbus);
25293023Snsouch		alpm->smbus = NULL;
25393023Snsouch	}
25443973Snsouch
25593023Snsouch	if (alpm->res)
25693023Snsouch		bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res);
25743973Snsouch
25843973Snsouch	return (0);
25943973Snsouch}
26043973Snsouch
26143973Snsouchstatic int
262162234Sjhbalpm_callback(device_t dev, int index, void *data)
26343973Snsouch{
26443973Snsouch	int error = 0;
26543973Snsouch
26643973Snsouch	switch (index) {
26743973Snsouch	case SMB_REQUEST_BUS:
26843973Snsouch	case SMB_RELEASE_BUS:
26943973Snsouch		/* ok, bus allocation accepted */
27043973Snsouch		break;
27143973Snsouch	default:
27243973Snsouch		error = EINVAL;
27343973Snsouch	}
27443973Snsouch
27543973Snsouch	return (error);
27643973Snsouch}
27743973Snsouch
27843973Snsouchstatic int
27993023Snsouchalpm_clear(struct alpm_softc *sc)
28043973Snsouch{
28143973Snsouch	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
28243973Snsouch	DELAY(10);
28343973Snsouch
28443973Snsouch	return (0);
28543973Snsouch}
28643973Snsouch
28743973Snsouch#if 0
28843973Snsouchstatic int
28993023Snsouchalpm_abort(struct alpm_softc *sc)
29043973Snsouch{
29143973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
29243973Snsouch
29343973Snsouch	return (0);
29443973Snsouch}
29543973Snsouch#endif
29643973Snsouch
29743973Snsouchstatic int
29893023Snsouchalpm_idle(struct alpm_softc *sc)
29943973Snsouch{
30043973Snsouch	u_char sts;
30143973Snsouch
30243973Snsouch	sts = ALPM_SMBINB(sc, SMBSTS);
30343973Snsouch
30443973Snsouch	ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts));
30543973Snsouch
30643973Snsouch	return (sts & IDL_STS);
30743973Snsouch}
30843973Snsouch
30943973Snsouch/*
31043973Snsouch * Poll the SMBus controller
31143973Snsouch */
31243973Snsouchstatic int
31393023Snsouchalpm_wait(struct alpm_softc *sc)
31443973Snsouch{
31543973Snsouch	int count = 10000;
31670606Snsouch	u_char sts = 0;
31743973Snsouch	int error;
31843973Snsouch
31943973Snsouch	/* wait for command to complete and SMBus controller is idle */
32043973Snsouch	while(count--) {
32143973Snsouch		DELAY(10);
32243973Snsouch		sts = ALPM_SMBINB(sc, SMBSTS);
32343973Snsouch		if (sts & SMI_I_STS)
32443973Snsouch			break;
32543973Snsouch	}
32643973Snsouch
32743973Snsouch	ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts));
32843973Snsouch
32943973Snsouch	error = SMB_ENOERR;
33043973Snsouch
33143973Snsouch	if (!count)
33243973Snsouch		error |= SMB_ETIMEOUT;
33343973Snsouch
33443973Snsouch	if (sts & TERMINATE)
33543973Snsouch		error |= SMB_EABORT;
33643973Snsouch
33743973Snsouch	if (sts & BUS_COLLI)
33843973Snsouch		error |= SMB_ENOACK;
33943973Snsouch
34043973Snsouch	if (sts & DEVICE_ERR)
34143973Snsouch		error |= SMB_EBUSERR;
34243973Snsouch
34343973Snsouch	if (error != SMB_ENOERR)
34493023Snsouch		alpm_clear(sc);
34543973Snsouch
34643973Snsouch	return (error);
34743973Snsouch}
34843973Snsouch
34943973Snsouchstatic int
35093023Snsouchalpm_quick(device_t dev, u_char slave, int how)
35143973Snsouch{
35293023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
35343973Snsouch	int error;
35443973Snsouch
35593023Snsouch	alpm_clear(sc);
35693023Snsouch	if (!alpm_idle(sc))
35743973Snsouch		return (EBUSY);
35843973Snsouch
35943973Snsouch	switch (how) {
36043973Snsouch	case SMB_QWRITE:
36143973Snsouch		ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave));
36243973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
36343973Snsouch		break;
36443973Snsouch	case SMB_QREAD:
36543973Snsouch		ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave));
36643973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
36743973Snsouch		break;
36843973Snsouch	default:
36987599Sobrien		panic("%s: unknown QUICK command (%x)!", __func__,
37043973Snsouch			how);
37143973Snsouch	}
37243973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
37343973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
37443973Snsouch
37593023Snsouch	error = alpm_wait(sc);
37643973Snsouch
37743973Snsouch	ALPM_DEBUG(printf(", error=0x%x\n", error));
37843973Snsouch
37943973Snsouch	return (error);
38043973Snsouch}
38143973Snsouch
38243973Snsouchstatic int
38393023Snsouchalpm_sendb(device_t dev, u_char slave, char byte)
38443973Snsouch{
38593023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
38643973Snsouch	int error;
38743973Snsouch
38893023Snsouch	alpm_clear(sc);
38993023Snsouch	if (!alpm_idle(sc))
39043973Snsouch		return (SMB_EBUSY);
39143973Snsouch
39243973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
39343973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
39443973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
39543973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
39643973Snsouch
39793023Snsouch	error = alpm_wait(sc);
39843973Snsouch
39943973Snsouch	ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
40043973Snsouch
40143973Snsouch	return (error);
40243973Snsouch}
40343973Snsouch
40443973Snsouchstatic int
40593023Snsouchalpm_recvb(device_t dev, u_char slave, char *byte)
40643973Snsouch{
40793023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
40843973Snsouch	int error;
40943973Snsouch
41093023Snsouch	alpm_clear(sc);
41193023Snsouch	if (!alpm_idle(sc))
41243973Snsouch		return (SMB_EBUSY);
41343973Snsouch
41443973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
41543973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
41643973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
41743973Snsouch
41893023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR)
41943973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
42043973Snsouch
42143973Snsouch	ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
42243973Snsouch
42343973Snsouch	return (error);
42443973Snsouch}
42543973Snsouch
42643973Snsouchstatic int
42793023Snsouchalpm_writeb(device_t dev, u_char slave, char cmd, char byte)
42843973Snsouch{
42993023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
43043973Snsouch	int error;
43143973Snsouch
43293023Snsouch	alpm_clear(sc);
43393023Snsouch	if (!alpm_idle(sc))
43443973Snsouch		return (SMB_EBUSY);
43543973Snsouch
43643973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
43743973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
43843973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
43943973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
44043973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
44143973Snsouch
44293023Snsouch	error = alpm_wait(sc);
44343973Snsouch
44443973Snsouch	ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
44543973Snsouch
44643973Snsouch	return (error);
44743973Snsouch}
44843973Snsouch
44943973Snsouchstatic int
45093023Snsouchalpm_readb(device_t dev, u_char slave, char cmd, char *byte)
45143973Snsouch{
45293023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
45343973Snsouch	int error;
45443973Snsouch
45593023Snsouch	alpm_clear(sc);
45693023Snsouch	if (!alpm_idle(sc))
45743973Snsouch		return (SMB_EBUSY);
45843973Snsouch
45943973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
46043973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
46143973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
46243973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
46343973Snsouch
46493023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR)
46543973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
46643973Snsouch
46743973Snsouch	ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
46843973Snsouch
46943973Snsouch	return (error);
47043973Snsouch}
47143973Snsouch
47243973Snsouchstatic int
47393023Snsouchalpm_writew(device_t dev, u_char slave, char cmd, short word)
47443973Snsouch{
47593023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
47643973Snsouch	int error;
47743973Snsouch
47893023Snsouch	alpm_clear(sc);
47993023Snsouch	if (!alpm_idle(sc))
48043973Snsouch		return (SMB_EBUSY);
48143973Snsouch
48243973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
48343973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
48443973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
48543973Snsouch	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
48643973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
48743973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
48843973Snsouch
48993023Snsouch	error = alpm_wait(sc);
49043973Snsouch
49143973Snsouch	ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
49243973Snsouch
49343973Snsouch	return (error);
49443973Snsouch}
49543973Snsouch
49643973Snsouchstatic int
49793023Snsouchalpm_readw(device_t dev, u_char slave, char cmd, short *word)
49843973Snsouch{
49993023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
50043973Snsouch	int error;
50143973Snsouch	u_char high, low;
50243973Snsouch
50393023Snsouch	alpm_clear(sc);
50493023Snsouch	if (!alpm_idle(sc))
50543973Snsouch		return (SMB_EBUSY);
50643973Snsouch
50743973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
50843973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
50943973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
51043973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
51143973Snsouch
51293023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR) {
51343973Snsouch		low = ALPM_SMBINB(sc, SMBHDATA);
51443973Snsouch		high = ALPM_SMBINB(sc, SMBHDATB);
51543973Snsouch
51643973Snsouch		*word = ((high & 0xff) << 8) | (low & 0xff);
51743973Snsouch	}
51843973Snsouch
51943973Snsouch	ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
52043973Snsouch
52143973Snsouch	return (error);
52243973Snsouch}
52343973Snsouch
52443973Snsouchstatic int
52593023Snsouchalpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
52643973Snsouch{
52793023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
528162234Sjhb	u_char i;
529162234Sjhb	int error;
53043973Snsouch
531162234Sjhb	if (count < 1 || count > 32)
532162234Sjhb		return (SMB_EINVAL);
53393023Snsouch	alpm_clear(sc);
53493023Snsouch	if(!alpm_idle(sc))
53543973Snsouch		return (SMB_EBUSY);
53643973Snsouch
537162234Sjhb	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
53843973Snsouch
539162234Sjhb	/* set the cmd and reset the
540162234Sjhb	 * 32-byte long internal buffer */
541162234Sjhb	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
54243973Snsouch
543162234Sjhb	ALPM_SMBOUTB(sc, SMBHDATA, count);
54443973Snsouch
545162234Sjhb	/* fill the 32-byte internal buffer */
546162234Sjhb	for (i = 0; i < count; i++) {
547162234Sjhb		ALPM_SMBOUTB(sc, SMBHBLOCK, buf[i]);
548162234Sjhb		DELAY(2);
549162234Sjhb	}
550162234Sjhb	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
551162234Sjhb	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
55243973Snsouch
553162234Sjhb	error = alpm_wait(sc);
55443973Snsouch
55543973Snsouch	ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
55643973Snsouch
55743973Snsouch	return (error);
55843973Snsouch}
55943973Snsouch
56043973Snsouchstatic int
561162234Sjhbalpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
56243973Snsouch{
56393023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
564162234Sjhb	u_char data, len, i;
565162234Sjhb	int error;
56643973Snsouch
567162234Sjhb	if (*count < 1 || *count > 32)
568162234Sjhb		return (SMB_EINVAL);
56993023Snsouch	alpm_clear(sc);
57093023Snsouch	if (!alpm_idle(sc))
57143973Snsouch		return (SMB_EBUSY);
57243973Snsouch
573162234Sjhb	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
57443973Snsouch
575162234Sjhb	/* set the cmd and reset the
576162234Sjhb	 * 32-byte long internal buffer */
577162234Sjhb	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
57843973Snsouch
579162234Sjhb	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
580162234Sjhb	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
58143973Snsouch
582162234Sjhb	if ((error = alpm_wait(sc)) != SMB_ENOERR)
58343973Snsouch			goto error;
58443973Snsouch
585162234Sjhb	len = ALPM_SMBINB(sc, SMBHDATA);
58643973Snsouch
587162234Sjhb	/* read the 32-byte internal buffer */
588162234Sjhb	for (i = 0; i < len; i++) {
589162234Sjhb		data = ALPM_SMBINB(sc, SMBHBLOCK);
590162234Sjhb		if (i < *count)
591162234Sjhb			buf[i] = data;
592162234Sjhb		DELAY(2);
593162234Sjhb	}
594162234Sjhb	*count = len;
59543973Snsouch
59643973Snsoucherror:
597162234Sjhb	ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
59843973Snsouch
59943973Snsouch	return (error);
60043973Snsouch}
60143973Snsouch
60293023Snsouchstatic devclass_t alpm_devclass;
60393023Snsouch
60493023Snsouchstatic device_method_t alpm_methods[] = {
60593023Snsouch	/* device interface */
60693023Snsouch	DEVMETHOD(device_probe,		alpm_probe),
60793023Snsouch	DEVMETHOD(device_attach,	alpm_attach),
60893023Snsouch	DEVMETHOD(device_detach,	alpm_detach),
60993023Snsouch
61093023Snsouch	/* smbus interface */
61193023Snsouch	DEVMETHOD(smbus_callback,	alpm_callback),
61293023Snsouch	DEVMETHOD(smbus_quick,		alpm_quick),
61393023Snsouch	DEVMETHOD(smbus_sendb,		alpm_sendb),
61493023Snsouch	DEVMETHOD(smbus_recvb,		alpm_recvb),
61593023Snsouch	DEVMETHOD(smbus_writeb,		alpm_writeb),
61693023Snsouch	DEVMETHOD(smbus_readb,		alpm_readb),
61793023Snsouch	DEVMETHOD(smbus_writew,		alpm_writew),
61893023Snsouch	DEVMETHOD(smbus_readw,		alpm_readw),
61993023Snsouch	DEVMETHOD(smbus_bwrite,		alpm_bwrite),
62093023Snsouch	DEVMETHOD(smbus_bread,		alpm_bread),
62193023Snsouch
62293023Snsouch	{ 0, 0 }
62393023Snsouch};
62493023Snsouch
62593023Snsouchstatic driver_t alpm_driver = {
62693023Snsouch	"alpm",
62793023Snsouch	alpm_methods,
62893023Snsouch	sizeof(struct alpm_softc)
62993023Snsouch};
63093023Snsouch
63193023SnsouchDRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0);
632162234SjhbDRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, 0, 0);
633113506SmdoddMODULE_DEPEND(alpm, pci, 1, 1, 1);
63493023SnsouchMODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
63593023SnsouchMODULE_VERSION(alpm, 1);
636