Merge branches 'acpi-processor', 'acpi-cppc', 'acpi-apei' and 'acpi-sleep'
[linux-2.6-block.git] / drivers / iio / imu / inv_mpu6050 / inv_mpu_acpi.c
CommitLineData
a35c5d1a
SP
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
23enum inv_mpu_product_name {
24 INV_MPU_NOT_MATCHED,
25 INV_MPU_ASUS_T100TA,
26};
27
28static enum inv_mpu_product_name matched_product_name;
29
30static 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
37static 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
51static 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;
6e85dbe4 59 int ret;
a35c5d1a
SP
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
0e791375 70 elem = &cpm->package.elements[i];
a35c5d1a
SP
71 for (j = 0; j < elem->package.count; ++j) {
72 union acpi_object *sub_elem;
73
0e791375 74 sub_elem = &elem->package.elements[j];
a35c5d1a
SP
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 }
6e85dbe4 86 ret = cpm->package.count;
a35c5d1a
SP
87 kfree(buffer.pointer);
88
6e85dbe4 89 return ret;
a35c5d1a
SP
90}
91
92static 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
112static 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
b3eea8da 143int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
a35c5d1a 144{
b3eea8da 145 struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
a35c5d1a
SP
146
147 st->mux_client = NULL;
b3eea8da 148 if (ACPI_HANDLE(&client->dev)) {
a35c5d1a
SP
149 struct i2c_board_info info;
150 struct acpi_device *adev;
151 int ret = -1;
152
b3eea8da 153 adev = ACPI_COMPANION(&client->dev);
a35c5d1a
SP
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:
b3eea8da 159 ret = asus_acpi_get_sensor_info(adev, client,
a35c5d1a
SP
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
b3eea8da 171 ret = inv_mpu_process_acpi_config(client, &primary,
a35c5d1a
SP
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 }
51f97f6d 187 st->mux_client = i2c_new_device(st->muxc->adapter[0], &info);
a35c5d1a
SP
188 if (!st->mux_client)
189 return -ENODEV;
a35c5d1a
SP
190 }
191
192 return 0;
193}
194
b3eea8da 195void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
a35c5d1a 196{
b3eea8da 197 struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
d92241a0 198
a35c5d1a
SP
199 if (st->mux_client)
200 i2c_unregister_device(st->mux_client);
201}
202#else
203
204#include "inv_mpu_iio.h"
205
b3eea8da 206int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
a35c5d1a
SP
207{
208 return 0;
209}
210
b3eea8da 211void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
a35c5d1a
SP
212{
213}
214#endif