acpi_i2c.c revision 1.13 1 1.13 jmcneill /* $NetBSD: acpi_i2c.c,v 1.13 2024/12/08 20:44:40 jmcneill Exp $ */
2 1.1 bouyer
3 1.1 bouyer /*-
4 1.11 thorpej * Copyright (c) 2017, 2021 The NetBSD Foundation, Inc.
5 1.1 bouyer * All rights reserved.
6 1.1 bouyer *
7 1.1 bouyer * This code is derived from software contributed to The NetBSD Foundation
8 1.1 bouyer * by Manuel Bouyer.
9 1.1 bouyer *
10 1.1 bouyer * Redistribution and use in source and binary forms, with or without
11 1.1 bouyer * modification, are permitted provided that the following conditions
12 1.1 bouyer * are met:
13 1.1 bouyer * 1. Redistributions of source code must retain the above copyright
14 1.1 bouyer * notice, this list of conditions and the following disclaimer.
15 1.1 bouyer * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 bouyer * notice, this list of conditions and the following disclaimer in the
17 1.1 bouyer * documentation and/or other materials provided with the distribution.
18 1.1 bouyer *
19 1.1 bouyer * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 bouyer * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 bouyer * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 bouyer * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 bouyer * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 bouyer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 bouyer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 bouyer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 bouyer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 bouyer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 bouyer * POSSIBILITY OF SUCH DAMAGE.
30 1.1 bouyer */
31 1.1 bouyer
32 1.1 bouyer #include <sys/cdefs.h>
33 1.13 jmcneill __KERNEL_RCSID(0, "$NetBSD: acpi_i2c.c,v 1.13 2024/12/08 20:44:40 jmcneill Exp $");
34 1.13 jmcneill
35 1.13 jmcneill #include <sys/device.h>
36 1.1 bouyer
37 1.1 bouyer #include <dev/acpi/acpireg.h>
38 1.1 bouyer #include <dev/acpi/acpivar.h>
39 1.1 bouyer #include <dev/acpi/acpi_i2c.h>
40 1.9 jmcneill #include <dev/i2c/i2cvar.h>
41 1.1 bouyer
42 1.11 thorpej #include <sys/kmem.h>
43 1.11 thorpej
44 1.2 bouyer #define _COMPONENT ACPI_BUS_COMPONENT
45 1.2 bouyer ACPI_MODULE_NAME ("acpi_i2c")
46 1.2 bouyer
47 1.13 jmcneill static const struct device_compatible_entry hid_compat_data[] = {
48 1.13 jmcneill { .compat = "PNP0C50" },
49 1.13 jmcneill DEVICE_COMPAT_EOL
50 1.13 jmcneill };
51 1.13 jmcneill
52 1.1 bouyer struct acpi_i2c_context {
53 1.1 bouyer uint16_t i2c_addr;
54 1.13 jmcneill struct acpi_devnode *res_src;
55 1.1 bouyer };
56 1.1 bouyer
57 1.13 jmcneill static struct acpi_devnode *
58 1.13 jmcneill acpi_i2c_resource_find_source(ACPI_RESOURCE_SOURCE *rs)
59 1.13 jmcneill {
60 1.13 jmcneill ACPI_STATUS rv;
61 1.13 jmcneill ACPI_HANDLE hdl;
62 1.13 jmcneill struct acpi_devnode *ad;
63 1.13 jmcneill
64 1.13 jmcneill if (rs->StringPtr == NULL) {
65 1.13 jmcneill return NULL;
66 1.13 jmcneill }
67 1.13 jmcneill
68 1.13 jmcneill rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
69 1.13 jmcneill if (ACPI_FAILURE(rv)) {
70 1.13 jmcneill printf("%s: couldn't lookup '%s': %s\n", __func__,
71 1.13 jmcneill rs->StringPtr, AcpiFormatException(rv));
72 1.13 jmcneill return NULL;
73 1.13 jmcneill }
74 1.13 jmcneill
75 1.13 jmcneill SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
76 1.13 jmcneill if (ad->ad_handle == hdl) {
77 1.13 jmcneill return ad;
78 1.13 jmcneill }
79 1.13 jmcneill }
80 1.13 jmcneill
81 1.13 jmcneill printf("%s: no acpi devnode matching resource source '%s'\n",
82 1.13 jmcneill __func__, rs->StringPtr);
83 1.13 jmcneill return NULL;
84 1.13 jmcneill }
85 1.13 jmcneill
86 1.1 bouyer static ACPI_STATUS
87 1.1 bouyer acpi_i2c_resource_parse_callback(ACPI_RESOURCE *res, void *context)
88 1.1 bouyer {
89 1.1 bouyer struct acpi_i2c_context *i2cc = context;
90 1.1 bouyer
91 1.1 bouyer switch (res->Type) {
92 1.1 bouyer case ACPI_RESOURCE_TYPE_END_TAG:
93 1.1 bouyer break;
94 1.1 bouyer case ACPI_RESOURCE_TYPE_SERIAL_BUS:
95 1.1 bouyer switch (res->Data.I2cSerialBus.Type) {
96 1.1 bouyer case ACPI_RESOURCE_SERIAL_TYPE_I2C:
97 1.1 bouyer i2cc->i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
98 1.13 jmcneill i2cc->res_src = acpi_i2c_resource_find_source(
99 1.13 jmcneill &res->Data.I2cSerialBus.ResourceSource);
100 1.1 bouyer break;
101 1.1 bouyer }
102 1.1 bouyer break;
103 1.1 bouyer case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
104 1.1 bouyer break;
105 1.1 bouyer default:
106 1.8 msaitoh printf("resource type 0x%x ignored\n", res->Type);
107 1.1 bouyer }
108 1.1 bouyer return_ACPI_STATUS(AE_OK);
109 1.1 bouyer }
110 1.1 bouyer
111 1.1 bouyer static void
112 1.1 bouyer acpi_enter_i2c_device(struct acpi_devnode *ad, prop_array_t array)
113 1.1 bouyer {
114 1.1 bouyer prop_dictionary_t dev;
115 1.1 bouyer struct acpi_i2c_context i2cc;
116 1.1 bouyer ACPI_STATUS rv;
117 1.11 thorpej char *clist;
118 1.11 thorpej size_t clist_size;
119 1.1 bouyer
120 1.1 bouyer memset(&i2cc, 0, sizeof(i2cc));
121 1.1 bouyer rv = AcpiWalkResources(ad->ad_handle, "_CRS",
122 1.1 bouyer acpi_i2c_resource_parse_callback, &i2cc);
123 1.1 bouyer if (ACPI_FAILURE(rv)) {
124 1.1 bouyer aprint_error("ACPI: unable to get resources "
125 1.1 bouyer "for %s: %s\n", ad->ad_name,
126 1.1 bouyer AcpiFormatException(rv));
127 1.1 bouyer return;
128 1.1 bouyer }
129 1.1 bouyer if (i2cc.i2c_addr == 0)
130 1.1 bouyer return;
131 1.1 bouyer dev = prop_dictionary_create();
132 1.1 bouyer if (dev == NULL) {
133 1.1 bouyer aprint_error("ignoring device %s (no memory)\n",
134 1.1 bouyer ad->ad_name);
135 1.1 bouyer return;
136 1.1 bouyer }
137 1.12 thorpej clist = acpi_pack_compat_list(ad, &clist_size);
138 1.11 thorpej if (clist == NULL) {
139 1.11 thorpej prop_object_release(dev);
140 1.11 thorpej aprint_error("ignoring device %s (no _HID or _CID)\n",
141 1.11 thorpej ad->ad_name);
142 1.11 thorpej return;
143 1.11 thorpej }
144 1.12 thorpej prop_dictionary_set_string(dev, "name", ad->ad_name);
145 1.1 bouyer prop_dictionary_set_uint32(dev, "addr", i2cc.i2c_addr);
146 1.1 bouyer prop_dictionary_set_uint64(dev, "cookie", (uintptr_t)ad->ad_handle);
147 1.9 jmcneill prop_dictionary_set_uint32(dev, "cookietype", I2C_COOKIE_ACPI);
148 1.11 thorpej prop_dictionary_set_data(dev, "compatible", clist, clist_size);
149 1.11 thorpej kmem_free(clist, clist_size);
150 1.11 thorpej
151 1.1 bouyer prop_array_add(array, dev);
152 1.1 bouyer prop_object_release(dev);
153 1.1 bouyer }
154 1.1 bouyer
155 1.13 jmcneill static void
156 1.13 jmcneill acpi_enter_i2chid_devs(device_t dev, struct acpi_devnode *devnode,
157 1.13 jmcneill prop_array_t array)
158 1.13 jmcneill {
159 1.13 jmcneill struct acpi_devnode *ad;
160 1.13 jmcneill
161 1.13 jmcneill KASSERT(dev != NULL);
162 1.13 jmcneill
163 1.13 jmcneill SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
164 1.13 jmcneill struct acpi_attach_args aa = {
165 1.13 jmcneill .aa_node = ad
166 1.13 jmcneill };
167 1.13 jmcneill struct acpi_i2c_context i2cc;
168 1.13 jmcneill ACPI_STATUS rv;
169 1.13 jmcneill
170 1.13 jmcneill if (!acpi_device_present(ad->ad_handle))
171 1.13 jmcneill continue;
172 1.13 jmcneill if (ad->ad_device != NULL)
173 1.13 jmcneill continue;
174 1.13 jmcneill if (acpi_compatible_match(&aa, hid_compat_data) == 0)
175 1.13 jmcneill continue;
176 1.13 jmcneill
177 1.13 jmcneill memset(&i2cc, 0, sizeof(i2cc));
178 1.13 jmcneill rv = AcpiWalkResources(ad->ad_handle, "_CRS",
179 1.13 jmcneill acpi_i2c_resource_parse_callback, &i2cc);
180 1.13 jmcneill if (ACPI_SUCCESS(rv) &&
181 1.13 jmcneill i2cc.i2c_addr != 0 &&
182 1.13 jmcneill i2cc.res_src == devnode) {
183 1.13 jmcneill aprint_debug_dev(dev, "claiming %s\n", ad->ad_name);
184 1.13 jmcneill acpi_enter_i2c_device(ad, array);
185 1.13 jmcneill }
186 1.13 jmcneill }
187 1.13 jmcneill }
188 1.13 jmcneill
189 1.1 bouyer prop_array_t
190 1.10 jmcneill acpi_enter_i2c_devs(device_t dev, struct acpi_devnode *devnode)
191 1.1 bouyer {
192 1.1 bouyer struct acpi_devnode *ad;
193 1.1 bouyer prop_array_t array = prop_array_create();
194 1.1 bouyer
195 1.1 bouyer if (array == NULL)
196 1.1 bouyer return NULL;
197 1.1 bouyer
198 1.1 bouyer SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
199 1.1 bouyer if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE)
200 1.1 bouyer continue;
201 1.4 christos if (!acpi_device_present(ad->ad_handle))
202 1.4 christos continue;
203 1.1 bouyer acpi_enter_i2c_device(ad, array);
204 1.1 bouyer }
205 1.10 jmcneill
206 1.10 jmcneill if (dev != NULL) {
207 1.10 jmcneill acpi_claim_childdevs(dev, devnode);
208 1.13 jmcneill acpi_enter_i2chid_devs(dev, devnode, array);
209 1.10 jmcneill }
210 1.10 jmcneill
211 1.1 bouyer return array;
212 1.1 bouyer }
213