xref: /netbsd/sys/dev/acpi/smbus_acpi.c (revision 6550d01e)
1 /* $NetBSD: smbus_acpi.c,v 1.13 2010/07/29 11:03:09 jruoho Exp $ */
2 
3 /*-
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Paul Goyette
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * ACPI SMBus Controller driver
34  *
35  * See http://smbus.org/specs/smbus_cmi10.pdf for specifications
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: smbus_acpi.c,v 1.13 2010/07/29 11:03:09 jruoho Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/device.h>
43 #include <sys/callout.h>
44 #include <sys/kernel.h>
45 #include <sys/mutex.h>
46 #include <sys/systm.h>
47 
48 #include <dev/acpi/acpireg.h>
49 #include <dev/acpi/acpivar.h>
50 
51 #include <dev/i2c/i2cvar.h>
52 
53 #define _COMPONENT		ACPI_BUS_COMPONENT
54 ACPI_MODULE_NAME		("smbus_acpi")
55 
56 /*
57  * ACPI SMBus CMI protocol codes.
58  */
59 #define	ACPI_SMBUS_RD_QUICK	0x03
60 #define	ACPI_SMBUS_RCV_BYTE	0x05
61 #define	ACPI_SMBUS_RD_BYTE	0x07
62 #define	ACPI_SMBUS_RD_WORD	0x09
63 #define	ACPI_SMBUS_RD_BLOCK	0x0B
64 #define	ACPI_SMBUS_WR_QUICK	0x02
65 #define	ACPI_SMBUS_SND_BYTE	0x04
66 #define	ACPI_SMBUS_WR_BYTE	0x06
67 #define	ACPI_SMBUS_WR_WORD	0x08
68 #define	ACPI_SMBUS_WR_BLOCK	0x0A
69 #define	ACPI_SMBUS_PROCESS_CALL	0x0C
70 
71 struct acpi_smbus_softc {
72 	struct acpi_devnode 	*sc_devnode;
73 	struct callout		sc_callout;
74 	struct i2c_controller	sc_i2c_tag;
75 	device_t		sc_dv;
76 	kmutex_t		sc_i2c_mutex;
77 	int			sc_poll_alert;
78 };
79 
80 static int	acpi_smbus_match(device_t, cfdata_t, void *);
81 static void	acpi_smbus_attach(device_t, device_t, void *);
82 static int	acpi_smbus_detach(device_t, int);
83 static int	acpi_smbus_poll_alert(ACPI_HANDLE, int *);
84 static int	acpi_smbus_acquire_bus(void *, int);
85 static void	acpi_smbus_release_bus(void *, int);
86 static int	acpi_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
87 				size_t, void *, size_t, int);
88 static void	acpi_smbus_alerts(struct acpi_smbus_softc *);
89 static void	acpi_smbus_tick(void *);
90 static void	acpi_smbus_notify_handler(ACPI_HANDLE, uint32_t, void *);
91 
92 struct SMB_UDID {
93 	uint8_t		dev_cap;
94 	uint8_t		vers_rev;
95 	uint16_t	vendor;
96 	uint16_t	device;
97 	uint16_t	interface;
98 	uint16_t	subsys_vendor;
99 	uint16_t	subsys_device;
100 	uint8_t		reserved[4];
101 };
102 
103 struct SMB_DEVICE {
104 	uint8_t		slave_addr;
105 	uint8_t		reserved;
106 	struct SMB_UDID	dev_id;
107 };
108 
109 struct SMB_INFO {
110 	uint8_t		struct_ver;
111 	uint8_t		spec_ver;
112 	uint8_t		hw_cap;
113 	uint8_t		poll_int;
114 	uint8_t		dev_count;
115 	struct SMB_DEVICE device[1];
116 };
117 
118 static const char * const smbus_acpi_ids[] = {
119 	"SMBUS01",	/* SMBus CMI v1.0 */
120 	NULL
121 };
122 
123 CFATTACH_DECL_NEW(acpismbus, sizeof(struct acpi_smbus_softc),
124     acpi_smbus_match, acpi_smbus_attach, acpi_smbus_detach, NULL);
125 
126 static int
127 acpi_smbus_match(device_t parent, cfdata_t match, void *aux)
128 {
129 	struct acpi_attach_args *aa = aux;
130 
131 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
132 		return 0;
133 
134 	if (acpi_match_hid(aa->aa_node->ad_devinfo, smbus_acpi_ids) == 0)
135 		return 0;
136 
137 	return acpi_smbus_poll_alert(aa->aa_node->ad_handle, NULL);
138 }
139 
140 static void
141 acpi_smbus_attach(device_t parent, device_t self, void *aux)
142 {
143 	struct acpi_smbus_softc *sc = device_private(self);
144 	struct acpi_attach_args *aa = aux;
145 	struct i2cbus_attach_args iba;
146 
147 	aprint_naive("\n");
148 
149 	sc->sc_devnode = aa->aa_node;
150 	sc->sc_dv = self;
151 	sc->sc_poll_alert = 2;
152 
153 	/* Attach I2C bus. */
154 	mutex_init(&sc->sc_i2c_mutex, MUTEX_DEFAULT, IPL_NONE);
155 
156 	sc->sc_i2c_tag.ic_cookie = sc;
157 	sc->sc_i2c_tag.ic_acquire_bus = acpi_smbus_acquire_bus;
158 	sc->sc_i2c_tag.ic_release_bus = acpi_smbus_release_bus;
159 	sc->sc_i2c_tag.ic_exec = acpi_smbus_exec;
160 
161 	(void)acpi_smbus_poll_alert(aa->aa_node->ad_handle,&sc->sc_poll_alert);
162 
163 	/* If failed, fall-back to polling. */
164 	if (acpi_register_notify(sc->sc_devnode,
165 		acpi_smbus_notify_handler) != true)
166 		sc->sc_poll_alert = 2;
167 
168 	callout_init(&sc->sc_callout, 0);
169 	callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self);
170 
171 	if (sc->sc_poll_alert != 0) {
172 		aprint_debug(": alert_poll %d sec", sc->sc_poll_alert);
173 		callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
174 	}
175 
176 	aprint_normal("\n");
177 
178 	(void)memset(&iba, 0, sizeof(iba));
179 	(void)pmf_device_register(self, NULL, NULL);
180 
181 	iba.iba_tag = &sc->sc_i2c_tag;
182 
183 	(void)config_found_ia(self, "i2cbus", &iba, iicbus_print);
184 }
185 
186 static int
187 acpi_smbus_detach(device_t self, int flags)
188 {
189 	struct acpi_smbus_softc *sc = device_private(self);
190 
191 	pmf_device_deregister(self);
192 	acpi_deregister_notify(sc->sc_devnode);
193 
194 	callout_halt(&sc->sc_callout, NULL);
195 	callout_destroy(&sc->sc_callout);
196 
197 	mutex_destroy(&sc->sc_i2c_mutex);
198 
199 	return 0;
200 }
201 
202 static int
203 acpi_smbus_poll_alert(ACPI_HANDLE hdl, int *alert)
204 {
205 	struct SMB_INFO *info;
206 	ACPI_BUFFER smi_buf;
207 	ACPI_OBJECT *e, *p;
208 	ACPI_STATUS rv;
209 
210 	/*
211 	 * Retrieve polling interval for SMBus Alerts.
212 	 */
213 	rv = acpi_eval_struct(hdl, "_SBI", &smi_buf);
214 
215 	if (ACPI_FAILURE(rv))
216 		return 0;
217 
218 	p = smi_buf.Pointer;
219 
220 	if (p->Type != ACPI_TYPE_PACKAGE) {
221 		rv = AE_TYPE;
222 		goto out;
223 	}
224 
225 	if (p->Package.Count == 0) {
226 		rv = AE_LIMIT;
227 		goto out;
228 	}
229 
230 	e = p->Package.Elements;
231 
232 	if (e[0].Type != ACPI_TYPE_INTEGER) {
233 		rv = AE_TYPE;
234 		goto out;
235 	}
236 
237 	/* Verify CMI version. */
238 	if (e[0].Integer.Value != 0x10) {
239 		rv = AE_SUPPORT;
240 		goto out;
241 	}
242 
243 	if (alert != NULL) {
244 
245 		if (p->Package.Count < 2)
246 			goto out;
247 
248 		if (e[1].Type != ACPI_TYPE_BUFFER)
249 			goto out;
250 
251 		info = (struct SMB_INFO *)(e[1].Buffer.Pointer);
252 		*alert = info->poll_int;
253 	}
254 
255 out:
256 	if (smi_buf.Pointer != NULL)
257 		ACPI_FREE(smi_buf.Pointer);
258 
259 	return (ACPI_FAILURE(rv)) ? 0 : 1;
260 }
261 
262 static int
263 acpi_smbus_acquire_bus(void *cookie, int flags)
264 {
265         struct acpi_smbus_softc *sc = cookie;
266 
267         mutex_enter(&sc->sc_i2c_mutex);
268 
269         return 0;
270 }
271 
272 static void
273 acpi_smbus_release_bus(void *cookie, int flags)
274 {
275         struct acpi_smbus_softc *sc = cookie;
276 
277         mutex_exit(&sc->sc_i2c_mutex);
278 }
279 static int
280 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
281 	const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
282 {
283         struct acpi_smbus_softc *sc = cookie;
284 	const uint8_t *c = cmdbuf;
285 	uint8_t *b = buf, *xb;
286 	const char *path;
287 	ACPI_OBJECT_LIST args;
288 	ACPI_OBJECT arg[5];
289 	ACPI_OBJECT *p, *e;
290 	ACPI_BUFFER smbuf;
291 	ACPI_STATUS rv;
292 	int i, r, xlen;
293 
294 	/*
295 	 *	arg[0] : protocol
296 	 *	arg[1] : slave address
297 	 *	arg[2] : command
298 	 *	arg[3] : data length
299 	 *	arg[4] : data
300 	 */
301 	for (i = r = 0; i < __arraycount(arg); i++)
302 		arg[i].Type = ACPI_TYPE_INTEGER;
303 
304 	args.Pointer = arg;
305 
306 	smbuf.Pointer = NULL;
307 	smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
308 
309 	arg[1].Integer.Value = addr;
310 
311 	if (I2C_OP_READ_P(op)) {
312 
313 		path = "_SBR";
314 		args.Count = 3;
315 
316 		switch (len) {
317 
318 		case 0:
319 			arg[0].Integer.Value = (cmdlen != 0) ?
320 			    ACPI_SMBUS_RCV_BYTE : ACPI_SMBUS_RD_QUICK;
321 
322 			arg[2].Integer.Value = 0;
323 			break;
324 
325 		case 1:
326 			arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE;
327 			arg[2].Integer.Value = *c;
328 			break;
329 
330 		case 2:
331 			arg[0].Integer.Value = ACPI_SMBUS_RD_WORD;
332 			arg[2].Integer.Value = *c;
333 			break;
334 
335 		default:
336 			arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK;
337 			arg[2].Integer.Value = *c;
338 			break;
339 		}
340 
341 	} else {
342 
343 		path = "_SBW";
344 		args.Count = 5;
345 
346 		arg[3].Integer.Value = len;
347 
348 		switch (len) {
349 
350 		case 0:
351 			if (cmdlen == 0) {
352 				arg[2].Integer.Value = 0;
353 				arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK;
354 			} else {
355 				arg[2].Integer.Value = *c;
356 				arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE;
357 			}
358 
359 			arg[4].Integer.Value = 0;
360 			break;
361 
362 		case 1:
363 			arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE;
364 			arg[2].Integer.Value = *c;
365 			arg[4].Integer.Value = *b;
366 			break;
367 
368 		case 2:
369 			arg[0].Integer.Value = ACPI_SMBUS_WR_WORD;
370 			arg[2].Integer.Value = *c;
371 			arg[4].Integer.Value = *b++;
372 			arg[4].Integer.Value += (*b--) << 8;
373 			break;
374 
375 		default:
376 			arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK;
377 			arg[2].Integer.Value = *c;
378 			arg[4].Type = ACPI_TYPE_BUFFER;
379 			arg[4].Buffer.Pointer = buf;
380 			arg[4].Buffer.Length = (len < 32) ? len : 32;
381 			break;
382 		}
383 	}
384 
385 	rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, path, &args,&smbuf);
386 
387 	if (ACPI_FAILURE(rv))
388 		goto out;
389 
390 	p = smbuf.Pointer;
391 
392 	if (p->Type != ACPI_TYPE_PACKAGE) {
393 		rv = AE_TYPE;
394 		goto out;
395 	}
396 
397 	if (p->Package.Count < 1) {
398 		rv = AE_LIMIT;
399 		goto out;
400 	}
401 
402 	e = p->Package.Elements;
403 
404 	if (e->Type != ACPI_TYPE_INTEGER) {
405 		rv = AE_TYPE;
406 		goto out;
407 	}
408 
409 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT,
410 		"return status: %"PRIu64"\n", e[0].Integer.Value));
411 
412 	if (e[0].Integer.Value != 0) {
413 		rv = AE_BAD_VALUE;
414 		goto out;
415 	}
416 
417 	/*
418 	 * For read operations, copy data to user buffer.
419 	 */
420 	if (I2C_OP_READ_P(op)) {
421 
422 		if (p->Package.Count < 3) {
423 			rv = AE_LIMIT;
424 			goto out;
425 		}
426 
427 		if (e[1].Type != ACPI_TYPE_INTEGER) {
428 			rv = AE_TYPE;
429 			goto out;
430 		}
431 
432 		xlen = e[1].Integer.Value;
433 
434 		if (xlen > len)
435 			xlen = len;
436 
437 		switch (e[2].Type) {
438 
439 		case ACPI_TYPE_BUFFER:
440 
441 			if (xlen == 0) {
442 				rv = AE_LIMIT;
443 				goto out;
444 			}
445 
446 			xb = e[2].Buffer.Pointer;
447 
448 			if (xb == NULL) {
449 				rv = AE_NULL_OBJECT;
450 				goto out;
451 			}
452 
453 			(void)memcpy(b, xb, xlen);
454 			break;
455 
456 		case ACPI_TYPE_INTEGER:
457 
458 			if (xlen > 0)
459 				*b++ = e[2].Integer.Value & 0xff;
460 
461 			if (xlen > 1)
462 				*b = e[2].Integer.Value >> 8;
463 
464 			break;
465 
466 		default:
467 			rv = AE_TYPE;
468 			goto out;
469 		}
470 	}
471 
472 out:
473 	if (smbuf.Pointer != NULL)
474 		ACPI_FREE(smbuf.Pointer);
475 
476 	if (ACPI_SUCCESS(rv))
477 		return 0;
478 
479 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to "
480 		"evaluate %s: %s\n", path, AcpiFormatException(rv)));
481 
482 	return 1;
483 }
484 
485 /*
486  * Whether triggered by periodic polling or a Notify(),
487  * retrieve all pending SMBus device alerts.
488  */
489 static void
490 acpi_smbus_alerts(struct acpi_smbus_softc *sc)
491 {
492 	const ACPI_HANDLE hdl = sc->sc_devnode->ad_handle;
493 	ACPI_OBJECT *e, *p;
494 	ACPI_BUFFER alert;
495 	ACPI_STATUS rv;
496 	int status = 0;
497 	uint8_t addr;
498 
499 	do {
500 		rv = acpi_eval_struct(hdl, "_SBA", &alert);
501 
502 		if (ACPI_FAILURE(rv)) {
503 			status = 1;
504 			goto done;
505 		}
506 
507 		p = alert.Pointer;
508 
509 		if (p->Type == ACPI_TYPE_PACKAGE && p->Package.Count >= 2) {
510 
511 			status = 1;
512 
513 			e = p->Package.Elements;
514 
515 			if (e[0].Type == ACPI_TYPE_INTEGER)
516 				status = e[0].Integer.Value;
517 
518 			if (status == 0 && e[1].Type == ACPI_TYPE_INTEGER) {
519 				addr = e[1].Integer.Value;
520 
521 				aprint_debug_dev(sc->sc_dv,
522 				    "alert for 0x%x\n", addr);
523 
524 				(void)iic_smbus_intr(&sc->sc_i2c_tag);
525 			}
526 		}
527 done:
528 		if (alert.Pointer != NULL)
529 			ACPI_FREE(alert.Pointer);
530 
531 	} while (status == 0);
532 }
533 
534 static void
535 acpi_smbus_tick(void *opaque)
536 {
537 	device_t dv = opaque;
538 	struct acpi_smbus_softc *sc = device_private(dv);
539 
540 	acpi_smbus_alerts(sc);
541 
542 	callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
543 }
544 
545 static void
546 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
547 {
548 	device_t dv = opaque;
549 	struct acpi_smbus_softc *sc = device_private(dv);
550 
551 	aprint_debug_dev(dv, "received notify message 0x%x\n", notify);
552 
553 	acpi_smbus_alerts(sc);
554 }
555