aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Buesch <mb@bu3sch.de>2008-08-28 13:33:40 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-08-29 16:24:13 -0400
commit18c8adeb0244f823ba78a51e23f813fe68bc3c54 (patch)
tree1b294669d04c207d8e90b5807780fe4eb81778d9
parentef1a628d83fc0423c36e773281162be790503168 (diff)
b43: Rewrite TX power adjustment
This patch rewrites the TX power recalculation algorithms to scale better with changed enviromnent. If there's low TX traffic, the power will be checked against the desired values every 60 seconds. If there is high TX traffic, the check is redone every 2 seconds. This improves the reaction times a lot and confuses the rate control less. It will also reduce the time it initially takes to tune to a new TX power value. With the old algorithm it could take about 30 to 45 seconds to settle to a new power value. This will happen in about two to four seconds now. Signed-off-by: Michael Buesch <mb@bu3sch.de> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/b43/b43.h10
-rw-r--r--drivers/net/wireless/b43/main.c18
-rw-r--r--drivers/net/wireless/b43/nphy.c15
-rw-r--r--drivers/net/wireless/b43/phy_a.c11
-rw-r--r--drivers/net/wireless/b43/phy_common.c91
-rw-r--r--drivers/net/wireless/b43/phy_common.h75
-rw-r--r--drivers/net/wireless/b43/phy_g.c218
-rw-r--r--drivers/net/wireless/b43/phy_g.h22
-rw-r--r--drivers/net/wireless/b43/xmit.c2
9 files changed, 341 insertions, 121 deletions
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index fc280157596c..f9c8161671d9 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -173,6 +173,11 @@ enum {
173#define B43_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */ 173#define B43_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */
174#define B43_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5Ghz channel */ 174#define B43_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5Ghz channel */
175#define B43_SHM_SH_BCMCFIFOID 0x0108 /* Last posted cookie to the bcast/mcast FIFO */ 175#define B43_SHM_SH_BCMCFIFOID 0x0108 /* Last posted cookie to the bcast/mcast FIFO */
176/* TSSI information */
177#define B43_SHM_SH_TSSI_CCK 0x0058 /* TSSI for last 4 CCK frames (32bit) */
178#define B43_SHM_SH_TSSI_OFDM_A 0x0068 /* TSSI for last 4 OFDM frames (32bit) */
179#define B43_SHM_SH_TSSI_OFDM_G 0x0070 /* TSSI for last 4 OFDM frames (32bit) */
180#define B43_TSSI_MAX 0x7F /* Max value for one TSSI value */
176/* SHM_SHARED TX FIFO variables */ 181/* SHM_SHARED TX FIFO variables */
177#define B43_SHM_SH_SIZE01 0x0098 /* TX FIFO size for FIFO 0 (low) and 1 (high) */ 182#define B43_SHM_SH_SIZE01 0x0098 /* TX FIFO size for FIFO 0 (low) and 1 (high) */
178#define B43_SHM_SH_SIZE23 0x009A /* TX FIFO size for FIFO 2 and 3 */ 183#define B43_SHM_SH_SIZE23 0x009A /* TX FIFO size for FIFO 2 and 3 */
@@ -648,6 +653,11 @@ struct b43_wl {
648 struct b43_qos_params qos_params[4]; 653 struct b43_qos_params qos_params[4];
649 /* Workqueue for updating QOS parameters in hardware. */ 654 /* Workqueue for updating QOS parameters in hardware. */
650 struct work_struct qos_update_work; 655 struct work_struct qos_update_work;
656
657 /* Work for adjustment of the transmission power.
658 * This is scheduled when we determine that the actual TX output
659 * power doesn't match what we want. */
660 struct work_struct txpower_adjust_work;
651}; 661};
652 662
653/* In-memory representation of a cached microcode file. */ 663/* In-memory representation of a cached microcode file. */
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index af43f03b3189..63bafc2f3f0a 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -2805,6 +2805,9 @@ static void b43_periodic_every60sec(struct b43_wldev *dev)
2805 2805
2806 if (ops->pwork_60sec) 2806 if (ops->pwork_60sec)
2807 ops->pwork_60sec(dev); 2807 ops->pwork_60sec(dev);
2808
2809 /* Force check the TX power emission now. */
2810 b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME);
2808} 2811}
2809 2812
2810static void b43_periodic_every30sec(struct b43_wldev *dev) 2813static void b43_periodic_every30sec(struct b43_wldev *dev)
@@ -2835,8 +2838,6 @@ static void b43_periodic_every15sec(struct b43_wldev *dev)
2835 if (phy->ops->pwork_15sec) 2838 if (phy->ops->pwork_15sec)
2836 phy->ops->pwork_15sec(dev); 2839 phy->ops->pwork_15sec(dev);
2837 2840
2838 phy->ops->xmitpower(dev);
2839
2840 atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); 2841 atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT);
2841 wmb(); 2842 wmb();
2842} 2843}
@@ -3382,10 +3383,13 @@ static int b43_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
3382 3383
3383 /* Adjust the desired TX power level. */ 3384 /* Adjust the desired TX power level. */
3384 if (conf->power_level != 0) { 3385 if (conf->power_level != 0) {
3385 if (conf->power_level != phy->power_level) { 3386 spin_lock_irqsave(&wl->irq_lock, flags);
3386 phy->power_level = conf->power_level; 3387 if (conf->power_level != phy->desired_txpower) {
3387 phy->ops->xmitpower(dev); 3388 phy->desired_txpower = conf->power_level;
3389 b43_phy_txpower_check(dev, B43_TXPWR_IGNORE_TIME |
3390 B43_TXPWR_IGNORE_TSSI);
3388 } 3391 }
3392 spin_unlock_irqrestore(&wl->irq_lock, flags);
3389 } 3393 }
3390 3394
3391 /* Antennas for RX and management frame TX. */ 3395 /* Antennas for RX and management frame TX. */
@@ -3785,6 +3789,7 @@ static void setup_struct_phy_for_init(struct b43_wldev *dev,
3785 struct b43_phy *phy) 3789 struct b43_phy *phy)
3786{ 3790{
3787 phy->hardware_power_control = !!modparam_hwpctl; 3791 phy->hardware_power_control = !!modparam_hwpctl;
3792 phy->next_txpwr_check_time = jiffies;
3788 /* PHY TX errors counter. */ 3793 /* PHY TX errors counter. */
3789 atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT); 3794 atomic_set(&phy->txerr_cnt, B43_PHY_TX_BADNESS_LIMIT);
3790} 3795}
@@ -4204,6 +4209,8 @@ static void b43_op_stop(struct ieee80211_hw *hw)
4204 b43_wireless_core_stop(dev); 4209 b43_wireless_core_stop(dev);
4205 b43_wireless_core_exit(dev); 4210 b43_wireless_core_exit(dev);
4206 mutex_unlock(&wl->mutex); 4211 mutex_unlock(&wl->mutex);
4212
4213 cancel_work_sync(&(wl->txpower_adjust_work));
4207} 4214}
4208 4215
4209static int b43_op_set_retry_limit(struct ieee80211_hw *hw, 4216static int b43_op_set_retry_limit(struct ieee80211_hw *hw,
@@ -4581,6 +4588,7 @@ static int b43_wireless_init(struct ssb_device *dev)
4581 INIT_LIST_HEAD(&wl->devlist); 4588 INIT_LIST_HEAD(&wl->devlist);
4582 INIT_WORK(&wl->qos_update_work, b43_qos_update_work); 4589 INIT_WORK(&wl->qos_update_work, b43_qos_update_work);
4583 INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); 4590 INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work);
4591 INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work);
4584 4592
4585 ssb_set_devtypedata(dev, wl); 4593 ssb_set_devtypedata(dev, wl);
4586 b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id); 4594 b43info(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id);
diff --git a/drivers/net/wireless/b43/nphy.c b/drivers/net/wireless/b43/nphy.c
index 831986c459f8..4cfeab8560f6 100644
--- a/drivers/net/wireless/b43/nphy.c
+++ b/drivers/net/wireless/b43/nphy.c
@@ -34,10 +34,16 @@ void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
34{//TODO 34{//TODO
35} 35}
36 36
37void b43_nphy_xmitpower(struct b43_wldev *dev) 37static void b43_nphy_op_adjust_txpower(struct b43_wldev *dev)
38{//TODO 38{//TODO
39} 39}
40 40
41static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev,
42 bool ignore_tssi)
43{//TODO
44 return B43_TXPWR_RES_DONE;
45}
46
41static void b43_chantab_radio_upload(struct b43_wldev *dev, 47static void b43_chantab_radio_upload(struct b43_wldev *dev,
42 const struct b43_nphy_channeltab_entry *e) 48 const struct b43_nphy_channeltab_entry *e)
43{ 49{
@@ -602,10 +608,6 @@ static unsigned int b43_nphy_op_get_default_chan(struct b43_wldev *dev)
602 return 36; 608 return 36;
603} 609}
604 610
605static void b43_nphy_op_xmitpower(struct b43_wldev *dev)
606{//TODO
607}
608
609const struct b43_phy_operations b43_phyops_n = { 611const struct b43_phy_operations b43_phyops_n = {
610 .allocate = b43_nphy_op_allocate, 612 .allocate = b43_nphy_op_allocate,
611 .init = b43_nphy_op_init, 613 .init = b43_nphy_op_init,
@@ -617,5 +619,6 @@ const struct b43_phy_operations b43_phyops_n = {
617 .software_rfkill = b43_nphy_op_software_rfkill, 619 .software_rfkill = b43_nphy_op_software_rfkill,
618 .switch_channel = b43_nphy_op_switch_channel, 620 .switch_channel = b43_nphy_op_switch_channel,
619 .get_default_chan = b43_nphy_op_get_default_chan, 621 .get_default_chan = b43_nphy_op_get_default_chan,
620 .xmitpower = b43_nphy_op_xmitpower, 622 .recalc_txpower = b43_nphy_op_recalc_txpower,
623 .adjust_txpower = b43_nphy_op_adjust_txpower,
621}; 624};
diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c
index dd347314b76c..4d7d59e30960 100644
--- a/drivers/net/wireless/b43/phy_a.c
+++ b/drivers/net/wireless/b43/phy_a.c
@@ -505,10 +505,16 @@ static void b43_aphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
505 b43_hf_write(dev, hf); 505 b43_hf_write(dev, hf);
506} 506}
507 507
508static void b43_aphy_op_xmitpower(struct b43_wldev *dev) 508static void b43_aphy_op_adjust_txpower(struct b43_wldev *dev)
509{//TODO 509{//TODO
510} 510}
511 511
512static enum b43_txpwr_result b43_aphy_op_recalc_txpower(struct b43_wldev *dev,
513 bool ignore_tssi)
514{//TODO
515 return B43_TXPWR_RES_DONE;
516}
517
512static void b43_aphy_op_pwork_15sec(struct b43_wldev *dev) 518static void b43_aphy_op_pwork_15sec(struct b43_wldev *dev)
513{//TODO 519{//TODO
514} 520}
@@ -530,7 +536,8 @@ const struct b43_phy_operations b43_phyops_a = {
530 .switch_channel = b43_aphy_op_switch_channel, 536 .switch_channel = b43_aphy_op_switch_channel,
531 .get_default_chan = b43_aphy_op_get_default_chan, 537 .get_default_chan = b43_aphy_op_get_default_chan,
532 .set_rx_antenna = b43_aphy_op_set_rx_antenna, 538 .set_rx_antenna = b43_aphy_op_set_rx_antenna,
533 .xmitpower = b43_aphy_op_xmitpower, 539 .recalc_txpower = b43_aphy_op_recalc_txpower,
540 .adjust_txpower = b43_aphy_op_adjust_txpower,
534 .pwork_15sec = b43_aphy_op_pwork_15sec, 541 .pwork_15sec = b43_aphy_op_pwork_15sec,
535 .pwork_60sec = b43_aphy_op_pwork_60sec, 542 .pwork_60sec = b43_aphy_op_pwork_60sec,
536}; 543};
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index 45074c05d51d..5a550a7af2e9 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -274,3 +274,94 @@ void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state)
274 phy->ops->software_rfkill(dev, state); 274 phy->ops->software_rfkill(dev, state);
275 phy->radio_on = (state == RFKILL_STATE_UNBLOCKED); 275 phy->radio_on = (state == RFKILL_STATE_UNBLOCKED);
276} 276}
277
278/**
279 * b43_phy_txpower_adjust_work - TX power workqueue.
280 *
281 * Workqueue for updating the TX power parameters in hardware.
282 */
283void b43_phy_txpower_adjust_work(struct work_struct *work)
284{
285 struct b43_wl *wl = container_of(work, struct b43_wl,
286 txpower_adjust_work);
287 struct b43_wldev *dev;
288
289 mutex_lock(&wl->mutex);
290 dev = wl->current_dev;
291
292 if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED)))
293 dev->phy.ops->adjust_txpower(dev);
294
295 mutex_unlock(&wl->mutex);
296}
297
298/* Called with wl->irq_lock locked */
299void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags)
300{
301 struct b43_phy *phy = &dev->phy;
302 unsigned long now = jiffies;
303 enum b43_txpwr_result result;
304
305 if (!(flags & B43_TXPWR_IGNORE_TIME)) {
306 /* Check if it's time for a TXpower check. */
307 if (time_before(now, phy->next_txpwr_check_time))
308 return; /* Not yet */
309 }
310 /* The next check will be needed in two seconds, or later. */
311 phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2));
312
313 if ((dev->dev->bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) &&
314 (dev->dev->bus->boardinfo.type == SSB_BOARD_BU4306))
315 return; /* No software txpower adjustment needed */
316
317 result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI));
318 if (result == B43_TXPWR_RES_DONE)
319 return; /* We are done. */
320 B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST);
321 B43_WARN_ON(phy->ops->adjust_txpower == NULL);
322
323 /* We must adjust the transmission power in hardware.
324 * Schedule b43_phy_txpower_adjust_work(). */
325 queue_work(dev->wl->hw->workqueue, &dev->wl->txpower_adjust_work);
326}
327
328int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset)
329{
330 const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK);
331 unsigned int a, b, c, d;
332 unsigned int average;
333 u32 tmp;
334
335 tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset);
336 a = tmp & 0xFF;
337 b = (tmp >> 8) & 0xFF;
338 c = (tmp >> 16) & 0xFF;
339 d = (tmp >> 24) & 0xFF;
340 if (a == 0 || a == B43_TSSI_MAX ||
341 b == 0 || b == B43_TSSI_MAX ||
342 c == 0 || c == B43_TSSI_MAX ||
343 d == 0 || d == B43_TSSI_MAX)
344 return -ENOENT;
345 /* The values are OK. Clear them. */
346 tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) |
347 (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24);
348 b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp);
349
350 if (is_ofdm) {
351 a = (a + 32) & 0x3F;
352 b = (b + 32) & 0x3F;
353 c = (c + 32) & 0x3F;
354 d = (d + 32) & 0x3F;
355 }
356
357 /* Get the average of the values with 0.5 added to each value. */
358 average = (a + b + c + d + 2) / 4;
359 if (is_ofdm) {
360 /* Adjust for CCK-boost */
361 if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO)
362 & B43_HF_CCKBOOST)
363 average = (average >= 13) ? (average - 13) : 0;
364 }
365
366 return average;
367}
diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h
index 9b9635eda9c4..f8db9f40df5d 100644
--- a/drivers/net/wireless/b43/phy_common.h
+++ b/drivers/net/wireless/b43/phy_common.h
@@ -61,6 +61,17 @@ enum {
61}; 61};
62 62
63/** 63/**
64 * enum b43_txpwr_result - Return value for the recalc_txpower PHY op.
65 *
66 * @B43_TXPWR_RES_NEED_ADJUST: Values changed. Hardware adjustment is needed.
67 * @B43_TXPWR_RES_DONE: No more work to do. Everything is done.
68 */
69enum b43_txpwr_result {
70 B43_TXPWR_RES_NEED_ADJUST,
71 B43_TXPWR_RES_DONE,
72};
73
74/**
64 * struct b43_phy_operations - Function pointers for PHY ops. 75 * struct b43_phy_operations - Function pointers for PHY ops.
65 * 76 *
66 * @prepare: Prepare the PHY. This is called before @init. 77 * @prepare: Prepare the PHY. This is called before @init.
@@ -96,8 +107,23 @@ enum {
96 * @interf_mitigation: Switch the Interference Mitigation mode. 107 * @interf_mitigation: Switch the Interference Mitigation mode.
97 * Can be NULL, if not supported. 108 * Can be NULL, if not supported.
98 * 109 *
99 * @xmitpower: FIXME REMOVEME 110 * @recalc_txpower: Recalculate the transmission power parameters.
111 * This callback has to recalculate the TX power settings,
112 * but does not need to write them to the hardware, yet.
113 * Returns enum b43_txpwr_result to indicate whether the hardware
114 * needs to be adjusted.
115 * If B43_TXPWR_NEED_ADJUST is returned, @adjust_txpower
116 * will be called later.
117 * If the parameter "ignore_tssi" is true, the TSSI values should
118 * be ignored and a recalculation of the power settings should be
119 * done even if the TSSI values did not change.
120 * This callback is called with wl->irq_lock held and must not sleep.
100 * Must not be NULL. 121 * Must not be NULL.
122 * @adjust_txpower: Write the previously calculated TX power settings
123 * (from @recalc_txpower) to the hardware.
124 * This function may sleep.
125 * Can be NULL, if (and ONLY if) @recalc_txpower _always_
126 * returns B43_TXPWR_RES_DONE.
101 * 127 *
102 * @pwork_15sec: Periodic work. Called every 15 seconds. 128 * @pwork_15sec: Periodic work. Called every 15 seconds.
103 * Can be NULL, if not required. 129 * Can be NULL, if not required.
@@ -127,7 +153,9 @@ struct b43_phy_operations {
127 enum b43_interference_mitigation new_mode); 153 enum b43_interference_mitigation new_mode);
128 154
129 /* Transmission power adjustment */ 155 /* Transmission power adjustment */
130 void (*xmitpower)(struct b43_wldev *dev); 156 enum b43_txpwr_result (*recalc_txpower)(struct b43_wldev *dev,
157 bool ignore_tssi);
158 void (*adjust_txpower)(struct b43_wldev *dev);
131 159
132 /* Misc */ 160 /* Misc */
133 void (*pwork_15sec)(struct b43_wldev *dev); 161 void (*pwork_15sec)(struct b43_wldev *dev);
@@ -183,11 +211,15 @@ struct b43_phy {
183 211
184 /* Desired TX power level (in dBm). 212 /* Desired TX power level (in dBm).
185 * This is set by the user and adjusted in b43_phy_xmitpower(). */ 213 * This is set by the user and adjusted in b43_phy_xmitpower(). */
186 u8 power_level; 214 int desired_txpower;
187 215
188 /* Hardware Power Control enabled? */ 216 /* Hardware Power Control enabled? */
189 bool hardware_power_control; 217 bool hardware_power_control;
190 218
219 /* The time (in absolute jiffies) when the next TX power output
220 * check is needed. */
221 unsigned long next_txpwr_check_time;
222
191 /* current channel */ 223 /* current channel */
192 unsigned int channel; 224 unsigned int channel;
193 225
@@ -309,4 +341,41 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
309 */ 341 */
310void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state); 342void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state);
311 343
344/**
345 * b43_phy_txpower_check - Check TX power output.
346 *
347 * Compare the current TX power output to the desired power emission
348 * and schedule an adjustment in case it mismatches.
349 * Requires wl->irq_lock locked.
350 *
351 * @flags: OR'ed enum b43_phy_txpower_check_flags flags.
352 * See the docs below.
353 */
354void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags);
355/**
356 * enum b43_phy_txpower_check_flags - Flags for b43_phy_txpower_check()
357 *
358 * @B43_TXPWR_IGNORE_TIME: Ignore the schedule time and force-redo
359 * the check now.
360 * @B43_TXPWR_IGNORE_TSSI: Redo the recalculation, even if the average
361 * TSSI did not change.
362 */
363enum b43_phy_txpower_check_flags {
364 B43_TXPWR_IGNORE_TIME = (1 << 0),
365 B43_TXPWR_IGNORE_TSSI = (1 << 1),
366};
367
368struct work_struct;
369void b43_phy_txpower_adjust_work(struct work_struct *work);
370
371/**
372 * b43_phy_shm_tssi_read - Read the average of the last 4 TSSI from SHM.
373 *
374 * @shm_offset: The SHM address to read the values from.
375 *
376 * Returns the average of the 4 TSSI values, or a negative error code.
377 */
378int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset);
379
380
312#endif /* LINUX_B43_PHY_COMMON_H_ */ 381#endif /* LINUX_B43_PHY_COMMON_H_ */
diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c
index bb95c54cd43d..fce84896d34c 100644
--- a/drivers/net/wireless/b43/phy_g.c
+++ b/drivers/net/wireless/b43/phy_g.c
@@ -2659,6 +2659,7 @@ static int b43_gphy_op_allocate(struct b43_wldev *dev)
2659 /* OFDM-table address caching. */ 2659 /* OFDM-table address caching. */
2660 gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_UNKNOWN; 2660 gphy->ofdmtab_addr_direction = B43_OFDMTAB_DIRECTION_UNKNOWN;
2661 2661
2662 gphy->average_tssi = 0xFF;
2662 2663
2663 lo = kzalloc(sizeof(*lo), GFP_KERNEL); 2664 lo = kzalloc(sizeof(*lo), GFP_KERNEL);
2664 if (!lo) { 2665 if (!lo) {
@@ -3011,113 +3012,20 @@ static void b43_put_attenuation_into_ranges(struct b43_wldev *dev,
3011 *_bbatt = clamp_val(bbatt, bb_min, bb_max); 3012 *_bbatt = clamp_val(bbatt, bb_min, bb_max);
3012} 3013}
3013 3014
3014static void b43_gphy_op_xmitpower(struct b43_wldev *dev) 3015static void b43_gphy_op_adjust_txpower(struct b43_wldev *dev)
3015{ 3016{
3016 struct ssb_bus *bus = dev->dev->bus;
3017 struct b43_phy *phy = &dev->phy; 3017 struct b43_phy *phy = &dev->phy;
3018 struct b43_phy_g *gphy = phy->g; 3018 struct b43_phy_g *gphy = phy->g;
3019 u16 tmp;
3020 s8 v0, v1, v2, v3;
3021 s8 average;
3022 int max_pwr;
3023 int desired_pwr, estimated_pwr, pwr_adjust;
3024 int rfatt_delta, bbatt_delta;
3025 int rfatt, bbatt; 3019 int rfatt, bbatt;
3026 u8 tx_control; 3020 u8 tx_control;
3027 3021
3028 if (gphy->cur_idle_tssi == 0) 3022 spin_lock_irq(&dev->wl->irq_lock);
3029 return;
3030 if ((bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) &&
3031 (bus->boardinfo.type == SSB_BOARD_BU4306))
3032 return;
3033
3034 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x0058);
3035 v0 = (s8) (tmp & 0x00FF);
3036 v1 = (s8) ((tmp & 0xFF00) >> 8);
3037 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x005A);
3038 v2 = (s8) (tmp & 0x00FF);
3039 v3 = (s8) ((tmp & 0xFF00) >> 8);
3040 tmp = 0;
3041
3042 if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F
3043 || v3 == 0x7F) {
3044 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x0070);
3045 v0 = (s8) (tmp & 0x00FF);
3046 v1 = (s8) ((tmp & 0xFF00) >> 8);
3047 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x0072);
3048 v2 = (s8) (tmp & 0x00FF);
3049 v3 = (s8) ((tmp & 0xFF00) >> 8);
3050 if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F
3051 || v3 == 0x7F)
3052 return;
3053 v0 = (v0 + 0x20) & 0x3F;
3054 v1 = (v1 + 0x20) & 0x3F;
3055 v2 = (v2 + 0x20) & 0x3F;
3056 v3 = (v3 + 0x20) & 0x3F;
3057 tmp = 1;
3058 }
3059 b43_shm_clear_tssi(dev);
3060
3061 average = (v0 + v1 + v2 + v3 + 2) / 4;
3062
3063 if (tmp && (b43_shm_read16(dev, B43_SHM_SHARED, 0x005E) & 0x8))
3064 average -= 13;
3065
3066 estimated_pwr = b43_gphy_estimate_power_out(dev, average);
3067
3068 max_pwr = dev->dev->bus->sprom.maxpwr_bg;
3069 if ((dev->dev->bus->sprom.boardflags_lo
3070 & B43_BFL_PACTRL) && (phy->type == B43_PHYTYPE_G))
3071 max_pwr -= 0x3;
3072 if (unlikely(max_pwr <= 0)) {
3073 b43warn(dev->wl,
3074 "Invalid max-TX-power value in SPROM.\n");
3075 max_pwr = 60; /* fake it */
3076 dev->dev->bus->sprom.maxpwr_bg = max_pwr;
3077 }
3078
3079 /*TODO:
3080 max_pwr = min(REG - dev->dev->bus->sprom.antennagain_bgphy - 0x6, max_pwr)
3081 where REG is the max power as per the regulatory domain
3082 */
3083
3084 /* Get desired power (in Q5.2) */
3085 desired_pwr = INT_TO_Q52(phy->power_level);
3086 /* And limit it. max_pwr already is Q5.2 */
3087 desired_pwr = clamp_val(desired_pwr, 0, max_pwr);
3088 if (b43_debug(dev, B43_DBG_XMITPOWER)) {
3089 b43dbg(dev->wl,
3090 "Current TX power output: " Q52_FMT
3091 " dBm, " "Desired TX power output: "
3092 Q52_FMT " dBm\n", Q52_ARG(estimated_pwr),
3093 Q52_ARG(desired_pwr));
3094 }
3095
3096 /* Calculate the adjustment delta. */
3097 pwr_adjust = desired_pwr - estimated_pwr;
3098
3099 /* RF attenuation delta. */
3100 rfatt_delta = ((pwr_adjust + 7) / 8);
3101 /* Lower attenuation => Bigger power output. Negate it. */
3102 rfatt_delta = -rfatt_delta;
3103
3104 /* Baseband attenuation delta. */
3105 bbatt_delta = pwr_adjust / 2;
3106 /* Lower attenuation => Bigger power output. Negate it. */
3107 bbatt_delta = -bbatt_delta;
3108 /* RF att affects power level 4 times as much as
3109 * Baseband attennuation. Subtract it. */
3110 bbatt_delta -= 4 * rfatt_delta;
3111
3112 /* So do we finally need to adjust something? */
3113 if ((rfatt_delta == 0) && (bbatt_delta == 0))
3114 return;
3115 3023
3116 /* Calculate the new attenuation values. */ 3024 /* Calculate the new attenuation values. */
3117 bbatt = gphy->bbatt.att; 3025 bbatt = gphy->bbatt.att;
3118 bbatt += bbatt_delta; 3026 bbatt += gphy->bbatt_delta;
3119 rfatt = gphy->rfatt.att; 3027 rfatt = gphy->rfatt.att;
3120 rfatt += rfatt_delta; 3028 rfatt += gphy->rfatt_delta;
3121 3029
3122 b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt); 3030 b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt);
3123 tx_control = gphy->tx_control; 3031 tx_control = gphy->tx_control;
@@ -3152,6 +3060,14 @@ static void b43_gphy_op_xmitpower(struct b43_wldev *dev)
3152 gphy->rfatt.att = rfatt; 3060 gphy->rfatt.att = rfatt;
3153 gphy->bbatt.att = bbatt; 3061 gphy->bbatt.att = bbatt;
3154 3062
3063 /* We drop the lock early, so we can sleep during hardware
3064 * adjustment. Possible races with op_recalc_txpower are harmless,
3065 * as we will be called once again in case we raced. */
3066 spin_unlock_irq(&dev->wl->irq_lock);
3067
3068 if (b43_debug(dev, B43_DBG_XMITPOWER))
3069 b43dbg(dev->wl, "Adjusting TX power\n");
3070
3155 /* Adjust the hardware */ 3071 /* Adjust the hardware */
3156 b43_phy_lock(dev); 3072 b43_phy_lock(dev);
3157 b43_radio_lock(dev); 3073 b43_radio_lock(dev);
@@ -3161,6 +3077,111 @@ static void b43_gphy_op_xmitpower(struct b43_wldev *dev)
3161 b43_phy_unlock(dev); 3077 b43_phy_unlock(dev);
3162} 3078}
3163 3079
3080static enum b43_txpwr_result b43_gphy_op_recalc_txpower(struct b43_wldev *dev,
3081 bool ignore_tssi)
3082{
3083 struct b43_phy *phy = &dev->phy;
3084 struct b43_phy_g *gphy = phy->g;
3085 unsigned int average_tssi;
3086 int cck_result, ofdm_result;
3087 int estimated_pwr, desired_pwr, pwr_adjust;
3088 int rfatt_delta, bbatt_delta;
3089 unsigned int max_pwr;
3090
3091 /* First get the average TSSI */
3092 cck_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_CCK);
3093 ofdm_result = b43_phy_shm_tssi_read(dev, B43_SHM_SH_TSSI_OFDM_G);
3094 if ((cck_result < 0) && (ofdm_result < 0)) {
3095 /* No TSSI information available */
3096 if (!ignore_tssi)
3097 goto no_adjustment_needed;
3098 cck_result = 0;
3099 ofdm_result = 0;
3100 }
3101 if (cck_result < 0)
3102 average_tssi = ofdm_result;
3103 else if (ofdm_result < 0)
3104 average_tssi = cck_result;
3105 else
3106 average_tssi = (cck_result + ofdm_result) / 2;
3107 /* Merge the average with the stored value. */
3108 if (likely(gphy->average_tssi != 0xFF))
3109 average_tssi = (average_tssi + gphy->average_tssi) / 2;
3110 gphy->average_tssi = average_tssi;
3111 B43_WARN_ON(average_tssi >= B43_TSSI_MAX);
3112
3113 /* Estimate the TX power emission based on the TSSI */
3114 estimated_pwr = b43_gphy_estimate_power_out(dev, average_tssi);
3115
3116 B43_WARN_ON(phy->type != B43_PHYTYPE_G);
3117 max_pwr = dev->dev->bus->sprom.maxpwr_bg;
3118 if (dev->dev->bus->sprom.boardflags_lo & B43_BFL_PACTRL)
3119 max_pwr -= 3; /* minus 0.75 */
3120 if (unlikely(max_pwr >= INT_TO_Q52(30/*dBm*/))) {
3121 b43warn(dev->wl,
3122 "Invalid max-TX-power value in SPROM.\n");
3123 max_pwr = INT_TO_Q52(20); /* fake it */
3124 dev->dev->bus->sprom.maxpwr_bg = max_pwr;
3125 }
3126
3127 /* Get desired power (in Q5.2) */
3128 if (phy->desired_txpower < 0)
3129 desired_pwr = INT_TO_Q52(0);
3130 else
3131 desired_pwr = INT_TO_Q52(phy->desired_txpower);
3132 /* And limit it. max_pwr already is Q5.2 */
3133 desired_pwr = clamp_val(desired_pwr, 0, max_pwr);
3134 if (b43_debug(dev, B43_DBG_XMITPOWER)) {
3135 b43dbg(dev->wl,
3136 "[TX power] current = " Q52_FMT
3137 " dBm, desired = " Q52_FMT
3138 " dBm, max = " Q52_FMT "\n",
3139 Q52_ARG(estimated_pwr),
3140 Q52_ARG(desired_pwr),
3141 Q52_ARG(max_pwr));
3142 }
3143
3144 /* Calculate the adjustment delta. */
3145 pwr_adjust = desired_pwr - estimated_pwr;
3146 if (pwr_adjust == 0)
3147 goto no_adjustment_needed;
3148
3149 /* RF attenuation delta. */
3150 rfatt_delta = ((pwr_adjust + 7) / 8);
3151 /* Lower attenuation => Bigger power output. Negate it. */
3152 rfatt_delta = -rfatt_delta;
3153
3154 /* Baseband attenuation delta. */
3155 bbatt_delta = pwr_adjust / 2;
3156 /* Lower attenuation => Bigger power output. Negate it. */
3157 bbatt_delta = -bbatt_delta;
3158 /* RF att affects power level 4 times as much as
3159 * Baseband attennuation. Subtract it. */
3160 bbatt_delta -= 4 * rfatt_delta;
3161
3162 if (b43_debug(dev, B43_DBG_XMITPOWER)) {
3163 int dbm = pwr_adjust < 0 ? -pwr_adjust : pwr_adjust;
3164 b43dbg(dev->wl,
3165 "[TX power deltas] %s" Q52_FMT " dBm => "
3166 "bbatt-delta = %d, rfatt-delta = %d\n",
3167 (pwr_adjust < 0 ? "-" : ""), Q52_ARG(dbm),
3168 bbatt_delta, rfatt_delta);
3169 }
3170 /* So do we finally need to adjust something in hardware? */
3171 if ((rfatt_delta == 0) && (bbatt_delta == 0))
3172 goto no_adjustment_needed;
3173
3174 /* Save the deltas for later when we adjust the power. */
3175 gphy->bbatt_delta = bbatt_delta;
3176 gphy->rfatt_delta = rfatt_delta;
3177
3178 /* We need to adjust the TX power on the device. */
3179 return B43_TXPWR_RES_NEED_ADJUST;
3180
3181no_adjustment_needed:
3182 return B43_TXPWR_RES_DONE;
3183}
3184
3164static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev) 3185static void b43_gphy_op_pwork_15sec(struct b43_wldev *dev)
3165{ 3186{
3166 struct b43_phy *phy = &dev->phy; 3187 struct b43_phy *phy = &dev->phy;
@@ -3223,7 +3244,8 @@ const struct b43_phy_operations b43_phyops_g = {
3223 .get_default_chan = b43_gphy_op_get_default_chan, 3244 .get_default_chan = b43_gphy_op_get_default_chan,
3224 .set_rx_antenna = b43_gphy_op_set_rx_antenna, 3245 .set_rx_antenna = b43_gphy_op_set_rx_antenna,
3225 .interf_mitigation = b43_gphy_op_interf_mitigation, 3246 .interf_mitigation = b43_gphy_op_interf_mitigation,
3226 .xmitpower = b43_gphy_op_xmitpower, 3247 .recalc_txpower = b43_gphy_op_recalc_txpower,
3248 .adjust_txpower = b43_gphy_op_adjust_txpower,
3227 .pwork_15sec = b43_gphy_op_pwork_15sec, 3249 .pwork_15sec = b43_gphy_op_pwork_15sec,
3228 .pwork_60sec = b43_gphy_op_pwork_60sec, 3250 .pwork_60sec = b43_gphy_op_pwork_60sec,
3229}; 3251};
diff --git a/drivers/net/wireless/b43/phy_g.h b/drivers/net/wireless/b43/phy_g.h
index 1f0daebd6eb6..7f95edea1c63 100644
--- a/drivers/net/wireless/b43/phy_g.h
+++ b/drivers/net/wireless/b43/phy_g.h
@@ -115,7 +115,6 @@ struct b43_txpower_lo_control;
115 115
116struct b43_phy_g { 116struct b43_phy_g {
117 bool initialised; 117 bool initialised;
118 bool dyn_tssi_tbl; /* tssi2dbm is kmalloc()ed. */
119 118
120 /* ACI (adjacent channel interference) flags. */ 119 /* ACI (adjacent channel interference) flags. */
121 bool aci_enable; 120 bool aci_enable;
@@ -135,12 +134,26 @@ struct b43_phy_g {
135 u16 minlowsig[2]; 134 u16 minlowsig[2];
136 u16 minlowsigpos[2]; 135 u16 minlowsigpos[2];
137 136
138 /* TSSI to dBm table in use */ 137 /* Pointer to the table used to convert a
138 * TSSI value to dBm-Q5.2 */
139 const s8 *tssi2dbm; 139 const s8 *tssi2dbm;
140 /* tssi2dbm is kmalloc()ed. Only used for free()ing. */
141 bool dyn_tssi_tbl;
140 /* Target idle TSSI */ 142 /* Target idle TSSI */
141 int tgt_idle_tssi; 143 int tgt_idle_tssi;
142 /* Current idle TSSI */ 144 /* Current idle TSSI */
143 int cur_idle_tssi; 145 int cur_idle_tssi;
146 /* The current average TSSI.
147 * Needs irq_lock, as it's updated in the IRQ path. */
148 u8 average_tssi;
149 /* Current TX power level attenuation control values */
150 struct b43_bbatt bbatt;
151 struct b43_rfatt rfatt;
152 u8 tx_control; /* B43_TXCTL_XXX */
153 /* The calculated attenuation deltas that are used later
154 * when adjusting the actual power output. */
155 int bbatt_delta;
156 int rfatt_delta;
144 157
145 /* LocalOscillator control values. */ 158 /* LocalOscillator control values. */
146 struct b43_txpower_lo_control *lo_control; 159 struct b43_txpower_lo_control *lo_control;
@@ -151,11 +164,6 @@ struct b43_phy_g {
151 s16 lna_gain; /* LNA */ 164 s16 lna_gain; /* LNA */
152 s16 pga_gain; /* PGA */ 165 s16 pga_gain; /* PGA */
153 166
154 /* Current TX power level attenuation control values */
155 struct b43_bbatt bbatt;
156 struct b43_rfatt rfatt;
157 u8 tx_control; /* B43_TXCTL_XXX */
158
159 /* Current Interference Mitigation mode */ 167 /* Current Interference Mitigation mode */
160 int interfmode; 168 int interfmode;
161 /* Stack of saved values from the Interference Mitigation code. 169 /* Stack of saved values from the Interference Mitigation code.
diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c
index c8a831234e4a..5e0b71c3ad02 100644
--- a/drivers/net/wireless/b43/xmit.c
+++ b/drivers/net/wireless/b43/xmit.c
@@ -680,6 +680,8 @@ void b43_handle_txstatus(struct b43_wldev *dev,
680 b43_pio_handle_txstatus(dev, status); 680 b43_pio_handle_txstatus(dev, status);
681 else 681 else
682 b43_dma_handle_txstatus(dev, status); 682 b43_dma_handle_txstatus(dev, status);
683
684 b43_phy_txpower_check(dev, 0);
683} 685}
684 686
685/* Fill out the mac80211 TXstatus report based on the b43-specific 687/* Fill out the mac80211 TXstatus report based on the b43-specific