File Index
Files and directories
Sign in
FILE

inv_mpu_acpi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c C
  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
 17enum inv_mpu_product_name {
 18	INV_MPU_NOT_MATCHED,
 19	INV_MPU_ASUS_T100TA,
 20};
 21
 22static enum inv_mpu_product_name matched_product_name;
 23
 24static 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
 31static 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
 45static 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
 86static 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
102static 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
126int 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		dmi_check_system(inv_mpu_dev_list);
138		switch (matched_product_name) {
139		case INV_MPU_ASUS_T100TA:
140			ret = asus_acpi_get_sensor_info(adev, client,
141							&info);
142			break;
143		/* Add more matched product processing here */
144		default:
145			break;
146		}
147
148		if (ret < 0) {
149			/* No matching DMI, so create device on INV6XX type */
150			unsigned short primary, secondary;
151
152			ret = inv_mpu_process_acpi_config(client, &primary,
153							  &secondary);
154			if (!ret && secondary) {
155				char *name;
156
157				info.addr = secondary;
158				strscpy(info.type, dev_name(&adev->dev),
159					sizeof(info.type));
160				name = strchr(info.type, ':');
161				if (name)
162					*name = '\0';
163				strlcat(info.type, "-client",
164					sizeof(info.type));
165			} else
166				return 0; /* no secondary addr, which is OK */
167		}
168		mux_client = i2c_new_client_device(st->muxc->adapter[0], &info);
169		if (IS_ERR(mux_client))
170			return PTR_ERR(mux_client);
171		st->mux_client = mux_client;
172	}
173
174	return 0;
175}
176
177void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
178{
179	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
180
181	i2c_unregister_device(st->mux_client);
182}
183#else
184
185#include "inv_mpu_iio.h"
186
187int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
188{
189	return 0;
190}
191
192void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
193{
194}
195#endif
~/ Read-only file preview.