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$");
33116192Sobrien
3443973Snsouch#include <sys/param.h>
35165951Sjhb#include <sys/bus.h>
3643973Snsouch#include <sys/kernel.h>
37165951Sjhb#include <sys/lock.h>
38165951Sjhb#include <sys/module.h>
39165951Sjhb#include <sys/mutex.h>
4043973Snsouch#include <sys/systm.h>
4143973Snsouch
4243973Snsouch#include <machine/bus.h>
4370606Snsouch#include <machine/resource.h>
4470606Snsouch#include <sys/rman.h>
4543973Snsouch
46119288Simp#include <dev/pci/pcivar.h>
47119288Simp#include <dev/pci/pcireg.h>
4843973Snsouch
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
62165951Sjhb/*
63165951Sjhb * I/O registers offsets - the base address is programmed via the
6443973Snsouch * SMBBA PCI configuration register
6543973Snsouch */
6643973Snsouch#define SMBSTS		0x0	/* SMBus host/slave status register */
6743973Snsouch#define SMBCMD		0x1	/* SMBus host/slave command register */
6843973Snsouch#define SMBSTART	0x2	/* start to generate programmed cycle */
6943973Snsouch#define SMBHADDR	0x3	/* host address register */
7043973Snsouch#define SMBHDATA	0x4	/* data A register for host controller */
7143973Snsouch#define SMBHDATB	0x5	/* data B register for host controller */
7243973Snsouch#define SMBHBLOCK	0x6	/* block register for host controller */
7343973Snsouch#define SMBHCMD		0x7	/* command register for host controller */
7443973Snsouch
75165951Sjhb/* SMBHADDR mask. */
76165951Sjhb#define	LSB		0x1	/* XXX: Better name: Read/Write? */
77165951Sjhb
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
105165951Sjhb#define SMBBA		PCIR_BAR(1)
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;
130165951Sjhb	struct mtx lock;
13143973Snsouch};
13243973Snsouch
133165951Sjhb#define	ALPM_LOCK(alpm)		mtx_lock(&(alpm)->lock)
134165951Sjhb#define	ALPM_UNLOCK(alpm)	mtx_unlock(&(alpm)->lock)
135165951Sjhb#define	ALPM_LOCK_ASSERT(alpm)	mtx_assert(&(alpm)->lock, MA_OWNED)
136165951Sjhb
13793023Snsouch#define ALPM_SMBINB(alpm,register) \
13893023Snsouch	(bus_space_read_1(alpm->smbst, alpm->smbsh, register))
13993023Snsouch#define ALPM_SMBOUTB(alpm,register,value) \
14093023Snsouch	(bus_space_write_1(alpm->smbst, alpm->smbsh, register, value))
14143973Snsouch
142165951Sjhbstatic int	alpm_detach(device_t dev);
143165951Sjhb
14493023Snsouchstatic int
14593023Snsouchalpm_probe(device_t dev)
14693023Snsouch{
14743973Snsouch
14893023Snsouch	if (pci_get_devid(dev) == ACER_M1543_PMU_ID) {
14993023Snsouch		device_set_desc(dev, "AcerLabs M15x3 Power Management Unit");
15043973Snsouch
151142398Simp		return (BUS_PROBE_DEFAULT);
15293023Snsouch	}
15343973Snsouch
15493023Snsouch	return (ENXIO);
15543973Snsouch}
15643973Snsouch
15770606Snsouchstatic int
15893023Snsouchalpm_attach(device_t dev)
15943973Snsouch{
160115532Sphk	int rid;
16170606Snsouch	u_int32_t l;
16293023Snsouch	struct alpm_softc *alpm;
16343973Snsouch
16470606Snsouch	alpm = device_get_softc(dev);
16543973Snsouch
16643973Snsouch	/* Unlock SMBIO base register access */
16770606Snsouch	l = pci_read_config(dev, ATPC, 1);
16870606Snsouch	pci_write_config(dev, ATPC, l & ~ATPC_SMBCTRL, 1);
16943973Snsouch
17070606Snsouch	/*
17170606Snsouch	 * XX linux sets clock to 74k, should we?
17270606Snsouch	l = pci_read_config(dev, SMBHCBC, 1);
17370606Snsouch	l &= 0x1f;
17470606Snsouch	l |= SMBCLOCK_74K;
17570606Snsouch	pci_write_config(dev, SMBHCBC, l, 1);
17670606Snsouch	 */
17770606Snsouch
17893023Snsouch	if (bootverbose || alpm_debug) {
17970606Snsouch		l = pci_read_config(dev, SMBHSI, 1);
18093023Snsouch		device_printf(dev, "%s/%s",
18143973Snsouch			(l & SMBHSI_HOST) ? "host":"nohost",
18243973Snsouch			(l & SMBHSI_SLAVE) ? "slave":"noslave");
18343973Snsouch
18470606Snsouch		l = pci_read_config(dev, SMBHCBC, 1);
18543973Snsouch		switch (l & SMBHCBC_CLOCK) {
18643973Snsouch		case SMBCLOCK_149K:
18743973Snsouch			printf(" 149K");
18843973Snsouch			break;
18943973Snsouch		case SMBCLOCK_74K:
19043973Snsouch			printf(" 74K");
19143973Snsouch			break;
19243973Snsouch		case SMBCLOCK_37K:
19343973Snsouch			printf(" 37K");
19443973Snsouch			break;
19543973Snsouch		case SMBCLOCK_223K:
19643973Snsouch			printf(" 223K");
19743973Snsouch			break;
19843973Snsouch		case SMBCLOCK_111K:
19943973Snsouch			printf(" 111K");
20043973Snsouch			break;
20143973Snsouch		case SMBCLOCK_55K:
20243973Snsouch			printf(" 55K");
20343973Snsouch			break;
20493023Snsouch		default:
20593023Snsouch			printf("unkown");
20693023Snsouch			break;
20743973Snsouch		}
20893023Snsouch		printf("\n");
20943973Snsouch	}
21043973Snsouch
21193023Snsouch	rid = SMBBA;
212127135Snjl	alpm->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
213127135Snjl	    RF_ACTIVE);
21443973Snsouch
21593023Snsouch	if (alpm->res == NULL) {
21670606Snsouch		device_printf(dev,"Could not allocate Bus space\n");
21793023Snsouch		return (ENXIO);
21870606Snsouch	}
21993023Snsouch	alpm->smbst = rman_get_bustag(alpm->res);
22093023Snsouch	alpm->smbsh = rman_get_bushandle(alpm->res);
221165951Sjhb	mtx_init(&alpm->lock, device_get_nameunit(dev), "alpm", MTX_DEF);
22270606Snsouch
22393023Snsouch	/* attach the smbus */
22493023Snsouch	alpm->smbus = device_add_child(dev, "smbus", -1);
225165951Sjhb	if (alpm->smbus == NULL) {
226165951Sjhb		alpm_detach(dev);
227165951Sjhb		return (EINVAL);
228165951Sjhb	}
22993023Snsouch	bus_generic_attach(dev);
23043973Snsouch
23143973Snsouch	return (0);
23243973Snsouch}
23343973Snsouch
23443973Snsouchstatic int
23593023Snsouchalpm_detach(device_t dev)
23643973Snsouch{
23793023Snsouch	struct alpm_softc *alpm = device_get_softc(dev);
23843973Snsouch
23993023Snsouch	if (alpm->smbus) {
24093023Snsouch		device_delete_child(dev, alpm->smbus);
24193023Snsouch		alpm->smbus = NULL;
24293023Snsouch	}
243165951Sjhb	mtx_destroy(&alpm->lock);
24443973Snsouch
24593023Snsouch	if (alpm->res)
24693023Snsouch		bus_release_resource(dev, SYS_RES_IOPORT, SMBBA, alpm->res);
24743973Snsouch
24843973Snsouch	return (0);
24943973Snsouch}
25043973Snsouch
25143973Snsouchstatic int
252162234Sjhbalpm_callback(device_t dev, int index, void *data)
25343973Snsouch{
25443973Snsouch	int error = 0;
25543973Snsouch
25643973Snsouch	switch (index) {
25743973Snsouch	case SMB_REQUEST_BUS:
25843973Snsouch	case SMB_RELEASE_BUS:
25943973Snsouch		/* ok, bus allocation accepted */
26043973Snsouch		break;
26143973Snsouch	default:
26243973Snsouch		error = EINVAL;
26343973Snsouch	}
26443973Snsouch
26543973Snsouch	return (error);
26643973Snsouch}
26743973Snsouch
26843973Snsouchstatic int
26993023Snsouchalpm_clear(struct alpm_softc *sc)
27043973Snsouch{
27143973Snsouch	ALPM_SMBOUTB(sc, SMBSTS, 0xff);
27243973Snsouch	DELAY(10);
27343973Snsouch
27443973Snsouch	return (0);
27543973Snsouch}
27643973Snsouch
27743973Snsouch#if 0
27843973Snsouchstatic int
27993023Snsouchalpm_abort(struct alpm_softc *sc)
28043973Snsouch{
28143973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, T_OUT_CMD | ABORT_HOST);
28243973Snsouch
28343973Snsouch	return (0);
28443973Snsouch}
28543973Snsouch#endif
28643973Snsouch
28743973Snsouchstatic int
28893023Snsouchalpm_idle(struct alpm_softc *sc)
28943973Snsouch{
29043973Snsouch	u_char sts;
29143973Snsouch
29243973Snsouch	sts = ALPM_SMBINB(sc, SMBSTS);
29343973Snsouch
29443973Snsouch	ALPM_DEBUG(printf("alpm: idle? STS=0x%x\n", sts));
29543973Snsouch
29643973Snsouch	return (sts & IDL_STS);
29743973Snsouch}
29843973Snsouch
29943973Snsouch/*
30043973Snsouch * Poll the SMBus controller
30143973Snsouch */
30243973Snsouchstatic int
30393023Snsouchalpm_wait(struct alpm_softc *sc)
30443973Snsouch{
30543973Snsouch	int count = 10000;
30670606Snsouch	u_char sts = 0;
30743973Snsouch	int error;
30843973Snsouch
30943973Snsouch	/* wait for command to complete and SMBus controller is idle */
310165951Sjhb	while (count--) {
31143973Snsouch		DELAY(10);
31243973Snsouch		sts = ALPM_SMBINB(sc, SMBSTS);
31343973Snsouch		if (sts & SMI_I_STS)
31443973Snsouch			break;
31543973Snsouch	}
31643973Snsouch
31743973Snsouch	ALPM_DEBUG(printf("alpm: STS=0x%x\n", sts));
31843973Snsouch
31943973Snsouch	error = SMB_ENOERR;
32043973Snsouch
32143973Snsouch	if (!count)
32243973Snsouch		error |= SMB_ETIMEOUT;
32343973Snsouch
32443973Snsouch	if (sts & TERMINATE)
32543973Snsouch		error |= SMB_EABORT;
32643973Snsouch
32743973Snsouch	if (sts & BUS_COLLI)
32843973Snsouch		error |= SMB_ENOACK;
32943973Snsouch
33043973Snsouch	if (sts & DEVICE_ERR)
33143973Snsouch		error |= SMB_EBUSERR;
33243973Snsouch
33343973Snsouch	if (error != SMB_ENOERR)
33493023Snsouch		alpm_clear(sc);
33543973Snsouch
33643973Snsouch	return (error);
33743973Snsouch}
33843973Snsouch
33943973Snsouchstatic int
34093023Snsouchalpm_quick(device_t dev, u_char slave, int how)
34143973Snsouch{
34293023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
34343973Snsouch	int error;
34443973Snsouch
345165951Sjhb	ALPM_LOCK(sc);
34693023Snsouch	alpm_clear(sc);
347165951Sjhb	if (!alpm_idle(sc)) {
348165951Sjhb		ALPM_UNLOCK(sc);
34943973Snsouch		return (EBUSY);
350165951Sjhb	}
35143973Snsouch
35243973Snsouch	switch (how) {
35343973Snsouch	case SMB_QWRITE:
35443973Snsouch		ALPM_DEBUG(printf("alpm: QWRITE to 0x%x", slave));
35543973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
35643973Snsouch		break;
35743973Snsouch	case SMB_QREAD:
35843973Snsouch		ALPM_DEBUG(printf("alpm: QREAD to 0x%x", slave));
35943973Snsouch		ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
36043973Snsouch		break;
36143973Snsouch	default:
36287599Sobrien		panic("%s: unknown QUICK command (%x)!", __func__,
36343973Snsouch			how);
36443973Snsouch	}
36543973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBQUICK);
36643973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
36743973Snsouch
36893023Snsouch	error = alpm_wait(sc);
36943973Snsouch
37043973Snsouch	ALPM_DEBUG(printf(", error=0x%x\n", error));
371165951Sjhb	ALPM_UNLOCK(sc);
37243973Snsouch
37343973Snsouch	return (error);
37443973Snsouch}
37543973Snsouch
37643973Snsouchstatic int
37793023Snsouchalpm_sendb(device_t dev, u_char slave, char byte)
37843973Snsouch{
37993023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
38043973Snsouch	int error;
38143973Snsouch
382165951Sjhb	ALPM_LOCK(sc);
38393023Snsouch	alpm_clear(sc);
384165951Sjhb	if (!alpm_idle(sc)) {
385165951Sjhb		ALPM_UNLOCK(sc);
38643973Snsouch		return (SMB_EBUSY);
387165951Sjhb	}
38843973Snsouch
38943973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
39043973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
39143973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
39243973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
39343973Snsouch
39493023Snsouch	error = alpm_wait(sc);
39543973Snsouch
39643973Snsouch	ALPM_DEBUG(printf("alpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
397165951Sjhb	ALPM_UNLOCK(sc);
39843973Snsouch
39943973Snsouch	return (error);
40043973Snsouch}
40143973Snsouch
40243973Snsouchstatic int
40393023Snsouchalpm_recvb(device_t dev, u_char slave, char *byte)
40443973Snsouch{
40593023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
40643973Snsouch	int error;
40743973Snsouch
408165951Sjhb	ALPM_LOCK(sc);
40993023Snsouch	alpm_clear(sc);
410165951Sjhb	if (!alpm_idle(sc)) {
411165951Sjhb		ALPM_UNLOCK(sc);
41243973Snsouch		return (SMB_EBUSY);
413165951Sjhb	}
41443973Snsouch
41543973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
41643973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBSRBYTE);
41743973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
41843973Snsouch
41993023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR)
42043973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
42143973Snsouch
42243973Snsouch	ALPM_DEBUG(printf("alpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
423165951Sjhb	ALPM_UNLOCK(sc);
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
434165951Sjhb	ALPM_LOCK(sc);
43593023Snsouch	alpm_clear(sc);
436165951Sjhb	if (!alpm_idle(sc)) {
437165951Sjhb		ALPM_UNLOCK(sc);
43843973Snsouch		return (SMB_EBUSY);
439165951Sjhb	}
44043973Snsouch
44143973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
44243973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
44343973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, byte);
44443973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
44543973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
44643973Snsouch
44793023Snsouch	error = alpm_wait(sc);
44843973Snsouch
44943973Snsouch	ALPM_DEBUG(printf("alpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
450165951Sjhb	ALPM_UNLOCK(sc);
45143973Snsouch
45243973Snsouch	return (error);
45343973Snsouch}
45443973Snsouch
45543973Snsouchstatic int
45693023Snsouchalpm_readb(device_t dev, u_char slave, char cmd, char *byte)
45743973Snsouch{
45893023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
45943973Snsouch	int error;
46043973Snsouch
461165951Sjhb	ALPM_LOCK(sc);
46293023Snsouch	alpm_clear(sc);
463165951Sjhb	if (!alpm_idle(sc)) {
464165951Sjhb		ALPM_UNLOCK(sc);
46543973Snsouch		return (SMB_EBUSY);
466165951Sjhb	}
46743973Snsouch
46843973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
46943973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBYTE);
47043973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
47143973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
47243973Snsouch
47393023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR)
47443973Snsouch		*byte = ALPM_SMBINB(sc, SMBHDATA);
47543973Snsouch
47643973Snsouch	ALPM_DEBUG(printf("alpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
477165951Sjhb	ALPM_UNLOCK(sc);
47843973Snsouch
47943973Snsouch	return (error);
48043973Snsouch}
48143973Snsouch
48243973Snsouchstatic int
48393023Snsouchalpm_writew(device_t dev, u_char slave, char cmd, short word)
48443973Snsouch{
48593023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
48643973Snsouch	int error;
48743973Snsouch
488165951Sjhb	ALPM_LOCK(sc);
48993023Snsouch	alpm_clear(sc);
490165951Sjhb	if (!alpm_idle(sc)) {
491165951Sjhb		ALPM_UNLOCK(sc);
49243973Snsouch		return (SMB_EBUSY);
493165951Sjhb	}
49443973Snsouch
49543973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
49643973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
49743973Snsouch	ALPM_SMBOUTB(sc, SMBHDATA, word & 0x00ff);
49843973Snsouch	ALPM_SMBOUTB(sc, SMBHDATB, (word & 0xff00) >> 8);
49943973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
50043973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
50143973Snsouch
50293023Snsouch	error = alpm_wait(sc);
50343973Snsouch
50443973Snsouch	ALPM_DEBUG(printf("alpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
505165951Sjhb	ALPM_UNLOCK(sc);
50643973Snsouch
50743973Snsouch	return (error);
50843973Snsouch}
50943973Snsouch
51043973Snsouchstatic int
51193023Snsouchalpm_readw(device_t dev, u_char slave, char cmd, short *word)
51243973Snsouch{
51393023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
51443973Snsouch	int error;
51543973Snsouch	u_char high, low;
51643973Snsouch
517165951Sjhb	ALPM_LOCK(sc);
51893023Snsouch	alpm_clear(sc);
519165951Sjhb	if (!alpm_idle(sc)) {
520165951Sjhb		ALPM_UNLOCK(sc);
52143973Snsouch		return (SMB_EBUSY);
522165951Sjhb	}
52343973Snsouch
52443973Snsouch	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
52543973Snsouch	ALPM_SMBOUTB(sc, SMBCMD, SMBWRWORD);
52643973Snsouch	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
52743973Snsouch	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
52843973Snsouch
52993023Snsouch	if ((error = alpm_wait(sc)) == SMB_ENOERR) {
53043973Snsouch		low = ALPM_SMBINB(sc, SMBHDATA);
53143973Snsouch		high = ALPM_SMBINB(sc, SMBHDATB);
53243973Snsouch
53343973Snsouch		*word = ((high & 0xff) << 8) | (low & 0xff);
53443973Snsouch	}
53543973Snsouch
53643973Snsouch	ALPM_DEBUG(printf("alpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
537165951Sjhb	ALPM_UNLOCK(sc);
53843973Snsouch
53943973Snsouch	return (error);
54043973Snsouch}
54143973Snsouch
54243973Snsouchstatic int
54393023Snsouchalpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
54443973Snsouch{
54593023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
546162234Sjhb	u_char i;
547162234Sjhb	int error;
54843973Snsouch
549162234Sjhb	if (count < 1 || count > 32)
550162234Sjhb		return (SMB_EINVAL);
551165951Sjhb
552165951Sjhb	ALPM_LOCK(sc);
55393023Snsouch	alpm_clear(sc);
554165951Sjhb	if(!alpm_idle(sc)) {
555165951Sjhb		ALPM_UNLOCK(sc);
55643973Snsouch		return (SMB_EBUSY);
557165951Sjhb	}
55843973Snsouch
559162234Sjhb	ALPM_SMBOUTB(sc, SMBHADDR, slave & ~LSB);
56043973Snsouch
561162234Sjhb	/* set the cmd and reset the
562162234Sjhb	 * 32-byte long internal buffer */
563162234Sjhb	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
56443973Snsouch
565162234Sjhb	ALPM_SMBOUTB(sc, SMBHDATA, count);
56643973Snsouch
567162234Sjhb	/* fill the 32-byte internal buffer */
568162234Sjhb	for (i = 0; i < count; i++) {
569162234Sjhb		ALPM_SMBOUTB(sc, SMBHBLOCK, buf[i]);
570162234Sjhb		DELAY(2);
571162234Sjhb	}
572162234Sjhb	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
573162234Sjhb	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
57443973Snsouch
575162234Sjhb	error = alpm_wait(sc);
57643973Snsouch
57743973Snsouch	ALPM_DEBUG(printf("alpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
578165951Sjhb	ALPM_UNLOCK(sc);
57943973Snsouch
58043973Snsouch	return (error);
58143973Snsouch}
58243973Snsouch
58343973Snsouchstatic int
584162234Sjhbalpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
58543973Snsouch{
58693023Snsouch	struct alpm_softc *sc = (struct alpm_softc *)device_get_softc(dev);
587162234Sjhb	u_char data, len, i;
588162234Sjhb	int error;
58943973Snsouch
590162234Sjhb	if (*count < 1 || *count > 32)
591162234Sjhb		return (SMB_EINVAL);
592165951Sjhb
593165951Sjhb	ALPM_LOCK(sc);
59493023Snsouch	alpm_clear(sc);
595165951Sjhb	if (!alpm_idle(sc)) {
596165951Sjhb		ALPM_UNLOCK(sc);
59743973Snsouch		return (SMB_EBUSY);
598165951Sjhb	}
59943973Snsouch
600162234Sjhb	ALPM_SMBOUTB(sc, SMBHADDR, slave | LSB);
60143973Snsouch
602162234Sjhb	/* set the cmd and reset the
603162234Sjhb	 * 32-byte long internal buffer */
604162234Sjhb	ALPM_SMBOUTB(sc, SMBCMD, SMBWRBLOCK | SMB_BLK_CLR);
60543973Snsouch
606162234Sjhb	ALPM_SMBOUTB(sc, SMBHCMD, cmd);
607162234Sjhb	ALPM_SMBOUTB(sc, SMBSTART, 0xff);
60843973Snsouch
609162234Sjhb	if ((error = alpm_wait(sc)) != SMB_ENOERR)
61043973Snsouch			goto error;
61143973Snsouch
612162234Sjhb	len = ALPM_SMBINB(sc, SMBHDATA);
61343973Snsouch
614162234Sjhb	/* read the 32-byte internal buffer */
615162234Sjhb	for (i = 0; i < len; i++) {
616162234Sjhb		data = ALPM_SMBINB(sc, SMBHBLOCK);
617162234Sjhb		if (i < *count)
618162234Sjhb			buf[i] = data;
619162234Sjhb		DELAY(2);
620162234Sjhb	}
621162234Sjhb	*count = len;
62243973Snsouch
62343973Snsoucherror:
624162234Sjhb	ALPM_DEBUG(printf("alpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
625165951Sjhb	ALPM_UNLOCK(sc);
62643973Snsouch
62743973Snsouch	return (error);
62843973Snsouch}
62943973Snsouch
63093023Snsouchstatic devclass_t alpm_devclass;
63193023Snsouch
63293023Snsouchstatic device_method_t alpm_methods[] = {
63393023Snsouch	/* device interface */
63493023Snsouch	DEVMETHOD(device_probe,		alpm_probe),
63593023Snsouch	DEVMETHOD(device_attach,	alpm_attach),
63693023Snsouch	DEVMETHOD(device_detach,	alpm_detach),
63793023Snsouch
63893023Snsouch	/* smbus interface */
63993023Snsouch	DEVMETHOD(smbus_callback,	alpm_callback),
64093023Snsouch	DEVMETHOD(smbus_quick,		alpm_quick),
64193023Snsouch	DEVMETHOD(smbus_sendb,		alpm_sendb),
64293023Snsouch	DEVMETHOD(smbus_recvb,		alpm_recvb),
64393023Snsouch	DEVMETHOD(smbus_writeb,		alpm_writeb),
64493023Snsouch	DEVMETHOD(smbus_readb,		alpm_readb),
64593023Snsouch	DEVMETHOD(smbus_writew,		alpm_writew),
64693023Snsouch	DEVMETHOD(smbus_readw,		alpm_readw),
64793023Snsouch	DEVMETHOD(smbus_bwrite,		alpm_bwrite),
64893023Snsouch	DEVMETHOD(smbus_bread,		alpm_bread),
64993023Snsouch
65093023Snsouch	{ 0, 0 }
65193023Snsouch};
65293023Snsouch
65393023Snsouchstatic driver_t alpm_driver = {
65493023Snsouch	"alpm",
65593023Snsouch	alpm_methods,
65693023Snsouch	sizeof(struct alpm_softc)
65793023Snsouch};
65893023Snsouch
65993023SnsouchDRIVER_MODULE(alpm, pci, alpm_driver, alpm_devclass, 0, 0);
660162234SjhbDRIVER_MODULE(smbus, alpm, smbus_driver, smbus_devclass, 0, 0);
661113506SmdoddMODULE_DEPEND(alpm, pci, 1, 1, 1);
66293023SnsouchMODULE_DEPEND(alpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
66393023SnsouchMODULE_VERSION(alpm, 1);
664