acpi_i2c.c revision 1.11.4.1 1 /* $NetBSD: acpi_i2c.c,v 1.11.4.1 2021/04/25 21:49:56 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2017, 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Manuel Bouyer and Jason R. Thorpe.
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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: acpi_i2c.c,v 1.11.4.1 2021/04/25 21:49:56 thorpej Exp $");
34
35 #include <dev/acpi/acpireg.h>
36 #include <dev/acpi/acpivar.h>
37 #include <dev/i2c/i2cvar.h>
38
39 #include <sys/kmem.h>
40
41 #define _COMPONENT ACPI_BUS_COMPONENT
42 ACPI_MODULE_NAME ("acpi_i2c")
43
44 struct acpi_i2c_context {
45 uint16_t i2c_addr;
46 };
47
48 static ACPI_STATUS
49 acpi_i2c_resource_parse_callback(ACPI_RESOURCE *res, void *context)
50 {
51 struct acpi_i2c_context *i2cc = context;
52
53 switch (res->Type) {
54 case ACPI_RESOURCE_TYPE_END_TAG:
55 break;
56 case ACPI_RESOURCE_TYPE_SERIAL_BUS:
57 switch (res->Data.I2cSerialBus.Type) {
58 case ACPI_RESOURCE_SERIAL_TYPE_I2C:
59 i2cc->i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
60 break;
61 }
62 break;
63 case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
64 break;
65 default:
66 printf("resource type 0x%x ignored\n", res->Type);
67 }
68 return_ACPI_STATUS(AE_OK);
69 }
70
71 static bool
72 acpi_i2c_enumerate_device(device_t dev, struct acpi_devnode *ad,
73 struct i2c_enumerate_devices_args * const args)
74 {
75 char *clist;
76 size_t clist_size;
77 prop_dictionary_t props;
78 struct acpi_i2c_context i2cc;
79 bool cbrv;
80 ACPI_STATUS rv;
81
82 memset(&i2cc, 0, sizeof(i2cc));
83 rv = AcpiWalkResources(ad->ad_handle, "_CRS",
84 acpi_i2c_resource_parse_callback, &i2cc);
85 if (ACPI_FAILURE(rv)) {
86 aprint_error("ACPI: unable to get resources "
87 "for %s: %s\n", ad->ad_name,
88 AcpiFormatException(rv));
89 return true; /* keep enumerating */
90 }
91 if (i2cc.i2c_addr == 0)
92 return true; /* keep enumerating */
93
94 clist = acpi_pack_compat_list(ad->ad_devinfo, &clist_size);
95 if (clist == NULL) {
96 aprint_error("ACPI: ignoring device %s (no _HID or _CID)\n",
97 ad->ad_name);
98 return true; /* keep enumerating */
99 }
100 props = prop_dictionary_create();
101
102 args->ia->ia_addr = i2cc.i2c_addr;
103 args->ia->ia_name = ad->ad_name;
104 args->ia->ia_clist = clist;
105 args->ia->ia_clist_size = clist_size;
106 args->ia->ia_prop = props;
107 args->ia->ia_devhandle = devhandle_from_acpi(ad->ad_handle);
108 args->ia->ia_cookie = (uint64_t)ad->ad_handle; /* XXX */
109 args->ia->ia_cookietype = I2C_COOKIE_ACPI; /* XXX */
110
111 cbrv = args->callback(dev, args);
112
113 prop_object_release(props);
114 kmem_free(clist, clist_size);
115
116 return cbrv; /* callback decides if we keep enumerating */
117 }
118
119 static int
120 acpi_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v)
121 {
122 struct i2c_enumerate_devices_args *args = v;
123 struct acpi_devnode *ad, *devnode;
124 ACPI_HANDLE *hdl = devhandle_to_acpi(call_handle);
125
126 devnode = acpi_match_node(hdl);
127 if (devnode == NULL) {
128 aprint_verbose_dev(dev, "%s: no devnode matching handle\n",
129 __func__);
130 return 0;
131 }
132
133 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
134 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE)
135 continue;
136 if (!acpi_device_present(ad->ad_handle))
137 continue;
138 if (!acpi_i2c_enumerate_device(dev, ad, args))
139 break;
140 }
141
142 acpi_claim_childdevs(dev, devnode);
143
144 return 0;
145 }
146 ACPI_DEVICE_CALL_REGISTER("i2c-enumerate-devices", acpi_i2c_enumerate_devices)
147