Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-2.6.git] / drivers / hwmon / asus_atk0110.c
index fe4fa29..b5e8920 100644 (file)
@@ -5,10 +5,15 @@
  * See COPYING in the top level directory of the kernel tree.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/debugfs.h>
 #include <linux/kernel.h>
 #include <linux/hwmon.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/dmi.h>
 
 #include <acpi/acpi.h>
 #include <acpi/acpixf.h>
 
 #define ATK_HID "ATK0110"
 
+static bool new_if;
+module_param(new_if, bool, 0);
+MODULE_PARM_DESC(new_if, "Override detection heuristic and force the use of the new ATK0110 interface");
+
+static const struct dmi_system_id __initconst atk_force_new_if[] = {
+       {
+               /* Old interface has broken MCH temp monitoring */
+               .ident = "Asus Sabertooth X58",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "SABERTOOTH X58")
+               }
+       },
+       { }
+};
+
 /* Minimum time between readings, enforced in order to avoid
  * hogging the CPU.
  */
 #define METHOD_OLD_ENUM_FAN    "FSIF"
 
 #define ATK_MUX_HWMON          0x00000006ULL
+#define ATK_MUX_MGMT           0x00000011ULL
 
 #define ATK_CLASS_MASK         0xff000000ULL
 #define ATK_CLASS_FREQ_CTL     0x03000000ULL
 #define ATK_CLASS_FAN_CTL      0x04000000ULL
 #define ATK_CLASS_HWMON                0x06000000ULL
+#define ATK_CLASS_MGMT         0x11000000ULL
 
 #define ATK_TYPE_MASK          0x00ff0000ULL
 #define HWMON_TYPE_VOLT                0x00020000ULL
 #define HWMON_TYPE_TEMP                0x00030000ULL
 #define HWMON_TYPE_FAN         0x00040000ULL
 
-#define HWMON_SENSOR_ID_MASK   0x0000ffffULL
+#define ATK_ELEMENT_ID_MASK    0x0000ffffULL
+
+#define ATK_EC_ID              0x11060004ULL
 
 enum atk_pack_member {
        HWMON_PACK_FLAGS,
@@ -89,11 +113,19 @@ struct atk_data {
        /* new inteface */
        acpi_handle enumerate_handle;
        acpi_handle read_handle;
+       acpi_handle write_handle;
+
+       bool disable_ec;
 
        int voltage_count;
        int temperature_count;
        int fan_count;
        struct list_head sensor_list;
+
+       struct {
+               struct dentry *root;
+               u32 id;
+       } debugfs;
 };
 
 
@@ -129,9 +161,22 @@ struct atk_sensor_data {
        char const *acpi_name;
 };
 
-struct atk_acpi_buffer_u64 {
-       union acpi_object buf;
-       u64 value;
+/* Return buffer format:
+ * [0-3] "value" is valid flag
+ * [4-7] value
+ * [8- ] unknown stuff on newer mobos
+ */
+struct atk_acpi_ret_buffer {
+       u32 flags;
+       u32 value;
+       u8 data[];
+};
+
+/* Input buffer used for GITM and SITM methods */
+struct atk_acpi_input_buf {
+       u32 id;
+       u32 param1;
+       u32 param2;
 };
 
 static int atk_add(struct acpi_device *device);
@@ -439,52 +484,147 @@ static int atk_read_value_old(struct atk_sensor_data *sensor, u64 *value)
        return 0;
 }
 
-static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value)
+static union acpi_object *atk_ggrp(struct atk_data *data, u16 mux)
 {
-       struct atk_data *data = sensor->data;
        struct device *dev = &data->acpi_dev->dev;
+       struct acpi_buffer buf;
+       acpi_status ret;
        struct acpi_object_list params;
-       struct acpi_buffer ret;
        union acpi_object id;
-       struct atk_acpi_buffer_u64 tmp;
-       acpi_status status;
+       union acpi_object *pack;
 
        id.type = ACPI_TYPE_INTEGER;
-       id.integer.value = sensor->id;
-
+       id.integer.value = mux;
        params.count = 1;
        params.pointer = &id;
 
-       tmp.buf.type = ACPI_TYPE_BUFFER;
-       tmp.buf.buffer.pointer = (u8 *)&tmp.value;
-       tmp.buf.buffer.length = sizeof(u64);
-       ret.length = sizeof(tmp);
-       ret.pointer = &tmp;
+       buf.length = ACPI_ALLOCATE_BUFFER;
+       ret = acpi_evaluate_object(data->enumerate_handle, NULL, &params, &buf);
+       if (ret != AE_OK) {
+               dev_err(dev, "GGRP[%#x] ACPI exception: %s\n", mux,
+                               acpi_format_exception(ret));
+               return ERR_PTR(-EIO);
+       }
+       pack = buf.pointer;
+       if (pack->type != ACPI_TYPE_PACKAGE) {
+               /* Execution was successful, but the id was not found */
+               ACPI_FREE(pack);
+               return ERR_PTR(-ENOENT);
+       }
+
+       if (pack->package.count < 1) {
+               dev_err(dev, "GGRP[%#x] package is too small\n", mux);
+               ACPI_FREE(pack);
+               return ERR_PTR(-EIO);
+       }
+       return pack;
+}
+
+static union acpi_object *atk_gitm(struct atk_data *data, u64 id)
+{
+       struct device *dev = &data->acpi_dev->dev;
+       struct atk_acpi_input_buf buf;
+       union acpi_object tmp;
+       struct acpi_object_list params;
+       struct acpi_buffer ret;
+       union acpi_object *obj;
+       acpi_status status;
+
+       buf.id = id;
+       buf.param1 = 0;
+       buf.param2 = 0;
+
+       tmp.type = ACPI_TYPE_BUFFER;
+       tmp.buffer.pointer = (u8 *)&buf;
+       tmp.buffer.length = sizeof(buf);
 
+       params.count = 1;
+       params.pointer = (void *)&tmp;
+
+       ret.length = ACPI_ALLOCATE_BUFFER;
        status = acpi_evaluate_object_typed(data->read_handle, NULL, &params,
                        &ret, ACPI_TYPE_BUFFER);
        if (status != AE_OK) {
-               dev_warn(dev, "%s: ACPI exception: %s\n", __func__,
+               dev_warn(dev, "GITM[%#llx] ACPI exception: %s\n", id,
                                acpi_format_exception(status));
-               return -EIO;
+               return ERR_PTR(-EIO);
+       }
+       obj = ret.pointer;
+
+       /* Sanity check */
+       if (obj->buffer.length < 8) {
+               dev_warn(dev, "Unexpected ASBF length: %u\n",
+                               obj->buffer.length);
+               ACPI_FREE(obj);
+               return ERR_PTR(-EIO);
        }
+       return obj;
+}
 
-       /* Return buffer format:
-        * [0-3] "value" is valid flag
-        * [4-7] value
-        */
-       if (!(tmp.value & 0xffffffff)) {
+static union acpi_object *atk_sitm(struct atk_data *data,
+               struct atk_acpi_input_buf *buf)
+{
+       struct device *dev = &data->acpi_dev->dev;
+       struct acpi_object_list params;
+       union acpi_object tmp;
+       struct acpi_buffer ret;
+       union acpi_object *obj;
+       acpi_status status;
+
+       tmp.type = ACPI_TYPE_BUFFER;
+       tmp.buffer.pointer = (u8 *)buf;
+       tmp.buffer.length = sizeof(*buf);
+
+       params.count = 1;
+       params.pointer = &tmp;
+
+       ret.length = ACPI_ALLOCATE_BUFFER;
+       status = acpi_evaluate_object_typed(data->write_handle, NULL, &params,
+                       &ret, ACPI_TYPE_BUFFER);
+       if (status != AE_OK) {
+               dev_warn(dev, "SITM[%#x] ACPI exception: %s\n", buf->id,
+                               acpi_format_exception(status));
+               return ERR_PTR(-EIO);
+       }
+       obj = ret.pointer;
+
+       /* Sanity check */
+       if (obj->buffer.length < 8) {
+               dev_warn(dev, "Unexpected ASBF length: %u\n",
+                               obj->buffer.length);
+               ACPI_FREE(obj);
+               return ERR_PTR(-EIO);
+       }
+       return obj;
+}
+
+static int atk_read_value_new(struct atk_sensor_data *sensor, u64 *value)
+{
+       struct atk_data *data = sensor->data;
+       struct device *dev = &data->acpi_dev->dev;
+       union acpi_object *obj;
+       struct atk_acpi_ret_buffer *buf;
+       int err = 0;
+
+       obj = atk_gitm(data, sensor->id);
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
+
+       buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer;
+       if (buf->flags == 0) {
                /* The reading is not valid, possible causes:
                 * - sensor failure
                 * - enumeration was FUBAR (and we didn't notice)
                 */
-               dev_info(dev, "Failure: %#llx\n", tmp.value);
-               return -EIO;
+               dev_warn(dev, "Read failed, sensor = %#llx\n", sensor->id);
+               err = -EIO;
+               goto out;
        }
 
-       *value = (tmp.value & 0xffffffff00000000ULL) >> 32;
-
-       return 0;
+       *value = buf->value;
+out:
+       ACPI_FREE(obj);
+       return err;
 }
 
 static int atk_read_value(struct atk_sensor_data *sensor, u64 *value)
@@ -509,6 +649,188 @@ static int atk_read_value(struct atk_sensor_data *sensor, u64 *value)
        return err;
 }
 
+#ifdef CONFIG_DEBUG_FS
+static int atk_debugfs_gitm_get(void *p, u64 *val)
+{
+       struct atk_data *data = p;
+       union acpi_object *ret;
+       struct atk_acpi_ret_buffer *buf;
+       int err = 0;
+
+       if (!data->read_handle)
+               return -ENODEV;
+
+       if (!data->debugfs.id)
+               return -EINVAL;
+
+       ret = atk_gitm(data, data->debugfs.id);
+       if (IS_ERR(ret))
+               return PTR_ERR(ret);
+
+       buf = (struct atk_acpi_ret_buffer *)ret->buffer.pointer;
+       if (buf->flags)
+               *val = buf->value;
+       else
+               err = -EIO;
+
+       return err;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(atk_debugfs_gitm,
+                       atk_debugfs_gitm_get,
+                       NULL,
+                       "0x%08llx\n")
+
+static int atk_acpi_print(char *buf, size_t sz, union acpi_object *obj)
+{
+       int ret = 0;
+
+       switch (obj->type) {
+       case ACPI_TYPE_INTEGER:
+               ret = snprintf(buf, sz, "0x%08llx\n", obj->integer.value);
+               break;
+       case ACPI_TYPE_STRING:
+               ret = snprintf(buf, sz, "%s\n", obj->string.pointer);
+               break;
+       }
+
+       return ret;
+}
+
+static void atk_pack_print(char *buf, size_t sz, union acpi_object *pack)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < pack->package.count; i++) {
+               union acpi_object *obj = &pack->package.elements[i];
+
+               ret = atk_acpi_print(buf, sz, obj);
+               if (ret >= sz)
+                       break;
+               buf += ret;
+               sz -= ret;
+       }
+}
+
+static int atk_debugfs_ggrp_open(struct inode *inode, struct file *file)
+{
+       struct atk_data *data = inode->i_private;
+       char *buf = NULL;
+       union acpi_object *ret;
+       u8 cls;
+       int i;
+
+       if (!data->enumerate_handle)
+               return -ENODEV;
+       if (!data->debugfs.id)
+               return -EINVAL;
+
+       cls = (data->debugfs.id & 0xff000000) >> 24;
+       ret = atk_ggrp(data, cls);
+       if (IS_ERR(ret))
+               return PTR_ERR(ret);
+
+       for (i = 0; i < ret->package.count; i++) {
+               union acpi_object *pack = &ret->package.elements[i];
+               union acpi_object *id;
+
+               if (pack->type != ACPI_TYPE_PACKAGE)
+                       continue;
+               if (!pack->package.count)
+                       continue;
+               id = &pack->package.elements[0];
+               if (id->integer.value == data->debugfs.id) {
+                       /* Print the package */
+                       buf = kzalloc(512, GFP_KERNEL);
+                       if (!buf) {
+                               ACPI_FREE(ret);
+                               return -ENOMEM;
+                       }
+                       atk_pack_print(buf, 512, pack);
+                       break;
+               }
+       }
+       ACPI_FREE(ret);
+
+       if (!buf)
+               return -EINVAL;
+
+       file->private_data = buf;
+
+       return nonseekable_open(inode, file);
+}
+
+static ssize_t atk_debugfs_ggrp_read(struct file *file, char __user *buf,
+               size_t count, loff_t *pos)
+{
+       char *str = file->private_data;
+       size_t len = strlen(str);
+
+       return simple_read_from_buffer(buf, count, pos, str, len);
+}
+
+static int atk_debugfs_ggrp_release(struct inode *inode, struct file *file)
+{
+       kfree(file->private_data);
+       return 0;
+}
+
+static const struct file_operations atk_debugfs_ggrp_fops = {
+       .read           = atk_debugfs_ggrp_read,
+       .open           = atk_debugfs_ggrp_open,
+       .release        = atk_debugfs_ggrp_release,
+       .llseek         = no_llseek,
+};
+
+static void atk_debugfs_init(struct atk_data *data)
+{
+       struct dentry *d;
+       struct dentry *f;
+
+       data->debugfs.id = 0;
+
+       d = debugfs_create_dir("asus_atk0110", NULL);
+       if (!d || IS_ERR(d))
+               return;
+
+       f = debugfs_create_x32("id", S_IRUSR | S_IWUSR, d, &data->debugfs.id);
+       if (!f || IS_ERR(f))
+               goto cleanup;
+
+       f = debugfs_create_file("gitm", S_IRUSR, d, data,
+                       &atk_debugfs_gitm);
+       if (!f || IS_ERR(f))
+               goto cleanup;
+
+       f = debugfs_create_file("ggrp", S_IRUSR, d, data,
+                       &atk_debugfs_ggrp_fops);
+       if (!f || IS_ERR(f))
+               goto cleanup;
+
+       data->debugfs.root = d;
+
+       return;
+cleanup:
+       debugfs_remove_recursive(d);
+}
+
+static void atk_debugfs_cleanup(struct atk_data *data)
+{
+       debugfs_remove_recursive(data->debugfs.root);
+}
+
+#else /* CONFIG_DEBUG_FS */
+
+static void atk_debugfs_init(struct atk_data *data)
+{
+}
+
+static void atk_debugfs_cleanup(struct atk_data *data)
+{
+}
+#endif
+
 static int atk_add_sensor(struct atk_data *data, union acpi_object *obj)
 {
        struct device *dev = &data->acpi_dev->dev;
@@ -713,43 +1035,141 @@ cleanup:
        return ret;
 }
 
-static int atk_enumerate_new_hwmon(struct atk_data *data)
+static int atk_ec_present(struct atk_data *data)
 {
        struct device *dev = &data->acpi_dev->dev;
-       struct acpi_buffer buf;
-       acpi_status ret;
-       struct acpi_object_list params;
-       union acpi_object id;
        union acpi_object *pack;
-       int err;
+       union acpi_object *ec;
+       int ret;
        int i;
 
-       dev_dbg(dev, "Enumerating hwmon sensors\n");
+       pack = atk_ggrp(data, ATK_MUX_MGMT);
+       if (IS_ERR(pack)) {
+               if (PTR_ERR(pack) == -ENOENT) {
+                       /* The MGMT class does not exists - that's ok */
+                       dev_dbg(dev, "Class %#llx not found\n", ATK_MUX_MGMT);
+                       return 0;
+               }
+               return PTR_ERR(pack);
+       }
 
-       id.type = ACPI_TYPE_INTEGER;
-       id.integer.value = ATK_MUX_HWMON;
-       params.count = 1;
-       params.pointer = &id;
+       /* Search the EC */
+       ec = NULL;
+       for (i = 0; i < pack->package.count; i++) {
+               union acpi_object *obj = &pack->package.elements[i];
+               union acpi_object *id;
 
-       buf.length = ACPI_ALLOCATE_BUFFER;
-       ret = acpi_evaluate_object_typed(data->enumerate_handle, NULL, &params,
-                       &buf, ACPI_TYPE_PACKAGE);
-       if (ret != AE_OK) {
-               dev_warn(dev, METHOD_ENUMERATE ": ACPI exception: %s\n",
-                               acpi_format_exception(ret));
-               return -ENODEV;
+               if (obj->type != ACPI_TYPE_PACKAGE)
+                       continue;
+
+               id = &obj->package.elements[0];
+               if (id->type != ACPI_TYPE_INTEGER)
+                       continue;
+
+               if (id->integer.value == ATK_EC_ID) {
+                       ec = obj;
+                       break;
+               }
        }
 
-       /* Result must be a package */
-       pack = buf.pointer;
+       ret = (ec != NULL);
+       if (!ret)
+               /* The system has no EC */
+               dev_dbg(dev, "EC not found\n");
 
-       if (pack->package.count < 1) {
-               dev_dbg(dev, "%s: hwmon package is too small: %d\n", __func__,
-                               pack->package.count);
-               err = -EINVAL;
-               goto out;
+       ACPI_FREE(pack);
+       return ret;
+}
+
+static int atk_ec_enabled(struct atk_data *data)
+{
+       struct device *dev = &data->acpi_dev->dev;
+       union acpi_object *obj;
+       struct atk_acpi_ret_buffer *buf;
+       int err;
+
+       obj = atk_gitm(data, ATK_EC_ID);
+       if (IS_ERR(obj)) {
+               dev_err(dev, "Unable to query EC status\n");
+               return PTR_ERR(obj);
+       }
+       buf = (struct atk_acpi_ret_buffer *)obj->buffer.pointer;
+
+       if (buf->flags == 0) {
+               dev_err(dev, "Unable to query EC status\n");
+               err = -EIO;
+       } else {
+               err = (buf->value != 0);
+               dev_dbg(dev, "EC is %sabled\n",
+                               err ? "en" : "dis");
+       }
+
+       ACPI_FREE(obj);
+       return err;
+}
+
+static int atk_ec_ctl(struct atk_data *data, int enable)
+{
+       struct device *dev = &data->acpi_dev->dev;
+       union acpi_object *obj;
+       struct atk_acpi_input_buf sitm;
+       struct atk_acpi_ret_buffer *ec_ret;
+       int err = 0;
+
+       sitm.id = ATK_EC_ID;
+       sitm.param1 = enable;
+       sitm.param2 = 0;
+
+       obj = atk_sitm(data, &sitm);
+       if (IS_ERR(obj)) {
+               dev_err(dev, "Failed to %sable the EC\n",
+                               enable ? "en" : "dis");
+               return PTR_ERR(obj);
+       }
+       ec_ret = (struct atk_acpi_ret_buffer *)obj->buffer.pointer;
+       if (ec_ret->flags == 0) {
+               dev_err(dev, "Failed to %sable the EC\n",
+                               enable ? "en" : "dis");
+               err = -EIO;
+       } else {
+               dev_info(dev, "EC %sabled\n",
+                               enable ? "en" : "dis");
+       }
+
+       ACPI_FREE(obj);
+       return err;
+}
+
+static int atk_enumerate_new_hwmon(struct atk_data *data)
+{
+       struct device *dev = &data->acpi_dev->dev;
+       union acpi_object *pack;
+       int err;
+       int i;
+
+       err = atk_ec_present(data);
+       if (err < 0)
+               return err;
+       if (err) {
+               err = atk_ec_enabled(data);
+               if (err < 0)
+                       return err;
+               /* If the EC was disabled we will disable it again on unload */
+               data->disable_ec = err;
+
+               err = atk_ec_ctl(data, 1);
+               if (err) {
+                       data->disable_ec = false;
+                       return err;
+               }
        }
 
+       dev_dbg(dev, "Enumerating hwmon sensors\n");
+
+       pack = atk_ggrp(data, ATK_MUX_HWMON);
+       if (IS_ERR(pack))
+               return PTR_ERR(pack);
+
        for (i = 0; i < pack->package.count; i++) {
                union acpi_object *obj = &pack->package.elements[i];
 
@@ -758,8 +1178,7 @@ static int atk_enumerate_new_hwmon(struct atk_data *data)
 
        err = data->voltage_count + data->temperature_count + data->fan_count;
 
-out:
-       ACPI_FREE(buf.pointer);
+       ACPI_FREE(pack);
        return err;
 }
 
@@ -769,15 +1188,19 @@ static int atk_create_files(struct atk_data *data)
        int err;
 
        list_for_each_entry(s, &data->sensor_list, list) {
+               sysfs_attr_init(&s->input_attr.attr);
                err = device_create_file(data->hwmon_dev, &s->input_attr);
                if (err)
                        return err;
+               sysfs_attr_init(&s->label_attr.attr);
                err = device_create_file(data->hwmon_dev, &s->label_attr);
                if (err)
                        return err;
+               sysfs_attr_init(&s->limit1_attr.attr);
                err = device_create_file(data->hwmon_dev, &s->limit1_attr);
                if (err)
                        return err;
+               sysfs_attr_init(&s->limit2_attr.attr);
                err = device_create_file(data->hwmon_dev, &s->limit2_attr);
                if (err)
                        return err;
@@ -835,67 +1258,77 @@ remove:
        return err;
 }
 
-static int atk_check_old_if(struct atk_data *data)
+static int atk_probe_if(struct atk_data *data)
 {
        struct device *dev = &data->acpi_dev->dev;
        acpi_handle ret;
        acpi_status status;
+       int err = 0;
 
        /* RTMP: read temperature */
        status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_TMP, &ret);
-       if (status != AE_OK) {
+       if (ACPI_SUCCESS(status))
+               data->rtmp_handle = ret;
+       else
                dev_dbg(dev, "method " METHOD_OLD_READ_TMP " not found: %s\n",
                                acpi_format_exception(status));
-               return -ENODEV;
-       }
-       data->rtmp_handle = ret;
 
        /* RVLT: read voltage */
        status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_VLT, &ret);
-       if (status != AE_OK) {
+       if (ACPI_SUCCESS(status))
+               data->rvlt_handle = ret;
+       else
                dev_dbg(dev, "method " METHOD_OLD_READ_VLT " not found: %s\n",
                                acpi_format_exception(status));
-               return -ENODEV;
-       }
-       data->rvlt_handle = ret;
 
        /* RFAN: read fan status */
        status = acpi_get_handle(data->atk_handle, METHOD_OLD_READ_FAN, &ret);
-       if (status != AE_OK) {
+       if (ACPI_SUCCESS(status))
+               data->rfan_handle = ret;
+       else
                dev_dbg(dev, "method " METHOD_OLD_READ_FAN " not found: %s\n",
                                acpi_format_exception(status));
-               return -ENODEV;
-       }
-       data->rfan_handle = ret;
-
-       return 0;
-}
-
-static int atk_check_new_if(struct atk_data *data)
-{
-       struct device *dev = &data->acpi_dev->dev;
-       acpi_handle ret;
-       acpi_status status;
 
        /* Enumeration */
        status = acpi_get_handle(data->atk_handle, METHOD_ENUMERATE, &ret);
-       if (status != AE_OK) {
+       if (ACPI_SUCCESS(status))
+               data->enumerate_handle = ret;
+       else
                dev_dbg(dev, "method " METHOD_ENUMERATE " not found: %s\n",
                                acpi_format_exception(status));
-               return -ENODEV;
-       }
-       data->enumerate_handle = ret;
 
        /* De-multiplexer (read) */
        status = acpi_get_handle(data->atk_handle, METHOD_READ, &ret);
-       if (status != AE_OK) {
+       if (ACPI_SUCCESS(status))
+               data->read_handle = ret;
+       else
                dev_dbg(dev, "method " METHOD_READ " not found: %s\n",
                                acpi_format_exception(status));
-               return -ENODEV;
-       }
-       data->read_handle = ret;
 
-       return 0;
+       /* De-multiplexer (write) */
+       status = acpi_get_handle(data->atk_handle, METHOD_WRITE, &ret);
+       if (ACPI_SUCCESS(status))
+               data->write_handle = ret;
+       else
+               dev_dbg(dev, "method " METHOD_WRITE " not found: %s\n",
+                                acpi_format_exception(status));
+
+       /* Check for hwmon methods: first check "old" style methods; note that
+        * both may be present: in this case we stick to the old interface;
+        * analysis of multiple DSDTs indicates that when both interfaces
+        * are present the new one (GGRP/GITM) is not functional.
+        */
+       if (new_if)
+               dev_info(dev, "Overriding interface detection\n");
+       if (data->rtmp_handle && data->rvlt_handle && data->rfan_handle && !new_if)
+               data->old_interface = true;
+       else if (data->enumerate_handle && data->read_handle &&
+                       data->write_handle)
+               data->old_interface = false;
+       else
+               err = -ENODEV;
+
+       return err;
 }
 
 static int atk_add(struct acpi_device *device)
@@ -915,46 +1348,37 @@ static int atk_add(struct acpi_device *device)
        data->acpi_dev = device;
        data->atk_handle = device->handle;
        INIT_LIST_HEAD(&data->sensor_list);
+       data->disable_ec = false;
 
        buf.length = ACPI_ALLOCATE_BUFFER;
        ret = acpi_evaluate_object_typed(data->atk_handle, BOARD_ID, NULL,
                        &buf, ACPI_TYPE_PACKAGE);
        if (ret != AE_OK) {
                dev_dbg(&device->dev, "atk: method MBIF not found\n");
-               err = -ENODEV;
-               goto out;
+       } else {
+               obj = buf.pointer;
+               if (obj->package.count >= 2) {
+                       union acpi_object *id = &obj->package.elements[1];
+                       if (id->type == ACPI_TYPE_STRING)
+                               dev_dbg(&device->dev, "board ID = %s\n",
+                                       id->string.pointer);
+               }
+               ACPI_FREE(buf.pointer);
        }
 
-       obj = buf.pointer;
-       if (obj->package.count >= 2 &&
-                       obj->package.elements[1].type == ACPI_TYPE_STRING) {
-               dev_dbg(&device->dev, "board ID = %s\n",
-                               obj->package.elements[1].string.pointer);
+       err = atk_probe_if(data);
+       if (err) {
+               dev_err(&device->dev, "No usable hwmon interface detected\n");
+               goto out;
        }
-       ACPI_FREE(buf.pointer);
 
-       /* Check for hwmon methods: first check "old" style methods; note that
-        * both may be present: in this case we stick to the old interface;
-        * analysis of multiple DSDTs indicates that when both interfaces
-        * are present the new one (GGRP/GITM) is not functional.
-        */
-       err = atk_check_old_if(data);
-       if (!err) {
+       if (data->old_interface) {
                dev_dbg(&device->dev, "Using old hwmon interface\n");
-               data->old_interface = true;
+               err = atk_enumerate_old_hwmon(data);
        } else {
-               err = atk_check_new_if(data);
-               if (err)
-                       goto out;
-
                dev_dbg(&device->dev, "Using new hwmon interface\n");
-               data->old_interface = false;
-       }
-
-       if (data->old_interface)
-               err = atk_enumerate_old_hwmon(data);
-       else
                err = atk_enumerate_new_hwmon(data);
+       }
        if (err < 0)
                goto out;
        if (err == 0) {
@@ -968,11 +1392,15 @@ static int atk_add(struct acpi_device *device)
        if (err)
                goto cleanup;
 
+       atk_debugfs_init(data);
+
        device->driver_data = data;
        return 0;
 cleanup:
        atk_free_sensors(data);
 out:
+       if (data->disable_ec)
+               atk_ec_ctl(data, 0);
        kfree(data);
        return err;
 }
@@ -984,10 +1412,17 @@ static int atk_remove(struct acpi_device *device, int type)
 
        device->driver_data = NULL;
 
+       atk_debugfs_cleanup(data);
+
        atk_remove_files(data);
        atk_free_sensors(data);
        hwmon_device_unregister(data->hwmon_dev);
 
+       if (data->disable_ec) {
+               if (atk_ec_ctl(data, 0))
+                       dev_err(&device->dev, "Failed to disable EC\n");
+       }
+
        kfree(data);
 
        return 0;
@@ -997,9 +1432,18 @@ static int __init atk0110_init(void)
 {
        int ret;
 
+       /* Make sure it's safe to access the device through ACPI */
+       if (!acpi_resources_are_enforced()) {
+               pr_err("Resources not safely usable due to acpi_enforce_resources kernel parameter\n");
+               return -EBUSY;
+       }
+
+       if (dmi_check_system(atk_force_new_if))
+               new_if = true;
+
        ret = acpi_bus_register_driver(&atk_driver);
        if (ret)
-               pr_info("atk: acpi_bus_register_driver failed: %d\n", ret);
+               pr_info("acpi_bus_register_driver failed: %d\n", ret);
 
        return ret;
 }