1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019-2020 Vladimir Kondratyev <wulf@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #include <sys/param.h>
30 #include <sys/bus.h>
31 #include <sys/endian.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/module.h>
35 #include <sys/rman.h>
36
37 #include <machine/resource.h>
38
39 #include <contrib/dev/acpica/include/acpi.h>
40 #include <contrib/dev/acpica/include/accommon.h>
41 #include <contrib/dev/acpica/include/amlcode.h>
42 #include <dev/acpica/acpivar.h>
43
44 #include <dev/iicbus/iiconf.h>
45 #include <dev/iicbus/iicbus.h>
46
47 #define ACPI_IICBUS_LOCAL_BUFSIZE 32 /* Fits max SMBUS block size */
48
49 /*
50 * Make a copy of ACPI_RESOURCE_I2C_SERIALBUS type and replace "pointer to ACPI
51 * object name string" field with pointer to ACPI object itself.
52 * This saves us extra strdup()/free() pair on acpi_iicbus_get_i2cres call.
53 */
54 typedef ACPI_RESOURCE_I2C_SERIALBUS ACPI_IICBUS_RESOURCE_I2C_SERIALBUS;
55 #define ResourceSource_Handle ResourceSource.StringPtr
56
57 /* Hooks for the ACPI CA debugging infrastructure. */
58 #define _COMPONENT ACPI_BUS
59 ACPI_MODULE_NAME("IIC")
60
61 struct gsb_buffer {
62 UINT8 status;
63 UINT8 len;
64 UINT8 data[];
65 } __packed;
66
67 struct acpi_iicbus_softc {
68 struct iicbus_softc super_sc;
69 ACPI_CONNECTION_INFO space_handler_info;
70 bool space_handler_installed;
71 };
72
73 struct acpi_iicbus_ivars {
74 struct iicbus_ivar super_ivar;
75 ACPI_HANDLE handle;
76 };
77
78 static int install_space_handler = 0;
79 TUNABLE_INT("hw.iicbus.enable_acpi_space_handler", &install_space_handler);
80
81 static inline bool
acpi_resource_is_i2c_serialbus(ACPI_RESOURCE * res)82 acpi_resource_is_i2c_serialbus(ACPI_RESOURCE *res)
83 {
84
85 return (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
86 res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_I2C);
87 }
88
89 /*
90 * IICBUS Address space handler
91 */
92 static int
acpi_iicbus_sendb(device_t dev,u_char slave,char byte)93 acpi_iicbus_sendb(device_t dev, u_char slave, char byte)
94 {
95 struct iic_msg msgs[] = {
96 { slave, IIC_M_WR, 1, &byte },
97 };
98
99 return (iicbus_transfer(dev, msgs, nitems(msgs)));
100 }
101
102 static int
acpi_iicbus_recvb(device_t dev,u_char slave,char * byte)103 acpi_iicbus_recvb(device_t dev, u_char slave, char *byte)
104 {
105 char buf;
106 struct iic_msg msgs[] = {
107 { slave, IIC_M_RD, 1, &buf },
108 };
109 int error;
110
111 error = iicbus_transfer(dev, msgs, nitems(msgs));
112 if (error == 0)
113 *byte = buf;
114
115 return (error);
116 }
117
118 static int
acpi_iicbus_write(device_t dev,u_char slave,char cmd,void * buf,uint16_t buflen)119 acpi_iicbus_write(device_t dev, u_char slave, char cmd, void *buf,
120 uint16_t buflen)
121 {
122 struct iic_msg msgs[] = {
123 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
124 { slave, IIC_M_WR | IIC_M_NOSTART, buflen, buf },
125 };
126
127 return (iicbus_transfer(dev, msgs, nitems(msgs)));
128 }
129
130 static int
acpi_iicbus_read(device_t dev,u_char slave,char cmd,void * buf,uint16_t buflen)131 acpi_iicbus_read(device_t dev, u_char slave, char cmd, void *buf,
132 uint16_t buflen)
133 {
134 uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
135 struct iic_msg msgs[] = {
136 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
137 { slave, IIC_M_RD, buflen, NULL },
138 };
139 int error;
140
141 if (buflen <= sizeof(local_buffer))
142 msgs[1].buf = local_buffer;
143 else
144 msgs[1].buf = malloc(buflen, M_DEVBUF, M_WAITOK);
145 error = iicbus_transfer(dev, msgs, nitems(msgs));
146 if (error == 0)
147 memcpy(buf, msgs[1].buf, buflen);
148 if (msgs[1].buf != local_buffer)
149 free(msgs[1].buf, M_DEVBUF);
150
151 return (error);
152 }
153
154 static int
acpi_iicbus_bwrite(device_t dev,u_char slave,char cmd,u_char count,char * buf)155 acpi_iicbus_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
156 {
157 uint8_t bytes[2] = { cmd, count };
158 struct iic_msg msgs[] = {
159 { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
160 { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
161 };
162
163 if (count == 0)
164 return (errno2iic(EINVAL));
165
166 return (iicbus_transfer(dev, msgs, nitems(msgs)));
167 }
168
169 static int
acpi_iicbus_bread(device_t dev,u_char slave,char cmd,u_char * count,char * buf)170 acpi_iicbus_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
171 {
172 uint8_t local_buffer[ACPI_IICBUS_LOCAL_BUFSIZE];
173 u_char len;
174 struct iic_msg msgs[] = {
175 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
176 { slave, IIC_M_RD | IIC_M_NOSTOP, 1, &len },
177 };
178 struct iic_msg block_msg[] = {
179 { slave, IIC_M_RD | IIC_M_NOSTART, 0, NULL },
180 };
181 device_t parent = device_get_parent(dev);
182 int error;
183
184 /* Have to do this because the command is split in two transfers. */
185 error = iicbus_request_bus(parent, dev, IIC_WAIT);
186 if (error == 0)
187 error = iicbus_transfer(dev, msgs, nitems(msgs));
188 if (error == 0) {
189 /*
190 * If the slave offers an empty reply,
191 * read one byte to generate the stop or abort.
192 */
193 if (len == 0)
194 block_msg[0].len = 1;
195 else
196 block_msg[0].len = len;
197 if (len <= sizeof(local_buffer))
198 block_msg[0].buf = local_buffer;
199 else
200 block_msg[0].buf = malloc(len, M_DEVBUF, M_WAITOK);
201 error = iicbus_transfer(dev, block_msg, nitems(block_msg));
202 if (len == 0)
203 error = errno2iic(EBADMSG);
204 if (error == 0) {
205 *count = len;
206 memcpy(buf, block_msg[0].buf, len);
207 }
208 if (block_msg[0].buf != local_buffer)
209 free(block_msg[0].buf, M_DEVBUF);
210 }
211 (void)iicbus_release_bus(parent, dev);
212 return (error);
213 }
214
215 static ACPI_STATUS
acpi_iicbus_space_handler(UINT32 Function,ACPI_PHYSICAL_ADDRESS Address,UINT32 BitWidth,UINT64 * Value,void * HandlerContext,void * RegionContext)216 acpi_iicbus_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
217 UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
218 {
219 struct gsb_buffer *gsb;
220 struct acpi_iicbus_softc *sc;
221 device_t dev;
222 ACPI_CONNECTION_INFO *info;
223 ACPI_RESOURCE_I2C_SERIALBUS *sb;
224 ACPI_RESOURCE *res;
225 ACPI_STATUS s;
226 int val;
227
228 gsb = (struct gsb_buffer *)Value;
229 if (gsb == NULL)
230 return (AE_BAD_PARAMETER);
231
232 info = HandlerContext;
233 s = AcpiBufferToResource(info->Connection, info->Length, &res);
234 if (ACPI_FAILURE(s))
235 return (s);
236
237 if (!acpi_resource_is_i2c_serialbus(res)) {
238 s = AE_BAD_PARAMETER;
239 goto err;
240 }
241
242 sb = &res->Data.I2cSerialBus;
243
244 /* XXX Ignore 10bit addressing for now */
245 if (sb->AccessMode == ACPI_I2C_10BIT_MODE) {
246 s = AE_BAD_PARAMETER;
247 goto err;
248 }
249
250 #define AML_FIELD_ATTRIB_MASK 0x0F
251 #define AML_FIELD_ATTRIO(attr, io) (((attr) << 16) | (io))
252
253 Function &= AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_MASK, ACPI_IO_MASK);
254 sc = __containerof(info, struct acpi_iicbus_softc, space_handler_info);
255 dev = sc->super_sc.dev;
256
257 switch (Function) {
258 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_READ):
259 val = acpi_iicbus_recvb(dev, sb->SlaveAddress, gsb->data);
260 break;
261
262 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_SEND_RECEIVE, ACPI_WRITE):
263 val = acpi_iicbus_sendb(dev, sb->SlaveAddress, gsb->data[0]);
264 break;
265
266 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_READ):
267 val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
268 gsb->data, 1);
269 break;
270
271 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTE, ACPI_WRITE):
272 val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
273 gsb->data, 1);
274 break;
275
276 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_READ):
277 val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
278 gsb->data, 2);
279 break;
280
281 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_WORD, ACPI_WRITE):
282 val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
283 gsb->data, 2);
284 break;
285
286 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_READ):
287 val = acpi_iicbus_bread(dev, sb->SlaveAddress, Address,
288 &gsb->len, gsb->data);
289 break;
290
291 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BLOCK, ACPI_WRITE):
292 val = acpi_iicbus_bwrite(dev, sb->SlaveAddress, Address,
293 gsb->len, gsb->data);
294 break;
295
296 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_READ):
297 val = acpi_iicbus_read(dev, sb->SlaveAddress, Address,
298 gsb->data, info->AccessLength);
299 break;
300
301 case AML_FIELD_ATTRIO(AML_FIELD_ATTRIB_BYTES, ACPI_WRITE):
302 val = acpi_iicbus_write(dev, sb->SlaveAddress, Address,
303 gsb->data, info->AccessLength);
304 break;
305
306 default:
307 device_printf(dev, "protocol(0x%04x) is not supported.\n",
308 Function);
309 s = AE_BAD_PARAMETER;
310 goto err;
311 }
312
313 gsb->status = val;
314
315 err:
316 ACPI_FREE(res);
317
318 return (s);
319 }
320
321 static int
acpi_iicbus_install_address_space_handler(struct acpi_iicbus_softc * sc)322 acpi_iicbus_install_address_space_handler(struct acpi_iicbus_softc *sc)
323 {
324 ACPI_HANDLE handle;
325 ACPI_STATUS s;
326
327 handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
328 s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
329 &acpi_iicbus_space_handler, NULL, &sc->space_handler_info);
330 if (ACPI_FAILURE(s)) {
331 device_printf(sc->super_sc.dev,
332 "Failed to install GSBUS Address Space Handler in ACPI\n");
333 return (ENXIO);
334 }
335
336 return (0);
337 }
338
339 static int
acpi_iicbus_remove_address_space_handler(struct acpi_iicbus_softc * sc)340 acpi_iicbus_remove_address_space_handler(struct acpi_iicbus_softc *sc)
341 {
342 ACPI_HANDLE handle;
343 ACPI_STATUS s;
344
345 handle = acpi_get_handle(device_get_parent(sc->super_sc.dev));
346 s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
347 &acpi_iicbus_space_handler);
348 if (ACPI_FAILURE(s)) {
349 device_printf(sc->super_sc.dev,
350 "Failed to remove GSBUS Address Space Handler from ACPI\n");
351 return (ENXIO);
352 }
353
354 return (0);
355 }
356
357 static ACPI_STATUS
acpi_iicbus_get_i2cres_cb(ACPI_RESOURCE * res,void * context)358 acpi_iicbus_get_i2cres_cb(ACPI_RESOURCE *res, void *context)
359 {
360 ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb = context;
361 ACPI_STATUS status;
362 ACPI_HANDLE handle;
363
364 if (acpi_resource_is_i2c_serialbus(res)) {
365 status = AcpiGetHandle(ACPI_ROOT_OBJECT,
366 res->Data.I2cSerialBus.ResourceSource.StringPtr, &handle);
367 if (ACPI_FAILURE(status))
368 return (status);
369 memcpy(sb, &res->Data.I2cSerialBus,
370 sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
371 /*
372 * replace "pointer to ACPI object name string" field
373 * with pointer to ACPI object itself.
374 */
375 sb->ResourceSource_Handle = handle;
376 return (AE_CTRL_TERMINATE);
377 } else if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
378 return (AE_NOT_FOUND);
379
380 return (AE_OK);
381 }
382
383 static ACPI_STATUS
acpi_iicbus_get_i2cres(ACPI_HANDLE handle,ACPI_RESOURCE_I2C_SERIALBUS * sb)384 acpi_iicbus_get_i2cres(ACPI_HANDLE handle, ACPI_RESOURCE_I2C_SERIALBUS *sb)
385 {
386
387 return (AcpiWalkResources(handle, "_CRS",
388 acpi_iicbus_get_i2cres_cb, sb));
389 }
390
391 static ACPI_STATUS
acpi_iicbus_parse_resources_cb(ACPI_RESOURCE * res,void * context)392 acpi_iicbus_parse_resources_cb(ACPI_RESOURCE *res, void *context)
393 {
394 device_t dev = context;
395 struct iicbus_ivar *super_devi = device_get_ivars(dev);
396 struct resource_list *rl = &super_devi->rl;
397 int irq, gpio_pin;
398
399 switch(res->Type) {
400 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
401 if (res->Data.ExtendedIrq.InterruptCount > 0) {
402 irq = res->Data.ExtendedIrq.Interrupts[0];
403 if (bootverbose)
404 printf(" IRQ: %d\n", irq);
405 resource_list_add_next(rl, SYS_RES_IRQ, irq, irq, 1);
406 return (AE_CTRL_TERMINATE);
407 }
408 break;
409 case ACPI_RESOURCE_TYPE_GPIO:
410 if (res->Data.Gpio.ConnectionType ==
411 ACPI_RESOURCE_GPIO_TYPE_INT) {
412 /* Not supported by FreeBSD yet */
413 gpio_pin = res->Data.Gpio.PinTable[0];
414 if (bootverbose)
415 printf(" GPIO IRQ pin: %d\n", gpio_pin);
416 return (AE_CTRL_TERMINATE);
417 }
418 break;
419 default:
420 break;
421 }
422
423 return (AE_OK);
424 }
425
426 static ACPI_STATUS
acpi_iicbus_parse_resources(ACPI_HANDLE handle,device_t dev)427 acpi_iicbus_parse_resources(ACPI_HANDLE handle, device_t dev)
428 {
429
430 return (AcpiWalkResources(handle, "_CRS",
431 acpi_iicbus_parse_resources_cb, dev));
432 }
433
434 static void
acpi_iicbus_dump_res(device_t dev,ACPI_IICBUS_RESOURCE_I2C_SERIALBUS * sb)435 acpi_iicbus_dump_res(device_t dev, ACPI_IICBUS_RESOURCE_I2C_SERIALBUS *sb)
436 {
437 device_printf(dev, "found ACPI child\n");
438 printf(" SlaveAddress: 0x%04hx\n", sb->SlaveAddress);
439 printf(" ConnectionSpeed: %uHz\n", sb->ConnectionSpeed);
440 printf(" SlaveMode: %s\n",
441 sb->SlaveMode == ACPI_CONTROLLER_INITIATED ?
442 "ControllerInitiated" : "DeviceInitiated");
443 printf(" AddressingMode: %uBit\n", sb->AccessMode == 0 ? 7 : 10);
444 printf(" ConnectionSharing: %s\n", sb->ConnectionSharing == 0 ?
445 "Exclusive" : "Shared");
446 }
447
448 static device_t
acpi_iicbus_add_child(device_t dev,u_int order,const char * name,int unit)449 acpi_iicbus_add_child(device_t dev, u_int order, const char *name, int unit)
450 {
451
452 return (iicbus_add_child_common(
453 dev, order, name, unit, sizeof(struct acpi_iicbus_ivars)));
454 }
455
456 static ACPI_STATUS
acpi_iicbus_enumerate_child(ACPI_HANDLE handle,UINT32 level,void * context,void ** result)457 acpi_iicbus_enumerate_child(ACPI_HANDLE handle, UINT32 level,
458 void *context, void **result)
459 {
460 device_t iicbus, child, acpi_child, acpi0;
461 struct iicbus_softc *super_sc;
462 ACPI_IICBUS_RESOURCE_I2C_SERIALBUS sb;
463 ACPI_STATUS status;
464 UINT32 sta;
465
466 iicbus = context;
467 super_sc = device_get_softc(iicbus);
468
469 /*
470 * If no _STA method or if it failed, then assume that
471 * the device is present.
472 */
473 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
474 !ACPI_DEVICE_PRESENT(sta))
475 return (AE_OK);
476
477 if (!acpi_has_hid(handle))
478 return (AE_OK);
479
480 /*
481 * Read "I2C Serial Bus Connection Resource Descriptor"
482 * described in p.19.6.57 of ACPI specification.
483 */
484 bzero(&sb, sizeof(ACPI_IICBUS_RESOURCE_I2C_SERIALBUS));
485 if (ACPI_FAILURE(acpi_iicbus_get_i2cres(handle, &sb)) ||
486 sb.SlaveAddress == 0)
487 return (AE_OK);
488 if (sb.ResourceSource_Handle !=
489 acpi_get_handle(device_get_parent(iicbus)))
490 return (AE_OK);
491 if (bootverbose)
492 acpi_iicbus_dump_res(iicbus, &sb);
493
494 /* Find out speed of the slowest slave */
495 if (super_sc->bus_freq == 0 || super_sc->bus_freq > sb.ConnectionSpeed)
496 super_sc->bus_freq = sb.ConnectionSpeed;
497
498 /* Delete existing child of acpi bus */
499 acpi_child = acpi_get_device(handle);
500 if (acpi_child != NULL) {
501 acpi0 = devclass_get_device(devclass_find("acpi"), 0);
502 if (device_get_parent(acpi_child) != acpi0)
503 return (AE_OK);
504
505 if (device_is_attached(acpi_child))
506 return (AE_OK);
507
508 if (device_delete_child(acpi0, acpi_child) != 0)
509 return (AE_OK);
510 }
511
512 child = BUS_ADD_CHILD(iicbus, 0, NULL, -1);
513 if (child == NULL) {
514 device_printf(iicbus, "add child failed\n");
515 return (AE_OK);
516 }
517
518 iicbus_set_addr(child, sb.SlaveAddress);
519 acpi_set_handle(child, handle);
520 (void)acpi_iicbus_parse_resources(handle, child);
521
522 /*
523 * Update ACPI-CA to use the IIC enumerated device_t for this handle.
524 */
525 status = AcpiAttachData(handle, acpi_fake_objhandler, child);
526 if (ACPI_FAILURE(status))
527 printf("WARNING: Unable to attach object data to %s - %s\n",
528 acpi_name(handle), AcpiFormatException(status));
529
530 return (AE_OK);
531 }
532
533 static ACPI_STATUS
acpi_iicbus_enumerate_children(device_t dev)534 acpi_iicbus_enumerate_children(device_t dev)
535 {
536
537 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
538 ACPI_UINT32_MAX, acpi_iicbus_enumerate_child, NULL, dev, NULL));
539 }
540
541 static void
acpi_iicbus_set_power_children(device_t dev,int state,bool all_children)542 acpi_iicbus_set_power_children(device_t dev, int state, bool all_children)
543 {
544 device_t *devlist;
545 int i, numdevs;
546
547 if (device_get_children(dev, &devlist, &numdevs) != 0)
548 return;
549
550 for (i = 0; i < numdevs; i++)
551 if (all_children || device_is_attached(devlist[i]) != 0)
552 acpi_set_powerstate(devlist[i], state);
553
554 free(devlist, M_TEMP);
555 }
556
557 static int
acpi_iicbus_probe(device_t dev)558 acpi_iicbus_probe(device_t dev)
559 {
560 ACPI_HANDLE handle;
561 device_t controller;
562
563 if (acpi_disabled("iicbus"))
564 return (ENXIO);
565
566 controller = device_get_parent(dev);
567 if (controller == NULL)
568 return (ENXIO);
569
570 handle = acpi_get_handle(controller);
571 if (handle == NULL)
572 return (ENXIO);
573
574 device_set_desc(dev, "Philips I2C bus (ACPI-hinted)");
575 return (BUS_PROBE_DEFAULT);
576 }
577
578 static int
acpi_iicbus_attach(device_t dev)579 acpi_iicbus_attach(device_t dev)
580 {
581 struct acpi_iicbus_softc *sc = device_get_softc(dev);
582 int error;
583
584 if (ACPI_FAILURE(acpi_iicbus_enumerate_children(dev)))
585 device_printf(dev, "children enumeration failed\n");
586
587 acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, true);
588 error = iicbus_attach_common(dev, sc->super_sc.bus_freq);
589 if (error == 0 && install_space_handler != 0 &&
590 acpi_iicbus_install_address_space_handler(sc) == 0)
591 sc->space_handler_installed = true;
592
593 return (error);
594 }
595
596 static int
acpi_iicbus_detach(device_t dev)597 acpi_iicbus_detach(device_t dev)
598 {
599 struct acpi_iicbus_softc *sc = device_get_softc(dev);
600
601 if (sc->space_handler_installed)
602 acpi_iicbus_remove_address_space_handler(sc);
603 acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
604
605 return (iicbus_detach(dev));
606 }
607
608 static int
acpi_iicbus_suspend(device_t dev)609 acpi_iicbus_suspend(device_t dev)
610 {
611 int error;
612
613 error = bus_generic_suspend(dev);
614 if (error == 0)
615 acpi_iicbus_set_power_children(dev, ACPI_STATE_D3, false);
616
617 return (error);
618 }
619
620 static int
acpi_iicbus_resume(device_t dev)621 acpi_iicbus_resume(device_t dev)
622 {
623
624 acpi_iicbus_set_power_children(dev, ACPI_STATE_D0, false);
625
626 return (bus_generic_resume(dev));
627 }
628
629 /*
630 * If this device is an ACPI child but no one claimed it, attempt
631 * to power it off. We'll power it back up when a driver is added.
632 */
633 static void
acpi_iicbus_probe_nomatch(device_t bus,device_t child)634 acpi_iicbus_probe_nomatch(device_t bus, device_t child)
635 {
636
637 iicbus_probe_nomatch(bus, child);
638 acpi_set_powerstate(child, ACPI_STATE_D3);
639 }
640
641 /*
642 * If a new driver has a chance to probe a child, first power it up.
643 */
644 static void
acpi_iicbus_driver_added(device_t dev,driver_t * driver)645 acpi_iicbus_driver_added(device_t dev, driver_t *driver)
646 {
647 device_t child, *devlist;
648 int i, numdevs;
649
650 DEVICE_IDENTIFY(driver, dev);
651 if (device_get_children(dev, &devlist, &numdevs) != 0)
652 return;
653
654 for (i = 0; i < numdevs; i++) {
655 child = devlist[i];
656 if (device_get_state(child) == DS_NOTPRESENT) {
657 acpi_set_powerstate(child, ACPI_STATE_D0);
658 if (device_probe_and_attach(child) != 0)
659 acpi_set_powerstate(child, ACPI_STATE_D3);
660 }
661 }
662 free(devlist, M_TEMP);
663 }
664
665 static void
acpi_iicbus_child_deleted(device_t bus,device_t child)666 acpi_iicbus_child_deleted(device_t bus, device_t child)
667 {
668 struct acpi_iicbus_ivars *devi = device_get_ivars(child);
669
670 if (acpi_get_device(devi->handle) == child)
671 AcpiDetachData(devi->handle, acpi_fake_objhandler);
672 }
673
674 static int
acpi_iicbus_read_ivar(device_t bus,device_t child,int which,uintptr_t * res)675 acpi_iicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *res)
676 {
677 struct acpi_iicbus_ivars *devi = device_get_ivars(child);
678
679 switch (which) {
680 case ACPI_IVAR_HANDLE:
681 *res = (uintptr_t)devi->handle;
682 break;
683 default:
684 return (iicbus_read_ivar(bus, child, which, res));
685 }
686
687 return (0);
688 }
689
690 static int
acpi_iicbus_write_ivar(device_t bus,device_t child,int which,uintptr_t val)691 acpi_iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t val)
692 {
693 struct acpi_iicbus_ivars *devi = device_get_ivars(child);
694
695 switch (which) {
696 case ACPI_IVAR_HANDLE:
697 if (devi->handle != NULL)
698 return (EINVAL);
699 devi->handle = (ACPI_HANDLE)val;
700 break;
701 default:
702 return (iicbus_write_ivar(bus, child, which, val));
703 }
704
705 return (0);
706 }
707
708 /* Location hint for devctl(8). Concatenate IIC and ACPI hints. */
709 static int
acpi_iicbus_child_location_str(device_t bus,device_t child,char * buf,size_t buflen)710 acpi_iicbus_child_location_str(device_t bus, device_t child,
711 char *buf, size_t buflen)
712 {
713 struct acpi_iicbus_ivars *devi = device_get_ivars(child);
714 int error;
715
716 /* read IIC location hint string into the buffer. */
717 error = iicbus_child_location_str(bus, child, buf, buflen);
718 if (error != 0)
719 return (error);
720
721 /* Place ACPI string right after IIC one's terminating NUL. */
722 if (devi->handle != NULL &&
723 ((buf[0] != '\0' && strlcat(buf, " ", buflen) >= buflen) ||
724 strlcat(buf, "handle=", buflen) >= buflen ||
725 strlcat(buf, acpi_name(devi->handle), buflen) >= buflen))
726 return (EOVERFLOW);
727
728 return (0);
729 }
730
731 /* PnP information for devctl(8). Concatenate IIC and ACPI info strings. */
732 static int
acpi_iicbus_child_pnpinfo_str(device_t bus,device_t child,char * buf,size_t buflen)733 acpi_iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
734 size_t buflen)
735 {
736 struct acpi_iicbus_ivars *devi = device_get_ivars(child);
737 size_t acpi_offset;
738 int error;
739
740 /* read IIC PnP string into the buffer. */
741 error = iicbus_child_pnpinfo_str(bus, child, buf, buflen);
742 if (error != 0)
743 return (error);
744
745 if (devi->handle == NULL)
746 return (0);
747
748 /* Place ACPI string right after IIC one's terminating NUL. */
749 acpi_offset = strlen(buf);
750 if (acpi_offset != 0)
751 acpi_offset++;
752 error = acpi_pnpinfo_str(devi->handle, buf + acpi_offset,
753 buflen - acpi_offset);
754
755 /* Coalesce both strings if they are not empty. */
756 if (acpi_offset > 0 && acpi_offset < buflen && buf[acpi_offset] != 0)
757 buf[acpi_offset - 1] = ' ';
758
759 return (error);
760 }
761
762 static device_method_t acpi_iicbus_methods[] = {
763 /* Device interface */
764 DEVMETHOD(device_probe, acpi_iicbus_probe),
765 DEVMETHOD(device_attach, acpi_iicbus_attach),
766 DEVMETHOD(device_detach, acpi_iicbus_detach),
767 DEVMETHOD(device_suspend, acpi_iicbus_suspend),
768 DEVMETHOD(device_resume, acpi_iicbus_resume),
769
770 /* Bus interface */
771 DEVMETHOD(bus_add_child, acpi_iicbus_add_child),
772 DEVMETHOD(bus_probe_nomatch, acpi_iicbus_probe_nomatch),
773 DEVMETHOD(bus_driver_added, acpi_iicbus_driver_added),
774 DEVMETHOD(bus_child_deleted, acpi_iicbus_child_deleted),
775 DEVMETHOD(bus_read_ivar, acpi_iicbus_read_ivar),
776 DEVMETHOD(bus_write_ivar, acpi_iicbus_write_ivar),
777 DEVMETHOD(bus_child_location_str,acpi_iicbus_child_location_str),
778 DEVMETHOD(bus_child_pnpinfo_str,acpi_iicbus_child_pnpinfo_str),
779
780 DEVMETHOD_END,
781 };
782
783 DEFINE_CLASS_1(iicbus, acpi_iicbus_driver, acpi_iicbus_methods,
784 sizeof(struct acpi_iicbus_softc), iicbus_driver);
785 MODULE_VERSION(acpi_iicbus, 1);
786 MODULE_DEPEND(acpi_iicbus, acpi, 1, 1, 1);
787