]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
Merge branch 'for-4.8/core' of git://git.kernel.dk/linux-block
[karo-tx-linux.git] / drivers / iio / imu / inv_mpu6050 / inv_mpu_acpi.c
1 /*
2  * inv_mpu_acpi: ACPI processing for creating client devices
3  * Copyright (c) 2015, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  */
14
15 #ifdef CONFIG_ACPI
16
17 #include <linux/kernel.h>
18 #include <linux/i2c.h>
19 #include <linux/dmi.h>
20 #include <linux/acpi.h>
21 #include "inv_mpu_iio.h"
22
23 enum inv_mpu_product_name {
24         INV_MPU_NOT_MATCHED,
25         INV_MPU_ASUS_T100TA,
26 };
27
28 static enum inv_mpu_product_name matched_product_name;
29
30 static int __init asus_t100_matched(const struct dmi_system_id *d)
31 {
32         matched_product_name = INV_MPU_ASUS_T100TA;
33
34         return 0;
35 }
36
37 static const struct dmi_system_id inv_mpu_dev_list[] = {
38         {
39         .callback = asus_t100_matched,
40         .ident = "Asus Transformer Book T100",
41                 .matches = {
42                         DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"),
43                         DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
44                         DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
45                 },
46         },
47         /* Add more matching tables here..*/
48         {}
49 };
50
51 static int asus_acpi_get_sensor_info(struct acpi_device *adev,
52                                      struct i2c_client *client,
53                                      struct i2c_board_info *info)
54 {
55         struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
56         int i;
57         acpi_status status;
58         union acpi_object *cpm;
59         int ret;
60
61         status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
62         if (ACPI_FAILURE(status))
63                 return -ENODEV;
64
65         cpm = buffer.pointer;
66         for (i = 0; i < cpm->package.count; ++i) {
67                 union acpi_object *elem;
68                 int j;
69
70                 elem = &cpm->package.elements[i];
71                 for (j = 0; j < elem->package.count; ++j) {
72                         union acpi_object *sub_elem;
73
74                         sub_elem = &elem->package.elements[j];
75                         if (sub_elem->type == ACPI_TYPE_STRING)
76                                 strlcpy(info->type, sub_elem->string.pointer,
77                                         sizeof(info->type));
78                         else if (sub_elem->type == ACPI_TYPE_INTEGER) {
79                                 if (sub_elem->integer.value != client->addr) {
80                                         info->addr = sub_elem->integer.value;
81                                         break; /* Not a MPU6500 primary */
82                                 }
83                         }
84                 }
85         }
86         ret = cpm->package.count;
87         kfree(buffer.pointer);
88
89         return ret;
90 }
91
92 static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
93 {
94         u32 *addr = data;
95
96         if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
97                 struct acpi_resource_i2c_serialbus *sb;
98
99                 sb = &ares->data.i2c_serial_bus;
100                 if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
101                         if (*addr)
102                                 *addr |= (sb->slave_address << 16);
103                         else
104                                 *addr = sb->slave_address;
105                 }
106         }
107
108         /* Tell the ACPI core that we already copied this address */
109         return 1;
110 }
111
112 static int inv_mpu_process_acpi_config(struct i2c_client *client,
113                                        unsigned short *primary_addr,
114                                        unsigned short *secondary_addr)
115 {
116         const struct acpi_device_id *id;
117         struct acpi_device *adev;
118         u32 i2c_addr = 0;
119         LIST_HEAD(resources);
120         int ret;
121
122         id = acpi_match_device(client->dev.driver->acpi_match_table,
123                                &client->dev);
124         if (!id)
125                 return -ENODEV;
126
127         adev = ACPI_COMPANION(&client->dev);
128         if (!adev)
129                 return -ENODEV;
130
131         ret = acpi_dev_get_resources(adev, &resources,
132                                      acpi_i2c_check_resource, &i2c_addr);
133         if (ret < 0)
134                 return ret;
135
136         acpi_dev_free_resource_list(&resources);
137         *primary_addr = i2c_addr & 0x0000ffff;
138         *secondary_addr = (i2c_addr & 0xffff0000) >> 16;
139
140         return 0;
141 }
142
143 int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
144 {
145         struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
146
147         st->mux_client = NULL;
148         if (ACPI_HANDLE(&client->dev)) {
149                 struct i2c_board_info info;
150                 struct acpi_device *adev;
151                 int ret = -1;
152
153                 adev = ACPI_COMPANION(&client->dev);
154                 memset(&info, 0, sizeof(info));
155
156                 dmi_check_system(inv_mpu_dev_list);
157                 switch (matched_product_name) {
158                 case INV_MPU_ASUS_T100TA:
159                         ret = asus_acpi_get_sensor_info(adev, client,
160                                                         &info);
161                         break;
162                 /* Add more matched product processing here */
163                 default:
164                         break;
165                 }
166
167                 if (ret < 0) {
168                         /* No matching DMI, so create device on INV6XX type */
169                         unsigned short primary, secondary;
170
171                         ret = inv_mpu_process_acpi_config(client, &primary,
172                                                           &secondary);
173                         if (!ret && secondary) {
174                                 char *name;
175
176                                 info.addr = secondary;
177                                 strlcpy(info.type, dev_name(&adev->dev),
178                                         sizeof(info.type));
179                                 name = strchr(info.type, ':');
180                                 if (name)
181                                         *name = '\0';
182                                 strlcat(info.type, "-client",
183                                         sizeof(info.type));
184                         } else
185                                 return 0; /* no secondary addr, which is OK */
186                 }
187                 st->mux_client = i2c_new_device(st->muxc->adapter[0], &info);
188                 if (!st->mux_client)
189                         return -ENODEV;
190         }
191
192         return 0;
193 }
194
195 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
196 {
197         struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
198
199         if (st->mux_client)
200                 i2c_unregister_device(st->mux_client);
201 }
202 #else
203
204 #include "inv_mpu_iio.h"
205
206 int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
207 {
208         return 0;
209 }
210
211 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
212 {
213 }
214 #endif