ideapad-laptop: Change Lenovo Yoga 2 series rfkill handling
authorHans de Goede <hdegoede@redhat.com>
Mon, 23 Jun 2014 14:45:51 +0000 (16:45 +0200)
committerMatthew Garrett <matthew.garrett@nebula.com>
Sat, 16 Aug 2014 08:23:50 +0000 (01:23 -0700)
It seems that the same problems which lead to adding an rfkill blacklist and
putting the Lenovo Yoga 2 11 on it are also present on the Lenovo Yoga 2 13
and Lenovo Yoga 2 Pro too:
https://bugzilla.redhat.com/show_bug.cgi?id=1021036
https://forums.lenovo.com/t5/Linux-Discussion/Yoga-2-13-not-Pro-Linux-Warning/m-p/1517612

Testing has shown that the firmware rfkill settings are persistent over
reboots. So blacklisting the driver is not good enough, if the wifi is blocked
at the firmware level the wifi needs to be explictly unblocked through the
ideapad-laptop interface.

And at least on the Lenovo Yoga 2 13 the VPCCMD_RF register which on devices
with hardware kill switch reports the hardware switch state, needs to be
explictly set to 1 (radio enabled / not blocked).

So this patch does 3 things to get proper rfkill handling on these models:

1) Instead of blacklisting the rfkill functionality, which means that people
with a firmware blocked wifi get stuck in that situation, ignore the value
reported by the not present hardware rfkill switch, as this is what is causing
ideapad-laptop to wrongly report all radios as hardware blocks. But do register
the rfkill interfaces so that the user can soft [un]block them.

2) On models without a hardware rfkill switch, explictly set VPCCMD_RF to 1

3) Drop the " 11" postfix from the dmi match string, as the entire Yoga 2
series is affected.

Yoga 2 11:
Reported-and-tested-by: Vincent Gerris <vgerris@gmail.com>
Yoga 2 13:
Tested-by: madls05 <http://ubuntuforums.org/showthread.php?t=2215044>
Yoga 2 Pro:
Reported-and-tested-by: Peter F. Patel-Schneider <pfpschneider@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
drivers/platform/x86/ideapad-laptop.c

index b4c495a62eeca14be5bc0c7a191786deafec205c..b0e3a2eef0ac7f894d39ec339ff0db86b97b9403 100644 (file)
@@ -87,6 +87,7 @@ struct ideapad_private {
        struct backlight_device *blightdev;
        struct dentry *debug;
        unsigned long cfg;
+       bool has_hw_rfkill_switch;
 };
 
 static bool no_bt_rfkill;
@@ -473,12 +474,14 @@ static struct rfkill_ops ideapad_rfk_ops = {
 
 static void ideapad_sync_rfk_state(struct ideapad_private *priv)
 {
-       unsigned long hw_blocked;
+       unsigned long hw_blocked = 0;
        int i;
 
-       if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
-               return;
-       hw_blocked = !hw_blocked;
+       if (priv->has_hw_rfkill_switch) {
+               if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
+                       return;
+               hw_blocked = !hw_blocked;
+       }
 
        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
                if (priv->rfk[i])
@@ -821,14 +824,17 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
        }
 }
 
-/* Blacklist for devices where the ideapad rfkill interface does not work */
-static struct dmi_system_id rfkill_blacklist[] = {
-       /* The Lenovo Yoga 2 11 always reports everything as blocked */
+/*
+ * Some ideapads don't have a hardware rfkill switch, reading VPCCMD_R_RF
+ * always results in 0 on these models, causing ideapad_laptop to wrongly
+ * report all radios as hardware-blocked.
+ */
+static struct dmi_system_id no_hw_rfkill_list[] = {
        {
-               .ident = "Lenovo Yoga 2 11",
+               .ident = "Lenovo Yoga 2 11 / 13 / Pro",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"),
                },
        },
        {}
@@ -856,6 +862,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
        priv->cfg = cfg;
        priv->adev = adev;
        priv->platform_device = pdev;
+       priv->has_hw_rfkill_switch = !dmi_check_system(no_hw_rfkill_list);
 
        ret = ideapad_sysfs_init(priv);
        if (ret)
@@ -869,11 +876,17 @@ static int ideapad_acpi_add(struct platform_device *pdev)
        if (ret)
                goto input_failed;
 
-       if (!dmi_check_system(rfkill_blacklist)) {
-               for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
-                       if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
-                               ideapad_register_rfkill(priv, i);
-       }
+       /*
+        * On some models without a hw-switch (the yoga 2 13 at least)
+        * VPCCMD_W_RF must be explicitly set to 1 for the wifi to work.
+        */
+       if (!priv->has_hw_rfkill_switch)
+               write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
+
+       for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
+               if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
+                       ideapad_register_rfkill(priv, i);
+
        ideapad_sync_rfk_state(priv);
        ideapad_sync_touchpad_state(priv);