linux/drivers/i2c/busses/i2c-scmi.c
<<
>>
Prefs
   1/*
   2 * SMBus driver for ACPI SMBus CMI
   3 *
   4 * Copyright (C) 2009 Crane Cai <crane.cai@amd.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation version 2.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/slab.h>
  13#include <linux/kernel.h>
  14#include <linux/stddef.h>
  15#include <linux/i2c.h>
  16#include <linux/acpi.h>
  17
  18#define ACPI_SMBUS_HC_CLASS             "smbus"
  19#define ACPI_SMBUS_HC_DEVICE_NAME       "cmi"
  20
  21ACPI_MODULE_NAME("smbus_cmi");
  22
  23struct smbus_methods_t {
  24        char *mt_info;
  25        char *mt_sbr;
  26        char *mt_sbw;
  27};
  28
  29struct acpi_smbus_cmi {
  30        acpi_handle handle;
  31        struct i2c_adapter adapter;
  32        u8 cap_info:1;
  33        u8 cap_read:1;
  34        u8 cap_write:1;
  35        struct smbus_methods_t *methods;
  36};
  37
  38static const struct smbus_methods_t smbus_methods = {
  39        .mt_info = "_SBI",
  40        .mt_sbr  = "_SBR",
  41        .mt_sbw  = "_SBW",
  42};
  43
  44/* Some IBM BIOSes omit the leading underscore */
  45static const struct smbus_methods_t ibm_smbus_methods = {
  46        .mt_info = "SBI_",
  47        .mt_sbr  = "SBR_",
  48        .mt_sbw  = "SBW_",
  49};
  50
  51static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
  52        {"SMBUS01", (kernel_ulong_t)&smbus_methods},
  53        {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods},
  54        {"", 0}
  55};
  56MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids);
  57
  58#define ACPI_SMBUS_STATUS_OK                    0x00
  59#define ACPI_SMBUS_STATUS_FAIL                  0x07
  60#define ACPI_SMBUS_STATUS_DNAK                  0x10
  61#define ACPI_SMBUS_STATUS_DERR                  0x11
  62#define ACPI_SMBUS_STATUS_CMD_DENY              0x12
  63#define ACPI_SMBUS_STATUS_UNKNOWN               0x13
  64#define ACPI_SMBUS_STATUS_ACC_DENY              0x17
  65#define ACPI_SMBUS_STATUS_TIMEOUT               0x18
  66#define ACPI_SMBUS_STATUS_NOTSUP                0x19
  67#define ACPI_SMBUS_STATUS_BUSY                  0x1a
  68#define ACPI_SMBUS_STATUS_PEC                   0x1f
  69
  70#define ACPI_SMBUS_PRTCL_WRITE                  0x00
  71#define ACPI_SMBUS_PRTCL_READ                   0x01
  72#define ACPI_SMBUS_PRTCL_QUICK                  0x02
  73#define ACPI_SMBUS_PRTCL_BYTE                   0x04
  74#define ACPI_SMBUS_PRTCL_BYTE_DATA              0x06
  75#define ACPI_SMBUS_PRTCL_WORD_DATA              0x08
  76#define ACPI_SMBUS_PRTCL_BLOCK_DATA             0x0a
  77
  78
  79static int
  80acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
  81                   char read_write, u8 command, int size,
  82                   union i2c_smbus_data *data)
  83{
  84        int result = 0;
  85        struct acpi_smbus_cmi *smbus_cmi = adap->algo_data;
  86        unsigned char protocol;
  87        acpi_status status = 0;
  88        struct acpi_object_list input;
  89        union acpi_object mt_params[5];
  90        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  91        union acpi_object *obj;
  92        union acpi_object *pkg;
  93        char *method;
  94        int len = 0;
  95
  96        dev_dbg(&adap->dev, "access size: %d %s\n", size,
  97                (read_write) ? "READ" : "WRITE");
  98        switch (size) {
  99        case I2C_SMBUS_QUICK:
 100                protocol = ACPI_SMBUS_PRTCL_QUICK;
 101                command = 0;
 102                if (read_write == I2C_SMBUS_WRITE) {
 103                        mt_params[3].type = ACPI_TYPE_INTEGER;
 104                        mt_params[3].integer.value = 0;
 105                        mt_params[4].type = ACPI_TYPE_INTEGER;
 106                        mt_params[4].integer.value = 0;
 107                }
 108                break;
 109
 110        case I2C_SMBUS_BYTE:
 111                protocol = ACPI_SMBUS_PRTCL_BYTE;
 112                if (read_write == I2C_SMBUS_WRITE) {
 113                        mt_params[3].type = ACPI_TYPE_INTEGER;
 114                        mt_params[3].integer.value = 0;
 115                        mt_params[4].type = ACPI_TYPE_INTEGER;
 116                        mt_params[4].integer.value = 0;
 117                } else {
 118                        command = 0;
 119                }
 120                break;
 121
 122        case I2C_SMBUS_BYTE_DATA:
 123                protocol = ACPI_SMBUS_PRTCL_BYTE_DATA;
 124                if (read_write == I2C_SMBUS_WRITE) {
 125                        mt_params[3].type = ACPI_TYPE_INTEGER;
 126                        mt_params[3].integer.value = 1;
 127                        mt_params[4].type = ACPI_TYPE_INTEGER;
 128                        mt_params[4].integer.value = data->byte;
 129                }
 130                break;
 131
 132        case I2C_SMBUS_WORD_DATA:
 133                protocol = ACPI_SMBUS_PRTCL_WORD_DATA;
 134                if (read_write == I2C_SMBUS_WRITE) {
 135                        mt_params[3].type = ACPI_TYPE_INTEGER;
 136                        mt_params[3].integer.value = 2;
 137                        mt_params[4].type = ACPI_TYPE_INTEGER;
 138                        mt_params[4].integer.value = data->word;
 139                }
 140                break;
 141
 142        case I2C_SMBUS_BLOCK_DATA:
 143                protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA;
 144                if (read_write == I2C_SMBUS_WRITE) {
 145                        len = data->block[0];
 146                        if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
 147                                return -EINVAL;
 148                        mt_params[3].type = ACPI_TYPE_INTEGER;
 149                        mt_params[3].integer.value = len;
 150                        mt_params[4].type = ACPI_TYPE_BUFFER;
 151                        mt_params[4].buffer.pointer = data->block + 1;
 152                }
 153                break;
 154
 155        default:
 156                dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
 157                return -EOPNOTSUPP;
 158        }
 159
 160        if (read_write == I2C_SMBUS_READ) {
 161                protocol |= ACPI_SMBUS_PRTCL_READ;
 162                method = smbus_cmi->methods->mt_sbr;
 163                input.count = 3;
 164        } else {
 165                protocol |= ACPI_SMBUS_PRTCL_WRITE;
 166                method = smbus_cmi->methods->mt_sbw;
 167                input.count = 5;
 168        }
 169
 170        input.pointer = mt_params;
 171        mt_params[0].type = ACPI_TYPE_INTEGER;
 172        mt_params[0].integer.value = protocol;
 173        mt_params[1].type = ACPI_TYPE_INTEGER;
 174        mt_params[1].integer.value = addr;
 175        mt_params[2].type = ACPI_TYPE_INTEGER;
 176        mt_params[2].integer.value = command;
 177
 178        status = acpi_evaluate_object(smbus_cmi->handle, method, &input,
 179                                      &buffer);
 180        if (ACPI_FAILURE(status)) {
 181                ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status));
 182                return -EIO;
 183        }
 184
 185        pkg = buffer.pointer;
 186        if (pkg && pkg->type == ACPI_TYPE_PACKAGE)
 187                obj = pkg->package.elements;
 188        else {
 189                ACPI_ERROR((AE_INFO, "Invalid argument type"));
 190                result = -EIO;
 191                goto out;
 192        }
 193        if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
 194                ACPI_ERROR((AE_INFO, "Invalid argument type"));
 195                result = -EIO;
 196                goto out;
 197        }
 198
 199        result = obj->integer.value;
 200        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n",
 201                          method, result));
 202
 203        switch (result) {
 204        case ACPI_SMBUS_STATUS_OK:
 205                result = 0;
 206                break;
 207        case ACPI_SMBUS_STATUS_BUSY:
 208                result = -EBUSY;
 209                goto out;
 210        case ACPI_SMBUS_STATUS_TIMEOUT:
 211                result = -ETIMEDOUT;
 212                goto out;
 213        case ACPI_SMBUS_STATUS_DNAK:
 214                result = -ENXIO;
 215                goto out;
 216        default:
 217                result = -EIO;
 218                goto out;
 219        }
 220
 221        if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK)
 222                goto out;
 223
 224        obj = pkg->package.elements + 1;
 225        if (obj->type != ACPI_TYPE_INTEGER) {
 226                ACPI_ERROR((AE_INFO, "Invalid argument type"));
 227                result = -EIO;
 228                goto out;
 229        }
 230
 231        len = obj->integer.value;
 232        obj = pkg->package.elements + 2;
 233        switch (size) {
 234        case I2C_SMBUS_BYTE:
 235        case I2C_SMBUS_BYTE_DATA:
 236        case I2C_SMBUS_WORD_DATA:
 237                if (obj->type != ACPI_TYPE_INTEGER) {
 238                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 239                        result = -EIO;
 240                        goto out;
 241                }
 242                if (len == 2)
 243                        data->word = obj->integer.value;
 244                else
 245                        data->byte = obj->integer.value;
 246                break;
 247        case I2C_SMBUS_BLOCK_DATA:
 248                if (obj->type != ACPI_TYPE_BUFFER) {
 249                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 250                        result = -EIO;
 251                        goto out;
 252                }
 253                if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
 254                        return -EPROTO;
 255                data->block[0] = len;
 256                memcpy(data->block + 1, obj->buffer.pointer, len);
 257                break;
 258        }
 259
 260out:
 261        kfree(buffer.pointer);
 262        dev_dbg(&adap->dev, "Transaction status: %i\n", result);
 263        return result;
 264}
 265
 266static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter)
 267{
 268        struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data;
 269        u32 ret;
 270
 271        ret = smbus_cmi->cap_read | smbus_cmi->cap_write ?
 272                I2C_FUNC_SMBUS_QUICK : 0;
 273
 274        ret |= smbus_cmi->cap_read ?
 275                (I2C_FUNC_SMBUS_READ_BYTE |
 276                I2C_FUNC_SMBUS_READ_BYTE_DATA |
 277                I2C_FUNC_SMBUS_READ_WORD_DATA |
 278                I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0;
 279
 280        ret |= smbus_cmi->cap_write ?
 281                (I2C_FUNC_SMBUS_WRITE_BYTE |
 282                I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
 283                I2C_FUNC_SMBUS_WRITE_WORD_DATA |
 284                I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0;
 285
 286        return ret;
 287}
 288
 289static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {
 290        .smbus_xfer = acpi_smbus_cmi_access,
 291        .functionality = acpi_smbus_cmi_func,
 292};
 293
 294
 295static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
 296                                  const char *name)
 297{
 298        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 299        union acpi_object *obj;
 300        acpi_status status;
 301
 302        if (!strcmp(name, smbus_cmi->methods->mt_info)) {
 303                status = acpi_evaluate_object(smbus_cmi->handle,
 304                                        smbus_cmi->methods->mt_info,
 305                                        NULL, &buffer);
 306                if (ACPI_FAILURE(status)) {
 307                        ACPI_ERROR((AE_INFO, "Evaluating %s: %i",
 308                                   smbus_cmi->methods->mt_info, status));
 309                        return -EIO;
 310                }
 311
 312                obj = buffer.pointer;
 313                if (obj && obj->type == ACPI_TYPE_PACKAGE)
 314                        obj = obj->package.elements;
 315                else {
 316                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 317                        kfree(buffer.pointer);
 318                        return -EIO;
 319                }
 320
 321                if (obj->type != ACPI_TYPE_INTEGER) {
 322                        ACPI_ERROR((AE_INFO, "Invalid argument type"));
 323                        kfree(buffer.pointer);
 324                        return -EIO;
 325                } else
 326                        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x"
 327                                          "\n", (int)obj->integer.value));
 328
 329                kfree(buffer.pointer);
 330                smbus_cmi->cap_info = 1;
 331        } else if (!strcmp(name, smbus_cmi->methods->mt_sbr))
 332                smbus_cmi->cap_read = 1;
 333        else if (!strcmp(name, smbus_cmi->methods->mt_sbw))
 334                smbus_cmi->cap_write = 1;
 335        else
 336                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n",
 337                                 name));
 338
 339        return 0;
 340}
 341
 342static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,
 343                        void *context, void **return_value)
 344{
 345        char node_name[5];
 346        struct acpi_buffer buffer = { sizeof(node_name), node_name };
 347        struct acpi_smbus_cmi *smbus_cmi = context;
 348        acpi_status status;
 349
 350        status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
 351
 352        if (ACPI_SUCCESS(status))
 353                acpi_smbus_cmi_add_cap(smbus_cmi, node_name);
 354
 355        return AE_OK;
 356}
 357
 358static int acpi_smbus_cmi_add(struct acpi_device *device)
 359{
 360        struct acpi_smbus_cmi *smbus_cmi;
 361        const struct acpi_device_id *id;
 362
 363        smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);
 364        if (!smbus_cmi)
 365                return -ENOMEM;
 366
 367        smbus_cmi->handle = device->handle;
 368        strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME);
 369        strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS);
 370        device->driver_data = smbus_cmi;
 371        smbus_cmi->cap_info = 0;
 372        smbus_cmi->cap_read = 0;
 373        smbus_cmi->cap_write = 0;
 374
 375        for (id = acpi_smbus_cmi_ids; id->id[0]; id++)
 376                if (!strcmp(id->id, acpi_device_hid(device)))
 377                        smbus_cmi->methods =
 378                                (struct smbus_methods_t *) id->driver_data;
 379
 380        acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
 381                            acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
 382
 383        if (smbus_cmi->cap_info == 0)
 384                goto err;
 385
 386        snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
 387                "SMBus CMI adapter %s",
 388                acpi_device_name(device));
 389        smbus_cmi->adapter.owner = THIS_MODULE;
 390        smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;
 391        smbus_cmi->adapter.algo_data = smbus_cmi;
 392        smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
 393        smbus_cmi->adapter.dev.parent = &device->dev;
 394
 395        if (i2c_add_adapter(&smbus_cmi->adapter)) {
 396                dev_err(&device->dev, "Couldn't register adapter!\n");
 397                goto err;
 398        }
 399
 400        return 0;
 401
 402err:
 403        kfree(smbus_cmi);
 404        device->driver_data = NULL;
 405        return -EIO;
 406}
 407
 408static int acpi_smbus_cmi_remove(struct acpi_device *device)
 409{
 410        struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device);
 411
 412        i2c_del_adapter(&smbus_cmi->adapter);
 413        kfree(smbus_cmi);
 414        device->driver_data = NULL;
 415
 416        return 0;
 417}
 418
 419static struct acpi_driver acpi_smbus_cmi_driver = {
 420        .name = ACPI_SMBUS_HC_DEVICE_NAME,
 421        .class = ACPI_SMBUS_HC_CLASS,
 422        .ids = acpi_smbus_cmi_ids,
 423        .ops = {
 424                .add = acpi_smbus_cmi_add,
 425                .remove = acpi_smbus_cmi_remove,
 426        },
 427};
 428module_acpi_driver(acpi_smbus_cmi_driver);
 429
 430MODULE_LICENSE("GPL");
 431MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>");
 432MODULE_DESCRIPTION("ACPI SMBus CMI driver");
 433