linux/drivers/i2c/busses/i2c-scmi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * SMBus driver for ACPI SMBus CMI
   4 *
   5 * Copyright (C) 2009 Crane Cai <crane.cai@amd.com>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/slab.h>
  10#include <linux/kernel.h>
  11#include <linux/stddef.h>
  12#include <linux/i2c.h>
  13#include <linux/acpi.h>
  14
  15#define ACPI_SMBUS_HC_CLASS             "smbus"
  16#define ACPI_SMBUS_HC_DEVICE_NAME       "cmi"
  17
  18/* SMBUS HID definition as supported by Microsoft Windows */
  19#define ACPI_SMBUS_MS_HID               "SMB0001"
  20
  21struct smbus_methods_t {
  22        char *mt_info;
  23        char *mt_sbr;
  24        char *mt_sbw;
  25};
  26
  27struct acpi_smbus_cmi {
  28        acpi_handle handle;
  29        struct i2c_adapter adapter;
  30        u8 cap_info:1;
  31        u8 cap_read:1;
  32        u8 cap_write:1;
  33        struct smbus_methods_t *methods;
  34};
  35
  36static const struct smbus_methods_t smbus_methods = {
  37        .mt_info = "_SBI",
  38        .mt_sbr  = "_SBR",
  39        .mt_sbw  = "_SBW",
  40};
  41
  42/* Some IBM BIOSes omit the leading underscore */
  43static const struct smbus_methods_t ibm_smbus_methods = {
  44        .mt_info = "SBI_",
  45        .mt_sbr  = "SBR_",
  46        .mt_sbw  = "SBW_",
  47};
  48
  49static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
  50        {"SMBUS01", (kernel_ulong_t)&smbus_methods},
  51        {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods},
  52        {ACPI_SMBUS_MS_HID, (kernel_ulong_t)&smbus_methods},
  53        {"", 0}
  54};
  55MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids);
  56
  57#define ACPI_SMBUS_STATUS_OK                    0x00
  58#define ACPI_SMBUS_STATUS_FAIL                  0x07
  59#define ACPI_SMBUS_STATUS_DNAK                  0x10
  60#define ACPI_SMBUS_STATUS_DERR                  0x11
  61#define ACPI_SMBUS_STATUS_CMD_DENY              0x12
  62#define ACPI_SMBUS_STATUS_UNKNOWN               0x13
  63#define ACPI_SMBUS_STATUS_ACC_DENY              0x17
  64#define ACPI_SMBUS_STATUS_TIMEOUT               0x18
  65#define ACPI_SMBUS_STATUS_NOTSUP                0x19
  66#define ACPI_SMBUS_STATUS_BUSY                  0x1a
  67#define ACPI_SMBUS_STATUS_PEC                   0x1f
  68
  69#define ACPI_SMBUS_PRTCL_WRITE                  0x00
  70#define ACPI_SMBUS_PRTCL_READ                   0x01
  71#define ACPI_SMBUS_PRTCL_QUICK                  0x02
  72#define ACPI_SMBUS_PRTCL_BYTE                   0x04
  73#define ACPI_SMBUS_PRTCL_BYTE_DATA              0x06
  74#define ACPI_SMBUS_PRTCL_WORD_DATA              0x08
  75#define ACPI_SMBUS_PRTCL_BLOCK_DATA             0x0a
  76
  77
  78static int
  79acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
  80                   char read_write, u8 command, int size,
  81                   union i2c_smbus_data *data)
  82{
  83        int result = 0;
  84        struct acpi_smbus_cmi *smbus_cmi = adap->algo_data;
  85        unsigned char protocol;
  86        acpi_status status = 0;
  87        struct acpi_object_list input;
  88        union acpi_object mt_params[5];
  89        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
  90        union acpi_object *obj;
  91        union acpi_object *pkg;
  92        char *method;
  93        int len = 0;
  94
  95        dev_dbg(&adap->dev, "access size: %d %s\n", size,
  96                (read_write) ? "READ" : "WRITE");
  97        switch (size) {
  98        case I2C_SMBUS_QUICK:
  99                protocol = ACPI_SMBUS_PRTCL_QUICK;
 100                command = 0;
 101                if (read_write == I2C_SMBUS_WRITE) {
 102                        mt_params[3].type = ACPI_TYPE_INTEGER;
 103                        mt_params[3].integer.value = 0;
 104                        mt_params[4].type = ACPI_TYPE_INTEGER;
 105                        mt_params[4].integer.value = 0;
 106                }
 107                break;
 108
 109        case I2C_SMBUS_BYTE:
 110                protocol = ACPI_SMBUS_PRTCL_BYTE;
 111                if (read_write == I2C_SMBUS_WRITE) {
 112                        mt_params[3].type = ACPI_TYPE_INTEGER;
 113                        mt_params[3].integer.value = 0;
 114                        mt_params[4].type = ACPI_TYPE_INTEGER;
 115                        mt_params[4].integer.value = 0;
 116                } else {
 117                        command = 0;
 118                }
 119                break;
 120
 121        case I2C_SMBUS_BYTE_DATA:
 122                protocol = ACPI_SMBUS_PRTCL_BYTE_DATA;
 123                if (read_write == I2C_SMBUS_WRITE) {
 124                        mt_params[3].type = ACPI_TYPE_INTEGER;
 125                        mt_params[3].integer.value = 1;
 126                        mt_params[4].type = ACPI_TYPE_INTEGER;
 127                        mt_params[4].integer.value = data->byte;
 128                }
 129                break;
 130
 131        case I2C_SMBUS_WORD_DATA:
 132                protocol = ACPI_SMBUS_PRTCL_WORD_DATA;
 133                if (read_write == I2C_SMBUS_WRITE) {
 134                        mt_params[3].type = ACPI_TYPE_INTEGER;
 135                        mt_params[3].integer.value = 2;
 136                        mt_params[4].type = ACPI_TYPE_INTEGER;
 137                        mt_params[4].integer.value = data->word;
 138                }
 139                break;
 140
 141        case I2C_SMBUS_BLOCK_DATA:
 142                protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA;
 143                if (read_write == I2C_SMBUS_WRITE) {
 144                        len = data->block[0];
 145                        if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
 146                                return -EINVAL;
 147                        mt_params[3].type = ACPI_TYPE_INTEGER;
 148                        mt_params[3].integer.value = len;
 149                        mt_params[4].type = ACPI_TYPE_BUFFER;
 150                        mt_params[4].buffer.length = len;
 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_handle_err(smbus_cmi->handle,
 182                                "Failed to evaluate %s: %i\n", 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_handle_err(smbus_cmi->handle, "Invalid argument type\n");
 191                result = -EIO;
 192                goto out;
 193        }
 194        if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
 195                acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
 196                result = -EIO;
 197                goto out;
 198        }
 199
 200        result = obj->integer.value;
 201        acpi_handle_debug(smbus_cmi->handle,  "%s return status: %i\n", method,
 202                          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->type != ACPI_TYPE_INTEGER) {
 227                acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
 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->type != ACPI_TYPE_INTEGER) {
 239                        acpi_handle_err(smbus_cmi->handle,
 240                                        "Invalid argument type\n");
 241                        result = -EIO;
 242                        goto out;
 243                }
 244                if (len == 2)
 245                        data->word = obj->integer.value;
 246                else
 247                        data->byte = obj->integer.value;
 248                break;
 249        case I2C_SMBUS_BLOCK_DATA:
 250                if (obj->type != ACPI_TYPE_BUFFER) {
 251                        acpi_handle_err(smbus_cmi->handle,
 252                                        "Invalid argument type\n");
 253                        result = -EIO;
 254                        goto out;
 255                }
 256                if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
 257                        return -EPROTO;
 258                data->block[0] = len;
 259                memcpy(data->block + 1, obj->buffer.pointer, len);
 260                break;
 261        }
 262
 263out:
 264        kfree(buffer.pointer);
 265        dev_dbg(&adap->dev, "Transaction status: %i\n", result);
 266        return result;
 267}
 268
 269static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter)
 270{
 271        struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data;
 272        u32 ret;
 273
 274        ret = smbus_cmi->cap_read | smbus_cmi->cap_write ?
 275                I2C_FUNC_SMBUS_QUICK : 0;
 276
 277        ret |= smbus_cmi->cap_read ?
 278                (I2C_FUNC_SMBUS_READ_BYTE |
 279                I2C_FUNC_SMBUS_READ_BYTE_DATA |
 280                I2C_FUNC_SMBUS_READ_WORD_DATA |
 281                I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0;
 282
 283        ret |= smbus_cmi->cap_write ?
 284                (I2C_FUNC_SMBUS_WRITE_BYTE |
 285                I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
 286                I2C_FUNC_SMBUS_WRITE_WORD_DATA |
 287                I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0;
 288
 289        return ret;
 290}
 291
 292static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {
 293        .smbus_xfer = acpi_smbus_cmi_access,
 294        .functionality = acpi_smbus_cmi_func,
 295};
 296
 297
 298static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
 299                                  const char *name)
 300{
 301        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 302        struct acpi_handle *handle = smbus_cmi->handle;
 303        union acpi_object *obj;
 304        acpi_status status;
 305
 306        if (!strcmp(name, smbus_cmi->methods->mt_info)) {
 307                status = acpi_evaluate_object(smbus_cmi->handle,
 308                                        smbus_cmi->methods->mt_info,
 309                                        NULL, &buffer);
 310                if (ACPI_FAILURE(status)) {
 311                        acpi_handle_err(handle, "Failed to evaluate %s: %i\n",
 312                                        smbus_cmi->methods->mt_info, status);
 313                        return -EIO;
 314                }
 315
 316                obj = buffer.pointer;
 317                if (obj && obj->type == ACPI_TYPE_PACKAGE)
 318                        obj = obj->package.elements;
 319                else {
 320                        acpi_handle_err(handle, "Invalid argument type\n");
 321                        kfree(buffer.pointer);
 322                        return -EIO;
 323                }
 324
 325                if (obj->type != ACPI_TYPE_INTEGER) {
 326                        acpi_handle_err(handle, "Invalid argument type\n");
 327                        kfree(buffer.pointer);
 328                        return -EIO;
 329                } else
 330                        acpi_handle_debug(handle, "SMBus CMI Version %x\n",
 331                                          (int)obj->integer.value);
 332
 333                kfree(buffer.pointer);
 334                smbus_cmi->cap_info = 1;
 335        } else if (!strcmp(name, smbus_cmi->methods->mt_sbr))
 336                smbus_cmi->cap_read = 1;
 337        else if (!strcmp(name, smbus_cmi->methods->mt_sbw))
 338                smbus_cmi->cap_write = 1;
 339        else
 340                acpi_handle_debug(handle, "Unsupported CMI method: %s\n", name);
 341
 342        return 0;
 343}
 344
 345static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,
 346                        void *context, void **return_value)
 347{
 348        char node_name[5];
 349        struct acpi_buffer buffer = { sizeof(node_name), node_name };
 350        struct acpi_smbus_cmi *smbus_cmi = context;
 351        acpi_status status;
 352
 353        status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer);
 354
 355        if (ACPI_SUCCESS(status))
 356                acpi_smbus_cmi_add_cap(smbus_cmi, node_name);
 357
 358        return AE_OK;
 359}
 360
 361static int acpi_smbus_cmi_add(struct acpi_device *device)
 362{
 363        struct acpi_smbus_cmi *smbus_cmi;
 364        const struct acpi_device_id *id;
 365        int ret;
 366
 367        smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL);
 368        if (!smbus_cmi)
 369                return -ENOMEM;
 370
 371        smbus_cmi->handle = device->handle;
 372        strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME);
 373        strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS);
 374        device->driver_data = smbus_cmi;
 375        smbus_cmi->cap_info = 0;
 376        smbus_cmi->cap_read = 0;
 377        smbus_cmi->cap_write = 0;
 378
 379        for (id = acpi_smbus_cmi_ids; id->id[0]; id++)
 380                if (!strcmp(id->id, acpi_device_hid(device)))
 381                        smbus_cmi->methods =
 382                                (struct smbus_methods_t *) id->driver_data;
 383
 384        acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
 385                            acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
 386
 387        if (smbus_cmi->cap_info == 0) {
 388                ret = -ENODEV;
 389                goto err;
 390        }
 391
 392        snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name),
 393                "SMBus CMI adapter %s",
 394                acpi_device_name(device));
 395        smbus_cmi->adapter.owner = THIS_MODULE;
 396        smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm;
 397        smbus_cmi->adapter.algo_data = smbus_cmi;
 398        smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
 399        smbus_cmi->adapter.dev.parent = &device->dev;
 400
 401        ret = i2c_add_adapter(&smbus_cmi->adapter);
 402        if (ret) {
 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 ret;
 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