Hi,

My D-link DWL-G520 card attaches on ath(4):

ath0 at pci0 dev 9 function 0 "Atheros AR5212" rev 0x01: irq 11
ath0: AR2414 7.9 phy 4.5 rf2413 5.6, FCC2A*, address 00:17:9a:09:f4:5a

On ifconfig ath0 down && ifconfig ath0 up I've got system freeze.

Googleing showed that system can freeze on register read or write
while AR5212 chip in full sleep mode. And it is not possible to wake it up.
This implies only for some AR5212 chips.

In linux this is solved by making warm reset, instead 
of putting it in full sleep mode.
The diff below does the same thing.
What do you think about it? Or warm_reset function 
must be called only for particular chips?

Index: ar5212.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/ar5212.c,v
retrieving revision 1.52
diff -u -r1.52 ar5212.c
--- ar5212.c    14 Oct 2011 17:08:09 -0000      1.52
+++ ar5212.c    16 Jan 2013 16:58:33 -0000
@@ -30,6 +30,7 @@
 u_int16_t       ar5k_ar5212_radio_revision(struct ath_hal *, HAL_CHIP);
 void            ar5k_ar5212_fill(struct ath_hal *);
 HAL_BOOL        ar5k_ar5212_txpower(struct ath_hal *, HAL_CHANNEL *, u_int);
+HAL_BOOL        ar5k_ar5212_warm_reset(struct ath_hal *);
 
 /*
  * Initial register setting for the AR5212
@@ -2420,6 +2421,44 @@
 }
 
 /*
+ * Put MAC and Baseband on warm reset and keep that state
+ * (don't clean sleep control register). After this MAC
+ * and Baseband are disabled and a full reset is needed
+ * to come back. This way we save as much power as possible
+ * without putting the card on full sleep.
+ */
+
+HAL_BOOL
+ar5k_ar5212_warm_reset(struct ath_hal *hal)
+{
+       u_int32_t flags;
+
+       flags = AR5K_AR5212_RC_PCU | AR5K_AR5212_RC_BB;
+       if (hal->ah_pci_express == AH_FALSE)
+               flags |= AR5K_AR5212_RC_PCI;
+
+       if (ar5k_ar5212_nic_reset(hal, flags) == AH_FALSE) {
+               AR5K_PRINT("failed to reset the AR5212 + PCI chipset\n");
+               return (AH_FALSE);
+       }
+
+       /* ...wakeup */
+       if (ar5k_ar5212_set_power(hal,
+               HAL_PM_AWAKE, AH_TRUE, 0) == AH_FALSE) {
+               AR5K_PRINT("failed to resume the AR5212 (again)\n");
+               return (AH_FALSE);
+       }
+
+       /* Put chipset on warm reset... */
+       if (ar5k_ar5212_nic_reset(hal, 0) == AH_FALSE) {
+               AR5K_PRINT("failed to warm reset the AR5212\n");
+               return (AH_FALSE);
+       }
+
+       return (AH_TRUE);
+}
+
+/*
  * Power management functions
  */
 
@@ -2445,10 +2484,8 @@
                break;
 
        case HAL_PM_FULL_SLEEP:
-               if (set_chip == AH_TRUE) {
-                       AR5K_REG_WRITE(AR5K_AR5212_SCR,
-                           AR5K_AR5212_SCR_SLE_SLP);
-               }
+               if (set_chip == AH_TRUE)
+                       ar5k_ar5212_warm_reset(hal);
                staid |= AR5K_AR5212_STA_ID1_PWR_SV;
                break;
 


-- 
Dinar Talypov <t.dina...@gmail.com>

Reply via email to