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