Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / iio / imu / inv_mpu6050 / inv_mpu_acpi.c
diff --git a/kernel/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/kernel/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
new file mode 100644 (file)
index 0000000..1c982a5
--- /dev/null
@@ -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