From d6431df9c1c19e5b1f4f387e36c8f1825bce3b8f Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Tue, 26 Jul 2011 20:53:40 +0000
Subject: [PATCH] ath9k: rework tx power handling - display the correct
 *current* tx power, and also fix tx power limits with ATH_USER_REGD set

SVN-Revision: 27788
---
 .../patches/590-mac80211_cur_txpower.patch    |  32 +++++
 .../591-ath9k_initialize_chainmask.patch      |  13 ++
 .../592-ath9k_remove_tx_indexoffset.patch     | 125 ++++++++++++++++++
 .../patches/593-ath9k_fix_rate_power.patch    |  83 ++++++++++++
 .../patches/594-ath9k_test_txpower.patch      |  25 ++++
 .../patches/595-ath9k_cur_txpower.patch       |  35 +++++
 6 files changed, 313 insertions(+)
 create mode 100644 package/mac80211/patches/590-mac80211_cur_txpower.patch
 create mode 100644 package/mac80211/patches/591-ath9k_initialize_chainmask.patch
 create mode 100644 package/mac80211/patches/592-ath9k_remove_tx_indexoffset.patch
 create mode 100644 package/mac80211/patches/593-ath9k_fix_rate_power.patch
 create mode 100644 package/mac80211/patches/594-ath9k_test_txpower.patch
 create mode 100644 package/mac80211/patches/595-ath9k_cur_txpower.patch

diff --git a/package/mac80211/patches/590-mac80211_cur_txpower.patch b/package/mac80211/patches/590-mac80211_cur_txpower.patch
new file mode 100644
index 0000000000..8e729c1a44
--- /dev/null
+++ b/package/mac80211/patches/590-mac80211_cur_txpower.patch
@@ -0,0 +1,32 @@
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -779,7 +779,7 @@ enum ieee80211_smps_mode {
+  */
+ struct ieee80211_conf {
+ 	u32 flags;
+-	int power_level, dynamic_ps_timeout;
++	int cur_power_level, power_level, dynamic_ps_timeout;
+ 	int max_sleep_period;
+ 
+ 	u16 listen_interval;
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -1524,7 +1524,7 @@ static int ieee80211_get_tx_power(struct
+ {
+ 	struct ieee80211_local *local = wiphy_priv(wiphy);
+ 
+-	*dbm = local->hw.conf.power_level;
++	*dbm = local->hw.conf.cur_power_level;
+ 
+ 	return 0;
+ }
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -210,6 +210,7 @@ int ieee80211_hw_config(struct ieee80211
+ 
+ 	if (local->hw.conf.power_level != power) {
+ 		changed |= IEEE80211_CONF_CHANGE_POWER;
++		local->hw.conf.cur_power_level = power;
+ 		local->hw.conf.power_level = power;
+ 	}
+ 
diff --git a/package/mac80211/patches/591-ath9k_initialize_chainmask.patch b/package/mac80211/patches/591-ath9k_initialize_chainmask.patch
new file mode 100644
index 0000000000..03f7c36a35
--- /dev/null
+++ b/package/mac80211/patches/591-ath9k_initialize_chainmask.patch
@@ -0,0 +1,13 @@
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -667,8 +667,10 @@ static void ath9k_init_band_txpower(stru
+ static void ath9k_init_txpower_limits(struct ath_softc *sc)
+ {
+ 	struct ath_hw *ah = sc->sc_ah;
++	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ 	struct ath9k_channel *curchan = ah->curchan;
+ 
++	ah->txchainmask = common->tx_chainmask;
+ 	if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ)
+ 		ath9k_init_band_txpower(sc, IEEE80211_BAND_2GHZ);
+ 	if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
diff --git a/package/mac80211/patches/592-ath9k_remove_tx_indexoffset.patch b/package/mac80211/patches/592-ath9k_remove_tx_indexoffset.patch
new file mode 100644
index 0000000000..c8e2640eec
--- /dev/null
+++ b/package/mac80211/patches/592-ath9k_remove_tx_indexoffset.patch
@@ -0,0 +1,125 @@
+--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+@@ -247,8 +247,7 @@ static u32 ath9k_hw_4k_get_eeprom(struct
+ }
+ 
+ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah,
+-				  struct ath9k_channel *chan,
+-				  int16_t *pTxPowerIndexOffset)
++				  struct ath9k_channel *chan)
+ {
+ 	struct ath_common *common = ath9k_hw_common(ah);
+ 	struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
+@@ -358,8 +357,6 @@ static void ath9k_hw_set_4k_power_cal_ta
+ 			REGWRITE_BUFFER_FLUSH(ah);
+ 		}
+ 	}
+-
+-	*pTxPowerIndexOffset = 0;
+ }
+ 
+ static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah,
+@@ -582,7 +579,6 @@ static void ath9k_hw_4k_set_txpower(stru
+ 	struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k;
+ 	struct modal_eep_4k_header *pModal = &pEepData->modalHeader;
+ 	int16_t ratesArray[Ar5416RateSize];
+-	int16_t txPowerIndexOffset = 0;
+ 	u8 ht40PowerIncForPdadc = 2;
+ 	int i;
+ 
+@@ -599,11 +595,10 @@ static void ath9k_hw_4k_set_txpower(stru
+ 					     twiceMaxRegulatoryPower,
+ 					     powerLimit);
+ 
+-	ath9k_hw_set_4k_power_cal_table(ah, chan, &txPowerIndexOffset);
++	ath9k_hw_set_4k_power_cal_table(ah, chan);
+ 
+ 	regulatory->max_power_level = 0;
+ 	for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
+-		ratesArray[i] =	(int16_t)(txPowerIndexOffset + ratesArray[i]);
+ 		if (ratesArray[i] > MAX_RATE_POWER)
+ 			ratesArray[i] = MAX_RATE_POWER;
+ 
+--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+@@ -306,8 +306,7 @@ static void ar9287_eeprom_olpc_set_pdadc
+ }
+ 
+ static void ath9k_hw_set_ar9287_power_cal_table(struct ath_hw *ah,
+-						struct ath9k_channel *chan,
+-						int16_t *pTxPowerIndexOffset)
++						struct ath9k_channel *chan)
+ {
+ 	struct cal_data_per_freq_ar9287 *pRawDataset;
+ 	struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop;
+@@ -446,8 +445,6 @@ static void ath9k_hw_set_ar9287_power_ca
+ 			REGWRITE_BUFFER_FLUSH(ah);
+ 		}
+ 	}
+-
+-	*pTxPowerIndexOffset = 0;
+ }
+ 
+ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,
+@@ -722,7 +719,6 @@ static void ath9k_hw_ar9287_set_txpower(
+ 	struct ar9287_eeprom *pEepData = &ah->eeprom.map9287;
+ 	struct modal_eep_ar9287_header *pModal = &pEepData->modalHeader;
+ 	int16_t ratesArray[Ar5416RateSize];
+-	int16_t txPowerIndexOffset = 0;
+ 	u8 ht40PowerIncForPdadc = 2;
+ 	int i;
+ 
+@@ -738,11 +734,10 @@ static void ath9k_hw_ar9287_set_txpower(
+ 						 twiceMaxRegulatoryPower,
+ 						 powerLimit);
+ 
+-	ath9k_hw_set_ar9287_power_cal_table(ah, chan, &txPowerIndexOffset);
++	ath9k_hw_set_ar9287_power_cal_table(ah, chan);
+ 
+ 	regulatory->max_power_level = 0;
+ 	for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
+-		ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]);
+ 		if (ratesArray[i] > MAX_RATE_POWER)
+ 			ratesArray[i] = MAX_RATE_POWER;
+ 
+--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
+@@ -692,8 +692,7 @@ static void ath9k_adjust_pdadc_values(st
+ }
+ 
+ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
+-				  struct ath9k_channel *chan,
+-				  int16_t *pTxPowerIndexOffset)
++				  struct ath9k_channel *chan)
+ {
+ #define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x)
+ #define SM_PDGAIN_B(x, y) \
+@@ -857,7 +856,6 @@ static void ath9k_hw_set_def_power_cal_t
+ 		}
+ 	}
+ 
+-	*pTxPowerIndexOffset = 0;
+ #undef SM_PD_GAIN
+ #undef SM_PDGAIN_B
+ }
+@@ -1145,7 +1143,6 @@ static void ath9k_hw_def_set_txpower(str
+ 	struct modal_eep_header *pModal =
+ 		&(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]);
+ 	int16_t ratesArray[Ar5416RateSize];
+-	int16_t txPowerIndexOffset = 0;
+ 	u8 ht40PowerIncForPdadc = 2;
+ 	int i, cck_ofdm_delta = 0;
+ 
+@@ -1162,11 +1159,10 @@ static void ath9k_hw_def_set_txpower(str
+ 					       twiceMaxRegulatoryPower,
+ 					       powerLimit);
+ 
+-	ath9k_hw_set_def_power_cal_table(ah, chan, &txPowerIndexOffset);
++	ath9k_hw_set_def_power_cal_table(ah, chan);
+ 
+ 	regulatory->max_power_level = 0;
+ 	for (i = 0; i < ARRAY_SIZE(ratesArray); i++) {
+-		ratesArray[i] =	(int16_t)(txPowerIndexOffset + ratesArray[i]);
+ 		if (ratesArray[i] > MAX_RATE_POWER)
+ 			ratesArray[i] = MAX_RATE_POWER;
+ 		if (ratesArray[i] > regulatory->max_power_level)
diff --git a/package/mac80211/patches/593-ath9k_fix_rate_power.patch b/package/mac80211/patches/593-ath9k_fix_rate_power.patch
new file mode 100644
index 0000000000..fc79d5f9e2
--- /dev/null
+++ b/package/mac80211/patches/593-ath9k_fix_rate_power.patch
@@ -0,0 +1,83 @@
+--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+@@ -4922,25 +4922,7 @@ static void ath9k_hw_ar9300_set_txpower(
+ 			"TPC[%02d] 0x%08x\n", i, targetPowerValT2[i]);
+ 	}
+ 
+-	/*
+-	 * This is the TX power we send back to driver core,
+-	 * and it can use to pass to userspace to display our
+-	 * currently configured TX power setting.
+-	 *
+-	 * Since power is rate dependent, use one of the indices
+-	 * from the AR9300_Rates enum to select an entry from
+-	 * targetPowerValT2[] to report. Currently returns the
+-	 * power for HT40 MCS 0, HT20 MCS 0, or OFDM 6 Mbps
+-	 * as CCK power is less interesting (?).
+-	 */
+-	i = ALL_TARGET_LEGACY_6_24; /* legacy */
+-	if (IS_CHAN_HT40(chan))
+-		i = ALL_TARGET_HT40_0_8_16; /* ht40 */
+-	else if (IS_CHAN_HT20(chan))
+-		i = ALL_TARGET_HT20_0_8_16; /* ht20 */
+-
+-	ah->txpower_limit = targetPowerValT2[i];
+-	regulatory->max_power_level = targetPowerValT2[i];
++	ah->txpower_limit = regulatory->max_power_level;
+ 
+ 	/* Write target power array to registers */
+ 	ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
+--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+@@ -609,15 +609,6 @@ static void ath9k_hw_4k_set_txpower(stru
+ 	if (test)
+ 	    return;
+ 
+-	/* Update regulatory */
+-	i = rate6mb;
+-	if (IS_CHAN_HT40(chan))
+-		i = rateHt40_0;
+-	else if (IS_CHAN_HT20(chan))
+-		i = rateHt20_0;
+-
+-	regulatory->max_power_level = ratesArray[i];
+-
+ 	if (AR_SREV_9280_20_OR_LATER(ah)) {
+ 		for (i = 0; i < Ar5416RateSize; i++)
+ 			ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
+--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+@@ -748,13 +748,6 @@ static void ath9k_hw_ar9287_set_txpower(
+ 	if (test)
+ 		return;
+ 
+-	if (IS_CHAN_2GHZ(chan))
+-		i = rate1l;
+-	else
+-		i = rate6mb;
+-
+-	regulatory->max_power_level = ratesArray[i];
+-
+ 	if (AR_SREV_9280_20_OR_LATER(ah)) {
+ 		for (i = 0; i < Ar5416RateSize; i++)
+ 			ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2;
+--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
+@@ -1169,17 +1169,6 @@ static void ath9k_hw_def_set_txpower(str
+ 			regulatory->max_power_level = ratesArray[i];
+ 	}
+ 
+-	if (!test) {
+-		i = rate6mb;
+-
+-		if (IS_CHAN_HT40(chan))
+-			i = rateHt40_0;
+-		else if (IS_CHAN_HT20(chan))
+-			i = rateHt20_0;
+-
+-		regulatory->max_power_level = ratesArray[i];
+-	}
+-
+ 	switch(ar5416_get_ntxchains(ah->txchainmask)) {
+ 	case 1:
+ 		break;
diff --git a/package/mac80211/patches/594-ath9k_test_txpower.patch b/package/mac80211/patches/594-ath9k_test_txpower.patch
new file mode 100644
index 0000000000..45a51be61e
--- /dev/null
+++ b/package/mac80211/patches/594-ath9k_test_txpower.patch
@@ -0,0 +1,25 @@
+--- a/drivers/net/wireless/ath/ath9k/hw.c
++++ b/drivers/net/wireless/ath/ath9k/hw.c
+@@ -2442,15 +2442,18 @@ void ath9k_hw_set_txpowerlimit(struct at
+ 	struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
+ 	struct ath9k_channel *chan = ah->curchan;
+ 	struct ieee80211_channel *channel = chan->chan;
++	int reg_pwr = min_t(int, MAX_RATE_POWER, regulatory->power_limit);
++	int chan_pwr = channel->max_power * 2;
++
++	if (test)
++		reg_pwr = chan_pwr = MAX_RATE_POWER;
+ 
+ 	regulatory->power_limit = min(limit, (u32) MAX_RATE_POWER);
+ 
+ 	ah->eep_ops->set_txpower(ah, chan,
+ 				 ath9k_regd_get_ctl(regulatory, chan),
+ 				 channel->max_antenna_gain * 2,
+-				 channel->max_power * 2,
+-				 min((u32) MAX_RATE_POWER,
+-				 (u32) regulatory->power_limit), test);
++				 chan_pwr, reg_pwr, test);
+ }
+ EXPORT_SYMBOL(ath9k_hw_set_txpowerlimit);
+ 
+
diff --git a/package/mac80211/patches/595-ath9k_cur_txpower.patch b/package/mac80211/patches/595-ath9k_cur_txpower.patch
new file mode 100644
index 0000000000..29ed81aba3
--- /dev/null
+++ b/package/mac80211/patches/595-ath9k_cur_txpower.patch
@@ -0,0 +1,35 @@
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -1724,6 +1724,8 @@ static int ath9k_config(struct ieee80211
+ 			return -EINVAL;
+ 		}
+ 
++		conf->cur_power_level = sc->curtxpow / 2;
++
+ 		/*
+ 		 * The most recent snapshot of channel->noisefloor for the old
+ 		 * channel is only available after the hardware reset. Copy it to
+@@ -1741,6 +1743,7 @@ static int ath9k_config(struct ieee80211
+ 		ath9k_cmn_update_txpow(ah, sc->curtxpow,
+ 				       sc->config.txpowlimit, &sc->curtxpow);
+ 		ath9k_ps_restore(sc);
++		conf->cur_power_level = sc->curtxpow / 2;
+ 	}
+ 
+ 	if (disable_radio) {
+--- a/drivers/net/wireless/ath/ath9k/common.c
++++ b/drivers/net/wireless/ath/ath9k/common.c
+@@ -161,10 +161,12 @@ EXPORT_SYMBOL(ath9k_cmn_count_streams);
+ void ath9k_cmn_update_txpow(struct ath_hw *ah, u16 cur_txpow,
+ 			    u16 new_txpow, u16 *txpower)
+ {
++	struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
++
+ 	if (cur_txpow != new_txpow) {
+ 		ath9k_hw_set_txpowerlimit(ah, new_txpow, false);
+ 		/* read back in case value is clamped */
+-		*txpower = ath9k_hw_regulatory(ah)->power_limit;
++		*txpower = min_t(u16, reg->power_limit, reg->max_power_level);
+ 	}
+ }
+ EXPORT_SYMBOL(ath9k_cmn_update_txpow);
-- 
2.30.2