diff options
Diffstat (limited to 'drivers/iio/imu')
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/Makefile | 2 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c | 211 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 7 | ||||
-rw-r--r-- | drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 3 | ||||
-rw-r--r-- | drivers/iio/imu/kmx61.c | 70 |
5 files changed, 249 insertions, 44 deletions
diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile index 3a677c7..f566f6a 100644 --- a/drivers/iio/imu/inv_mpu6050/Makefile +++ b/drivers/iio/imu/inv_mpu6050/Makefile @@ -3,4 +3,4 @@ # obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o -inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o +inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o inv_mpu_acpi.o diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c new file mode 100644 index 0000000..1c982a5 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -0,0 +1,211 @@ +/* + * inv_mpu_acpi: ACPI processing for creating client devices + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#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; + + 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 */ + } + } + } + } + + kfree(buffer.pointer); + + return cpm->package.count; +} + +static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data) +{ + u32 *addr = data; + + if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + struct acpi_resource_i2c_serialbus *sb; + + sb = &ares->data.i2c_serial_bus; + if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { + 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) +{ + const struct acpi_device_id *id; + struct acpi_device *adev; + 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; + + adev = ACPI_COMPANION(&client->dev); + if (!adev) + 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 inv_mpu6050_state *st) +{ + + st->mux_client = NULL; + if (ACPI_HANDLE(&st->client->dev)) { + struct i2c_board_info info; + struct acpi_device *adev; + int ret = -1; + + adev = ACPI_COMPANION(&st->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, st->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(st->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 */ + } + st->mux_client = i2c_new_device(st->mux_adapter, &info); + if (!st->mux_client) + return -ENODEV; + + } + + return 0; +} + +void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) +{ + if (st->mux_client) + i2c_unregister_device(st->mux_client); +} +#else + +#include "inv_mpu_iio.h" + +int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) +{ + return 0; +} + +void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) +{ +} +#endif diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index ef76afe..17d4bb1 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -829,8 +829,14 @@ static int inv_mpu_probe(struct i2c_client *client, goto out_unreg_device; } + result = inv_mpu_acpi_create_mux_client(st); + if (result) + goto out_del_mux; + return 0; +out_del_mux: + i2c_del_mux_adapter(st->mux_adapter); out_unreg_device: iio_device_unregister(indio_dev); out_remove_trigger: @@ -845,6 +851,7 @@ static int inv_mpu_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct inv_mpu6050_state *st = iio_priv(indio_dev); + inv_mpu_acpi_delete_mux_client(st); i2c_del_mux_adapter(st->mux_adapter); iio_device_unregister(indio_dev); inv_mpu6050_remove_trigger(st); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index aa837de..db0a4a2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -121,6 +121,7 @@ struct inv_mpu6050_state { spinlock_t time_stamp_lock; struct i2c_client *client; struct i2c_adapter *mux_adapter; + struct i2c_client *mux_client; unsigned int powerup_count; struct inv_mpu6050_platform_data plat_data; DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); @@ -251,3 +252,5 @@ int inv_reset_fifo(struct iio_dev *indio_dev); int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask); int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on); +int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st); +void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st); diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index b3a3637..462a010 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -169,19 +169,18 @@ static const u16 kmx61_uscale_table[] = {9582, 19163, 38326}; static const struct { int val; int val2; - u8 odr_bits; -} kmx61_samp_freq_table[] = { {12, 500000, 0x00}, - {25, 0, 0x01}, - {50, 0, 0x02}, - {100, 0, 0x03}, - {200, 0, 0x04}, - {400, 0, 0x05}, - {800, 0, 0x06}, - {1600, 0, 0x07}, - {0, 781000, 0x08}, - {1, 563000, 0x09}, - {3, 125000, 0x0A}, - {6, 250000, 0x0B} }; +} kmx61_samp_freq_table[] = { {12, 500000}, + {25, 0}, + {50, 0}, + {100, 0}, + {200, 0}, + {400, 0}, + {800, 0}, + {1600, 0}, + {0, 781000}, + {1, 563000}, + {3, 125000}, + {6, 250000} }; static const struct { int val; @@ -302,24 +301,10 @@ static int kmx61_convert_freq_to_bit(int val, int val2) for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) if (val == kmx61_samp_freq_table[i].val && val2 == kmx61_samp_freq_table[i].val2) - return kmx61_samp_freq_table[i].odr_bits; + return i; return -EINVAL; } -static int kmx61_convert_bit_to_freq(u8 odr_bits, int *val, int *val2) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) - if (odr_bits == kmx61_samp_freq_table[i].odr_bits) { - *val = kmx61_samp_freq_table[i].val; - *val2 = kmx61_samp_freq_table[i].val2; - return 0; - } - return -EINVAL; -} - - static int kmx61_convert_wake_up_odr_to_bit(int val, int val2) { int i; @@ -478,7 +463,7 @@ static int kmx61_set_odr(struct kmx61_data *data, int val, int val2, u8 device) static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2, u8 device) -{ int i; +{ u8 lodr_bits; if (device & KMX61_ACC) @@ -490,13 +475,13 @@ static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2, else return -EINVAL; - for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) - if (lodr_bits == kmx61_samp_freq_table[i].odr_bits) { - *val = kmx61_samp_freq_table[i].val; - *val2 = kmx61_samp_freq_table[i].val2; - return 0; - } - return -EINVAL; + if (lodr_bits >= ARRAY_SIZE(kmx61_samp_freq_table)) + return -EINVAL; + + *val = kmx61_samp_freq_table[lodr_bits].val; + *val2 = kmx61_samp_freq_table[lodr_bits].val2; + + return 0; } static int kmx61_set_range(struct kmx61_data *data, u8 range) @@ -580,8 +565,11 @@ static int kmx61_chip_init(struct kmx61_data *data) } data->odr_bits = ret; - /* set output data rate for wake up (motion detection) function */ - ret = kmx61_convert_bit_to_freq(data->odr_bits, &val, &val2); + /* + * set output data rate for wake up (motion detection) function + * to match data rate for accelerometer sampling + */ + ret = kmx61_get_odr(data, &val, &val2, KMX61_ACC); if (ret < 0) return ret; @@ -1267,16 +1255,12 @@ static int kmx61_gpio_probe(struct i2c_client *client, struct kmx61_data *data) dev = &client->dev; /* data ready gpio interrupt pin */ - gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0); + gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0, GPIOD_IN); if (IS_ERR(gpio)) { dev_err(dev, "acpi gpio get index failed\n"); return PTR_ERR(gpio); } - ret = gpiod_direction_input(gpio); - if (ret) - return ret; - ret = gpiod_to_irq(gpio); dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); |