]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/misc/thinkpad_acpi.c
Merge git://git.infradead.org/mtd-2.6
[linux-2.6-omap-h63xx.git] / drivers / misc / thinkpad_acpi.c
index c6c25a460c9cb4a7a5201961fc44724531ad792d..7ba1acad54025d1fd3cccf9ec9756345fe6bb27c 100644 (file)
@@ -3,7 +3,7 @@
  *
  *
  *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
- *  Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
+ *  Copyright (C) 2006-2008 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,8 +21,8 @@
  *  02110-1301, USA.
  */
 
-#define TPACPI_VERSION "0.18"
-#define TPACPI_SYSFS_VERSION 0x020101
+#define TPACPI_VERSION "0.19"
+#define TPACPI_SYSFS_VERSION 0x020200
 
 /*
  *  Changelog:
@@ -414,6 +414,7 @@ static int acpi_ec_write(int i, u8 v)
        return 1;
 }
 
+#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY)
 static int _sta(acpi_handle handle)
 {
        int status;
@@ -423,6 +424,7 @@ static int _sta(acpi_handle handle)
 
        return status;
 }
+#endif
 
 static int issue_thinkpad_cmos_command(int cmos_cmd)
 {
@@ -1018,6 +1020,14 @@ static unsigned int hotkey_config_change;
 
 static struct mutex hotkey_mutex;
 
+static enum {  /* Reasons for waking up */
+       TP_ACPI_WAKEUP_NONE = 0,        /* None or unknown */
+       TP_ACPI_WAKEUP_BAYEJ,           /* Bay ejection request */
+       TP_ACPI_WAKEUP_UNDOCK,          /* Undock request */
+} hotkey_wakeup_reason;
+
+static int hotkey_autosleep_ack;
+
 static int hotkey_orig_status;
 static u32 hotkey_orig_mask;
 static u32 hotkey_all_mask;
@@ -1633,7 +1643,7 @@ static struct device_attribute dev_attr_hotkey_poll_freq =
 
 #endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
 
-/* sysfs hotkey radio_sw ----------------------------------------------- */
+/* sysfs hotkey radio_sw (pollable) ------------------------------------ */
 static ssize_t hotkey_radio_sw_show(struct device *dev,
                           struct device_attribute *attr,
                           char *buf)
@@ -1649,6 +1659,13 @@ static ssize_t hotkey_radio_sw_show(struct device *dev,
 static struct device_attribute dev_attr_hotkey_radio_sw =
        __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL);
 
+static void hotkey_radio_sw_notify_change(void)
+{
+       if (tp_features.hotkey_wlsw)
+               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+                            "hotkey_radio_sw");
+}
+
 /* sysfs hotkey report_mode -------------------------------------------- */
 static ssize_t hotkey_report_mode_show(struct device *dev,
                           struct device_attribute *attr,
@@ -1661,6 +1678,43 @@ static ssize_t hotkey_report_mode_show(struct device *dev,
 static struct device_attribute dev_attr_hotkey_report_mode =
        __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
 
+/* sysfs wakeup reason (pollable) -------------------------------------- */
+static ssize_t hotkey_wakeup_reason_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason);
+}
+
+static struct device_attribute dev_attr_hotkey_wakeup_reason =
+       __ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL);
+
+void hotkey_wakeup_reason_notify_change(void)
+{
+       if (tp_features.hotkey_mask)
+               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+                            "wakeup_reason");
+}
+
+/* sysfs wakeup hotunplug_complete (pollable) -------------------------- */
+static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack);
+}
+
+static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete =
+       __ATTR(wakeup_hotunplug_complete, S_IRUGO,
+              hotkey_wakeup_hotunplug_complete_show, NULL);
+
+void hotkey_wakeup_hotunplug_complete_notify_change(void)
+{
+       if (tp_features.hotkey_mask)
+               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+                            "wakeup_hotunplug_complete");
+}
+
 /* --------------------------------------------------------------------- */
 
 static struct attribute *hotkey_attributes[] __initdata = {
@@ -1683,6 +1737,8 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
        &dev_attr_hotkey_all_mask.attr,
        &dev_attr_hotkey_recommended_mask.attr,
 #endif
+       &dev_attr_hotkey_wakeup_reason.attr,
+       &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
 };
 
 static int __init hotkey_init(struct ibm_init_struct *iibm)
@@ -1822,7 +1878,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                str_supported(tp_features.hotkey));
 
        if (tp_features.hotkey) {
-               hotkey_dev_attributes = create_attr_set(10, NULL);
+               hotkey_dev_attributes = create_attr_set(12, NULL);
                if (!hotkey_dev_attributes)
                        return -ENOMEM;
                res = add_many_to_attr_set(hotkey_dev_attributes,
@@ -2007,6 +2063,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
        unsigned int scancode;
        int send_acpi_ev;
        int ignore_acpi_ev;
+       int unk_ev;
 
        if (event != 0x80) {
                printk(TPACPI_ERR
@@ -2030,8 +2087,9 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
                        return;
                }
 
-               send_acpi_ev = 0;
+               send_acpi_ev = 1;
                ignore_acpi_ev = 0;
+               unk_ev = 0;
 
                switch (hkey >> 12) {
                case 1:
@@ -2041,46 +2099,92 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
                                scancode--;
                                if (!(hotkey_source_mask & (1 << scancode))) {
                                        tpacpi_input_send_key(scancode);
+                                       send_acpi_ev = 0;
                                } else {
                                        ignore_acpi_ev = 1;
                                }
                        } else {
-                               printk(TPACPI_ERR
-                                      "hotkey 0x%04x out of range "
-                                      "for keyboard map\n", hkey);
-                               send_acpi_ev = 1;
+                               unk_ev = 1;
                        }
                        break;
-               case 5:
-                       /* 0x5000-0x5FFF: LID */
-                       /* we don't handle it through this path, just
-                        * eat up known LID events */
-                       if (hkey != 0x5001 && hkey != 0x5002) {
-                               printk(TPACPI_ERR
-                                      "unknown LID-related HKEY event: "
-                                      "0x%04x\n", hkey);
-                               send_acpi_ev = 1;
+               case 2:
+                       /* Wakeup reason */
+                       switch (hkey) {
+                       case 0x2304: /* suspend, undock */
+                       case 0x2404: /* hibernation, undock */
+                               hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK;
+                               ignore_acpi_ev = 1;
+                               break;
+                       case 0x2305: /* suspend, bay eject */
+                       case 0x2405: /* hibernation, bay eject */
+                               hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ;
+                               ignore_acpi_ev = 1;
+                               break;
+                       default:
+                               unk_ev = 1;
+                       }
+                       if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) {
+                               printk(TPACPI_INFO
+                                      "woke up due to a hot-unplug "
+                                      "request...\n");
+                               hotkey_wakeup_reason_notify_change();
+                       }
+                       break;
+               case 3:
+                       /* bay-related wakeups */
+                       if (hkey == 0x3003) {
+                               hotkey_autosleep_ack = 1;
+                               printk(TPACPI_INFO
+                                      "bay ejected\n");
+                               hotkey_wakeup_hotunplug_complete_notify_change();
                        } else {
+                               unk_ev = 1;
+                       }
+                       break;
+               case 4:
+                       /* dock-related wakeups */
+                       if (hkey == 0x4003) {
+                               hotkey_autosleep_ack = 1;
+                               printk(TPACPI_INFO
+                                      "undocked\n");
+                               hotkey_wakeup_hotunplug_complete_notify_change();
+                       } else {
+                               unk_ev = 1;
+                       }
+                       break;
+               case 5:
+                       /* 0x5000-0x5FFF: human interface helpers */
+                       switch (hkey) {
+                       case 0x5010: /* Lenovo new BIOS: brightness changed */
+                       case 0x5009: /* X61t: swivel up (tablet mode) */
+                       case 0x500a: /* X61t: swivel down (normal mode) */
+                       case 0x500b: /* X61t: tablet pen inserted into bay */
+                       case 0x500c: /* X61t: tablet pen removed from bay */
+                               break;
+                       case 0x5001:
+                       case 0x5002:
+                               /* LID switch events.  Do not propagate */
                                ignore_acpi_ev = 1;
+                               break;
+                       default:
+                               unk_ev = 1;
                        }
                        break;
                case 7:
                        /* 0x7000-0x7FFF: misc */
                        if (tp_features.hotkey_wlsw && hkey == 0x7000) {
                                tpacpi_input_send_radiosw();
+                               hotkey_radio_sw_notify_change();
+                               send_acpi_ev = 0;
                                break;
                        }
                        /* fallthrough to default */
                default:
-                       /* case 2: dock-related */
-                       /*      0x2305 - T43 waking up due to bay lever
-                        *               eject while aslept */
-                       /* case 3: ultra-bay related. maybe bay in dock? */
-                       /*      0x3003 - T43 after wake up by bay lever
-                        *               eject (0x2305) */
+                       unk_ev = 1;
+               }
+               if (unk_ev) {
                        printk(TPACPI_NOTICE
                               "unhandled HKEY event 0x%04x\n", hkey);
-                       send_acpi_ev = 1;
                }
 
                /* Legacy events */
@@ -2100,6 +2204,13 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
        }
 }
 
+static void hotkey_suspend(pm_message_t state)
+{
+       /* Do these on suspend, we get the events on early resume! */
+       hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
+       hotkey_autosleep_ack = 0;
+}
+
 static void hotkey_resume(void)
 {
        if (hotkey_mask_get())
@@ -2107,6 +2218,9 @@ static void hotkey_resume(void)
                       "error while trying to read hot key mask "
                       "from firmware\n");
        tpacpi_input_send_radiosw();
+       hotkey_radio_sw_notify_change();
+       hotkey_wakeup_reason_notify_change();
+       hotkey_wakeup_hotunplug_complete_notify_change();
 #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
        hotkey_poll_setup_safe(0);
 #endif
@@ -2207,6 +2321,7 @@ static struct ibm_struct hotkey_driver_data = {
        .write = hotkey_write,
        .exit = hotkey_exit,
        .resume = hotkey_resume,
+       .suspend = hotkey_suspend,
        .acpi = &ibm_hotkey_acpidriver,
 };
 
@@ -4675,7 +4790,7 @@ static int fan_set_level(int level)
                 * or FULLSPEED mode bits and just ignore them */
                if (level & TP_EC_FAN_FULLSPEED)
                        level |= 7;     /* safety min speed 7 */
-               else if (level & TP_EC_FAN_FULLSPEED)
+               else if (level & TP_EC_FAN_AUTO)
                        level |= 4;     /* safety min speed 4 */
 
                if (!acpi_ec_write(fan_status_offset, level))