1/* 2 * inv_mpu_acpi: ACPI processing for creating client devices 3 * Copyright (c) 2015, Intel Corporation. 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms and conditions of the GNU General Public License, 7 * version 2, as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 */ 14 15#ifdef CONFIG_ACPI 16 17#include <linux/kernel.h> 18#include <linux/i2c.h> 19#include <linux/dmi.h> 20#include <linux/acpi.h> 21#include "inv_mpu_iio.h" 22 23enum inv_mpu_product_name { 24 INV_MPU_NOT_MATCHED, 25 INV_MPU_ASUS_T100TA, 26}; 27 28static enum inv_mpu_product_name matched_product_name; 29 30static int __init asus_t100_matched(const struct dmi_system_id *d) 31{ 32 matched_product_name = INV_MPU_ASUS_T100TA; 33 34 return 0; 35} 36 37static const struct dmi_system_id inv_mpu_dev_list[] = { 38 { 39 .callback = asus_t100_matched, 40 .ident = "Asus Transformer Book T100", 41 .matches = { 42 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"), 43 DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), 44 DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), 45 }, 46 }, 47 /* Add more matching tables here..*/ 48 {} 49}; 50 51static int asus_acpi_get_sensor_info(struct acpi_device *adev, 52 struct i2c_client *client, 53 struct i2c_board_info *info) 54{ 55 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 56 int i; 57 acpi_status status; 58 union acpi_object *cpm; 59 60 status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer); 61 if (ACPI_FAILURE(status)) 62 return -ENODEV; 63 64 cpm = buffer.pointer; 65 for (i = 0; i < cpm->package.count; ++i) { 66 union acpi_object *elem; 67 int j; 68 69 elem = &(cpm->package.elements[i]); 70 for (j = 0; j < elem->package.count; ++j) { 71 union acpi_object *sub_elem; 72 73 sub_elem = &(elem->package.elements[j]); 74 if (sub_elem->type == ACPI_TYPE_STRING) 75 strlcpy(info->type, sub_elem->string.pointer, 76 sizeof(info->type)); 77 else if (sub_elem->type == ACPI_TYPE_INTEGER) { 78 if (sub_elem->integer.value != client->addr) { 79 info->addr = sub_elem->integer.value; 80 break; /* Not a MPU6500 primary */ 81 } 82 } 83 } 84 } 85 86 kfree(buffer.pointer); 87 88 return cpm->package.count; 89} 90 91static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data) 92{ 93 u32 *addr = data; 94 95 if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { 96 struct acpi_resource_i2c_serialbus *sb; 97 98 sb = &ares->data.i2c_serial_bus; 99 if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { 100 if (*addr) 101 *addr |= (sb->slave_address << 16); 102 else 103 *addr = sb->slave_address; 104 } 105 } 106 107 /* Tell the ACPI core that we already copied this address */ 108 return 1; 109} 110 111static int inv_mpu_process_acpi_config(struct i2c_client *client, 112 unsigned short *primary_addr, 113 unsigned short *secondary_addr) 114{ 115 const struct acpi_device_id *id; 116 struct acpi_device *adev; 117 u32 i2c_addr = 0; 118 LIST_HEAD(resources); 119 int ret; 120 121 id = acpi_match_device(client->dev.driver->acpi_match_table, 122 &client->dev); 123 if (!id) 124 return -ENODEV; 125 126 adev = ACPI_COMPANION(&client->dev); 127 if (!adev) 128 return -ENODEV; 129 130 ret = acpi_dev_get_resources(adev, &resources, 131 acpi_i2c_check_resource, &i2c_addr); 132 if (ret < 0) 133 return ret; 134 135 acpi_dev_free_resource_list(&resources); 136 *primary_addr = i2c_addr & 0x0000ffff; 137 *secondary_addr = (i2c_addr & 0xffff0000) >> 16; 138 139 return 0; 140} 141 142int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) 143{ 144 145 st->mux_client = NULL; 146 if (ACPI_HANDLE(&st->client->dev)) { 147 struct i2c_board_info info; 148 struct acpi_device *adev; 149 int ret = -1; 150 151 adev = ACPI_COMPANION(&st->client->dev); 152 memset(&info, 0, sizeof(info)); 153 154 dmi_check_system(inv_mpu_dev_list); 155 switch (matched_product_name) { 156 case INV_MPU_ASUS_T100TA: 157 ret = asus_acpi_get_sensor_info(adev, st->client, 158 &info); 159 break; 160 /* Add more matched product processing here */ 161 default: 162 break; 163 } 164 165 if (ret < 0) { 166 /* No matching DMI, so create device on INV6XX type */ 167 unsigned short primary, secondary; 168 169 ret = inv_mpu_process_acpi_config(st->client, &primary, 170 &secondary); 171 if (!ret && secondary) { 172 char *name; 173 174 info.addr = secondary; 175 strlcpy(info.type, dev_name(&adev->dev), 176 sizeof(info.type)); 177 name = strchr(info.type, ':'); 178 if (name) 179 *name = '\0'; 180 strlcat(info.type, "-client", 181 sizeof(info.type)); 182 } else 183 return 0; /* no secondary addr, which is OK */ 184 } 185 st->mux_client = i2c_new_device(st->mux_adapter, &info); 186 if (!st->mux_client) 187 return -ENODEV; 188 189 } 190 191 return 0; 192} 193 194void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) 195{ 196 if (st->mux_client) 197 i2c_unregister_device(st->mux_client); 198} 199#else 200 201#include "inv_mpu_iio.h" 202 203int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) 204{ 205 return 0; 206} 207 208void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) 209{ 210} 211#endif 212