1175533Sru/*-
2175533Sru * Copyright (c) 2005 Ruslan Ermilov
3175533Sru * All rights reserved.
4175533Sru *
5175533Sru * Redistribution and use in source and binary forms, with or without
6175533Sru * modification, are permitted provided that the following conditions
7175533Sru * are met:
8175533Sru * 1. Redistributions of source code must retain the above copyright
9175533Sru *    notice, this list of conditions and the following disclaimer.
10175533Sru * 2. Redistributions in binary form must reproduce the above copyright
11175533Sru *    notice, this list of conditions and the following disclaimer in the
12175533Sru *    documentation and/or other materials provided with the distribution.
13175533Sru *
14175533Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15175533Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16175533Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17175533Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18175533Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19175533Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20175533Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21175533Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22175533Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23175533Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24175533Sru * SUCH DAMAGE.
25175533Sru */
26175533Sru
27153618Sru#include <sys/cdefs.h>
28153618Sru__FBSDID("$FreeBSD: releng/10.2/sys/pci/amdsmb.c 179622 2008-06-06 18:29:56Z jhb $");
29153618Sru
30153618Sru#include <sys/param.h>
31165951Sjhb#include <sys/bus.h>
32153618Sru#include <sys/kernel.h>
33165951Sjhb#include <sys/lock.h>
34165951Sjhb#include <sys/module.h>
35165951Sjhb#include <sys/mutex.h>
36153618Sru#include <sys/systm.h>
37153618Sru
38153618Sru#include <machine/bus.h>
39153618Sru#include <machine/resource.h>
40153618Sru#include <sys/rman.h>
41153618Sru
42153618Sru#include <dev/pci/pcivar.h>
43153618Sru#include <dev/pci/pcireg.h>
44153618Sru
45153618Sru#include <dev/smbus/smbconf.h>
46153618Sru#include "smbus_if.h"
47153618Sru
48153618Sru#define	AMDSMB_DEBUG(x)	if (amdsmb_debug) (x)
49153618Sru
50153618Sru#ifdef DEBUG
51153618Srustatic int amdsmb_debug = 1;
52153618Sru#else
53153618Srustatic int amdsmb_debug = 0;
54153618Sru#endif
55153618Sru
56153618Sru#define	AMDSMB_VENDORID_AMD		0x1022
57153618Sru#define	AMDSMB_DEVICEID_AMD8111_SMB2	0x746a
58153618Sru
59153618Sru/*
60153618Sru * ACPI 3.0, Chapter 12, Embedded Controller Interface.
61153618Sru */
62153618Sru#define	EC_DATA		0x00	/* data register */
63153618Sru#define	EC_SC		0x04	/* status of controller */
64153618Sru#define	EC_CMD		0x04	/* command register */
65153618Sru
66153618Sru#define	EC_SC_IBF	0x02	/* data ready for embedded controller */
67153618Sru#define	EC_SC_OBF	0x01	/* data ready for host */
68153618Sru#define	EC_CMD_WR	0x81	/* write EC */
69153618Sru#define	EC_CMD_RD	0x80	/* read EC */
70153618Sru
71153618Sru/*
72153618Sru * ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
73153618Sru */
74153618Sru#define	SMB_PRTCL	0x00	/* protocol */
75153618Sru#define	SMB_STS		0x01	/* status */
76153618Sru#define	SMB_ADDR	0x02	/* address */
77153618Sru#define	SMB_CMD		0x03	/* command */
78153618Sru#define	SMB_DATA	0x04	/* 32 data registers */
79153618Sru#define	SMB_BCNT	0x24	/* number of data bytes */
80153618Sru#define	SMB_ALRM_A	0x25	/* alarm address */
81153618Sru#define	SMB_ALRM_D	0x26	/* 2 bytes alarm data */
82153618Sru
83153618Sru#define	SMB_STS_DONE	0x80
84153618Sru#define	SMB_STS_ALRM	0x40
85153618Sru#define	SMB_STS_RES	0x20
86153618Sru#define	SMB_STS_STATUS	0x1f
87153618Sru#define	SMB_STS_OK	0x00	/* OK */
88153618Sru#define	SMB_STS_UF	0x07	/* Unknown Failure */
89153618Sru#define	SMB_STS_DANA	0x10	/* Device Address Not Acknowledged */
90153618Sru#define	SMB_STS_DED	0x11	/* Device Error Detected */
91153618Sru#define	SMB_STS_DCAD	0x12	/* Device Command Access Denied */
92153618Sru#define	SMB_STS_UE	0x13	/* Unknown Error */
93153618Sru#define	SMB_STS_DAD	0x17	/* Device Access Denied */
94153618Sru#define	SMB_STS_T	0x18	/* Timeout */
95153618Sru#define	SMB_STS_HUP	0x19	/* Host Unsupported Protocol */
96153618Sru#define	SMB_STS_B	0x1a	/* Busy */
97153618Sru#define	SMB_STS_PEC	0x1f	/* PEC (CRC-8) Error */
98153618Sru
99153618Sru#define	SMB_PRTCL_WRITE			0x00
100153618Sru#define	SMB_PRTCL_READ			0x01
101153618Sru#define	SMB_PRTCL_QUICK			0x02
102153618Sru#define	SMB_PRTCL_BYTE			0x04
103153618Sru#define	SMB_PRTCL_BYTE_DATA		0x06
104153618Sru#define	SMB_PRTCL_WORD_DATA		0x08
105153618Sru#define	SMB_PRTCL_BLOCK_DATA		0x0a
106153618Sru#define	SMB_PRTCL_PROC_CALL		0x0c
107153618Sru#define	SMB_PRTCL_BLOCK_PROC_CALL	0x0d
108153618Sru#define	SMB_PRTCL_PEC			0x80
109153618Sru
110153618Srustruct amdsmb_softc {
111153618Sru	int rid;
112153618Sru	struct resource *res;
113153618Sru	device_t smbus;
114165951Sjhb	struct mtx lock;
115153618Sru};
116153618Sru
117165951Sjhb#define	AMDSMB_LOCK(amdsmb)		mtx_lock(&(amdsmb)->lock)
118165951Sjhb#define	AMDSMB_UNLOCK(amdsmb)		mtx_unlock(&(amdsmb)->lock)
119165951Sjhb#define	AMDSMB_LOCK_ASSERT(amdsmb)	mtx_assert(&(amdsmb)->lock, MA_OWNED)
120165951Sjhb
121165951Sjhb#define	AMDSMB_ECINB(amdsmb, register)					\
122179622Sjhb	(bus_read_1(amdsmb->res, register))
123153618Sru#define	AMDSMB_ECOUTB(amdsmb, register, value) \
124179622Sjhb	(bus_write_1(amdsmb->res, register, value))
125153618Sru
126165951Sjhbstatic int	amdsmb_detach(device_t dev);
127165951Sjhb
128153618Srustatic int
129153618Sruamdsmb_probe(device_t dev)
130153618Sru{
131153618Sru	u_int16_t vid;
132153618Sru	u_int16_t did;
133153618Sru
134153618Sru	vid = pci_get_vendor(dev);
135153618Sru	did = pci_get_device(dev);
136153618Sru
137153618Sru	if (vid == AMDSMB_VENDORID_AMD) {
138153618Sru		switch(did) {
139153618Sru		case AMDSMB_DEVICEID_AMD8111_SMB2:
140153618Sru			device_set_desc(dev, "AMD-8111 SMBus 2.0 Controller");
141153618Sru			return (BUS_PROBE_DEFAULT);
142153618Sru		}
143153618Sru	}
144153618Sru
145153618Sru	return (ENXIO);
146153618Sru}
147153618Sru
148153618Srustatic int
149153618Sruamdsmb_attach(device_t dev)
150153618Sru{
151153618Sru	struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
152153618Sru
153153618Sru	/* Allocate I/O space */
154153618Sru	amdsmb_sc->rid = PCIR_BAR(0);
155153618Sru
156153618Sru	amdsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
157153618Sru		&amdsmb_sc->rid, RF_ACTIVE);
158153618Sru
159153618Sru	if (amdsmb_sc->res == NULL) {
160153618Sru		device_printf(dev, "could not map i/o space\n");
161153618Sru		return (ENXIO);
162153618Sru	}
163153618Sru
164165951Sjhb	mtx_init(&amdsmb_sc->lock, device_get_nameunit(dev), "amdsmb", MTX_DEF);
165153618Sru
166153618Sru	/* Allocate a new smbus device */
167153618Sru	amdsmb_sc->smbus = device_add_child(dev, "smbus", -1);
168165951Sjhb	if (!amdsmb_sc->smbus) {
169165951Sjhb		amdsmb_detach(dev);
170153618Sru		return (EINVAL);
171165951Sjhb	}
172153618Sru
173153618Sru	bus_generic_attach(dev);
174153618Sru
175153618Sru	return (0);
176153618Sru}
177153618Sru
178153618Srustatic int
179153618Sruamdsmb_detach(device_t dev)
180153618Sru{
181153618Sru	struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
182153618Sru
183153618Sru	if (amdsmb_sc->smbus) {
184153618Sru		device_delete_child(dev, amdsmb_sc->smbus);
185153618Sru		amdsmb_sc->smbus = NULL;
186153618Sru	}
187153618Sru
188165951Sjhb	mtx_destroy(&amdsmb_sc->lock);
189153618Sru	if (amdsmb_sc->res)
190153618Sru		bus_release_resource(dev, SYS_RES_IOPORT, amdsmb_sc->rid,
191153618Sru		    amdsmb_sc->res);
192153618Sru
193153618Sru	return (0);
194153618Sru}
195153618Sru
196153618Srustatic int
197162234Sjhbamdsmb_callback(device_t dev, int index, void *data)
198153618Sru{
199153618Sru	int error = 0;
200153618Sru
201153618Sru	switch (index) {
202153618Sru	case SMB_REQUEST_BUS:
203153618Sru	case SMB_RELEASE_BUS:
204153618Sru		break;
205153618Sru	default:
206153618Sru		error = EINVAL;
207153618Sru	}
208153618Sru
209153618Sru	return (error);
210153618Sru}
211153618Sru
212153618Srustatic int
213153618Sruamdsmb_ec_wait_write(struct amdsmb_softc *sc)
214153618Sru{
215153618Sru	int timeout = 500;
216153618Sru
217153618Sru	while (timeout-- && AMDSMB_ECINB(sc, EC_SC) & EC_SC_IBF)
218153618Sru		DELAY(1);
219153618Sru	if (timeout == 0) {
220153618Sru		device_printf(sc->smbus, "timeout waiting for IBF to clear\n");
221153618Sru		return (1);
222153618Sru	}
223153618Sru	return (0);
224153618Sru}
225153618Sru
226153618Srustatic int
227153618Sruamdsmb_ec_wait_read(struct amdsmb_softc *sc)
228153618Sru{
229153618Sru	int timeout = 500;
230153618Sru
231153618Sru	while (timeout-- && ~AMDSMB_ECINB(sc, EC_SC) & EC_SC_OBF)
232153618Sru		DELAY(1);
233153618Sru	if (timeout == 0) {
234153618Sru		device_printf(sc->smbus, "timeout waiting for OBF to set\n");
235153618Sru		return (1);
236153618Sru	}
237153618Sru	return (0);
238153618Sru}
239153618Sru
240153618Srustatic int
241153618Sruamdsmb_ec_read(struct amdsmb_softc *sc, u_char addr, u_char *data)
242153618Sru{
243153618Sru
244165951Sjhb	AMDSMB_LOCK_ASSERT(sc);
245153618Sru	if (amdsmb_ec_wait_write(sc))
246153618Sru		return (1);
247153618Sru	AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_RD);
248153618Sru
249153618Sru	if (amdsmb_ec_wait_write(sc))
250153618Sru		return (1);
251153618Sru	AMDSMB_ECOUTB(sc, EC_DATA, addr);
252153618Sru
253153618Sru	if (amdsmb_ec_wait_read(sc))
254153618Sru		return (1);
255153618Sru	*data = AMDSMB_ECINB(sc, EC_DATA);
256153618Sru
257153618Sru	return (0);
258153618Sru}
259153618Sru
260153618Srustatic int
261153618Sruamdsmb_ec_write(struct amdsmb_softc *sc, u_char addr, u_char data)
262153618Sru{
263153618Sru
264165951Sjhb	AMDSMB_LOCK_ASSERT(sc);
265153618Sru	if (amdsmb_ec_wait_write(sc))
266153618Sru		return (1);
267153618Sru	AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_WR);
268153618Sru
269153618Sru	if (amdsmb_ec_wait_write(sc))
270153618Sru		return (1);
271153618Sru	AMDSMB_ECOUTB(sc, EC_DATA, addr);
272153618Sru
273153618Sru	if (amdsmb_ec_wait_write(sc))
274153618Sru		return (1);
275153618Sru	AMDSMB_ECOUTB(sc, EC_DATA, data);
276153618Sru
277153618Sru	return (0);
278153618Sru}
279153618Sru
280153618Srustatic int
281153618Sruamdsmb_wait(struct amdsmb_softc *sc)
282153618Sru{
283153618Sru	u_char sts, temp;
284153618Sru	int error, count;
285153618Sru
286165951Sjhb	AMDSMB_LOCK_ASSERT(sc);
287153618Sru	amdsmb_ec_read(sc, SMB_PRTCL, &temp);
288153618Sru	if (temp != 0)
289153618Sru	{
290153618Sru		count = 10000;
291153618Sru		do {
292153618Sru			DELAY(500);
293153618Sru			amdsmb_ec_read(sc, SMB_PRTCL, &temp);
294153618Sru		} while (temp != 0 && count--);
295153618Sru		if (count == 0)
296153618Sru			return (SMB_ETIMEOUT);
297153618Sru	}
298153618Sru
299153618Sru	amdsmb_ec_read(sc, SMB_STS, &sts);
300153618Sru	sts &= SMB_STS_STATUS;
301153618Sru	AMDSMB_DEBUG(printf("amdsmb: STS=0x%x\n", sts));
302153618Sru
303153618Sru	switch (sts) {
304153618Sru	case SMB_STS_OK:
305153618Sru		error = SMB_ENOERR;
306153618Sru		break;
307153618Sru	case SMB_STS_DANA:
308153618Sru		error = SMB_ENOACK;
309153618Sru		break;
310153618Sru	case SMB_STS_B:
311153618Sru		error = SMB_EBUSY;
312153618Sru		break;
313153618Sru	case SMB_STS_T:
314153618Sru		error = SMB_ETIMEOUT;
315153618Sru		break;
316153618Sru	case SMB_STS_DCAD:
317153618Sru	case SMB_STS_DAD:
318153618Sru	case SMB_STS_HUP:
319153618Sru		error = SMB_ENOTSUPP;
320153618Sru		break;
321153618Sru	default:
322153618Sru		error = SMB_EBUSERR;
323153618Sru		break;
324153618Sru	}
325153618Sru
326153618Sru	return (error);
327153618Sru}
328153618Sru
329153618Srustatic int
330153618Sruamdsmb_quick(device_t dev, u_char slave, int how)
331153618Sru{
332153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
333153618Sru	u_char protocol;
334153618Sru	int error;
335153618Sru
336153618Sru	protocol = SMB_PRTCL_QUICK;
337153618Sru
338153618Sru	switch (how) {
339153618Sru	case SMB_QWRITE:
340153618Sru		protocol |= SMB_PRTCL_WRITE;
341153618Sru		AMDSMB_DEBUG(printf("amdsmb: QWRITE to 0x%x", slave));
342153618Sru		break;
343153618Sru	case SMB_QREAD:
344153618Sru		protocol |= SMB_PRTCL_READ;
345153618Sru		AMDSMB_DEBUG(printf("amdsmb: QREAD to 0x%x", slave));
346153618Sru		break;
347153618Sru	default:
348153618Sru		panic("%s: unknown QUICK command (%x)!", __func__, how);
349153618Sru	}
350153618Sru
351165951Sjhb	AMDSMB_LOCK(sc);
352153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
353153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, protocol);
354153618Sru
355153618Sru	error = amdsmb_wait(sc);
356153618Sru
357153618Sru	AMDSMB_DEBUG(printf(", error=0x%x\n", error));
358165951Sjhb	AMDSMB_UNLOCK(sc);
359153618Sru
360153618Sru	return (error);
361153618Sru}
362153618Sru
363153618Srustatic int
364153618Sruamdsmb_sendb(device_t dev, u_char slave, char byte)
365153618Sru{
366153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
367153618Sru	int error;
368153618Sru
369165951Sjhb	AMDSMB_LOCK(sc);
370153618Sru	amdsmb_ec_write(sc, SMB_CMD, byte);
371153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
372153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
373153618Sru
374153618Sru	error = amdsmb_wait(sc);
375153618Sru
376153618Sru	AMDSMB_DEBUG(printf("amdsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n",
377153618Sru	   slave, byte, error));
378165951Sjhb	AMDSMB_UNLOCK(sc);
379153618Sru
380153618Sru	return (error);
381153618Sru}
382153618Sru
383153618Srustatic int
384153618Sruamdsmb_recvb(device_t dev, u_char slave, char *byte)
385153618Sru{
386153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
387153618Sru	int error;
388153618Sru
389165951Sjhb	AMDSMB_LOCK(sc);
390153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
391153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
392153618Sru
393153618Sru	if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
394153618Sru		amdsmb_ec_read(sc, SMB_DATA, byte);
395153618Sru
396153618Sru	AMDSMB_DEBUG(printf("amdsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n",
397153618Sru	    slave, *byte, error));
398165951Sjhb	AMDSMB_UNLOCK(sc);
399153618Sru
400153618Sru	return (error);
401153618Sru}
402153618Sru
403153618Srustatic int
404153618Sruamdsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
405153618Sru{
406153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
407153618Sru	int error;
408153618Sru
409165951Sjhb	AMDSMB_LOCK(sc);
410153618Sru	amdsmb_ec_write(sc, SMB_CMD, cmd);
411153618Sru	amdsmb_ec_write(sc, SMB_DATA, byte);
412153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
413153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
414153618Sru
415153618Sru	error = amdsmb_wait(sc);
416153618Sru
417153618Sru	AMDSMB_DEBUG(printf("amdsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, "
418153618Sru	    "error=0x%x\n", slave, cmd, byte, error));
419165951Sjhb	AMDSMB_UNLOCK(sc);
420153618Sru
421153618Sru	return (error);
422153618Sru}
423153618Sru
424153618Srustatic int
425153618Sruamdsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
426153618Sru{
427153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
428153618Sru	int error;
429153618Sru
430165951Sjhb	AMDSMB_LOCK(sc);
431153618Sru	amdsmb_ec_write(sc, SMB_CMD, cmd);
432153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
433153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
434153618Sru
435153618Sru	if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
436153618Sru		amdsmb_ec_read(sc, SMB_DATA, byte);
437153618Sru
438153618Sru	AMDSMB_DEBUG(printf("amdsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, "
439153618Sru	    "error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
440165951Sjhb	AMDSMB_UNLOCK(sc);
441153618Sru
442153618Sru	return (error);
443153618Sru}
444153618Sru
445153618Srustatic int
446153618Sruamdsmb_writew(device_t dev, u_char slave, char cmd, short word)
447153618Sru{
448153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
449153618Sru	int error;
450153618Sru
451165951Sjhb	AMDSMB_LOCK(sc);
452153618Sru	amdsmb_ec_write(sc, SMB_CMD, cmd);
453153618Sru	amdsmb_ec_write(sc, SMB_DATA, word);
454153618Sru	amdsmb_ec_write(sc, SMB_DATA + 1, word >> 8);
455153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
456153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
457153618Sru
458153618Sru	error = amdsmb_wait(sc);
459153618Sru
460153618Sru	AMDSMB_DEBUG(printf("amdsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, "
461153618Sru	    "error=0x%x\n", slave, cmd, word, error));
462165951Sjhb	AMDSMB_UNLOCK(sc);
463153618Sru
464153618Sru	return (error);
465153618Sru}
466153618Sru
467153618Srustatic int
468153618Sruamdsmb_readw(device_t dev, u_char slave, char cmd, short *word)
469153618Sru{
470153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
471153618Sru	u_char temp[2];
472153618Sru	int error;
473153618Sru
474165951Sjhb	AMDSMB_LOCK(sc);
475153618Sru	amdsmb_ec_write(sc, SMB_CMD, cmd);
476153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
477153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
478153618Sru
479153618Sru	if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
480153618Sru		amdsmb_ec_read(sc, SMB_DATA + 0, &temp[0]);
481153618Sru		amdsmb_ec_read(sc, SMB_DATA + 1, &temp[1]);
482153618Sru		*word = temp[0] | (temp[1] << 8);
483153618Sru	}
484153618Sru
485153618Sru	AMDSMB_DEBUG(printf("amdsmb: READW from 0x%x, cmd=0x%x, word=0x%x, "
486153618Sru	    "error=0x%x\n", slave, cmd, (unsigned short)*word, error));
487165951Sjhb	AMDSMB_UNLOCK(sc);
488153618Sru
489153618Sru	return (error);
490153618Sru}
491153618Sru
492153618Srustatic int
493153618Sruamdsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
494153618Sru{
495153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
496162234Sjhb	u_char i;
497153618Sru	int error;
498153618Sru
499162234Sjhb	if (count < 1 || count > 32)
500162234Sjhb		return (SMB_EINVAL);
501165951Sjhb
502165951Sjhb	AMDSMB_LOCK(sc);
503153618Sru	amdsmb_ec_write(sc, SMB_CMD, cmd);
504162234Sjhb	amdsmb_ec_write(sc, SMB_BCNT, count);
505162234Sjhb	for (i = 0; i < count; i++)
506153618Sru		amdsmb_ec_write(sc, SMB_DATA + i, buf[i]);
507153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
508153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
509153618Sru
510153618Sru	error = amdsmb_wait(sc);
511153618Sru
512153618Sru	AMDSMB_DEBUG(printf("amdsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, "
513153618Sru	    "error=0x%x", slave, count, cmd, error));
514165951Sjhb	AMDSMB_UNLOCK(sc);
515153618Sru
516153618Sru	return (error);
517153618Sru}
518153618Sru
519153618Srustatic int
520162234Sjhbamdsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
521153618Sru{
522153618Sru	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
523162234Sjhb	u_char data, len, i;
524153618Sru	int error;
525153618Sru
526162234Sjhb	if (*count < 1 || *count > 32)
527162234Sjhb		return (SMB_EINVAL);
528165951Sjhb
529165951Sjhb	AMDSMB_LOCK(sc);
530153618Sru	amdsmb_ec_write(sc, SMB_CMD, cmd);
531153618Sru	amdsmb_ec_write(sc, SMB_ADDR, slave);
532153618Sru	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
533153618Sru
534153618Sru	if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
535153618Sru		amdsmb_ec_read(sc, SMB_BCNT, &len);
536162234Sjhb		for (i = 0; i < len; i++) {
537162234Sjhb			amdsmb_ec_read(sc, SMB_DATA + i, &data);
538162234Sjhb			if (i < *count)
539162234Sjhb				buf[i] = data;
540162234Sjhb		}
541162234Sjhb		*count = len;
542153618Sru	}
543153618Sru
544153618Sru	AMDSMB_DEBUG(printf("amdsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, "
545162234Sjhb	    "error=0x%x", slave, *count, cmd, error));
546165951Sjhb	AMDSMB_UNLOCK(sc);
547153618Sru
548153618Sru	return (error);
549153618Sru}
550153618Sru
551153618Srustatic device_method_t amdsmb_methods[] = {
552153618Sru	/* Device interface */
553153618Sru	DEVMETHOD(device_probe,		amdsmb_probe),
554153618Sru	DEVMETHOD(device_attach,	amdsmb_attach),
555153618Sru	DEVMETHOD(device_detach,	amdsmb_detach),
556153618Sru
557153618Sru	/* SMBus interface */
558153618Sru	DEVMETHOD(smbus_callback,	amdsmb_callback),
559153618Sru	DEVMETHOD(smbus_quick,		amdsmb_quick),
560153618Sru	DEVMETHOD(smbus_sendb,		amdsmb_sendb),
561153618Sru	DEVMETHOD(smbus_recvb,		amdsmb_recvb),
562153618Sru	DEVMETHOD(smbus_writeb,		amdsmb_writeb),
563153618Sru	DEVMETHOD(smbus_readb,		amdsmb_readb),
564153618Sru	DEVMETHOD(smbus_writew,		amdsmb_writew),
565153618Sru	DEVMETHOD(smbus_readw,		amdsmb_readw),
566153618Sru	DEVMETHOD(smbus_bwrite,		amdsmb_bwrite),
567153618Sru	DEVMETHOD(smbus_bread,		amdsmb_bread),
568153618Sru
569153618Sru	{ 0, 0 }
570153618Sru};
571153618Sru
572153618Srustatic devclass_t amdsmb_devclass;
573153618Sru
574153618Srustatic driver_t amdsmb_driver = {
575153618Sru	"amdsmb",
576153618Sru	amdsmb_methods,
577153618Sru	sizeof(struct amdsmb_softc),
578153618Sru};
579153618Sru
580153618SruDRIVER_MODULE(amdsmb, pci, amdsmb_driver, amdsmb_devclass, 0, 0);
581162234SjhbDRIVER_MODULE(smbus, amdsmb, smbus_driver, smbus_devclass, 0, 0);
582153618Sru
583153618SruMODULE_DEPEND(amdsmb, pci, 1, 1, 1);
584153618SruMODULE_DEPEND(amdsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
585153618SruMODULE_VERSION(amdsmb, 1);
586