diff options
-rw-r--r-- | Documentation/laptops/thinkpad-acpi.txt | 18 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 206 |
2 files changed, 223 insertions, 1 deletions
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index ddc371e0a1a6..91c00010b15c 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -1413,6 +1413,24 @@ Sysfs notes: | |||
1413 | rfkill controller switch "tpacpi_wwan_sw": refer to | 1413 | rfkill controller switch "tpacpi_wwan_sw": refer to |
1414 | Documentation/rfkill.txt for details. | 1414 | Documentation/rfkill.txt for details. |
1415 | 1415 | ||
1416 | EXPERIMENTAL: UWB | ||
1417 | ----------------- | ||
1418 | |||
1419 | This feature is marked EXPERIMENTAL because it has not been extensively | ||
1420 | tested and validated in various ThinkPad models yet. The feature may not | ||
1421 | work as expected. USE WITH CAUTION! To use this feature, you need to supply | ||
1422 | the experimental=1 parameter when loading the module. | ||
1423 | |||
1424 | sysfs rfkill class: switch "tpacpi_uwb_sw" | ||
1425 | |||
1426 | This feature exports an rfkill controller for the UWB device, if one is | ||
1427 | present and enabled in the BIOS. | ||
1428 | |||
1429 | Sysfs notes: | ||
1430 | |||
1431 | rfkill controller switch "tpacpi_uwb_sw": refer to | ||
1432 | Documentation/rfkill.txt for details. | ||
1433 | |||
1416 | Multiple Commands, Module Parameters | 1434 | Multiple Commands, Module Parameters |
1417 | ------------------------------------ | 1435 | ------------------------------------ |
1418 | 1436 | ||
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 27d709bac98f..c1d40410ad79 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -169,6 +169,7 @@ enum { | |||
169 | enum { | 169 | enum { |
170 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, | 170 | TPACPI_RFK_BLUETOOTH_SW_ID = 0, |
171 | TPACPI_RFK_WWAN_SW_ID, | 171 | TPACPI_RFK_WWAN_SW_ID, |
172 | TPACPI_RFK_UWB_SW_ID, | ||
172 | }; | 173 | }; |
173 | 174 | ||
174 | /* Debugging */ | 175 | /* Debugging */ |
@@ -261,6 +262,7 @@ static struct { | |||
261 | u32 bright_16levels:1; | 262 | u32 bright_16levels:1; |
262 | u32 bright_acpimode:1; | 263 | u32 bright_acpimode:1; |
263 | u32 wan:1; | 264 | u32 wan:1; |
265 | u32 uwb:1; | ||
264 | u32 fan_ctrl_status_undef:1; | 266 | u32 fan_ctrl_status_undef:1; |
265 | u32 input_device_registered:1; | 267 | u32 input_device_registered:1; |
266 | u32 platform_drv_registered:1; | 268 | u32 platform_drv_registered:1; |
@@ -317,6 +319,8 @@ static int dbg_bluetoothemul; | |||
317 | static int tpacpi_bluetooth_emulstate; | 319 | static int tpacpi_bluetooth_emulstate; |
318 | static int dbg_wwanemul; | 320 | static int dbg_wwanemul; |
319 | static int tpacpi_wwan_emulstate; | 321 | static int tpacpi_wwan_emulstate; |
322 | static int dbg_uwbemul; | ||
323 | static int tpacpi_uwb_emulstate; | ||
320 | #endif | 324 | #endif |
321 | 325 | ||
322 | 326 | ||
@@ -967,6 +971,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id, | |||
967 | struct rfkill **rfk, | 971 | struct rfkill **rfk, |
968 | const enum rfkill_type rfktype, | 972 | const enum rfkill_type rfktype, |
969 | const char *name, | 973 | const char *name, |
974 | const bool set_default, | ||
970 | int (*toggle_radio)(void *, enum rfkill_state), | 975 | int (*toggle_radio)(void *, enum rfkill_state), |
971 | int (*get_state)(void *, enum rfkill_state *)) | 976 | int (*get_state)(void *, enum rfkill_state *)) |
972 | { | 977 | { |
@@ -978,7 +983,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id, | |||
978 | printk(TPACPI_ERR | 983 | printk(TPACPI_ERR |
979 | "failed to read initial state for %s, error %d; " | 984 | "failed to read initial state for %s, error %d; " |
980 | "will turn radio off\n", name, res); | 985 | "will turn radio off\n", name, res); |
981 | } else { | 986 | } else if (set_default) { |
982 | /* try to set the initial state as the default for the rfkill | 987 | /* try to set the initial state as the default for the rfkill |
983 | * type, since we ask the firmware to preserve it across S5 in | 988 | * type, since we ask the firmware to preserve it across S5 in |
984 | * NVRAM */ | 989 | * NVRAM */ |
@@ -1148,6 +1153,31 @@ static DRIVER_ATTR(wwan_emulstate, S_IWUSR | S_IRUGO, | |||
1148 | tpacpi_driver_wwan_emulstate_show, | 1153 | tpacpi_driver_wwan_emulstate_show, |
1149 | tpacpi_driver_wwan_emulstate_store); | 1154 | tpacpi_driver_wwan_emulstate_store); |
1150 | 1155 | ||
1156 | /* uwb_emulstate ------------------------------------------------- */ | ||
1157 | static ssize_t tpacpi_driver_uwb_emulstate_show( | ||
1158 | struct device_driver *drv, | ||
1159 | char *buf) | ||
1160 | { | ||
1161 | return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate); | ||
1162 | } | ||
1163 | |||
1164 | static ssize_t tpacpi_driver_uwb_emulstate_store( | ||
1165 | struct device_driver *drv, | ||
1166 | const char *buf, size_t count) | ||
1167 | { | ||
1168 | unsigned long t; | ||
1169 | |||
1170 | if (parse_strtoul(buf, 1, &t)) | ||
1171 | return -EINVAL; | ||
1172 | |||
1173 | tpacpi_uwb_emulstate = !!t; | ||
1174 | |||
1175 | return count; | ||
1176 | } | ||
1177 | |||
1178 | static DRIVER_ATTR(uwb_emulstate, S_IWUSR | S_IRUGO, | ||
1179 | tpacpi_driver_uwb_emulstate_show, | ||
1180 | tpacpi_driver_uwb_emulstate_store); | ||
1151 | #endif | 1181 | #endif |
1152 | 1182 | ||
1153 | /* --------------------------------------------------------------------- */ | 1183 | /* --------------------------------------------------------------------- */ |
@@ -1175,6 +1205,8 @@ static int __init tpacpi_create_driver_attributes(struct device_driver *drv) | |||
1175 | res = driver_create_file(drv, &driver_attr_bluetooth_emulstate); | 1205 | res = driver_create_file(drv, &driver_attr_bluetooth_emulstate); |
1176 | if (!res && dbg_wwanemul) | 1206 | if (!res && dbg_wwanemul) |
1177 | res = driver_create_file(drv, &driver_attr_wwan_emulstate); | 1207 | res = driver_create_file(drv, &driver_attr_wwan_emulstate); |
1208 | if (!res && dbg_uwbemul) | ||
1209 | res = driver_create_file(drv, &driver_attr_uwb_emulstate); | ||
1178 | #endif | 1210 | #endif |
1179 | 1211 | ||
1180 | return res; | 1212 | return res; |
@@ -1191,6 +1223,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv) | |||
1191 | driver_remove_file(drv, &driver_attr_wlsw_emulstate); | 1223 | driver_remove_file(drv, &driver_attr_wlsw_emulstate); |
1192 | driver_remove_file(drv, &driver_attr_bluetooth_emulstate); | 1224 | driver_remove_file(drv, &driver_attr_bluetooth_emulstate); |
1193 | driver_remove_file(drv, &driver_attr_wwan_emulstate); | 1225 | driver_remove_file(drv, &driver_attr_wwan_emulstate); |
1226 | driver_remove_file(drv, &driver_attr_uwb_emulstate); | ||
1194 | #endif | 1227 | #endif |
1195 | } | 1228 | } |
1196 | 1229 | ||
@@ -2125,6 +2158,7 @@ static struct attribute *hotkey_mask_attributes[] __initdata = { | |||
2125 | 2158 | ||
2126 | static void bluetooth_update_rfk(void); | 2159 | static void bluetooth_update_rfk(void); |
2127 | static void wan_update_rfk(void); | 2160 | static void wan_update_rfk(void); |
2161 | static void uwb_update_rfk(void); | ||
2128 | static void tpacpi_send_radiosw_update(void) | 2162 | static void tpacpi_send_radiosw_update(void) |
2129 | { | 2163 | { |
2130 | int wlsw; | 2164 | int wlsw; |
@@ -2134,6 +2168,8 @@ static void tpacpi_send_radiosw_update(void) | |||
2134 | bluetooth_update_rfk(); | 2168 | bluetooth_update_rfk(); |
2135 | if (tp_features.wan) | 2169 | if (tp_features.wan) |
2136 | wan_update_rfk(); | 2170 | wan_update_rfk(); |
2171 | if (tp_features.uwb) | ||
2172 | uwb_update_rfk(); | ||
2137 | 2173 | ||
2138 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { | 2174 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) { |
2139 | mutex_lock(&tpacpi_inputdev_send_mutex); | 2175 | mutex_lock(&tpacpi_inputdev_send_mutex); |
@@ -3035,6 +3071,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) | |||
3035 | &tpacpi_bluetooth_rfkill, | 3071 | &tpacpi_bluetooth_rfkill, |
3036 | RFKILL_TYPE_BLUETOOTH, | 3072 | RFKILL_TYPE_BLUETOOTH, |
3037 | "tpacpi_bluetooth_sw", | 3073 | "tpacpi_bluetooth_sw", |
3074 | true, | ||
3038 | tpacpi_bluetooth_rfk_set, | 3075 | tpacpi_bluetooth_rfk_set, |
3039 | tpacpi_bluetooth_rfk_get); | 3076 | tpacpi_bluetooth_rfk_get); |
3040 | if (res) { | 3077 | if (res) { |
@@ -3309,6 +3346,7 @@ static int __init wan_init(struct ibm_init_struct *iibm) | |||
3309 | &tpacpi_wan_rfkill, | 3346 | &tpacpi_wan_rfkill, |
3310 | RFKILL_TYPE_WWAN, | 3347 | RFKILL_TYPE_WWAN, |
3311 | "tpacpi_wwan_sw", | 3348 | "tpacpi_wwan_sw", |
3349 | true, | ||
3312 | tpacpi_wan_rfk_set, | 3350 | tpacpi_wan_rfk_set, |
3313 | tpacpi_wan_rfk_get); | 3351 | tpacpi_wan_rfk_get); |
3314 | if (res) { | 3352 | if (res) { |
@@ -3366,6 +3404,162 @@ static struct ibm_struct wan_driver_data = { | |||
3366 | }; | 3404 | }; |
3367 | 3405 | ||
3368 | /************************************************************************* | 3406 | /************************************************************************* |
3407 | * UWB subdriver | ||
3408 | */ | ||
3409 | |||
3410 | enum { | ||
3411 | /* ACPI GUWB/SUWB bits */ | ||
3412 | TP_ACPI_UWB_HWPRESENT = 0x01, /* UWB hw available */ | ||
3413 | TP_ACPI_UWB_RADIOSSW = 0x02, /* UWB radio enabled */ | ||
3414 | }; | ||
3415 | |||
3416 | static struct rfkill *tpacpi_uwb_rfkill; | ||
3417 | |||
3418 | static int uwb_get_radiosw(void) | ||
3419 | { | ||
3420 | int status; | ||
3421 | |||
3422 | if (!tp_features.uwb) | ||
3423 | return -ENODEV; | ||
3424 | |||
3425 | /* WLSW overrides UWB in firmware/hardware, reflect that */ | ||
3426 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status) | ||
3427 | return RFKILL_STATE_HARD_BLOCKED; | ||
3428 | |||
3429 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3430 | if (dbg_uwbemul) | ||
3431 | return (tpacpi_uwb_emulstate) ? | ||
3432 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
3433 | #endif | ||
3434 | |||
3435 | if (!acpi_evalf(hkey_handle, &status, "GUWB", "d")) | ||
3436 | return -EIO; | ||
3437 | |||
3438 | return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ? | ||
3439 | RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED; | ||
3440 | } | ||
3441 | |||
3442 | static void uwb_update_rfk(void) | ||
3443 | { | ||
3444 | int status; | ||
3445 | |||
3446 | if (!tpacpi_uwb_rfkill) | ||
3447 | return; | ||
3448 | |||
3449 | status = uwb_get_radiosw(); | ||
3450 | if (status < 0) | ||
3451 | return; | ||
3452 | rfkill_force_state(tpacpi_uwb_rfkill, status); | ||
3453 | } | ||
3454 | |||
3455 | static int uwb_set_radiosw(int radio_on, int update_rfk) | ||
3456 | { | ||
3457 | int status; | ||
3458 | |||
3459 | if (!tp_features.uwb) | ||
3460 | return -ENODEV; | ||
3461 | |||
3462 | /* WLSW overrides UWB in firmware/hardware, but there is no | ||
3463 | * reason to risk weird behaviour. */ | ||
3464 | if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status | ||
3465 | && radio_on) | ||
3466 | return -EPERM; | ||
3467 | |||
3468 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3469 | if (dbg_uwbemul) { | ||
3470 | tpacpi_uwb_emulstate = !!radio_on; | ||
3471 | if (update_rfk) | ||
3472 | uwb_update_rfk(); | ||
3473 | return 0; | ||
3474 | } | ||
3475 | #endif | ||
3476 | |||
3477 | status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0; | ||
3478 | if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status)) | ||
3479 | return -EIO; | ||
3480 | |||
3481 | if (update_rfk) | ||
3482 | uwb_update_rfk(); | ||
3483 | |||
3484 | return 0; | ||
3485 | } | ||
3486 | |||
3487 | /* --------------------------------------------------------------------- */ | ||
3488 | |||
3489 | static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) | ||
3490 | { | ||
3491 | int uwbs = uwb_get_radiosw(); | ||
3492 | |||
3493 | if (uwbs < 0) | ||
3494 | return uwbs; | ||
3495 | |||
3496 | *state = uwbs; | ||
3497 | return 0; | ||
3498 | } | ||
3499 | |||
3500 | static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) | ||
3501 | { | ||
3502 | return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); | ||
3503 | } | ||
3504 | |||
3505 | static void uwb_exit(void) | ||
3506 | { | ||
3507 | if (tpacpi_uwb_rfkill) | ||
3508 | rfkill_unregister(tpacpi_uwb_rfkill); | ||
3509 | } | ||
3510 | |||
3511 | static int __init uwb_init(struct ibm_init_struct *iibm) | ||
3512 | { | ||
3513 | int res; | ||
3514 | int status = 0; | ||
3515 | |||
3516 | vdbg_printk(TPACPI_DBG_INIT, "initializing uwb subdriver\n"); | ||
3517 | |||
3518 | TPACPI_ACPIHANDLE_INIT(hkey); | ||
3519 | |||
3520 | tp_features.uwb = hkey_handle && | ||
3521 | acpi_evalf(hkey_handle, &status, "GUWB", "qd"); | ||
3522 | |||
3523 | vdbg_printk(TPACPI_DBG_INIT, "uwb is %s, status 0x%02x\n", | ||
3524 | str_supported(tp_features.uwb), | ||
3525 | status); | ||
3526 | |||
3527 | #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES | ||
3528 | if (dbg_uwbemul) { | ||
3529 | tp_features.uwb = 1; | ||
3530 | printk(TPACPI_INFO | ||
3531 | "uwb switch emulation enabled\n"); | ||
3532 | } else | ||
3533 | #endif | ||
3534 | if (tp_features.uwb && | ||
3535 | !(status & TP_ACPI_UWB_HWPRESENT)) { | ||
3536 | /* no uwb hardware present in system */ | ||
3537 | tp_features.uwb = 0; | ||
3538 | dbg_printk(TPACPI_DBG_INIT, | ||
3539 | "uwb hardware not installed\n"); | ||
3540 | } | ||
3541 | |||
3542 | if (!tp_features.uwb) | ||
3543 | return 1; | ||
3544 | |||
3545 | res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, | ||
3546 | &tpacpi_uwb_rfkill, | ||
3547 | RFKILL_TYPE_UWB, | ||
3548 | "tpacpi_uwb_sw", | ||
3549 | false, | ||
3550 | tpacpi_uwb_rfk_set, | ||
3551 | tpacpi_uwb_rfk_get); | ||
3552 | |||
3553 | return res; | ||
3554 | } | ||
3555 | |||
3556 | static struct ibm_struct uwb_driver_data = { | ||
3557 | .name = "uwb", | ||
3558 | .exit = uwb_exit, | ||
3559 | .flags.experimental = 1, | ||
3560 | }; | ||
3561 | |||
3562 | /************************************************************************* | ||
3369 | * Video subdriver | 3563 | * Video subdriver |
3370 | */ | 3564 | */ |
3371 | 3565 | ||
@@ -6830,6 +7024,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
6830 | .init = wan_init, | 7024 | .init = wan_init, |
6831 | .data = &wan_driver_data, | 7025 | .data = &wan_driver_data, |
6832 | }, | 7026 | }, |
7027 | { | ||
7028 | .init = uwb_init, | ||
7029 | .data = &uwb_driver_data, | ||
7030 | }, | ||
6833 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO | 7031 | #ifdef CONFIG_THINKPAD_ACPI_VIDEO |
6834 | { | 7032 | { |
6835 | .init = video_init, | 7033 | .init = video_init, |
@@ -6986,6 +7184,12 @@ MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); | |||
6986 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); | 7184 | module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); |
6987 | MODULE_PARM_DESC(wwan_state, | 7185 | MODULE_PARM_DESC(wwan_state, |
6988 | "Initial state of the emulated WWAN switch"); | 7186 | "Initial state of the emulated WWAN switch"); |
7187 | |||
7188 | module_param(dbg_uwbemul, uint, 0); | ||
7189 | MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); | ||
7190 | module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); | ||
7191 | MODULE_PARM_DESC(uwb_state, | ||
7192 | "Initial state of the emulated UWB switch"); | ||
6989 | #endif | 7193 | #endif |
6990 | 7194 | ||
6991 | static void thinkpad_acpi_module_exit(void) | 7195 | static void thinkpad_acpi_module_exit(void) |