| From 54328e64047a54b8fc2362c2e1f0fa16c90f739f Mon Sep 17 00:00:00 2001 |
| From: Larry Finger <Larry.Finger@lwfinger.net> |
| Date: Fri, 2 Oct 2015 11:44:30 -0500 |
| Subject: rtlwifi: rtl8821ae: Fix system lockups on boot |
| |
| From: Larry Finger <Larry.Finger@lwfinger.net> |
| |
| commit 54328e64047a54b8fc2362c2e1f0fa16c90f739f upstream. |
| |
| In commit 1277fa2ab2f9 ("rtlwifi: Remove the clear interrupt routine from all |
| drivers"), the code that cleared all interrupt enable bits before setting them |
| was removed for all PCI drivers. This fixed an issue that caused TX to be |
| blocked for 3-5 seconds. On some RTL8821AE units, this change causes soft |
| lockups to occur on boot. For that reason, the portion of the earlier commit |
| that applied to rtl8821ae is reverted. Kernels 4.1 and newer are affected. |
| |
| See http://marc.info/?l=linux-wireless&m=144373370103285&w=2 and |
| https://bugzilla.opensuse.org/show_bug.cgi?id=944978 for two cases where |
| this regression affected user systems. Note that this bug does not appear on |
| any of the developer's setups. For those users whose systems are affected |
| by the TX blockage, but do not lock up on boot, a module parameter is added |
| to disable the interrupt clear |
| |
| Fixes: 1277fa2ab2f9 ("rtlwifi: Remove the clear interrupt routine from all drivers") |
| Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> |
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/wireless/rtlwifi/pci.h | 2 ++ |
| drivers/net/wireless/rtlwifi/rtl8821ae/hw.c | 17 +++++++++++++++++ |
| drivers/net/wireless/rtlwifi/rtl8821ae/sw.c | 5 +++++ |
| drivers/net/wireless/rtlwifi/wifi.h | 3 +++ |
| 4 files changed, 27 insertions(+) |
| |
| --- a/drivers/net/wireless/rtlwifi/pci.h |
| +++ b/drivers/net/wireless/rtlwifi/pci.h |
| @@ -247,6 +247,8 @@ struct rtl_pci { |
| /* MSI support */ |
| bool msi_support; |
| bool using_msi; |
| + /* interrupt clear before set */ |
| + bool int_clear; |
| }; |
| |
| struct mp_adapter { |
| --- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c |
| +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c |
| @@ -2253,11 +2253,28 @@ void rtl8821ae_set_qos(struct ieee80211_ |
| } |
| } |
| |
| +static void rtl8821ae_clear_interrupt(struct ieee80211_hw *hw) |
| +{ |
| + struct rtl_priv *rtlpriv = rtl_priv(hw); |
| + u32 tmp = rtl_read_dword(rtlpriv, REG_HISR); |
| + |
| + rtl_write_dword(rtlpriv, REG_HISR, tmp); |
| + |
| + tmp = rtl_read_dword(rtlpriv, REG_HISRE); |
| + rtl_write_dword(rtlpriv, REG_HISRE, tmp); |
| + |
| + tmp = rtl_read_dword(rtlpriv, REG_HSISR); |
| + rtl_write_dword(rtlpriv, REG_HSISR, tmp); |
| +} |
| + |
| void rtl8821ae_enable_interrupt(struct ieee80211_hw *hw) |
| { |
| struct rtl_priv *rtlpriv = rtl_priv(hw); |
| struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); |
| |
| + if (!rtlpci->int_clear) |
| + rtl8821ae_clear_interrupt(hw);/*clear it here first*/ |
| + |
| rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); |
| rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF); |
| rtlpci->irq_enabled = true; |
| --- a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c |
| +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c |
| @@ -96,6 +96,7 @@ int rtl8821ae_init_sw_vars(struct ieee80 |
| |
| rtl8821ae_bt_reg_init(hw); |
| rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; |
| + rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear; |
| rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); |
| |
| rtlpriv->dm.dm_initialgain_enable = 1; |
| @@ -167,6 +168,7 @@ int rtl8821ae_init_sw_vars(struct ieee80 |
| rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; |
| rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; |
| rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; |
| + rtlpci->msi_support = rtlpriv->cfg->mod_params->int_clear; |
| if (rtlpriv->cfg->mod_params->disable_watchdog) |
| pr_info("watchdog disabled\n"); |
| rtlpriv->psc.reg_fwctrl_lps = 3; |
| @@ -308,6 +310,7 @@ static struct rtl_mod_params rtl8821ae_m |
| .swctrl_lps = false, |
| .fwctrl_lps = true, |
| .msi_support = true, |
| + .int_clear = true, |
| .debug = DBG_EMERG, |
| .disable_watchdog = 0, |
| }; |
| @@ -437,6 +440,7 @@ module_param_named(fwlps, rtl8821ae_mod_ |
| module_param_named(msi, rtl8821ae_mod_params.msi_support, bool, 0444); |
| module_param_named(disable_watchdog, rtl8821ae_mod_params.disable_watchdog, |
| bool, 0444); |
| +module_param_named(int_clear, rtl8821ae_mod_params.int_clear, bool, 0444); |
| MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); |
| MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); |
| MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); |
| @@ -444,6 +448,7 @@ MODULE_PARM_DESC(fwlps, "Set to 1 to use |
| MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 1)\n"); |
| MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); |
| MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); |
| +MODULE_PARM_DESC(int_clear, "Set to 1 to disable interrupt clear before set (default 0)\n"); |
| |
| static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); |
| |
| --- a/drivers/net/wireless/rtlwifi/wifi.h |
| +++ b/drivers/net/wireless/rtlwifi/wifi.h |
| @@ -2233,6 +2233,9 @@ struct rtl_mod_params { |
| |
| /* default 0: 1 means disable */ |
| bool disable_watchdog; |
| + |
| + /* default 0: 1 means do not disable interrupts */ |
| + bool int_clear; |
| }; |
| |
| struct rtl_hal_usbint_cfg { |