xref: /freebsd/sys/dev/amdpm/amdpm.c (revision 11a9117871e6037ae7b8011b243939322efce569)
1f495ec29SRui Paulo /*-
2f495ec29SRui Paulo  * Copyright (c) 2000 Matthew C. Forman
3f495ec29SRui Paulo  *
4f495ec29SRui Paulo  * Based (heavily) on alpm.c which is:
5f495ec29SRui Paulo  *
6f495ec29SRui Paulo  * Copyright (c) 1998, 1999 Nicolas Souchu
7f495ec29SRui Paulo  * All rights reserved.
8f495ec29SRui Paulo  *
9f495ec29SRui Paulo  * Redistribution and use in source and binary forms, with or without
10f495ec29SRui Paulo  * modification, are permitted provided that the following conditions
11f495ec29SRui Paulo  * are met:
12f495ec29SRui Paulo  * 1. Redistributions of source code must retain the above copyright
13f495ec29SRui Paulo  *    notice, this list of conditions and the following disclaimer.
14f495ec29SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
15f495ec29SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
16f495ec29SRui Paulo  *    documentation and/or other materials provided with the distribution.
17f495ec29SRui Paulo  *
18f495ec29SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19f495ec29SRui Paulo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20f495ec29SRui Paulo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21f495ec29SRui Paulo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22f495ec29SRui Paulo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23f495ec29SRui Paulo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24f495ec29SRui Paulo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25f495ec29SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26f495ec29SRui Paulo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27f495ec29SRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28f495ec29SRui Paulo  * SUCH DAMAGE.
29f495ec29SRui Paulo  */
30f495ec29SRui Paulo 
31f495ec29SRui Paulo /*
32f495ec29SRui Paulo  * Power management function/SMBus function support for the AMD 756 chip.
33f495ec29SRui Paulo  */
34f495ec29SRui Paulo 
35f495ec29SRui Paulo #include <sys/param.h>
36f495ec29SRui Paulo #include <sys/bus.h>
37f495ec29SRui Paulo #include <sys/kernel.h>
38f495ec29SRui Paulo #include <sys/lock.h>
39f495ec29SRui Paulo #include <sys/module.h>
40f495ec29SRui Paulo #include <sys/mutex.h>
41f495ec29SRui Paulo #include <sys/systm.h>
42f495ec29SRui Paulo 
43f495ec29SRui Paulo #include <machine/bus.h>
44f495ec29SRui Paulo #include <machine/resource.h>
45f495ec29SRui Paulo #include <sys/rman.h>
46f495ec29SRui Paulo 
47f495ec29SRui Paulo #include <dev/pci/pcivar.h>
48f495ec29SRui Paulo #include <dev/pci/pcireg.h>
49f495ec29SRui Paulo 
50f495ec29SRui Paulo #include <dev/smbus/smbconf.h>
51f495ec29SRui Paulo #include "smbus_if.h"
52f495ec29SRui Paulo 
53f495ec29SRui Paulo #define AMDPM_DEBUG(x)	if (amdpm_debug) (x)
54f495ec29SRui Paulo 
55f495ec29SRui Paulo #ifdef DEBUG
56f495ec29SRui Paulo static int amdpm_debug = 1;
57f495ec29SRui Paulo #else
58f495ec29SRui Paulo static int amdpm_debug = 0;
59f495ec29SRui Paulo #endif
60f495ec29SRui Paulo 
61f495ec29SRui Paulo #define AMDPM_VENDORID_AMD 0x1022
62f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD756PM 0x740b
63f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD766PM 0x7413
64f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD768PM 0x7443
65f495ec29SRui Paulo #define AMDPM_DEVICEID_AMD8111PM 0x746B
66f495ec29SRui Paulo 
674afea6b4SKonstantin Belousov #define AMDPM_VENDORID_HYGON 0x1d94
684afea6b4SKonstantin Belousov 
69f495ec29SRui Paulo /* nVidia nForce chipset */
70f495ec29SRui Paulo #define AMDPM_VENDORID_NVIDIA 0x10de
71f495ec29SRui Paulo #define AMDPM_DEVICEID_NF_SMB 0x01b4
72f495ec29SRui Paulo 
73f495ec29SRui Paulo /* PCI Configuration space registers */
74f495ec29SRui Paulo #define AMDPCI_PMBASE 0x58
75f495ec29SRui Paulo #define NFPCI_PMBASE  0x14
76f495ec29SRui Paulo 
77f495ec29SRui Paulo #define AMDPCI_GEN_CONFIG_PM 0x41
78f495ec29SRui Paulo #define AMDPCI_PMIOEN (1<<7)
79f495ec29SRui Paulo 
80f495ec29SRui Paulo #define AMDPCI_SCIINT_CONFIG_PM 0x42
81f495ec29SRui Paulo #define AMDPCI_SCISEL_IRQ11 11
82f495ec29SRui Paulo 
83f495ec29SRui Paulo #define AMDPCI_REVID 0x08
84f495ec29SRui Paulo 
85f495ec29SRui Paulo /*
86f495ec29SRui Paulo  * I/O registers.
87f495ec29SRui Paulo  * Base address programmed via AMDPCI_PMBASE.
88f495ec29SRui Paulo  */
89f495ec29SRui Paulo 
90f495ec29SRui Paulo #define AMDSMB_GLOBAL_STATUS (0x00)
91f495ec29SRui Paulo #define AMDSMB_GS_TO_STS (1<<5)
92f495ec29SRui Paulo #define AMDSMB_GS_HCYC_STS (1<<4)
93f495ec29SRui Paulo #define AMDSMB_GS_HST_STS (1<<3)
94f495ec29SRui Paulo #define AMDSMB_GS_PRERR_STS (1<<2)
95f495ec29SRui Paulo #define AMDSMB_GS_COL_STS (1<<1)
96f495ec29SRui Paulo #define AMDSMB_GS_ABRT_STS (1<<0)
97f495ec29SRui Paulo #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)
98f495ec29SRui Paulo 
99f495ec29SRui Paulo #define AMDSMB_GLOBAL_ENABLE (0x02)
100f495ec29SRui Paulo #define AMDSMB_GE_ABORT (1<<5)
101f495ec29SRui Paulo #define AMDSMB_GE_HCYC_EN (1<<4)
102f495ec29SRui Paulo #define AMDSMB_GE_HOST_STC (1<<3)
103f495ec29SRui Paulo #define AMDSMB_GE_CYC_QUICK 0
104f495ec29SRui Paulo #define AMDSMB_GE_CYC_BYTE 1
105f495ec29SRui Paulo #define AMDSMB_GE_CYC_BDATA 2
106f495ec29SRui Paulo #define AMDSMB_GE_CYC_WDATA 3
107f495ec29SRui Paulo #define AMDSMB_GE_CYC_PROCCALL 4
108f495ec29SRui Paulo #define AMDSMB_GE_CYC_BLOCK 5
109f495ec29SRui Paulo 
110f495ec29SRui Paulo #define	LSB		0x1	/* XXX: Better name: Read/Write? */
111f495ec29SRui Paulo 
112f495ec29SRui Paulo #define AMDSMB_HSTADDR  (0x04)
113f495ec29SRui Paulo #define AMDSMB_HSTDATA  (0x06)
114f495ec29SRui Paulo #define AMDSMB_HSTCMD   (0x08)
115f495ec29SRui Paulo #define AMDSMB_HSTDFIFO (0x09)
116f495ec29SRui Paulo #define AMDSMB_HSLVDATA (0x0A)
117f495ec29SRui Paulo #define AMDSMB_HSLVDA   (0x0C)
118f495ec29SRui Paulo #define AMDSMB_HSLVDDR  (0x0E)
119f495ec29SRui Paulo #define AMDSMB_SNPADDR  (0x0F)
120f495ec29SRui Paulo 
121f495ec29SRui Paulo struct amdpm_softc {
122f495ec29SRui Paulo 	int base;
123f495ec29SRui Paulo 	int rid;
124f495ec29SRui Paulo 	struct resource *res;
125f495ec29SRui Paulo 	device_t smbus;
126f495ec29SRui Paulo 	struct mtx lock;
127f495ec29SRui Paulo };
128f495ec29SRui Paulo 
129f495ec29SRui Paulo #define	AMDPM_LOCK(amdpm)		mtx_lock(&(amdpm)->lock)
130f495ec29SRui Paulo #define	AMDPM_UNLOCK(amdpm)		mtx_unlock(&(amdpm)->lock)
131f495ec29SRui Paulo #define	AMDPM_LOCK_ASSERT(amdpm)	mtx_assert(&(amdpm)->lock, MA_OWNED)
132f495ec29SRui Paulo 
133f495ec29SRui Paulo #define AMDPM_SMBINB(amdpm,register) \
134f495ec29SRui Paulo 	(bus_read_1(amdpm->res, register))
135f495ec29SRui Paulo #define AMDPM_SMBOUTB(amdpm,register,value) \
136f495ec29SRui Paulo 	(bus_write_1(amdpm->res, register, value))
137f495ec29SRui Paulo #define AMDPM_SMBINW(amdpm,register) \
138f495ec29SRui Paulo 	(bus_read_2(amdpm->res, register))
139f495ec29SRui Paulo #define AMDPM_SMBOUTW(amdpm,register,value) \
140f495ec29SRui Paulo 	(bus_write_2(amdpm->res, register, value))
141f495ec29SRui Paulo 
142f495ec29SRui Paulo static int	amdpm_detach(device_t dev);
143f495ec29SRui Paulo 
144f495ec29SRui Paulo static int
amdpm_probe(device_t dev)145f495ec29SRui Paulo amdpm_probe(device_t dev)
146f495ec29SRui Paulo {
147f495ec29SRui Paulo 	u_long base;
148f495ec29SRui Paulo 	u_int16_t vid;
149f495ec29SRui Paulo 	u_int16_t did;
150f495ec29SRui Paulo 
151f495ec29SRui Paulo 	vid = pci_get_vendor(dev);
152f495ec29SRui Paulo 	did = pci_get_device(dev);
153f495ec29SRui Paulo 	if ((vid == AMDPM_VENDORID_AMD) &&
154f495ec29SRui Paulo 	    ((did == AMDPM_DEVICEID_AMD756PM) ||
155f495ec29SRui Paulo 	     (did == AMDPM_DEVICEID_AMD766PM) ||
156f495ec29SRui Paulo 	     (did == AMDPM_DEVICEID_AMD768PM) ||
157f495ec29SRui Paulo 	     (did == AMDPM_DEVICEID_AMD8111PM))) {
158f495ec29SRui Paulo 		device_set_desc(dev, "AMD 756/766/768/8111 Power Management Controller");
159f495ec29SRui Paulo 
160f495ec29SRui Paulo 		/*
161f495ec29SRui Paulo 		 * We have to do this, since the BIOS won't give us the
162f495ec29SRui Paulo 		 * resource info (not mine, anyway).
163f495ec29SRui Paulo 		 */
164f495ec29SRui Paulo 		base = pci_read_config(dev, AMDPCI_PMBASE, 4);
165f495ec29SRui Paulo 		base &= 0xff00;
166f495ec29SRui Paulo 		bus_set_resource(dev, SYS_RES_IOPORT, AMDPCI_PMBASE,
167f495ec29SRui Paulo 				 base+0xe0, 32);
168f495ec29SRui Paulo 		return (BUS_PROBE_DEFAULT);
169f495ec29SRui Paulo 	}
170f495ec29SRui Paulo 
171f495ec29SRui Paulo 	if ((vid == AMDPM_VENDORID_NVIDIA) &&
172f495ec29SRui Paulo 	    (did == AMDPM_DEVICEID_NF_SMB)) {
173f495ec29SRui Paulo 		device_set_desc(dev, "nForce SMBus Controller");
174f495ec29SRui Paulo 
175f495ec29SRui Paulo 		/*
176f495ec29SRui Paulo 		* We have to do this, since the BIOS won't give us the
177f495ec29SRui Paulo 		* resource info (not mine, anyway).
178f495ec29SRui Paulo 		*/
179f495ec29SRui Paulo 		base = pci_read_config(dev, NFPCI_PMBASE, 4);
180f495ec29SRui Paulo 		base &= 0xff00;
181f495ec29SRui Paulo 		bus_set_resource(dev, SYS_RES_IOPORT, NFPCI_PMBASE,
182f495ec29SRui Paulo 				 base, 32);
183f495ec29SRui Paulo 
184f495ec29SRui Paulo 		return (BUS_PROBE_DEFAULT);
185f495ec29SRui Paulo 	}
186f495ec29SRui Paulo 
187f495ec29SRui Paulo 	return ENXIO;
188f495ec29SRui Paulo }
189f495ec29SRui Paulo 
190f495ec29SRui Paulo static int
amdpm_attach(device_t dev)191f495ec29SRui Paulo amdpm_attach(device_t dev)
192f495ec29SRui Paulo {
193f495ec29SRui Paulo 	struct amdpm_softc *amdpm_sc = device_get_softc(dev);
194f495ec29SRui Paulo 	u_char val_b;
195f495ec29SRui Paulo 
196f495ec29SRui Paulo 	/* Enable I/O block access */
197f495ec29SRui Paulo 	val_b = pci_read_config(dev, AMDPCI_GEN_CONFIG_PM, 1);
198f495ec29SRui Paulo 	pci_write_config(dev, AMDPCI_GEN_CONFIG_PM, val_b | AMDPCI_PMIOEN, 1);
199f495ec29SRui Paulo 
200f495ec29SRui Paulo 	/* Allocate I/O space */
2014afea6b4SKonstantin Belousov 	if (pci_get_vendor(dev) == AMDPM_VENDORID_AMD ||
2024afea6b4SKonstantin Belousov 	    pci_get_vendor(dev) == AMDPM_VENDORID_HYGON)
203f495ec29SRui Paulo 		amdpm_sc->rid = AMDPCI_PMBASE;
204f495ec29SRui Paulo 	else
205f495ec29SRui Paulo 		amdpm_sc->rid = NFPCI_PMBASE;
206f495ec29SRui Paulo 	amdpm_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
207f495ec29SRui Paulo 		&amdpm_sc->rid, RF_ACTIVE);
208f495ec29SRui Paulo 
209f495ec29SRui Paulo 	if (amdpm_sc->res == NULL) {
210f495ec29SRui Paulo 		device_printf(dev, "could not map i/o space\n");
211f495ec29SRui Paulo 		return (ENXIO);
212f495ec29SRui Paulo 	}
213f495ec29SRui Paulo 
214f495ec29SRui Paulo 	mtx_init(&amdpm_sc->lock, device_get_nameunit(dev), "amdpm", MTX_DEF);
215f495ec29SRui Paulo 
216f495ec29SRui Paulo 	/* Allocate a new smbus device */
2175b56413dSWarner Losh 	amdpm_sc->smbus = device_add_child(dev, "smbus", DEVICE_UNIT_ANY);
218f495ec29SRui Paulo 	if (!amdpm_sc->smbus) {
219f495ec29SRui Paulo 		amdpm_detach(dev);
220f495ec29SRui Paulo 		return (EINVAL);
221f495ec29SRui Paulo 	}
222f495ec29SRui Paulo 
22318250ec6SJohn Baldwin 	bus_attach_children(dev);
224f495ec29SRui Paulo 
225f495ec29SRui Paulo 	return (0);
226f495ec29SRui Paulo }
227f495ec29SRui Paulo 
228f495ec29SRui Paulo static int
amdpm_detach(device_t dev)229f495ec29SRui Paulo amdpm_detach(device_t dev)
230f495ec29SRui Paulo {
231f495ec29SRui Paulo 	struct amdpm_softc *amdpm_sc = device_get_softc(dev);
232*11a91178SJohn Baldwin 	int error;
233f495ec29SRui Paulo 
234*11a91178SJohn Baldwin 	error = bus_generic_detach(dev);
235*11a91178SJohn Baldwin 	if (error != 0)
236*11a91178SJohn Baldwin 		return (error);
237f495ec29SRui Paulo 
238f495ec29SRui Paulo 	mtx_destroy(&amdpm_sc->lock);
239f495ec29SRui Paulo 	if (amdpm_sc->res)
240f495ec29SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, amdpm_sc->rid,
241f495ec29SRui Paulo 				     amdpm_sc->res);
242f495ec29SRui Paulo 
243f495ec29SRui Paulo 	return (0);
244f495ec29SRui Paulo }
245f495ec29SRui Paulo 
246f495ec29SRui Paulo static int
amdpm_callback(device_t dev,int index,void * data)247f495ec29SRui Paulo amdpm_callback(device_t dev, int index, void *data)
248f495ec29SRui Paulo {
249f495ec29SRui Paulo 	int error = 0;
250f495ec29SRui Paulo 
251f495ec29SRui Paulo 	switch (index) {
252f495ec29SRui Paulo 	case SMB_REQUEST_BUS:
253f495ec29SRui Paulo 	case SMB_RELEASE_BUS:
254f495ec29SRui Paulo 		break;
255f495ec29SRui Paulo 	default:
256f495ec29SRui Paulo 		error = EINVAL;
257f495ec29SRui Paulo 	}
258f495ec29SRui Paulo 
259f495ec29SRui Paulo 	return (error);
260f495ec29SRui Paulo }
261f495ec29SRui Paulo 
262f495ec29SRui Paulo static int
amdpm_clear(struct amdpm_softc * sc)263f495ec29SRui Paulo amdpm_clear(struct amdpm_softc *sc)
264f495ec29SRui Paulo {
265f495ec29SRui Paulo 
266f495ec29SRui Paulo 	AMDPM_LOCK_ASSERT(sc);
267f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_STATUS, AMDSMB_GS_CLEAR_STS);
268f495ec29SRui Paulo 	DELAY(10);
269f495ec29SRui Paulo 
270f495ec29SRui Paulo 	return (0);
271f495ec29SRui Paulo }
272f495ec29SRui Paulo 
273f495ec29SRui Paulo #if 0
274f495ec29SRui Paulo static int
275f495ec29SRui Paulo amdpm_abort(struct amdpm_softc *sc)
276f495ec29SRui Paulo {
277f495ec29SRui Paulo 	u_short l;
278f495ec29SRui Paulo 
279f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
280f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, l | AMDSMB_GE_ABORT);
281f495ec29SRui Paulo 
282f495ec29SRui Paulo 	return (0);
283f495ec29SRui Paulo }
284f495ec29SRui Paulo #endif
285f495ec29SRui Paulo 
286f495ec29SRui Paulo static int
amdpm_idle(struct amdpm_softc * sc)287f495ec29SRui Paulo amdpm_idle(struct amdpm_softc *sc)
288f495ec29SRui Paulo {
289f495ec29SRui Paulo 	u_short sts;
290f495ec29SRui Paulo 
291f495ec29SRui Paulo 	AMDPM_LOCK_ASSERT(sc);
292f495ec29SRui Paulo 	sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS);
293f495ec29SRui Paulo 
294f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: busy? STS=0x%x\n", sts));
295f495ec29SRui Paulo 
296f495ec29SRui Paulo 	return (~(sts & AMDSMB_GS_HST_STS));
297f495ec29SRui Paulo }
298f495ec29SRui Paulo 
299f495ec29SRui Paulo /*
300f495ec29SRui Paulo  * Poll the SMBus controller
301f495ec29SRui Paulo  */
302f495ec29SRui Paulo static int
amdpm_wait(struct amdpm_softc * sc)303f495ec29SRui Paulo amdpm_wait(struct amdpm_softc *sc)
304f495ec29SRui Paulo {
305f495ec29SRui Paulo 	int count = 10000;
306f495ec29SRui Paulo 	u_short sts = 0;
307f495ec29SRui Paulo 	int error;
308f495ec29SRui Paulo 
309f495ec29SRui Paulo 	AMDPM_LOCK_ASSERT(sc);
310f495ec29SRui Paulo 	/* Wait for command to complete (SMBus controller is idle) */
311f495ec29SRui Paulo 	while(count--) {
312f495ec29SRui Paulo 		DELAY(10);
313f495ec29SRui Paulo 		sts = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_STATUS);
314f495ec29SRui Paulo 		if (!(sts & AMDSMB_GS_HST_STS))
315f495ec29SRui Paulo 			break;
316f495ec29SRui Paulo 	}
317f495ec29SRui Paulo 
318f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: STS=0x%x (count=%d)\n", sts, count));
319f495ec29SRui Paulo 
320f495ec29SRui Paulo 	error = SMB_ENOERR;
321f495ec29SRui Paulo 
322f495ec29SRui Paulo 	if (!count)
323f495ec29SRui Paulo 		error |= SMB_ETIMEOUT;
324f495ec29SRui Paulo 
325f495ec29SRui Paulo 	if (sts & AMDSMB_GS_ABRT_STS)
326f495ec29SRui Paulo 		error |= SMB_EABORT;
327f495ec29SRui Paulo 
328f495ec29SRui Paulo 	if (sts & AMDSMB_GS_COL_STS)
329f495ec29SRui Paulo 		error |= SMB_ENOACK;
330f495ec29SRui Paulo 
331f495ec29SRui Paulo 	if (sts & AMDSMB_GS_PRERR_STS)
332f495ec29SRui Paulo 		error |= SMB_EBUSERR;
333f495ec29SRui Paulo 
334f495ec29SRui Paulo 	if (error != SMB_ENOERR)
335f495ec29SRui Paulo 		amdpm_clear(sc);
336f495ec29SRui Paulo 
337f495ec29SRui Paulo 	return (error);
338f495ec29SRui Paulo }
339f495ec29SRui Paulo 
340f495ec29SRui Paulo static int
amdpm_quick(device_t dev,u_char slave,int how)341f495ec29SRui Paulo amdpm_quick(device_t dev, u_char slave, int how)
342f495ec29SRui Paulo {
343f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
344f495ec29SRui Paulo 	int error;
345f495ec29SRui Paulo 	u_short l;
346f495ec29SRui Paulo 
347f495ec29SRui Paulo 	AMDPM_LOCK(sc);
348f495ec29SRui Paulo 	amdpm_clear(sc);
349f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
350f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
351f495ec29SRui Paulo 		return (EBUSY);
352f495ec29SRui Paulo 	}
353f495ec29SRui Paulo 
354f495ec29SRui Paulo 	switch (how) {
355f495ec29SRui Paulo 	case SMB_QWRITE:
356f495ec29SRui Paulo 		AMDPM_DEBUG(printf("amdpm: QWRITE to 0x%x", slave));
357f495ec29SRui Paulo 		AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
358f495ec29SRui Paulo 		break;
359f495ec29SRui Paulo 	case SMB_QREAD:
360f495ec29SRui Paulo 		AMDPM_DEBUG(printf("amdpm: QREAD to 0x%x", slave));
361f495ec29SRui Paulo 		AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
362f495ec29SRui Paulo 		break;
363f495ec29SRui Paulo 	default:
364f495ec29SRui Paulo 		panic("%s: unknown QUICK command (%x)!", __func__, how);
365f495ec29SRui Paulo 	}
366f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
367f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_QUICK | AMDSMB_GE_HOST_STC);
368f495ec29SRui Paulo 
369f495ec29SRui Paulo 	error = amdpm_wait(sc);
370f495ec29SRui Paulo 
371f495ec29SRui Paulo 	AMDPM_DEBUG(printf(", error=0x%x\n", error));
372f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
373f495ec29SRui Paulo 
374f495ec29SRui Paulo 	return (error);
375f495ec29SRui Paulo }
376f495ec29SRui Paulo 
377f495ec29SRui Paulo static int
amdpm_sendb(device_t dev,u_char slave,char byte)378f495ec29SRui Paulo amdpm_sendb(device_t dev, u_char slave, char byte)
379f495ec29SRui Paulo {
380f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
381f495ec29SRui Paulo 	int error;
382f495ec29SRui Paulo 	u_short l;
383f495ec29SRui Paulo 
384f495ec29SRui Paulo 	AMDPM_LOCK(sc);
385f495ec29SRui Paulo 	amdpm_clear(sc);
386f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
387f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
388f495ec29SRui Paulo 		return (SMB_EBUSY);
389f495ec29SRui Paulo 	}
390f495ec29SRui Paulo 
391f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
392f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
393f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
394f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC);
395f495ec29SRui Paulo 
396f495ec29SRui Paulo 	error = amdpm_wait(sc);
397f495ec29SRui Paulo 
398f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
399f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
400f495ec29SRui Paulo 
401f495ec29SRui Paulo 	return (error);
402f495ec29SRui Paulo }
403f495ec29SRui Paulo 
404f495ec29SRui Paulo static int
amdpm_recvb(device_t dev,u_char slave,char * byte)405f495ec29SRui Paulo amdpm_recvb(device_t dev, u_char slave, char *byte)
406f495ec29SRui Paulo {
407f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
408f495ec29SRui Paulo 	int error;
409f495ec29SRui Paulo 	u_short l;
410f495ec29SRui Paulo 
411f495ec29SRui Paulo 	AMDPM_LOCK(sc);
412f495ec29SRui Paulo 	amdpm_clear(sc);
413f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
414f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
415f495ec29SRui Paulo 		return (SMB_EBUSY);
416f495ec29SRui Paulo 	}
417f495ec29SRui Paulo 
418f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
419f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
420f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BYTE | AMDSMB_GE_HOST_STC);
421f495ec29SRui Paulo 
422f495ec29SRui Paulo 	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
423f495ec29SRui Paulo 		*byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
424f495ec29SRui Paulo 
425f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
426f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
427f495ec29SRui Paulo 
428f495ec29SRui Paulo 	return (error);
429f495ec29SRui Paulo }
430f495ec29SRui Paulo 
431f495ec29SRui Paulo static int
amdpm_writeb(device_t dev,u_char slave,char cmd,char byte)432f495ec29SRui Paulo amdpm_writeb(device_t dev, u_char slave, char cmd, char byte)
433f495ec29SRui Paulo {
434f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
435f495ec29SRui Paulo 	int error;
436f495ec29SRui Paulo 	u_short l;
437f495ec29SRui Paulo 
438f495ec29SRui Paulo 	AMDPM_LOCK(sc);
439f495ec29SRui Paulo 	amdpm_clear(sc);
440f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
441f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
442f495ec29SRui Paulo 		return (SMB_EBUSY);
443f495ec29SRui Paulo 	}
444f495ec29SRui Paulo 
445f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
446f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, byte);
447f495ec29SRui Paulo 	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
448f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
449f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC);
450f495ec29SRui Paulo 
451f495ec29SRui Paulo 	error = amdpm_wait(sc);
452f495ec29SRui Paulo 
453f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
454f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
455f495ec29SRui Paulo 
456f495ec29SRui Paulo 	return (error);
457f495ec29SRui Paulo }
458f495ec29SRui Paulo 
459f495ec29SRui Paulo static int
amdpm_readb(device_t dev,u_char slave,char cmd,char * byte)460f495ec29SRui Paulo amdpm_readb(device_t dev, u_char slave, char cmd, char *byte)
461f495ec29SRui Paulo {
462f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
463f495ec29SRui Paulo 	int error;
464f495ec29SRui Paulo 	u_short l;
465f495ec29SRui Paulo 
466f495ec29SRui Paulo 	AMDPM_LOCK(sc);
467f495ec29SRui Paulo 	amdpm_clear(sc);
468f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
469f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
470f495ec29SRui Paulo 		return (SMB_EBUSY);
471f495ec29SRui Paulo 	}
472f495ec29SRui Paulo 
473f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
474f495ec29SRui Paulo 	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
475f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
476f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_BDATA | AMDSMB_GE_HOST_STC);
477f495ec29SRui Paulo 
478f495ec29SRui Paulo 	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
479f495ec29SRui Paulo 		*byte = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
480f495ec29SRui Paulo 
481f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
482f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
483f495ec29SRui Paulo 
484f495ec29SRui Paulo 	return (error);
485f495ec29SRui Paulo }
486f495ec29SRui Paulo 
487f495ec29SRui Paulo static int
amdpm_writew(device_t dev,u_char slave,char cmd,short word)488f495ec29SRui Paulo amdpm_writew(device_t dev, u_char slave, char cmd, short word)
489f495ec29SRui Paulo {
490f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
491f495ec29SRui Paulo 	int error;
492f495ec29SRui Paulo 	u_short l;
493f495ec29SRui Paulo 
494f495ec29SRui Paulo 	AMDPM_LOCK(sc);
495f495ec29SRui Paulo 	amdpm_clear(sc);
496f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
497f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
498f495ec29SRui Paulo 		return (SMB_EBUSY);
499f495ec29SRui Paulo 	}
500f495ec29SRui Paulo 
501f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
502f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, word);
503f495ec29SRui Paulo 	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
504f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
505f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC);
506f495ec29SRui Paulo 
507f495ec29SRui Paulo 	error = amdpm_wait(sc);
508f495ec29SRui Paulo 
509f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
510f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
511f495ec29SRui Paulo 
512f495ec29SRui Paulo 	return (error);
513f495ec29SRui Paulo }
514f495ec29SRui Paulo 
515f495ec29SRui Paulo static int
amdpm_readw(device_t dev,u_char slave,char cmd,short * word)516f495ec29SRui Paulo amdpm_readw(device_t dev, u_char slave, char cmd, short *word)
517f495ec29SRui Paulo {
518f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
519f495ec29SRui Paulo 	int error;
520f495ec29SRui Paulo 	u_short l;
521f495ec29SRui Paulo 
522f495ec29SRui Paulo 	AMDPM_LOCK(sc);
523f495ec29SRui Paulo 	amdpm_clear(sc);
524f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
525f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
526f495ec29SRui Paulo 		return (SMB_EBUSY);
527f495ec29SRui Paulo 	}
528f495ec29SRui Paulo 
529f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
530f495ec29SRui Paulo 	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
531f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
532f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE, (l & 0xfff8) | AMDSMB_GE_CYC_WDATA | AMDSMB_GE_HOST_STC);
533f495ec29SRui Paulo 
534f495ec29SRui Paulo 	if ((error = amdpm_wait(sc)) == SMB_ENOERR)
535f495ec29SRui Paulo 		*word = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
536f495ec29SRui Paulo 
537f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
538f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
539f495ec29SRui Paulo 
540f495ec29SRui Paulo 	return (error);
541f495ec29SRui Paulo }
542f495ec29SRui Paulo 
543f495ec29SRui Paulo static int
amdpm_bwrite(device_t dev,u_char slave,char cmd,u_char count,char * buf)544f495ec29SRui Paulo amdpm_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
545f495ec29SRui Paulo {
546f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
547f495ec29SRui Paulo 	u_char i;
548f495ec29SRui Paulo 	int error;
549f495ec29SRui Paulo 	u_short l;
550f495ec29SRui Paulo 
551f495ec29SRui Paulo 	if (count < 1 || count > 32)
552f495ec29SRui Paulo 		return (SMB_EINVAL);
553f495ec29SRui Paulo 
554f495ec29SRui Paulo 	AMDPM_LOCK(sc);
555f495ec29SRui Paulo 	amdpm_clear(sc);
556f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
557f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
558f495ec29SRui Paulo 		return (SMB_EBUSY);
559f495ec29SRui Paulo 	}
560f495ec29SRui Paulo 
561f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave & ~LSB);
562f495ec29SRui Paulo 
563f495ec29SRui Paulo 	/*
564f495ec29SRui Paulo 	 * Do we have to reset the internal 32-byte buffer?
565f495ec29SRui Paulo 	 * Can't see how to do this from the data sheet.
566f495ec29SRui Paulo 	 */
567f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTDATA, count);
568f495ec29SRui Paulo 
569f495ec29SRui Paulo 	/* Fill the 32-byte internal buffer */
570f495ec29SRui Paulo 	for (i = 0; i < count; i++) {
571f495ec29SRui Paulo 		AMDPM_SMBOUTB(sc, AMDSMB_HSTDFIFO, buf[i]);
572f495ec29SRui Paulo 		DELAY(2);
573f495ec29SRui Paulo 	}
574f495ec29SRui Paulo 	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
575f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
576f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
577f495ec29SRui Paulo 	    (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
578f495ec29SRui Paulo 
579f495ec29SRui Paulo 	error = amdpm_wait(sc);
580f495ec29SRui Paulo 
581f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
582f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
583f495ec29SRui Paulo 
584f495ec29SRui Paulo 	return (error);
585f495ec29SRui Paulo }
586f495ec29SRui Paulo 
587f495ec29SRui Paulo static int
amdpm_bread(device_t dev,u_char slave,char cmd,u_char * count,char * buf)588f495ec29SRui Paulo amdpm_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
589f495ec29SRui Paulo {
590f495ec29SRui Paulo 	struct amdpm_softc *sc = (struct amdpm_softc *)device_get_softc(dev);
591f495ec29SRui Paulo 	u_char data, len, i;
592f495ec29SRui Paulo 	int error;
593f495ec29SRui Paulo 	u_short l;
594f495ec29SRui Paulo 
595f495ec29SRui Paulo 	if (*count < 1 || *count > 32)
596f495ec29SRui Paulo 		return (SMB_EINVAL);
597f495ec29SRui Paulo 
598f495ec29SRui Paulo 	AMDPM_LOCK(sc);
599f495ec29SRui Paulo 	amdpm_clear(sc);
600f495ec29SRui Paulo 	if (!amdpm_idle(sc)) {
601f495ec29SRui Paulo 		AMDPM_UNLOCK(sc);
602f495ec29SRui Paulo 		return (SMB_EBUSY);
603f495ec29SRui Paulo 	}
604f495ec29SRui Paulo 
605f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_HSTADDR, slave | LSB);
606f495ec29SRui Paulo 
607f495ec29SRui Paulo 	AMDPM_SMBOUTB(sc, AMDSMB_HSTCMD, cmd);
608f495ec29SRui Paulo 
609f495ec29SRui Paulo 	l = AMDPM_SMBINW(sc, AMDSMB_GLOBAL_ENABLE);
610f495ec29SRui Paulo 	AMDPM_SMBOUTW(sc, AMDSMB_GLOBAL_ENABLE,
611f495ec29SRui Paulo 	    (l & 0xfff8) | AMDSMB_GE_CYC_BLOCK | AMDSMB_GE_HOST_STC);
612f495ec29SRui Paulo 
613f495ec29SRui Paulo 	if ((error = amdpm_wait(sc)) != SMB_ENOERR)
614f495ec29SRui Paulo 		goto error;
615f495ec29SRui Paulo 
616f495ec29SRui Paulo 	len = AMDPM_SMBINW(sc, AMDSMB_HSTDATA);
617f495ec29SRui Paulo 
618f495ec29SRui Paulo 	/* Read the 32-byte internal buffer */
619f495ec29SRui Paulo 	for (i = 0; i < len; i++) {
620f495ec29SRui Paulo 		data = AMDPM_SMBINB(sc, AMDSMB_HSTDFIFO);
621f495ec29SRui Paulo 		if (i < *count)
622f495ec29SRui Paulo 			buf[i] = data;
623f495ec29SRui Paulo 		DELAY(2);
624f495ec29SRui Paulo 	}
625f495ec29SRui Paulo 	*count = len;
626f495ec29SRui Paulo 
627f495ec29SRui Paulo error:
628f495ec29SRui Paulo 	AMDPM_DEBUG(printf("amdpm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, *count, cmd, error));
629f495ec29SRui Paulo 	AMDPM_UNLOCK(sc);
630f495ec29SRui Paulo 
631f495ec29SRui Paulo 	return (error);
632f495ec29SRui Paulo }
633f495ec29SRui Paulo 
634f495ec29SRui Paulo static device_method_t amdpm_methods[] = {
635f495ec29SRui Paulo 	/* Device interface */
636f495ec29SRui Paulo 	DEVMETHOD(device_probe,		amdpm_probe),
637f495ec29SRui Paulo 	DEVMETHOD(device_attach,	amdpm_attach),
638f495ec29SRui Paulo 	DEVMETHOD(device_detach,	amdpm_detach),
639f495ec29SRui Paulo 
640f495ec29SRui Paulo 	/* SMBus interface */
641f495ec29SRui Paulo 	DEVMETHOD(smbus_callback,	amdpm_callback),
642f495ec29SRui Paulo 	DEVMETHOD(smbus_quick,		amdpm_quick),
643f495ec29SRui Paulo 	DEVMETHOD(smbus_sendb,		amdpm_sendb),
644f495ec29SRui Paulo 	DEVMETHOD(smbus_recvb,		amdpm_recvb),
645f495ec29SRui Paulo 	DEVMETHOD(smbus_writeb,		amdpm_writeb),
646f495ec29SRui Paulo 	DEVMETHOD(smbus_readb,		amdpm_readb),
647f495ec29SRui Paulo 	DEVMETHOD(smbus_writew,		amdpm_writew),
648f495ec29SRui Paulo 	DEVMETHOD(smbus_readw,		amdpm_readw),
649f495ec29SRui Paulo 	DEVMETHOD(smbus_bwrite,		amdpm_bwrite),
650f495ec29SRui Paulo 	DEVMETHOD(smbus_bread,		amdpm_bread),
651f495ec29SRui Paulo 	{ 0, 0 }
652f495ec29SRui Paulo };
653f495ec29SRui Paulo 
654f495ec29SRui Paulo static driver_t amdpm_driver = {
655f495ec29SRui Paulo 	"amdpm",
656f495ec29SRui Paulo 	amdpm_methods,
657f495ec29SRui Paulo 	sizeof(struct amdpm_softc),
658f495ec29SRui Paulo };
659f495ec29SRui Paulo 
66083a273efSJohn Baldwin DRIVER_MODULE(amdpm, pci, amdpm_driver, 0, 0);
661c6d39765SJohn Baldwin DRIVER_MODULE(smbus, amdpm, smbus_driver, 0, 0);
662f495ec29SRui Paulo 
663f495ec29SRui Paulo MODULE_DEPEND(amdpm, pci, 1, 1, 1);
664f495ec29SRui Paulo MODULE_DEPEND(amdpm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
665f495ec29SRui Paulo MODULE_VERSION(amdpm, 1);
666