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