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