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