ath9k: add external_reset callback to ath9k_platfom_data for AR9330
authorGabor Juhos <juhosg@openwrt.org>
Tue, 21 Jun 2011 09:23:51 +0000 (11:23 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 22 Jun 2011 20:09:57 +0000 (16:09 -0400)
The patch adds a callback to ath9k_platform_data. If the
callback is provided by the platform code, then it can be
used to hard reset the WMAC device.

The callback is required for doing a hard reset of the AR9330
chips to get them working again after a hang.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
include/linux/ath9k_platform.h

index 4da5284c7773b906446c3d285c39ae4da0a7223b..839ba64c9e49f46cd40b690f4207cdc719b33d49 100644 (file)
@@ -1161,6 +1161,41 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
                        rst_flags |= AR_RTC_RC_MAC_COLD;
        }
 
+       if (AR_SREV_9330(ah)) {
+               int npend = 0;
+               int i;
+
+               /* AR9330 WAR:
+                * call external reset function to reset WMAC if:
+                * - doing a cold reset
+                * - we have pending frames in the TX queues
+                */
+
+               for (i = 0; i < AR_NUM_QCU; i++) {
+                       npend = ath9k_hw_numtxpending(ah, i);
+                       if (npend)
+                               break;
+               }
+
+               if (ah->external_reset &&
+                   (npend || type == ATH9K_RESET_COLD)) {
+                       int reset_err = 0;
+
+                       ath_dbg(ath9k_hw_common(ah), ATH_DBG_RESET,
+                               "reset MAC via external reset\n");
+
+                       reset_err = ah->external_reset();
+                       if (reset_err) {
+                               ath_err(ath9k_hw_common(ah),
+                                       "External reset failed, err=%d\n",
+                                       reset_err);
+                               return false;
+                       }
+
+                       REG_WRITE(ah, AR_RTC_RESET, 1);
+               }
+       }
+
        REG_WRITE(ah, AR_RTC_RC, rst_flags);
 
        REGWRITE_BUFFER_FLUSH(ah);
index 0749fa8c3a589ced6e0b9bbabecef76094323152..818acdd1ba907aa10375a0fd6fd6b236d66281ab 100644 (file)
@@ -863,6 +863,7 @@ struct ath_hw {
 
        bool is_clk_25mhz;
        int (*get_mac_revision)(void);
+       int (*external_reset)(void);
 };
 
 struct ath_bus_ops {
index 1851c222fff04ec888d55b92e5a47a0c3c675825..50103b2792b5b72455e760f1d838c0b785bf7b61 100644 (file)
@@ -575,6 +575,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
                sc->sc_ah->led_pin = pdata->led_pin;
                ah->is_clk_25mhz = pdata->is_clk_25mhz;
                ah->get_mac_revision = pdata->get_mac_revision;
+               ah->external_reset = pdata->external_reset;
        }
 
        common = ath9k_hw_common(ah);
index c207607acada48a6763aa88fea889fa2fc749e0e..6e3f54f37844af0475611eda1d0a1fb754117b04 100644 (file)
@@ -31,6 +31,7 @@ struct ath9k_platform_data {
 
        bool is_clk_25mhz;
        int (*get_mac_revision)(void);
+       int (*external_reset)(void);
 };
 
 #endif /* _LINUX_ATH9K_PLATFORM_H */