ath5k: Fix reset and interrupts for AHB type of devices.

On WiSoc we cannot access mac register before it is resetted.
It will crash hardware otherwise.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Wojciech Dubowik <Wojciech.Dubowik@neratec.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Felix Fietkau 2010-12-02 10:27:21 +01:00 committed by John W. Linville
parent a0b907ee2a
commit 4cebb34caa
2 changed files with 97 additions and 13 deletions

View File

@ -2148,7 +2148,8 @@ ath5k_intr(int irq, void *dev_id)
unsigned int counter = 1000; unsigned int counter = 1000;
if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) || if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) ||
!ath5k_hw_is_intr_pending(ah))) ((ath5k_get_bus_type(ah) != ATH_AHB) &&
!ath5k_hw_is_intr_pending(ah))))
return IRQ_NONE; return IRQ_NONE;
do { do {
@ -2214,6 +2215,10 @@ ath5k_intr(int irq, void *dev_id)
tasklet_schedule(&sc->rf_kill.toggleq); tasklet_schedule(&sc->rf_kill.toggleq);
} }
if (ath5k_get_bus_type(ah) == ATH_AHB)
break;
} while (ath5k_hw_is_intr_pending(ah) && --counter > 0); } while (ath5k_hw_is_intr_pending(ah) && --counter > 0);
if (unlikely(!counter)) if (unlikely(!counter))

View File

@ -27,6 +27,7 @@
#include <linux/pci.h> /* To determine if a card is pci-e */ #include <linux/pci.h> /* To determine if a card is pci-e */
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/platform_device.h>
#include "ath5k.h" #include "ath5k.h"
#include "reg.h" #include "reg.h"
#include "base.h" #include "base.h"
@ -141,7 +142,9 @@ static void ath5k_hw_init_core_clock(struct ath5k_hw *ah)
/* Set 32MHz USEC counter */ /* Set 32MHz USEC counter */
if ((ah->ah_radio == AR5K_RF5112) || if ((ah->ah_radio == AR5K_RF5112) ||
(ah->ah_radio == AR5K_RF5413)) (ah->ah_radio == AR5K_RF5413) ||
(ah->ah_radio == AR5K_RF2316) ||
(ah->ah_radio == AR5K_RF2317))
/* Remain on 40MHz clock ? */ /* Remain on 40MHz clock ? */
sclock = 40 - 1; sclock = 40 - 1;
else else
@ -244,6 +247,7 @@ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
if ((ah->ah_radio == AR5K_RF5112) || if ((ah->ah_radio == AR5K_RF5112) ||
(ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF5413) ||
(ah->ah_radio == AR5K_RF2316) ||
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
spending = 0x14; spending = 0x14;
else else
@ -299,6 +303,7 @@ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
if ((ah->ah_radio == AR5K_RF5112) || if ((ah->ah_radio == AR5K_RF5112) ||
(ah->ah_radio == AR5K_RF5413) || (ah->ah_radio == AR5K_RF5413) ||
(ah->ah_radio == AR5K_RF2316) ||
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
spending = 0x14; spending = 0x14;
else else
@ -357,6 +362,64 @@ static int ath5k_hw_nic_reset(struct ath5k_hw *ah, u32 val)
return ret; return ret;
} }
/*
* Reset AHB chipset
* AR5K_RESET_CTL_PCU flag resets WMAC
* AR5K_RESET_CTL_BASEBAND flag resets WBB
*/
static int ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags)
{
u32 mask = flags ? flags : ~0U;
volatile u32 *reg;
u32 regval;
u32 val = 0;
/* ah->ah_mac_srev is not available at this point yet */
if (ah->ah_sc->devid >= AR5K_SREV_AR2315_R6) {
reg = (u32 *) AR5K_AR2315_RESET;
if (mask & AR5K_RESET_CTL_PCU)
val |= AR5K_AR2315_RESET_WMAC;
if (mask & AR5K_RESET_CTL_BASEBAND)
val |= AR5K_AR2315_RESET_BB_WARM;
} else {
reg = (u32 *) AR5K_AR5312_RESET;
if (to_platform_device(ah->ah_sc->dev)->id == 0) {
if (mask & AR5K_RESET_CTL_PCU)
val |= AR5K_AR5312_RESET_WMAC0;
if (mask & AR5K_RESET_CTL_BASEBAND)
val |= AR5K_AR5312_RESET_BB0_COLD |
AR5K_AR5312_RESET_BB0_WARM;
} else {
if (mask & AR5K_RESET_CTL_PCU)
val |= AR5K_AR5312_RESET_WMAC1;
if (mask & AR5K_RESET_CTL_BASEBAND)
val |= AR5K_AR5312_RESET_BB1_COLD |
AR5K_AR5312_RESET_BB1_WARM;
}
}
/* Put BB/MAC into reset */
regval = __raw_readl(reg);
__raw_writel(regval | val, reg);
regval = __raw_readl(reg);
udelay(100);
/* Bring BB/MAC out of reset */
__raw_writel(regval & ~val, reg);
regval = __raw_readl(reg);
/*
* Reset configuration register (for hw byte-swap). Note that this
* is only set for big endian. We do the necessary magic in
* AR5K_INIT_CFG.
*/
if ((flags & AR5K_RESET_CTL_PCU) == 0)
ath5k_hw_reg_write(ah, AR5K_INIT_CFG, AR5K_CFG);
return 0;
}
/* /*
* Sleep control * Sleep control
*/ */
@ -456,6 +519,9 @@ int ath5k_hw_on_hold(struct ath5k_hw *ah)
u32 bus_flags; u32 bus_flags;
int ret; int ret;
if (ath5k_get_bus_type(ah) == ATH_AHB)
return 0;
/* Make sure device is awake */ /* Make sure device is awake */
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) { if (ret) {
@ -511,12 +577,14 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
mode = 0; mode = 0;
clock = 0; clock = 0;
if ((ath5k_get_bus_type(ah) != ATH_AHB) || !initial) {
/* Wakeup the device */ /* Wakeup the device */
ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0);
if (ret) { if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n"); ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n");
return ret; return ret;
} }
}
/* /*
* Put chipset on warm reset... * Put chipset on warm reset...
@ -534,6 +602,10 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI);
mdelay(2); mdelay(2);
} else { } else {
if (ath5k_get_bus_type(ah) == ATH_AHB)
ret = ath5k_hw_wisoc_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_BASEBAND);
else
ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU |
AR5K_RESET_CTL_BASEBAND | bus_flags); AR5K_RESET_CTL_BASEBAND | bus_flags);
} }
@ -550,9 +622,15 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial)
return ret; return ret;
} }
/* ...clear reset control register and pull device out of /* ...reset configuration regiter on Wisoc ...
* warm reset */ * ...clear reset control register and pull device out of
if (ath5k_hw_nic_reset(ah, 0)) { * warm reset on others */
if (ath5k_get_bus_type(ah) == ATH_AHB)
ret = ath5k_hw_wisoc_reset(ah, 0);
else
ret = ath5k_hw_nic_reset(ah, 0);
if (ret) {
ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n"); ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n");
return -EIO; return -EIO;
} }
@ -708,6 +786,7 @@ static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
/* Set fast ADC */ /* Set fast ADC */
if ((ah->ah_radio == AR5K_RF5413) || if ((ah->ah_radio == AR5K_RF5413) ||
(ah->ah_radio == AR5K_RF2317) ||
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) { (ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4))) {
u32 fast_adc = true; u32 fast_adc = true;