1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * inv_mpu_acpi: ACPI processing for creating client devices
4 * Copyright (c) 2015, Intel Corporation.
5 */
6
7 #ifdef CONFIG_ACPI
8
9 #include <linux/kernel.h>
10 #include <linux/i2c.h>
11 #include <linux/dmi.h>
12 #include <linux/acpi.h>
13 #include <linux/wordpart.h>
14
15 #include "inv_mpu_iio.h"
16
17 enum inv_mpu_product_name {
18 INV_MPU_NOT_MATCHED,
19 INV_MPU_ASUS_T100TA,
20 };
21
22 static enum inv_mpu_product_name matched_product_name;
23
asus_t100_matched(const struct dmi_system_id * d)24 static int __init asus_t100_matched(const struct dmi_system_id *d)
25 {
26 matched_product_name = INV_MPU_ASUS_T100TA;
27
28 return 0;
29 }
30
31 static const struct dmi_system_id inv_mpu_dev_list[] = {
32 {
33 .callback = asus_t100_matched,
34 .ident = "Asus Transformer Book T100",
35 .matches = {
36 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"),
37 DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
38 DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
39 },
40 },
41 /* Add more matching tables here..*/
42 {}
43 };
44
asus_acpi_get_sensor_info(struct acpi_device * adev,struct i2c_client * client,struct i2c_board_info * info)45 static int asus_acpi_get_sensor_info(struct acpi_device *adev,
46 struct i2c_client *client,
47 struct i2c_board_info *info)
48 {
49 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
50 int i;
51 acpi_status status;
52 union acpi_object *cpm;
53 int ret;
54
55 status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
56 if (ACPI_FAILURE(status))
57 return -ENODEV;
58
59 cpm = buffer.pointer;
60 for (i = 0; i < cpm->package.count; ++i) {
61 union acpi_object *elem;
62 int j;
63
64 elem = &cpm->package.elements[i];
65 for (j = 0; j < elem->package.count; ++j) {
66 union acpi_object *sub_elem;
67
68 sub_elem = &elem->package.elements[j];
69 if (sub_elem->type == ACPI_TYPE_STRING)
70 strscpy(info->type, sub_elem->string.pointer,
71 sizeof(info->type));
72 else if (sub_elem->type == ACPI_TYPE_INTEGER) {
73 if (sub_elem->integer.value != client->addr) {
74 info->addr = sub_elem->integer.value;
75 break; /* Not a MPU6500 primary */
76 }
77 }
78 }
79 }
80 ret = cpm->package.count;
81 kfree(buffer.pointer);
82
83 return ret;
84 }
85
acpi_i2c_check_resource(struct acpi_resource * ares,void * data)86 static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
87 {
88 struct acpi_resource_i2c_serialbus *sb;
89 u32 *addr = data;
90
91 if (i2c_acpi_get_i2c_resource(ares, &sb)) {
92 if (*addr)
93 *addr |= (sb->slave_address << 16);
94 else
95 *addr = sb->slave_address;
96 }
97
98 /* Tell the ACPI core that we already copied this address */
99 return 1;
100 }
101
inv_mpu_process_acpi_config(struct i2c_client * client,unsigned short * primary_addr,unsigned short * secondary_addr)102 static int inv_mpu_process_acpi_config(struct i2c_client *client,
103 unsigned short *primary_addr,
104 unsigned short *secondary_addr)
105 {
106 struct acpi_device *adev = ACPI_COMPANION(&client->dev);
107 u32 i2c_addr = 0;
108 LIST_HEAD(resources);
109 int ret;
110
111 if (!is_acpi_device_node(dev_fwnode(&client->dev)))
112 return -ENODEV;
113
114 ret = acpi_dev_get_resources(adev, &resources,
115 acpi_i2c_check_resource, &i2c_addr);
116 if (ret < 0)
117 return ret;
118
119 acpi_dev_free_resource_list(&resources);
120 *primary_addr = lower_16_bits(i2c_addr);
121 *secondary_addr = upper_16_bits(i2c_addr);
122
123 return 0;
124 }
125
inv_mpu_acpi_create_mux_client(struct i2c_client * client)126 int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
127 {
128 struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
129 struct acpi_device *adev = ACPI_COMPANION(&client->dev);
130
131 st->mux_client = NULL;
132 if (adev) {
133 struct i2c_board_info info;
134 struct i2c_client *mux_client;
135 int ret = -1;
136
137 memset(&info, 0, sizeof(info));
138
139 dmi_check_system(inv_mpu_dev_list);
140 switch (matched_product_name) {
141 case INV_MPU_ASUS_T100TA:
142 ret = asus_acpi_get_sensor_info(adev, client,
143 &info);
144 break;
145 /* Add more matched product processing here */
146 default:
147 break;
148 }
149
150 if (ret < 0) {
151 /* No matching DMI, so create device on INV6XX type */
152 unsigned short primary, secondary;
153
154 ret = inv_mpu_process_acpi_config(client, &primary,
155 &secondary);
156 if (!ret && secondary) {
157 char *name;
158
159 info.addr = secondary;
160 strscpy(info.type, dev_name(&adev->dev),
161 sizeof(info.type));
162 name = strchr(info.type, ':');
163 if (name)
164 *name = '\0';
165 strlcat(info.type, "-client",
166 sizeof(info.type));
167 } else
168 return 0; /* no secondary addr, which is OK */
169 }
170 mux_client = i2c_new_client_device(st->muxc->adapter[0], &info);
171 if (IS_ERR(mux_client))
172 return PTR_ERR(mux_client);
173 st->mux_client = mux_client;
174 }
175
176 return 0;
177 }
178
inv_mpu_acpi_delete_mux_client(struct i2c_client * client)179 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
180 {
181 struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
182
183 i2c_unregister_device(st->mux_client);
184 }
185 #else
186
187 #include "inv_mpu_iio.h"
188
inv_mpu_acpi_create_mux_client(struct i2c_client * client)189 int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
190 {
191 return 0;
192 }
193
inv_mpu_acpi_delete_mux_client(struct i2c_client * client)194 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
195 {
196 }
197 #endif
198