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 24 ACPI_MODULE_NAME("smbus_cmi"); 25 26 struct smbus_methods_t { 27 char *mt_info; 28 char *mt_sbr; 29 char *mt_sbw; 30 }; 31 32 struct 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 41 static 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 */ 48 static const struct smbus_methods_t ibm_smbus_methods = { 49 .mt_info = "SBI_", 50 .mt_sbr = "SBR_", 51 .mt_sbw = "SBW_", 52 }; 53 54 static 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 }; 60 MODULE_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 83 static int 84 acpi_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.length = len; 156 mt_params[4].buffer.pointer = data->block + 1; 157 } 158 break; 159 160 default: 161 dev_warn(&adap->dev, "Unsupported transaction %d\n", size); 162 return -EOPNOTSUPP; 163 } 164 165 if (read_write == I2C_SMBUS_READ) { 166 protocol |= ACPI_SMBUS_PRTCL_READ; 167 method = smbus_cmi->methods->mt_sbr; 168 input.count = 3; 169 } else { 170 protocol |= ACPI_SMBUS_PRTCL_WRITE; 171 method = smbus_cmi->methods->mt_sbw; 172 input.count = 5; 173 } 174 175 input.pointer = mt_params; 176 mt_params[0].type = ACPI_TYPE_INTEGER; 177 mt_params[0].integer.value = protocol; 178 mt_params[1].type = ACPI_TYPE_INTEGER; 179 mt_params[1].integer.value = addr; 180 mt_params[2].type = ACPI_TYPE_INTEGER; 181 mt_params[2].integer.value = command; 182 183 status = acpi_evaluate_object(smbus_cmi->handle, method, &input, 184 &buffer); 185 if (ACPI_FAILURE(status)) { 186 acpi_handle_err(smbus_cmi->handle, 187 "Failed to evaluate %s: %i\n", method, status); 188 return -EIO; 189 } 190 191 pkg = buffer.pointer; 192 if (pkg && pkg->type == ACPI_TYPE_PACKAGE) 193 obj = pkg->package.elements; 194 else { 195 acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n"); 196 result = -EIO; 197 goto out; 198 } 199 if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { 200 acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n"); 201 result = -EIO; 202 goto out; 203 } 204 205 result = obj->integer.value; 206 acpi_handle_debug(smbus_cmi->handle, "%s return status: %i\n", method, 207 result); 208 209 switch (result) { 210 case ACPI_SMBUS_STATUS_OK: 211 result = 0; 212 break; 213 case ACPI_SMBUS_STATUS_BUSY: 214 result = -EBUSY; 215 goto out; 216 case ACPI_SMBUS_STATUS_TIMEOUT: 217 result = -ETIMEDOUT; 218 goto out; 219 case ACPI_SMBUS_STATUS_DNAK: 220 result = -ENXIO; 221 goto out; 222 default: 223 result = -EIO; 224 goto out; 225 } 226 227 if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK) 228 goto out; 229 230 obj = pkg->package.elements + 1; 231 if (obj->type != ACPI_TYPE_INTEGER) { 232 acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n"); 233 result = -EIO; 234 goto out; 235 } 236 237 len = obj->integer.value; 238 obj = pkg->package.elements + 2; 239 switch (size) { 240 case I2C_SMBUS_BYTE: 241 case I2C_SMBUS_BYTE_DATA: 242 case I2C_SMBUS_WORD_DATA: 243 if (obj->type != ACPI_TYPE_INTEGER) { 244 acpi_handle_err(smbus_cmi->handle, 245 "Invalid argument type\n"); 246 result = -EIO; 247 goto out; 248 } 249 if (len == 2) 250 data->word = obj->integer.value; 251 else 252 data->byte = obj->integer.value; 253 break; 254 case I2C_SMBUS_BLOCK_DATA: 255 if (obj->type != ACPI_TYPE_BUFFER) { 256 acpi_handle_err(smbus_cmi->handle, 257 "Invalid argument type\n"); 258 result = -EIO; 259 goto out; 260 } 261 if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) 262 return -EPROTO; 263 data->block[0] = len; 264 memcpy(data->block + 1, obj->buffer.pointer, len); 265 break; 266 } 267 268 out: 269 kfree(buffer.pointer); 270 dev_dbg(&adap->dev, "Transaction status: %i\n", result); 271 return result; 272 } 273 274 static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter) 275 { 276 struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data; 277 u32 ret; 278 279 ret = smbus_cmi->cap_read | smbus_cmi->cap_write ? 280 I2C_FUNC_SMBUS_QUICK : 0; 281 282 ret |= smbus_cmi->cap_read ? 283 (I2C_FUNC_SMBUS_READ_BYTE | 284 I2C_FUNC_SMBUS_READ_BYTE_DATA | 285 I2C_FUNC_SMBUS_READ_WORD_DATA | 286 I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0; 287 288 ret |= smbus_cmi->cap_write ? 289 (I2C_FUNC_SMBUS_WRITE_BYTE | 290 I2C_FUNC_SMBUS_WRITE_BYTE_DATA | 291 I2C_FUNC_SMBUS_WRITE_WORD_DATA | 292 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0; 293 294 return ret; 295 } 296 297 static const struct i2c_algorithm acpi_smbus_cmi_algorithm = { 298 .smbus_xfer = acpi_smbus_cmi_access, 299 .functionality = acpi_smbus_cmi_func, 300 }; 301 302 303 static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi, 304 const char *name) 305 { 306 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 307 struct acpi_handle *handle = smbus_cmi->handle; 308 union acpi_object *obj; 309 acpi_status status; 310 311 if (!strcmp(name, smbus_cmi->methods->mt_info)) { 312 status = acpi_evaluate_object(smbus_cmi->handle, 313 smbus_cmi->methods->mt_info, 314 NULL, &buffer); 315 if (ACPI_FAILURE(status)) { 316 acpi_handle_err(handle, "Failed to evaluate %s: %i\n", 317 smbus_cmi->methods->mt_info, status); 318 return -EIO; 319 } 320 321 obj = buffer.pointer; 322 if (obj && obj->type == ACPI_TYPE_PACKAGE) 323 obj = obj->package.elements; 324 else { 325 acpi_handle_err(handle, "Invalid argument type\n"); 326 kfree(buffer.pointer); 327 return -EIO; 328 } 329 330 if (obj->type != ACPI_TYPE_INTEGER) { 331 acpi_handle_err(handle, "Invalid argument type\n"); 332 kfree(buffer.pointer); 333 return -EIO; 334 } else 335 acpi_handle_debug(handle, "SMBus CMI Version %x\n", 336 (int)obj->integer.value); 337 338 kfree(buffer.pointer); 339 smbus_cmi->cap_info = 1; 340 } else if (!strcmp(name, smbus_cmi->methods->mt_sbr)) 341 smbus_cmi->cap_read = 1; 342 else if (!strcmp(name, smbus_cmi->methods->mt_sbw)) 343 smbus_cmi->cap_write = 1; 344 else 345 acpi_handle_debug(handle, "Unsupported CMI method: %s\n", name); 346 347 return 0; 348 } 349 350 static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level, 351 void *context, void **return_value) 352 { 353 char node_name[5]; 354 struct acpi_buffer buffer = { sizeof(node_name), node_name }; 355 struct acpi_smbus_cmi *smbus_cmi = context; 356 acpi_status status; 357 358 status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); 359 360 if (ACPI_SUCCESS(status)) 361 acpi_smbus_cmi_add_cap(smbus_cmi, node_name); 362 363 return AE_OK; 364 } 365 366 static int acpi_smbus_cmi_add(struct acpi_device *device) 367 { 368 struct acpi_smbus_cmi *smbus_cmi; 369 const struct acpi_device_id *id; 370 int ret; 371 372 smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL); 373 if (!smbus_cmi) 374 return -ENOMEM; 375 376 smbus_cmi->handle = device->handle; 377 strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME); 378 strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS); 379 device->driver_data = smbus_cmi; 380 smbus_cmi->cap_info = 0; 381 smbus_cmi->cap_read = 0; 382 smbus_cmi->cap_write = 0; 383 384 for (id = acpi_smbus_cmi_ids; id->id[0]; id++) 385 if (!strcmp(id->id, acpi_device_hid(device))) 386 smbus_cmi->methods = 387 (struct smbus_methods_t *) id->driver_data; 388 389 acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, 390 acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL); 391 392 if (smbus_cmi->cap_info == 0) { 393 ret = -ENODEV; 394 goto err; 395 } 396 397 snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name), 398 "SMBus CMI adapter %s", 399 acpi_device_name(device)); 400 smbus_cmi->adapter.owner = THIS_MODULE; 401 smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm; 402 smbus_cmi->adapter.algo_data = smbus_cmi; 403 smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 404 smbus_cmi->adapter.dev.parent = &device->dev; 405 406 ret = i2c_add_adapter(&smbus_cmi->adapter); 407 if (ret) { 408 dev_err(&device->dev, "Couldn't register adapter!\n"); 409 goto err; 410 } 411 412 return 0; 413 414 err: 415 kfree(smbus_cmi); 416 device->driver_data = NULL; 417 return ret; 418 } 419 420 static int acpi_smbus_cmi_remove(struct acpi_device *device) 421 { 422 struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device); 423 424 i2c_del_adapter(&smbus_cmi->adapter); 425 kfree(smbus_cmi); 426 device->driver_data = NULL; 427 428 return 0; 429 } 430 431 static struct acpi_driver acpi_smbus_cmi_driver = { 432 .name = ACPI_SMBUS_HC_DEVICE_NAME, 433 .class = ACPI_SMBUS_HC_CLASS, 434 .ids = acpi_smbus_cmi_ids, 435 .ops = { 436 .add = acpi_smbus_cmi_add, 437 .remove = acpi_smbus_cmi_remove, 438 }, 439 }; 440 module_acpi_driver(acpi_smbus_cmi_driver); 441 442 MODULE_LICENSE("GPL"); 443 MODULE_AUTHOR("Crane Cai <crane.cai@amd.com>"); 444 MODULE_DESCRIPTION("ACPI SMBus CMI driver"); 445