Line data Source code
1 : /* $OpenBSD: dwiic_acpi.c,v 1.8 2018/07/01 11:37:11 kettenis Exp $ */
2 : /*
3 : * Synopsys DesignWare I2C controller
4 : *
5 : * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org>
6 : *
7 : * Permission to use, copy, modify, and/or distribute this software for any
8 : * purpose with or without fee is hereby granted, provided that the above
9 : * copyright notice and this permission notice appear in all copies.
10 : *
11 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 : */
19 :
20 : #include <sys/param.h>
21 : #include <sys/systm.h>
22 : #include <sys/kernel.h>
23 : #include <sys/kthread.h>
24 :
25 : #include <dev/acpi/acpireg.h>
26 : #include <dev/acpi/acpivar.h>
27 : #include <dev/acpi/acpidev.h>
28 : #include <dev/acpi/amltypes.h>
29 : #include <dev/acpi/dsdt.h>
30 :
31 : #include <dev/ic/dwiicvar.h>
32 :
33 : struct dwiic_crs {
34 : int irq_int;
35 : uint8_t irq_flags;
36 : uint32_t addr_min;
37 : uint32_t addr_bas;
38 : uint32_t addr_len;
39 : uint16_t i2c_addr;
40 : struct aml_node *devnode;
41 : struct aml_node *gpio_int_node;
42 : uint16_t gpio_int_pin;
43 : uint16_t gpio_int_flags;
44 : };
45 :
46 : int dwiic_acpi_match(struct device *, void *, void *);
47 : void dwiic_acpi_attach(struct device *, struct device *, void *);
48 :
49 : int dwiic_acpi_parse_crs(int, union acpi_resource *, void *);
50 : int dwiic_acpi_found_ihidev(struct dwiic_softc *,
51 : struct aml_node *, char *, struct dwiic_crs);
52 : int dwiic_acpi_found_iatp(struct dwiic_softc *, struct aml_node *,
53 : char *, struct dwiic_crs);
54 : void dwiic_acpi_get_params(struct dwiic_softc *, char *, uint16_t *,
55 : uint16_t *, uint32_t *);
56 : void dwiic_acpi_power(struct dwiic_softc *, int);
57 : void dwiic_acpi_bus_scan(struct device *,
58 : struct i2cbus_attach_args *, void *);
59 :
60 : struct cfattach dwiic_acpi_ca = {
61 : sizeof(struct dwiic_softc),
62 : dwiic_acpi_match,
63 : dwiic_acpi_attach,
64 : NULL,
65 : dwiic_activate
66 : };
67 :
68 : const char *dwiic_hids[] = {
69 : "INT33C2",
70 : "INT33C3",
71 : "INT3432",
72 : "INT3433",
73 : "80860F41",
74 : "808622C1",
75 : NULL
76 : };
77 :
78 : const char *ihidev_hids[] = {
79 : "PNP0C50",
80 : "ACPI0C50",
81 : NULL
82 : };
83 :
84 : const char *iatp_hids[] = {
85 : "ATML0000",
86 : "ATML0001",
87 : NULL
88 : };
89 :
90 : int
91 0 : dwiic_acpi_match(struct device *parent, void *match, void *aux)
92 : {
93 0 : struct acpi_attach_args *aaa = aux;
94 0 : struct cfdata *cf = match;
95 :
96 0 : return acpi_matchhids(aaa, dwiic_hids, cf->cf_driver->cd_name);
97 : }
98 :
99 : void
100 0 : dwiic_acpi_attach(struct device *parent, struct device *self, void *aux)
101 : {
102 0 : struct dwiic_softc *sc = (struct dwiic_softc *)self;
103 0 : struct acpi_attach_args *aa = aux;
104 0 : struct aml_value res;
105 0 : struct dwiic_crs crs;
106 :
107 0 : sc->sc_acpi = (struct acpi_softc *)parent;
108 0 : sc->sc_devnode = aa->aaa_node;
109 0 : memcpy(&sc->sc_hid, aa->aaa_dev, sizeof(sc->sc_hid));
110 :
111 0 : printf(" %s", sc->sc_devnode->name);
112 :
113 0 : if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) {
114 0 : printf(", no _CRS method\n");
115 0 : return;
116 : }
117 0 : if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
118 0 : printf(", invalid _CRS object (type %d len %d)\n",
119 0 : res.type, res.length);
120 0 : aml_freevalue(&res);
121 0 : return;
122 : }
123 0 : memset(&crs, 0, sizeof(crs));
124 0 : crs.devnode = sc->sc_devnode;
125 0 : aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs);
126 0 : aml_freevalue(&res);
127 :
128 0 : if (crs.addr_bas == 0) {
129 0 : printf(", can't find address\n");
130 0 : return;
131 : }
132 :
133 0 : printf(" addr 0x%x/0x%x", crs.addr_bas, crs.addr_len);
134 :
135 0 : sc->sc_iot = aa->aaa_memt;
136 0 : if (bus_space_map(sc->sc_iot, crs.addr_bas, crs.addr_len, 0,
137 0 : &sc->sc_ioh)) {
138 0 : printf(", failed mapping at 0x%x\n", crs.addr_bas);
139 0 : return;
140 : }
141 :
142 : /* power up the controller */
143 0 : dwiic_acpi_power(sc, 1);
144 :
145 : /* fetch timing parameters */
146 0 : sc->ss_hcnt = dwiic_read(sc, DW_IC_SS_SCL_HCNT);
147 0 : sc->ss_lcnt = dwiic_read(sc, DW_IC_SS_SCL_LCNT);
148 0 : sc->fs_hcnt = dwiic_read(sc, DW_IC_FS_SCL_HCNT);
149 0 : sc->fs_lcnt = dwiic_read(sc, DW_IC_FS_SCL_LCNT);
150 0 : sc->sda_hold_time = dwiic_read(sc, DW_IC_SDA_HOLD);
151 0 : dwiic_acpi_get_params(sc, "SSCN", &sc->ss_hcnt, &sc->ss_lcnt, NULL);
152 0 : dwiic_acpi_get_params(sc, "FMCN", &sc->fs_hcnt, &sc->fs_lcnt,
153 : &sc->sda_hold_time);
154 :
155 0 : if (dwiic_init(sc)) {
156 0 : printf(", failed initializing\n");
157 0 : bus_space_unmap(sc->sc_iot, sc->sc_ioh, crs.addr_len);
158 0 : return;
159 : }
160 :
161 : /* leave the controller disabled */
162 0 : dwiic_write(sc, DW_IC_INTR_MASK, 0);
163 0 : dwiic_enable(sc, 0);
164 0 : dwiic_read(sc, DW_IC_CLR_INTR);
165 :
166 : /* try to register interrupt with apic, but not fatal without it */
167 0 : if (crs.irq_int > 0) {
168 0 : printf(" irq %d", crs.irq_int);
169 :
170 0 : sc->sc_ih = acpi_intr_establish(crs.irq_int, crs.irq_flags,
171 0 : IPL_BIO, dwiic_intr, sc, sc->sc_dev.dv_xname);
172 0 : if (sc->sc_ih == NULL)
173 0 : printf(", can't establish interrupt");
174 : }
175 :
176 0 : printf("\n");
177 :
178 0 : rw_init(&sc->sc_i2c_lock, "iiclk");
179 :
180 : /* setup and attach iic bus */
181 0 : sc->sc_i2c_tag.ic_cookie = sc;
182 0 : sc->sc_i2c_tag.ic_acquire_bus = dwiic_i2c_acquire_bus;
183 0 : sc->sc_i2c_tag.ic_release_bus = dwiic_i2c_release_bus;
184 0 : sc->sc_i2c_tag.ic_exec = dwiic_i2c_exec;
185 0 : sc->sc_i2c_tag.ic_intr_establish = dwiic_i2c_intr_establish;
186 0 : sc->sc_i2c_tag.ic_intr_string = dwiic_i2c_intr_string;
187 :
188 0 : bzero(&sc->sc_iba, sizeof(sc->sc_iba));
189 0 : sc->sc_iba.iba_name = "iic";
190 0 : sc->sc_iba.iba_tag = &sc->sc_i2c_tag;
191 0 : sc->sc_iba.iba_bus_scan = dwiic_acpi_bus_scan;
192 0 : sc->sc_iba.iba_bus_scan_arg = sc;
193 :
194 0 : config_found((struct device *)sc, &sc->sc_iba, iicbus_print);
195 :
196 : #ifndef SMALL_KERNEL
197 0 : sc->sc_devnode->i2c = &sc->sc_i2c_tag;
198 0 : acpi_register_gsb(sc->sc_acpi, sc->sc_devnode);
199 : #endif
200 :
201 0 : return;
202 0 : }
203 :
204 : int
205 0 : dwiic_acpi_parse_crs(int crsidx, union acpi_resource *crs, void *arg)
206 : {
207 0 : struct dwiic_crs *sc_crs = arg;
208 : struct aml_node *node;
209 : uint16_t pin;
210 :
211 0 : switch (AML_CRSTYPE(crs)) {
212 : case SR_IRQ:
213 0 : sc_crs->irq_int = ffs(letoh16(crs->sr_irq.irq_mask)) - 1;
214 0 : sc_crs->irq_flags = crs->sr_irq.irq_flags;
215 0 : break;
216 :
217 : case LR_EXTIRQ:
218 0 : sc_crs->irq_int = letoh32(crs->lr_extirq.irq[0]);
219 0 : sc_crs->irq_flags = crs->lr_extirq.flags;
220 0 : break;
221 :
222 : case LR_GPIO:
223 0 : node = aml_searchname(sc_crs->devnode,
224 0 : (char *)&crs->pad[crs->lr_gpio.res_off]);
225 0 : pin = *(uint16_t *)&crs->pad[crs->lr_gpio.pin_off];
226 0 : if (crs->lr_gpio.type == LR_GPIO_INT) {
227 0 : sc_crs->gpio_int_node = node;
228 0 : sc_crs->gpio_int_pin = pin;
229 0 : sc_crs->gpio_int_flags = crs->lr_gpio.tflags;
230 0 : }
231 : break;
232 :
233 : case LR_MEM32:
234 0 : sc_crs->addr_min = letoh32(crs->lr_m32._min);
235 0 : sc_crs->addr_len = letoh32(crs->lr_m32._len);
236 0 : break;
237 :
238 : case LR_MEM32FIXED:
239 0 : sc_crs->addr_bas = letoh32(crs->lr_m32fixed._bas);
240 0 : sc_crs->addr_len = letoh32(crs->lr_m32fixed._len);
241 0 : break;
242 :
243 : case LR_SERBUS:
244 0 : if (crs->lr_serbus.type == LR_SERBUS_I2C)
245 0 : sc_crs->i2c_addr = letoh16(crs->lr_i2cbus._adr);
246 : break;
247 :
248 : default:
249 : DPRINTF(("%s: unknown resource type %d\n", __func__,
250 : AML_CRSTYPE(crs)));
251 : }
252 :
253 0 : return 0;
254 : }
255 :
256 : void
257 0 : dwiic_acpi_get_params(struct dwiic_softc *sc, char *method, uint16_t *hcnt,
258 : uint16_t *lcnt, uint32_t *sda_hold_time)
259 : {
260 0 : struct aml_value res;
261 :
262 0 : if (!aml_searchname(sc->sc_devnode, method))
263 0 : return;
264 :
265 0 : if (aml_evalname(sc->sc_acpi, sc->sc_devnode, method, 0, NULL, &res)) {
266 0 : printf(": eval of %s at %s failed", method,
267 0 : aml_nodename(sc->sc_devnode));
268 0 : return;
269 : }
270 :
271 0 : if (res.type != AML_OBJTYPE_PACKAGE) {
272 0 : printf(": %s is not a package (%d)", method, res.type);
273 0 : aml_freevalue(&res);
274 0 : return;
275 : }
276 :
277 0 : if (res.length <= 2) {
278 0 : printf(": %s returned package of len %d", method, res.length);
279 0 : aml_freevalue(&res);
280 0 : return;
281 : }
282 :
283 0 : *hcnt = aml_val2int(res.v_package[0]);
284 0 : *lcnt = aml_val2int(res.v_package[1]);
285 0 : if (sda_hold_time)
286 0 : *sda_hold_time = aml_val2int(res.v_package[2]);
287 0 : aml_freevalue(&res);
288 0 : }
289 :
290 : void
291 0 : dwiic_acpi_bus_scan(struct device *iic, struct i2cbus_attach_args *iba,
292 : void *aux)
293 : {
294 0 : struct dwiic_softc *sc = (struct dwiic_softc *)aux;
295 :
296 0 : sc->sc_iic = iic;
297 0 : aml_find_node(sc->sc_devnode, "_HID", dwiic_acpi_found_hid, sc);
298 0 : }
299 :
300 : void *
301 0 : dwiic_i2c_intr_establish(void *cookie, void *ih, int level,
302 : int (*func)(void *), void *arg, const char *name)
303 : {
304 0 : struct dwiic_crs *crs = ih;
305 :
306 0 : if (crs->gpio_int_node && crs->gpio_int_node->gpio) {
307 : struct acpi_gpio *gpio = crs->gpio_int_node->gpio;
308 0 : gpio->intr_establish(gpio->cookie, crs->gpio_int_pin,
309 0 : crs->gpio_int_flags, func, arg);
310 : return ih;
311 : }
312 :
313 0 : return acpi_intr_establish(crs->irq_int, crs->irq_flags,
314 : level, func, arg, name);
315 0 : }
316 :
317 : const char *
318 0 : dwiic_i2c_intr_string(void *cookie, void *ih)
319 : {
320 0 : struct dwiic_crs *crs = ih;
321 : static char irqstr[64];
322 :
323 0 : if (crs->gpio_int_node && crs->gpio_int_node->gpio)
324 0 : snprintf(irqstr, sizeof(irqstr), "gpio %d", crs->gpio_int_pin);
325 : else
326 0 : snprintf(irqstr, sizeof(irqstr), "irq %d", crs->irq_int);
327 :
328 0 : return irqstr;
329 : }
330 :
331 : int
332 0 : dwiic_matchhids(const char *hid, const char *hids[])
333 : {
334 : int i;
335 :
336 0 : for (i = 0; hids[i]; i++)
337 0 : if (!strcmp(hid, hids[i]))
338 0 : return (1);
339 :
340 0 : return (0);
341 0 : }
342 :
343 : int
344 0 : dwiic_acpi_found_hid(struct aml_node *node, void *arg)
345 : {
346 0 : struct dwiic_softc *sc = (struct dwiic_softc *)arg;
347 0 : struct dwiic_crs crs;
348 0 : struct aml_value res;
349 0 : int64_t sta;
350 0 : char cdev[16], dev[16];
351 0 : struct i2c_attach_args ia;
352 :
353 : /* Skip our own _HID. */
354 0 : if (node->parent == sc->sc_devnode)
355 0 : return 0;
356 :
357 0 : if (acpi_parsehid(node, arg, cdev, dev, 16) != 0)
358 0 : return 0;
359 :
360 0 : if (aml_evalinteger(acpi_softc, node->parent, "_STA", 0, NULL, &sta))
361 0 : sta = STA_PRESENT | STA_ENABLED | STA_DEV_OK | 0x1000;
362 :
363 0 : if ((sta & STA_PRESENT) == 0)
364 0 : return 0;
365 :
366 : DPRINTF(("%s: found HID %s at %s\n", sc->sc_dev.dv_xname, dev,
367 : aml_nodename(node)));
368 :
369 0 : if (aml_evalname(acpi_softc, node->parent, "_CRS", 0, NULL, &res)) {
370 0 : printf("%s: no _CRS method at %s\n", sc->sc_dev.dv_xname,
371 0 : aml_nodename(node->parent));
372 0 : return (0);
373 : }
374 0 : if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
375 0 : printf("%s: invalid _CRS object (type %d len %d)\n",
376 0 : sc->sc_dev.dv_xname, res.type, res.length);
377 0 : aml_freevalue(&res);
378 0 : return (0);
379 : }
380 0 : memset(&crs, 0, sizeof(crs));
381 0 : crs.devnode = sc->sc_devnode;
382 0 : aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs);
383 0 : aml_freevalue(&res);
384 :
385 0 : acpi_attach_deps(acpi_softc, node->parent);
386 :
387 0 : if (dwiic_matchhids(cdev, ihidev_hids))
388 0 : return dwiic_acpi_found_ihidev(sc, node, dev, crs);
389 0 : else if (dwiic_matchhids(dev, iatp_hids))
390 0 : return dwiic_acpi_found_iatp(sc, node, dev, crs);
391 :
392 0 : memset(&ia, 0, sizeof(ia));
393 0 : ia.ia_tag = sc->sc_iba.iba_tag;
394 0 : ia.ia_name = dev;
395 0 : ia.ia_addr = crs.i2c_addr;
396 0 : ia.ia_cookie = node->parent;
397 :
398 0 : if (crs.irq_int != 0 || crs.gpio_int_node != NULL)
399 0 : ia.ia_intr = &crs;
400 :
401 0 : config_found(sc->sc_iic, &ia, dwiic_i2c_print);
402 0 : node->parent->attached = 1;
403 :
404 0 : return 0;
405 0 : }
406 :
407 : int
408 0 : dwiic_acpi_found_ihidev(struct dwiic_softc *sc, struct aml_node *node,
409 : char *dev, struct dwiic_crs crs)
410 : {
411 0 : struct i2c_attach_args ia;
412 0 : struct aml_value cmd[4], res;
413 :
414 : /* 3cdff6f7-4267-4555-ad05-b30a3d8938de */
415 : static uint8_t i2c_hid_guid[] = {
416 : 0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,
417 : 0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,
418 : };
419 :
420 0 : if (!aml_searchname(node->parent, "_DSM")) {
421 0 : printf("%s: couldn't find _DSM at %s\n", sc->sc_dev.dv_xname,
422 0 : aml_nodename(node->parent));
423 0 : return 0;
424 : }
425 :
426 0 : bzero(&cmd, sizeof(cmd));
427 0 : cmd[0].type = AML_OBJTYPE_BUFFER;
428 0 : cmd[0].v_buffer = (uint8_t *)&i2c_hid_guid;
429 0 : cmd[0].length = sizeof(i2c_hid_guid);
430 : /* rev */
431 0 : cmd[1].type = AML_OBJTYPE_INTEGER;
432 0 : cmd[1].v_integer = 1;
433 0 : cmd[1].length = 1;
434 : /* func */
435 0 : cmd[2].type = AML_OBJTYPE_INTEGER;
436 0 : cmd[2].v_integer = 1; /* HID */
437 0 : cmd[2].length = 1;
438 : /* not used */
439 0 : cmd[3].type = AML_OBJTYPE_PACKAGE;
440 0 : cmd[3].length = 0;
441 :
442 0 : if (aml_evalname(acpi_softc, node->parent, "_DSM", 4, cmd, &res)) {
443 0 : printf("%s: eval of _DSM at %s failed\n",
444 0 : sc->sc_dev.dv_xname, aml_nodename(node->parent));
445 0 : return 0;
446 : }
447 :
448 0 : if (res.type != AML_OBJTYPE_INTEGER) {
449 0 : printf("%s: bad _DSM result at %s: %d\n",
450 0 : sc->sc_dev.dv_xname, aml_nodename(node->parent), res.type);
451 0 : aml_freevalue(&res);
452 0 : return 0;
453 : }
454 :
455 0 : memset(&ia, 0, sizeof(ia));
456 0 : ia.ia_tag = sc->sc_iba.iba_tag;
457 0 : ia.ia_size = 1;
458 0 : ia.ia_name = "ihidev";
459 0 : ia.ia_size = aml_val2int(&res); /* hid descriptor address */
460 0 : ia.ia_addr = crs.i2c_addr;
461 0 : ia.ia_cookie = dev;
462 :
463 0 : aml_freevalue(&res);
464 :
465 0 : if (!sc->sc_poll_ihidev &&
466 0 : !(crs.irq_int == 0 && crs.gpio_int_node == NULL))
467 0 : ia.ia_intr = &crs;
468 :
469 0 : if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) {
470 0 : node->parent->attached = 1;
471 0 : return 0;
472 : }
473 :
474 0 : return 1;
475 0 : }
476 :
477 : int
478 0 : dwiic_acpi_found_iatp(struct dwiic_softc *sc, struct aml_node *node, char *dev,
479 : struct dwiic_crs crs)
480 : {
481 0 : struct i2c_attach_args ia;
482 0 : struct aml_value res;
483 :
484 0 : if (aml_evalname(acpi_softc, node->parent, "GPIO", 0, NULL, &res))
485 : /* no gpio, assume this is the bootloader interface */
486 0 : return (0);
487 :
488 0 : memset(&ia, 0, sizeof(ia));
489 0 : ia.ia_tag = sc->sc_iba.iba_tag;
490 0 : ia.ia_size = 1;
491 0 : ia.ia_name = "iatp";
492 0 : ia.ia_addr = crs.i2c_addr;
493 0 : ia.ia_cookie = dev;
494 :
495 0 : if (crs.irq_int <= 0 && crs.gpio_int_node == NULL) {
496 0 : printf("%s: couldn't find irq for %s\n", sc->sc_dev.dv_xname,
497 0 : aml_nodename(node->parent));
498 0 : return 0;
499 : }
500 0 : ia.ia_intr = &crs;
501 :
502 0 : if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) {
503 0 : node->parent->attached = 1;
504 0 : return 0;
505 : }
506 :
507 0 : return 1;
508 0 : }
509 :
510 : void
511 0 : dwiic_acpi_power(struct dwiic_softc *sc, int power)
512 : {
513 0 : char ps[] = "_PS0";
514 :
515 0 : if (!power)
516 0 : ps[3] = '3';
517 :
518 0 : if (aml_searchname(sc->sc_devnode, ps)) {
519 0 : if (aml_evalname(sc->sc_acpi, sc->sc_devnode, ps, 0, NULL,
520 : NULL)) {
521 0 : printf("%s: failed powering %s with %s\n",
522 0 : sc->sc_dev.dv_xname, power ? "on" : "off",
523 : ps);
524 0 : return;
525 : }
526 :
527 0 : DELAY(10000); /* 10 milliseconds */
528 0 : } else
529 : DPRINTF(("%s: no %s method\n", sc->sc_dev.dv_xname, ps));
530 :
531 0 : if (strcmp(sc->sc_hid, "INT3432") == 0 ||
532 0 : strcmp(sc->sc_hid, "INT3433") == 0) {
533 : /*
534 : * XXX: broadwell i2c devices may need this for initial power
535 : * up and/or after s3 resume.
536 : *
537 : * linux does this write via LPSS -> clk_register_gate ->
538 : * clk_gate_enable -> clk_gate_endisable -> clk_writel
539 : */
540 0 : dwiic_write(sc, 0x800, 1);
541 0 : }
542 0 : }
|