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