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