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 23 enum inv_mpu_product_name { 24 INV_MPU_NOT_MATCHED, 25 INV_MPU_ASUS_T100TA, 26 }; 27 28 static enum inv_mpu_product_name matched_product_name; 29 30 static 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 37 static 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 51 static 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 int ret; 60 61 status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer); 62 if (ACPI_FAILURE(status)) 63 return -ENODEV; 64 65 cpm = buffer.pointer; 66 for (i = 0; i < cpm->package.count; ++i) { 67 union acpi_object *elem; 68 int j; 69 70 elem = &cpm->package.elements[i]; 71 for (j = 0; j < elem->package.count; ++j) { 72 union acpi_object *sub_elem; 73 74 sub_elem = &elem->package.elements[j]; 75 if (sub_elem->type == ACPI_TYPE_STRING) 76 strlcpy(info->type, sub_elem->string.pointer, 77 sizeof(info->type)); 78 else if (sub_elem->type == ACPI_TYPE_INTEGER) { 79 if (sub_elem->integer.value != client->addr) { 80 info->addr = sub_elem->integer.value; 81 break; /* Not a MPU6500 primary */ 82 } 83 } 84 } 85 } 86 ret = cpm->package.count; 87 kfree(buffer.pointer); 88 89 return ret; 90 } 91 92 static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data) 93 { 94 struct acpi_resource_i2c_serialbus *sb; 95 u32 *addr = data; 96 97 if (i2c_acpi_get_i2c_resource(ares, &sb)) { 98 if (*addr) 99 *addr |= (sb->slave_address << 16); 100 else 101 *addr = sb->slave_address; 102 } 103 104 /* Tell the ACPI core that we already copied this address */ 105 return 1; 106 } 107 108 static int inv_mpu_process_acpi_config(struct i2c_client *client, 109 unsigned short *primary_addr, 110 unsigned short *secondary_addr) 111 { 112 const struct acpi_device_id *id; 113 struct acpi_device *adev; 114 u32 i2c_addr = 0; 115 LIST_HEAD(resources); 116 int ret; 117 118 id = acpi_match_device(client->dev.driver->acpi_match_table, 119 &client->dev); 120 if (!id) 121 return -ENODEV; 122 123 adev = ACPI_COMPANION(&client->dev); 124 if (!adev) 125 return -ENODEV; 126 127 ret = acpi_dev_get_resources(adev, &resources, 128 acpi_i2c_check_resource, &i2c_addr); 129 if (ret < 0) 130 return ret; 131 132 acpi_dev_free_resource_list(&resources); 133 *primary_addr = i2c_addr & 0x0000ffff; 134 *secondary_addr = (i2c_addr & 0xffff0000) >> 16; 135 136 return 0; 137 } 138 139 int inv_mpu_acpi_create_mux_client(struct i2c_client *client) 140 { 141 struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); 142 143 st->mux_client = NULL; 144 if (ACPI_HANDLE(&client->dev)) { 145 struct i2c_board_info info; 146 struct acpi_device *adev; 147 int ret = -1; 148 149 adev = ACPI_COMPANION(&client->dev); 150 memset(&info, 0, sizeof(info)); 151 152 dmi_check_system(inv_mpu_dev_list); 153 switch (matched_product_name) { 154 case INV_MPU_ASUS_T100TA: 155 ret = asus_acpi_get_sensor_info(adev, client, 156 &info); 157 break; 158 /* Add more matched product processing here */ 159 default: 160 break; 161 } 162 163 if (ret < 0) { 164 /* No matching DMI, so create device on INV6XX type */ 165 unsigned short primary, secondary; 166 167 ret = inv_mpu_process_acpi_config(client, &primary, 168 &secondary); 169 if (!ret && secondary) { 170 char *name; 171 172 info.addr = secondary; 173 strlcpy(info.type, dev_name(&adev->dev), 174 sizeof(info.type)); 175 name = strchr(info.type, ':'); 176 if (name) 177 *name = '\0'; 178 strlcat(info.type, "-client", 179 sizeof(info.type)); 180 } else 181 return 0; /* no secondary addr, which is OK */ 182 } 183 st->mux_client = i2c_new_device(st->muxc->adapter[0], &info); 184 if (!st->mux_client) 185 return -ENODEV; 186 } 187 188 return 0; 189 } 190 191 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client) 192 { 193 struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); 194 195 i2c_unregister_device(st->mux_client); 196 } 197 #else 198 199 #include "inv_mpu_iio.h" 200 201 int inv_mpu_acpi_create_mux_client(struct i2c_client *client) 202 { 203 return 0; 204 } 205 206 void inv_mpu_acpi_delete_mux_client(struct i2c_client *client) 207 { 208 } 209 #endif 210