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