amdpm.c revision 165951
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: head/sys/pci/amdpm.c 165951 2007-01-11 19:56:24Z jhb $");
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;
126103764Snsouch	bus_space_tag_t smbst;
127103764Snsouch	bus_space_handle_t smbsh;
12883553Smurray	device_t smbus;
129165951Sjhb	struct mtx lock;
13083553Smurray};
13183553Smurray
132165951Sjhb#define	AMDPM_LOCK(amdpm)		mtx_lock(&(amdpm)->lock)
133165951Sjhb#define	AMDPM_UNLOCK(amdpm)		mtx_unlock(&(amdpm)->lock)
134165951Sjhb#define	AMDPM_LOCK_ASSERT(amdpm)	mtx_assert(&(amdpm)->lock, MA_OWNED)
135165951Sjhb
136103764Snsouch#define AMDPM_SMBINB(amdpm,register) \
137103764Snsouch	(bus_space_read_1(amdpm->smbst, amdpm->smbsh, register))
138103764Snsouch#define AMDPM_SMBOUTB(amdpm,register,value) \
139103764Snsouch	(bus_space_write_1(amdpm->smbst, amdpm->smbsh, register, value))
140103764Snsouch#define AMDPM_SMBINW(amdpm,register) \
141103764Snsouch	(bus_space_read_2(amdpm->smbst, amdpm->smbsh, register))
142103764Snsouch#define AMDPM_SMBOUTW(amdpm,register,value) \
143103764Snsouch	(bus_space_write_2(amdpm->smbst, amdpm->smbsh, register, value))
14483553Smurray
145165951Sjhbstatic int	amdpm_detach(device_t dev);
146165951Sjhb
14783553Smurraystatic int
14883553Smurrayamdpm_probe(device_t dev)
14983553Smurray{
15083553Smurray	u_long base;
151119796Sdfr	u_int16_t vid;
152119655Sdfr	u_int16_t did;
153119655Sdfr
154119796Sdfr	vid = pci_get_vendor(dev);
155119655Sdfr	did = pci_get_device(dev);
156119796Sdfr	if ((vid == AMDPM_VENDORID_AMD) &&
157119655Sdfr	    ((did == AMDPM_DEVICEID_AMD756PM) ||
158119655Sdfr	     (did == AMDPM_DEVICEID_AMD766PM) ||
159128472Sobrien	     (did == AMDPM_DEVICEID_AMD768PM) ||
160128472Sobrien	     (did == AMDPM_DEVICEID_AMD8111PM))) {
161128472Sobrien		device_set_desc(dev, "AMD 756/766/768/8111 Power Management Controller");
162119798Sdfr
163153491Sru		/*
164119798Sdfr		 * We have to do this, since the BIOS won't give us the
165119798Sdfr		 * resource info (not mine, anyway).
166119798Sdfr		 */
167119798Sdfr		base = pci_read_config(dev, AMDPCI_PMBASE, 4);
168119798Sdfr		base &= 0xff00;
169119798Sdfr		bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE,
170119798Sdfr				 base+0xe0, 32);
171142398Simp		return (BUS_PROBE_DEFAULT);
17283553Smurray	}
173119796Sdfr
174153491Sru	if ((vid == AMDPM_VENDORID_NVIDIA) &&
175153491Sru	    (did == AMDPM_DEVICEID_NF_SMB)) {
176153491Sru		device_set_desc(dev, "nForce SMBus Controller");
177119796Sdfr
178153491Sru		/*
179153491Sru		* We have to do this, since the BIOS won't give us the
180153491Sru		* resource info (not mine, anyway).
181153491Sru		*/
182153491Sru		base = pci_read_config(dev, NFPCI_PMBASE, 4);
183153491Sru		base &= 0xff00;
184153491Sru		bus_set_resource(dev, SYS_RES_IOPORT, NFPCI_PMBASE,
185153491Sru				 base, 32);
186119796Sdfr
187153491Sru		return (BUS_PROBE_DEFAULT);
188119796Sdfr	}
189119796Sdfr
19083553Smurray	return ENXIO;
19183553Smurray}
19283553Smurray
19383553Smurraystatic int
19483553Smurrayamdpm_attach(device_t dev)
19583553Smurray{
19683553Smurray	struct amdpm_softc *amdpm_sc = device_get_softc(dev);
19783553Smurray	u_char val_b;
198153491Sru
19983553Smurray	/* Enable I/O block access */
20083553Smurray	val_b = pci_read_config(dev, AMDPCI_GEN_CONFIG_PM, 1);
20183553Smurray	pci_write_config(dev, AMDPCI_GEN_CONFIG_PM, val_b | AMDPCI_PMIOEN, 1);
20283553Smurray
20383553Smurray	/* Allocate I/O space */
204119796Sdfr	if (pci_get_vendor(dev) == AMDPM_VENDORID_AMD)
205119796Sdfr		amdpm_sc->rid = AMDPCI_PMBASE;
206153491Sru	else
207153419Sjhb		amdpm_sc->rid = NFPCI_PMBASE;
208127135Snjl	amdpm_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
209127135Snjl		&amdpm_sc->rid, RF_ACTIVE);
210153491Sru
21183553Smurray	if (amdpm_sc->res == NULL) {
21283553Smurray		device_printf(dev, "could not map i/o space\n");
21383553Smurray		return (ENXIO);
214153491Sru	}
21583553Smurray
21683553Smurray	amdpm_sc->smbst = rman_get_bustag(amdpm_sc->res);
21783553Smurray	amdpm_sc->smbsh = rman_get_bushandle(amdpm_sc->res);
218165951Sjhb	mtx_init(&amdpm_sc->lock, device_get_nameunit(dev), "amdpm", MTX_DEF);
21983553Smurray
220103764Snsouch	/* Allocate a new smbus device */
221103764Snsouch	amdpm_sc->smbus = device_add_child(dev, "smbus", -1);
222165951Sjhb	if (!amdpm_sc->smbus) {
223165951Sjhb		amdpm_detach(dev);
224119798Sdfr		return (EINVAL);
225165951Sjhb	}
226103764Snsouch
227103764Snsouch	bus_generic_attach(dev);
228103764Snsouch
22983553Smurray	return (0);
23083553Smurray}
23183553Smurray
23283553Smurraystatic int
233103764Snsouchamdpm_detach(device_t dev)
23483553Smurray{
235103764Snsouch	struct amdpm_softc *amdpm_sc = device_get_softc(dev);
23683553Smurray
237103764Snsouch	if (amdpm_sc->smbus) {
238119798Sdfr		device_delete_child(dev, amdpm_sc->smbus);
239119798Sdfr		amdpm_sc->smbus = NULL;
240103764Snsouch	}
24193040Snsouch
242165951Sjhb	mtx_destroy(&amdpm_sc->lock);
243103764Snsouch	if (amdpm_sc->res)
244119798Sdfr		bus_release_resource(dev, SYS_RES_IOPORT, amdpm_sc->rid,
245119798Sdfr				     amdpm_sc->res);
24683553Smurray
24783553Smurray	return (0);
24883553Smurray}
24983553Smurray
25083553Smurraystatic int
251162234Sjhbamdpm_callback(device_t dev, int index, void *data)
25283553Smurray{
25383553Smurray	int error = 0;
25483553Smurray
25583553Smurray	switch (index) {
25683553Smurray	case SMB_REQUEST_BUS:
25783553Smurray	case SMB_RELEASE_BUS:
25883553Smurray		break;
25983553Smurray	default:
26083553Smurray		error = EINVAL;
26183553Smurray	}
26283553Smurray
26383553Smurray	return (error);
26483553Smurray}
26583553Smurray
26683553Smurraystatic int
267103764Snsouchamdpm_clear(struct amdpm_softc *sc)
26883553Smurray{
269165951Sjhb
270165951Sjhb	AMDPM_LOCK_ASSERT(sc);
27183553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_STATUS, AMDSMB_GS_CLEAR_STS);
27283553Smurray	DELAY(10);
27383553Smurray
27483553Smurray	return (0);
27583553Smurray}
27683553Smurray
27791265Speter#if 0
27883553Smurraystatic int
279103764Snsouchamdpm_abort(struct amdpm_softc *sc)
28083553Smurray{
28183553Smurray	u_short l;
282153491Sru
28383553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
28483553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, l | AMDSMB_GE_ABORT);
28583553Smurray
28683553Smurray	return (0);
28783553Smurray}
28891265Speter#endif
28983553Smurray
29083553Smurraystatic int
291103764Snsouchamdpm_idle(struct amdpm_softc *sc)
29283553Smurray{
29383553Smurray	u_short sts;
29483553Smurray
295165951Sjhb	AMDPM_LOCK_ASSERT(sc);
29683553Smurray	sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS);
29783553Smurray
29883553Smurray	AMDPM_DEBUG(printf("amdpm: busy? STS=0x%x\n", sts));
29983553Smurray
30083553Smurray	return (~(sts & AMDSMB_GS_HST_STS));
30183553Smurray}
30283553Smurray
30383553Smurray/*
30483553Smurray * Poll the SMBus controller
30583553Smurray */
30683553Smurraystatic int
307103764Snsouchamdpm_wait(struct amdpm_softc *sc)
30883553Smurray{
30983553Smurray	int count = 10000;
31083553Smurray	u_short sts = 0;
31183553Smurray	int error;
31283553Smurray
313165951Sjhb	AMDPM_LOCK_ASSERT(sc);
31483553Smurray	/* Wait for command to complete (SMBus controller is idle) */
31583553Smurray	while(count--) {
31683553Smurray		DELAY(10);
31783553Smurray		sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS);
31883553Smurray		if (!(sts & AMDSMB_GS_HST_STS))
31983553Smurray			break;
32083553Smurray	}
32183553Smurray
32283553Smurray	AMDPM_DEBUG(printf("amdpm: STS=0x%x (count=%d)\n", sts, count));
32383553Smurray
32483553Smurray	error = SMB_ENOERR;
32583553Smurray
32683553Smurray	if (!count)
32783553Smurray		error |= SMB_ETIMEOUT;
32883553Smurray
32983553Smurray	if (sts & AMDSMB_GS_ABRT_STS)
33083553Smurray		error |= SMB_EABORT;
33183553Smurray
33283553Smurray	if (sts & AMDSMB_GS_COL_STS)
33383553Smurray		error |= SMB_ENOACK;
33483553Smurray
33583553Smurray	if (sts & AMDSMB_GS_PRERR_STS)
33683553Smurray		error |= SMB_EBUSERR;
33783553Smurray
33883553Smurray	if (error != SMB_ENOERR)
339103764Snsouch		amdpm_clear(sc);
34083553Smurray
34183553Smurray	return (error);
34283553Smurray}
34383553Smurray
34483553Smurraystatic int
345103764Snsouchamdpm_quick(device_t dev, u_char slave, int how)
34683553Smurray{
347103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
34883553Smurray	int error;
34983553Smurray	u_short l;
35083553Smurray
351165951Sjhb	AMDPM_LOCK(sc);
352103764Snsouch	amdpm_clear(sc);
353165951Sjhb	if (!amdpm_idle(sc)) {
354165951Sjhb		AMDPM_UNLOCK(sc);
35583553Smurray		return (EBUSY);
356165951Sjhb	}
35783553Smurray
35883553Smurray	switch (how) {
35983553Smurray	case SMB_QWRITE:
36083553Smurray		AMDPM_DEBUG(printf("amdpm: QWRITE to 0x%x", slave));
36183553Smurray		AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
36283553Smurray		break;
36383553Smurray	case SMB_QREAD:
36483553Smurray		AMDPM_DEBUG(printf("amdpm: QREAD to 0x%x", slave));
36583553Smurray		AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
36683553Smurray		break;
36783553Smurray	default:
36887599Sobrien		panic("%s: unknown QUICK command (%x)!", __func__, how);
36983553Smurray	}
37083553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
37183553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_QUICK | AMDSMB_GE_HOST_STC);
37283553Smurray
373103764Snsouch	error = amdpm_wait(sc);
37483553Smurray
37583553Smurray	AMDPM_DEBUG(printf(", error=0x%x\n", error));
376165951Sjhb	AMDPM_UNLOCK(sc);
37783553Smurray
37883553Smurray	return (error);
37983553Smurray}
38083553Smurray
38183553Smurraystatic int
382103764Snsouchamdpm_sendb(device_t dev, u_char slave, char byte)
38383553Smurray{
384103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
38583553Smurray	int error;
38683553Smurray	u_short l;
38783553Smurray
388165951Sjhb	AMDPM_LOCK(sc);
389103764Snsouch	amdpm_clear(sc);
390165951Sjhb	if (!amdpm_idle(sc)) {
391165951Sjhb		AMDPM_UNLOCK(sc);
39283553Smurray		return (SMB_EBUSY);
393165951Sjhb	}
39483553Smurray
39583553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
39683553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
39783553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
39883553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC);
39983553Smurray
400103764Snsouch	error = amdpm_wait(sc);
40183553Smurray
40283553Smurray	AMDPM_DEBUG(printf("amdpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
403165951Sjhb	AMDPM_UNLOCK(sc);
40483553Smurray
40583553Smurray	return (error);
40683553Smurray}
40783553Smurray
40883553Smurraystatic int
409103764Snsouchamdpm_recvb(device_t dev, u_char slave, char *byte)
41083553Smurray{
411103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
41283553Smurray	int error;
41383553Smurray	u_short l;
41483553Smurray
415165951Sjhb	AMDPM_LOCK(sc);
416103764Snsouch	amdpm_clear(sc);
417165951Sjhb	if (!amdpm_idle(sc)) {
418165951Sjhb		AMDPM_UNLOCK(sc);
41983553Smurray		return (SMB_EBUSY);
420165951Sjhb	}
42183553Smurray
42283553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
42383553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
42483553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC);
42583553Smurray
426103764Snsouch	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
42783553Smurray		*byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
42883553Smurray
42983553Smurray	AMDPM_DEBUG(printf("amdpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
430165951Sjhb	AMDPM_UNLOCK(sc);
43183553Smurray
43283553Smurray	return (error);
43383553Smurray}
43483553Smurray
43583553Smurraystatic int
436103764Snsouchamdpm_writeb(device_t dev, u_char slave, char cmd, char byte)
43783553Smurray{
438103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
43983553Smurray	int error;
44083553Smurray	u_short l;
44183553Smurray
442165951Sjhb	AMDPM_LOCK(sc);
443103764Snsouch	amdpm_clear(sc);
444165951Sjhb	if (!amdpm_idle(sc)) {
445165951Sjhb		AMDPM_UNLOCK(sc);
44683553Smurray		return (SMB_EBUSY);
447165951Sjhb	}
44883553Smurray
44983553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
45083553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
45183553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
45283553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
45383553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC);
45483553Smurray
455103764Snsouch	error = amdpm_wait(sc);
45683553Smurray
45783553Smurray	AMDPM_DEBUG(printf("amdpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
458165951Sjhb	AMDPM_UNLOCK(sc);
45983553Smurray
46083553Smurray	return (error);
46183553Smurray}
46283553Smurray
46383553Smurraystatic int
464103764Snsouchamdpm_readb(device_t dev, u_char slave, char cmd, char *byte)
46583553Smurray{
466103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
46783553Smurray	int error;
46883553Smurray	u_short l;
46983553Smurray
470165951Sjhb	AMDPM_LOCK(sc);
471103764Snsouch	amdpm_clear(sc);
472165951Sjhb	if (!amdpm_idle(sc)) {
473165951Sjhb		AMDPM_UNLOCK(sc);
47483553Smurray		return (SMB_EBUSY);
475165951Sjhb	}
47683553Smurray
47783553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
47883553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
47983553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
48083553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC);
48183553Smurray
482103764Snsouch	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
48383553Smurray		*byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
48483553Smurray
48583553Smurray	AMDPM_DEBUG(printf("amdpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
486165951Sjhb	AMDPM_UNLOCK(sc);
48783553Smurray
48883553Smurray	return (error);
48983553Smurray}
49083553Smurray
49183553Smurraystatic int
492103764Snsouchamdpm_writew(device_t dev, u_char slave, char cmd, short word)
49383553Smurray{
494103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
49583553Smurray	int error;
49683553Smurray	u_short l;
49783553Smurray
498165951Sjhb	AMDPM_LOCK(sc);
499103764Snsouch	amdpm_clear(sc);
500165951Sjhb	if (!amdpm_idle(sc)) {
501165951Sjhb		AMDPM_UNLOCK(sc);
50283553Smurray		return (SMB_EBUSY);
503165951Sjhb	}
50483553Smurray
50583553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
50683553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, word);
50783553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
50883553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
50983553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC);
51083553Smurray
511103764Snsouch	error = amdpm_wait(sc);
51283553Smurray
51383553Smurray	AMDPM_DEBUG(printf("amdpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
514165951Sjhb	AMDPM_UNLOCK(sc);
51583553Smurray
51683553Smurray	return (error);
51783553Smurray}
51883553Smurray
51983553Smurraystatic int
520103764Snsouchamdpm_readw(device_t dev, u_char slave, char cmd, short *word)
52183553Smurray{
522103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
52383553Smurray	int error;
52483553Smurray	u_short l;
52583553Smurray
526165951Sjhb	AMDPM_LOCK(sc);
527103764Snsouch	amdpm_clear(sc);
528165951Sjhb	if (!amdpm_idle(sc)) {
529165951Sjhb		AMDPM_UNLOCK(sc);
53083553Smurray		return (SMB_EBUSY);
531165951Sjhb	}
53283553Smurray
53383553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
53483553Smurray	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
53583553Smurray	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
53683553Smurray	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC);
53783553Smurray
538103764Snsouch	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
53983553Smurray		*word = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
54083553Smurray
54183553Smurray	AMDPM_DEBUG(printf("amdpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
542165951Sjhb	AMDPM_UNLOCK(sc);
54383553Smurray
54483553Smurray	return (error);
54583553Smurray}
54683553Smurray
54783553Smurraystatic int
548103764Snsouchamdpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
54983553Smurray{
550103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
551162234Sjhb	u_char i;
552162234Sjhb	int error;
55383553Smurray	u_short l;
55483553Smurray
555162234Sjhb	if (count < 1 || count > 32)
556162234Sjhb		return (SMB_EINVAL);
557165951Sjhb
558165951Sjhb	AMDPM_LOCK(sc);
559103764Snsouch	amdpm_clear(sc);
560165951Sjhb	if (!amdpm_idle(sc)) {
561165951Sjhb		AMDPM_UNLOCK(sc);
56283553Smurray		return (SMB_EBUSY);
563165951Sjhb	}
56483553Smurray
565162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
566153491Sru
567162234Sjhb	/*
568162234Sjhb	 * Do we have to reset the internal 32-byte buffer?
569162234Sjhb	 * Can't see how to do this from the data sheet.
570162234Sjhb	 */
571162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, count);
57283553Smurray
573162234Sjhb	/* Fill the 32-byte internal buffer */
574162234Sjhb	for (i = 0; i < count; i++) {
575162234Sjhb		AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[i]);
576162234Sjhb		DELAY(2);
577162234Sjhb	}
578162234Sjhb	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
579162234Sjhb	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
580162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
581162234Sjhb	    (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
58283553Smurray
583162234Sjhb	error = amdpm_wait(sc);
58483553Smurray
58583553Smurray	AMDPM_DEBUG(printf("amdpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
586165951Sjhb	AMDPM_UNLOCK(sc);
58783553Smurray
58883553Smurray	return (error);
58983553Smurray}
59083553Smurray
59183553Smurraystatic int
592162234Sjhbamdpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
59383553Smurray{
594103764Snsouch	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
595162234Sjhb	u_char data, len, i;
596162234Sjhb	int error;
59783553Smurray	u_short l;
59883553Smurray
599162234Sjhb	if (*count < 1 || *count > 32)
600162234Sjhb		return (SMB_EINVAL);
601165951Sjhb
602165951Sjhb	AMDPM_LOCK(sc);
603103764Snsouch	amdpm_clear(sc);
604165951Sjhb	if (!amdpm_idle(sc)) {
605165951Sjhb		AMDPM_UNLOCK(sc);
60683553Smurray		return (SMB_EBUSY);
607165951Sjhb	}
60883553Smurray
609162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
610153491Sru
611162234Sjhb	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
61283553Smurray
613162234Sjhb	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
614162234Sjhb	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
615162234Sjhb	    (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
616153491Sru
617162234Sjhb	if ((error = amdpm_wait(sc)) != SMB_ENOERR)
618162234Sjhb		goto error;
61983553Smurray
620162234Sjhb	len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
62183553Smurray
622162234Sjhb	/* Read the 32-byte internal buffer */
623162234Sjhb	for (i = 0; i < len; i++) {
624162234Sjhb		data = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO);
625162234Sjhb		if (i < *count)
626162234Sjhb			buf[i] = data;
627162234Sjhb		DELAY(2);
628162234Sjhb	}
629162234Sjhb	*count = len;
63083553Smurray
63183553Smurrayerror:
632162234Sjhb	AMDPM_DEBUG(printf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
633165951Sjhb	AMDPM_UNLOCK(sc);
63483553Smurray
63583553Smurray	return (error);
63683553Smurray}
63783553Smurray
638153491Srustatic devclass_t amdpm_devclass;
63983553Smurray
64083553Smurraystatic device_method_t amdpm_methods[] = {
64183553Smurray	/* Device interface */
64283553Smurray	DEVMETHOD(device_probe,		amdpm_probe),
64383553Smurray	DEVMETHOD(device_attach,	amdpm_attach),
644103764Snsouch	DEVMETHOD(device_detach,	amdpm_detach),
645153491Sru
646103764Snsouch	/* SMBus interface */
647103764Snsouch	DEVMETHOD(smbus_callback,	amdpm_callback),
648103764Snsouch	DEVMETHOD(smbus_quick,		amdpm_quick),
649103764Snsouch	DEVMETHOD(smbus_sendb,		amdpm_sendb),
650103764Snsouch	DEVMETHOD(smbus_recvb,		amdpm_recvb),
651103764Snsouch	DEVMETHOD(smbus_writeb,		amdpm_writeb),
652103764Snsouch	DEVMETHOD(smbus_readb,		amdpm_readb),
653103764Snsouch	DEVMETHOD(smbus_writew,		amdpm_writew),
654103764Snsouch	DEVMETHOD(smbus_readw,		amdpm_readw),
655103764Snsouch	DEVMETHOD(smbus_bwrite,		amdpm_bwrite),
656103764Snsouch	DEVMETHOD(smbus_bread,		amdpm_bread),
657153491Sru
65883553Smurray	{ 0, 0 }
65983553Smurray};
66083553Smurray
66183553Smurraystatic driver_t amdpm_driver = {
66283553Smurray	"amdpm",
66383553Smurray	amdpm_methods,
66483553Smurray	sizeof(struct amdpm_softc),
66583553Smurray};
66683553Smurray
66783553SmurrayDRIVER_MODULE(amdpm, pci, amdpm_driver, amdpm_devclass, 0, 0);
668162234SjhbDRIVER_MODULE(smbus, amdpm, smbus_driver, smbus_devclass, 0, 0);
669103764Snsouch
670113506SmdoddMODULE_DEPEND(amdpm, pci, 1, 1, 1);
67193040SnsouchMODULE_DEPEND(amdpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
67293040SnsouchMODULE_VERSION(amdpm, 1);
673