xref: /linux/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c (revision 2eff01ee2881becc9daaa0d53477ec202136b1f4)
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 	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 
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 
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 
189 int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
190 {
191 	return 0;
192 }
193 
194 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
195 {
196 }
197 #endif
198