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