amdsmb.c revision 162234
1#include <sys/cdefs.h>
2__FBSDID("$FreeBSD: head/sys/pci/amdsmb.c 162234 2006-09-11 20:52:41Z jhb $");
3
4#include <sys/param.h>
5#include <sys/kernel.h>
6#include <sys/systm.h>
7#include <sys/module.h>
8#include <sys/bus.h>
9#include <sys/uio.h>
10
11#include <machine/bus.h>
12#include <machine/resource.h>
13#include <sys/rman.h>
14
15#include <dev/pci/pcivar.h>
16#include <dev/pci/pcireg.h>
17
18#include <dev/iicbus/iiconf.h>
19#include <dev/smbus/smbconf.h>
20#include "smbus_if.h"
21
22#define	AMDSMB_DEBUG(x)	if (amdsmb_debug) (x)
23
24#ifdef DEBUG
25static int amdsmb_debug = 1;
26#else
27static int amdsmb_debug = 0;
28#endif
29
30#define	AMDSMB_VENDORID_AMD		0x1022
31#define	AMDSMB_DEVICEID_AMD8111_SMB2	0x746a
32
33/*
34 * ACPI 3.0, Chapter 12, Embedded Controller Interface.
35 */
36#define	EC_DATA		0x00	/* data register */
37#define	EC_SC		0x04	/* status of controller */
38#define	EC_CMD		0x04	/* command register */
39
40#define	EC_SC_IBF	0x02	/* data ready for embedded controller */
41#define	EC_SC_OBF	0x01	/* data ready for host */
42#define	EC_CMD_WR	0x81	/* write EC */
43#define	EC_CMD_RD	0x80	/* read EC */
44
45/*
46 * ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
47 */
48#define	SMB_PRTCL	0x00	/* protocol */
49#define	SMB_STS		0x01	/* status */
50#define	SMB_ADDR	0x02	/* address */
51#define	SMB_CMD		0x03	/* command */
52#define	SMB_DATA	0x04	/* 32 data registers */
53#define	SMB_BCNT	0x24	/* number of data bytes */
54#define	SMB_ALRM_A	0x25	/* alarm address */
55#define	SMB_ALRM_D	0x26	/* 2 bytes alarm data */
56
57#define	SMB_STS_DONE	0x80
58#define	SMB_STS_ALRM	0x40
59#define	SMB_STS_RES	0x20
60#define	SMB_STS_STATUS	0x1f
61#define	SMB_STS_OK	0x00	/* OK */
62#define	SMB_STS_UF	0x07	/* Unknown Failure */
63#define	SMB_STS_DANA	0x10	/* Device Address Not Acknowledged */
64#define	SMB_STS_DED	0x11	/* Device Error Detected */
65#define	SMB_STS_DCAD	0x12	/* Device Command Access Denied */
66#define	SMB_STS_UE	0x13	/* Unknown Error */
67#define	SMB_STS_DAD	0x17	/* Device Access Denied */
68#define	SMB_STS_T	0x18	/* Timeout */
69#define	SMB_STS_HUP	0x19	/* Host Unsupported Protocol */
70#define	SMB_STS_B	0x1a	/* Busy */
71#define	SMB_STS_PEC	0x1f	/* PEC (CRC-8) Error */
72
73#define	SMB_PRTCL_WRITE			0x00
74#define	SMB_PRTCL_READ			0x01
75#define	SMB_PRTCL_QUICK			0x02
76#define	SMB_PRTCL_BYTE			0x04
77#define	SMB_PRTCL_BYTE_DATA		0x06
78#define	SMB_PRTCL_WORD_DATA		0x08
79#define	SMB_PRTCL_BLOCK_DATA		0x0a
80#define	SMB_PRTCL_PROC_CALL		0x0c
81#define	SMB_PRTCL_BLOCK_PROC_CALL	0x0d
82#define	SMB_PRTCL_PEC			0x80
83
84struct amdsmb_softc {
85	int rid;
86	struct resource *res;
87	bus_space_tag_t smbst;
88	bus_space_handle_t smbsh;
89
90	device_t smbus;
91};
92
93#define	AMDSMB_ECINB(amdsmb, register) \
94	(bus_space_read_1(amdsmb->smbst, amdsmb->smbsh, register))
95#define	AMDSMB_ECOUTB(amdsmb, register, value) \
96	(bus_space_write_1(amdsmb->smbst, amdsmb->smbsh, register, value))
97
98static int
99amdsmb_probe(device_t dev)
100{
101	u_int16_t vid;
102	u_int16_t did;
103
104	vid = pci_get_vendor(dev);
105	did = pci_get_device(dev);
106
107	if (vid == AMDSMB_VENDORID_AMD) {
108		switch(did) {
109		case AMDSMB_DEVICEID_AMD8111_SMB2:
110			device_set_desc(dev, "AMD-8111 SMBus 2.0 Controller");
111			return (BUS_PROBE_DEFAULT);
112		}
113	}
114
115	return (ENXIO);
116}
117
118static int
119amdsmb_attach(device_t dev)
120{
121	struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
122
123	/* Allocate I/O space */
124	amdsmb_sc->rid = PCIR_BAR(0);
125
126	amdsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
127		&amdsmb_sc->rid, RF_ACTIVE);
128
129	if (amdsmb_sc->res == NULL) {
130		device_printf(dev, "could not map i/o space\n");
131		return (ENXIO);
132	}
133
134	amdsmb_sc->smbst = rman_get_bustag(amdsmb_sc->res);
135	amdsmb_sc->smbsh = rman_get_bushandle(amdsmb_sc->res);
136
137	/* Allocate a new smbus device */
138	amdsmb_sc->smbus = device_add_child(dev, "smbus", -1);
139	if (!amdsmb_sc->smbus)
140		return (EINVAL);
141
142	bus_generic_attach(dev);
143
144	return (0);
145}
146
147static int
148amdsmb_detach(device_t dev)
149{
150	struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
151
152	if (amdsmb_sc->smbus) {
153		device_delete_child(dev, amdsmb_sc->smbus);
154		amdsmb_sc->smbus = NULL;
155	}
156
157	if (amdsmb_sc->res)
158		bus_release_resource(dev, SYS_RES_IOPORT, amdsmb_sc->rid,
159		    amdsmb_sc->res);
160
161	return (0);
162}
163
164static int
165amdsmb_callback(device_t dev, int index, void *data)
166{
167	int error = 0;
168
169	switch (index) {
170	case SMB_REQUEST_BUS:
171	case SMB_RELEASE_BUS:
172		break;
173	default:
174		error = EINVAL;
175	}
176
177	return (error);
178}
179
180static int
181amdsmb_ec_wait_write(struct amdsmb_softc *sc)
182{
183	int timeout = 500;
184
185	while (timeout-- && AMDSMB_ECINB(sc, EC_SC) & EC_SC_IBF)
186		DELAY(1);
187	if (timeout == 0) {
188		device_printf(sc->smbus, "timeout waiting for IBF to clear\n");
189		return (1);
190	}
191	return (0);
192}
193
194static int
195amdsmb_ec_wait_read(struct amdsmb_softc *sc)
196{
197	int timeout = 500;
198
199	while (timeout-- && ~AMDSMB_ECINB(sc, EC_SC) & EC_SC_OBF)
200		DELAY(1);
201	if (timeout == 0) {
202		device_printf(sc->smbus, "timeout waiting for OBF to set\n");
203		return (1);
204	}
205	return (0);
206}
207
208static int
209amdsmb_ec_read(struct amdsmb_softc *sc, u_char addr, u_char *data)
210{
211
212	if (amdsmb_ec_wait_write(sc))
213		return (1);
214	AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_RD);
215
216	if (amdsmb_ec_wait_write(sc))
217		return (1);
218	AMDSMB_ECOUTB(sc, EC_DATA, addr);
219
220	if (amdsmb_ec_wait_read(sc))
221		return (1);
222	*data = AMDSMB_ECINB(sc, EC_DATA);
223
224	return (0);
225}
226
227static int
228amdsmb_ec_write(struct amdsmb_softc *sc, u_char addr, u_char data)
229{
230
231	if (amdsmb_ec_wait_write(sc))
232		return (1);
233	AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_WR);
234
235	if (amdsmb_ec_wait_write(sc))
236		return (1);
237	AMDSMB_ECOUTB(sc, EC_DATA, addr);
238
239	if (amdsmb_ec_wait_write(sc))
240		return (1);
241	AMDSMB_ECOUTB(sc, EC_DATA, data);
242
243	return (0);
244}
245
246static int
247amdsmb_wait(struct amdsmb_softc *sc)
248{
249	u_char sts, temp;
250	int error, count;
251
252	amdsmb_ec_read(sc, SMB_PRTCL, &temp);
253	if (temp != 0)
254	{
255		count = 10000;
256		do {
257			DELAY(500);
258			amdsmb_ec_read(sc, SMB_PRTCL, &temp);
259		} while (temp != 0 && count--);
260		if (count == 0)
261			return (SMB_ETIMEOUT);
262	}
263
264	amdsmb_ec_read(sc, SMB_STS, &sts);
265	sts &= SMB_STS_STATUS;
266	AMDSMB_DEBUG(printf("amdsmb: STS=0x%x\n", sts));
267
268	switch (sts) {
269	case SMB_STS_OK:
270		error = SMB_ENOERR;
271		break;
272	case SMB_STS_DANA:
273		error = SMB_ENOACK;
274		break;
275	case SMB_STS_B:
276		error = SMB_EBUSY;
277		break;
278	case SMB_STS_T:
279		error = SMB_ETIMEOUT;
280		break;
281	case SMB_STS_DCAD:
282	case SMB_STS_DAD:
283	case SMB_STS_HUP:
284		error = SMB_ENOTSUPP;
285		break;
286	default:
287		error = SMB_EBUSERR;
288		break;
289	}
290
291	return (error);
292}
293
294static int
295amdsmb_quick(device_t dev, u_char slave, int how)
296{
297	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
298	u_char protocol;
299	int error;
300
301	protocol = SMB_PRTCL_QUICK;
302
303	switch (how) {
304	case SMB_QWRITE:
305		protocol |= SMB_PRTCL_WRITE;
306		AMDSMB_DEBUG(printf("amdsmb: QWRITE to 0x%x", slave));
307		break;
308	case SMB_QREAD:
309		protocol |= SMB_PRTCL_READ;
310		AMDSMB_DEBUG(printf("amdsmb: QREAD to 0x%x", slave));
311		break;
312	default:
313		panic("%s: unknown QUICK command (%x)!", __func__, how);
314	}
315
316	amdsmb_ec_write(sc, SMB_ADDR, slave);
317	amdsmb_ec_write(sc, SMB_PRTCL, protocol);
318
319	error = amdsmb_wait(sc);
320
321	AMDSMB_DEBUG(printf(", error=0x%x\n", error));
322
323	return (error);
324}
325
326static int
327amdsmb_sendb(device_t dev, u_char slave, char byte)
328{
329	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
330	int error;
331
332	amdsmb_ec_write(sc, SMB_CMD, byte);
333	amdsmb_ec_write(sc, SMB_ADDR, slave);
334	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
335
336	error = amdsmb_wait(sc);
337
338	AMDSMB_DEBUG(printf("amdsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n",
339	   slave, byte, error));
340
341	return (error);
342}
343
344static int
345amdsmb_recvb(device_t dev, u_char slave, char *byte)
346{
347	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
348	int error;
349
350	amdsmb_ec_write(sc, SMB_ADDR, slave);
351	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
352
353	if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
354		amdsmb_ec_read(sc, SMB_DATA, byte);
355
356	AMDSMB_DEBUG(printf("amdsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n",
357	    slave, *byte, error));
358
359	return (error);
360}
361
362static int
363amdsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
364{
365	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
366	int error;
367
368	amdsmb_ec_write(sc, SMB_CMD, cmd);
369	amdsmb_ec_write(sc, SMB_DATA, byte);
370	amdsmb_ec_write(sc, SMB_ADDR, slave);
371	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
372
373	error = amdsmb_wait(sc);
374
375	AMDSMB_DEBUG(printf("amdsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, "
376	    "error=0x%x\n", slave, cmd, byte, error));
377
378	return (error);
379}
380
381static int
382amdsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
383{
384	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
385	int error;
386
387	amdsmb_ec_write(sc, SMB_CMD, cmd);
388	amdsmb_ec_write(sc, SMB_ADDR, slave);
389	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
390
391	if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
392		amdsmb_ec_read(sc, SMB_DATA, byte);
393
394	AMDSMB_DEBUG(printf("amdsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, "
395	    "error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
396
397	return (error);
398}
399
400static int
401amdsmb_writew(device_t dev, u_char slave, char cmd, short word)
402{
403	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
404	int error;
405
406	amdsmb_ec_write(sc, SMB_CMD, cmd);
407	amdsmb_ec_write(sc, SMB_DATA, word);
408	amdsmb_ec_write(sc, SMB_DATA + 1, word >> 8);
409	amdsmb_ec_write(sc, SMB_ADDR, slave);
410	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
411
412	error = amdsmb_wait(sc);
413
414	AMDSMB_DEBUG(printf("amdsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, "
415	    "error=0x%x\n", slave, cmd, word, error));
416
417	return (error);
418}
419
420static int
421amdsmb_readw(device_t dev, u_char slave, char cmd, short *word)
422{
423	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
424	u_char temp[2];
425	int error;
426
427	amdsmb_ec_write(sc, SMB_CMD, cmd);
428	amdsmb_ec_write(sc, SMB_ADDR, slave);
429	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
430
431	if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
432		amdsmb_ec_read(sc, SMB_DATA + 0, &temp[0]);
433		amdsmb_ec_read(sc, SMB_DATA + 1, &temp[1]);
434		*word = temp[0] | (temp[1] << 8);
435	}
436
437	AMDSMB_DEBUG(printf("amdsmb: READW from 0x%x, cmd=0x%x, word=0x%x, "
438	    "error=0x%x\n", slave, cmd, (unsigned short)*word, error));
439
440	return (error);
441}
442
443static int
444amdsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
445{
446	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
447	u_char i;
448	int error;
449
450	if (count < 1 || count > 32)
451		return (SMB_EINVAL);
452	amdsmb_ec_write(sc, SMB_CMD, cmd);
453	amdsmb_ec_write(sc, SMB_BCNT, count);
454	for (i = 0; i < count; i++)
455		amdsmb_ec_write(sc, SMB_DATA + i, buf[i]);
456	amdsmb_ec_write(sc, SMB_ADDR, slave);
457	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
458
459	error = amdsmb_wait(sc);
460
461	AMDSMB_DEBUG(printf("amdsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, "
462	    "error=0x%x", slave, count, cmd, error));
463
464	return (error);
465}
466
467static int
468amdsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
469{
470	struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
471	u_char data, len, i;
472	int error;
473
474	if (*count < 1 || *count > 32)
475		return (SMB_EINVAL);
476	amdsmb_ec_write(sc, SMB_CMD, cmd);
477	amdsmb_ec_write(sc, SMB_ADDR, slave);
478	amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
479
480	if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
481		amdsmb_ec_read(sc, SMB_BCNT, &len);
482		for (i = 0; i < len; i++) {
483			amdsmb_ec_read(sc, SMB_DATA + i, &data);
484			if (i < *count)
485				buf[i] = data;
486		}
487		*count = len;
488	}
489
490	AMDSMB_DEBUG(printf("amdsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, "
491	    "error=0x%x", slave, *count, cmd, error));
492
493	return (error);
494}
495
496static device_method_t amdsmb_methods[] = {
497	/* Device interface */
498	DEVMETHOD(device_probe,		amdsmb_probe),
499	DEVMETHOD(device_attach,	amdsmb_attach),
500	DEVMETHOD(device_detach,	amdsmb_detach),
501
502	/* SMBus interface */
503	DEVMETHOD(smbus_callback,	amdsmb_callback),
504	DEVMETHOD(smbus_quick,		amdsmb_quick),
505	DEVMETHOD(smbus_sendb,		amdsmb_sendb),
506	DEVMETHOD(smbus_recvb,		amdsmb_recvb),
507	DEVMETHOD(smbus_writeb,		amdsmb_writeb),
508	DEVMETHOD(smbus_readb,		amdsmb_readb),
509	DEVMETHOD(smbus_writew,		amdsmb_writew),
510	DEVMETHOD(smbus_readw,		amdsmb_readw),
511	DEVMETHOD(smbus_bwrite,		amdsmb_bwrite),
512	DEVMETHOD(smbus_bread,		amdsmb_bread),
513
514	{ 0, 0 }
515};
516
517static devclass_t amdsmb_devclass;
518
519static driver_t amdsmb_driver = {
520	"amdsmb",
521	amdsmb_methods,
522	sizeof(struct amdsmb_softc),
523};
524
525DRIVER_MODULE(amdsmb, pci, amdsmb_driver, amdsmb_devclass, 0, 0);
526DRIVER_MODULE(smbus, amdsmb, smbus_driver, smbus_devclass, 0, 0);
527
528MODULE_DEPEND(amdsmb, pci, 1, 1, 1);
529MODULE_DEPEND(amdsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
530MODULE_VERSION(amdsmb, 1);
531