199 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0-only
 | 
						|
/*
 | 
						|
 * inv_mpu_acpi: ACPI processing for creating client devices
 | 
						|
 * Copyright (c) 2015, Intel Corporation.
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef CONFIG_ACPI
 | 
						|
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/i2c.h>
 | 
						|
#include <linux/dmi.h>
 | 
						|
#include <linux/acpi.h>
 | 
						|
#include "inv_mpu_iio.h"
 | 
						|
 | 
						|
enum inv_mpu_product_name {
 | 
						|
	INV_MPU_NOT_MATCHED,
 | 
						|
	INV_MPU_ASUS_T100TA,
 | 
						|
};
 | 
						|
 | 
						|
static enum inv_mpu_product_name matched_product_name;
 | 
						|
 | 
						|
static int __init asus_t100_matched(const struct dmi_system_id *d)
 | 
						|
{
 | 
						|
	matched_product_name = INV_MPU_ASUS_T100TA;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static const struct dmi_system_id inv_mpu_dev_list[] = {
 | 
						|
	{
 | 
						|
	.callback = asus_t100_matched,
 | 
						|
	.ident = "Asus Transformer Book T100",
 | 
						|
		.matches = {
 | 
						|
			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"),
 | 
						|
			DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
 | 
						|
			DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
 | 
						|
		},
 | 
						|
	},
 | 
						|
	/* Add more matching tables here..*/
 | 
						|
	{}
 | 
						|
};
 | 
						|
 | 
						|
static int asus_acpi_get_sensor_info(struct acpi_device *adev,
 | 
						|
				     struct i2c_client *client,
 | 
						|
				     struct i2c_board_info *info)
 | 
						|
{
 | 
						|
	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
 | 
						|
	int i;
 | 
						|
	acpi_status status;
 | 
						|
	union acpi_object *cpm;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
 | 
						|
	if (ACPI_FAILURE(status))
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	cpm = buffer.pointer;
 | 
						|
	for (i = 0; i < cpm->package.count; ++i) {
 | 
						|
		union acpi_object *elem;
 | 
						|
		int j;
 | 
						|
 | 
						|
		elem = &cpm->package.elements[i];
 | 
						|
		for (j = 0; j < elem->package.count; ++j) {
 | 
						|
			union acpi_object *sub_elem;
 | 
						|
 | 
						|
			sub_elem = &elem->package.elements[j];
 | 
						|
			if (sub_elem->type == ACPI_TYPE_STRING)
 | 
						|
				strscpy(info->type, sub_elem->string.pointer,
 | 
						|
					sizeof(info->type));
 | 
						|
			else if (sub_elem->type == ACPI_TYPE_INTEGER) {
 | 
						|
				if (sub_elem->integer.value != client->addr) {
 | 
						|
					info->addr = sub_elem->integer.value;
 | 
						|
					break; /* Not a MPU6500 primary */
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	ret = cpm->package.count;
 | 
						|
	kfree(buffer.pointer);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
 | 
						|
{
 | 
						|
	struct acpi_resource_i2c_serialbus *sb;
 | 
						|
	u32 *addr = data;
 | 
						|
 | 
						|
	if (i2c_acpi_get_i2c_resource(ares, &sb)) {
 | 
						|
		if (*addr)
 | 
						|
			*addr |= (sb->slave_address << 16);
 | 
						|
		else
 | 
						|
			*addr = sb->slave_address;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Tell the ACPI core that we already copied this address */
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int inv_mpu_process_acpi_config(struct i2c_client *client,
 | 
						|
				       unsigned short *primary_addr,
 | 
						|
				       unsigned short *secondary_addr)
 | 
						|
{
 | 
						|
	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
 | 
						|
	const struct acpi_device_id *id;
 | 
						|
	u32 i2c_addr = 0;
 | 
						|
	LIST_HEAD(resources);
 | 
						|
	int ret;
 | 
						|
 | 
						|
	id = acpi_match_device(client->dev.driver->acpi_match_table,
 | 
						|
			       &client->dev);
 | 
						|
	if (!id)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	ret = acpi_dev_get_resources(adev, &resources,
 | 
						|
				     acpi_i2c_check_resource, &i2c_addr);
 | 
						|
	if (ret < 0)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	acpi_dev_free_resource_list(&resources);
 | 
						|
	*primary_addr = i2c_addr & 0x0000ffff;
 | 
						|
	*secondary_addr = (i2c_addr & 0xffff0000) >> 16;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
 | 
						|
{
 | 
						|
	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
 | 
						|
	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
 | 
						|
 | 
						|
	st->mux_client = NULL;
 | 
						|
	if (adev) {
 | 
						|
		struct i2c_board_info info;
 | 
						|
		struct i2c_client *mux_client;
 | 
						|
		int ret = -1;
 | 
						|
 | 
						|
		memset(&info, 0, sizeof(info));
 | 
						|
 | 
						|
		dmi_check_system(inv_mpu_dev_list);
 | 
						|
		switch (matched_product_name) {
 | 
						|
		case INV_MPU_ASUS_T100TA:
 | 
						|
			ret = asus_acpi_get_sensor_info(adev, client,
 | 
						|
							&info);
 | 
						|
			break;
 | 
						|
		/* Add more matched product processing here */
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		if (ret < 0) {
 | 
						|
			/* No matching DMI, so create device on INV6XX type */
 | 
						|
			unsigned short primary, secondary;
 | 
						|
 | 
						|
			ret = inv_mpu_process_acpi_config(client, &primary,
 | 
						|
							  &secondary);
 | 
						|
			if (!ret && secondary) {
 | 
						|
				char *name;
 | 
						|
 | 
						|
				info.addr = secondary;
 | 
						|
				strscpy(info.type, dev_name(&adev->dev),
 | 
						|
					sizeof(info.type));
 | 
						|
				name = strchr(info.type, ':');
 | 
						|
				if (name)
 | 
						|
					*name = '\0';
 | 
						|
				strlcat(info.type, "-client",
 | 
						|
					sizeof(info.type));
 | 
						|
			} else
 | 
						|
				return 0; /* no secondary addr, which is OK */
 | 
						|
		}
 | 
						|
		mux_client = i2c_new_client_device(st->muxc->adapter[0], &info);
 | 
						|
		if (IS_ERR(mux_client))
 | 
						|
			return PTR_ERR(mux_client);
 | 
						|
		st->mux_client = mux_client;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
 | 
						|
{
 | 
						|
	struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
 | 
						|
 | 
						|
	i2c_unregister_device(st->mux_client);
 | 
						|
}
 | 
						|
#else
 | 
						|
 | 
						|
#include "inv_mpu_iio.h"
 | 
						|
 | 
						|
int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
 | 
						|
{
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
 | 
						|
{
 | 
						|
}
 | 
						|
#endif
 |