smbus_acpi.c revision 1.9.2.3 1 /* $NetBSD: smbus_acpi.c,v 1.9.2.3 2010/08/11 22:53:16 yamt 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.9.2.3 2010/08/11 22:53:16 yamt 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