diff options
Diffstat (limited to 'drivers/platform/x86/thinkpad_acpi.c')
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 910 |
1 files changed, 758 insertions, 152 deletions
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3478453eba7a..bcbc05107ba8 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.21" | 24 | #define TPACPI_VERSION "0.22" |
25 | #define TPACPI_SYSFS_VERSION 0x020200 | 25 | #define TPACPI_SYSFS_VERSION 0x020200 |
26 | 26 | ||
27 | /* | 27 | /* |
@@ -122,6 +122,27 @@ enum { | |||
122 | #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ | 122 | #define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */ |
123 | #define TPACPI_HKEY_INPUT_VERSION 0x4101 | 123 | #define TPACPI_HKEY_INPUT_VERSION 0x4101 |
124 | 124 | ||
125 | /* ACPI \WGSV commands */ | ||
126 | enum { | ||
127 | TP_ACPI_WGSV_GET_STATE = 0x01, /* Get state information */ | ||
128 | TP_ACPI_WGSV_PWR_ON_ON_RESUME = 0x02, /* Resume WWAN powered on */ | ||
129 | TP_ACPI_WGSV_PWR_OFF_ON_RESUME = 0x03, /* Resume WWAN powered off */ | ||
130 | TP_ACPI_WGSV_SAVE_STATE = 0x04, /* Save state for S4/S5 */ | ||
131 | }; | ||
132 | |||
133 | /* TP_ACPI_WGSV_GET_STATE bits */ | ||
134 | enum { | ||
135 | TP_ACPI_WGSV_STATE_WWANEXIST = 0x0001, /* WWAN hw available */ | ||
136 | TP_ACPI_WGSV_STATE_WWANPWR = 0x0002, /* WWAN radio enabled */ | ||
137 | TP_ACPI_WGSV_STATE_WWANPWRRES = 0x0004, /* WWAN state at resume */ | ||
138 | TP_ACPI_WGSV_STATE_WWANBIOSOFF = 0x0008, /* WWAN disabled in BIOS */ | ||
139 | TP_ACPI_WGSV_STATE_BLTHEXIST = 0x0001, /* BLTH hw available */ | ||
140 | TP_ACPI_WGSV_STATE_BLTHPWR = 0x0002, /* BLTH radio enabled */ | ||
141 | TP_ACPI_WGSV_STATE_BLTHPWRRES = 0x0004, /* BLTH state at resume */ | ||
142 | TP_ACPI_WGSV_STATE_BLTHBIOSOFF = 0x0008, /* BLTH disabled in BIOS */ | ||
143 | TP_ACPI_WGSV_STATE_UWBEXIST = 0x0010, /* UWB hw available */ | ||
144 | TP_ACPI_WGSV_STATE_UWBPWR = 0x0020, /* UWB radio enabled */ | ||
145 | }; | ||
125 | 146 | ||
126 | /**************************************************************************** | 147 | /**************************************************************************** |
127 | * Main driver | 148 | * Main driver |
@@ -148,14 +169,17 @@ enum { | |||
148 | enum { | 169 | enum { |
149 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | 170 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, |
150 | TPACPI_RFK_WWAN_SW_ID, | 171 | TPACPI_RFK_WWAN_SW_ID, |
172 | TPACPI_RFK_UWB_SW_ID, | ||
151 | }; | 173 | }; |
152 | 174 | ||
153 | /* Debugging */ | 175 | /* Debugging */ |
154 | #define TPACPI_LOG TPACPI_FILE ": " | 176 | #define TPACPI_LOG TPACPI_FILE ": " |
155 | #define TPACPI_ERR KERN_ERR TPACPI_LOG | 177 | #define TPACPI_ALERT KERN_ALERT TPACPI_LOG |
156 | #define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG | 178 | #define TPACPI_CRIT KERN_CRIT TPACPI_LOG |
157 | #define TPACPI_INFO KERN_INFO TPACPI_LOG | 179 | #define TPACPI_ERR KERN_ERR TPACPI_LOG |
158 | #define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG | 180 | #define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG |
181 | #define TPACPI_INFO KERN_INFO TPACPI_LOG | ||
182 | #define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG | ||
159 | 183 | ||
160 | #define TPACPI_DBG_ALL 0xffff | 184 | #define TPACPI_DBG_ALL 0xffff |
161 | #define TPACPI_DBG_INIT 0x0001 | 185 | #define TPACPI_DBG_INIT 0x0001 |
@@ -201,6 +225,7 @@ struct ibm_struct { | |||
201 | void (*exit) (void); | 225 | void (*exit) (void); |
202 | void (*resume) (void); | 226 | void (*resume) (void); |
203 | void (*suspend) (pm_message_t state); | 227 | void (*suspend) (pm_message_t state); |
228 | void (*shutdown) (void); | ||
204 | 229 | ||
205 | struct list_head all_drivers; | 230 | struct list_head all_drivers; |
206 | 231 | ||
@@ -239,6 +264,7 @@ static struct { | |||
239 | u32 bright_16levels:1; | 264 | u32 bright_16levels:1; |
240 | u32 bright_acpimode:1; | 265 | u32 bright_acpimode:1; |
241 | u32 wan:1; | 266 | u32 wan:1; |
267 | u32 uwb:1; | ||
242 | u32 fan_ctrl_status_undef:1; | 268 | u32 fan_ctrl_status_undef:1; |
243 | u32 input_device_registered:1; | 269 | u32 input_device_registered:1; |
244 | u32 platform_drv_registered:1; | 270 | u32 platform_drv_registered:1; |
@@ -288,6 +314,18 @@ struct tpacpi_led_classdev { | |||
288 | unsigned int led; | 314 | unsigned int led; |
289 | }; | 315 | }; |
290 | 316 | ||
317 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
318 | static int dbg_wlswemul; | ||
319 | static int tpacpi_wlsw_emulstate; | ||
320 | static int dbg_bluetoothemul; | ||
321 | static int tpacpi_bluetooth_emulstate; | ||
322 | static int dbg_wwanemul; | ||
323 | static int tpacpi_wwan_emulstate; | ||
324 | static int dbg_uwbemul; | ||
325 | static int tpacpi_uwb_emulstate; | ||
326 | #endif | ||
327 | |||
328 | |||
291 | /**************************************************************************** | 329 | /**************************************************************************** |
292 | **************************************************************************** | 330 | **************************************************************************** |
293 | * | 331 | * |
@@ -728,6 +766,18 @@ static int tpacpi_resume_handler(struct platform_device *pdev) | |||
728 | return 0; | 766 | return 0; |
729 | } | 767 | } |
730 | 768 | ||
769 | static void tpacpi_shutdown_handler(struct platform_device *pdev) | ||
770 | { | ||
771 | struct ibm_struct *ibm, *itmp; | ||
772 | |||
773 | list_for_each_entry_safe(ibm, itmp, | ||
774 | &tpacpi_all_drivers, | ||
775 | all_drivers) { | ||
776 | if (ibm->shutdown) | ||
777 | (ibm->shutdown)(); | ||
778 | } | ||
779 | } | ||
780 | |||
731 | static struct platform_driver tpacpi_pdriver = { | 781 | static struct platform_driver tpacpi_pdriver = { |
732 | .driver = { | 782 | .driver = { |
733 | .name = TPACPI_DRVR_NAME, | 783 | .name = TPACPI_DRVR_NAME, |
@@ -735,6 +785,7 @@ static struct platform_driver tpacpi_pdriver = { | |||
735 | }, | 785 | }, |
736 | .suspend = tpacpi_suspend_handler, | 786 | .suspend = tpacpi_suspend_handler, |
737 | .resume = tpacpi_resume_handler, | 787 | .resume = tpacpi_resume_handler, |
788 | .shutdown = tpacpi_shutdown_handler, | ||
738 | }; | 789 | }; |
739 | 790 | ||
740 | static struct platform_driver tpacpi_hwmon_pdriver = { | 791 | static struct platform_driver tpacpi_hwmon_pdriver = { |
@@ -922,11 +973,27 @@ static int __init tpacpi_new_rfkill(const unsigned int id, | |||
922 | struct rfkill **rfk, | 973 | struct rfkill **rfk, |
923 | const enum rfkill_type rfktype, | 974 | const enum rfkill_type rfktype, |
924 | const char *name, | 975 | const char *name, |
976 | const bool set_default, | ||
925 | int (*toggle_radio)(void *, enum rfkill_state), | 977 | int (*toggle_radio)(void *, enum rfkill_state), |
926 | int (*get_state)(void *, enum rfkill_state *)) | 978 | int (*get_state)(void *, enum rfkill_state *)) |
927 | { | 979 | { |
928 | int res; | 980 | int res; |
929 | enum rfkill_state initial_state; | 981 | enum rfkill_state initial_state = RFKILL_STATE_SOFT_BLOCKED; |
982 | |||
983 | res = get_state(NULL, &initial_state); | ||
984 | if (res < 0) { | ||
985 | printk(TPACPI_ERR | ||
986 | "failed to read initial state for %s, error %d; " | ||
987 | "will turn radio off\n", name, res); | ||
988 | } else if (set_default) { | ||
989 | /* try to set the initial state as the default for the rfkill | ||
990 | * type, since we ask the firmware to preserve it across S5 in | ||
991 | * NVRAM */ | ||
992 | rfkill_set_default(rfktype, | ||
993 | (initial_state == RFKILL_STATE_UNBLOCKED) ? | ||
994 | RFKILL_STATE_UNBLOCKED : | ||
995 | RFKILL_STATE_SOFT_BLOCKED); | ||
996 | } | ||
930 | 997 | ||
931 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); | 998 | *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); |
932 | if (!*rfk) { | 999 | if (!*rfk) { |
@@ -938,9 +1005,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id, | |||
938 | (*rfk)->name = name; | 1005 | (*rfk)->name = name; |
939 | (*rfk)->get_state = get_state; | 1006 | (*rfk)->get_state = get_state; |
940 | (*rfk)->toggle_radio = toggle_radio; | 1007 | (*rfk)->toggle_radio = toggle_radio; |
941 | 1008 | (*rfk)->state = initial_state; | |
942 | if (!get_state(NULL, &initial_state)) | ||
943 | (*rfk)->state = initial_state; | ||
944 | 1009 | ||
945 | res = rfkill_register(*rfk); | 1010 | res = rfkill_register(*rfk); |
946 | if (res < 0) { | 1011 | if (res < 0) { |
@@ -1006,6 +1071,119 @@ static DRIVER_ATTR(version, S_IRUGO, | |||
1006 | 1071 | ||
1007 | /* --------------------------------------------------------------------- */ | 1072 | /* --------------------------------------------------------------------- */ |
1008 | 1073 | ||
1074 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
1075 | |||
1076 | static void tpacpi_send_radiosw_update(void); | ||
1077 | |||
1078 | /* wlsw_emulstate ------------------------------------------------------ */ | ||
1079 | static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv, | ||
1080 | char *buf) | ||
1081 | { | ||
1082 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wlsw_emulstate); | ||
1083 | } | ||
1084 | |||
1085 | static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv, | ||
1086 | const char *buf, size_t count) | ||
1087 | { | ||
1088 | unsigned long t; | ||
1089 | |||
1090 | if (parse_strtoul(buf, 1, &t)) | ||
1091 | return -EINVAL; | ||
1092 | |||
1093 | if (tpacpi_wlsw_emulstate != t) { | ||
1094 | tpacpi_wlsw_emulstate = !!t; | ||
1095 | tpacpi_send_radiosw_update(); | ||
1096 | } else | ||
1097 | tpacpi_wlsw_emulstate = !!t; | ||
1098 | |||
1099 | return count; | ||
1100 | } | ||
1101 | |||
1102 | static DRIVER_ATTR(wlsw_emulstate, S_IWUSR | S_IRUGO, | ||
1103 | tpacpi_driver_wlsw_emulstate_show, | ||
1104 | tpacpi_driver_wlsw_emulstate_store); | ||
1105 | |||
1106 | /* bluetooth_emulstate ------------------------------------------------- */ | ||
1107 | static ssize_t tpacpi_driver_bluetooth_emulstate_show( | ||
1108 | struct device_driver *drv, | ||
1109 | char *buf) | ||
1110 | { | ||
1111 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_bluetooth_emulstate); | ||
1112 | } | ||
1113 | |||
1114 | static ssize_t tpacpi_driver_bluetooth_emulstate_store( | ||
1115 | struct device_driver *drv, | ||
1116 | const char *buf, size_t count) | ||
1117 | { | ||
1118 | unsigned long t; | ||
1119 | |||
1120 | if (parse_strtoul(buf, 1, &t)) | ||
1121 | return -EINVAL; | ||
1122 | |||
1123 | tpacpi_bluetooth_emulstate = !!t; | ||
1124 | |||
1125 | return count; | ||
1126 | } | ||
1127 | |||
1128 | static DRIVER_ATTR(bluetooth_emulstate, S_IWUSR | S_IRUGO, | ||
1129 | tpacpi_driver_bluetooth_emulstate_show, | ||
1130 | tpacpi_driver_bluetooth_emulstate_store); | ||
1131 | |||
1132 | /* wwan_emulstate ------------------------------------------------- */ | ||
1133 | static ssize_t tpacpi_driver_wwan_emulstate_show( | ||
1134 | struct device_driver *drv, | ||
1135 | char *buf) | ||
1136 | { | ||
1137 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wwan_emulstate); | ||
1138 | } | ||
1139 | |||
1140 | static ssize_t tpacpi_driver_wwan_emulstate_store( | ||
1141 | struct device_driver *drv, | ||
1142 | const char *buf, size_t count) | ||
1143 | { | ||
1144 | unsigned long t; | ||
1145 | |||
1146 | if (parse_strtoul(buf, 1, &t)) | ||
1147 | return -EINVAL; | ||
1148 | |||
1149 | tpacpi_wwan_emulstate = !!t; | ||
1150 | |||
1151 | return count; | ||
1152 | } | ||
1153 | |||
1154 | static DRIVER_ATTR(wwan_emulstate, S_IWUSR | S_IRUGO, | ||
1155 | tpacpi_driver_wwan_emulstate_show, | ||
1156 | tpacpi_driver_wwan_emulstate_store); | ||
1157 | |||
1158 | /* uwb_emulstate ------------------------------------------------- */ | ||
1159 | static ssize_t tpacpi_driver_uwb_emulstate_show( | ||
1160 | struct device_driver *drv, | ||
1161 | char *buf) | ||
1162 | { | ||
1163 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate); | ||
1164 | } | ||
1165 | |||
1166 | static ssize_t tpacpi_driver_uwb_emulstate_store( | ||
1167 | struct device_driver *drv, | ||
1168 | const char *buf, size_t count) | ||
1169 | { | ||
1170 | unsigned long t; | ||
1171 | |||
1172 | if (parse_strtoul(buf, 1, &t)) | ||
1173 | return -EINVAL; | ||
1174 | |||
1175 | tpacpi_uwb_emulstate = !!t; | ||
1176 | |||
1177 | return count; | ||
1178 | } | ||
1179 | |||
1180 | static DRIVER_ATTR(uwb_emulstate, S_IWUSR | S_IRUGO, | ||
1181 | tpacpi_driver_uwb_emulstate_show, | ||
1182 | tpacpi_driver_uwb_emulstate_store); | ||
1183 | #endif | ||
1184 | |||
1185 | /* --------------------------------------------------------------------- */ | ||
1186 | |||
1009 | static struct driver_attribute *tpacpi_driver_attributes[] = { | 1187 | static struct driver_attribute *tpacpi_driver_attributes[] = { |
1010 | &driver_attr_debug_level, &driver_attr_version, | 1188 | &driver_attr_debug_level, &driver_attr_version, |
1011 | &driver_attr_interface_version, | 1189 | &driver_attr_interface_version, |
@@ -1022,6 +1200,17 @@ static int __init tpacpi_create_driver_attributes(struct device_driver *drv) | |||
1022 | i++; | 1200 | i++; |
1023 | } | 1201 | } |
1024 | 1202 | ||
1203 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
1204 | if (!res && dbg_wlswemul) | ||
1205 | res = driver_create_file(drv, &driver_attr_wlsw_emulstate); | ||
1206 | if (!res && dbg_bluetoothemul) | ||
1207 | res = driver_create_file(drv, &driver_attr_bluetooth_emulstate); | ||
1208 | if (!res && dbg_wwanemul) | ||
1209 | res = driver_create_file(drv, &driver_attr_wwan_emulstate); | ||
1210 | if (!res && dbg_uwbemul) | ||
1211 | res = driver_create_file(drv, &driver_attr_uwb_emulstate); | ||
1212 | #endif | ||
1213 | |||
1025 | return res; | 1214 | return res; |
1026 | } | 1215 | } |
1027 | 1216 | ||
@@ -1031,6 +1220,13 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) | |||
1031 | 1220 | ||
1032 | for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) | 1221 | for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) |
1033 | driver_remove_file(drv, tpacpi_driver_attributes[i]); | 1222 | driver_remove_file(drv, tpacpi_driver_attributes[i]); |
1223 | |||
1224 | #ifdef THINKPAD_ACPI_DEBUGFACILITIES | ||
1225 | driver_remove_file(drv, &driver_attr_wlsw_emulstate); | ||
1226 | driver_remove_file(drv, &driver_attr_bluetooth_emulstate); | ||
1227 | driver_remove_file(drv, &driver_attr_wwan_emulstate); | ||
1228 | driver_remove_file(drv, &driver_attr_uwb_emulstate); | ||
1229 | #endif | ||
1034 | } | 1230 | } |
1035 | 1231 | ||
1036 | /**************************************************************************** | 1232 | /**************************************************************************** |
@@ -1216,6 +1412,12 @@ static struct attribute_set *hotkey_dev_attributes; | |||
1216 | 1412 | ||
1217 | static int hotkey_get_wlsw(int *status) | 1413 | static int hotkey_get_wlsw(int *status) |
1218 | { | 1414 | { |
1415 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
1416 | if (dbg_wlswemul) { | ||
1417 | *status = !!tpacpi_wlsw_emulstate; | ||
1418 | return 0; | ||
1419 | } | ||
1420 | #endif | ||
1219 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) | 1421 | if (!acpi_evalf(hkey_handle, status, "WLSW", "d")) |
1220 | return -EIO; | 1422 | return -EIO; |
1221 | return 0; | 1423 | return 0; |
@@ -1678,7 +1880,7 @@ static ssize_t hotkey_mask_show(struct device *dev, | |||
1678 | { | 1880 | { |
1679 | int res; | 1881 | int res; |
1680 | 1882 | ||
1681 | if (mutex_lock_interruptible(&hotkey_mutex)) | 1883 | if (mutex_lock_killable(&hotkey_mutex)) |
1682 | return -ERESTARTSYS; | 1884 | return -ERESTARTSYS; |
1683 | res = hotkey_mask_get(); | 1885 | res = hotkey_mask_get(); |
1684 | mutex_unlock(&hotkey_mutex); | 1886 | mutex_unlock(&hotkey_mutex); |
@@ -1697,7 +1899,7 @@ static ssize_t hotkey_mask_store(struct device *dev, | |||
1697 | if (parse_strtoul(buf, 0xffffffffUL, &t)) | 1899 | if (parse_strtoul(buf, 0xffffffffUL, &t)) |
1698 | return -EINVAL; | 1900 | return -EINVAL; |
1699 | 1901 | ||
1700 | if (mutex_lock_interruptible(&hotkey_mutex)) | 1902 | if (mutex_lock_killable(&hotkey_mutex)) |
1701 | return -ERESTARTSYS; | 1903 | return -ERESTARTSYS; |
1702 | 1904 | ||
1703 | res = hotkey_mask_set(t); | 1905 | res = hotkey_mask_set(t); |
@@ -1783,7 +1985,7 @@ static ssize_t hotkey_source_mask_store(struct device *dev, | |||
1783 | ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) | 1985 | ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0)) |
1784 | return -EINVAL; | 1986 | return -EINVAL; |
1785 | 1987 | ||
1786 | if (mutex_lock_interruptible(&hotkey_mutex)) | 1988 | if (mutex_lock_killable(&hotkey_mutex)) |
1787 | return -ERESTARTSYS; | 1989 | return -ERESTARTSYS; |
1788 | 1990 | ||
1789 | HOTKEY_CONFIG_CRITICAL_START | 1991 | HOTKEY_CONFIG_CRITICAL_START |
@@ -1818,7 +2020,7 @@ static ssize_t hotkey_poll_freq_store(struct device *dev, | |||
1818 | if (parse_strtoul(buf, 25, &t)) | 2020 | if (parse_strtoul(buf, 25, &t)) |
1819 | return -EINVAL; | 2021 | return -EINVAL; |
1820 | 2022 | ||
1821 | if (mutex_lock_interruptible(&hotkey_mutex)) | 2023 | if (mutex_lock_killable(&hotkey_mutex)) |
1822 | return -ERESTARTSYS; | 2024 | return -ERESTARTSYS; |
1823 | 2025 | ||
1824 | hotkey_poll_freq = t; | 2026 | hotkey_poll_freq = t; |
@@ -1958,6 +2160,7 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
1958 | 2160 | ||
1959 | static void bluetooth_update_rfk(void); | 2161 | static void bluetooth_update_rfk(void); |
1960 | static void wan_update_rfk(void); | 2162 | static void wan_update_rfk(void); |
2163 | static void uwb_update_rfk(void); | ||
1961 | static void tpacpi_send_radiosw_update(void) | 2164 | static void tpacpi_send_radiosw_update(void) |
1962 | { | 2165 | { |
1963 | int wlsw; | 2166 | int wlsw; |
@@ -1967,6 +2170,8 @@ static void tpacpi_send_radiosw_update(void) | |||
1967 | bluetooth_update_rfk(); | 2170 | bluetooth_update_rfk(); |
1968 | if (tp_features.wan) | 2171 | if (tp_features.wan) |
1969 | wan_update_rfk(); | 2172 | wan_update_rfk(); |
2173 | if (tp_features.uwb) | ||
2174 | uwb_update_rfk(); | ||
1970 | 2175 | ||
1971 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | 2176 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { |
1972 | mutex_lock(&tpacpi_inputdev_send_mutex); | 2177 | mutex_lock(&tpacpi_inputdev_send_mutex); |
@@ -2222,6 +2427,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2222 | hotkey_source_mask, hotkey_poll_freq); | 2427 | hotkey_source_mask, hotkey_poll_freq); |
2223 | #endif | 2428 | #endif |
2224 | 2429 | ||
2430 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
2431 | if (dbg_wlswemul) { | ||
2432 | tp_features.hotkey_wlsw = 1; | ||
2433 | printk(TPACPI_INFO | ||
2434 | "radio switch emulation enabled\n"); | ||
2435 | } else | ||
2436 | #endif | ||
2225 | /* Not all thinkpads have a hardware radio switch */ | 2437 | /* Not all thinkpads have a hardware radio switch */ |
2226 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { | 2438 | if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { |
2227 | tp_features.hotkey_wlsw = 1; | 2439 | tp_features.hotkey_wlsw = 1; |
@@ -2361,13 +2573,154 @@ err_exit: | |||
2361 | return (res < 0)? res : 1; | 2573 | return (res < 0)? res : 1; |
2362 | } | 2574 | } |
2363 | 2575 | ||
2576 | static bool hotkey_notify_hotkey(const u32 hkey, | ||
2577 | bool *send_acpi_ev, | ||
2578 | bool *ignore_acpi_ev) | ||
2579 | { | ||
2580 | /* 0x1000-0x1FFF: key presses */ | ||
2581 | unsigned int scancode = hkey & 0xfff; | ||
2582 | *send_acpi_ev = true; | ||
2583 | *ignore_acpi_ev = false; | ||
2584 | |||
2585 | if (scancode > 0 && scancode < 0x21) { | ||
2586 | scancode--; | ||
2587 | if (!(hotkey_source_mask & (1 << scancode))) { | ||
2588 | tpacpi_input_send_key(scancode); | ||
2589 | *send_acpi_ev = false; | ||
2590 | } else { | ||
2591 | *ignore_acpi_ev = true; | ||
2592 | } | ||
2593 | return true; | ||
2594 | } | ||
2595 | return false; | ||
2596 | } | ||
2597 | |||
2598 | static bool hotkey_notify_wakeup(const u32 hkey, | ||
2599 | bool *send_acpi_ev, | ||
2600 | bool *ignore_acpi_ev) | ||
2601 | { | ||
2602 | /* 0x2000-0x2FFF: Wakeup reason */ | ||
2603 | *send_acpi_ev = true; | ||
2604 | *ignore_acpi_ev = false; | ||
2605 | |||
2606 | switch (hkey) { | ||
2607 | case 0x2304: /* suspend, undock */ | ||
2608 | case 0x2404: /* hibernation, undock */ | ||
2609 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; | ||
2610 | *ignore_acpi_ev = true; | ||
2611 | break; | ||
2612 | |||
2613 | case 0x2305: /* suspend, bay eject */ | ||
2614 | case 0x2405: /* hibernation, bay eject */ | ||
2615 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; | ||
2616 | *ignore_acpi_ev = true; | ||
2617 | break; | ||
2618 | |||
2619 | case 0x2313: /* Battery on critical low level (S3) */ | ||
2620 | case 0x2413: /* Battery on critical low level (S4) */ | ||
2621 | printk(TPACPI_ALERT | ||
2622 | "EMERGENCY WAKEUP: battery almost empty\n"); | ||
2623 | /* how to auto-heal: */ | ||
2624 | /* 2313: woke up from S3, go to S4/S5 */ | ||
2625 | /* 2413: woke up from S4, go to S5 */ | ||
2626 | break; | ||
2627 | |||
2628 | default: | ||
2629 | return false; | ||
2630 | } | ||
2631 | |||
2632 | if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { | ||
2633 | printk(TPACPI_INFO | ||
2634 | "woke up due to a hot-unplug " | ||
2635 | "request...\n"); | ||
2636 | hotkey_wakeup_reason_notify_change(); | ||
2637 | } | ||
2638 | return true; | ||
2639 | } | ||
2640 | |||
2641 | static bool hotkey_notify_usrevent(const u32 hkey, | ||
2642 | bool *send_acpi_ev, | ||
2643 | bool *ignore_acpi_ev) | ||
2644 | { | ||
2645 | /* 0x5000-0x5FFF: human interface helpers */ | ||
2646 | *send_acpi_ev = true; | ||
2647 | *ignore_acpi_ev = false; | ||
2648 | |||
2649 | switch (hkey) { | ||
2650 | case 0x5010: /* Lenovo new BIOS: brightness changed */ | ||
2651 | case 0x500b: /* X61t: tablet pen inserted into bay */ | ||
2652 | case 0x500c: /* X61t: tablet pen removed from bay */ | ||
2653 | return true; | ||
2654 | |||
2655 | case 0x5009: /* X41t-X61t: swivel up (tablet mode) */ | ||
2656 | case 0x500a: /* X41t-X61t: swivel down (normal mode) */ | ||
2657 | tpacpi_input_send_tabletsw(); | ||
2658 | hotkey_tablet_mode_notify_change(); | ||
2659 | *send_acpi_ev = false; | ||
2660 | return true; | ||
2661 | |||
2662 | case 0x5001: | ||
2663 | case 0x5002: | ||
2664 | /* LID switch events. Do not propagate */ | ||
2665 | *ignore_acpi_ev = true; | ||
2666 | return true; | ||
2667 | |||
2668 | default: | ||
2669 | return false; | ||
2670 | } | ||
2671 | } | ||
2672 | |||
2673 | static bool hotkey_notify_thermal(const u32 hkey, | ||
2674 | bool *send_acpi_ev, | ||
2675 | bool *ignore_acpi_ev) | ||
2676 | { | ||
2677 | /* 0x6000-0x6FFF: thermal alarms */ | ||
2678 | *send_acpi_ev = true; | ||
2679 | *ignore_acpi_ev = false; | ||
2680 | |||
2681 | switch (hkey) { | ||
2682 | case 0x6011: | ||
2683 | printk(TPACPI_CRIT | ||
2684 | "THERMAL ALARM: battery is too hot!\n"); | ||
2685 | /* recommended action: warn user through gui */ | ||
2686 | return true; | ||
2687 | case 0x6012: | ||
2688 | printk(TPACPI_ALERT | ||
2689 | "THERMAL EMERGENCY: battery is extremely hot!\n"); | ||
2690 | /* recommended action: immediate sleep/hibernate */ | ||
2691 | return true; | ||
2692 | case 0x6021: | ||
2693 | printk(TPACPI_CRIT | ||
2694 | "THERMAL ALARM: " | ||
2695 | "a sensor reports something is too hot!\n"); | ||
2696 | /* recommended action: warn user through gui, that */ | ||
2697 | /* some internal component is too hot */ | ||
2698 | return true; | ||
2699 | case 0x6022: | ||
2700 | printk(TPACPI_ALERT | ||
2701 | "THERMAL EMERGENCY: " | ||
2702 | "a sensor reports something is extremely hot!\n"); | ||
2703 | /* recommended action: immediate sleep/hibernate */ | ||
2704 | return true; | ||
2705 | case 0x6030: | ||
2706 | printk(TPACPI_INFO | ||
2707 | "EC reports that Thermal Table has changed\n"); | ||
2708 | /* recommended action: do nothing, we don't have | ||
2709 | * Lenovo ATM information */ | ||
2710 | return true; | ||
2711 | default: | ||
2712 | printk(TPACPI_ALERT | ||
2713 | "THERMAL ALERT: unknown thermal alarm received\n"); | ||
2714 | return false; | ||
2715 | } | ||
2716 | } | ||
2717 | |||
2364 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 2718 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
2365 | { | 2719 | { |
2366 | u32 hkey; | 2720 | u32 hkey; |
2367 | unsigned int scancode; | 2721 | bool send_acpi_ev; |
2368 | int send_acpi_ev; | 2722 | bool ignore_acpi_ev; |
2369 | int ignore_acpi_ev; | 2723 | bool known_ev; |
2370 | int unk_ev; | ||
2371 | 2724 | ||
2372 | if (event != 0x80) { | 2725 | if (event != 0x80) { |
2373 | printk(TPACPI_ERR | 2726 | printk(TPACPI_ERR |
@@ -2375,7 +2728,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2375 | /* forward it to userspace, maybe it knows how to handle it */ | 2728 | /* forward it to userspace, maybe it knows how to handle it */ |
2376 | acpi_bus_generate_netlink_event( | 2729 | acpi_bus_generate_netlink_event( |
2377 | ibm->acpi->device->pnp.device_class, | 2730 | ibm->acpi->device->pnp.device_class, |
2378 | ibm->acpi->device->dev.bus_id, | 2731 | dev_name(&ibm->acpi->device->dev), |
2379 | event, 0); | 2732 | event, 0); |
2380 | return; | 2733 | return; |
2381 | } | 2734 | } |
@@ -2391,107 +2744,72 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2391 | return; | 2744 | return; |
2392 | } | 2745 | } |
2393 | 2746 | ||
2394 | send_acpi_ev = 1; | 2747 | send_acpi_ev = true; |
2395 | ignore_acpi_ev = 0; | 2748 | ignore_acpi_ev = false; |
2396 | unk_ev = 0; | ||
2397 | 2749 | ||
2398 | switch (hkey >> 12) { | 2750 | switch (hkey >> 12) { |
2399 | case 1: | 2751 | case 1: |
2400 | /* 0x1000-0x1FFF: key presses */ | 2752 | /* 0x1000-0x1FFF: key presses */ |
2401 | scancode = hkey & 0xfff; | 2753 | known_ev = hotkey_notify_hotkey(hkey, &send_acpi_ev, |
2402 | if (scancode > 0 && scancode < 0x21) { | 2754 | &ignore_acpi_ev); |
2403 | scancode--; | ||
2404 | if (!(hotkey_source_mask & (1 << scancode))) { | ||
2405 | tpacpi_input_send_key(scancode); | ||
2406 | send_acpi_ev = 0; | ||
2407 | } else { | ||
2408 | ignore_acpi_ev = 1; | ||
2409 | } | ||
2410 | } else { | ||
2411 | unk_ev = 1; | ||
2412 | } | ||
2413 | break; | 2755 | break; |
2414 | case 2: | 2756 | case 2: |
2415 | /* Wakeup reason */ | 2757 | /* 0x2000-0x2FFF: Wakeup reason */ |
2416 | switch (hkey) { | 2758 | known_ev = hotkey_notify_wakeup(hkey, &send_acpi_ev, |
2417 | case 0x2304: /* suspend, undock */ | 2759 | &ignore_acpi_ev); |
2418 | case 0x2404: /* hibernation, undock */ | ||
2419 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK; | ||
2420 | ignore_acpi_ev = 1; | ||
2421 | break; | ||
2422 | case 0x2305: /* suspend, bay eject */ | ||
2423 | case 0x2405: /* hibernation, bay eject */ | ||
2424 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ; | ||
2425 | ignore_acpi_ev = 1; | ||
2426 | break; | ||
2427 | default: | ||
2428 | unk_ev = 1; | ||
2429 | } | ||
2430 | if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) { | ||
2431 | printk(TPACPI_INFO | ||
2432 | "woke up due to a hot-unplug " | ||
2433 | "request...\n"); | ||
2434 | hotkey_wakeup_reason_notify_change(); | ||
2435 | } | ||
2436 | break; | 2760 | break; |
2437 | case 3: | 2761 | case 3: |
2438 | /* bay-related wakeups */ | 2762 | /* 0x3000-0x3FFF: bay-related wakeups */ |
2439 | if (hkey == 0x3003) { | 2763 | if (hkey == 0x3003) { |
2440 | hotkey_autosleep_ack = 1; | 2764 | hotkey_autosleep_ack = 1; |
2441 | printk(TPACPI_INFO | 2765 | printk(TPACPI_INFO |
2442 | "bay ejected\n"); | 2766 | "bay ejected\n"); |
2443 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2767 | hotkey_wakeup_hotunplug_complete_notify_change(); |
2768 | known_ev = true; | ||
2444 | } else { | 2769 | } else { |
2445 | unk_ev = 1; | 2770 | known_ev = false; |
2446 | } | 2771 | } |
2447 | break; | 2772 | break; |
2448 | case 4: | 2773 | case 4: |
2449 | /* dock-related wakeups */ | 2774 | /* 0x4000-0x4FFF: dock-related wakeups */ |
2450 | if (hkey == 0x4003) { | 2775 | if (hkey == 0x4003) { |
2451 | hotkey_autosleep_ack = 1; | 2776 | hotkey_autosleep_ack = 1; |
2452 | printk(TPACPI_INFO | 2777 | printk(TPACPI_INFO |
2453 | "undocked\n"); | 2778 | "undocked\n"); |
2454 | hotkey_wakeup_hotunplug_complete_notify_change(); | 2779 | hotkey_wakeup_hotunplug_complete_notify_change(); |
2780 | known_ev = true; | ||
2455 | } else { | 2781 | } else { |
2456 | unk_ev = 1; | 2782 | known_ev = false; |
2457 | } | 2783 | } |
2458 | break; | 2784 | break; |
2459 | case 5: | 2785 | case 5: |
2460 | /* 0x5000-0x5FFF: human interface helpers */ | 2786 | /* 0x5000-0x5FFF: human interface helpers */ |
2461 | switch (hkey) { | 2787 | known_ev = hotkey_notify_usrevent(hkey, &send_acpi_ev, |
2462 | case 0x5010: /* Lenovo new BIOS: brightness changed */ | 2788 | &ignore_acpi_ev); |
2463 | case 0x500b: /* X61t: tablet pen inserted into bay */ | 2789 | break; |
2464 | case 0x500c: /* X61t: tablet pen removed from bay */ | 2790 | case 6: |
2465 | break; | 2791 | /* 0x6000-0x6FFF: thermal alarms */ |
2466 | case 0x5009: /* X41t-X61t: swivel up (tablet mode) */ | 2792 | known_ev = hotkey_notify_thermal(hkey, &send_acpi_ev, |
2467 | case 0x500a: /* X41t-X61t: swivel down (normal mode) */ | 2793 | &ignore_acpi_ev); |
2468 | tpacpi_input_send_tabletsw(); | ||
2469 | hotkey_tablet_mode_notify_change(); | ||
2470 | send_acpi_ev = 0; | ||
2471 | break; | ||
2472 | case 0x5001: | ||
2473 | case 0x5002: | ||
2474 | /* LID switch events. Do not propagate */ | ||
2475 | ignore_acpi_ev = 1; | ||
2476 | break; | ||
2477 | default: | ||
2478 | unk_ev = 1; | ||
2479 | } | ||
2480 | break; | 2794 | break; |
2481 | case 7: | 2795 | case 7: |
2482 | /* 0x7000-0x7FFF: misc */ | 2796 | /* 0x7000-0x7FFF: misc */ |
2483 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { | 2797 | if (tp_features.hotkey_wlsw && hkey == 0x7000) { |
2484 | tpacpi_send_radiosw_update(); | 2798 | tpacpi_send_radiosw_update(); |
2485 | send_acpi_ev = 0; | 2799 | send_acpi_ev = 0; |
2800 | known_ev = true; | ||
2486 | break; | 2801 | break; |
2487 | } | 2802 | } |
2488 | /* fallthrough to default */ | 2803 | /* fallthrough to default */ |
2489 | default: | 2804 | default: |
2490 | unk_ev = 1; | 2805 | known_ev = false; |
2491 | } | 2806 | } |
2492 | if (unk_ev) { | 2807 | if (!known_ev) { |
2493 | printk(TPACPI_NOTICE | 2808 | printk(TPACPI_NOTICE |
2494 | "unhandled HKEY event 0x%04x\n", hkey); | 2809 | "unhandled HKEY event 0x%04x\n", hkey); |
2810 | printk(TPACPI_NOTICE | ||
2811 | "please report the conditions when this " | ||
2812 | "event happened to %s\n", TPACPI_MAIL); | ||
2495 | } | 2813 | } |
2496 | 2814 | ||
2497 | /* Legacy events */ | 2815 | /* Legacy events */ |
@@ -2505,7 +2823,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
2505 | if (!ignore_acpi_ev && send_acpi_ev) { | 2823 | if (!ignore_acpi_ev && send_acpi_ev) { |
2506 | acpi_bus_generate_netlink_event( | 2824 | acpi_bus_generate_netlink_event( |
2507 | ibm->acpi->device->pnp.device_class, | 2825 | ibm->acpi->device->pnp.device_class, |
2508 | ibm->acpi->device->dev.bus_id, | 2826 | dev_name(&ibm->acpi->device->dev), |
2509 | event, hkey); | 2827 | event, hkey); |
2510 | } | 2828 | } |
2511 | } | 2829 | } |
@@ -2544,7 +2862,7 @@ static int hotkey_read(char *p) | |||
2544 | return len; | 2862 | return len; |
2545 | } | 2863 | } |
2546 | 2864 | ||
2547 | if (mutex_lock_interruptible(&hotkey_mutex)) | 2865 | if (mutex_lock_killable(&hotkey_mutex)) |
2548 | return -ERESTARTSYS; | 2866 | return -ERESTARTSYS; |
2549 | res = hotkey_status_get(&status); | 2867 | res = hotkey_status_get(&status); |
2550 | if (!res) | 2868 | if (!res) |
@@ -2575,7 +2893,7 @@ static int hotkey_write(char *buf) | |||
2575 | if (!tp_features.hotkey) | 2893 | if (!tp_features.hotkey) |
2576 | return -ENODEV; | 2894 | return -ENODEV; |
2577 | 2895 | ||
2578 | if (mutex_lock_interruptible(&hotkey_mutex)) | 2896 | if (mutex_lock_killable(&hotkey_mutex)) |
2579 | return -ERESTARTSYS; | 2897 | return -ERESTARTSYS; |
2580 | 2898 | ||
2581 | status = -1; | 2899 | status = -1; |
@@ -2640,11 +2958,28 @@ enum { | |||
2640 | /* ACPI GBDC/SBDC bits */ | 2958 | /* ACPI GBDC/SBDC bits */ |
2641 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ | 2959 | TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ |
2642 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ | 2960 | TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ |
2643 | TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ | 2961 | TP_ACPI_BLUETOOTH_RESUMECTRL = 0x04, /* Bluetooth state at resume: |
2962 | off / last state */ | ||
2963 | }; | ||
2964 | |||
2965 | enum { | ||
2966 | /* ACPI \BLTH commands */ | ||
2967 | TP_ACPI_BLTH_GET_ULTRAPORT_ID = 0x00, /* Get Ultraport BT ID */ | ||
2968 | TP_ACPI_BLTH_GET_PWR_ON_RESUME = 0x01, /* Get power-on-resume state */ | ||
2969 | TP_ACPI_BLTH_PWR_ON_ON_RESUME = 0x02, /* Resume powered on */ | ||
2970 | TP_ACPI_BLTH_PWR_OFF_ON_RESUME = 0x03, /* Resume powered off */ | ||
2971 | TP_ACPI_BLTH_SAVE_STATE = 0x05, /* Save state for S4/S5 */ | ||
2644 | }; | 2972 | }; |
2645 | 2973 | ||
2646 | static struct rfkill *tpacpi_bluetooth_rfkill; | 2974 | static struct rfkill *tpacpi_bluetooth_rfkill; |
2647 | 2975 | ||
2976 | static void bluetooth_suspend(pm_message_t state) | ||
2977 | { | ||
2978 | /* Try to make sure radio will resume powered off */ | ||
2979 | acpi_evalf(NULL, NULL, "\\BLTH", "vd", | ||
2980 | TP_ACPI_BLTH_PWR_OFF_ON_RESUME); | ||
2981 | } | ||
2982 | |||
2648 | static int bluetooth_get_radiosw(void) | 2983 | static int bluetooth_get_radiosw(void) |
2649 | { | 2984 | { |
2650 | int status; | 2985 | int status; |
@@ -2656,6 +2991,12 @@ static int bluetooth_get_radiosw(void) | |||
2656 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | 2991 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) |
2657 | return RFKILL_STATE_HARD_BLOCKED; | 2992 | return RFKILL_STATE_HARD_BLOCKED; |
2658 | 2993 | ||
2994 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
2995 | if (dbg_bluetoothemul) | ||
2996 | return (tpacpi_bluetooth_emulstate) ? | ||
2997 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
2998 | #endif | ||
2999 | |||
2659 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 3000 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) |
2660 | return -EIO; | 3001 | return -EIO; |
2661 | 3002 | ||
@@ -2689,12 +3030,20 @@ static int bluetooth_set_radiosw(int radio_on, int update_rfk) | |||
2689 | && radio_on) | 3030 | && radio_on) |
2690 | return -EPERM; | 3031 | return -EPERM; |
2691 | 3032 | ||
2692 | if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 3033 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
2693 | return -EIO; | 3034 | if (dbg_bluetoothemul) { |
3035 | tpacpi_bluetooth_emulstate = !!radio_on; | ||
3036 | if (update_rfk) | ||
3037 | bluetooth_update_rfk(); | ||
3038 | return 0; | ||
3039 | } | ||
3040 | #endif | ||
3041 | |||
3042 | /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ | ||
2694 | if (radio_on) | 3043 | if (radio_on) |
2695 | status |= TP_ACPI_BLUETOOTH_RADIOSSW; | 3044 | status = TP_ACPI_BLUETOOTH_RADIOSSW; |
2696 | else | 3045 | else |
2697 | status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; | 3046 | status = 0; |
2698 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 3047 | if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) |
2699 | return -EIO; | 3048 | return -EIO; |
2700 | 3049 | ||
@@ -2765,8 +3114,19 @@ static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) | |||
2765 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | 3114 | return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); |
2766 | } | 3115 | } |
2767 | 3116 | ||
3117 | static void bluetooth_shutdown(void) | ||
3118 | { | ||
3119 | /* Order firmware to save current state to NVRAM */ | ||
3120 | if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", | ||
3121 | TP_ACPI_BLTH_SAVE_STATE)) | ||
3122 | printk(TPACPI_NOTICE | ||
3123 | "failed to save bluetooth state to NVRAM\n"); | ||
3124 | } | ||
3125 | |||
2768 | static void bluetooth_exit(void) | 3126 | static void bluetooth_exit(void) |
2769 | { | 3127 | { |
3128 | bluetooth_shutdown(); | ||
3129 | |||
2770 | if (tpacpi_bluetooth_rfkill) | 3130 | if (tpacpi_bluetooth_rfkill) |
2771 | rfkill_unregister(tpacpi_bluetooth_rfkill); | 3131 | rfkill_unregister(tpacpi_bluetooth_rfkill); |
2772 | 3132 | ||
@@ -2792,6 +3152,13 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
2792 | str_supported(tp_features.bluetooth), | 3152 | str_supported(tp_features.bluetooth), |
2793 | status); | 3153 | status); |
2794 | 3154 | ||
3155 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3156 | if (dbg_bluetoothemul) { | ||
3157 | tp_features.bluetooth = 1; | ||
3158 | printk(TPACPI_INFO | ||
3159 | "bluetooth switch emulation enabled\n"); | ||
3160 | } else | ||
3161 | #endif | ||
2795 | if (tp_features.bluetooth && | 3162 | if (tp_features.bluetooth && |
2796 | !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { | 3163 | !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { |
2797 | /* no bluetooth hardware present in system */ | 3164 | /* no bluetooth hardware present in system */ |
@@ -2812,6 +3179,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
2812 | &tpacpi_bluetooth_rfkill, | 3179 | &tpacpi_bluetooth_rfkill, |
2813 | RFKILL_TYPE_BLUETOOTH, | 3180 | RFKILL_TYPE_BLUETOOTH, |
2814 | "tpacpi_bluetooth_sw", | 3181 | "tpacpi_bluetooth_sw", |
3182 | true, | ||
2815 | tpacpi_bluetooth_rfk_set, | 3183 | tpacpi_bluetooth_rfk_set, |
2816 | tpacpi_bluetooth_rfk_get); | 3184 | tpacpi_bluetooth_rfk_get); |
2817 | if (res) { | 3185 | if (res) { |
@@ -2864,6 +3232,8 @@ static struct ibm_struct bluetooth_driver_data = { | |||
2864 | .read = bluetooth_read, | 3232 | .read = bluetooth_read, |
2865 | .write = bluetooth_write, | 3233 | .write = bluetooth_write, |
2866 | .exit = bluetooth_exit, | 3234 | .exit = bluetooth_exit, |
3235 | .suspend = bluetooth_suspend, | ||
3236 | .shutdown = bluetooth_shutdown, | ||
2867 | }; | 3237 | }; |
2868 | 3238 | ||
2869 | /************************************************************************* | 3239 | /************************************************************************* |
@@ -2874,11 +3244,19 @@ enum { | |||
2874 | /* ACPI GWAN/SWAN bits */ | 3244 | /* ACPI GWAN/SWAN bits */ |
2875 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ | 3245 | TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ |
2876 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ | 3246 | TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ |
2877 | TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ | 3247 | TP_ACPI_WANCARD_RESUMECTRL = 0x04, /* Wan state at resume: |
3248 | off / last state */ | ||
2878 | }; | 3249 | }; |
2879 | 3250 | ||
2880 | static struct rfkill *tpacpi_wan_rfkill; | 3251 | static struct rfkill *tpacpi_wan_rfkill; |
2881 | 3252 | ||
3253 | static void wan_suspend(pm_message_t state) | ||
3254 | { | ||
3255 | /* Try to make sure radio will resume powered off */ | ||
3256 | acpi_evalf(NULL, NULL, "\\WGSV", "qvd", | ||
3257 | TP_ACPI_WGSV_PWR_OFF_ON_RESUME); | ||
3258 | } | ||
3259 | |||
2882 | static int wan_get_radiosw(void) | 3260 | static int wan_get_radiosw(void) |
2883 | { | 3261 | { |
2884 | int status; | 3262 | int status; |
@@ -2890,6 +3268,12 @@ static int wan_get_radiosw(void) | |||
2890 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | 3268 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) |
2891 | return RFKILL_STATE_HARD_BLOCKED; | 3269 | return RFKILL_STATE_HARD_BLOCKED; |
2892 | 3270 | ||
3271 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3272 | if (dbg_wwanemul) | ||
3273 | return (tpacpi_wwan_emulstate) ? | ||
3274 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
3275 | #endif | ||
3276 | |||
2893 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3277 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) |
2894 | return -EIO; | 3278 | return -EIO; |
2895 | 3279 | ||
@@ -2923,12 +3307,20 @@ static int wan_set_radiosw(int radio_on, int update_rfk) | |||
2923 | && radio_on) | 3307 | && radio_on) |
2924 | return -EPERM; | 3308 | return -EPERM; |
2925 | 3309 | ||
2926 | if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) | 3310 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES |
2927 | return -EIO; | 3311 | if (dbg_wwanemul) { |
3312 | tpacpi_wwan_emulstate = !!radio_on; | ||
3313 | if (update_rfk) | ||
3314 | wan_update_rfk(); | ||
3315 | return 0; | ||
3316 | } | ||
3317 | #endif | ||
3318 | |||
3319 | /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ | ||
2928 | if (radio_on) | 3320 | if (radio_on) |
2929 | status |= TP_ACPI_WANCARD_RADIOSSW; | 3321 | status = TP_ACPI_WANCARD_RADIOSSW; |
2930 | else | 3322 | else |
2931 | status &= ~TP_ACPI_WANCARD_RADIOSSW; | 3323 | status = 0; |
2932 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) | 3324 | if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) |
2933 | return -EIO; | 3325 | return -EIO; |
2934 | 3326 | ||
@@ -2999,8 +3391,19 @@ static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) | |||
2999 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | 3391 | return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); |
3000 | } | 3392 | } |
3001 | 3393 | ||
3394 | static void wan_shutdown(void) | ||
3395 | { | ||
3396 | /* Order firmware to save current state to NVRAM */ | ||
3397 | if (!acpi_evalf(NULL, NULL, "\\WGSV", "vd", | ||
3398 | TP_ACPI_WGSV_SAVE_STATE)) | ||
3399 | printk(TPACPI_NOTICE | ||
3400 | "failed to save WWAN state to NVRAM\n"); | ||
3401 | } | ||
3402 | |||
3002 | static void wan_exit(void) | 3403 | static void wan_exit(void) |
3003 | { | 3404 | { |
3405 | wan_shutdown(); | ||
3406 | |||
3004 | if (tpacpi_wan_rfkill) | 3407 | if (tpacpi_wan_rfkill) |
3005 | rfkill_unregister(tpacpi_wan_rfkill); | 3408 | rfkill_unregister(tpacpi_wan_rfkill); |
3006 | 3409 | ||
@@ -3024,6 +3427,13 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3024 | str_supported(tp_features.wan), | 3427 | str_supported(tp_features.wan), |
3025 | status); | 3428 | status); |
3026 | 3429 | ||
3430 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3431 | if (dbg_wwanemul) { | ||
3432 | tp_features.wan = 1; | ||
3433 | printk(TPACPI_INFO | ||
3434 | "wwan switch emulation enabled\n"); | ||
3435 | } else | ||
3436 | #endif | ||
3027 | if (tp_features.wan && | 3437 | if (tp_features.wan && |
3028 | !(status & TP_ACPI_WANCARD_HWPRESENT)) { | 3438 | !(status & TP_ACPI_WANCARD_HWPRESENT)) { |
3029 | /* no wan hardware present in system */ | 3439 | /* no wan hardware present in system */ |
@@ -3044,6 +3454,7 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3044 | &tpacpi_wan_rfkill, | 3454 | &tpacpi_wan_rfkill, |
3045 | RFKILL_TYPE_WWAN, | 3455 | RFKILL_TYPE_WWAN, |
3046 | "tpacpi_wwan_sw", | 3456 | "tpacpi_wwan_sw", |
3457 | true, | ||
3047 | tpacpi_wan_rfk_set, | 3458 | tpacpi_wan_rfk_set, |
3048 | tpacpi_wan_rfk_get); | 3459 | tpacpi_wan_rfk_get); |
3049 | if (res) { | 3460 | if (res) { |
@@ -3096,6 +3507,164 @@ static struct ibm_struct wan_driver_data = { | |||
3096 | .read = wan_read, | 3507 | .read = wan_read, |
3097 | .write = wan_write, | 3508 | .write = wan_write, |
3098 | .exit = wan_exit, | 3509 | .exit = wan_exit, |
3510 | .suspend = wan_suspend, | ||
3511 | .shutdown = wan_shutdown, | ||
3512 | }; | ||
3513 | |||
3514 | /************************************************************************* | ||
3515 | * UWB subdriver | ||
3516 | */ | ||
3517 | |||
3518 | enum { | ||
3519 | /* ACPI GUWB/SUWB bits */ | ||
3520 | TP_ACPI_UWB_HWPRESENT = 0x01, /* UWB hw available */ | ||
3521 | TP_ACPI_UWB_RADIOSSW = 0x02, /* UWB radio enabled */ | ||
3522 | }; | ||
3523 | |||
3524 | static struct rfkill *tpacpi_uwb_rfkill; | ||
3525 | |||
3526 | static int uwb_get_radiosw(void) | ||
3527 | { | ||
3528 | int status; | ||
3529 | |||
3530 | if (!tp_features.uwb) | ||
3531 | return -ENODEV; | ||
3532 | |||
3533 | /* WLSW overrides UWB in firmware/hardware, reflect that */ | ||
3534 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3535 | return RFKILL_STATE_HARD_BLOCKED; | ||
3536 | |||
3537 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3538 | if (dbg_uwbemul) | ||
3539 | return (tpacpi_uwb_emulstate) ? | ||
3540 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
3541 | #endif | ||
3542 | |||
3543 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) | ||
3544 | return -EIO; | ||
3545 | |||
3546 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? | ||
3547 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
3548 | } | ||
3549 | |||
3550 | static void uwb_update_rfk(void) | ||
3551 | { | ||
3552 | int status; | ||
3553 | |||
3554 | if (!tpacpi_uwb_rfkill) | ||
3555 | return; | ||
3556 | |||
3557 | status = uwb_get_radiosw(); | ||
3558 | if (status < 0) | ||
3559 | return; | ||
3560 | rfkill_force_state(tpacpi_uwb_rfkill, status); | ||
3561 | } | ||
3562 | |||
3563 | static int uwb_set_radiosw(int radio_on, int update_rfk) | ||
3564 | { | ||
3565 | int status; | ||
3566 | |||
3567 | if (!tp_features.uwb) | ||
3568 | return -ENODEV; | ||
3569 | |||
3570 | /* WLSW overrides UWB in firmware/hardware, but there is no | ||
3571 | * reason to risk weird behaviour. */ | ||
3572 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3573 | && radio_on) | ||
3574 | return -EPERM; | ||
3575 | |||
3576 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3577 | if (dbg_uwbemul) { | ||
3578 | tpacpi_uwb_emulstate = !!radio_on; | ||
3579 | if (update_rfk) | ||
3580 | uwb_update_rfk(); | ||
3581 | return 0; | ||
3582 | } | ||
3583 | #endif | ||
3584 | |||
3585 | status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; | ||
3586 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) | ||
3587 | return -EIO; | ||
3588 | |||
3589 | if (update_rfk) | ||
3590 | uwb_update_rfk(); | ||
3591 | |||
3592 | return 0; | ||
3593 | } | ||
3594 | |||
3595 | /* --------------------------------------------------------------------- */ | ||
3596 | |||
3597 | static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) | ||
3598 | { | ||
3599 | int uwbs = uwb_get_radiosw(); | ||
3600 | |||
3601 | if (uwbs < 0) | ||
3602 | return uwbs; | ||
3603 | |||
3604 | *state = uwbs; | ||
3605 | return 0; | ||
3606 | } | ||
3607 | |||
3608 | static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) | ||
3609 | { | ||
3610 | return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3611 | } | ||
3612 | |||
3613 | static void uwb_exit(void) | ||
3614 | { | ||
3615 | if (tpacpi_uwb_rfkill) | ||
3616 | rfkill_unregister(tpacpi_uwb_rfkill); | ||
3617 | } | ||
3618 | |||
3619 | static int __init uwb_init(struct ibm_init_struct *iibm) | ||
3620 | { | ||
3621 | int res; | ||
3622 | int status = 0; | ||
3623 | |||
3624 | vdbg_printk(TPACPI_DBG_INIT, "initializing uwb subdriver\n"); | ||
3625 | |||
3626 | TPACPI_ACPIHANDLE_INIT(hkey); | ||
3627 | |||
3628 | tp_features.uwb = hkey_handle && | ||
3629 | acpi_evalf(hkey_handle, &status, "GUWB", "qd"); | ||
3630 | |||
3631 | vdbg_printk(TPACPI_DBG_INIT, "uwb is %s, status 0x%02x\n", | ||
3632 | str_supported(tp_features.uwb), | ||
3633 | status); | ||
3634 | |||
3635 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3636 | if (dbg_uwbemul) { | ||
3637 | tp_features.uwb = 1; | ||
3638 | printk(TPACPI_INFO | ||
3639 | "uwb switch emulation enabled\n"); | ||
3640 | } else | ||
3641 | #endif | ||
3642 | if (tp_features.uwb && | ||
3643 | !(status & TP_ACPI_UWB_HWPRESENT)) { | ||
3644 | /* no uwb hardware present in system */ | ||
3645 | tp_features.uwb = 0; | ||
3646 | dbg_printk(TPACPI_DBG_INIT, | ||
3647 | "uwb hardware not installed\n"); | ||
3648 | } | ||
3649 | |||
3650 | if (!tp_features.uwb) | ||
3651 | return 1; | ||
3652 | |||
3653 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, | ||
3654 | &tpacpi_uwb_rfkill, | ||
3655 | RFKILL_TYPE_UWB, | ||
3656 | "tpacpi_uwb_sw", | ||
3657 | false, | ||
3658 | tpacpi_uwb_rfk_set, | ||
3659 | tpacpi_uwb_rfk_get); | ||
3660 | |||
3661 | return res; | ||
3662 | } | ||
3663 | |||
3664 | static struct ibm_struct uwb_driver_data = { | ||
3665 | .name = "uwb", | ||
3666 | .exit = uwb_exit, | ||
3667 | .flags.experimental = 1, | ||
3099 | }; | 3668 | }; |
3100 | 3669 | ||
3101 | /************************************************************************* | 3670 | /************************************************************************* |
@@ -3724,7 +4293,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event) | |||
3724 | } | 4293 | } |
3725 | acpi_bus_generate_proc_event(ibm->acpi->device, event, data); | 4294 | acpi_bus_generate_proc_event(ibm->acpi->device, event, data); |
3726 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, | 4295 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, |
3727 | ibm->acpi->device->dev.bus_id, | 4296 | dev_name(&ibm->acpi->device->dev), |
3728 | event, data); | 4297 | event, data); |
3729 | } | 4298 | } |
3730 | 4299 | ||
@@ -3826,7 +4395,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event) | |||
3826 | { | 4395 | { |
3827 | acpi_bus_generate_proc_event(ibm->acpi->device, event, 0); | 4396 | acpi_bus_generate_proc_event(ibm->acpi->device, event, 0); |
3828 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, | 4397 | acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class, |
3829 | ibm->acpi->device->dev.bus_id, | 4398 | dev_name(&ibm->acpi->device->dev), |
3830 | event, 0); | 4399 | event, 0); |
3831 | } | 4400 | } |
3832 | 4401 | ||
@@ -4850,7 +5419,7 @@ static int brightness_set(int value) | |||
4850 | value < 0) | 5419 | value < 0) |
4851 | return -EINVAL; | 5420 | return -EINVAL; |
4852 | 5421 | ||
4853 | res = mutex_lock_interruptible(&brightness_mutex); | 5422 | res = mutex_lock_killable(&brightness_mutex); |
4854 | if (res < 0) | 5423 | if (res < 0) |
4855 | return res; | 5424 | return res; |
4856 | 5425 | ||
@@ -5334,6 +5903,60 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ | |||
5334 | ); /* all others */ | 5903 | ); /* all others */ |
5335 | 5904 | ||
5336 | /* | 5905 | /* |
5906 | * Unitialized HFSP quirk: ACPI DSDT and EC fail to initialize the | ||
5907 | * HFSP register at boot, so it contains 0x07 but the Thinkpad could | ||
5908 | * be in auto mode (0x80). | ||
5909 | * | ||
5910 | * This is corrected by any write to HFSP either by the driver, or | ||
5911 | * by the firmware. | ||
5912 | * | ||
5913 | * We assume 0x07 really means auto mode while this quirk is active, | ||
5914 | * as this is far more likely than the ThinkPad being in level 7, | ||
5915 | * which is only used by the firmware during thermal emergencies. | ||
5916 | */ | ||
5917 | |||
5918 | static void fan_quirk1_detect(void) | ||
5919 | { | ||
5920 | /* In some ThinkPads, neither the EC nor the ACPI | ||
5921 | * DSDT initialize the HFSP register, and it ends up | ||
5922 | * being initially set to 0x07 when it *could* be | ||
5923 | * either 0x07 or 0x80. | ||
5924 | * | ||
5925 | * Enable for TP-1Y (T43), TP-78 (R51e), | ||
5926 | * TP-76 (R52), TP-70 (T43, R52), which are known | ||
5927 | * to be buggy. */ | ||
5928 | if (fan_control_initial_status == 0x07) { | ||
5929 | switch (thinkpad_id.ec_model) { | ||
5930 | case 0x5931: /* TP-1Y */ | ||
5931 | case 0x3837: /* TP-78 */ | ||
5932 | case 0x3637: /* TP-76 */ | ||
5933 | case 0x3037: /* TP-70 */ | ||
5934 | printk(TPACPI_NOTICE | ||
5935 | "fan_init: initial fan status is unknown, " | ||
5936 | "assuming it is in auto mode\n"); | ||
5937 | tp_features.fan_ctrl_status_undef = 1; | ||
5938 | ;; | ||
5939 | } | ||
5940 | } | ||
5941 | } | ||
5942 | |||
5943 | static void fan_quirk1_handle(u8 *fan_status) | ||
5944 | { | ||
5945 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
5946 | if (*fan_status != fan_control_initial_status) { | ||
5947 | /* something changed the HFSP regisnter since | ||
5948 | * driver init time, so it is not undefined | ||
5949 | * anymore */ | ||
5950 | tp_features.fan_ctrl_status_undef = 0; | ||
5951 | } else { | ||
5952 | /* Return most likely status. In fact, it | ||
5953 | * might be the only possible status */ | ||
5954 | *fan_status = TP_EC_FAN_AUTO; | ||
5955 | } | ||
5956 | } | ||
5957 | } | ||
5958 | |||
5959 | /* | ||
5337 | * Call with fan_mutex held | 5960 | * Call with fan_mutex held |
5338 | */ | 5961 | */ |
5339 | static void fan_update_desired_level(u8 status) | 5962 | static void fan_update_desired_level(u8 status) |
@@ -5371,8 +5994,10 @@ static int fan_get_status(u8 *status) | |||
5371 | if (unlikely(!acpi_ec_read(fan_status_offset, &s))) | 5994 | if (unlikely(!acpi_ec_read(fan_status_offset, &s))) |
5372 | return -EIO; | 5995 | return -EIO; |
5373 | 5996 | ||
5374 | if (likely(status)) | 5997 | if (likely(status)) { |
5375 | *status = s; | 5998 | *status = s; |
5999 | fan_quirk1_handle(status); | ||
6000 | } | ||
5376 | 6001 | ||
5377 | break; | 6002 | break; |
5378 | 6003 | ||
@@ -5388,7 +6013,7 @@ static int fan_get_status_safe(u8 *status) | |||
5388 | int rc; | 6013 | int rc; |
5389 | u8 s; | 6014 | u8 s; |
5390 | 6015 | ||
5391 | if (mutex_lock_interruptible(&fan_mutex)) | 6016 | if (mutex_lock_killable(&fan_mutex)) |
5392 | return -ERESTARTSYS; | 6017 | return -ERESTARTSYS; |
5393 | rc = fan_get_status(&s); | 6018 | rc = fan_get_status(&s); |
5394 | if (!rc) | 6019 | if (!rc) |
@@ -5471,7 +6096,7 @@ static int fan_set_level_safe(int level) | |||
5471 | if (!fan_control_allowed) | 6096 | if (!fan_control_allowed) |
5472 | return -EPERM; | 6097 | return -EPERM; |
5473 | 6098 | ||
5474 | if (mutex_lock_interruptible(&fan_mutex)) | 6099 | if (mutex_lock_killable(&fan_mutex)) |
5475 | return -ERESTARTSYS; | 6100 | return -ERESTARTSYS; |
5476 | 6101 | ||
5477 | if (level == TPACPI_FAN_LAST_LEVEL) | 6102 | if (level == TPACPI_FAN_LAST_LEVEL) |
@@ -5493,7 +6118,7 @@ static int fan_set_enable(void) | |||
5493 | if (!fan_control_allowed) | 6118 | if (!fan_control_allowed) |
5494 | return -EPERM; | 6119 | return -EPERM; |
5495 | 6120 | ||
5496 | if (mutex_lock_interruptible(&fan_mutex)) | 6121 | if (mutex_lock_killable(&fan_mutex)) |
5497 | return -ERESTARTSYS; | 6122 | return -ERESTARTSYS; |
5498 | 6123 | ||
5499 | switch (fan_control_access_mode) { | 6124 | switch (fan_control_access_mode) { |
@@ -5548,7 +6173,7 @@ static int fan_set_disable(void) | |||
5548 | if (!fan_control_allowed) | 6173 | if (!fan_control_allowed) |
5549 | return -EPERM; | 6174 | return -EPERM; |
5550 | 6175 | ||
5551 | if (mutex_lock_interruptible(&fan_mutex)) | 6176 | if (mutex_lock_killable(&fan_mutex)) |
5552 | return -ERESTARTSYS; | 6177 | return -ERESTARTSYS; |
5553 | 6178 | ||
5554 | rc = 0; | 6179 | rc = 0; |
@@ -5586,7 +6211,7 @@ static int fan_set_speed(int speed) | |||
5586 | if (!fan_control_allowed) | 6211 | if (!fan_control_allowed) |
5587 | return -EPERM; | 6212 | return -EPERM; |
5588 | 6213 | ||
5589 | if (mutex_lock_interruptible(&fan_mutex)) | 6214 | if (mutex_lock_killable(&fan_mutex)) |
5590 | return -ERESTARTSYS; | 6215 | return -ERESTARTSYS; |
5591 | 6216 | ||
5592 | rc = 0; | 6217 | rc = 0; |
@@ -5682,16 +6307,6 @@ static ssize_t fan_pwm1_enable_show(struct device *dev, | |||
5682 | if (res) | 6307 | if (res) |
5683 | return res; | 6308 | return res; |
5684 | 6309 | ||
5685 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
5686 | if (status != fan_control_initial_status) { | ||
5687 | tp_features.fan_ctrl_status_undef = 0; | ||
5688 | } else { | ||
5689 | /* Return most likely status. In fact, it | ||
5690 | * might be the only possible status */ | ||
5691 | status = TP_EC_FAN_AUTO; | ||
5692 | } | ||
5693 | } | ||
5694 | |||
5695 | if (status & TP_EC_FAN_FULLSPEED) { | 6310 | if (status & TP_EC_FAN_FULLSPEED) { |
5696 | mode = 0; | 6311 | mode = 0; |
5697 | } else if (status & TP_EC_FAN_AUTO) { | 6312 | } else if (status & TP_EC_FAN_AUTO) { |
@@ -5756,14 +6371,6 @@ static ssize_t fan_pwm1_show(struct device *dev, | |||
5756 | if (res) | 6371 | if (res) |
5757 | return res; | 6372 | return res; |
5758 | 6373 | ||
5759 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
5760 | if (status != fan_control_initial_status) { | ||
5761 | tp_features.fan_ctrl_status_undef = 0; | ||
5762 | } else { | ||
5763 | status = TP_EC_FAN_AUTO; | ||
5764 | } | ||
5765 | } | ||
5766 | |||
5767 | if ((status & | 6374 | if ((status & |
5768 | (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) | 6375 | (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) |
5769 | status = fan_control_desired_level; | 6376 | status = fan_control_desired_level; |
@@ -5788,7 +6395,7 @@ static ssize_t fan_pwm1_store(struct device *dev, | |||
5788 | /* scale down from 0-255 to 0-7 */ | 6395 | /* scale down from 0-255 to 0-7 */ |
5789 | newlevel = (s >> 5) & 0x07; | 6396 | newlevel = (s >> 5) & 0x07; |
5790 | 6397 | ||
5791 | if (mutex_lock_interruptible(&fan_mutex)) | 6398 | if (mutex_lock_killable(&fan_mutex)) |
5792 | return -ERESTARTSYS; | 6399 | return -ERESTARTSYS; |
5793 | 6400 | ||
5794 | rc = fan_get_status(&status); | 6401 | rc = fan_get_status(&status); |
@@ -5895,29 +6502,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
5895 | if (likely(acpi_ec_read(fan_status_offset, | 6502 | if (likely(acpi_ec_read(fan_status_offset, |
5896 | &fan_control_initial_status))) { | 6503 | &fan_control_initial_status))) { |
5897 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; | 6504 | fan_status_access_mode = TPACPI_FAN_RD_TPEC; |
5898 | 6505 | fan_quirk1_detect(); | |
5899 | /* In some ThinkPads, neither the EC nor the ACPI | ||
5900 | * DSDT initialize the fan status, and it ends up | ||
5901 | * being set to 0x07 when it *could* be either | ||
5902 | * 0x07 or 0x80. | ||
5903 | * | ||
5904 | * Enable for TP-1Y (T43), TP-78 (R51e), | ||
5905 | * TP-76 (R52), TP-70 (T43, R52), which are known | ||
5906 | * to be buggy. */ | ||
5907 | if (fan_control_initial_status == 0x07) { | ||
5908 | switch (thinkpad_id.ec_model) { | ||
5909 | case 0x5931: /* TP-1Y */ | ||
5910 | case 0x3837: /* TP-78 */ | ||
5911 | case 0x3637: /* TP-76 */ | ||
5912 | case 0x3037: /* TP-70 */ | ||
5913 | printk(TPACPI_NOTICE | ||
5914 | "fan_init: initial fan status " | ||
5915 | "is unknown, assuming it is " | ||
5916 | "in auto mode\n"); | ||
5917 | tp_features.fan_ctrl_status_undef = 1; | ||
5918 | ;; | ||
5919 | } | ||
5920 | } | ||
5921 | } else { | 6506 | } else { |
5922 | printk(TPACPI_ERR | 6507 | printk(TPACPI_ERR |
5923 | "ThinkPad ACPI EC access misbehaving, " | 6508 | "ThinkPad ACPI EC access misbehaving, " |
@@ -6106,15 +6691,6 @@ static int fan_read(char *p) | |||
6106 | if (rc < 0) | 6691 | if (rc < 0) |
6107 | return rc; | 6692 | return rc; |
6108 | 6693 | ||
6109 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
6110 | if (status != fan_control_initial_status) | ||
6111 | tp_features.fan_ctrl_status_undef = 0; | ||
6112 | else | ||
6113 | /* Return most likely status. In fact, it | ||
6114 | * might be the only possible status */ | ||
6115 | status = TP_EC_FAN_AUTO; | ||
6116 | } | ||
6117 | |||
6118 | len += sprintf(p + len, "status:\t\t%s\n", | 6694 | len += sprintf(p + len, "status:\t\t%s\n", |
6119 | (status != 0) ? "enabled" : "disabled"); | 6695 | (status != 0) ? "enabled" : "disabled"); |
6120 | 6696 | ||
@@ -6563,6 +7139,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
6563 | .init = wan_init, | 7139 | .init = wan_init, |
6564 | .data = &wan_driver_data, | 7140 | .data = &wan_driver_data, |
6565 | }, | 7141 | }, |
7142 | { | ||
7143 | .init = uwb_init, | ||
7144 | .data = &uwb_driver_data, | ||
7145 | }, | ||
6566 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO | 7146 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO |
6567 | { | 7147 | { |
6568 | .init = video_init, | 7148 | .init = video_init, |
@@ -6701,6 +7281,32 @@ TPACPI_PARAM(brightness); | |||
6701 | TPACPI_PARAM(volume); | 7281 | TPACPI_PARAM(volume); |
6702 | TPACPI_PARAM(fan); | 7282 | TPACPI_PARAM(fan); |
6703 | 7283 | ||
7284 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
7285 | module_param(dbg_wlswemul, uint, 0); | ||
7286 | MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); | ||
7287 | module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); | ||
7288 | MODULE_PARM_DESC(wlsw_state, | ||
7289 | "Initial state of the emulated WLSW switch"); | ||
7290 | |||
7291 | module_param(dbg_bluetoothemul, uint, 0); | ||
7292 | MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); | ||
7293 | module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); | ||
7294 | MODULE_PARM_DESC(bluetooth_state, | ||
7295 | "Initial state of the emulated bluetooth switch"); | ||
7296 | |||
7297 | module_param(dbg_wwanemul, uint, 0); | ||
7298 | MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); | ||
7299 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); | ||
7300 | MODULE_PARM_DESC(wwan_state, | ||
7301 | "Initial state of the emulated WWAN switch"); | ||
7302 | |||
7303 | module_param(dbg_uwbemul, uint, 0); | ||
7304 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); | ||
7305 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); | ||
7306 | MODULE_PARM_DESC(uwb_state, | ||
7307 | "Initial state of the emulated UWB switch"); | ||
7308 | #endif | ||
7309 | |||
6704 | static void thinkpad_acpi_module_exit(void) | 7310 | static void thinkpad_acpi_module_exit(void) |
6705 | { | 7311 | { |
6706 | struct ibm_struct *ibm, *itmp; | 7312 | struct ibm_struct *ibm, *itmp; |