2014-11-20 13:31:44

by Lorenzo Bianconi

[permalink] [raw]
Subject: [RFC 0/3] add TPC capability for AR9003 based chips

This patchset adds TPC capability to ath9k for AR9003 based chips. For the time
being some FCC checks are missing in ar9003_hw_init_txpower_stbc() and CDD mode
is not supported.
*[RFC 1/3]: add chainmask parameter to ath9k_hw_get_scaled_power() to compute
maximum TX power for different number of TX chains
*[RFC 2/3]: add TX power per-rate per-chain tables to cap TX power in TX
descriptor path
*[RFC 3/3]: cap per-packet TX power according to TX power per-rate per-chain
tables
This pachset is based on Adrian Chadd's hints
(https://www.mail-archive.com/[email protected]/msg10396.html)

Lorenzo Bianconi (3):
ath9k: add chainmask parameter to ath9k_hw_get_scaled_power()
ath9k: add TX power per-rate per-chain tables
ath9k: add TPC capability to TX descriptor path

drivers/net/wireless/ath/ath9k/ar9002_mac.c | 8 +-
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 68 +++++-
drivers/net/wireless/ath/ath9k/ar9003_eeprom.h | 5 +
drivers/net/wireless/ath/ath9k/ar9003_mac.c | 8 +-
drivers/net/wireless/ath/ath9k/ar9003_phy.c | 281 +++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/ath9k.h | 1 +
drivers/net/wireless/ath/ath9k/beacon.c | 5 +-
drivers/net/wireless/ath/ath9k/eeprom.c | 4 +-
drivers/net/wireless/ath/ath9k/eeprom.h | 2 +-
drivers/net/wireless/ath/ath9k/eeprom_9287.c | 3 +-
drivers/net/wireless/ath/ath9k/eeprom_def.c | 3 +-
drivers/net/wireless/ath/ath9k/hw.h | 6 +
drivers/net/wireless/ath/ath9k/mac.h | 2 +-
drivers/net/wireless/ath/ath9k/reg.h | 2 +
drivers/net/wireless/ath/ath9k/xmit.c | 41 +++-
15 files changed, 419 insertions(+), 20 deletions(-)

--
2.1.0



2014-11-21 13:14:00

by Lorenzo Bianconi

[permalink] [raw]
Subject: Re: [RFC 2/3] ath9k: add TX power per-rate per-chain tables

> On 2014-11-20 14:31, Lorenzo Bianconi wrote:
>> Add TX power per-rate per-chain tables for different MIMO modes (e.g STBC) in
>> order to cap the maximum TX power value per-rate in the TX descriptor path.
>> Cap TX power for self generated frames (ACK, RTS/CTS).
>> Currently TPC is supported just by AR9003 based chips
>>
>> Signed-off-by: Lorenzo Bianconi <[email protected]>
> I think you should make the ah->txpower array one-dimensional and store
> it for the current chainmask only - this will simplify the code.
> The chainmask cannot be changed while the interface is up, so there's no
> need to make this dynamically switchable.
>
> - Felix

Ack. In this way I can drop patch 1/3 and simplify the code. Thx :)

Lorenzo

--
UNIX is Sexy: who | grep -i blonde | talk; cd ~; wine; talk; touch;
unzip; touch; strip; gasp; finger; gasp; mount; fsck; more; yes; gasp;
umount; make clean; sleep

2014-11-20 13:31:47

by Lorenzo Bianconi

[permalink] [raw]
Subject: [RFC 3/3] ath9k: add TPC capability to TX descriptor path

Add TPC capability to TX descriptor path. Cap per-packet TX power according to
TX power per-rate per-chain tables. Currently TPC is supported just by AR9003
based chips

Signed-off-by: Lorenzo Bianconi <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_mac.c | 8 +++---
drivers/net/wireless/ath/ath9k/ar9003_mac.c | 8 +++---
drivers/net/wireless/ath/ath9k/ath9k.h | 1 +
drivers/net/wireless/ath/ath9k/beacon.c | 5 ++--
drivers/net/wireless/ath/ath9k/mac.h | 2 +-
drivers/net/wireless/ath/ath9k/xmit.c | 41 ++++++++++++++++++++++++++++-
6 files changed, 53 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index 2a93519..f816909 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -281,7 +281,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)

ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(i->txpower, AR_XmitPower0)
+ | SM(i->txpower[0], AR_XmitPower0)
| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
| (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
@@ -307,9 +307,9 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)
| set11nRateFlags(i->rates, 3)
| SM(i->rtscts_rate, AR_RTSCTSRate);

- ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1);
- ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2);
- ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3);
+ ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower[1], AR_XmitPower1);
+ ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower[2], AR_XmitPower2);
+ ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower[3], AR_XmitPower3);
}

static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 057b165..da84b70 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -101,7 +101,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)

ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(i->txpower, AR_XmitPower0)
+ | SM(i->txpower[0], AR_XmitPower0)
| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
| (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
@@ -152,9 +152,9 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i)

ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;

- ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1);
- ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2);
- ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3);
+ ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1);
+ ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2);
+ ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3);
}

static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index abe8bd6..1a9fe09 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -189,6 +189,7 @@ struct ath_frame_info {
u8 rtscts_rate;
u8 retries : 7;
u8 baw_tracked : 1;
+ u8 tx_power;
};

struct ath_rxbuf {
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index ecb783b..cb366ad 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -78,7 +78,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
struct ath_tx_info info;
struct ieee80211_supported_band *sband;
u8 chainmask = ah->txchainmask;
- u8 rate = 0;
+ u8 i, rate = 0;

sband = &common->sbands[sc->cur_chandef.chan->band];
rate = sband->bitrates[rateidx].hw_value;
@@ -88,7 +88,8 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
memset(&info, 0, sizeof(info));
info.pkt_len = skb->len + FCS_LEN;
info.type = ATH9K_PKT_TYPE_BEACON;
- info.txpower = MAX_RATE_POWER;
+ for (i = 0; i < 4; i++)
+ info.txpower[i] = MAX_RATE_POWER;
info.keyix = ATH9K_TXKEYIX_INVALID;
info.keytype = ATH9K_KEY_TYPE_CLEAR;
info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index aa69cea..e55fa11 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -704,7 +704,7 @@ struct ath_tx_info {
enum ath9k_pkt_type type;
enum ath9k_key_type keytype;
u8 keyix;
- u8 txpower;
+ u8 txpower[4];
};

struct ath_hw;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index d6e54a3..f0e18d9 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1096,6 +1096,39 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop)
}
}

+static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
+ u8 rateidx, u8 chainmask)
+{
+ u8 max_power;
+ struct ath_hw *ah = sc->sc_ah;
+
+ if (sc->tx99_state)
+ return MAX_RATE_POWER;
+
+ if (!AR_SREV_9300_20_OR_LATER(ah)) {
+ /* ar9002 is not sipported for the moment */
+ return MAX_RATE_POWER;
+ }
+
+ if (!bf->bf_state.bfs_paprd) {
+ struct sk_buff *skb = bf->bf_mpdu;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ath_frame_info *fi = get_frame_info(skb);
+ u8 nchain = ar5416_get_ntxchains(chainmask);
+
+ if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
+ max_power = min(ah->tx_power_stbc[rateidx][nchain - 1],
+ fi->tx_power);
+ else
+ max_power = min(ah->tx_power[rateidx][nchain - 1],
+ fi->tx_power);
+ } else {
+ max_power = ah->paprd_training_power;
+ }
+
+ return max_power;
+}
+
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_info *info, int len, bool rts)
{
@@ -1166,6 +1199,9 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
is_40, is_sgi, is_sp);
if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
+
+ info->txpower[i] = ath_get_rate_txpower(sc, bf, rix,
+ info->rates[i].ChSel);
continue;
}

@@ -1193,6 +1229,9 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,

info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
phy, rate->bitrate * 100, len, rix, is_sp);
+
+ info->txpower[i] = ath_get_rate_txpower(sc, bf, rix,
+ info->rates[i].ChSel);
}

/* For AR5416 - RTS cannot be followed by a frame larger than 8K */
@@ -1239,7 +1278,6 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
memset(&info, 0, sizeof(info));
info.is_first = true;
info.is_last = true;
- info.txpower = MAX_RATE_POWER;
info.qcu = txq->axq_qnum;

while (bf) {
@@ -2063,6 +2101,7 @@ static void setup_frame_info(struct ieee80211_hw *hw,
fi->keyix = ATH9K_TXKEYIX_INVALID;
fi->keytype = keytype;
fi->framelen = framelen;
+ fi->tx_power = MAX_RATE_POWER;

if (!rate)
return;
--
2.1.0


2014-11-20 13:31:45

by Lorenzo Bianconi

[permalink] [raw]
Subject: [RFC 1/3] ath9k: add chainmask parameter to ath9k_hw_get_scaled_power()

Add chainmask parameter to ath9k_hw_get_scaled_power() in order to compute
maximum TX power per rate for different number of tx chains.
ath9k_hw_get_scaled_power() will be used during TPC TX power initialization

Signed-off-by: Lorenzo Bianconi <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 3 ++-
drivers/net/wireless/ath/ath9k/eeprom.c | 4 ++--
drivers/net/wireless/ath/ath9k/eeprom.h | 2 +-
drivers/net/wireless/ath/ath9k/eeprom_9287.c | 3 ++-
drivers/net/wireless/ath/ath9k/eeprom_def.c | 3 ++-
5 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index e726e40..96d7538 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -5114,7 +5114,8 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah,

ath9k_hw_get_channel_centers(ah, chan, &centers);
scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit,
- antenna_reduction);
+ antenna_reduction,
+ ah->txchainmask);

if (is2ghz) {
/* Setup for CTL modes */
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index 971d770..9304dca 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -308,7 +308,7 @@ u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower,
}

u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit,
- u8 antenna_reduction)
+ u8 antenna_reduction, u8 chainmask)
{
u16 reduction = antenna_reduction;

@@ -316,7 +316,7 @@ u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit,
* Reduce scaled Power by number of chains active
* to get the per chain tx power level.
*/
- switch (ar5416_get_ntxchains(ah->txchainmask)) {
+ switch (ar5416_get_ntxchains(chainmask)) {
case 1:
break;
case 2:
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h
index 40d4f62..f83bc0f 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/eeprom.h
@@ -684,7 +684,7 @@ void ath9k_hw_get_target_powers(struct ath_hw *ah,
u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower,
bool is2GHz, int num_band_edges);
u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit,
- u8 antenna_reduction);
+ u8 antenna_reduction, u8 chainmask);
void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah);
int ath9k_hw_eeprom_init(struct ath_hw *ah);

diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
index 5ba1385..beb5b6b 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
@@ -584,7 +584,8 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah,

ath9k_hw_get_channel_centers(ah, chan, &centers);
scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit,
- antenna_reduction);
+ antenna_reduction,
+ ah->txchainmask);

/*
* Get TX power from EEPROM.
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 122b846..8de2874 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -1009,7 +1009,8 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah,
ath9k_hw_get_channel_centers(ah, chan, &centers);

scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit,
- antenna_reduction);
+ antenna_reduction,
+ ah->txchainmask);

if (IS_CHAN_2GHZ(chan)) {
numCtlModes = ARRAY_SIZE(ctlModesFor11g) -
--
2.1.0


2014-11-20 14:50:00

by Felix Fietkau

[permalink] [raw]
Subject: Re: [RFC 2/3] ath9k: add TX power per-rate per-chain tables

On 2014-11-20 14:31, Lorenzo Bianconi wrote:
> Add TX power per-rate per-chain tables for different MIMO modes (e.g STBC) in
> order to cap the maximum TX power value per-rate in the TX descriptor path.
> Cap TX power for self generated frames (ACK, RTS/CTS).
> Currently TPC is supported just by AR9003 based chips
>
> Signed-off-by: Lorenzo Bianconi <[email protected]>
I think you should make the ah->txpower array one-dimensional and store
it for the current chainmask only - this will simplify the code.
The chainmask cannot be changed while the interface is up, so there's no
need to make this dynamically switchable.

- Felix

2014-11-20 13:31:46

by Lorenzo Bianconi

[permalink] [raw]
Subject: [RFC 2/3] ath9k: add TX power per-rate per-chain tables

Add TX power per-rate per-chain tables for different MIMO modes (e.g STBC) in
order to cap the maximum TX power value per-rate in the TX descriptor path.
Cap TX power for self generated frames (ACK, RTS/CTS).
Currently TPC is supported just by AR9003 based chips

Signed-off-by: Lorenzo Bianconi <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 67 +++++-
drivers/net/wireless/ath/ath9k/ar9003_eeprom.h | 5 +
drivers/net/wireless/ath/ath9k/ar9003_phy.c | 281 +++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/hw.h | 6 +
drivers/net/wireless/ath/ath9k/reg.h | 2 +
5 files changed, 358 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 96d7538..8acabcd 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -4377,6 +4377,25 @@ static u8 ar9003_hw_eeprom_get_cck_tgt_pwr(struct ath_hw *ah,
targetPowerArray, numPiers);
}

+static void ar9003_hw_selfgen_tpc_txpower(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 *pwr_array)
+{
+ u32 val;
+
+ /* target power values for self generated frames (ACK,RTS/CTS) */
+ if (IS_CHAN_2GHZ(chan)) {
+ val = SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_ACK) |
+ SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_CTS) |
+ SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+ } else {
+ val = SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_ACK) |
+ SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_CTS) |
+ SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+ }
+ REG_WRITE(ah, AR_TPC, val);
+}
+
/* Set tx power registers to array of values passed in */
static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
{
@@ -5089,7 +5108,7 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah,
struct ath9k_channel *chan,
u8 *pPwrArray, u16 cfgCtl,
u8 antenna_reduction,
- u16 powerLimit)
+ u16 powerLimit, u8 chainmask)
{
struct ath_common *common = ath9k_hw_common(ah);
struct ar9300_eeprom *pEepData = &ah->eeprom.ar9300_eep;
@@ -5115,7 +5134,7 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah,
ath9k_hw_get_channel_centers(ah, chan, &centers);
scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit,
antenna_reduction,
- ah->txchainmask);
+ chainmask);

if (is2ghz) {
/* Setup for CTL modes */
@@ -5313,6 +5332,7 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
struct ar9300_modal_eep_header *modal_hdr;
u8 targetPowerValT2[ar9300RateSize];
u8 target_power_val_t2_eep[ar9300RateSize];
+ u8 targetPowerValT2_tpc[ar9300RateSize];
unsigned int i = 0, paprd_scale_factor = 0;
u8 pwr_idx, min_pwridx = 0;

@@ -5359,10 +5379,13 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
sizeof(targetPowerValT2));
}

+ memcpy(targetPowerValT2_tpc, targetPowerValT2,
+ sizeof(targetPowerValT2));
+
ar9003_hw_set_power_per_rate_table(ah, chan,
targetPowerValT2, cfgCtl,
twiceAntennaReduction,
- powerLimit);
+ powerLimit, ah->txchainmask);

if (ar9003_is_paprd_enabled(ah)) {
for (i = 0; i < ar9300RateSize; i++) {
@@ -5397,6 +5420,44 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
ar9003_hw_calibration_apply(ah, chan->channel);
ar9003_paprd_set_txpower(ah, chan, targetPowerValT2);
+
+ ar9003_hw_selfgen_tpc_txpower(ah, chan, targetPowerValT2);
+
+ /* TPC initializations */
+ if (ah->tpc_enabled) {
+ u32 val;
+ u8 chainmask[] = {
+ AR9300_1_CHAINMASK,
+ AR9300_2LOHI_CHAINMASK,
+ AR9300_3_CHAINMASK
+ };
+ for (i = 0; i < AR9300_MAX_CHAINS; i++) {
+ memcpy(targetPowerValT2, targetPowerValT2_tpc,
+ sizeof(targetPowerValT2));
+ ar9003_hw_set_power_per_rate_table(ah, chan,
+ targetPowerValT2,
+ cfgCtl,
+ twiceAntennaReduction,
+ powerLimit,
+ chainmask[i]);
+ ar9003_hw_init_rate_txpower(ah, targetPowerValT2,
+ chan, chainmask[i]);
+ }
+ /* Enable TPC */
+ REG_WRITE(ah, AR_PHY_PWRTX_MAX,
+ AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+ /* Disable per chain power reduction */
+ val = REG_READ(ah, AR_PHY_POWER_TX_SUB);
+ if (AR_SREV_9340(ah))
+ REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+ val & 0xFFFFFFC0);
+ else
+ REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+ val & 0xFFFFF000);
+ } else {
+ /* Disable TPC */
+ REG_WRITE(ah, AR_PHY_PWRTX_MAX, 0);
+ }
}

static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
index 694ca2e..02bcc85 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
@@ -46,6 +46,11 @@
#define AR9300_ANT_16S 25
#define AR9300_FUTURE_MODAL_SZ 6

+#define AR9300_1_CHAINMASK 1
+#define AR9300_2LOMID_CHAINMASK 3
+#define AR9300_2LOHI_CHAINMASK 5
+#define AR9300_3_CHAINMASK 7
+
#define AR9300_PAPRD_RATE_MASK 0x01ffffff
#define AR9300_PAPRD_SCALE_1 0x0e000000
#define AR9300_PAPRD_SCALE_1_S 25
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 2df6d2e..9f1445e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -18,6 +18,21 @@
#include "hw.h"
#include "ar9003_phy.h"

+#define AR9300_OFDM_RATES 8
+#define AR9300_HT_SS_RATES 8
+#define AR9300_HT_DS_RATES 8
+#define AR9300_HT_TS_RATES 8
+
+#define AR9300_11NA_OFDM_SHIFT 0
+#define AR9300_11NA_HT_SS_SHIFT 8
+#define AR9300_11NA_HT_DS_SHIFT 16
+#define AR9300_11NA_HT_TS_SHIFT 24
+
+#define AR9300_11NG_OFDM_SHIFT 4
+#define AR9300_11NG_HT_SS_SHIFT 12
+#define AR9300_11NG_HT_DS_SHIFT 20
+#define AR9300_11NG_HT_TS_SHIFT 28
+
static const int firstep_table[] =
/* level: 0 1 2 3 4 5 6 7 8 */
{ -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */
@@ -40,6 +55,71 @@ static const int m2ThreshLowExt_off = 127;
static const int m1ThreshExt_off = 127;
static const int m2ThreshExt_off = 127;

+static const u8 ofdm2pwr[] = {
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_36,
+ ALL_TARGET_LEGACY_48,
+ ALL_TARGET_LEGACY_54
+};
+
+static const u8 mcs2pwr_ht20[] = {
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_4,
+ ALL_TARGET_HT20_5,
+ ALL_TARGET_HT20_6,
+ ALL_TARGET_HT20_7,
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_12,
+ ALL_TARGET_HT20_13,
+ ALL_TARGET_HT20_14,
+ ALL_TARGET_HT20_15,
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_20,
+ ALL_TARGET_HT20_21,
+ ALL_TARGET_HT20_22,
+ ALL_TARGET_HT20_23
+};
+
+static const u8 mcs2pwr_ht40[] = {
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_4,
+ ALL_TARGET_HT40_5,
+ ALL_TARGET_HT40_6,
+ ALL_TARGET_HT40_7,
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_12,
+ ALL_TARGET_HT40_13,
+ ALL_TARGET_HT40_14,
+ ALL_TARGET_HT40_15,
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_20,
+ ALL_TARGET_HT40_21,
+ ALL_TARGET_HT40_22,
+ ALL_TARGET_HT40_23,
+};
+
/**
* ar9003_hw_set_channel - set channel on single-chip device
* @ah: atheros hardware structure
@@ -1799,6 +1879,207 @@ static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower)
ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0));
}

+static void ar9003_hw_init_txpower_cck(struct ath_hw *ah, u8 *rate_array)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ah->tx_power[0][i] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+ ah->tx_power[1][i] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+ ah->tx_power[2][i] = min(rate_array[ALL_TARGET_LEGACY_1L_5L],
+ rate_array[ALL_TARGET_LEGACY_5S]);
+ ah->tx_power[3][i] = min(rate_array[ALL_TARGET_LEGACY_11L],
+ rate_array[ALL_TARGET_LEGACY_11S]);
+ }
+}
+
+static void ar9003_hw_init_txpower_ofdm(struct ath_hw *ah, u8 *rate_array,
+ int offset, u8 chainmask)
+{
+ int i, j;
+
+ for (i = offset; i < offset + AR9300_OFDM_RATES; i++) {
+ /* OFDM rate to power table idx */
+ j = ofdm2pwr[i - offset];
+ switch (chainmask) {
+ case AR9300_1_CHAINMASK:
+ ah->tx_power[i][0] = rate_array[j];
+ break;
+ case AR9300_2LOMID_CHAINMASK:
+ case AR9300_2LOHI_CHAINMASK:
+ ah->tx_power[i][1] = rate_array[j];
+ break;
+ case AR9300_3_CHAINMASK:
+ ah->tx_power[i][2] = rate_array[j];
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void ar9003_hw_init_txpower_ht(struct ath_hw *ah, u8 *rate_array,
+ int ss_offset, int ds_offset,
+ int ts_offset, u8 chainmask, bool is_40)
+{
+ int i, j, mcs_idx = 0;
+ const u8 *mcs2pwr = (is_40) ? mcs2pwr_ht40 : mcs2pwr_ht20;
+
+ for (i = ss_offset; i < ss_offset + AR9300_HT_SS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ switch (chainmask) {
+ case AR9300_1_CHAINMASK:
+ ah->tx_power[i][0] = rate_array[j];
+ break;
+ case AR9300_2LOMID_CHAINMASK:
+ case AR9300_2LOHI_CHAINMASK:
+ ah->tx_power[i][1] = rate_array[j];
+ break;
+ case AR9300_3_CHAINMASK:
+ ah->tx_power[i][2] = rate_array[j];
+ break;
+ default:
+ break;
+ }
+ mcs_idx++;
+ }
+
+ for (i = ds_offset; i < ds_offset + AR9300_HT_DS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ switch (chainmask) {
+ case AR9300_1_CHAINMASK:
+ ah->tx_power[i][0] = rate_array[j];
+ break;
+ case AR9300_2LOMID_CHAINMASK:
+ case AR9300_2LOHI_CHAINMASK:
+ ah->tx_power[i][1] = rate_array[j];
+ break;
+ case AR9300_3_CHAINMASK:
+ ah->tx_power[i][2] = rate_array[j];
+ break;
+ default:
+ break;
+ }
+ mcs_idx++;
+ }
+
+ for (i = ts_offset; i < ts_offset + AR9300_HT_TS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ switch (chainmask) {
+ case AR9300_1_CHAINMASK:
+ ah->tx_power[i][0] = rate_array[j];
+ break;
+ case AR9300_2LOMID_CHAINMASK:
+ case AR9300_2LOHI_CHAINMASK:
+ ah->tx_power[i][1] = rate_array[j];
+ break;
+ case AR9300_3_CHAINMASK:
+ ah->tx_power[i][2] = rate_array[j];
+ break;
+ default:
+ break;
+ }
+ mcs_idx++;
+ }
+}
+
+static void ar9003_hw_init_txpower_stbc(struct ath_hw *ah, u8 chainmask,
+ int ss_offset, int ds_offset,
+ int ts_offset)
+{
+ int i;
+
+ for (i = ss_offset; i < ss_offset + AR9300_HT_SS_RATES; i++) {
+ switch (chainmask) {
+ case AR9300_1_CHAINMASK:
+ ah->tx_power_stbc[i][0] = ah->tx_power[i][0];
+ break;
+ case AR9300_2LOMID_CHAINMASK:
+ case AR9300_2LOHI_CHAINMASK:
+ ah->tx_power_stbc[i][1] = ah->tx_power[i][1];
+ break;
+ case AR9300_3_CHAINMASK:
+ ah->tx_power_stbc[i][2] = ah->tx_power[i][2];
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = ds_offset; i < ds_offset + AR9300_HT_DS_RATES; i++) {
+ switch (chainmask) {
+ case AR9300_1_CHAINMASK:
+ ah->tx_power_stbc[i][0] = ah->tx_power[i][0];
+ break;
+ case AR9300_2LOMID_CHAINMASK:
+ case AR9300_2LOHI_CHAINMASK:
+ ah->tx_power_stbc[i][1] = ah->tx_power[i][1];
+ break;
+ case AR9300_3_CHAINMASK:
+ ah->tx_power_stbc[i][2] = ah->tx_power[i][2];
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = ts_offset; i < ts_offset + AR9300_HT_TS_RATES; i++) {
+ switch (chainmask) {
+ case AR9300_1_CHAINMASK:
+ ah->tx_power_stbc[i][0] = ah->tx_power[i][0];
+ break;
+ case AR9300_2LOMID_CHAINMASK:
+ case AR9300_2LOHI_CHAINMASK:
+ ah->tx_power_stbc[i][1] = ah->tx_power[i][1];
+ break;
+ case AR9300_3_CHAINMASK:
+ ah->tx_power_stbc[i][2] = ah->tx_power[i][2];
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ struct ath9k_channel *chan, u8 chainmask)
+{
+ if (IS_CHAN_5GHZ(chan)) {
+ ar9003_hw_init_txpower_ofdm(ah, rate_array,
+ AR9300_11NA_OFDM_SHIFT,
+ chainmask);
+ if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+ ar9003_hw_init_txpower_ht(ah, rate_array,
+ AR9300_11NA_HT_SS_SHIFT,
+ AR9300_11NA_HT_DS_SHIFT,
+ AR9300_11NA_HT_TS_SHIFT,
+ IS_CHAN_HT40(chan),
+ chainmask);
+ ar9003_hw_init_txpower_stbc(ah, chainmask,
+ AR9300_11NA_HT_SS_SHIFT,
+ AR9300_11NA_HT_DS_SHIFT,
+ AR9300_11NA_HT_TS_SHIFT);
+ }
+ } else {
+ ar9003_hw_init_txpower_cck(ah, rate_array);
+ ar9003_hw_init_txpower_ofdm(ah, rate_array,
+ AR9300_11NG_OFDM_SHIFT,
+ chainmask);
+ if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+ ar9003_hw_init_txpower_ht(ah, rate_array,
+ AR9300_11NG_HT_SS_SHIFT,
+ AR9300_11NG_HT_DS_SHIFT,
+ AR9300_11NG_HT_TS_SHIFT,
+ IS_CHAN_HT40(chan),
+ chainmask);
+ ar9003_hw_init_txpower_stbc(ah, chainmask,
+ AR9300_11NG_HT_SS_SHIFT,
+ AR9300_11NG_HT_DS_SHIFT,
+ AR9300_11NG_HT_TS_SHIFT);
+ }
+ }
+}
+
void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 4cf9e0a..3fcfdaf 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -940,6 +940,10 @@ struct ath_hw {
const struct firmware *eeprom_blob;

struct ath_dynack dynack;
+
+ bool tpc_enabled;
+ u8 tx_power[Ar5416RateSize][AR5416_MAX_CHAINS];
+ u8 tx_power_stbc[Ar5416RateSize][AR5416_MAX_CHAINS];
};

struct ath_bus_ops {
@@ -1080,6 +1084,8 @@ int ar9003_paprd_init_table(struct ath_hw *ah);
bool ar9003_paprd_is_done(struct ath_hw *ah);
bool ar9003_is_paprd_enabled(struct ath_hw *ah);
void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ struct ath9k_channel *chan, u8 chainmask);

/* Hardware family op attach helpers */
int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index ced36b4..fb11a91 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -1724,6 +1724,8 @@ enum {
#define AR_TPC_CTS_S 8
#define AR_TPC_CHIRP 0x003f0000
#define AR_TPC_CHIRP_S 16
+#define AR_TPC_RPT 0x3f000000
+#define AR_TPC_RPT_S 24

#define AR_QUIET1 0x80fc
#define AR_QUIET1_NEXT_QUIET_S 0
--
2.1.0