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