3 Broadcom B43 wireless driver
5 Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
6 Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it>
7 Copyright (c) 2005, 2006 Michael Buesch <mb@bu3sch.de>
8 Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org>
9 Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; see the file COPYING. If not, write to
23 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
24 Boston, MA 02110-1301, USA.
28 #include <linux/delay.h>
30 #include <linux/types.h>
31 #include <linux/bitrev.h>
42 static void b43_shm_clear_tssi(struct b43_wldev *dev)
44 struct b43_phy *phy = &dev->phy;
48 b43_shm_write16(dev, B43_SHM_SHARED, 0x0068, 0x7F7F);
49 b43_shm_write16(dev, B43_SHM_SHARED, 0x006a, 0x7F7F);
53 b43_shm_write16(dev, B43_SHM_SHARED, 0x0058, 0x7F7F);
54 b43_shm_write16(dev, B43_SHM_SHARED, 0x005a, 0x7F7F);
55 b43_shm_write16(dev, B43_SHM_SHARED, 0x0070, 0x7F7F);
56 b43_shm_write16(dev, B43_SHM_SHARED, 0x0072, 0x7F7F);
61 /* http://bcm-specs.sipsolutions.net/EstimatePowerOut
62 * This function converts a TSSI value to dBm in Q5.2
64 static s8 b43_phy_estimate_power_out(struct b43_wldev *dev, s8 tssi)
66 struct b43_phy *phy = &dev->phy;
70 tmp = (phy->tgt_idle_tssi - phy->cur_idle_tssi + tssi);
75 tmp = clamp_val(tmp, 0x00, 0xFF);
76 dbm = phy->tssi2dbm[tmp];
77 //TODO: There's a FIXME on the specs
81 tmp = clamp_val(tmp, 0x00, 0x3F);
82 dbm = phy->tssi2dbm[tmp];
91 void b43_put_attenuation_into_ranges(struct b43_wldev *dev,
92 int *_bbatt, int *_rfatt)
96 struct b43_txpower_lo_control *lo = dev->phy.lo_control;
98 /* Get baseband and radio attenuation values into their permitted ranges.
99 * Radio attenuation affects power level 4 times as much as baseband. */
101 /* Range constants */
102 const int rf_min = lo->rfatt_list.min_val;
103 const int rf_max = lo->rfatt_list.max_val;
104 const int bb_min = lo->bbatt_list.min_val;
105 const int bb_max = lo->bbatt_list.max_val;
108 if (rfatt > rf_max && bbatt > bb_max - 4)
109 break; /* Can not get it into ranges */
110 if (rfatt < rf_min && bbatt < bb_min + 4)
111 break; /* Can not get it into ranges */
112 if (bbatt > bb_max && rfatt > rf_max - 1)
113 break; /* Can not get it into ranges */
114 if (bbatt < bb_min && rfatt < rf_min + 1)
115 break; /* Can not get it into ranges */
117 if (bbatt > bb_max) {
122 if (bbatt < bb_min) {
127 if (rfatt > rf_max) {
132 if (rfatt < rf_min) {
140 *_rfatt = clamp_val(rfatt, rf_min, rf_max);
141 *_bbatt = clamp_val(bbatt, bb_min, bb_max);
144 /* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
145 void b43_phy_xmitpower(struct b43_wldev *dev)
147 struct ssb_bus *bus = dev->dev->bus;
148 struct b43_phy *phy = &dev->phy;
150 if (phy->cur_idle_tssi == 0)
152 if ((bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) &&
153 (bus->boardinfo.type == SSB_BOARD_BU4306))
155 #ifdef CONFIG_B43_DEBUG
156 if (phy->manual_txpower_control)
163 //TODO: Nothing for A PHYs yet :-/
173 int desired_pwr, estimated_pwr, pwr_adjust;
174 int rfatt_delta, bbatt_delta;
178 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x0058);
179 v0 = (s8) (tmp & 0x00FF);
180 v1 = (s8) ((tmp & 0xFF00) >> 8);
181 tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x005A);
182 v2 = (s8) (tmp & 0x00FF);
183 v3 = (s8) ((tmp & 0xFF00) >> 8);
186 if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F
189 b43_shm_read16(dev, B43_SHM_SHARED, 0x0070);
190 v0 = (s8) (tmp & 0x00FF);
191 v1 = (s8) ((tmp & 0xFF00) >> 8);
193 b43_shm_read16(dev, B43_SHM_SHARED, 0x0072);
194 v2 = (s8) (tmp & 0x00FF);
195 v3 = (s8) ((tmp & 0xFF00) >> 8);
196 if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F
199 v0 = (v0 + 0x20) & 0x3F;
200 v1 = (v1 + 0x20) & 0x3F;
201 v2 = (v2 + 0x20) & 0x3F;
202 v3 = (v3 + 0x20) & 0x3F;
205 b43_shm_clear_tssi(dev);
207 average = (v0 + v1 + v2 + v3 + 2) / 4;
210 && (b43_shm_read16(dev, B43_SHM_SHARED, 0x005E) &
215 b43_phy_estimate_power_out(dev, average);
217 max_pwr = dev->dev->bus->sprom.maxpwr_bg;
218 if ((dev->dev->bus->sprom.boardflags_lo
219 & B43_BFL_PACTRL) && (phy->type == B43_PHYTYPE_G))
221 if (unlikely(max_pwr <= 0)) {
223 "Invalid max-TX-power value in SPROM.\n");
224 max_pwr = 60; /* fake it */
225 dev->dev->bus->sprom.maxpwr_bg = max_pwr;
229 max_pwr = min(REG - dev->dev->bus->sprom.antennagain_bgphy - 0x6, max_pwr)
230 where REG is the max power as per the regulatory domain
233 /* Get desired power (in Q5.2) */
234 desired_pwr = INT_TO_Q52(phy->power_level);
235 /* And limit it. max_pwr already is Q5.2 */
236 desired_pwr = clamp_val(desired_pwr, 0, max_pwr);
237 if (b43_debug(dev, B43_DBG_XMITPOWER)) {
239 "Current TX power output: " Q52_FMT
240 " dBm, " "Desired TX power output: "
241 Q52_FMT " dBm\n", Q52_ARG(estimated_pwr),
242 Q52_ARG(desired_pwr));
245 /* Calculate the adjustment delta. */
246 pwr_adjust = desired_pwr - estimated_pwr;
248 /* RF attenuation delta. */
249 rfatt_delta = ((pwr_adjust + 7) / 8);
250 /* Lower attenuation => Bigger power output. Negate it. */
251 rfatt_delta = -rfatt_delta;
253 /* Baseband attenuation delta. */
254 bbatt_delta = pwr_adjust / 2;
255 /* Lower attenuation => Bigger power output. Negate it. */
256 bbatt_delta = -bbatt_delta;
257 /* RF att affects power level 4 times as much as
258 * Baseband attennuation. Subtract it. */
259 bbatt_delta -= 4 * rfatt_delta;
261 /* So do we finally need to adjust something? */
262 if ((rfatt_delta == 0) && (bbatt_delta == 0))
265 /* Calculate the new attenuation values. */
266 bbatt = phy->bbatt.att;
267 bbatt += bbatt_delta;
268 rfatt = phy->rfatt.att;
269 rfatt += rfatt_delta;
271 b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt);
272 tx_control = phy->tx_control;
273 if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) {
275 if (tx_control == 0) {
281 } else if (dev->dev->bus->sprom.
284 bbatt += 4 * (rfatt - 2);
287 } else if (rfatt > 4 && tx_control) {
298 /* Save the control values */
299 phy->tx_control = tx_control;
300 b43_put_attenuation_into_ranges(dev, &bbatt, &rfatt);
301 phy->rfatt.att = rfatt;
302 phy->bbatt.att = bbatt;
304 /* Adjust the hardware */
307 b43_set_txpower_g(dev, &phy->bbatt, &phy->rfatt,
309 b43_radio_unlock(dev);
314 b43_nphy_xmitpower(dev);
321 static inline s32 b43_tssi2dbm_ad(s32 num, s32 den)
326 return (num + den / 2) / den;
330 s8 b43_tssi2dbm_entry(s8 entry[], u8 index, s16 pab0, s16 pab1, s16 pab2)
332 s32 m1, m2, f = 256, q, delta;
335 m1 = b43_tssi2dbm_ad(16 * pab0 + index * pab1, 32);
336 m2 = max(b43_tssi2dbm_ad(32768 + index * pab2, 256), 1);
340 q = b43_tssi2dbm_ad(f * 4096 -
341 b43_tssi2dbm_ad(m2 * f, 16) * f, 2048);
345 } while (delta >= 2);
346 entry[index] = clamp_val(b43_tssi2dbm_ad(m1 * f, 8192), -127, 128);
350 /* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */
351 int b43_phy_init_tssi2dbm_table(struct b43_wldev *dev)
353 struct b43_phy *phy = &dev->phy;
354 s16 pab0, pab1, pab2;
358 if (phy->type == B43_PHYTYPE_A) {
359 pab0 = (s16) (dev->dev->bus->sprom.pa1b0);
360 pab1 = (s16) (dev->dev->bus->sprom.pa1b1);
361 pab2 = (s16) (dev->dev->bus->sprom.pa1b2);
363 pab0 = (s16) (dev->dev->bus->sprom.pa0b0);
364 pab1 = (s16) (dev->dev->bus->sprom.pa0b1);
365 pab2 = (s16) (dev->dev->bus->sprom.pa0b2);
368 if ((dev->dev->bus->chip_id == 0x4301) && (phy->radio_ver != 0x2050)) {
369 phy->tgt_idle_tssi = 0x34;
370 phy->tssi2dbm = b43_tssi2dbm_b_table;
374 if (pab0 != 0 && pab1 != 0 && pab2 != 0 &&
375 pab0 != -1 && pab1 != -1 && pab2 != -1) {
376 /* The pabX values are set in SPROM. Use them. */
377 if (phy->type == B43_PHYTYPE_A) {
378 if ((s8) dev->dev->bus->sprom.itssi_a != 0 &&
379 (s8) dev->dev->bus->sprom.itssi_a != -1)
381 (s8) (dev->dev->bus->sprom.itssi_a);
383 phy->tgt_idle_tssi = 62;
385 if ((s8) dev->dev->bus->sprom.itssi_bg != 0 &&
386 (s8) dev->dev->bus->sprom.itssi_bg != -1)
388 (s8) (dev->dev->bus->sprom.itssi_bg);
390 phy->tgt_idle_tssi = 62;
392 dyn_tssi2dbm = kmalloc(64, GFP_KERNEL);
393 if (dyn_tssi2dbm == NULL) {
394 b43err(dev->wl, "Could not allocate memory "
395 "for tssi2dbm table\n");
398 for (idx = 0; idx < 64; idx++)
399 if (b43_tssi2dbm_entry
400 (dyn_tssi2dbm, idx, pab0, pab1, pab2)) {
401 phy->tssi2dbm = NULL;
402 b43err(dev->wl, "Could not generate "
407 phy->tssi2dbm = dyn_tssi2dbm;
408 phy->dyn_tssi_tbl = 1;
410 /* pabX values not set in SPROM. */
413 /* APHY needs a generated table. */
414 phy->tssi2dbm = NULL;
415 b43err(dev->wl, "Could not generate tssi2dBm "
416 "table (wrong SPROM info)!\n");
419 phy->tgt_idle_tssi = 0x34;
420 phy->tssi2dbm = b43_tssi2dbm_b_table;
423 phy->tgt_idle_tssi = 0x34;
424 phy->tssi2dbm = b43_tssi2dbm_g_table;
432 void b43_radio_turn_on(struct b43_wldev *dev)
434 struct b43_phy *phy = &dev->phy;
445 b43_radio_write16(dev, 0x0004, 0x00C0);
446 b43_radio_write16(dev, 0x0005, 0x0008);
447 b43_phy_write(dev, 0x0010, b43_phy_read(dev, 0x0010) & 0xFFF7);
448 b43_phy_write(dev, 0x0011, b43_phy_read(dev, 0x0011) & 0xFFF7);
449 b43_radio_init2060(dev);
456 b43_nphy_radio_turn_on(dev);
464 void b43_radio_turn_off(struct b43_wldev *dev, bool force)
466 struct b43_phy *phy = &dev->phy;
468 if (!phy->radio_on && !force)
473 b43_nphy_radio_turn_off(dev);
476 b43_radio_write16(dev, 0x0004, 0x00FF);
477 b43_radio_write16(dev, 0x0005, 0x00FB);
478 b43_phy_write(dev, 0x0010, b43_phy_read(dev, 0x0010) | 0x0008);
479 b43_phy_write(dev, 0x0011, b43_phy_read(dev, 0x0011) | 0x0008);
481 case B43_PHYTYPE_G: {