183553Smurray/*-
283553Smurray * Copyright (c) 2000 Matthew C. Forman
383553Smurray *
483553Smurray * Based (heavily) on alpm.c which is:
583553Smurray *
683553Smurray * Copyright (c) 1998, 1999 Nicolas Souchu
783553Smurray * All rights reserved.
883553Smurray *
983553Smurray * Redistribution and use in source and binary forms, with or without
1083553Smurray * modification, are permitted provided that the following conditions
1183553Smurray * are met:
1283553Smurray * 1. Redistributions of source code must retain the above copyright
1383553Smurray *    notice, this list of conditions and the following disclaimer.
1483553Smurray * 2. Redistributions in binary form must reproduce the above copyright
1583553Smurray *    notice, this list of conditions and the following disclaimer in the
1683553Smurray *    documentation and/or other materials provided with the distribution.
1783553Smurray *
1883553Smurray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1983553Smurray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2083553Smurray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2183553Smurray * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2283553Smurray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2383553Smurray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2483553Smurray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2583553Smurray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2683553Smurray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2783553Smurray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2883553Smurray * SUCH DAMAGE.
2983553Smurray */
3083553Smurray
3183553Smurray/*
3283553Smurray * Power management function/SMBus function support for the AMD 756 chip.
3383553Smurray */
3483553Smurray
35116192Sobrien#include <sys/cdefs.h>
36116192Sobrien__FBSDID("$FreeBSD$");
37116192Sobrien
3883553Smurray#include <sys/param.h>
39165951Sjhb#include <sys/bus.h>
4083553Smurray#include <sys/kernel.h>
41165951Sjhb#include <sys/lock.h>
42165951Sjhb#include <sys/module.h>
43165951Sjhb#include <sys/mutex.h>
4483553Smurray#include <sys/systm.h>
4583553Smurray
4683553Smurray#include <machine/bus.h>
4783553Smurray#include <machine/resource.h>
4883553Smurray#include <sys/rman.h>
4983553Smurray
50119288Simp#include <dev/pci/pcivar.h>
51119288Simp#include <dev/pci/pcireg.h>
5283553Smurray
5383553Smurray#include <dev/smbus/smbconf.h>
5483553Smurray#include "smbus_if.h"
5583553Smurray
5683553Smurray#define AMDPM_DEBUG(x)	if (amdpm_debug) (x)
5783553Smurray
5883553Smurray#ifdef DEBUG
5983553Smurraystatic int amdpm_debug = 1;
6083553Smurray#else
6183553Smurraystatic int amdpm_debug = 0;
6283553Smurray#endif
6383553Smurray
6483553Smurray#define AMDPM_VENDORID_AMD 0x1022
6583553Smurray#define AMDPM_DEVICEID_AMD756PM 0x740b
66119655Sdfr#define AMDPM_DEVICEID_AMD766PM 0x7413
67119655Sdfr#define AMDPM_DEVICEID_AMD768PM 0x7443
68153479Sru#define AMDPM_DEVICEID_AMD8111PM 0x746B
6983553Smurray
70103764Snsouch/* nVidia nForce chipset */
71103764Snsouch#define AMDPM_VENDORID_NVIDIA 0x10de
72103764Snsouch#define AMDPM_DEVICEID_NF_SMB 0x01b4
73103764Snsouch
7483553Smurray/* PCI Configuration space registers */
7583553Smurray#define AMDPCI_PMBASE 0x58
76103764Snsouch#define NFPCI_PMBASE  0x14
7783553Smurray
7883553Smurray#define AMDPCI_GEN_CONFIG_PM 0x41
7983553Smurray#define AMDPCI_PMIOEN (1<<7)
8083553Smurray
8183553Smurray#define AMDPCI_SCIINT_CONFIG_PM 0x42
8283553Smurray#define AMDPCI_SCISEL_IRQ11 11
8383553Smurray
8483553Smurray#define AMDPCI_REVID 0x08
8583553Smurray
8683553Smurray/*
8783553Smurray * I/O registers.
8883553Smurray * Base address programmed via AMDPCI_PMBASE.
8983553Smurray */
90103764Snsouch
91119796Sdfr#define AMDSMB_GLOBAL_STATUS (0x00)
9283553Smurray#define AMDSMB_GS_TO_STS (1<<5)
9383553Smurray#define AMDSMB_GS_HCYC_STS (1<<4)
9483553Smurray#define AMDSMB_GS_HST_STS (1<<3)
9583553Smurray#define AMDSMB_GS_PRERR_STS (1<<2)
9683553Smurray#define AMDSMB_GS_COL_STS (1<<1)
9783553Smurray#define AMDSMB_GS_ABRT_STS (1<<0)
9883553Smurray#define AMDSMB_GS_CLEAR_STS (AMDSMB_GS_TO_STS|AMDSMB_GS_HCYC_STS|AMDSMB_GS_PRERR_STS|AMDSMB_GS_COL_STS|AMDSMB_GS_ABRT_STS)
9983553Smurray
100119796Sdfr#define AMDSMB_GLOBAL_ENABLE (0x02)
10183553Smurray#define AMDSMB_GE_ABORT (1<<5)
10283553Smurray#define AMDSMB_GE_HCYC_EN (1<<4)
10383553Smurray#define AMDSMB_GE_HOST_STC (1<<3)
10483553Smurray#define AMDSMB_GE_CYC_QUICK 0
10583553Smurray#define AMDSMB_GE_CYC_BYTE 1
10683553Smurray#define AMDSMB_GE_CYC_BDATA 2
10783553Smurray#define AMDSMB_GE_CYC_WDATA 3
10883553Smurray#define AMDSMB_GE_CYC_PROCCALL 4
10983553Smurray#define AMDSMB_GE_CYC_BLOCK 5
11083553Smurray
111165951Sjhb#define	LSB		0x1	/* XXX: Better name: Read/Write? */
112165951Sjhb
113119796Sdfr#define AMDSMB_HSTADDR  (0x04)
114119796Sdfr#define AMDSMB_HSTDATA  (0x06)
115119796Sdfr#define AMDSMB_HSTCMD   (0x08)
116119796Sdfr#define AMDSMB_HSTDFIFO (0x09)
117119796Sdfr#define AMDSMB_HSLVDATA (0x0A)
118119796Sdfr#define AMDSMB_HSLVDA   (0x0C)
119119796Sdfr#define AMDSMB_HSLVDDR  (0x0E)
120119796Sdfr#define AMDSMB_SNPADDR  (0x0F)
12183553Smurray
12283553Smurraystruct amdpm_softc {
12383553Smurray	int base;
12483553Smurray	int rid;
12583553Smurray	struct resource *res;
12683553Smurray	device_t smbus;
127165951Sjhb	struct mtx lock;
12883553Smurray};
12983553Smurray
130165951Sjhb#define	AMDPM_LOCK(amdpm)		mtx_lock(&(amdpm)->lock)
131165951Sjhb#define	AMDPM_UNLOCK(amdpm)		mtx_unlock(&(amdpm)->lock)
132165951Sjhb#define	AMDPM_LOCK_ASSERT(amdpm)	mtx_assert(&(amdpm)->lock, MA_OWNED)
133165951Sjhb
134103764Snsouch#define AMDPM_SMBINB(amdpm,register) \
135179622Sjhb	(bus_read_1(amdpm->res, register))
136103764Snsouch#define AMDPM_SMBOUTB(amdpm,register,value) \
137179622Sjhb	(bus_write_1(amdpm->res, register, value))
138103764Snsouch#define AMDPM_SMBINW(amdpm,register) \
139179622Sjhb	(bus_read_2(amdpm->res, register))
140103764Snsouch#define AMDPM_SMBOUTW(amdpm,register,value) \
141179622Sjhb	(bus_write_2(amdpm->res, register, value))
14283553Smurray
143165951Sjhbstatic int	amdpm_detach(device_t dev);
144165951Sjhb
14583553Smurraystatic int
14683553Smurrayamdpm_probe(device_t dev)
14783553Smurray{
14883553Smurray	u_long base;
149119796Sdfr	u_int16_t vid;
150119655Sdfr	u_int16_t did;
151119655Sdfr
152119796Sdfr	vid = pci_get_vendor(dev);
153119655Sdfr	did = pci_get_device(dev);
154119796Sdfr	if ((vid == AMDPM_VENDORID_AMD) &&
155119655Sdfr	    ((did == AMDPM_DEVICEID_AMD756PM) ||
156119655Sdfr	     (did == AMDPM_DEVICEID_AMD766PM) ||
157128472Sobrien	     (did == AMDPM_DEVICEID_AMD768PM) ||
158128472Sobrien	     (did == AMDPM_DEVICEID_AMD8111PM))) {
159128472Sobrien		device_set_desc(dev, "AMD 756/766/768/8111 Power Management Controller");
160119798Sdfr
161153491Sru		/*
162119798Sdfr		 * We have to do this, since the BIOS won't give us the
163119798Sdfr		 * resource info (not mine, anyway).
164119798Sdfr		 */
165119798Sdfr		base = pci_read_config(dev, AMDPCI_PMBASE, 4);
166119798Sdfr		base &= 0xff00;
167119798Sdfr		bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE,
168119798Sdfr				 base+0xe0, 32);
169142398Simp		return (BUS_PROBE_DEFAULT);
17083553Smurray	}
171119796Sdfr
172153491Sru	if ((vid == AMDPM_VENDORID_NVIDIA) &&
173153491Sru	    (did == AMDPM_DEVICEID_NF_SMB)) {
174153491Sru		device_set_desc(dev, "nForce SMBus Controller");
175119796Sdfr
176153491Sru		/*
177153491Sru		* We have to do this, since the BIOS won't give us the
178153491Sru		* resource info (not mine, anyway).
179153491Sru		*/
180153491Sru		base = pci_read_config(dev, NFPCI_PMBASE, 4);
181153491Sru		base &= 0xff00;
182153491Sru		bus_set_resource(dev, SYS_RES_IOPORT, NFPCI_PMBASE,
183153491Sru				 base, 32);
184119796Sdfr
185153491Sru		return (BUS_PROBE_DEFAULT);
186119796Sdfr	}
187119796Sdfr
18883553Smurray	return ENXIO;
18983553Smurray}
19083553Smurray
19183553Smurraystatic int
19283553Smurrayamdpm_attach(device_t dev)
19383553Smurray{
19483553Smurray	struct amdpm_softc *amdpm_sc = device_get_softc(dev);
19583553Smurray	u_char val_b;
196153491Sru
19783553Smurray	/* Enable I/O block access */
19883553Smurray	val_b = pci_read_config(dev, AMDPCI_GEN_CONFIG_PM, 1);
19983553Smurray	pci_write_config(dev, AMDPCI_GEN_CONFIG_PM, val_b | AMDPCI_PMIOEN, 1);
20083553Smurray
20183553Smurray	/* Allocate I/O space */
202119796Sdfr	if (pci_get_vendor(dev) == AMDPM_VENDORID_AMD)
203119796Sdfr		amdpm_sc->rid = AMDPCI_PMBASE;
204153491Sru	else
205153419Sjhb		amdpm_sc->rid = NFPCI_PMBASE;
206127135Snjl	amdpm_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
207127135Snjl		&amdpm_sc->rid, RF_ACTIVE);
208153491Sru
20983553Smurray	if (amdpm_sc->res == NULL) {
21083553Smurray		device_printf(dev, "could not map i/o space\n");
21183553Smurray		return (ENXIO);
212153491Sru	}
21383553Smurray
214165951Sjhb	mtx_init(&amdpm_sc->lock, device_get_nameunit(dev), "amdpm", MTX_DEF);
21583553Smurray
216103764Snsouch	/* Allocate a new smbus device */
217103764Snsouch	amdpm_sc->smbus = device_add_child(dev, "smbus", -1);
218165951Sjhb	if (!amdpm_sc->smbus) {
219165951Sjhb		amdpm_detach(dev);
220119798Sdfr		return (EINVAL);
221165951Sjhb	}
222103764Snsouch
223103764Snsouch	bus_generic_attach(dev);
224103764Snsouch
22583553Smurray	return (0);
22683553Smurray}
22783553Smurray
22883553Smurraystatic int
229103764Snsouchamdpm_detach(device_t dev)
23083553Smurray{
231103764Snsouch	struct amdpm_softc *amdpm_sc = device_get_softc(dev);
23283553Smurray
233103764Snsouch	if (amdpm_sc->smbus) {
234119798Sdfr		device_delete_child(dev, amdpm_sc->smbus);
235119798Sdfr		amdpm_sc->smbus = NULL;
236103764Snsouch	}
23793040Snsouch
238165951Sjhb	mtx_destroy(&amdpm_sc->lock);
239103764Snsouch	if (amdpm_sc->res)
240119798Sdfr		bus_release_resource(dev, SYS_RES_IOPORT, amdpm_sc->rid,
241119798Sdfr				     amdpm_sc->res);
24283553Smurray
24383553Smurray	return (0);
24483553Smurray}
24583553Smurray
24683553Smurraystatic int
247162234Sjhbamdpm_callback(device_t dev, int index, void *data)
24883553Smurray{
24983553Smurray	int error = 0;
25083553Smurray
25183553Smurray	switch (index) {
25283553Smurray	case SMB_REQUEST_BUS:
25383553Smurray	case SMB_RELEASE_BUS:
25483553Smurray		break;
25583553Smurray	default:
25683553Smurray		error = EINVAL;
25783553Smurray	}
25883553Smurray
25983553Smurray	return (error);
26083553Smurray}
26183553Smurray
26283553Smurraystatic int
263103764Snsouchamdpm_clear(struct amdpm_softc *sc)
26483553Smurray{
265165951Sjhb
266165951Sjhb	AMDPM_LOCK_ASSERT(sc);
26783553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_STATUS, AMDSMB_GS_CLEAR_STS);
26883553Smurray	DELAY(10);
26983553Smurray
27083553Smurray	return (0);
27183553Smurray}
27283553Smurray
27391265Speter#if 0
27483553Smurraystatic int
275103764Snsouchamdpm_abort(struct amdpm_softc *sc)
27683553Smurray{
27783553Smurray	u_short l;
278153491Sru
27983553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
28083553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, l | AMDSMB_GE_ABORT);
28183553Smurray
28283553Smurray	return (0);
28383553Smurray}
28491265Speter#endif
28583553Smurray
28683553Smurraystatic int
287103764Snsouchamdpm_idle(struct amdpm_softc *sc)
28883553Smurray{
28983553Smurray	u_short sts;
29083553Smurray
291165951Sjhb	AMDPM_LOCK_ASSERT(sc);
29283553Smurray	sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS);
29383553Smurray
29483553Smurray	AMDPM_DEBUG(printf("amdpm: busy? STS=0x%x\n", sts));
29583553Smurray
29683553Smurray	return (~(sts & AMDSMB_GS_HST_STS));
29783553Smurray}
29883553Smurray
29983553Smurray/*
30083553Smurray * Poll the SMBus controller
30183553Smurray */
30283553Smurraystatic int
303103764Snsouchamdpm_wait(struct amdpm_softc *sc)
30483553Smurray{
30583553Smurray	int count = 10000;
30683553Smurray	u_short sts = 0;
30783553Smurray	int error;
30883553Smurray
309165951Sjhb	AMDPM_LOCK_ASSERT(sc);
31083553Smurray	/* Wait for command to complete (SMBus controller is idle) */
31183553Smurray	while(count--) {
31283553Smurray		DELAY(10);
31383553Smurray		sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS);
31483553Smurray		if (!(sts & AMDSMB_GS_HST_STS))
31583553Smurray			break;
31683553Smurray	}
31783553Smurray
31883553Smurray	AMDPM_DEBUG(printf("amdpm: STS=0x%x (count=%d)\n", sts, count));
31983553Smurray
32083553Smurray	error = SMB_ENOERR;
32183553Smurray
32283553Smurray	if (!count)
32383553Smurray		error |= SMB_ETIMEOUT;
32483553Smurray
32583553Smurray	if (sts & AMDSMB_GS_ABRT_STS)
32683553Smurray		error |= SMB_EABORT;
32783553Smurray
32883553Smurray	if (sts & AMDSMB_GS_COL_STS)
32983553Smurray		error |= SMB_ENOACK;
33083553Smurray
33183553Smurray	if (sts & AMDSMB_GS_PRERR_STS)
33283553Smurray		error |= SMB_EBUSERR;
33383553Smurray
33483553Smurray	if (error != SMB_ENOERR)
335103764Snsouch		amdpm_clear(sc);
33683553Smurray
33783553Smurray	return (error);
33883553Smurray}
33983553Smurray
34083553Smurraystatic int
341103764Snsouchamdpm_quick(device_t dev, u_char slave, int how)
34283553Smurray{
343103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
34483553Smurray	int error;
34583553Smurray	u_short l;
34683553Smurray
347165951Sjhb	AMDPM_LOCK(sc);
348103764Snsouch	amdpm_clear(sc);
349165951Sjhb	if (!amdpm_idle(sc)) {
350165951Sjhb		AMDPM_UNLOCK(sc);
35183553Smurray		return (EBUSY);
352165951Sjhb	}
35383553Smurray
35483553Smurray	switch (how) {
35583553Smurray	case SMB_QWRITE:
35683553Smurray		AMDPM_DEBUG(printf("amdpm: QWRITE to 0x%x", slave));
35783553Smurray		AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
35883553Smurray		break;
35983553Smurray	case SMB_QREAD:
36083553Smurray		AMDPM_DEBUG(printf("amdpm: QREAD to 0x%x", slave));
36183553Smurray		AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
36283553Smurray		break;
36383553Smurray	default:
36487599Sobrien		panic("%s: unknown QUICK command (%x)!", __func__, how);
36583553Smurray	}
36683553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
36783553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_QUICK | AMDSMB_GE_HOST_STC);
36883553Smurray
369103764Snsouch	error = amdpm_wait(sc);
37083553Smurray
37183553Smurray	AMDPM_DEBUG(printf(", error=0x%x\n", error));
372165951Sjhb	AMDPM_UNLOCK(sc);
37383553Smurray
37483553Smurray	return (error);
37583553Smurray}
37683553Smurray
37783553Smurraystatic int
378103764Snsouchamdpm_sendb(device_t dev, u_char slave, char byte)
37983553Smurray{
380103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
38183553Smurray	int error;
38283553Smurray	u_short l;
38383553Smurray
384165951Sjhb	AMDPM_LOCK(sc);
385103764Snsouch	amdpm_clear(sc);
386165951Sjhb	if (!amdpm_idle(sc)) {
387165951Sjhb		AMDPM_UNLOCK(sc);
38883553Smurray		return (SMB_EBUSY);
389165951Sjhb	}
39083553Smurray
39183553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
39283553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
39383553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
39483553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC);
39583553Smurray
396103764Snsouch	error = amdpm_wait(sc);
39783553Smurray
39883553Smurray	AMDPM_DEBUG(printf("amdpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
399165951Sjhb	AMDPM_UNLOCK(sc);
40083553Smurray
40183553Smurray	return (error);
40283553Smurray}
40383553Smurray
40483553Smurraystatic int
405103764Snsouchamdpm_recvb(device_t dev, u_char slave, char *byte)
40683553Smurray{
407103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
40883553Smurray	int error;
40983553Smurray	u_short l;
41083553Smurray
411165951Sjhb	AMDPM_LOCK(sc);
412103764Snsouch	amdpm_clear(sc);
413165951Sjhb	if (!amdpm_idle(sc)) {
414165951Sjhb		AMDPM_UNLOCK(sc);
41583553Smurray		return (SMB_EBUSY);
416165951Sjhb	}
41783553Smurray
41883553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
41983553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
42083553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC);
42183553Smurray
422103764Snsouch	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
42383553Smurray		*byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
42483553Smurray
42583553Smurray	AMDPM_DEBUG(printf("amdpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
426165951Sjhb	AMDPM_UNLOCK(sc);
42783553Smurray
42883553Smurray	return (error);
42983553Smurray}
43083553Smurray
43183553Smurraystatic int
432103764Snsouchamdpm_writeb(device_t dev, u_char slave, char cmd, char byte)
43383553Smurray{
434103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
43583553Smurray	int error;
43683553Smurray	u_short l;
43783553Smurray
438165951Sjhb	AMDPM_LOCK(sc);
439103764Snsouch	amdpm_clear(sc);
440165951Sjhb	if (!amdpm_idle(sc)) {
441165951Sjhb		AMDPM_UNLOCK(sc);
44283553Smurray		return (SMB_EBUSY);
443165951Sjhb	}
44483553Smurray
44583553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
44683553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
44783553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
44883553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
44983553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC);
45083553Smurray
451103764Snsouch	error = amdpm_wait(sc);
45283553Smurray
45383553Smurray	AMDPM_DEBUG(printf("amdpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
454165951Sjhb	AMDPM_UNLOCK(sc);
45583553Smurray
45683553Smurray	return (error);
45783553Smurray}
45883553Smurray
45983553Smurraystatic int
460103764Snsouchamdpm_readb(device_t dev, u_char slave, char cmd, char *byte)
46183553Smurray{
462103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
46383553Smurray	int error;
46483553Smurray	u_short l;
46583553Smurray
466165951Sjhb	AMDPM_LOCK(sc);
467103764Snsouch	amdpm_clear(sc);
468165951Sjhb	if (!amdpm_idle(sc)) {
469165951Sjhb		AMDPM_UNLOCK(sc);
47083553Smurray		return (SMB_EBUSY);
471165951Sjhb	}
47283553Smurray
47383553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
47483553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
47583553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
47683553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC);
47783553Smurray
478103764Snsouch	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
47983553Smurray		*byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
48083553Smurray
48183553Smurray	AMDPM_DEBUG(printf("amdpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
482165951Sjhb	AMDPM_UNLOCK(sc);
48383553Smurray
48483553Smurray	return (error);
48583553Smurray}
48683553Smurray
48783553Smurraystatic int
488103764Snsouchamdpm_writew(device_t dev, u_char slave, char cmd, short word)
48983553Smurray{
490103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
49183553Smurray	int error;
49283553Smurray	u_short l;
49383553Smurray
494165951Sjhb	AMDPM_LOCK(sc);
495103764Snsouch	amdpm_clear(sc);
496165951Sjhb	if (!amdpm_idle(sc)) {
497165951Sjhb		AMDPM_UNLOCK(sc);
49883553Smurray		return (SMB_EBUSY);
499165951Sjhb	}
50083553Smurray
50183553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
50283553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, word);
50383553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
50483553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
50583553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC);
50683553Smurray
507103764Snsouch	error = amdpm_wait(sc);
50883553Smurray
50983553Smurray	AMDPM_DEBUG(printf("amdpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
510165951Sjhb	AMDPM_UNLOCK(sc);
51183553Smurray
51283553Smurray	return (error);
51383553Smurray}
51483553Smurray
51583553Smurraystatic int
516103764Snsouchamdpm_readw(device_t dev, u_char slave, char cmd, short *word)
51783553Smurray{
518103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
51983553Smurray	int error;
52083553Smurray	u_short l;
52183553Smurray
522165951Sjhb	AMDPM_LOCK(sc);
523103764Snsouch	amdpm_clear(sc);
524165951Sjhb	if (!amdpm_idle(sc)) {
525165951Sjhb		AMDPM_UNLOCK(sc);
52683553Smurray		return (SMB_EBUSY);
527165951Sjhb	}
52883553Smurray
52983553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
53083553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
53183553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
53283553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC);
53383553Smurray
534103764Snsouch	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
53583553Smurray		*word = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
53683553Smurray
53783553Smurray	AMDPM_DEBUG(printf("amdpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
538165951Sjhb	AMDPM_UNLOCK(sc);
53983553Smurray
54083553Smurray	return (error);
54183553Smurray}
54283553Smurray
54383553Smurraystatic int
544103764Snsouchamdpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
54583553Smurray{
546103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
547162234Sjhb	u_char i;
548162234Sjhb	int error;
54983553Smurray	u_short l;
55083553Smurray
551162234Sjhb	if (count < 1 || count > 32)
552162234Sjhb		return (SMB_EINVAL);
553165951Sjhb
554165951Sjhb	AMDPM_LOCK(sc);
555103764Snsouch	amdpm_clear(sc);
556165951Sjhb	if (!amdpm_idle(sc)) {
557165951Sjhb		AMDPM_UNLOCK(sc);
55883553Smurray		return (SMB_EBUSY);
559165951Sjhb	}
56083553Smurray
561162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
562153491Sru
563162234Sjhb	/*
564162234Sjhb	 * Do we have to reset the internal 32-byte buffer?
565162234Sjhb	 * Can't see how to do this from the data sheet.
566162234Sjhb	 */
567162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, count);
56883553Smurray
569162234Sjhb	/* Fill the 32-byte internal buffer */
570162234Sjhb	for (i = 0; i < count; i++) {
571162234Sjhb		AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[i]);
572162234Sjhb		DELAY(2);
573162234Sjhb	}
574162234Sjhb	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
575162234Sjhb	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
576162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
577162234Sjhb	    (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
57883553Smurray
579162234Sjhb	error = amdpm_wait(sc);
58083553Smurray
58183553Smurray	AMDPM_DEBUG(printf("amdpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
582165951Sjhb	AMDPM_UNLOCK(sc);
58383553Smurray
58483553Smurray	return (error);
58583553Smurray}
58683553Smurray
58783553Smurraystatic int
588162234Sjhbamdpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
58983553Smurray{
590103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
591162234Sjhb	u_char data, len, i;
592162234Sjhb	int error;
59383553Smurray	u_short l;
59483553Smurray
595162234Sjhb	if (*count < 1 || *count > 32)
596162234Sjhb		return (SMB_EINVAL);
597165951Sjhb
598165951Sjhb	AMDPM_LOCK(sc);
599103764Snsouch	amdpm_clear(sc);
600165951Sjhb	if (!amdpm_idle(sc)) {
601165951Sjhb		AMDPM_UNLOCK(sc);
60283553Smurray		return (SMB_EBUSY);
603165951Sjhb	}
60483553Smurray
605162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
606153491Sru
607162234Sjhb	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
60883553Smurray
609162234Sjhb	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
610162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
611162234Sjhb	    (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
612153491Sru
613162234Sjhb	if ((error = amdpm_wait(sc)) != SMB_ENOERR)
614162234Sjhb		goto error;
61583553Smurray
616162234Sjhb	len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
61783553Smurray
618162234Sjhb	/* Read the 32-byte internal buffer */
619162234Sjhb	for (i = 0; i < len; i++) {
620162234Sjhb		data = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO);
621162234Sjhb		if (i < *count)
622162234Sjhb			buf[i] = data;
623162234Sjhb		DELAY(2);
624162234Sjhb	}
625162234Sjhb	*count = len;
62683553Smurray
62783553Smurrayerror:
628162234Sjhb	AMDPM_DEBUG(printf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
629165951Sjhb	AMDPM_UNLOCK(sc);
63083553Smurray
63183553Smurray	return (error);
63283553Smurray}
63383553Smurray
634153491Srustatic devclass_t amdpm_devclass;
63583553Smurray
63683553Smurraystatic device_method_t amdpm_methods[] = {
63783553Smurray	/* Device interface */
63883553Smurray	DEVMETHOD(device_probe,		amdpm_probe),
63983553Smurray	DEVMETHOD(device_attach,	amdpm_attach),
640103764Snsouch	DEVMETHOD(device_detach,	amdpm_detach),
641153491Sru
642103764Snsouch	/* SMBus interface */
643103764Snsouch	DEVMETHOD(smbus_callback,	amdpm_callback),
644103764Snsouch	DEVMETHOD(smbus_quick,		amdpm_quick),
645103764Snsouch	DEVMETHOD(smbus_sendb,		amdpm_sendb),
646103764Snsouch	DEVMETHOD(smbus_recvb,		amdpm_recvb),
647103764Snsouch	DEVMETHOD(smbus_writeb,		amdpm_writeb),
648103764Snsouch	DEVMETHOD(smbus_readb,		amdpm_readb),
649103764Snsouch	DEVMETHOD(smbus_writew,		amdpm_writew),
650103764Snsouch	DEVMETHOD(smbus_readw,		amdpm_readw),
651103764Snsouch	DEVMETHOD(smbus_bwrite,		amdpm_bwrite),
652103764Snsouch	DEVMETHOD(smbus_bread,		amdpm_bread),
653153491Sru
65483553Smurray	{ 0, 0 }
65583553Smurray};
65683553Smurray
65783553Smurraystatic driver_t amdpm_driver = {
65883553Smurray	"amdpm",
65983553Smurray	amdpm_methods,
66083553Smurray	sizeof(struct amdpm_softc),
66183553Smurray};
66283553Smurray
66383553SmurrayDRIVER_MODULE(amdpm, pci, amdpm_driver, amdpm_devclass, 0, 0);
664162234SjhbDRIVER_MODULE(smbus, amdpm, smbus_driver, smbus_devclass, 0, 0);
665103764Snsouch
666113506SmdoddMODULE_DEPEND(amdpm, pci, 1, 1, 1);
66793040SnsouchMODULE_DEPEND(amdpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
66893040SnsouchMODULE_VERSION(amdpm, 1);
669