xref: /freebsd/sys/dev/amdsmb/amdsmb.c (revision 5b56413d04e608379c9a306373554a8e4d321bc0)
1271b33a6SRui Paulo /*-
2271b33a6SRui Paulo  * Copyright (c) 2005 Ruslan Ermilov
3271b33a6SRui Paulo  * All rights reserved.
4271b33a6SRui Paulo  *
5271b33a6SRui Paulo  * Redistribution and use in source and binary forms, with or without
6271b33a6SRui Paulo  * modification, are permitted provided that the following conditions
7271b33a6SRui Paulo  * are met:
8271b33a6SRui Paulo  * 1. Redistributions of source code must retain the above copyright
9271b33a6SRui Paulo  *    notice, this list of conditions and the following disclaimer.
10271b33a6SRui Paulo  * 2. Redistributions in binary form must reproduce the above copyright
11271b33a6SRui Paulo  *    notice, this list of conditions and the following disclaimer in the
12271b33a6SRui Paulo  *    documentation and/or other materials provided with the distribution.
13271b33a6SRui Paulo  *
14271b33a6SRui Paulo  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15271b33a6SRui Paulo  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16271b33a6SRui Paulo  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17271b33a6SRui Paulo  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18271b33a6SRui Paulo  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19271b33a6SRui Paulo  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20271b33a6SRui Paulo  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21271b33a6SRui Paulo  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22271b33a6SRui Paulo  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23271b33a6SRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24271b33a6SRui Paulo  * SUCH DAMAGE.
25271b33a6SRui Paulo  */
26271b33a6SRui Paulo 
27271b33a6SRui Paulo #include <sys/param.h>
28271b33a6SRui Paulo #include <sys/bus.h>
29271b33a6SRui Paulo #include <sys/kernel.h>
30271b33a6SRui Paulo #include <sys/lock.h>
31271b33a6SRui Paulo #include <sys/module.h>
32271b33a6SRui Paulo #include <sys/mutex.h>
33271b33a6SRui Paulo #include <sys/systm.h>
34271b33a6SRui Paulo 
35271b33a6SRui Paulo #include <machine/bus.h>
36271b33a6SRui Paulo #include <machine/resource.h>
37271b33a6SRui Paulo #include <sys/rman.h>
38271b33a6SRui Paulo 
39271b33a6SRui Paulo #include <dev/pci/pcivar.h>
40271b33a6SRui Paulo #include <dev/pci/pcireg.h>
41271b33a6SRui Paulo 
42271b33a6SRui Paulo #include <dev/smbus/smbconf.h>
43271b33a6SRui Paulo #include "smbus_if.h"
44271b33a6SRui Paulo 
45271b33a6SRui Paulo #define	AMDSMB_DEBUG(x)	if (amdsmb_debug) (x)
46271b33a6SRui Paulo 
47271b33a6SRui Paulo #ifdef DEBUG
48271b33a6SRui Paulo static int amdsmb_debug = 1;
49271b33a6SRui Paulo #else
50271b33a6SRui Paulo static int amdsmb_debug = 0;
51271b33a6SRui Paulo #endif
52271b33a6SRui Paulo 
53271b33a6SRui Paulo #define	AMDSMB_VENDORID_AMD		0x1022
54271b33a6SRui Paulo #define	AMDSMB_DEVICEID_AMD8111_SMB2	0x746a
55271b33a6SRui Paulo 
56271b33a6SRui Paulo /*
57271b33a6SRui Paulo  * ACPI 3.0, Chapter 12, Embedded Controller Interface.
58271b33a6SRui Paulo  */
59271b33a6SRui Paulo #define	EC_DATA		0x00	/* data register */
60271b33a6SRui Paulo #define	EC_SC		0x04	/* status of controller */
61271b33a6SRui Paulo #define	EC_CMD		0x04	/* command register */
62271b33a6SRui Paulo 
63271b33a6SRui Paulo #define	EC_SC_IBF	0x02	/* data ready for embedded controller */
64271b33a6SRui Paulo #define	EC_SC_OBF	0x01	/* data ready for host */
65271b33a6SRui Paulo #define	EC_CMD_WR	0x81	/* write EC */
66271b33a6SRui Paulo #define	EC_CMD_RD	0x80	/* read EC */
67271b33a6SRui Paulo 
68271b33a6SRui Paulo /*
69271b33a6SRui Paulo  * ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
70271b33a6SRui Paulo  */
71271b33a6SRui Paulo #define	SMB_PRTCL	0x00	/* protocol */
72271b33a6SRui Paulo #define	SMB_STS		0x01	/* status */
73271b33a6SRui Paulo #define	SMB_ADDR	0x02	/* address */
74271b33a6SRui Paulo #define	SMB_CMD		0x03	/* command */
75271b33a6SRui Paulo #define	SMB_DATA	0x04	/* 32 data registers */
76271b33a6SRui Paulo #define	SMB_BCNT	0x24	/* number of data bytes */
77271b33a6SRui Paulo #define	SMB_ALRM_A	0x25	/* alarm address */
78271b33a6SRui Paulo #define	SMB_ALRM_D	0x26	/* 2 bytes alarm data */
79271b33a6SRui Paulo 
80271b33a6SRui Paulo #define	SMB_STS_DONE	0x80
81271b33a6SRui Paulo #define	SMB_STS_ALRM	0x40
82271b33a6SRui Paulo #define	SMB_STS_RES	0x20
83271b33a6SRui Paulo #define	SMB_STS_STATUS	0x1f
84271b33a6SRui Paulo #define	SMB_STS_OK	0x00	/* OK */
85271b33a6SRui Paulo #define	SMB_STS_UF	0x07	/* Unknown Failure */
86271b33a6SRui Paulo #define	SMB_STS_DANA	0x10	/* Device Address Not Acknowledged */
87271b33a6SRui Paulo #define	SMB_STS_DED	0x11	/* Device Error Detected */
88271b33a6SRui Paulo #define	SMB_STS_DCAD	0x12	/* Device Command Access Denied */
89271b33a6SRui Paulo #define	SMB_STS_UE	0x13	/* Unknown Error */
90271b33a6SRui Paulo #define	SMB_STS_DAD	0x17	/* Device Access Denied */
91271b33a6SRui Paulo #define	SMB_STS_T	0x18	/* Timeout */
92271b33a6SRui Paulo #define	SMB_STS_HUP	0x19	/* Host Unsupported Protocol */
93271b33a6SRui Paulo #define	SMB_STS_B	0x1a	/* Busy */
94271b33a6SRui Paulo #define	SMB_STS_PEC	0x1f	/* PEC (CRC-8) Error */
95271b33a6SRui Paulo 
96271b33a6SRui Paulo #define	SMB_PRTCL_WRITE			0x00
97271b33a6SRui Paulo #define	SMB_PRTCL_READ			0x01
98271b33a6SRui Paulo #define	SMB_PRTCL_QUICK			0x02
99271b33a6SRui Paulo #define	SMB_PRTCL_BYTE			0x04
100271b33a6SRui Paulo #define	SMB_PRTCL_BYTE_DATA		0x06
101271b33a6SRui Paulo #define	SMB_PRTCL_WORD_DATA		0x08
102271b33a6SRui Paulo #define	SMB_PRTCL_BLOCK_DATA		0x0a
103271b33a6SRui Paulo #define	SMB_PRTCL_PROC_CALL		0x0c
104271b33a6SRui Paulo #define	SMB_PRTCL_BLOCK_PROC_CALL	0x0d
105271b33a6SRui Paulo #define	SMB_PRTCL_PEC			0x80
106271b33a6SRui Paulo 
107271b33a6SRui Paulo struct amdsmb_softc {
108271b33a6SRui Paulo 	int rid;
109271b33a6SRui Paulo 	struct resource *res;
110271b33a6SRui Paulo 	device_t smbus;
111271b33a6SRui Paulo 	struct mtx lock;
112271b33a6SRui Paulo };
113271b33a6SRui Paulo 
114271b33a6SRui Paulo #define	AMDSMB_LOCK(amdsmb)		mtx_lock(&(amdsmb)->lock)
115271b33a6SRui Paulo #define	AMDSMB_UNLOCK(amdsmb)		mtx_unlock(&(amdsmb)->lock)
116271b33a6SRui Paulo #define	AMDSMB_LOCK_ASSERT(amdsmb)	mtx_assert(&(amdsmb)->lock, MA_OWNED)
117271b33a6SRui Paulo 
118271b33a6SRui Paulo #define	AMDSMB_ECINB(amdsmb, register)					\
119271b33a6SRui Paulo 	(bus_read_1(amdsmb->res, register))
120271b33a6SRui Paulo #define	AMDSMB_ECOUTB(amdsmb, register, value) \
121271b33a6SRui Paulo 	(bus_write_1(amdsmb->res, register, value))
122271b33a6SRui Paulo 
123271b33a6SRui Paulo static int	amdsmb_detach(device_t dev);
124271b33a6SRui Paulo 
12569c173e2SWarner Losh struct pci_device_table amdsmb_devs[] = {
12669c173e2SWarner Losh 	{ PCI_DEV(AMDSMB_VENDORID_AMD, AMDSMB_DEVICEID_AMD8111_SMB2),
12769c173e2SWarner Losh 	  PCI_DESCR("AMD-8111 SMBus 2.0 Controller") }
12869c173e2SWarner Losh };
12969c173e2SWarner Losh 
130271b33a6SRui Paulo static int
131271b33a6SRui Paulo amdsmb_probe(device_t dev)
132271b33a6SRui Paulo {
13369c173e2SWarner Losh 	const struct pci_device_table *tbl;
134271b33a6SRui Paulo 
13569c173e2SWarner Losh 	tbl = PCI_MATCH(dev, amdsmb_devs);
13669c173e2SWarner Losh 	if (tbl == NULL)
137271b33a6SRui Paulo 		return (ENXIO);
13869c173e2SWarner Losh 	device_set_desc(dev, tbl->descr);
13969c173e2SWarner Losh 
14069c173e2SWarner Losh 	return (BUS_PROBE_DEFAULT);
141271b33a6SRui Paulo }
142271b33a6SRui Paulo 
143271b33a6SRui Paulo static int
144271b33a6SRui Paulo amdsmb_attach(device_t dev)
145271b33a6SRui Paulo {
146271b33a6SRui Paulo 	struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
147271b33a6SRui Paulo 
148271b33a6SRui Paulo 	/* Allocate I/O space */
149271b33a6SRui Paulo 	amdsmb_sc->rid = PCIR_BAR(0);
150271b33a6SRui Paulo 
151271b33a6SRui Paulo 	amdsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
152271b33a6SRui Paulo 		&amdsmb_sc->rid, RF_ACTIVE);
153271b33a6SRui Paulo 
154271b33a6SRui Paulo 	if (amdsmb_sc->res == NULL) {
155271b33a6SRui Paulo 		device_printf(dev, "could not map i/o space\n");
156271b33a6SRui Paulo 		return (ENXIO);
157271b33a6SRui Paulo 	}
158271b33a6SRui Paulo 
159271b33a6SRui Paulo 	mtx_init(&amdsmb_sc->lock, device_get_nameunit(dev), "amdsmb", MTX_DEF);
160271b33a6SRui Paulo 
161271b33a6SRui Paulo 	/* Allocate a new smbus device */
162*5b56413dSWarner Losh 	amdsmb_sc->smbus = device_add_child(dev, "smbus", DEVICE_UNIT_ANY);
163271b33a6SRui Paulo 	if (!amdsmb_sc->smbus) {
164271b33a6SRui Paulo 		amdsmb_detach(dev);
165271b33a6SRui Paulo 		return (EINVAL);
166271b33a6SRui Paulo 	}
167271b33a6SRui Paulo 
168271b33a6SRui Paulo 	bus_generic_attach(dev);
169271b33a6SRui Paulo 
170271b33a6SRui Paulo 	return (0);
171271b33a6SRui Paulo }
172271b33a6SRui Paulo 
173271b33a6SRui Paulo static int
174271b33a6SRui Paulo amdsmb_detach(device_t dev)
175271b33a6SRui Paulo {
176271b33a6SRui Paulo 	struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
177271b33a6SRui Paulo 
178271b33a6SRui Paulo 	if (amdsmb_sc->smbus) {
179271b33a6SRui Paulo 		device_delete_child(dev, amdsmb_sc->smbus);
180271b33a6SRui Paulo 		amdsmb_sc->smbus = NULL;
181271b33a6SRui Paulo 	}
182271b33a6SRui Paulo 
183271b33a6SRui Paulo 	mtx_destroy(&amdsmb_sc->lock);
184271b33a6SRui Paulo 	if (amdsmb_sc->res)
185271b33a6SRui Paulo 		bus_release_resource(dev, SYS_RES_IOPORT, amdsmb_sc->rid,
186271b33a6SRui Paulo 		    amdsmb_sc->res);
187271b33a6SRui Paulo 
188271b33a6SRui Paulo 	return (0);
189271b33a6SRui Paulo }
190271b33a6SRui Paulo 
191271b33a6SRui Paulo static int
192271b33a6SRui Paulo amdsmb_callback(device_t dev, int index, void *data)
193271b33a6SRui Paulo {
194271b33a6SRui Paulo 	int error = 0;
195271b33a6SRui Paulo 
196271b33a6SRui Paulo 	switch (index) {
197271b33a6SRui Paulo 	case SMB_REQUEST_BUS:
198271b33a6SRui Paulo 	case SMB_RELEASE_BUS:
199271b33a6SRui Paulo 		break;
200271b33a6SRui Paulo 	default:
201271b33a6SRui Paulo 		error = EINVAL;
202271b33a6SRui Paulo 	}
203271b33a6SRui Paulo 
204271b33a6SRui Paulo 	return (error);
205271b33a6SRui Paulo }
206271b33a6SRui Paulo 
207271b33a6SRui Paulo static int
208271b33a6SRui Paulo amdsmb_ec_wait_write(struct amdsmb_softc *sc)
209271b33a6SRui Paulo {
210271b33a6SRui Paulo 	int timeout = 500;
211271b33a6SRui Paulo 
212271b33a6SRui Paulo 	while (timeout-- && AMDSMB_ECINB(sc, EC_SC) & EC_SC_IBF)
213271b33a6SRui Paulo 		DELAY(1);
214271b33a6SRui Paulo 	if (timeout == 0) {
215271b33a6SRui Paulo 		device_printf(sc->smbus, "timeout waiting for IBF to clear\n");
216271b33a6SRui Paulo 		return (1);
217271b33a6SRui Paulo 	}
218271b33a6SRui Paulo 	return (0);
219271b33a6SRui Paulo }
220271b33a6SRui Paulo 
221271b33a6SRui Paulo static int
222271b33a6SRui Paulo amdsmb_ec_wait_read(struct amdsmb_softc *sc)
223271b33a6SRui Paulo {
224271b33a6SRui Paulo 	int timeout = 500;
225271b33a6SRui Paulo 
226271b33a6SRui Paulo 	while (timeout-- && ~AMDSMB_ECINB(sc, EC_SC) & EC_SC_OBF)
227271b33a6SRui Paulo 		DELAY(1);
228271b33a6SRui Paulo 	if (timeout == 0) {
229271b33a6SRui Paulo 		device_printf(sc->smbus, "timeout waiting for OBF to set\n");
230271b33a6SRui Paulo 		return (1);
231271b33a6SRui Paulo 	}
232271b33a6SRui Paulo 	return (0);
233271b33a6SRui Paulo }
234271b33a6SRui Paulo 
235271b33a6SRui Paulo static int
236271b33a6SRui Paulo amdsmb_ec_read(struct amdsmb_softc *sc, u_char addr, u_char *data)
237271b33a6SRui Paulo {
238271b33a6SRui Paulo 
239271b33a6SRui Paulo 	AMDSMB_LOCK_ASSERT(sc);
240271b33a6SRui Paulo 	if (amdsmb_ec_wait_write(sc))
241271b33a6SRui Paulo 		return (1);
242271b33a6SRui Paulo 	AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_RD);
243271b33a6SRui Paulo 
244271b33a6SRui Paulo 	if (amdsmb_ec_wait_write(sc))
245271b33a6SRui Paulo 		return (1);
246271b33a6SRui Paulo 	AMDSMB_ECOUTB(sc, EC_DATA, addr);
247271b33a6SRui Paulo 
248271b33a6SRui Paulo 	if (amdsmb_ec_wait_read(sc))
249271b33a6SRui Paulo 		return (1);
250271b33a6SRui Paulo 	*data = AMDSMB_ECINB(sc, EC_DATA);
251271b33a6SRui Paulo 
252271b33a6SRui Paulo 	return (0);
253271b33a6SRui Paulo }
254271b33a6SRui Paulo 
255271b33a6SRui Paulo static int
256271b33a6SRui Paulo amdsmb_ec_write(struct amdsmb_softc *sc, u_char addr, u_char data)
257271b33a6SRui Paulo {
258271b33a6SRui Paulo 
259271b33a6SRui Paulo 	AMDSMB_LOCK_ASSERT(sc);
260271b33a6SRui Paulo 	if (amdsmb_ec_wait_write(sc))
261271b33a6SRui Paulo 		return (1);
262271b33a6SRui Paulo 	AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_WR);
263271b33a6SRui Paulo 
264271b33a6SRui Paulo 	if (amdsmb_ec_wait_write(sc))
265271b33a6SRui Paulo 		return (1);
266271b33a6SRui Paulo 	AMDSMB_ECOUTB(sc, EC_DATA, addr);
267271b33a6SRui Paulo 
268271b33a6SRui Paulo 	if (amdsmb_ec_wait_write(sc))
269271b33a6SRui Paulo 		return (1);
270271b33a6SRui Paulo 	AMDSMB_ECOUTB(sc, EC_DATA, data);
271271b33a6SRui Paulo 
272271b33a6SRui Paulo 	return (0);
273271b33a6SRui Paulo }
274271b33a6SRui Paulo 
275271b33a6SRui Paulo static int
276271b33a6SRui Paulo amdsmb_wait(struct amdsmb_softc *sc)
277271b33a6SRui Paulo {
278271b33a6SRui Paulo 	u_char sts, temp;
279271b33a6SRui Paulo 	int error, count;
280271b33a6SRui Paulo 
281271b33a6SRui Paulo 	AMDSMB_LOCK_ASSERT(sc);
282271b33a6SRui Paulo 	amdsmb_ec_read(sc, SMB_PRTCL, &temp);
283271b33a6SRui Paulo 	if (temp != 0)
284271b33a6SRui Paulo 	{
285271b33a6SRui Paulo 		count = 10000;
286271b33a6SRui Paulo 		do {
287271b33a6SRui Paulo 			DELAY(500);
288271b33a6SRui Paulo 			amdsmb_ec_read(sc, SMB_PRTCL, &temp);
289271b33a6SRui Paulo 		} while (temp != 0 && count--);
290271b33a6SRui Paulo 		if (count == 0)
291271b33a6SRui Paulo 			return (SMB_ETIMEOUT);
292271b33a6SRui Paulo 	}
293271b33a6SRui Paulo 
294271b33a6SRui Paulo 	amdsmb_ec_read(sc, SMB_STS, &sts);
295271b33a6SRui Paulo 	sts &= SMB_STS_STATUS;
296271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: STS=0x%x\n", sts));
297271b33a6SRui Paulo 
298271b33a6SRui Paulo 	switch (sts) {
299271b33a6SRui Paulo 	case SMB_STS_OK:
300271b33a6SRui Paulo 		error = SMB_ENOERR;
301271b33a6SRui Paulo 		break;
302271b33a6SRui Paulo 	case SMB_STS_DANA:
303271b33a6SRui Paulo 		error = SMB_ENOACK;
304271b33a6SRui Paulo 		break;
305271b33a6SRui Paulo 	case SMB_STS_B:
306271b33a6SRui Paulo 		error = SMB_EBUSY;
307271b33a6SRui Paulo 		break;
308271b33a6SRui Paulo 	case SMB_STS_T:
309271b33a6SRui Paulo 		error = SMB_ETIMEOUT;
310271b33a6SRui Paulo 		break;
311271b33a6SRui Paulo 	case SMB_STS_DCAD:
312271b33a6SRui Paulo 	case SMB_STS_DAD:
313271b33a6SRui Paulo 	case SMB_STS_HUP:
314271b33a6SRui Paulo 		error = SMB_ENOTSUPP;
315271b33a6SRui Paulo 		break;
316271b33a6SRui Paulo 	default:
317271b33a6SRui Paulo 		error = SMB_EBUSERR;
318271b33a6SRui Paulo 		break;
319271b33a6SRui Paulo 	}
320271b33a6SRui Paulo 
321271b33a6SRui Paulo 	return (error);
322271b33a6SRui Paulo }
323271b33a6SRui Paulo 
324271b33a6SRui Paulo static int
325271b33a6SRui Paulo amdsmb_quick(device_t dev, u_char slave, int how)
326271b33a6SRui Paulo {
327271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
328271b33a6SRui Paulo 	u_char protocol;
329271b33a6SRui Paulo 	int error;
330271b33a6SRui Paulo 
331271b33a6SRui Paulo 	protocol = SMB_PRTCL_QUICK;
332271b33a6SRui Paulo 
333271b33a6SRui Paulo 	switch (how) {
334271b33a6SRui Paulo 	case SMB_QWRITE:
335271b33a6SRui Paulo 		protocol |= SMB_PRTCL_WRITE;
336271b33a6SRui Paulo 		AMDSMB_DEBUG(printf("amdsmb: QWRITE to 0x%x", slave));
337271b33a6SRui Paulo 		break;
338271b33a6SRui Paulo 	case SMB_QREAD:
339271b33a6SRui Paulo 		protocol |= SMB_PRTCL_READ;
340271b33a6SRui Paulo 		AMDSMB_DEBUG(printf("amdsmb: QREAD to 0x%x", slave));
341271b33a6SRui Paulo 		break;
342271b33a6SRui Paulo 	default:
343271b33a6SRui Paulo 		panic("%s: unknown QUICK command (%x)!", __func__, how);
344271b33a6SRui Paulo 	}
345271b33a6SRui Paulo 
346271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
347271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
348271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, protocol);
349271b33a6SRui Paulo 
350271b33a6SRui Paulo 	error = amdsmb_wait(sc);
351271b33a6SRui Paulo 
352271b33a6SRui Paulo 	AMDSMB_DEBUG(printf(", error=0x%x\n", error));
353271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
354271b33a6SRui Paulo 
355271b33a6SRui Paulo 	return (error);
356271b33a6SRui Paulo }
357271b33a6SRui Paulo 
358271b33a6SRui Paulo static int
359271b33a6SRui Paulo amdsmb_sendb(device_t dev, u_char slave, char byte)
360271b33a6SRui Paulo {
361271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
362271b33a6SRui Paulo 	int error;
363271b33a6SRui Paulo 
364271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
365271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_CMD, byte);
366271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
367271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
368271b33a6SRui Paulo 
369271b33a6SRui Paulo 	error = amdsmb_wait(sc);
370271b33a6SRui Paulo 
371271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n",
372271b33a6SRui Paulo 	   slave, byte, error));
373271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
374271b33a6SRui Paulo 
375271b33a6SRui Paulo 	return (error);
376271b33a6SRui Paulo }
377271b33a6SRui Paulo 
378271b33a6SRui Paulo static int
379271b33a6SRui Paulo amdsmb_recvb(device_t dev, u_char slave, char *byte)
380271b33a6SRui Paulo {
381271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
382271b33a6SRui Paulo 	int error;
383271b33a6SRui Paulo 
384271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
385271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
386271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
387271b33a6SRui Paulo 
388271b33a6SRui Paulo 	if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
389271b33a6SRui Paulo 		amdsmb_ec_read(sc, SMB_DATA, byte);
390271b33a6SRui Paulo 
391271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n",
392271b33a6SRui Paulo 	    slave, *byte, error));
393271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
394271b33a6SRui Paulo 
395271b33a6SRui Paulo 	return (error);
396271b33a6SRui Paulo }
397271b33a6SRui Paulo 
398271b33a6SRui Paulo static int
399271b33a6SRui Paulo amdsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
400271b33a6SRui Paulo {
401271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
402271b33a6SRui Paulo 	int error;
403271b33a6SRui Paulo 
404271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
405271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_CMD, cmd);
406271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_DATA, byte);
407271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
408271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
409271b33a6SRui Paulo 
410271b33a6SRui Paulo 	error = amdsmb_wait(sc);
411271b33a6SRui Paulo 
412271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, "
413271b33a6SRui Paulo 	    "error=0x%x\n", slave, cmd, byte, error));
414271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
415271b33a6SRui Paulo 
416271b33a6SRui Paulo 	return (error);
417271b33a6SRui Paulo }
418271b33a6SRui Paulo 
419271b33a6SRui Paulo static int
420271b33a6SRui Paulo amdsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
421271b33a6SRui Paulo {
422271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
423271b33a6SRui Paulo 	int error;
424271b33a6SRui Paulo 
425271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
426271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_CMD, cmd);
427271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
428271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
429271b33a6SRui Paulo 
430271b33a6SRui Paulo 	if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
431271b33a6SRui Paulo 		amdsmb_ec_read(sc, SMB_DATA, byte);
432271b33a6SRui Paulo 
433271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, "
434271b33a6SRui Paulo 	    "error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
435271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
436271b33a6SRui Paulo 
437271b33a6SRui Paulo 	return (error);
438271b33a6SRui Paulo }
439271b33a6SRui Paulo 
440271b33a6SRui Paulo static int
441271b33a6SRui Paulo amdsmb_writew(device_t dev, u_char slave, char cmd, short word)
442271b33a6SRui Paulo {
443271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
444271b33a6SRui Paulo 	int error;
445271b33a6SRui Paulo 
446271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
447271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_CMD, cmd);
448271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_DATA, word);
449271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_DATA + 1, word >> 8);
450271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
451271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
452271b33a6SRui Paulo 
453271b33a6SRui Paulo 	error = amdsmb_wait(sc);
454271b33a6SRui Paulo 
455271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, "
456271b33a6SRui Paulo 	    "error=0x%x\n", slave, cmd, word, error));
457271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
458271b33a6SRui Paulo 
459271b33a6SRui Paulo 	return (error);
460271b33a6SRui Paulo }
461271b33a6SRui Paulo 
462271b33a6SRui Paulo static int
463271b33a6SRui Paulo amdsmb_readw(device_t dev, u_char slave, char cmd, short *word)
464271b33a6SRui Paulo {
465271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
466271b33a6SRui Paulo 	u_char temp[2];
467271b33a6SRui Paulo 	int error;
468271b33a6SRui Paulo 
469271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
470271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_CMD, cmd);
471271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
472271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
473271b33a6SRui Paulo 
474271b33a6SRui Paulo 	if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
475271b33a6SRui Paulo 		amdsmb_ec_read(sc, SMB_DATA + 0, &temp[0]);
476271b33a6SRui Paulo 		amdsmb_ec_read(sc, SMB_DATA + 1, &temp[1]);
477271b33a6SRui Paulo 		*word = temp[0] | (temp[1] << 8);
478271b33a6SRui Paulo 	}
479271b33a6SRui Paulo 
480271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: READW from 0x%x, cmd=0x%x, word=0x%x, "
481271b33a6SRui Paulo 	    "error=0x%x\n", slave, cmd, (unsigned short)*word, error));
482271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
483271b33a6SRui Paulo 
484271b33a6SRui Paulo 	return (error);
485271b33a6SRui Paulo }
486271b33a6SRui Paulo 
487271b33a6SRui Paulo static int
488271b33a6SRui Paulo amdsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
489271b33a6SRui Paulo {
490271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
491271b33a6SRui Paulo 	u_char i;
492271b33a6SRui Paulo 	int error;
493271b33a6SRui Paulo 
494271b33a6SRui Paulo 	if (count < 1 || count > 32)
495271b33a6SRui Paulo 		return (SMB_EINVAL);
496271b33a6SRui Paulo 
497271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
498271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_CMD, cmd);
499271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_BCNT, count);
500271b33a6SRui Paulo 	for (i = 0; i < count; i++)
501271b33a6SRui Paulo 		amdsmb_ec_write(sc, SMB_DATA + i, buf[i]);
502271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
503271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
504271b33a6SRui Paulo 
505271b33a6SRui Paulo 	error = amdsmb_wait(sc);
506271b33a6SRui Paulo 
507271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, "
508271b33a6SRui Paulo 	    "error=0x%x", slave, count, cmd, error));
509271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
510271b33a6SRui Paulo 
511271b33a6SRui Paulo 	return (error);
512271b33a6SRui Paulo }
513271b33a6SRui Paulo 
514271b33a6SRui Paulo static int
515271b33a6SRui Paulo amdsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
516271b33a6SRui Paulo {
517271b33a6SRui Paulo 	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
518271b33a6SRui Paulo 	u_char data, len, i;
519271b33a6SRui Paulo 	int error;
520271b33a6SRui Paulo 
521271b33a6SRui Paulo 	if (*count < 1 || *count > 32)
522271b33a6SRui Paulo 		return (SMB_EINVAL);
523271b33a6SRui Paulo 
524271b33a6SRui Paulo 	AMDSMB_LOCK(sc);
525271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_CMD, cmd);
526271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_ADDR, slave);
527271b33a6SRui Paulo 	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
528271b33a6SRui Paulo 
529271b33a6SRui Paulo 	if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
530271b33a6SRui Paulo 		amdsmb_ec_read(sc, SMB_BCNT, &len);
531271b33a6SRui Paulo 		for (i = 0; i < len; i++) {
532271b33a6SRui Paulo 			amdsmb_ec_read(sc, SMB_DATA + i, &data);
533271b33a6SRui Paulo 			if (i < *count)
534271b33a6SRui Paulo 				buf[i] = data;
535271b33a6SRui Paulo 		}
536271b33a6SRui Paulo 		*count = len;
537271b33a6SRui Paulo 	}
538271b33a6SRui Paulo 
539271b33a6SRui Paulo 	AMDSMB_DEBUG(printf("amdsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, "
540271b33a6SRui Paulo 	    "error=0x%x", slave, *count, cmd, error));
541271b33a6SRui Paulo 	AMDSMB_UNLOCK(sc);
542271b33a6SRui Paulo 
543271b33a6SRui Paulo 	return (error);
544271b33a6SRui Paulo }
545271b33a6SRui Paulo 
546271b33a6SRui Paulo static device_method_t amdsmb_methods[] = {
547271b33a6SRui Paulo 	/* Device interface */
548271b33a6SRui Paulo 	DEVMETHOD(device_probe,		amdsmb_probe),
549271b33a6SRui Paulo 	DEVMETHOD(device_attach,	amdsmb_attach),
550271b33a6SRui Paulo 	DEVMETHOD(device_detach,	amdsmb_detach),
551271b33a6SRui Paulo 
552271b33a6SRui Paulo 	/* SMBus interface */
553271b33a6SRui Paulo 	DEVMETHOD(smbus_callback,	amdsmb_callback),
554271b33a6SRui Paulo 	DEVMETHOD(smbus_quick,		amdsmb_quick),
555271b33a6SRui Paulo 	DEVMETHOD(smbus_sendb,		amdsmb_sendb),
556271b33a6SRui Paulo 	DEVMETHOD(smbus_recvb,		amdsmb_recvb),
557271b33a6SRui Paulo 	DEVMETHOD(smbus_writeb,		amdsmb_writeb),
558271b33a6SRui Paulo 	DEVMETHOD(smbus_readb,		amdsmb_readb),
559271b33a6SRui Paulo 	DEVMETHOD(smbus_writew,		amdsmb_writew),
560271b33a6SRui Paulo 	DEVMETHOD(smbus_readw,		amdsmb_readw),
561271b33a6SRui Paulo 	DEVMETHOD(smbus_bwrite,		amdsmb_bwrite),
562271b33a6SRui Paulo 	DEVMETHOD(smbus_bread,		amdsmb_bread),
563271b33a6SRui Paulo 	{ 0, 0 }
564271b33a6SRui Paulo };
565271b33a6SRui Paulo 
566271b33a6SRui Paulo static driver_t amdsmb_driver = {
567271b33a6SRui Paulo 	"amdsmb",
568271b33a6SRui Paulo 	amdsmb_methods,
569271b33a6SRui Paulo 	sizeof(struct amdsmb_softc),
570271b33a6SRui Paulo };
571271b33a6SRui Paulo 
57283a273efSJohn Baldwin DRIVER_MODULE(amdsmb, pci, amdsmb_driver, 0, 0);
573c6d39765SJohn Baldwin DRIVER_MODULE(smbus, amdsmb, smbus_driver, 0, 0);
574271b33a6SRui Paulo 
575271b33a6SRui Paulo MODULE_DEPEND(amdsmb, pci, 1, 1, 1);
576271b33a6SRui Paulo MODULE_DEPEND(amdsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
577271b33a6SRui Paulo MODULE_VERSION(amdsmb, 1);
578