xref: /netbsd-src/sys/dev/acpi/smbus_acpi.c (revision 154bfe8e089c1a0a4e9ed8414f08d3da90949162)
1 /* $NetBSD: smbus_acpi.c,v 1.14 2019/12/22 23:23:32 thorpej 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.14 2019/12/22 23:23:32 thorpej 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 	int			sc_poll_alert;
77 };
78 
79 static int	acpi_smbus_match(device_t, cfdata_t, void *);
80 static void	acpi_smbus_attach(device_t, device_t, void *);
81 static int	acpi_smbus_detach(device_t, int);
82 static int	acpi_smbus_poll_alert(ACPI_HANDLE, int *);
83 static int	acpi_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
84 				size_t, void *, size_t, int);
85 static void	acpi_smbus_alerts(struct acpi_smbus_softc *);
86 static void	acpi_smbus_tick(void *);
87 static void	acpi_smbus_notify_handler(ACPI_HANDLE, uint32_t, void *);
88 
89 struct SMB_UDID {
90 	uint8_t		dev_cap;
91 	uint8_t		vers_rev;
92 	uint16_t	vendor;
93 	uint16_t	device;
94 	uint16_t	interface;
95 	uint16_t	subsys_vendor;
96 	uint16_t	subsys_device;
97 	uint8_t		reserved[4];
98 };
99 
100 struct SMB_DEVICE {
101 	uint8_t		slave_addr;
102 	uint8_t		reserved;
103 	struct SMB_UDID	dev_id;
104 };
105 
106 struct SMB_INFO {
107 	uint8_t		struct_ver;
108 	uint8_t		spec_ver;
109 	uint8_t		hw_cap;
110 	uint8_t		poll_int;
111 	uint8_t		dev_count;
112 	struct SMB_DEVICE device[1];
113 };
114 
115 static const char * const smbus_acpi_ids[] = {
116 	"SMBUS01",	/* SMBus CMI v1.0 */
117 	NULL
118 };
119 
120 CFATTACH_DECL_NEW(acpismbus, sizeof(struct acpi_smbus_softc),
121     acpi_smbus_match, acpi_smbus_attach, acpi_smbus_detach, NULL);
122 
123 static int
124 acpi_smbus_match(device_t parent, cfdata_t match, void *aux)
125 {
126 	struct acpi_attach_args *aa = aux;
127 
128 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
129 		return 0;
130 
131 	if (acpi_match_hid(aa->aa_node->ad_devinfo, smbus_acpi_ids) == 0)
132 		return 0;
133 
134 	return acpi_smbus_poll_alert(aa->aa_node->ad_handle, NULL);
135 }
136 
137 static void
138 acpi_smbus_attach(device_t parent, device_t self, void *aux)
139 {
140 	struct acpi_smbus_softc *sc = device_private(self);
141 	struct acpi_attach_args *aa = aux;
142 	struct i2cbus_attach_args iba;
143 
144 	aprint_naive("\n");
145 
146 	sc->sc_devnode = aa->aa_node;
147 	sc->sc_dv = self;
148 	sc->sc_poll_alert = 2;
149 
150 	/* Attach I2C bus. */
151 	iic_tag_init(&sc->sc_i2c_tag);
152 	sc->sc_i2c_tag.ic_cookie = sc;
153 	sc->sc_i2c_tag.ic_exec = acpi_smbus_exec;
154 
155 	(void)acpi_smbus_poll_alert(aa->aa_node->ad_handle,&sc->sc_poll_alert);
156 
157 	/* If failed, fall-back to polling. */
158 	if (acpi_register_notify(sc->sc_devnode,
159 		acpi_smbus_notify_handler) != true)
160 		sc->sc_poll_alert = 2;
161 
162 	callout_init(&sc->sc_callout, 0);
163 	callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self);
164 
165 	if (sc->sc_poll_alert != 0) {
166 		aprint_debug(": alert_poll %d sec", sc->sc_poll_alert);
167 		callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
168 	}
169 
170 	aprint_normal("\n");
171 
172 	(void)memset(&iba, 0, sizeof(iba));
173 	(void)pmf_device_register(self, NULL, NULL);
174 
175 	iba.iba_tag = &sc->sc_i2c_tag;
176 
177 	(void)config_found_ia(self, "i2cbus", &iba, iicbus_print);
178 }
179 
180 static int
181 acpi_smbus_detach(device_t self, int flags)
182 {
183 	struct acpi_smbus_softc *sc = device_private(self);
184 
185 	pmf_device_deregister(self);
186 	acpi_deregister_notify(sc->sc_devnode);
187 
188 	callout_halt(&sc->sc_callout, NULL);
189 	callout_destroy(&sc->sc_callout);
190 
191 	iic_tag_fini(&sc->sc_i2c_tag);
192 
193 	return 0;
194 }
195 
196 static int
197 acpi_smbus_poll_alert(ACPI_HANDLE hdl, int *alert)
198 {
199 	struct SMB_INFO *info;
200 	ACPI_BUFFER smi_buf;
201 	ACPI_OBJECT *e, *p;
202 	ACPI_STATUS rv;
203 
204 	/*
205 	 * Retrieve polling interval for SMBus Alerts.
206 	 */
207 	rv = acpi_eval_struct(hdl, "_SBI", &smi_buf);
208 
209 	if (ACPI_FAILURE(rv))
210 		return 0;
211 
212 	p = smi_buf.Pointer;
213 
214 	if (p->Type != ACPI_TYPE_PACKAGE) {
215 		rv = AE_TYPE;
216 		goto out;
217 	}
218 
219 	if (p->Package.Count == 0) {
220 		rv = AE_LIMIT;
221 		goto out;
222 	}
223 
224 	e = p->Package.Elements;
225 
226 	if (e[0].Type != ACPI_TYPE_INTEGER) {
227 		rv = AE_TYPE;
228 		goto out;
229 	}
230 
231 	/* Verify CMI version. */
232 	if (e[0].Integer.Value != 0x10) {
233 		rv = AE_SUPPORT;
234 		goto out;
235 	}
236 
237 	if (alert != NULL) {
238 
239 		if (p->Package.Count < 2)
240 			goto out;
241 
242 		if (e[1].Type != ACPI_TYPE_BUFFER)
243 			goto out;
244 
245 		info = (struct SMB_INFO *)(e[1].Buffer.Pointer);
246 		*alert = info->poll_int;
247 	}
248 
249 out:
250 	if (smi_buf.Pointer != NULL)
251 		ACPI_FREE(smi_buf.Pointer);
252 
253 	return (ACPI_FAILURE(rv)) ? 0 : 1;
254 }
255 
256 static int
257 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
258 	const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
259 {
260         struct acpi_smbus_softc *sc = cookie;
261 	const uint8_t *c = cmdbuf;
262 	uint8_t *b = buf, *xb;
263 	const char *path;
264 	ACPI_OBJECT_LIST args;
265 	ACPI_OBJECT arg[5];
266 	ACPI_OBJECT *p, *e;
267 	ACPI_BUFFER smbuf;
268 	ACPI_STATUS rv;
269 	int i, r, xlen;
270 
271 	/*
272 	 *	arg[0] : protocol
273 	 *	arg[1] : slave address
274 	 *	arg[2] : command
275 	 *	arg[3] : data length
276 	 *	arg[4] : data
277 	 */
278 	for (i = r = 0; i < __arraycount(arg); i++)
279 		arg[i].Type = ACPI_TYPE_INTEGER;
280 
281 	args.Pointer = arg;
282 
283 	smbuf.Pointer = NULL;
284 	smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
285 
286 	arg[1].Integer.Value = addr;
287 
288 	if (I2C_OP_READ_P(op)) {
289 
290 		path = "_SBR";
291 		args.Count = 3;
292 
293 		switch (len) {
294 
295 		case 0:
296 			arg[0].Integer.Value = (cmdlen != 0) ?
297 			    ACPI_SMBUS_RCV_BYTE : ACPI_SMBUS_RD_QUICK;
298 
299 			arg[2].Integer.Value = 0;
300 			break;
301 
302 		case 1:
303 			arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE;
304 			arg[2].Integer.Value = *c;
305 			break;
306 
307 		case 2:
308 			arg[0].Integer.Value = ACPI_SMBUS_RD_WORD;
309 			arg[2].Integer.Value = *c;
310 			break;
311 
312 		default:
313 			arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK;
314 			arg[2].Integer.Value = *c;
315 			break;
316 		}
317 
318 	} else {
319 
320 		path = "_SBW";
321 		args.Count = 5;
322 
323 		arg[3].Integer.Value = len;
324 
325 		switch (len) {
326 
327 		case 0:
328 			if (cmdlen == 0) {
329 				arg[2].Integer.Value = 0;
330 				arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK;
331 			} else {
332 				arg[2].Integer.Value = *c;
333 				arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE;
334 			}
335 
336 			arg[4].Integer.Value = 0;
337 			break;
338 
339 		case 1:
340 			arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE;
341 			arg[2].Integer.Value = *c;
342 			arg[4].Integer.Value = *b;
343 			break;
344 
345 		case 2:
346 			arg[0].Integer.Value = ACPI_SMBUS_WR_WORD;
347 			arg[2].Integer.Value = *c;
348 			arg[4].Integer.Value = *b++;
349 			arg[4].Integer.Value += (*b--) << 8;
350 			break;
351 
352 		default:
353 			arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK;
354 			arg[2].Integer.Value = *c;
355 			arg[4].Type = ACPI_TYPE_BUFFER;
356 			arg[4].Buffer.Pointer = buf;
357 			arg[4].Buffer.Length = (len < 32) ? len : 32;
358 			break;
359 		}
360 	}
361 
362 	rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, path, &args,&smbuf);
363 
364 	if (ACPI_FAILURE(rv))
365 		goto out;
366 
367 	p = smbuf.Pointer;
368 
369 	if (p->Type != ACPI_TYPE_PACKAGE) {
370 		rv = AE_TYPE;
371 		goto out;
372 	}
373 
374 	if (p->Package.Count < 1) {
375 		rv = AE_LIMIT;
376 		goto out;
377 	}
378 
379 	e = p->Package.Elements;
380 
381 	if (e->Type != ACPI_TYPE_INTEGER) {
382 		rv = AE_TYPE;
383 		goto out;
384 	}
385 
386 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT,
387 		"return status: %"PRIu64"\n", e[0].Integer.Value));
388 
389 	if (e[0].Integer.Value != 0) {
390 		rv = AE_BAD_VALUE;
391 		goto out;
392 	}
393 
394 	/*
395 	 * For read operations, copy data to user buffer.
396 	 */
397 	if (I2C_OP_READ_P(op)) {
398 
399 		if (p->Package.Count < 3) {
400 			rv = AE_LIMIT;
401 			goto out;
402 		}
403 
404 		if (e[1].Type != ACPI_TYPE_INTEGER) {
405 			rv = AE_TYPE;
406 			goto out;
407 		}
408 
409 		xlen = e[1].Integer.Value;
410 
411 		if (xlen > len)
412 			xlen = len;
413 
414 		switch (e[2].Type) {
415 
416 		case ACPI_TYPE_BUFFER:
417 
418 			if (xlen == 0) {
419 				rv = AE_LIMIT;
420 				goto out;
421 			}
422 
423 			xb = e[2].Buffer.Pointer;
424 
425 			if (xb == NULL) {
426 				rv = AE_NULL_OBJECT;
427 				goto out;
428 			}
429 
430 			(void)memcpy(b, xb, xlen);
431 			break;
432 
433 		case ACPI_TYPE_INTEGER:
434 
435 			if (xlen > 0)
436 				*b++ = e[2].Integer.Value & 0xff;
437 
438 			if (xlen > 1)
439 				*b = e[2].Integer.Value >> 8;
440 
441 			break;
442 
443 		default:
444 			rv = AE_TYPE;
445 			goto out;
446 		}
447 	}
448 
449 out:
450 	if (smbuf.Pointer != NULL)
451 		ACPI_FREE(smbuf.Pointer);
452 
453 	if (ACPI_SUCCESS(rv))
454 		return 0;
455 
456 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to "
457 		"evaluate %s: %s\n", path, AcpiFormatException(rv)));
458 
459 	return 1;
460 }
461 
462 /*
463  * Whether triggered by periodic polling or a Notify(),
464  * retrieve all pending SMBus device alerts.
465  */
466 static void
467 acpi_smbus_alerts(struct acpi_smbus_softc *sc)
468 {
469 	const ACPI_HANDLE hdl = sc->sc_devnode->ad_handle;
470 	ACPI_OBJECT *e, *p;
471 	ACPI_BUFFER alert;
472 	ACPI_STATUS rv;
473 	int status = 0;
474 	uint8_t addr;
475 
476 	do {
477 		rv = acpi_eval_struct(hdl, "_SBA", &alert);
478 
479 		if (ACPI_FAILURE(rv)) {
480 			status = 1;
481 			goto done;
482 		}
483 
484 		p = alert.Pointer;
485 
486 		if (p->Type == ACPI_TYPE_PACKAGE && p->Package.Count >= 2) {
487 
488 			status = 1;
489 
490 			e = p->Package.Elements;
491 
492 			if (e[0].Type == ACPI_TYPE_INTEGER)
493 				status = e[0].Integer.Value;
494 
495 			if (status == 0 && e[1].Type == ACPI_TYPE_INTEGER) {
496 				addr = e[1].Integer.Value;
497 
498 				aprint_debug_dev(sc->sc_dv,
499 				    "alert for 0x%x\n", addr);
500 
501 				(void)iic_smbus_intr(&sc->sc_i2c_tag);
502 			}
503 		}
504 done:
505 		if (alert.Pointer != NULL)
506 			ACPI_FREE(alert.Pointer);
507 
508 	} while (status == 0);
509 }
510 
511 static void
512 acpi_smbus_tick(void *opaque)
513 {
514 	device_t dv = opaque;
515 	struct acpi_smbus_softc *sc = device_private(dv);
516 
517 	acpi_smbus_alerts(sc);
518 
519 	callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
520 }
521 
522 static void
523 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
524 {
525 	device_t dv = opaque;
526 	struct acpi_smbus_softc *sc = device_private(dv);
527 
528 	aprint_debug_dev(dv, "received notify message 0x%x\n", notify);
529 
530 	acpi_smbus_alerts(sc);
531 }
532