Commit 18020a0d8cccad0d3642219d6aef789420c04c1f
Exists in
master
and in
7 other branches
Merge branch 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
* 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: i2c-scmi: Provide module aliases for automatic loading i2c-scmi: Support IBM SMBus CMI devices acpi: Support IBM SMBus CMI devices
Showing 3 changed files Side-by-side Diff
drivers/acpi/scan.c
... | ... | @@ -8,6 +8,7 @@ |
8 | 8 | #include <linux/acpi.h> |
9 | 9 | #include <linux/signal.h> |
10 | 10 | #include <linux/kthread.h> |
11 | +#include <linux/dmi.h> | |
11 | 12 | |
12 | 13 | #include <acpi/acpi_drivers.h> |
13 | 14 | |
... | ... | @@ -1032,6 +1033,41 @@ |
1032 | 1033 | list_add_tail(&id->list, &device->pnp.ids); |
1033 | 1034 | } |
1034 | 1035 | |
1036 | +/* | |
1037 | + * Old IBM workstations have a DSDT bug wherein the SMBus object | |
1038 | + * lacks the SMBUS01 HID and the methods do not have the necessary "_" | |
1039 | + * prefix. Work around this. | |
1040 | + */ | |
1041 | +static int acpi_ibm_smbus_match(struct acpi_device *device) | |
1042 | +{ | |
1043 | + acpi_handle h_dummy; | |
1044 | + struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; | |
1045 | + int result; | |
1046 | + | |
1047 | + if (!dmi_name_in_vendors("IBM")) | |
1048 | + return -ENODEV; | |
1049 | + | |
1050 | + /* Look for SMBS object */ | |
1051 | + result = acpi_get_name(device->handle, ACPI_SINGLE_NAME, &path); | |
1052 | + if (result) | |
1053 | + return result; | |
1054 | + | |
1055 | + if (strcmp("SMBS", path.pointer)) { | |
1056 | + result = -ENODEV; | |
1057 | + goto out; | |
1058 | + } | |
1059 | + | |
1060 | + /* Does it have the necessary (but misnamed) methods? */ | |
1061 | + result = -ENODEV; | |
1062 | + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "SBI", &h_dummy)) && | |
1063 | + ACPI_SUCCESS(acpi_get_handle(device->handle, "SBR", &h_dummy)) && | |
1064 | + ACPI_SUCCESS(acpi_get_handle(device->handle, "SBW", &h_dummy))) | |
1065 | + result = 0; | |
1066 | +out: | |
1067 | + kfree(path.pointer); | |
1068 | + return result; | |
1069 | +} | |
1070 | + | |
1035 | 1071 | static void acpi_device_set_id(struct acpi_device *device) |
1036 | 1072 | { |
1037 | 1073 | acpi_status status; |
... | ... | @@ -1082,6 +1118,8 @@ |
1082 | 1118 | acpi_add_id(device, ACPI_BAY_HID); |
1083 | 1119 | else if (ACPI_SUCCESS(acpi_dock_match(device))) |
1084 | 1120 | acpi_add_id(device, ACPI_DOCK_HID); |
1121 | + else if (!acpi_ibm_smbus_match(device)) | |
1122 | + acpi_add_id(device, ACPI_SMBUS_IBM_HID); | |
1085 | 1123 | |
1086 | 1124 | break; |
1087 | 1125 | case ACPI_BUS_TYPE_POWER: |
drivers/i2c/busses/i2c-scmi.c
... | ... | @@ -33,6 +33,7 @@ |
33 | 33 | u8 cap_info:1; |
34 | 34 | u8 cap_read:1; |
35 | 35 | u8 cap_write:1; |
36 | + struct smbus_methods_t *methods; | |
36 | 37 | }; |
37 | 38 | |
38 | 39 | static const struct smbus_methods_t smbus_methods = { |
39 | 40 | |
40 | 41 | |
... | ... | @@ -41,10 +42,19 @@ |
41 | 42 | .mt_sbw = "_SBW", |
42 | 43 | }; |
43 | 44 | |
45 | +/* Some IBM BIOSes omit the leading underscore */ | |
46 | +static const struct smbus_methods_t ibm_smbus_methods = { | |
47 | + .mt_info = "SBI_", | |
48 | + .mt_sbr = "SBR_", | |
49 | + .mt_sbw = "SBW_", | |
50 | +}; | |
51 | + | |
44 | 52 | static const struct acpi_device_id acpi_smbus_cmi_ids[] = { |
45 | - {"SMBUS01", 0}, | |
53 | + {"SMBUS01", (kernel_ulong_t)&smbus_methods}, | |
54 | + {ACPI_SMBUS_IBM_HID, (kernel_ulong_t)&ibm_smbus_methods}, | |
46 | 55 | {"", 0} |
47 | 56 | }; |
57 | +MODULE_DEVICE_TABLE(acpi, acpi_smbus_cmi_ids); | |
48 | 58 | |
49 | 59 | #define ACPI_SMBUS_STATUS_OK 0x00 |
50 | 60 | #define ACPI_SMBUS_STATUS_FAIL 0x07 |
51 | 61 | |
... | ... | @@ -150,11 +160,11 @@ |
150 | 160 | |
151 | 161 | if (read_write == I2C_SMBUS_READ) { |
152 | 162 | protocol |= ACPI_SMBUS_PRTCL_READ; |
153 | - method = smbus_methods.mt_sbr; | |
163 | + method = smbus_cmi->methods->mt_sbr; | |
154 | 164 | input.count = 3; |
155 | 165 | } else { |
156 | 166 | protocol |= ACPI_SMBUS_PRTCL_WRITE; |
157 | - method = smbus_methods.mt_sbw; | |
167 | + method = smbus_cmi->methods->mt_sbw; | |
158 | 168 | input.count = 5; |
159 | 169 | } |
160 | 170 | |
161 | 171 | |
162 | 172 | |
... | ... | @@ -290,13 +300,13 @@ |
290 | 300 | union acpi_object *obj; |
291 | 301 | acpi_status status; |
292 | 302 | |
293 | - if (!strcmp(name, smbus_methods.mt_info)) { | |
303 | + if (!strcmp(name, smbus_cmi->methods->mt_info)) { | |
294 | 304 | status = acpi_evaluate_object(smbus_cmi->handle, |
295 | - smbus_methods.mt_info, | |
305 | + smbus_cmi->methods->mt_info, | |
296 | 306 | NULL, &buffer); |
297 | 307 | if (ACPI_FAILURE(status)) { |
298 | 308 | ACPI_ERROR((AE_INFO, "Evaluating %s: %i", |
299 | - smbus_methods.mt_info, status)); | |
309 | + smbus_cmi->methods->mt_info, status)); | |
300 | 310 | return -EIO; |
301 | 311 | } |
302 | 312 | |
303 | 313 | |
... | ... | @@ -319,9 +329,9 @@ |
319 | 329 | |
320 | 330 | kfree(buffer.pointer); |
321 | 331 | smbus_cmi->cap_info = 1; |
322 | - } else if (!strcmp(name, smbus_methods.mt_sbr)) | |
332 | + } else if (!strcmp(name, smbus_cmi->methods->mt_sbr)) | |
323 | 333 | smbus_cmi->cap_read = 1; |
324 | - else if (!strcmp(name, smbus_methods.mt_sbw)) | |
334 | + else if (!strcmp(name, smbus_cmi->methods->mt_sbw)) | |
325 | 335 | smbus_cmi->cap_write = 1; |
326 | 336 | else |
327 | 337 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n", |
... | ... | @@ -349,6 +359,7 @@ |
349 | 359 | static int acpi_smbus_cmi_add(struct acpi_device *device) |
350 | 360 | { |
351 | 361 | struct acpi_smbus_cmi *smbus_cmi; |
362 | + const struct acpi_device_id *id; | |
352 | 363 | |
353 | 364 | smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL); |
354 | 365 | if (!smbus_cmi) |
... | ... | @@ -361,6 +372,11 @@ |
361 | 372 | smbus_cmi->cap_info = 0; |
362 | 373 | smbus_cmi->cap_read = 0; |
363 | 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; | |
364 | 380 | |
365 | 381 | acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, |
366 | 382 | acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL); |
include/acpi/acpi_drivers.h
... | ... | @@ -65,6 +65,8 @@ |
65 | 65 | #define ACPI_VIDEO_HID "LNXVIDEO" |
66 | 66 | #define ACPI_BAY_HID "LNXIOBAY" |
67 | 67 | #define ACPI_DOCK_HID "LNXDOCK" |
68 | +/* Quirk for broken IBM BIOSes */ | |
69 | +#define ACPI_SMBUS_IBM_HID "SMBUSIBM" | |
68 | 70 | |
69 | 71 | /* |
70 | 72 | * For fixed hardware buttons, we fabricate acpi_devices with HID |