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