smbus_acpi.c revision 1.8 1 /* $NetBSD: smbus_acpi.c,v 1.8 2010/03/04 20:46:18 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.8 2010/03/04 20:46:18 jruoho Exp $");
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/errno.h>
45 #include <sys/ioctl.h>
46 #include <sys/syslog.h>
47 #include <sys/device.h>
48 #include <sys/callout.h>
49 #include <sys/proc.h>
50 #include <sys/mutex.h>
51
52 #include <dev/acpi/acpica.h>
53 #include <dev/acpi/acpireg.h>
54 #include <dev/acpi/acpivar.h>
55
56 #include <dev/i2c/i2cvar.h>
57
58 #define _COMPONENT ACPI_BUS_COMPONENT
59 ACPI_MODULE_NAME ("smbus_acpi")
60
61 /*
62 * ACPI SMBus CMI protocol codes
63 */
64 #define ACPI_SMBUS_RD_QUICK 0x03
65 #define ACPI_SMBUS_RCV_BYTE 0x05
66 #define ACPI_SMBUS_RD_BYTE 0x07
67 #define ACPI_SMBUS_RD_WORD 0x09
68 #define ACPI_SMBUS_RD_BLOCK 0x0B
69 #define ACPI_SMBUS_WR_QUICK 0x02
70 #define ACPI_SMBUS_SND_BYTE 0x04
71 #define ACPI_SMBUS_WR_BYTE 0x06
72 #define ACPI_SMBUS_WR_WORD 0x08
73 #define ACPI_SMBUS_WR_BLOCK 0x0A
74 #define ACPI_SMBUS_PROCESS_CALL 0x0C
75
76 struct acpi_smbus_softc {
77 struct acpi_devnode *sc_devnode;
78 struct callout sc_callout;
79 struct i2c_controller sc_i2c_tag;
80 device_t sc_dv;
81 kmutex_t sc_i2c_mutex;
82 int sc_poll_alert;
83 };
84
85 static int acpi_smbus_match(device_t, cfdata_t, void *);
86 static void acpi_smbus_attach(device_t, device_t, void *);
87 static int acpi_smbus_detach(device_t, int);
88 static int acpi_smbus_acquire_bus(void *, int);
89 static void acpi_smbus_release_bus(void *, int);
90 static int acpi_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
91 size_t, void *, size_t, int);
92 static void acpi_smbus_alerts(struct acpi_smbus_softc *);
93 static void acpi_smbus_tick(void *);
94 static void acpi_smbus_notify_handler(ACPI_HANDLE, uint32_t, void *);
95
96 struct SMB_UDID {
97 uint8_t dev_cap;
98 uint8_t vers_rev;
99 uint16_t vendor;
100 uint16_t device;
101 uint16_t interface;
102 uint16_t subsys_vendor;
103 uint16_t subsys_device;
104 uint8_t reserved[4];
105 };
106
107 struct SMB_DEVICE {
108 uint8_t slave_addr;
109 uint8_t reserved;
110 struct SMB_UDID dev_id;
111 };
112
113 struct SMB_INFO {
114 uint8_t struct_ver;
115 uint8_t spec_ver;
116 uint8_t hw_cap;
117 uint8_t poll_int;
118 uint8_t dev_count;
119 struct SMB_DEVICE device[1];
120 };
121
122 static const char * const smbus_acpi_ids[] = {
123 "SMBUS01", /* SMBus CMI v1.0 */
124 NULL
125 };
126
127 static const char * const pcibus_acpi_ids[] = {
128 "PNP0A03",
129 "PNP0A08",
130 NULL
131 };
132
133 CFATTACH_DECL_NEW(acpismbus, sizeof(struct acpi_smbus_softc),
134 acpi_smbus_match, acpi_smbus_attach, acpi_smbus_detach, NULL);
135
136 /*
137 * acpi_smbus_match: autoconf(9) match routine
138 */
139 static int
140 acpi_smbus_match(device_t parent, cfdata_t match, void *aux)
141 {
142 struct acpi_attach_args *aa = aux;
143 int r = 0;
144 ACPI_STATUS rv;
145 ACPI_BUFFER smi_buf;
146 ACPI_OBJECT *e, *p;
147
148 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
149 return 0;
150
151 if (acpi_match_hid(aa->aa_node->ad_devinfo, smbus_acpi_ids) == 0)
152 return 0;
153
154 /* Ensure that device's CMI version is supported */
155 rv = acpi_eval_struct(aa->aa_node->ad_handle, "_SBI", &smi_buf);
156 if (ACPI_FAILURE(rv))
157 goto done;
158
159 p = smi_buf.Pointer;
160 if (p != NULL && p->Type == ACPI_TYPE_PACKAGE &&
161 p->Package.Count >= 1) {
162 e = p->Package.Elements;
163 if (e[0].Type == ACPI_TYPE_INTEGER &&
164 e[0].Integer.Value == 0x10)
165 r = 1;
166 }
167 done:
168 if (smi_buf.Pointer != NULL)
169 ACPI_FREE(smi_buf.Pointer);
170
171 return r;
172 }
173
174 /*
175 * acpitz_attach: autoconf(9) attach routine
176 */
177 static void
178 acpi_smbus_attach(device_t parent, device_t self, void *aux)
179 {
180 struct acpi_smbus_softc *sc = device_private(self);
181 struct acpi_attach_args *aa = aux;
182 struct i2cbus_attach_args iba;
183 ACPI_STATUS rv;
184 ACPI_HANDLE native_dev, native_bus;
185 ACPI_DEVICE_INFO *native_dev_info, *native_bus_info;
186 ACPI_BUFFER smi_buf;
187 ACPI_OBJECT *e, *p;
188 struct SMB_INFO *info;
189 int pci_bus, pci_dev, pci_func;
190
191 aprint_naive("\n");
192
193 sc->sc_devnode = aa->aa_node;
194 sc->sc_dv = self;
195 sc->sc_poll_alert = 2;
196
197 /* Attach I2C bus */
198 mutex_init(&sc->sc_i2c_mutex, MUTEX_DEFAULT, IPL_NONE);
199 sc->sc_i2c_tag.ic_cookie = sc;
200 sc->sc_i2c_tag.ic_acquire_bus = acpi_smbus_acquire_bus;
201 sc->sc_i2c_tag.ic_release_bus = acpi_smbus_release_bus;
202 sc->sc_i2c_tag.ic_exec = acpi_smbus_exec;
203
204 /* Retrieve polling interval for SMBus Alerts */
205 rv = acpi_eval_struct(aa->aa_node->ad_handle, "_SBI", &smi_buf);
206 if (ACPI_SUCCESS(rv)) {
207 p = smi_buf.Pointer;
208 if (p != NULL && p->Type == ACPI_TYPE_PACKAGE &&
209 p->Package.Count >= 2) {
210 e = p->Package.Elements;
211 if (e[1].Type == ACPI_TYPE_BUFFER) {
212 info = (struct SMB_INFO *)(e[1].Buffer.Pointer);
213 sc->sc_poll_alert = info->poll_int;
214 }
215 }
216 }
217 if (smi_buf.Pointer != NULL)
218 ACPI_FREE(smi_buf.Pointer);
219
220 /* Install notify handler if possible */
221 rv = AcpiInstallNotifyHandler(sc->sc_devnode->ad_handle,
222 ACPI_DEVICE_NOTIFY, acpi_smbus_notify_handler, self);
223 if (ACPI_FAILURE(rv)) {
224 aprint_error(": unable to install DEVICE NOTIFY handler: %s\n",
225 AcpiFormatException(rv));
226 sc->sc_poll_alert = 2; /* If failed, fall-back to polling */
227 }
228
229 callout_init(&sc->sc_callout, 0);
230 callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self);
231
232 if (!pmf_device_register(self, NULL, NULL))
233 aprint_error(": couldn't establish power handler\n");
234
235 if (sc->sc_poll_alert != 0) {
236 aprint_debug(" alert_poll %d sec", sc->sc_poll_alert);
237 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
238 }
239 aprint_normal("\n");
240
241 /*
242 * Retrieve and display native controller info
243 */
244 rv = AcpiGetParent(sc->sc_devnode->ad_handle, &native_dev);
245
246 native_bus_info = native_dev_info = NULL;
247
248 if (ACPI_SUCCESS(rv))
249 rv = AcpiGetParent(native_dev, &native_bus);
250
251 if (ACPI_SUCCESS(rv))
252 rv = AcpiGetObjectInfo(native_bus, &native_bus_info);
253
254 if (ACPI_SUCCESS(rv) &&
255 acpi_match_hid(native_bus_info, pcibus_acpi_ids) != 0) {
256
257 rv = AcpiGetObjectInfo(native_dev, &native_dev_info);
258
259 if (ACPI_SUCCESS(rv)) {
260 pci_bus = native_bus_info->Address;
261 pci_dev = ACPI_ADR_PCI_DEV(native_dev_info->Address);
262 pci_func = ACPI_ADR_PCI_FUNC(native_dev_info->Address);
263 aprint_debug_dev(self, "Native i2c host controller"
264 " is on PCI bus %d dev %d func %d\n",
265 pci_bus, pci_dev, pci_func);
266 /*
267 * XXX We really need a mechanism to prevent the
268 * XXX native controller from attaching
269 */
270 }
271 }
272
273 if (native_bus_info != NULL)
274 ACPI_FREE(native_bus_info);
275
276 if (native_dev_info != NULL)
277 ACPI_FREE(native_dev_info);
278
279 memset(&iba, 0, sizeof(iba));
280 iba.iba_tag = &sc->sc_i2c_tag;
281 config_found_ia(self, "i2cbus", &iba, iicbus_print);
282 }
283
284 static int
285 acpi_smbus_detach(device_t self, int flags)
286 {
287 struct acpi_smbus_softc *sc = device_private(self);
288 ACPI_STATUS rv;
289
290 rv = AcpiRemoveNotifyHandler(sc->sc_devnode->ad_handle,
291 ACPI_DEVICE_NOTIFY, acpi_smbus_notify_handler);
292
293 if (ACPI_FAILURE(rv))
294 return EBUSY;
295
296 pmf_device_deregister(self);
297
298 callout_halt(&sc->sc_callout, NULL);
299 callout_destroy(&sc->sc_callout);
300
301 mutex_destroy(&sc->sc_i2c_mutex);
302
303 return 0;
304 }
305
306 static int
307 acpi_smbus_acquire_bus(void *cookie, int flags)
308 {
309 struct acpi_smbus_softc *sc = cookie;
310
311 mutex_enter(&sc->sc_i2c_mutex);
312 return 0;
313 }
314
315 static void
316 acpi_smbus_release_bus(void *cookie, int flags)
317 {
318 struct acpi_smbus_softc *sc = cookie;
319
320 mutex_exit(&sc->sc_i2c_mutex);
321 }
322 static int
323 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
324 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
325 {
326 struct acpi_smbus_softc *sc = cookie;
327 const uint8_t *c = cmdbuf;
328 uint8_t *b = buf, *xb;
329 int xlen;
330 int r = 0;
331 ACPI_BUFFER smbuf;
332 ACPI_STATUS rv;
333 ACPI_OBJECT_LIST args;
334 ACPI_OBJECT arg[5];
335 ACPI_OBJECT *p, *e;
336
337 smbuf.Pointer = NULL;
338 smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
339 args.Pointer = arg;
340 arg[0].Type = ACPI_TYPE_INTEGER; /* Protocol */
341 arg[1].Type = ACPI_TYPE_INTEGER; /* Slave Addr */
342 arg[1].Integer.Value = addr;
343 arg[2].Type = ACPI_TYPE_INTEGER; /* Command */
344 if (I2C_OP_READ_P(op)) {
345 args.Count = 3;
346 if (len == 0) {
347 arg[2].Integer.Value = 0;
348 if (cmdlen == 0)
349 arg[0].Integer.Value = ACPI_SMBUS_RD_QUICK;
350 else
351 arg[0].Integer.Value = ACPI_SMBUS_RCV_BYTE;
352 } else
353 arg[2].Integer.Value = *c;
354 if (len == 1)
355 arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE;
356 else if (len == 2)
357 arg[0].Integer.Value = ACPI_SMBUS_RD_WORD;
358 else if (len > 2)
359 arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK;
360 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, "_SBR",
361 &args, &smbuf);
362 } else {
363 args.Count = 5;
364 arg[3].Type = ACPI_TYPE_INTEGER; /* Data Len */
365 arg[3].Integer.Value = len;
366 arg[4].Type = ACPI_TYPE_INTEGER; /* Data */
367 if (len == 0) {
368 arg[4].Integer.Value = 0;
369 if (cmdlen == 0) {
370 arg[2].Integer.Value = 0;
371 arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK;
372 } else {
373 arg[2].Integer.Value = *c;
374 arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE;
375 }
376 } else
377 arg[2].Integer.Value = *c;
378 if (len == 1) {
379 arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE;
380 arg[4].Integer.Value = *b;
381 } else if (len == 2) {
382 arg[0].Integer.Value = ACPI_SMBUS_WR_WORD;
383 arg[4].Integer.Value = *b++;
384 arg[4].Integer.Value += (*b--) << 8;
385 } else if (len > 2) {
386 arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK;
387 arg[4].Type = ACPI_TYPE_BUFFER;
388 arg[4].Buffer.Pointer = buf;
389 arg[4].Buffer.Length = (len < 32?len:32);
390 }
391 rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, "_SBW",
392 &args, &smbuf);
393 }
394 if (ACPI_FAILURE(rv))
395 r = 1;
396 else {
397 p = smbuf.Pointer;
398 if (p == NULL || p->Type != ACPI_TYPE_PACKAGE ||
399 p->Package.Count < 1)
400 r = 1;
401 else {
402 e = p->Package.Elements;
403 if (e->Type == ACPI_TYPE_INTEGER)
404 r = e[0].Integer.Value;
405 else
406 r = 1;
407 }
408 if (r != 0)
409 r = 1;
410
411 /* For read operations, copy data to user buffer */
412 if (r == 0 && I2C_OP_READ_P(op)) {
413 if (p->Package.Count >= 3 &&
414 e[1].Type == ACPI_TYPE_INTEGER) {
415 xlen = e[1].Integer.Value;
416 if (xlen > len)
417 xlen = len;
418 if (xlen != 0 &&
419 e[2].Type == ACPI_TYPE_BUFFER) {
420 xb = e[2].Buffer.Pointer;
421 if (xb != NULL)
422 memcpy(b, xb, xlen);
423 else
424 r = 1;
425 } else if (e[2].Type == ACPI_TYPE_INTEGER) {
426 if (xlen > 0)
427 *b++ = e[2].Integer.Value &
428 0xff;
429 if (xlen > 1)
430 *b = (e[2].Integer.Value >> 8);
431 } else
432 r = 1;
433 } else
434 r = 1;
435 }
436 }
437 if (smbuf.Pointer)
438 ACPI_FREE(smbuf.Pointer);
439
440 return r;
441 }
442
443 /*
444 * acpi_smbus_alerts
445 *
446 * Whether triggered by periodic polling or an AcpiNotify, retrieve
447 * all pending SMBus device alerts
448 */
449 static void
450 acpi_smbus_alerts(struct acpi_smbus_softc *sc)
451 {
452 int status = 0;
453 uint8_t slave_addr;
454 ACPI_STATUS rv;
455 ACPI_BUFFER alert;
456 ACPI_OBJECT *e, *p;
457
458 do {
459 rv = acpi_eval_struct(sc->sc_devnode->ad_handle, "_SBA",
460 &alert);
461 if (ACPI_FAILURE(rv)) {
462 status = 1;
463 goto done;
464 }
465
466 p = alert.Pointer;
467 if (p != NULL && p->Type == ACPI_TYPE_PACKAGE &&
468 p->Package.Count >= 2) {
469 e = p->Package.Elements;
470 if (e[0].Type == ACPI_TYPE_INTEGER)
471 status = e[0].Integer.Value;
472 else
473 status = 1;
474 if (status == 0x0 && e[1].Type == ACPI_TYPE_INTEGER) {
475 slave_addr = e[1].Integer.Value;
476 aprint_debug_dev(sc->sc_dv, "Alert for 0x%x\n",
477 slave_addr);
478 (void)iic_smbus_intr(&sc->sc_i2c_tag);
479 }
480 }
481 done:
482 if (alert.Pointer != NULL)
483 ACPI_FREE(alert.Pointer);
484 } while (status == 0);
485 }
486
487 static void
488 acpi_smbus_tick(void *opaque)
489 {
490 device_t dv = opaque;
491 struct acpi_smbus_softc *sc = device_private(dv);
492
493 acpi_smbus_alerts(sc);
494
495 callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
496 }
497
498 static void
499 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
500 {
501 device_t dv = opaque;
502 struct acpi_smbus_softc *sc = device_private(dv);
503
504 aprint_debug_dev(dv, "received notify message 0x%x\n", notify);
505 acpi_smbus_alerts(sc);
506 }
507