@@ -8,6 +8,7 @@
#include <linux/acpi.h>
#include <linux/signal.h>
#include <linux/kthread.h>
+#include <linux/dmi.h>
#include <acpi/acpi_drivers.h>
@@ -1014,6 +1015,41 @@ static void acpi_add_id(struct acpi_device *device, const char *dev_id)
list_add_tail(&id->list, &device->pnp.ids);
}
+/*
+ * Old IBM workstations have a DSDT bug wherein the SMBus object
+ * lacks the SMBUS01 HID and the methods do not have the necessary "_"
+ * prefix. Work around this.
+ */
+static int probe_ibm_smbus_device(struct acpi_device *device)
+{
+ acpi_handle h_dummy;
+ struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
+ int result;
+
+ if (!dmi_name_in_vendors("IBM"))
+ return -ENODEV;
+
+ /* Look for SMBS object */
+ result = acpi_get_name(device->handle, ACPI_SINGLE_NAME, &path);
+ if (result)
+ return result;
+
+ if (strcmp("SMBS", path.pointer)) {
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* Does it have the necessary (but misnamed) methods? */
+ result = -ENODEV;
+ if (ACPI_SUCCESS(acpi_get_handle(device->handle, "SBI", &h_dummy)) &&
+ ACPI_SUCCESS(acpi_get_handle(device->handle, "SBR", &h_dummy)) &&
+ ACPI_SUCCESS(acpi_get_handle(device->handle, "SBW", &h_dummy)))
+ result = 0;
+out:
+ kfree(path.pointer);
+ return result;
+}
+
static void acpi_device_set_id(struct acpi_device *device)
{
acpi_status status;
@@ -1064,6 +1100,8 @@ static void acpi_device_set_id(struct acpi_device *device)
acpi_add_id(device, ACPI_BAY_HID);
else if (ACPI_SUCCESS(acpi_dock_match(device)))
acpi_add_id(device, ACPI_DOCK_HID);
+ else if (!probe_ibm_smbus_device(device))
+ acpi_add_id(device, ACPI_SMBUS_HID);
break;
case ACPI_BUS_TYPE_POWER:
@@ -33,6 +33,7 @@ struct acpi_smbus_cmi {
u8 cap_info:1;
u8 cap_read:1;
u8 cap_write:1;
+ struct smbus_methods_t methods;
};
static const struct smbus_methods_t smbus_methods = {
@@ -41,8 +42,15 @@ static const struct smbus_methods_t smbus_methods = {
.mt_sbw = "_SBW",
};
+/* Some IBM BIOSes omit the leading underscore */
+static const struct smbus_methods_t ibm_smbus_methods = {
+ .mt_info = "SBI_",
+ .mt_sbr = "SBR_",
+ .mt_sbw = "SBW_",
+};
+
static const struct acpi_device_id acpi_smbus_cmi_ids[] = {
- {"SMBUS01", 0},
+ {ACPI_SMBUS_HID, 0},
{"", 0}
};
@@ -150,11 +158,11 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
if (read_write == I2C_SMBUS_READ) {
protocol |= ACPI_SMBUS_PRTCL_READ;
- method = smbus_methods.mt_sbr;
+ method = smbus_cmi->methods.mt_sbr;
input.count = 3;
} else {
protocol |= ACPI_SMBUS_PRTCL_WRITE;
- method = smbus_methods.mt_sbw;
+ method = smbus_cmi->methods.mt_sbw;
input.count = 5;
}
@@ -283,20 +291,21 @@ static const struct i2c_algorithm acpi_smbus_cmi_algorithm = {
};
-static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
- const char *name)
+static int acpi_smbus_cmi_probe_cap(struct acpi_smbus_cmi *smbus_cmi,
+ const char *name,
+ const struct smbus_methods_t *methods)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
- if (!strcmp(name, smbus_methods.mt_info)) {
+ if (!strcmp(name, methods->mt_info)) {
status = acpi_evaluate_object(smbus_cmi->handle,
- smbus_methods.mt_info,
+ methods->mt_info,
NULL, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, "Evaluating %s: %i",
- smbus_methods.mt_info, status));
+ methods->mt_info, status));
return -EIO;
}
@@ -319,17 +328,35 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
kfree(buffer.pointer);
smbus_cmi->cap_info = 1;
- } else if (!strcmp(name, smbus_methods.mt_sbr))
+ smbus_cmi->methods.mt_info = methods->mt_info;
+ } else if (!strcmp(name, methods->mt_sbr)) {
smbus_cmi->cap_read = 1;
- else if (!strcmp(name, smbus_methods.mt_sbw))
+ smbus_cmi->methods.mt_sbr = methods->mt_sbr;
+ } else if (!strcmp(name, methods->mt_sbw)) {
smbus_cmi->cap_write = 1;
- else
+ smbus_cmi->methods.mt_sbw = methods->mt_sbw;
+ } else {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n",
name));
+ return -ENODEV;
+ }
return 0;
}
+static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
+ const char *name)
+{
+ int res;
+
+ res = acpi_smbus_cmi_probe_cap(smbus_cmi, name, &smbus_methods);
+ if (!res)
+ return 0;
+
+ res = acpi_smbus_cmi_probe_cap(smbus_cmi, name, &ibm_smbus_methods);
+ return res;
+}
+
static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level,
void *context, void **return_value)
{
@@ -65,6 +65,7 @@
#define ACPI_VIDEO_HID "LNXVIDEO"
#define ACPI_BAY_HID "LNXIOBAY"
#define ACPI_DOCK_HID "LNXDOCK"
+#define ACPI_SMBUS_HID "SMBUS01"
/*
* For fixed hardware buttons, we fabricate acpi_devices with HID