aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2009-01-11 00:01:03 -0500
committerLen Brown <len.brown@intel.com>2009-01-15 13:36:51 -0500
commit0045c0aa7d5e787f78938e6a10927b8a516f0b83 (patch)
tree48f2ac334391e800616fb6c4ae30a4d01db565cd
parent90d9d3c79c44bcf95bc487e9bbceaff2de370310 (diff)
ACPI: thinkpad-acpi: add UWB radio support
Add rfkill support for USB UWB radio devices on very recent ThinkPad laptop models. The new subdriver is moslty a trimmed down copy of the wwan subdriver. Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Cc: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt18
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c206
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
1416EXPERIMENTAL: UWB
1417-----------------
1418
1419This feature is marked EXPERIMENTAL because it has not been extensively
1420tested and validated in various ThinkPad models yet. The feature may not
1421work as expected. USE WITH CAUTION! To use this feature, you need to supply
1422the experimental=1 parameter when loading the module.
1423
1424sysfs rfkill class: switch "tpacpi_uwb_sw"
1425
1426This feature exports an rfkill controller for the UWB device, if one is
1427present and enabled in the BIOS.
1428
1429Sysfs notes:
1430
1431 rfkill controller switch "tpacpi_uwb_sw": refer to
1432 Documentation/rfkill.txt for details.
1433
1416Multiple Commands, Module Parameters 1434Multiple 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 {
169enum { 169enum {
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;
317static int tpacpi_bluetooth_emulstate; 319static int tpacpi_bluetooth_emulstate;
318static int dbg_wwanemul; 320static int dbg_wwanemul;
319static int tpacpi_wwan_emulstate; 321static int tpacpi_wwan_emulstate;
322static int dbg_uwbemul;
323static 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 ------------------------------------------------- */
1157static 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
1164static 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
1178static 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
2126static void bluetooth_update_rfk(void); 2159static void bluetooth_update_rfk(void);
2127static void wan_update_rfk(void); 2160static void wan_update_rfk(void);
2161static void uwb_update_rfk(void);
2128static void tpacpi_send_radiosw_update(void) 2162static 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
3410enum {
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
3416static struct rfkill *tpacpi_uwb_rfkill;
3417
3418static 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
3442static 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
3455static 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
3489static 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
3500static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state)
3501{
3502 return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
3503}
3504
3505static void uwb_exit(void)
3506{
3507 if (tpacpi_uwb_rfkill)
3508 rfkill_unregister(tpacpi_uwb_rfkill);
3509}
3510
3511static 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
3556static 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");
6986module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); 7184module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0);
6987MODULE_PARM_DESC(wwan_state, 7185MODULE_PARM_DESC(wwan_state,
6988 "Initial state of the emulated WWAN switch"); 7186 "Initial state of the emulated WWAN switch");
7187
7188module_param(dbg_uwbemul, uint, 0);
7189MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation");
7190module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0);
7191MODULE_PARM_DESC(uwb_state,
7192 "Initial state of the emulated UWB switch");
6989#endif 7193#endif
6990 7194
6991static void thinkpad_acpi_module_exit(void) 7195static void thinkpad_acpi_module_exit(void)