|
@@ -0,0 +1,211 @@
|
|
|
|
+/*
|
|
|
|
+ * inv_mpu_acpi: ACPI processing for creating client devices
|
|
|
|
+ * Copyright (c) 2015, Intel Corporation.
|
|
|
|
+ *
|
|
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
|
|
+ * under the terms and conditions of the GNU General Public License,
|
|
|
|
+ * version 2, as published by the Free Software Foundation.
|
|
|
|
+ *
|
|
|
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
+ * more details.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#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;
|
|
|
|
+
|
|
|
|
+ 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)
|
|
|
|
+ strlcpy(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 */
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ kfree(buffer.pointer);
|
|
|
|
+
|
|
|
|
+ return cpm->package.count;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
|
|
|
|
+{
|
|
|
|
+ u32 *addr = data;
|
|
|
|
+
|
|
|
|
+ if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
|
|
|
+ struct acpi_resource_i2c_serialbus *sb;
|
|
|
|
+
|
|
|
|
+ sb = &ares->data.i2c_serial_bus;
|
|
|
|
+ if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
|
|
|
+ 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)
|
|
|
|
+{
|
|
|
|
+ const struct acpi_device_id *id;
|
|
|
|
+ struct acpi_device *adev;
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ adev = ACPI_COMPANION(&client->dev);
|
|
|
|
+ if (!adev)
|
|
|
|
+ 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 inv_mpu6050_state *st)
|
|
|
|
+{
|
|
|
|
+
|
|
|
|
+ st->mux_client = NULL;
|
|
|
|
+ if (ACPI_HANDLE(&st->client->dev)) {
|
|
|
|
+ struct i2c_board_info info;
|
|
|
|
+ struct acpi_device *adev;
|
|
|
|
+ int ret = -1;
|
|
|
|
+
|
|
|
|
+ adev = ACPI_COMPANION(&st->client->dev);
|
|
|
|
+ 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, st->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(st->client, &primary,
|
|
|
|
+ &secondary);
|
|
|
|
+ if (!ret && secondary) {
|
|
|
|
+ char *name;
|
|
|
|
+
|
|
|
|
+ info.addr = secondary;
|
|
|
|
+ strlcpy(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 */
|
|
|
|
+ }
|
|
|
|
+ st->mux_client = i2c_new_device(st->mux_adapter, &info);
|
|
|
|
+ if (!st->mux_client)
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
|
|
|
|
+{
|
|
|
|
+ if (st->mux_client)
|
|
|
|
+ i2c_unregister_device(st->mux_client);
|
|
|
|
+}
|
|
|
|
+#else
|
|
|
|
+
|
|
|
|
+#include "inv_mpu_iio.h"
|
|
|
|
+
|
|
|
|
+int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+#endif
|