xref: /linux/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c (revision 7f15c46a57c31956591f85b713d7e63cccb25556)
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 
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 
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 
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 
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 	const struct acpi_device_id *id;
108 	u32 i2c_addr = 0;
109 	LIST_HEAD(resources);
110 	int ret;
111 
112 	id = acpi_match_device(client->dev.driver->acpi_match_table,
113 			       &client->dev);
114 	if (!id)
115 		return -ENODEV;
116 
117 	ret = acpi_dev_get_resources(adev, &resources,
118 				     acpi_i2c_check_resource, &i2c_addr);
119 	if (ret < 0)
120 		return ret;
121 
122 	acpi_dev_free_resource_list(&resources);
123 	*primary_addr = lower_16_bits(i2c_addr);
124 	*secondary_addr = upper_16_bits(i2c_addr);
125 
126 	return 0;
127 }
128 
129 int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
130 {
131 	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
132 	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
133 
134 	st->mux_client = NULL;
135 	if (adev) {
136 		struct i2c_board_info info;
137 		struct i2c_client *mux_client;
138 		int ret = -1;
139 
140 		memset(&info, 0, sizeof(info));
141 
142 		dmi_check_system(inv_mpu_dev_list);
143 		switch (matched_product_name) {
144 		case INV_MPU_ASUS_T100TA:
145 			ret = asus_acpi_get_sensor_info(adev, client,
146 							&info);
147 			break;
148 		/* Add more matched product processing here */
149 		default:
150 			break;
151 		}
152 
153 		if (ret < 0) {
154 			/* No matching DMI, so create device on INV6XX type */
155 			unsigned short primary, secondary;
156 
157 			ret = inv_mpu_process_acpi_config(client, &primary,
158 							  &secondary);
159 			if (!ret && secondary) {
160 				char *name;
161 
162 				info.addr = secondary;
163 				strscpy(info.type, dev_name(&adev->dev),
164 					sizeof(info.type));
165 				name = strchr(info.type, ':');
166 				if (name)
167 					*name = '\0';
168 				strlcat(info.type, "-client",
169 					sizeof(info.type));
170 			} else
171 				return 0; /* no secondary addr, which is OK */
172 		}
173 		mux_client = i2c_new_client_device(st->muxc->adapter[0], &info);
174 		if (IS_ERR(mux_client))
175 			return PTR_ERR(mux_client);
176 		st->mux_client = mux_client;
177 	}
178 
179 	return 0;
180 }
181 
182 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
183 {
184 	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
185 
186 	i2c_unregister_device(st->mux_client);
187 }
188 #else
189 
190 #include "inv_mpu_iio.h"
191 
192 int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
193 {
194 	return 0;
195 }
196 
197 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
198 {
199 }
200 #endif
201