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
15enum inv_mpu_product_name {
16	INV_MPU_NOT_MATCHED,
17	INV_MPU_ASUS_T100TA,
18};
19
20static enum inv_mpu_product_name matched_product_name;
21
22static 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
29static 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
43static 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				strscpy(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
84static 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
100static 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
127int 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	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
131
132	st->mux_client = NULL;
133	if (adev) {
134		struct i2c_board_info info;
135		struct i2c_client *mux_client;
136		int ret = -1;
137
138		memset(&info, 0, sizeof(info));
139
140		dmi_check_system(inv_mpu_dev_list);
141		switch (matched_product_name) {
142		case INV_MPU_ASUS_T100TA:
143			ret = asus_acpi_get_sensor_info(adev, client,
144							&info);
145			break;
146		/* Add more matched product processing here */
147		default:
148			break;
149		}
150
151		if (ret < 0) {
152			/* No matching DMI, so create device on INV6XX type */
153			unsigned short primary, secondary;
154
155			ret = inv_mpu_process_acpi_config(client, &primary,
156							  &secondary);
157			if (!ret && secondary) {
158				char *name;
159
160				info.addr = secondary;
161				strscpy(info.type, dev_name(&adev->dev),
162					sizeof(info.type));
163				name = strchr(info.type, ':');
164				if (name)
165					*name = '\0';
166				strlcat(info.type, "-client",
167					sizeof(info.type));
168			} else
169				return 0; /* no secondary addr, which is OK */
170		}
171		mux_client = i2c_new_client_device(st->muxc->adapter[0], &info);
172		if (IS_ERR(mux_client))
173			return PTR_ERR(mux_client);
174		st->mux_client = mux_client;
175	}
176
177	return 0;
178}
179
180void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
181{
182	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
183
184	i2c_unregister_device(st->mux_client);
185}
186#else
187
188#include "inv_mpu_iio.h"
189
190int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
191{
192	return 0;
193}
194
195void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
196{
197}
198#endif
199