Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

Conflicts:
	drivers/net/wireless/iwlwifi/iwl-rx.c
diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c
index e0138ac..e974e58 100644
--- a/drivers/net/wireless/ath/ar9170/usb.c
+++ b/drivers/net/wireless/ath/ar9170/usb.c
@@ -64,6 +64,8 @@
 	{ USB_DEVICE(0x0cf3, 0x9170) },
 	/* Atheros TG121N */
 	{ USB_DEVICE(0x0cf3, 0x1001) },
+	/* TP-Link TL-WN821N v2 */
+	{ USB_DEVICE(0x0cf3, 0x1002) },
 	/* Cace Airpcap NX */
 	{ USB_DEVICE(0xcace, 0x0300) },
 	/* D-Link DWA 160A */
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 3234995..0ad6d0b 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -609,14 +609,24 @@
 		AR_PHY_CH1_EXT_CCA,
 		AR_PHY_CH2_EXT_CCA
 	};
-	u8 chainmask;
+	u8 chainmask, rx_chain_status;
 
+	rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK);
 	if (AR_SREV_9285(ah))
 		chainmask = 0x9;
-	else if (AR_SREV_9280(ah) || AR_SREV_9287(ah))
-		chainmask = 0x1B;
-	else
-		chainmask = 0x3F;
+	else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
+		if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4))
+			chainmask = 0x1B;
+		else
+			chainmask = 0x09;
+	} else {
+		if (rx_chain_status & 0x4)
+			chainmask = 0x3F;
+		else if (rx_chain_status & 0x2)
+			chainmask = 0x1B;
+		else
+			chainmask = 0x09;
+	}
 
 	h = ah->nfCalHist;
 
@@ -697,6 +707,8 @@
 		noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE;
 	else if (AR_SREV_9285(ah))
 		noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
+	else if (AR_SREV_9287(ah))
+		noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE;
 	else
 		noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE;
 
@@ -924,6 +936,7 @@
 		regVal |= (1 << (19 + i));
 		REG_WRITE(ah, 0x7834, regVal);
 		udelay(1);
+		regVal = REG_READ(ah, 0x7834);
 		regVal &= (~(0x1 << (19 + i)));
 		reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9);
 		regVal |= (reg_field << (19 + i));
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h
index 019bcbb..9028ab1 100644
--- a/drivers/net/wireless/ath/ath9k/calib.h
+++ b/drivers/net/wireless/ath/ath9k/calib.h
@@ -28,6 +28,7 @@
 #define AR_PHY_CCA_MAX_AR5416_GOOD_VALUE	-85
 #define AR_PHY_CCA_MAX_AR9280_GOOD_VALUE	-112
 #define AR_PHY_CCA_MAX_AR9285_GOOD_VALUE	-118
+#define AR_PHY_CCA_MAX_AR9287_GOOD_VALUE	-118
 #define AR_PHY_CCA_MAX_HIGH_VALUE      		-62
 #define AR_PHY_CCA_MIN_BAD_VALUE       		-140
 #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT     3
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index ae7fb5d..4071fc9 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -509,6 +509,8 @@
 			REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE,
 				      eep->baseEepHeader.dacLpMode);
 
+		udelay(100);
+
 		REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP,
 			      pModal->miscBits >> 2);
 
@@ -902,7 +904,7 @@
 						  u16 powerLimit)
 {
 #define REDUCE_SCALED_POWER_BY_TWO_CHAIN     6  /* 10*log10(2)*2 */
-#define REDUCE_SCALED_POWER_BY_THREE_CHAIN   10 /* 10*log10(3)*2 */
+#define REDUCE_SCALED_POWER_BY_THREE_CHAIN   9 /* 10*log10(3)*2 */
 
 	struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
 	struct ar5416_eeprom_def *pEepData = &ah->eeprom.def;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index b6c6cca..ca7694c 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -842,7 +842,7 @@
 
 static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah)
 {
-	if (AR_SREV_9287_11(ah))
+	if (AR_SREV_9287_11_OR_LATER(ah))
 		INIT_INI_ARRAY(&ah->iniModesRxGain,
 		ar9287Modes_rx_gain_9287_1_1,
 		ARRAY_SIZE(ar9287Modes_rx_gain_9287_1_1), 6);
@@ -853,7 +853,7 @@
 	else if (AR_SREV_9280_20(ah))
 		ath9k_hw_init_rxgain_ini(ah);
 
-	if (AR_SREV_9287_11(ah)) {
+	if (AR_SREV_9287_11_OR_LATER(ah)) {
 		INIT_INI_ARRAY(&ah->iniModesTxGain,
 		ar9287Modes_tx_gain_9287_1_1,
 		ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_1), 6);
@@ -965,7 +965,7 @@
 	ath9k_hw_init_mode_regs(ah);
 
 	if (ah->is_pciexpress)
-		ath9k_hw_configpcipowersave(ah, 0);
+		ath9k_hw_configpcipowersave(ah, 0, 0);
 	else
 		ath9k_hw_disablepcie(ah);
 
@@ -1273,6 +1273,15 @@
 	 */
 	REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
 
+	if (AR_SREV_9280_10_OR_LATER(ah)) {
+		val = REG_READ(ah, AR_PCU_MISC_MODE2) &
+			       (~AR_PCU_MISC_MODE2_HWWAR1);
+
+		if (AR_SREV_9287_10_OR_LATER(ah))
+			val = val & (~AR_PCU_MISC_MODE2_HWWAR2);
+
+		REG_WRITE(ah, AR_PCU_MISC_MODE2, val);
+	}
 
 	if (!AR_SREV_5416_20_OR_LATER(ah) ||
 	    AR_SREV_9280_10_OR_LATER(ah))
@@ -1784,7 +1793,7 @@
 static bool ath9k_hw_chip_reset(struct ath_hw *ah,
 				struct ath9k_channel *chan)
 {
-	if (OLC_FOR_AR9280_20_LATER) {
+	if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) {
 		if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON))
 			return false;
 	} else if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM))
@@ -2338,6 +2347,7 @@
 	struct ath9k_channel *curchan = ah->curchan;
 	u32 saveDefAntenna;
 	u32 macStaId1;
+	u64 tsf = 0;
 	int i, rx_chainmask, r;
 
 	ah->extprotspacing = sc->ht_extprotspacing;
@@ -2347,7 +2357,7 @@
 	if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
 		return -EIO;
 
-	if (curchan)
+	if (curchan && !ah->chip_fullsleep)
 		ath9k_hw_getnf(ah, curchan);
 
 	if (bChannelChange &&
@@ -2356,8 +2366,8 @@
 	    (chan->channel != ah->curchan->channel) &&
 	    ((chan->channelFlags & CHANNEL_ALL) ==
 	     (ah->curchan->channelFlags & CHANNEL_ALL)) &&
-	    (!AR_SREV_9280(ah) || (!IS_CHAN_A_5MHZ_SPACED(chan) &&
-				   !IS_CHAN_A_5MHZ_SPACED(ah->curchan)))) {
+	     !(AR_SREV_9280(ah) || IS_CHAN_A_5MHZ_SPACED(chan) ||
+	     IS_CHAN_A_5MHZ_SPACED(ah->curchan))) {
 
 		if (ath9k_hw_channel_change(ah, chan, sc->tx_chan_width)) {
 			ath9k_hw_loadnf(ah, ah->curchan);
@@ -2372,6 +2382,10 @@
 
 	macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B;
 
+	/* For chips on which RTC reset is done, save TSF before it gets cleared */
+	if (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))
+		tsf = ath9k_hw_gettsf64(ah);
+
 	saveLedState = REG_READ(ah, AR_CFG_LED) &
 		(AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
 		 AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW);
@@ -2398,6 +2412,10 @@
 		udelay(50);
 	}
 
+	/* Restore TSF */
+	if (tsf && AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))
+		ath9k_hw_settsf64(ah, tsf);
+
 	if (AR_SREV_9280_10_OR_LATER(ah))
 		REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE);
 
@@ -3005,9 +3023,10 @@
  * Programming the SerDes must go through the same 288 bit serial shift
  * register as the other analog registers.  Hence the 9 writes.
  */
-void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore)
+void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off)
 {
 	u8 i;
+	u32 val;
 
 	if (ah->is_pciexpress != true)
 		return;
@@ -3017,84 +3036,113 @@
 		return;
 
 	/* Nothing to do on restore for 11N */
-	if (restore)
-		return;
+	if (!restore) {
+		if (AR_SREV_9280_20_OR_LATER(ah)) {
+			/*
+			 * AR9280 2.0 or later chips use SerDes values from the
+			 * initvals.h initialized depending on chipset during
+			 * ath9k_hw_init()
+			 */
+			for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) {
+				REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0),
+					  INI_RA(&ah->iniPcieSerdes, i, 1));
+			}
+		} else if (AR_SREV_9280(ah) &&
+			   (ah->hw_version.macRev == AR_SREV_REVISION_9280_10)) {
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
 
-	if (AR_SREV_9280_20_OR_LATER(ah)) {
-		/*
-		 * AR9280 2.0 or later chips use SerDes values from the
-		 * initvals.h initialized depending on chipset during
-		 * ath9k_hw_init()
-		 */
-		for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) {
-			REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0),
-				  INI_RA(&ah->iniPcieSerdes, i, 1));
+			/* RX shut off when elecidle is asserted */
+			REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560);
+
+			/* Shut off CLKREQ active in L1 */
+			if (ah->config.pcie_clock_req)
+				REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc);
+			else
+				REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd);
+
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007);
+
+			/* Load the new settings */
+			REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
+
+		} else {
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
+
+			/* RX shut off when elecidle is asserted */
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579);
+
+			/*
+			 * Ignore ah->ah_config.pcie_clock_req setting for
+			 * pre-AR9280 11n
+			 */
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff);
+
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
+			REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007);
+
+			/* Load the new settings */
+			REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
 		}
-	} else if (AR_SREV_9280(ah) &&
-		   (ah->hw_version.macRev == AR_SREV_REVISION_9280_10)) {
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
 
-		/* RX shut off when elecidle is asserted */
-		REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560);
+		udelay(1000);
 
-		/* Shut off CLKREQ active in L1 */
-		if (ah->config.pcie_clock_req)
-			REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc);
-		else
-			REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd);
+		/* set bit 19 to allow forcing of pcie core into L1 state */
+		REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
 
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007);
+		/* Several PCIe massages to ensure proper behaviour */
+		if (ah->config.pcie_waen) {
+			val = ah->config.pcie_waen;
+			if (!power_off)
+				val &= (~AR_WA_D3_L1_DISABLE);
+		} else {
+			if (AR_SREV_9285(ah) || AR_SREV_9271(ah) ||
+			    AR_SREV_9287(ah)) {
+				val = AR9285_WA_DEFAULT;
+				if (!power_off)
+					val &= (~AR_WA_D3_L1_DISABLE);
+			} else if (AR_SREV_9280(ah)) {
+				/*
+				 * On AR9280 chips bit 22 of 0x4004 needs to be
+				 * set otherwise card may disappear.
+				 */
+				val = AR9280_WA_DEFAULT;
+				if (!power_off)
+					val &= (~AR_WA_D3_L1_DISABLE);
+			} else
+				val = AR_WA_DEFAULT;
+		}
 
-		/* Load the new settings */
-		REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
-
-	} else {
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924);
-
-		/* RX shut off when elecidle is asserted */
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579);
-
-		/*
-		 * Ignore ah->ah_config.pcie_clock_req setting for
-		 * pre-AR9280 11n
-		 */
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff);
-
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554);
-		REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007);
-
-		/* Load the new settings */
-		REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000);
+		REG_WRITE(ah, AR_WA, val);
 	}
 
-	udelay(1000);
-
-	/* set bit 19 to allow forcing of pcie core into L1 state */
-	REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
-
-	/* Several PCIe massages to ensure proper behaviour */
-	if (ah->config.pcie_waen) {
-		REG_WRITE(ah, AR_WA, ah->config.pcie_waen);
-	} else {
-		if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah))
-			REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT);
+	if (power_off) {
 		/*
-		 * On AR9280 chips bit 22 of 0x4004 needs to be set to
-		 * otherwise card may disappear.
+		 * Set PCIe workaround bits
+		 * bit 14 in WA register (disable L1) should only
+		 * be set when device enters D3 and be cleared
+		 * when device comes back to D0.
 		 */
-		else if (AR_SREV_9280(ah))
-			REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT);
-		else
-			REG_WRITE(ah, AR_WA, AR_WA_DEFAULT);
+		if (ah->config.pcie_waen) {
+			if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
+				REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE);
+		} else {
+			if (((AR_SREV_9285(ah) || AR_SREV_9271(ah) ||
+			      AR_SREV_9287(ah)) &&
+			     (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) ||
+			    (AR_SREV_9280(ah) &&
+			     (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) {
+				REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE);
+			}
+		}
 	}
 }
 
@@ -3652,15 +3700,7 @@
 	}
 #endif
 
-	if ((ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) ||
-	    (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE) ||
-	    (ah->hw_version.macVersion == AR_SREV_VERSION_9160) ||
-	    (ah->hw_version.macVersion == AR_SREV_VERSION_9100) ||
-	    (ah->hw_version.macVersion == AR_SREV_VERSION_9280) ||
-	    (ah->hw_version.macVersion == AR_SREV_VERSION_9285))
-		pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP;
-	else
-		pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP;
+	pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP;
 
 	if (AR_SREV_9280(ah) || AR_SREV_9285(ah))
 		pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS;
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 9106a0b..b892345 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -106,7 +106,7 @@
 #define AH_TSF_WRITE_TIMEOUT        100    /* (us) */
 #define AH_TIME_QUANTUM             10
 #define AR_KEYTABLE_SIZE            128
-#define POWER_UP_TIME               200000
+#define POWER_UP_TIME               10000
 #define SPUR_RSSI_THRESH            40
 
 #define CAB_TIMEOUT_VAL             10
@@ -650,7 +650,7 @@
 				    const struct ath9k_beacon_state *bs);
 bool ath9k_hw_setpower(struct ath_hw *ah,
 		       enum ath9k_power_mode mode);
-void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore);
+void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off);
 
 /* Interrupt Handling */
 bool ath9k_hw_intrpend(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 3dc7b5a..52bed89 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1131,7 +1131,7 @@
 	int r;
 
 	ath9k_ps_wakeup(sc);
-	ath9k_hw_configpcipowersave(ah, 0);
+	ath9k_hw_configpcipowersave(ah, 0, 0);
 
 	if (!ah->curchan)
 		ah->curchan = ath_get_curchannel(sc, sc->hw);
@@ -1202,7 +1202,7 @@
 	spin_unlock_bh(&sc->sc_resetlock);
 
 	ath9k_hw_phy_disable(ah);
-	ath9k_hw_configpcipowersave(ah, 1);
+	ath9k_hw_configpcipowersave(ah, 1, 1);
 	ath9k_ps_restore(sc);
 	ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
 }
@@ -1226,11 +1226,6 @@
 	bool blocked = !!ath_is_rfkill_set(sc);
 
 	wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
-
-	if (blocked)
-		ath_radio_disable(sc);
-	else
-		ath_radio_enable(sc);
 }
 
 static void ath_start_rfkill_poll(struct ath_softc *sc)
@@ -1260,6 +1255,7 @@
 	DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n");
 
 	ath_deinit_leds(sc);
+	wiphy_rfkill_stop_polling(sc->hw->wiphy);
 
 	for (i = 0; i < sc->num_sec_wiphy; i++) {
 		struct ath_wiphy *aphy = sc->sec_wiphy[i];
@@ -1942,7 +1938,7 @@
 	init_channel = ath_get_curchannel(sc, hw);
 
 	/* Reset SERDES registers */
-	ath9k_hw_configpcipowersave(sc->sc_ah, 0);
+	ath9k_hw_configpcipowersave(sc->sc_ah, 0, 0);
 
 	/*
 	 * The basic interface to setting the hardware in a good
@@ -2166,11 +2162,9 @@
 	} else
 		sc->rx.rxlink = NULL;
 
-	wiphy_rfkill_stop_polling(sc->hw->wiphy);
-
 	/* disable HAL and put h/w to sleep */
 	ath9k_hw_disable(sc->sc_ah);
-	ath9k_hw_configpcipowersave(sc->sc_ah, 1);
+	ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1);
 	ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
 
 	sc->sc_flags |= SC_OP_INVALID;
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index e5c29eb..d83b77f8 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -676,8 +676,9 @@
 #define AR_RC_HOSTIF         0x00000100
 
 #define AR_WA                		0x4004
+#define AR_WA_D3_L1_DISABLE		(1 << 14)
 #define AR9285_WA_DEFAULT 		0x004a05cb
-#define AR9280_WA_DEFAULT           	0x0040073f
+#define AR9280_WA_DEFAULT           	0x0040073b
 #define AR_WA_DEFAULT               	0x0000073f
 
 
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 83e3813..54ea61c 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -61,11 +61,28 @@
 
 	  If unsure, say N.
 
+config B43_SDIO
+	bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)"
+	depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL
+	select SSB_SDIOHOST
+	---help---
+	  Broadcom 43xx device support for Soft-MAC SDIO devices.
+
+	  With this config option you can drive Soft-MAC b43 cards with a
+	  Secure Digital I/O interface.
+	  This includes the WLAN daughter card found on the Nintendo Wii
+	  video game console.
+	  Note that this does not support Broadcom 43xx Full-MAC devices.
+
+	  It's safe to select Y here, even if you don't have a B43 SDIO device.
+
+	  If unsure, say N.
+
 # Data transfers to the device via PIO
-# This is only needed on PCMCIA devices. All others can do DMA properly.
+# This is only needed on PCMCIA and SDIO devices. All others can do DMA properly.
 config B43_PIO
 	bool
-	depends on B43 && (B43_PCMCIA || B43_FORCE_PIO)
+	depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO)
 	select SSB_BLOCKIO
 	default y
 
diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile
index da379f4..84772a2 100644
--- a/drivers/net/wireless/b43/Makefile
+++ b/drivers/net/wireless/b43/Makefile
@@ -16,6 +16,7 @@
 b43-y				+= rfkill.o
 b43-$(CONFIG_B43_LEDS)		+= leds.o
 b43-$(CONFIG_B43_PCMCIA)	+= pcmcia.o
+b43-$(CONFIG_B43_SDIO)		+= sdio.o
 b43-$(CONFIG_B43_DEBUG)		+= debugfs.o
 
 obj-$(CONFIG_B43)		+= b43.o
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index 09cfe68..fa1549a 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -629,13 +629,6 @@
 	 * from the mac80211 subsystem. */
 	u16 mac80211_initially_registered_queues;
 
-	/* R/W lock for data transmission.
-	 * Transmissions on 2+ queues can run concurrently, but somebody else
-	 * might sync with TX by write_lock_irqsave()'ing. */
-	rwlock_t tx_lock;
-	/* Lock for LEDs access. */
-	spinlock_t leds_lock;
-
 	/* We can only have one operating interface (802.11 core)
 	 * at a time. General information about this interface follows.
 	 */
@@ -686,6 +679,9 @@
 	struct work_struct tx_work;
 	/* Queue of packets to be transmitted. */
 	struct sk_buff_head tx_queue;
+
+	/* The device LEDs. */
+	struct b43_leds leds;
 };
 
 /* The type of the firmware file. */
@@ -768,13 +764,10 @@
 	/* The device initialization status.
 	 * Use b43_status() to query. */
 	atomic_t __init_status;
-	/* Saved init status for handling suspend. */
-	int suspend_init_status;
 
 	bool bad_frames_preempt;	/* Use "Bad Frames Preemption" (default off) */
 	bool dfq_valid;		/* Directed frame queue valid (IBSS PS mode, ATIM) */
 	bool radio_hw_enable;	/* saved state of radio hardware enabled state */
-	bool suspend_in_progress;	/* TRUE, if we are in a suspend/resume cycle */
 	bool qos_enabled;		/* TRUE, if QoS is used. */
 	bool hwcrypto_enabled;		/* TRUE, if HW crypto acceleration is enabled. */
 
@@ -794,12 +787,6 @@
 	/* Various statistics about the physical device. */
 	struct b43_stats stats;
 
-	/* The device LEDs. */
-	struct b43_led led_tx;
-	struct b43_led led_rx;
-	struct b43_led led_assoc;
-	struct b43_led led_radio;
-
 	/* Reason code of the last interrupt. */
 	u32 irq_reason;
 	u32 dma_reason[6];
@@ -830,6 +817,10 @@
 	/* Debugging stuff follows. */
 #ifdef CONFIG_B43_DEBUG
 	struct b43_dfsentry *dfsentry;
+	unsigned int irq_count;
+	unsigned int irq_bit_count[32];
+	unsigned int tx_count;
+	unsigned int rx_count;
 #endif
 };
 
diff --git a/drivers/net/wireless/b43/debugfs.c b/drivers/net/wireless/b43/debugfs.c
index 8f64943..80b19a4 100644
--- a/drivers/net/wireless/b43/debugfs.c
+++ b/drivers/net/wireless/b43/debugfs.c
@@ -689,6 +689,7 @@
 	add_dyn_dbg("debug_lo", B43_DBG_LO, 0);
 	add_dyn_dbg("debug_firmware", B43_DBG_FIRMWARE, 0);
 	add_dyn_dbg("debug_keys", B43_DBG_KEYS, 0);
+	add_dyn_dbg("debug_verbose_stats", B43_DBG_VERBOSESTATS, 0);
 
 #undef add_dyn_dbg
 }
diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h
index e47b4b4..822aad8 100644
--- a/drivers/net/wireless/b43/debugfs.h
+++ b/drivers/net/wireless/b43/debugfs.h
@@ -13,6 +13,7 @@
 	B43_DBG_LO,
 	B43_DBG_FIRMWARE,
 	B43_DBG_KEYS,
+	B43_DBG_VERBOSESTATS,
 	__B43_NR_DYNDBG,
 };
 
diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c
index a467ee2..8701034 100644
--- a/drivers/net/wireless/b43/dma.c
+++ b/drivers/net/wireless/b43/dma.c
@@ -1428,9 +1428,9 @@
 				ring->nr_failed_tx_packets++;
 			ring->nr_total_packet_tries += status->frame_count;
 #endif /* DEBUG */
-			ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb);
+			ieee80211_tx_status(dev->wl->hw, meta->skb);
 
-			/* skb is freed by ieee80211_tx_status_irqsafe() */
+			/* skb is freed by ieee80211_tx_status() */
 			meta->skb = NULL;
 		} else {
 			/* No need to call free_descriptor_buffer here, as
diff --git a/drivers/net/wireless/b43/leds.c b/drivers/net/wireless/b43/leds.c
index c8b3170..fbe3d4f 100644
--- a/drivers/net/wireless/b43/leds.c
+++ b/drivers/net/wireless/b43/leds.c
@@ -34,35 +34,75 @@
 static void b43_led_turn_on(struct b43_wldev *dev, u8 led_index,
 			    bool activelow)
 {
-	struct b43_wl *wl = dev->wl;
-	unsigned long flags;
 	u16 ctl;
 
-	spin_lock_irqsave(&wl->leds_lock, flags);
 	ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
 	if (activelow)
 		ctl &= ~(1 << led_index);
 	else
 		ctl |= (1 << led_index);
 	b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
-	spin_unlock_irqrestore(&wl->leds_lock, flags);
 }
 
 static void b43_led_turn_off(struct b43_wldev *dev, u8 led_index,
 			     bool activelow)
 {
-	struct b43_wl *wl = dev->wl;
-	unsigned long flags;
 	u16 ctl;
 
-	spin_lock_irqsave(&wl->leds_lock, flags);
 	ctl = b43_read16(dev, B43_MMIO_GPIO_CONTROL);
 	if (activelow)
 		ctl |= (1 << led_index);
 	else
 		ctl &= ~(1 << led_index);
 	b43_write16(dev, B43_MMIO_GPIO_CONTROL, ctl);
-	spin_unlock_irqrestore(&wl->leds_lock, flags);
+}
+
+static void b43_led_update(struct b43_wldev *dev,
+			   struct b43_led *led)
+{
+	bool radio_enabled;
+	bool turn_on;
+
+	if (!led->wl)
+		return;
+
+	radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable);
+
+	/* The led->state read is racy, but we don't care. In case we raced
+	 * with the brightness_set handler, we will be called again soon
+	 * to fixup our state. */
+	if (radio_enabled)
+		turn_on = atomic_read(&led->state) != LED_OFF;
+	else
+		turn_on = 0;
+	if (turn_on == led->hw_state)
+		return;
+	led->hw_state = turn_on;
+
+	if (turn_on)
+		b43_led_turn_on(dev, led->index, led->activelow);
+	else
+		b43_led_turn_off(dev, led->index, led->activelow);
+}
+
+static void b43_leds_work(struct work_struct *work)
+{
+	struct b43_leds *leds = container_of(work, struct b43_leds, work);
+	struct b43_wl *wl = container_of(leds, struct b43_wl, leds);
+	struct b43_wldev *dev;
+
+	mutex_lock(&wl->mutex);
+	dev = wl->current_dev;
+	if (unlikely(!dev || b43_status(dev) < B43_STAT_STARTED))
+		goto out_unlock;
+
+	b43_led_update(dev, &wl->leds.led_tx);
+	b43_led_update(dev, &wl->leds.led_rx);
+	b43_led_update(dev, &wl->leds.led_radio);
+	b43_led_update(dev, &wl->leds.led_assoc);
+
+out_unlock:
+	mutex_unlock(&wl->mutex);
 }
 
 /* Callback from the LED subsystem. */
@@ -70,21 +110,12 @@
 				   enum led_brightness brightness)
 {
 	struct b43_led *led = container_of(led_dev, struct b43_led, led_dev);
-	struct b43_wldev *dev = led->dev;
-	bool radio_enabled;
+	struct b43_wl *wl = led->wl;
 
-	if (unlikely(b43_status(dev) < B43_STAT_INITIALIZED))
-		return;
-
-	/* Checking the radio-enabled status here is slightly racy,
-	 * but we want to avoid the locking overhead and we don't care
-	 * whether the LED has the wrong state for a second. */
-	radio_enabled = (dev->phy.radio_on && dev->radio_hw_enable);
-
-	if (brightness == LED_OFF || !radio_enabled)
-		b43_led_turn_off(dev, led->index, led->activelow);
-	else
-		b43_led_turn_on(dev, led->index, led->activelow);
+	if (likely(!wl->leds.stop)) {
+		atomic_set(&led->state, brightness);
+		ieee80211_queue_work(wl->hw, &wl->leds.work);
+	}
 }
 
 static int b43_register_led(struct b43_wldev *dev, struct b43_led *led,
@@ -93,15 +124,15 @@
 {
 	int err;
 
-	b43_led_turn_off(dev, led_index, activelow);
-	if (led->dev)
+	if (led->wl)
 		return -EEXIST;
 	if (!default_trigger)
 		return -EINVAL;
-	led->dev = dev;
+	led->wl = dev->wl;
 	led->index = led_index;
 	led->activelow = activelow;
 	strncpy(led->name, name, sizeof(led->name));
+	atomic_set(&led->state, 0);
 
 	led->led_dev.name = led->name;
 	led->led_dev.default_trigger = default_trigger;
@@ -110,19 +141,19 @@
 	err = led_classdev_register(dev->dev->dev, &led->led_dev);
 	if (err) {
 		b43warn(dev->wl, "LEDs: Failed to register %s\n", name);
-		led->dev = NULL;
+		led->wl = NULL;
 		return err;
 	}
+
 	return 0;
 }
 
 static void b43_unregister_led(struct b43_led *led)
 {
-	if (!led->dev)
+	if (!led->wl)
 		return;
 	led_classdev_unregister(&led->led_dev);
-	b43_led_turn_off(led->dev, led->index, led->activelow);
-	led->dev = NULL;
+	led->wl = NULL;
 }
 
 static void b43_map_led(struct b43_wldev *dev,
@@ -137,24 +168,20 @@
 	 * generic LED triggers. */
 	switch (behaviour) {
 	case B43_LED_INACTIVE:
-		break;
 	case B43_LED_OFF:
-		b43_led_turn_off(dev, led_index, activelow);
-		break;
 	case B43_LED_ON:
-		b43_led_turn_on(dev, led_index, activelow);
 		break;
 	case B43_LED_ACTIVITY:
 	case B43_LED_TRANSFER:
 	case B43_LED_APTRANSFER:
 		snprintf(name, sizeof(name),
 			 "b43-%s::tx", wiphy_name(hw->wiphy));
-		b43_register_led(dev, &dev->led_tx, name,
+		b43_register_led(dev, &dev->wl->leds.led_tx, name,
 				 ieee80211_get_tx_led_name(hw),
 				 led_index, activelow);
 		snprintf(name, sizeof(name),
 			 "b43-%s::rx", wiphy_name(hw->wiphy));
-		b43_register_led(dev, &dev->led_rx, name,
+		b43_register_led(dev, &dev->wl->leds.led_rx, name,
 				 ieee80211_get_rx_led_name(hw),
 				 led_index, activelow);
 		break;
@@ -164,18 +191,15 @@
 	case B43_LED_MODE_BG:
 		snprintf(name, sizeof(name),
 			 "b43-%s::radio", wiphy_name(hw->wiphy));
-		b43_register_led(dev, &dev->led_radio, name,
+		b43_register_led(dev, &dev->wl->leds.led_radio, name,
 				 ieee80211_get_radio_led_name(hw),
 				 led_index, activelow);
-		/* Sync the RF-kill LED state with radio and switch states. */
-		if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev))
-			b43_led_turn_on(dev, led_index, activelow);
 		break;
 	case B43_LED_WEIRD:
 	case B43_LED_ASSOC:
 		snprintf(name, sizeof(name),
 			 "b43-%s::assoc", wiphy_name(hw->wiphy));
-		b43_register_led(dev, &dev->led_assoc, name,
+		b43_register_led(dev, &dev->wl->leds.led_assoc, name,
 				 ieee80211_get_assoc_led_name(hw),
 				 led_index, activelow);
 		break;
@@ -186,58 +210,150 @@
 	}
 }
 
-void b43_leds_init(struct b43_wldev *dev)
+static void b43_led_get_sprominfo(struct b43_wldev *dev,
+				  unsigned int led_index,
+				  enum b43_led_behaviour *behaviour,
+				  bool *activelow)
 {
 	struct ssb_bus *bus = dev->dev->bus;
 	u8 sprom[4];
-	int i;
-	enum b43_led_behaviour behaviour;
-	bool activelow;
 
 	sprom[0] = bus->sprom.gpio0;
 	sprom[1] = bus->sprom.gpio1;
 	sprom[2] = bus->sprom.gpio2;
 	sprom[3] = bus->sprom.gpio3;
 
-	for (i = 0; i < 4; i++) {
-		if (sprom[i] == 0xFF) {
-			/* There is no LED information in the SPROM
-			 * for this LED. Hardcode it here. */
-			activelow = 0;
-			switch (i) {
-			case 0:
-				behaviour = B43_LED_ACTIVITY;
-				activelow = 1;
-				if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
-					behaviour = B43_LED_RADIO_ALL;
-				break;
-			case 1:
-				behaviour = B43_LED_RADIO_B;
-				if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
-					behaviour = B43_LED_ASSOC;
-				break;
-			case 2:
-				behaviour = B43_LED_RADIO_A;
-				break;
-			case 3:
-				behaviour = B43_LED_OFF;
-				break;
-			default:
-				B43_WARN_ON(1);
-				return;
-			}
-		} else {
-			behaviour = sprom[i] & B43_LED_BEHAVIOUR;
-			activelow = !!(sprom[i] & B43_LED_ACTIVELOW);
+	if (sprom[led_index] == 0xFF) {
+		/* There is no LED information in the SPROM
+		 * for this LED. Hardcode it here. */
+		*activelow = 0;
+		switch (led_index) {
+		case 0:
+			*behaviour = B43_LED_ACTIVITY;
+			*activelow = 1;
+			if (bus->boardinfo.vendor == PCI_VENDOR_ID_COMPAQ)
+				*behaviour = B43_LED_RADIO_ALL;
+			break;
+		case 1:
+			*behaviour = B43_LED_RADIO_B;
+			if (bus->boardinfo.vendor == PCI_VENDOR_ID_ASUSTEK)
+				*behaviour = B43_LED_ASSOC;
+			break;
+		case 2:
+			*behaviour = B43_LED_RADIO_A;
+			break;
+		case 3:
+			*behaviour = B43_LED_OFF;
+			break;
+		default:
+			B43_WARN_ON(1);
+			return;
 		}
-		b43_map_led(dev, i, behaviour, activelow);
+	} else {
+		*behaviour = sprom[led_index] & B43_LED_BEHAVIOUR;
+		*activelow = !!(sprom[led_index] & B43_LED_ACTIVELOW);
 	}
 }
 
+void b43_leds_init(struct b43_wldev *dev)
+{
+	struct b43_led *led;
+	unsigned int i;
+	enum b43_led_behaviour behaviour;
+	bool activelow;
+
+	/* Sync the RF-kill LED state (if we have one) with radio and switch states. */
+	led = &dev->wl->leds.led_radio;
+	if (led->wl) {
+		if (dev->phy.radio_on && b43_is_hw_radio_enabled(dev)) {
+			b43_led_turn_on(dev, led->index, led->activelow);
+			led->hw_state = 1;
+			atomic_set(&led->state, 1);
+		} else {
+			b43_led_turn_off(dev, led->index, led->activelow);
+			led->hw_state = 0;
+			atomic_set(&led->state, 0);
+		}
+	}
+
+	/* Initialize TX/RX/ASSOC leds */
+	led = &dev->wl->leds.led_tx;
+	if (led->wl) {
+		b43_led_turn_off(dev, led->index, led->activelow);
+		led->hw_state = 0;
+		atomic_set(&led->state, 0);
+	}
+	led = &dev->wl->leds.led_rx;
+	if (led->wl) {
+		b43_led_turn_off(dev, led->index, led->activelow);
+		led->hw_state = 0;
+		atomic_set(&led->state, 0);
+	}
+	led = &dev->wl->leds.led_assoc;
+	if (led->wl) {
+		b43_led_turn_off(dev, led->index, led->activelow);
+		led->hw_state = 0;
+		atomic_set(&led->state, 0);
+	}
+
+	/* Initialize other LED states. */
+	for (i = 0; i < B43_MAX_NR_LEDS; i++) {
+		b43_led_get_sprominfo(dev, i, &behaviour, &activelow);
+		switch (behaviour) {
+		case B43_LED_OFF:
+			b43_led_turn_off(dev, i, activelow);
+			break;
+		case B43_LED_ON:
+			b43_led_turn_on(dev, i, activelow);
+			break;
+		default:
+			/* Leave others as-is. */
+			break;
+		}
+	}
+
+	dev->wl->leds.stop = 0;
+}
+
 void b43_leds_exit(struct b43_wldev *dev)
 {
-	b43_unregister_led(&dev->led_tx);
-	b43_unregister_led(&dev->led_rx);
-	b43_unregister_led(&dev->led_assoc);
-	b43_unregister_led(&dev->led_radio);
+	struct b43_leds *leds = &dev->wl->leds;
+
+	b43_led_turn_off(dev, leds->led_tx.index, leds->led_tx.activelow);
+	b43_led_turn_off(dev, leds->led_rx.index, leds->led_rx.activelow);
+	b43_led_turn_off(dev, leds->led_assoc.index, leds->led_assoc.activelow);
+	b43_led_turn_off(dev, leds->led_radio.index, leds->led_radio.activelow);
+}
+
+void b43_leds_stop(struct b43_wldev *dev)
+{
+	struct b43_leds *leds = &dev->wl->leds;
+
+	leds->stop = 1;
+	cancel_work_sync(&leds->work);
+}
+
+void b43_leds_register(struct b43_wldev *dev)
+{
+	unsigned int i;
+	enum b43_led_behaviour behaviour;
+	bool activelow;
+
+	INIT_WORK(&dev->wl->leds.work, b43_leds_work);
+
+	/* Register the LEDs to the LED subsystem. */
+	for (i = 0; i < B43_MAX_NR_LEDS; i++) {
+		b43_led_get_sprominfo(dev, i, &behaviour, &activelow);
+		b43_map_led(dev, i, behaviour, activelow);
+	}
+}
+
+void b43_leds_unregister(struct b43_wldev *dev)
+{
+	struct b43_leds *leds = &dev->wl->leds;
+
+	b43_unregister_led(&leds->led_tx);
+	b43_unregister_led(&leds->led_rx);
+	b43_unregister_led(&leds->led_assoc);
+	b43_unregister_led(&leds->led_radio);
 }
diff --git a/drivers/net/wireless/b43/leds.h b/drivers/net/wireless/b43/leds.h
index b8b1dd5..9592e4c 100644
--- a/drivers/net/wireless/b43/leds.h
+++ b/drivers/net/wireless/b43/leds.h
@@ -7,12 +7,13 @@
 
 #include <linux/types.h>
 #include <linux/leds.h>
+#include <linux/workqueue.h>
 
 
 #define B43_LED_MAX_NAME_LEN	31
 
 struct b43_led {
-	struct b43_wldev *dev;
+	struct b43_wl *wl;
 	/* The LED class device */
 	struct led_classdev led_dev;
 	/* The index number of the LED. */
@@ -22,8 +23,24 @@
 	bool activelow;
 	/* The unique name string for this LED device. */
 	char name[B43_LED_MAX_NAME_LEN + 1];
+	/* The current status of the LED. This is updated locklessly. */
+	atomic_t state;
+	/* The active state in hardware. */
+	bool hw_state;
 };
 
+struct b43_leds {
+	struct b43_led led_tx;
+	struct b43_led led_rx;
+	struct b43_led led_radio;
+	struct b43_led led_assoc;
+
+	bool stop;
+	struct work_struct work;
+};
+
+#define B43_MAX_NR_LEDS			4
+
 #define B43_LED_BEHAVIOUR		0x7F
 #define B43_LED_ACTIVELOW		0x80
 /* LED behaviour values */
@@ -42,23 +59,35 @@
 	B43_LED_INACTIVE,
 };
 
+void b43_leds_register(struct b43_wldev *dev);
+void b43_leds_unregister(struct b43_wldev *dev);
 void b43_leds_init(struct b43_wldev *dev);
 void b43_leds_exit(struct b43_wldev *dev);
+void b43_leds_stop(struct b43_wldev *dev);
 
 
 #else /* CONFIG_B43_LEDS */
 /* LED support disabled */
 
-struct b43_led {
+struct b43_leds {
 	/* empty */
 };
 
+static inline void b43_leds_register(struct b43_wldev *dev)
+{
+}
+static inline void b43_leds_unregister(struct b43_wldev *dev)
+{
+}
 static inline void b43_leds_init(struct b43_wldev *dev)
 {
 }
 static inline void b43_leds_exit(struct b43_wldev *dev)
 {
 }
+static inline void b43_leds_stop(struct b43_wldev *dev)
+{
+}
 #endif /* CONFIG_B43_LEDS */
 
 #endif /* B43_LEDS_H_ */
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index e789792..9b907a3 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -8,6 +8,9 @@
   Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
   Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
 
+  SDIO support
+  Copyright (c) 2009 Albert Herranz <albert_herranz@yahoo.es>
+
   Some parts of the code in this file are derived from the ipw2200
   driver  Copyright(c) 2003 - 2004 Intel Corporation.
 
@@ -53,6 +56,8 @@
 #include "xmit.h"
 #include "lo.h"
 #include "pcmcia.h"
+#include "sdio.h"
+#include <linux/mmc/sdio_func.h>
 
 MODULE_DESCRIPTION("Broadcom B43 wireless driver");
 MODULE_AUTHOR("Martin Langer");
@@ -1587,7 +1592,7 @@
 	mutex_lock(&wl->mutex);
 	dev = wl->current_dev;
 	if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) {
-		if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
+		if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
 			/* wl->mutex is enough. */
 			b43_do_beacon_update_trigger_work(dev);
 			mmiowb();
@@ -1825,6 +1830,16 @@
 
 	/* Re-enable interrupts on the device by restoring the current interrupt mask. */
 	b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, dev->irq_mask);
+
+#if B43_DEBUG
+	if (b43_debug(dev, B43_DBG_VERBOSESTATS)) {
+		dev->irq_count++;
+		for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) {
+			if (reason & (1 << i))
+				dev->irq_bit_count[i]++;
+		}
+	}
+#endif
 }
 
 /* Interrupt thread handler. Handles device interrupts in thread context. */
@@ -1905,6 +1920,21 @@
 	return ret;
 }
 
+/* SDIO interrupt handler. This runs in process context. */
+static void b43_sdio_interrupt_handler(struct b43_wldev *dev)
+{
+	struct b43_wl *wl = dev->wl;
+	irqreturn_t ret;
+
+	mutex_lock(&wl->mutex);
+
+	ret = b43_do_interrupt(dev);
+	if (ret == IRQ_WAKE_THREAD)
+		b43_do_interrupt_thread(dev);
+
+	mutex_unlock(&wl->mutex);
+}
+
 void b43_do_release_fw(struct b43_firmware_file *fw)
 {
 	release_firmware(fw->data);
@@ -2645,6 +2675,20 @@
 			cfp_pretbtt = 50;
 	}
 	b43_write16(dev, 0x612, cfp_pretbtt);
+
+	/* FIXME: We don't currently implement the PMQ mechanism,
+	 *        so always disable it. If we want to implement PMQ,
+	 *        we need to enable it here (clear DISCPMQ) in AP mode.
+	 */
+	if (0  /* ctl & B43_MACCTL_AP */) {
+		b43_write32(dev, B43_MMIO_MACCTL,
+			    b43_read32(dev, B43_MMIO_MACCTL)
+			    & ~B43_MACCTL_DISCPMQ);
+	} else {
+		b43_write32(dev, B43_MMIO_MACCTL,
+			    b43_read32(dev, B43_MMIO_MACCTL)
+			    | B43_MACCTL_DISCPMQ);
+	}
 }
 
 static void b43_rate_memory_write(struct b43_wldev *dev, u16 rate, int is_ofdm)
@@ -2873,6 +2917,27 @@
 
 	atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT);
 	wmb();
+
+#if B43_DEBUG
+	if (b43_debug(dev, B43_DBG_VERBOSESTATS)) {
+		unsigned int i;
+
+		b43dbg(dev->wl, "Stats: %7u IRQs/sec, %7u TX/sec, %7u RX/sec\n",
+		       dev->irq_count / 15,
+		       dev->tx_count / 15,
+		       dev->rx_count / 15);
+		dev->irq_count = 0;
+		dev->tx_count = 0;
+		dev->rx_count = 0;
+		for (i = 0; i < ARRAY_SIZE(dev->irq_bit_count); i++) {
+			if (dev->irq_bit_count[i]) {
+				b43dbg(dev->wl, "Stats: %7u IRQ-%02u/sec (0x%08X)\n",
+				       dev->irq_bit_count[i] / 15, i, (1 << i));
+				dev->irq_bit_count[i] = 0;
+			}
+		}
+	}
+#endif
 }
 
 static void do_periodic_work(struct b43_wldev *dev)
@@ -3002,14 +3067,18 @@
 static int b43_rng_read(struct hwrng *rng, u32 *data)
 {
 	struct b43_wl *wl = (struct b43_wl *)rng->priv;
+	struct b43_wldev *dev;
+	int count = -ENODEV;
 
-	/* FIXME: We need to take wl->mutex here to make sure the device
-	 * is not going away from under our ass. However it could deadlock
-	 * with hwrng internal locking. */
+	mutex_lock(&wl->mutex);
+	dev = wl->current_dev;
+	if (likely(dev && b43_status(dev) >= B43_STAT_INITIALIZED)) {
+		*data = b43_read16(dev, B43_MMIO_RNG);
+		count = sizeof(u16);
+	}
+	mutex_unlock(&wl->mutex);
 
-	*data = b43_read16(wl->current_dev, B43_MMIO_RNG);
-
-	return (sizeof(u16));
+	return count;
 }
 #endif /* CONFIG_B43_HWRNG */
 
@@ -3068,6 +3137,9 @@
 			dev_kfree_skb(skb); /* Drop it */
 	}
 
+#if B43_DEBUG
+	dev->tx_count++;
+#endif
 	mutex_unlock(&wl->mutex);
 }
 
@@ -3820,7 +3892,7 @@
 
 	/* Disable interrupts on the device. */
 	b43_set_status(dev, B43_STAT_INITIALIZED);
-	if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
+	if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
 		/* wl->mutex is locked. That is enough. */
 		b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0);
 		b43_read32(dev, B43_MMIO_GEN_IRQ_MASK);	/* Flush */
@@ -3830,10 +3902,15 @@
 		b43_read32(dev, B43_MMIO_GEN_IRQ_MASK);	/* Flush */
 		spin_unlock_irq(&wl->hardirq_lock);
 	}
-	/* Synchronize the interrupt handlers. Unlock to avoid deadlocks. */
+	/* Synchronize and free the interrupt handlers. Unlock to avoid deadlocks. */
 	orig_dev = dev;
 	mutex_unlock(&wl->mutex);
-	synchronize_irq(dev->dev->irq);
+	if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
+		b43_sdio_free_irq(dev);
+	} else {
+		synchronize_irq(dev->dev->irq);
+		free_irq(dev->dev->irq, dev);
+	}
 	mutex_lock(&wl->mutex);
 	dev = wl->current_dev;
 	if (!dev)
@@ -3850,7 +3927,7 @@
 		dev_kfree_skb(skb_dequeue(&wl->tx_queue));
 
 	b43_mac_suspend(dev);
-	free_irq(dev->dev->irq, dev);
+	b43_leds_exit(dev);
 	b43dbg(wl, "Wireless interface stopped\n");
 
 	return dev;
@@ -3864,12 +3941,20 @@
 	B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED);
 
 	drain_txstatus_queue(dev);
-	err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
-				   b43_interrupt_thread_handler,
-				   IRQF_SHARED, KBUILD_MODNAME, dev);
-	if (err) {
-		b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq);
-		goto out;
+	if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
+		err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler);
+		if (err) {
+			b43err(dev->wl, "Cannot request SDIO IRQ\n");
+			goto out;
+		}
+	} else {
+		err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
+					   b43_interrupt_thread_handler,
+					   IRQF_SHARED, KBUILD_MODNAME, dev);
+		if (err) {
+			b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq);
+			goto out;
+		}
 	}
 
 	/* We are ready to run. */
@@ -3882,8 +3967,10 @@
 	/* Start maintainance work */
 	b43_periodic_tasks_setup(dev);
 
+	b43_leds_init(dev);
+
 	b43dbg(dev->wl, "Wireless interface started\n");
-      out:
+out:
 	return err;
 }
 
@@ -4160,10 +4247,6 @@
 	macctl |= B43_MACCTL_PSM_JMP0;
 	b43_write32(dev, B43_MMIO_MACCTL, macctl);
 
-	if (!dev->suspend_in_progress) {
-		b43_leds_exit(dev);
-		b43_rng_exit(dev->wl);
-	}
 	b43_dma_free(dev);
 	b43_pio_free(dev);
 	b43_chip_exit(dev);
@@ -4180,7 +4263,6 @@
 /* Initialize a wireless core */
 static int b43_wireless_core_init(struct b43_wldev *dev)
 {
-	struct b43_wl *wl = dev->wl;
 	struct ssb_bus *bus = dev->dev->bus;
 	struct ssb_sprom *sprom = &bus->sprom;
 	struct b43_phy *phy = &dev->phy;
@@ -4264,7 +4346,9 @@
 	/* Maximum Contention Window */
 	b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
 
-	if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) {
+	if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) ||
+	    (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) ||
+	    B43_FORCE_PIO) {
 		dev->__using_pio_transfers = 1;
 		err = b43_pio_init(dev);
 	} else {
@@ -4280,15 +4364,13 @@
 	ssb_bus_powerup(bus, !(sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW));
 	b43_upload_card_macaddress(dev);
 	b43_security_init(dev);
-	if (!dev->suspend_in_progress)
-		b43_rng_init(wl);
+
+	ieee80211_wake_queues(dev->wl->hw);
 
 	ieee80211_wake_queues(dev->wl->hw);
 
 	b43_set_status(dev, B43_STAT_INITIALIZED);
 
-	if (!dev->suspend_in_progress)
-		b43_leds_init(dev);
 out:
 	return err;
 
@@ -4837,7 +4919,6 @@
 
 	/* Initialize struct b43_wl */
 	wl->hw = hw;
-	spin_lock_init(&wl->leds_lock);
 	mutex_init(&wl->mutex);
 	spin_lock_init(&wl->hardirq_lock);
 	INIT_LIST_HEAD(&wl->devlist);
@@ -4878,6 +4959,8 @@
 		err = ieee80211_register_hw(wl->hw);
 		if (err)
 			goto err_one_core_detach;
+		b43_leds_register(wl->current_dev);
+		b43_rng_init(wl);
 	}
 
       out:
@@ -4906,12 +4989,15 @@
 		 * might have modified it. Restoring is important, so the networking
 		 * stack can properly free resources. */
 		wl->hw->queues = wl->mac80211_initially_registered_queues;
+		b43_leds_stop(wldev);
 		ieee80211_unregister_hw(wl->hw);
 	}
 
 	b43_one_core_detach(dev);
 
 	if (list_empty(&wl->devlist)) {
+		b43_rng_exit(wl);
+		b43_leds_unregister(wldev);
 		/* Last core on the chip unregistered.
 		 * We can destroy common struct b43_wl.
 		 */
@@ -4929,80 +5015,17 @@
 	ieee80211_queue_work(dev->wl->hw, &dev->restart_work);
 }
 
-#ifdef CONFIG_PM
-
-static int b43_suspend(struct ssb_device *dev, pm_message_t state)
-{
-	struct b43_wldev *wldev = ssb_get_drvdata(dev);
-	struct b43_wl *wl = wldev->wl;
-
-	b43dbg(wl, "Suspending...\n");
-
-	mutex_lock(&wl->mutex);
-	wldev->suspend_in_progress = true;
-	wldev->suspend_init_status = b43_status(wldev);
-	if (wldev->suspend_init_status >= B43_STAT_STARTED)
-		wldev = b43_wireless_core_stop(wldev);
-	if (wldev && wldev->suspend_init_status >= B43_STAT_INITIALIZED)
-		b43_wireless_core_exit(wldev);
-	mutex_unlock(&wl->mutex);
-
-	b43dbg(wl, "Device suspended.\n");
-
-	return 0;
-}
-
-static int b43_resume(struct ssb_device *dev)
-{
-	struct b43_wldev *wldev = ssb_get_drvdata(dev);
-	struct b43_wl *wl = wldev->wl;
-	int err = 0;
-
-	b43dbg(wl, "Resuming...\n");
-
-	mutex_lock(&wl->mutex);
-	if (wldev->suspend_init_status >= B43_STAT_INITIALIZED) {
-		err = b43_wireless_core_init(wldev);
-		if (err) {
-			b43err(wl, "Resume failed at core init\n");
-			goto out;
-		}
-	}
-	if (wldev->suspend_init_status >= B43_STAT_STARTED) {
-		err = b43_wireless_core_start(wldev);
-		if (err) {
-			b43_leds_exit(wldev);
-			b43_rng_exit(wldev->wl);
-			b43_wireless_core_exit(wldev);
-			b43err(wl, "Resume failed at core start\n");
-			goto out;
-		}
-	}
-	b43dbg(wl, "Device resumed.\n");
- out:
-	wldev->suspend_in_progress = false;
-	mutex_unlock(&wl->mutex);
-	return err;
-}
-
-#else /* CONFIG_PM */
-# define b43_suspend	NULL
-# define b43_resume	NULL
-#endif /* CONFIG_PM */
-
 static struct ssb_driver b43_ssb_driver = {
 	.name		= KBUILD_MODNAME,
 	.id_table	= b43_ssb_tbl,
 	.probe		= b43_probe,
 	.remove		= b43_remove,
-	.suspend	= b43_suspend,
-	.resume		= b43_resume,
 };
 
 static void b43_print_driverinfo(void)
 {
 	const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "",
-		   *feat_leds = "";
+		   *feat_leds = "", *feat_sdio = "";
 
 #ifdef CONFIG_B43_PCI_AUTOSELECT
 	feat_pci = "P";
@@ -5016,11 +5039,14 @@
 #ifdef CONFIG_B43_LEDS
 	feat_leds = "L";
 #endif
+#ifdef CONFIG_B43_SDIO
+	feat_sdio = "S";
+#endif
 	printk(KERN_INFO "Broadcom 43xx driver loaded "
-	       "[ Features: %s%s%s%s, Firmware-ID: "
+	       "[ Features: %s%s%s%s%s, Firmware-ID: "
 	       B43_SUPPORTED_FIRMWARE_ID " ]\n",
 	       feat_pci, feat_pcmcia, feat_nphy,
-	       feat_leds);
+	       feat_leds, feat_sdio);
 }
 
 static int __init b43_init(void)
@@ -5031,13 +5057,18 @@
 	err = b43_pcmcia_init();
 	if (err)
 		goto err_dfs_exit;
-	err = ssb_driver_register(&b43_ssb_driver);
+	err = b43_sdio_init();
 	if (err)
 		goto err_pcmcia_exit;
+	err = ssb_driver_register(&b43_ssb_driver);
+	if (err)
+		goto err_sdio_exit;
 	b43_print_driverinfo();
 
 	return err;
 
+err_sdio_exit:
+	b43_sdio_exit();
 err_pcmcia_exit:
 	b43_pcmcia_exit();
 err_dfs_exit:
@@ -5048,6 +5079,7 @@
 static void __exit b43_exit(void)
 {
 	ssb_driver_unregister(&b43_ssb_driver);
+	b43_sdio_exit();
 	b43_pcmcia_exit();
 	b43_debugfs_exit();
 }
diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c
index 3e02d96..1e318d8 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/b43/phy_lp.c
@@ -2228,6 +2228,16 @@
 	return B43_TXPWR_RES_DONE;
 }
 
+void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on)
+{
+       if (on) {
+               b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8);
+       } else {
+               b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007);
+               b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007);
+       }
+}
+
 const struct b43_phy_operations b43_phyops_lp = {
 	.allocate		= b43_lpphy_op_allocate,
 	.free			= b43_lpphy_op_free,
@@ -2239,7 +2249,7 @@
 	.radio_read		= b43_lpphy_op_radio_read,
 	.radio_write		= b43_lpphy_op_radio_write,
 	.software_rfkill	= b43_lpphy_op_software_rfkill,
-	.switch_analog		= b43_phyop_switch_analog_generic,
+	.switch_analog		= b43_lpphy_op_switch_analog,
 	.switch_channel		= b43_lpphy_op_switch_channel,
 	.get_default_chan	= b43_lpphy_op_get_default_chan,
 	.set_rx_antenna		= b43_lpphy_op_set_rx_antenna,
diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c
index 3498b68..e96091b3 100644
--- a/drivers/net/wireless/b43/pio.c
+++ b/drivers/net/wireless/b43/pio.c
@@ -574,7 +574,7 @@
 	q->buffer_used -= total_len;
 	q->free_packet_slots += 1;
 
-	ieee80211_tx_status_irqsafe(dev->wl->hw, pack->skb);
+	ieee80211_tx_status(dev->wl->hw, pack->skb);
 	pack->skb = NULL;
 	list_add(&pack->list, &q->packets_list);
 
diff --git a/drivers/net/wireless/b43/rfkill.c b/drivers/net/wireless/b43/rfkill.c
index 31e5599..7a3218c 100644
--- a/drivers/net/wireless/b43/rfkill.c
+++ b/drivers/net/wireless/b43/rfkill.c
@@ -28,7 +28,7 @@
 /* Returns TRUE, if the radio is enabled in hardware. */
 bool b43_is_hw_radio_enabled(struct b43_wldev *dev)
 {
-	if (dev->phy.rev >= 3) {
+	if (dev->phy.rev >= 3 || dev->phy.type == B43_PHYTYPE_LP) {
 		if (!(b43_read32(dev, B43_MMIO_RADIO_HWENABLED_HI)
 		      & B43_MMIO_RADIO_HWENABLED_HI_MASK))
 			return 1;
diff --git a/drivers/net/wireless/b43/sdio.c b/drivers/net/wireless/b43/sdio.c
new file mode 100644
index 0000000..0d3ac64
--- /dev/null
+++ b/drivers/net/wireless/b43/sdio.c
@@ -0,0 +1,202 @@
+/*
+ * Broadcom B43 wireless driver
+ *
+ * SDIO over Sonics Silicon Backplane bus glue for b43.
+ *
+ * Copyright (C) 2009 Albert Herranz
+ * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/ssb/ssb.h>
+
+#include "sdio.h"
+#include "b43.h"
+
+
+#define HNBU_CHIPID		0x01	/* vendor & device id */
+
+#define B43_SDIO_BLOCK_SIZE	64	/* rx fifo max size in bytes */
+
+
+static const struct b43_sdio_quirk {
+	u16 vendor;
+	u16 device;
+	unsigned int quirks;
+} b43_sdio_quirks[] = {
+	{ 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, },
+	{ },
+};
+
+
+static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device)
+{
+	const struct b43_sdio_quirk *q;
+
+	for (q = b43_sdio_quirks; q->quirks; q++) {
+		if (vendor == q->vendor && device == q->device)
+			return q->quirks;
+	}
+
+	return 0;
+}
+
+static void b43_sdio_interrupt_dispatcher(struct sdio_func *func)
+{
+	struct b43_sdio *sdio = sdio_get_drvdata(func);
+	struct b43_wldev *dev = sdio->irq_handler_opaque;
+
+	if (unlikely(b43_status(dev) < B43_STAT_STARTED))
+		return;
+
+	sdio_release_host(func);
+	sdio->irq_handler(dev);
+	sdio_claim_host(func);
+}
+
+int b43_sdio_request_irq(struct b43_wldev *dev,
+			 void (*handler)(struct b43_wldev *dev))
+{
+	struct ssb_bus *bus = dev->dev->bus;
+	struct sdio_func *func = bus->host_sdio;
+	struct b43_sdio *sdio = sdio_get_drvdata(func);
+	int err;
+
+	sdio->irq_handler_opaque = dev;
+	sdio->irq_handler = handler;
+	sdio_claim_host(func);
+	err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher);
+	sdio_release_host(func);
+
+	return err;
+}
+
+void b43_sdio_free_irq(struct b43_wldev *dev)
+{
+	struct ssb_bus *bus = dev->dev->bus;
+	struct sdio_func *func = bus->host_sdio;
+	struct b43_sdio *sdio = sdio_get_drvdata(func);
+
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_release_host(func);
+	sdio->irq_handler_opaque = NULL;
+	sdio->irq_handler = NULL;
+}
+
+static int b43_sdio_probe(struct sdio_func *func,
+			  const struct sdio_device_id *id)
+{
+	struct b43_sdio *sdio;
+	struct sdio_func_tuple *tuple;
+	u16 vendor = 0, device = 0;
+	int error;
+
+	/* Look for the card chip identifier. */
+	tuple = func->tuples;
+	while (tuple) {
+		switch (tuple->code) {
+		case 0x80:
+			switch (tuple->data[0]) {
+			case HNBU_CHIPID:
+				if (tuple->size != 5)
+					break;
+				vendor = tuple->data[1] | (tuple->data[2]<<8);
+				device = tuple->data[3] | (tuple->data[4]<<8);
+				dev_info(&func->dev, "Chip ID %04x:%04x\n",
+					 vendor, device);
+				break;
+			default:
+				break;
+			}
+			break;
+		default:
+			break;
+		}
+		tuple = tuple->next;
+	}
+	if (!vendor || !device) {
+		error = -ENODEV;
+		goto out;
+	}
+
+	sdio_claim_host(func);
+	error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE);
+	if (error) {
+		dev_err(&func->dev, "failed to set block size to %u bytes,"
+			" error %d\n", B43_SDIO_BLOCK_SIZE, error);
+		goto err_release_host;
+	}
+	error = sdio_enable_func(func);
+	if (error) {
+		dev_err(&func->dev, "failed to enable func, error %d\n", error);
+		goto err_release_host;
+	}
+	sdio_release_host(func);
+
+	sdio = kzalloc(sizeof(*sdio), GFP_KERNEL);
+	if (!sdio) {
+		error = -ENOMEM;
+		dev_err(&func->dev, "failed to allocate ssb bus\n");
+		goto err_disable_func;
+	}
+	error = ssb_bus_sdiobus_register(&sdio->ssb, func,
+					 b43_sdio_get_quirks(vendor, device));
+	if (error) {
+		dev_err(&func->dev, "failed to register ssb sdio bus,"
+			" error %d\n", error);
+		goto err_free_ssb;
+	}
+	sdio_set_drvdata(func, sdio);
+
+	return 0;
+
+err_free_ssb:
+	kfree(sdio);
+err_disable_func:
+	sdio_disable_func(func);
+err_release_host:
+	sdio_release_host(func);
+out:
+	return error;
+}
+
+static void b43_sdio_remove(struct sdio_func *func)
+{
+	struct b43_sdio *sdio = sdio_get_drvdata(func);
+
+	ssb_bus_unregister(&sdio->ssb);
+	sdio_disable_func(func);
+	kfree(sdio);
+	sdio_set_drvdata(func, NULL);
+}
+
+static const struct sdio_device_id b43_sdio_ids[] = {
+	{ SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */
+	{ },
+};
+
+static struct sdio_driver b43_sdio_driver = {
+	.name		= "b43-sdio",
+	.id_table	= b43_sdio_ids,
+	.probe		= b43_sdio_probe,
+	.remove		= b43_sdio_remove,
+};
+
+int b43_sdio_init(void)
+{
+	return sdio_register_driver(&b43_sdio_driver);
+}
+
+void b43_sdio_exit(void)
+{
+	sdio_unregister_driver(&b43_sdio_driver);
+}
diff --git a/drivers/net/wireless/b43/sdio.h b/drivers/net/wireless/b43/sdio.h
new file mode 100644
index 0000000..fb63309
--- /dev/null
+++ b/drivers/net/wireless/b43/sdio.h
@@ -0,0 +1,45 @@
+#ifndef B43_SDIO_H_
+#define B43_SDIO_H_
+
+#include <linux/ssb/ssb.h>
+
+struct b43_wldev;
+
+
+#ifdef CONFIG_B43_SDIO
+
+struct b43_sdio {
+	struct ssb_bus ssb;
+	void *irq_handler_opaque;
+	void (*irq_handler)(struct b43_wldev *dev);
+};
+
+int b43_sdio_request_irq(struct b43_wldev *dev,
+			 void (*handler)(struct b43_wldev *dev));
+void b43_sdio_free_irq(struct b43_wldev *dev);
+
+int b43_sdio_init(void);
+void b43_sdio_exit(void);
+
+
+#else /* CONFIG_B43_SDIO */
+
+
+int b43_sdio_request_irq(struct b43_wldev *dev,
+			 void (*handler)(struct b43_wldev *dev))
+{
+	return -ENODEV;
+}
+void b43_sdio_free_irq(struct b43_wldev *dev)
+{
+}
+static inline int b43_sdio_init(void)
+{
+	return 0;
+}
+static inline void b43_sdio_exit(void)
+{
+}
+
+#endif /* CONFIG_B43_SDIO */
+#endif /* B43_SDIO_H_ */
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index 14f5412..ac9f600 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -690,8 +690,11 @@
 	}
 
 	memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
-	ieee80211_rx_irqsafe(dev->wl->hw, skb);
+	ieee80211_rx(dev->wl->hw, skb);
 
+#if B43_DEBUG
+	dev->rx_count++;
+#endif
 	return;
 drop:
 	b43dbg(dev->wl, "RX: Packet dropped\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index ca61d37..3259b88 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2021,6 +2021,12 @@
 					   agg->frame_count, txq_id, idx);
 
 			hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx);
+			if (!hdr) {
+				IWL_ERR(priv,
+					"BUG_ON idx doesn't point to valid skb"
+					" idx=%d, txq_id=%d\n", idx, txq_id);
+				return -1;
+			}
 
 			sc = le16_to_cpu(hdr->seq_ctrl);
 			if (idx != (SEQ_TO_SN(sc) & 0xff)) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index 1d539e3..a6391c7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -1163,6 +1163,12 @@
 					   agg->frame_count, txq_id, idx);
 
 			hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx);
+			if (!hdr) {
+				IWL_ERR(priv,
+					"BUG_ON idx doesn't point to valid skb"
+					" idx=%d, txq_id=%d\n", idx, txq_id);
+				return -1;
+			}
 
 			sc = le16_to_cpu(hdr->seq_ctrl);
 			if (idx != (SEQ_TO_SN(sc) & 0xff)) {
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index b90adcb..8e1bb53 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -250,12 +250,20 @@
 		}
 		spin_unlock_irqrestore(&rxq->lock, flags);
 
+		if (rxq->free_count > RX_LOW_WATERMARK)
+			priority |= __GFP_NOWARN;
 		/* Alloc a new receive buffer */
 		skb = alloc_skb(priv->hw_params.rx_buf_size + 256,
 						priority);
 
 		if (!skb) {
-			IWL_CRIT(priv, "Can not allocate SKB buffers\n");
+			if (net_ratelimit())
+				IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n");
+			if ((rxq->free_count <= RX_LOW_WATERMARK) &&
+			    net_ratelimit())
+				IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n",
+					 priority == GFP_ATOMIC ?  "GFP_ATOMIC" : "GFP_KERNEL",
+					 rxq->free_count);
 			/* We don't reschedule replenish work here -- we will
 			 * call the restock method and if it still needs
 			 * more buffers it will schedule replenish */
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index a2b9ec8..c6633fe 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -520,7 +520,7 @@
 	struct iwl_host_cmd cmd = {
 		.id = REPLY_WEPKEY,
 		.data = wep_cmd,
-		.flags = CMD_SYNC,
+		.flags = CMD_ASYNC,
 	};
 
 	memset(wep_cmd, 0, cmd_size +
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 0909668..4f2d439 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -1146,11 +1146,18 @@
 		}
 		spin_unlock_irqrestore(&rxq->lock, flags);
 
+		if (rxq->free_count > RX_LOW_WATERMARK)
+			priority |= __GFP_NOWARN;
 		/* Alloc a new receive buffer */
 		skb = alloc_skb(priv->hw_params.rx_buf_size, priority);
 		if (!skb) {
 			if (net_ratelimit())
-				IWL_CRIT(priv, ": Can not allocate SKB buffers\n");
+				IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n");
+			if ((rxq->free_count <= RX_LOW_WATERMARK) &&
+			    net_ratelimit())
+				IWL_CRIT(priv, "Failed to allocate SKB buffer with %s. Only %u free buffers remaining.\n",
+					 priority == GFP_ATOMIC ?  "GFP_ATOMIC" : "GFP_KERNEL",
+					 rxq->free_count);
 			/* We don't reschedule replenish work here -- we will
 			 * call the restock method and if it still needs
 			 * more buffers it will schedule replenish */
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 5462cb5..567f029 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -380,7 +380,7 @@
 {
 }
 
-static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb, bool l2pad,
+static inline void rt2x00crypto_rx_insert_iv(struct sk_buff *skb,
 					     unsigned int header_length,
 					     struct rxdone_entry_desc *rxdesc)
 {
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig
index 7b14d5b..88060e1 100644
--- a/drivers/net/wireless/wl12xx/Kconfig
+++ b/drivers/net/wireless/wl12xx/Kconfig
@@ -1,5 +1,5 @@
 menuconfig WL12XX
-	boolean "TI wl12xx driver support"
+	tristate "TI wl12xx driver support"
 	depends on MAC80211 && WLAN_80211 && EXPERIMENTAL
 	---help---
 	  This will enable TI wl12xx driver support. The drivers make
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 3868884..23a6a6d 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -1070,7 +1070,7 @@
 
 	/* Find bulk out endpoint */
 	endpoint = &iface_desc->endpoint[1].desc;
-	if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT &&
+	if (usb_endpoint_dir_out(endpoint) &&
 	    usb_endpoint_xfer_bulk(endpoint)) {
 		bulk_out_ep = endpoint->bEndpointAddress;
 	} else {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 0399011..71e10ca 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -90,8 +90,8 @@
 		bss->dtim_period = tim_ie->dtim_period;
 	}
 
-	/* set default value for buggy APs */
-	if (!elems->tim || bss->dtim_period == 0)
+	/* set default value for buggy AP/no TIM element */
+	if (bss->dtim_period == 0)
 		bss->dtim_period = 1;
 
 	bss->supp_rates_len = 0;
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index d16cd9e..bf72527 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -26,11 +26,11 @@
 
 	wdev->wext.connect.ie = wdev->wext.ie;
 	wdev->wext.connect.ie_len = wdev->wext.ie_len;
-	wdev->wext.connect.privacy = wdev->wext.default_key != -1;
 
 	if (wdev->wext.keys) {
 		wdev->wext.keys->def = wdev->wext.default_key;
 		wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
+		wdev->wext.connect.privacy = true;
 	}
 
 	if (!wdev->wext.connect.ssid_len)