forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
4.5 KiB
199 lines
4.5 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* inv_mpu_acpi: ACPI processing for creating client devices |
|
* Copyright (c) 2015, Intel Corporation. |
|
*/ |
|
|
|
#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; |
|
int ret; |
|
|
|
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 */ |
|
} |
|
} |
|
} |
|
} |
|
ret = cpm->package.count; |
|
kfree(buffer.pointer); |
|
|
|
return ret; |
|
} |
|
|
|
static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data) |
|
{ |
|
struct acpi_resource_i2c_serialbus *sb; |
|
u32 *addr = data; |
|
|
|
if (i2c_acpi_get_i2c_resource(ares, &sb)) { |
|
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) |
|
{ |
|
struct acpi_device *adev = ACPI_COMPANION(&client->dev); |
|
const struct acpi_device_id *id; |
|
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; |
|
|
|
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 i2c_client *client) |
|
{ |
|
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); |
|
|
|
st->mux_client = NULL; |
|
if (ACPI_HANDLE(&client->dev)) { |
|
struct i2c_board_info info; |
|
struct i2c_client *mux_client; |
|
struct acpi_device *adev; |
|
int ret = -1; |
|
|
|
adev = ACPI_COMPANION(&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, 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(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 */ |
|
} |
|
mux_client = i2c_new_client_device(st->muxc->adapter[0], &info); |
|
if (IS_ERR(mux_client)) |
|
return PTR_ERR(mux_client); |
|
st->mux_client = mux_client; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client) |
|
{ |
|
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); |
|
|
|
i2c_unregister_device(st->mux_client); |
|
} |
|
#else |
|
|
|
#include "inv_mpu_iio.h" |
|
|
|
int inv_mpu_acpi_create_mux_client(struct i2c_client *client) |
|
{ |
|
return 0; |
|
} |
|
|
|
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client) |
|
{ |
|
} |
|
#endif
|
|
|