linux/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
<<
>>
Prefs
   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
  15enum inv_mpu_product_name {
  16        INV_MPU_NOT_MATCHED,
  17        INV_MPU_ASUS_T100TA,
  18};
  19
  20static enum inv_mpu_product_name matched_product_name;
  21
  22static 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
  29static 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
  43static 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
  84static 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
 100static int inv_mpu_process_acpi_config(struct i2c_client *client,
 101                                       unsigned short *primary_addr,
 102                                       unsigned short *secondary_addr)
 103{
 104        struct acpi_device *adev = ACPI_COMPANION(&client->dev);
 105        const struct acpi_device_id *id;
 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        ret = acpi_dev_get_resources(adev, &resources,
 116                                     acpi_i2c_check_resource, &i2c_addr);
 117        if (ret < 0)
 118                return ret;
 119
 120        acpi_dev_free_resource_list(&resources);
 121        *primary_addr = i2c_addr & 0x0000ffff;
 122        *secondary_addr = (i2c_addr & 0xffff0000) >> 16;
 123
 124        return 0;
 125}
 126
 127int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
 128{
 129        struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
 130
 131        st->mux_client = NULL;
 132        if (ACPI_HANDLE(&client->dev)) {
 133                struct i2c_board_info info;
 134                struct i2c_client *mux_client;
 135                struct acpi_device *adev;
 136                int ret = -1;
 137
 138                adev = ACPI_COMPANION(&client->dev);
 139                memset(&info, 0, sizeof(info));
 140
 141                dmi_check_system(inv_mpu_dev_list);
 142                switch (matched_product_name) {
 143                case INV_MPU_ASUS_T100TA:
 144                        ret = asus_acpi_get_sensor_info(adev, client,
 145                                                        &info);
 146                        break;
 147                /* Add more matched product processing here */
 148                default:
 149                        break;
 150                }
 151
 152                if (ret < 0) {
 153                        /* No matching DMI, so create device on INV6XX type */
 154                        unsigned short primary, secondary;
 155
 156                        ret = inv_mpu_process_acpi_config(client, &primary,
 157                                                          &secondary);
 158                        if (!ret && secondary) {
 159                                char *name;
 160
 161                                info.addr = secondary;
 162                                strlcpy(info.type, dev_name(&adev->dev),
 163                                        sizeof(info.type));
 164                                name = strchr(info.type, ':');
 165                                if (name)
 166                                        *name = '\0';
 167                                strlcat(info.type, "-client",
 168                                        sizeof(info.type));
 169                        } else
 170                                return 0; /* no secondary addr, which is OK */
 171                }
 172                mux_client = i2c_new_client_device(st->muxc->adapter[0], &info);
 173                if (IS_ERR(mux_client))
 174                        return PTR_ERR(mux_client);
 175                st->mux_client = mux_client;
 176        }
 177
 178        return 0;
 179}
 180
 181void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
 182{
 183        struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
 184
 185        i2c_unregister_device(st->mux_client);
 186}
 187#else
 188
 189#include "inv_mpu_iio.h"
 190
 191int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
 192{
 193        return 0;
 194}
 195
 196void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
 197{
 198}
 199#endif
 200