📄 lo.c
字号:
/* Broadcom B43 wireless driver G PHY LO (LocalOscillator) Measuring and Control routines Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, Copyright (c) 2005, 2006 Stefano Brivio <st3@riseup.net> Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de> Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org> Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch> 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.*/#include "b43.h"#include "lo.h"#include "phy.h"#include "main.h"#include <linux/delay.h>#include <linux/sched.h>/* Define to 1 to always calibrate all possible LO control pairs. * This is a workaround until we fix the partial LO calibration optimization. */#define B43_CALIB_ALL_LOCTLS 1/* Write the LocalOscillator Control (adjust) value-pair. */static void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control){ struct b43_phy *phy = &dev->phy; u16 value; u16 reg; if (B43_DEBUG) { if (unlikely(abs(control->i) > 16 || abs(control->q) > 16)) { b43dbg(dev->wl, "Invalid LO control pair " "(I: %d, Q: %d)\n", control->i, control->q); dump_stack(); return; } } value = (u8) (control->q); value |= ((u8) (control->i)) << 8; reg = (phy->type == B43_PHYTYPE_B) ? 0x002F : B43_PHY_LO_CTL; b43_phy_write(dev, reg, value);}static int assert_rfatt_and_bbatt(const struct b43_rfatt *rfatt, const struct b43_bbatt *bbatt, struct b43_wldev *dev){ int err = 0; /* Check the attenuation values against the LO control array sizes. */ if (unlikely(rfatt->att >= B43_NR_RF)) { b43err(dev->wl, "rfatt(%u) >= size of LO array\n", rfatt->att); err = -EINVAL; } if (unlikely(bbatt->att >= B43_NR_BB)) { b43err(dev->wl, "bbatt(%u) >= size of LO array\n", bbatt->att); err = -EINVAL; } return err;}#if !B43_CALIB_ALL_LOCTLSstaticstruct b43_loctl *b43_get_lo_g_ctl_nopadmix(struct b43_wldev *dev, const struct b43_rfatt *rfatt, const struct b43_bbatt *bbatt){ struct b43_phy *phy = &dev->phy; struct b43_txpower_lo_control *lo = phy->lo_control; if (assert_rfatt_and_bbatt(rfatt, bbatt, dev)) return &(lo->no_padmix[0][0]); /* Just prevent a crash */ return &(lo->no_padmix[bbatt->att][rfatt->att]);}#endif /* !B43_CALIB_ALL_LOCTLS */struct b43_loctl *b43_get_lo_g_ctl(struct b43_wldev *dev, const struct b43_rfatt *rfatt, const struct b43_bbatt *bbatt){ struct b43_phy *phy = &dev->phy; struct b43_txpower_lo_control *lo = phy->lo_control; if (assert_rfatt_and_bbatt(rfatt, bbatt, dev)) return &(lo->no_padmix[0][0]); /* Just prevent a crash */ if (rfatt->with_padmix) return &(lo->with_padmix[bbatt->att][rfatt->att]); return &(lo->no_padmix[bbatt->att][rfatt->att]);}/* Call a function for every possible LO control value-pair. */static void b43_call_for_each_loctl(struct b43_wldev *dev, void (*func) (struct b43_wldev *, struct b43_loctl *)){ struct b43_phy *phy = &dev->phy; struct b43_txpower_lo_control *ctl = phy->lo_control; int i, j; for (i = 0; i < B43_NR_BB; i++) { for (j = 0; j < B43_NR_RF; j++) func(dev, &(ctl->with_padmix[i][j])); } for (i = 0; i < B43_NR_BB; i++) { for (j = 0; j < B43_NR_RF; j++) func(dev, &(ctl->no_padmix[i][j])); }}static u16 lo_b_r15_loop(struct b43_wldev *dev){ int i; u16 ret = 0; for (i = 0; i < 10; i++) { b43_phy_write(dev, 0x0015, 0xAFA0); udelay(1); b43_phy_write(dev, 0x0015, 0xEFA0); udelay(10); b43_phy_write(dev, 0x0015, 0xFFA0); udelay(40); ret += b43_phy_read(dev, 0x002C); } return ret;}void b43_lo_b_measure(struct b43_wldev *dev){ struct b43_phy *phy = &dev->phy; u16 regstack[12] = { 0 }; u16 mls; u16 fval; int i, j; regstack[0] = b43_phy_read(dev, 0x0015); regstack[1] = b43_radio_read16(dev, 0x0052) & 0xFFF0; if (phy->radio_ver == 0x2053) { regstack[2] = b43_phy_read(dev, 0x000A); regstack[3] = b43_phy_read(dev, 0x002A); regstack[4] = b43_phy_read(dev, 0x0035); regstack[5] = b43_phy_read(dev, 0x0003); regstack[6] = b43_phy_read(dev, 0x0001); regstack[7] = b43_phy_read(dev, 0x0030); regstack[8] = b43_radio_read16(dev, 0x0043); regstack[9] = b43_radio_read16(dev, 0x007A); regstack[10] = b43_read16(dev, 0x03EC); regstack[11] = b43_radio_read16(dev, 0x0052) & 0x00F0; b43_phy_write(dev, 0x0030, 0x00FF); b43_write16(dev, 0x03EC, 0x3F3F); b43_phy_write(dev, 0x0035, regstack[4] & 0xFF7F); b43_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0); } b43_phy_write(dev, 0x0015, 0xB000); b43_phy_write(dev, 0x002B, 0x0004); if (phy->radio_ver == 0x2053) { b43_phy_write(dev, 0x002B, 0x0203); b43_phy_write(dev, 0x002A, 0x08A3); } phy->minlowsig[0] = 0xFFFF; for (i = 0; i < 4; i++) { b43_radio_write16(dev, 0x0052, regstack[1] | i); lo_b_r15_loop(dev); } for (i = 0; i < 10; i++) { b43_radio_write16(dev, 0x0052, regstack[1] | i); mls = lo_b_r15_loop(dev) / 10; if (mls < phy->minlowsig[0]) { phy->minlowsig[0] = mls; phy->minlowsigpos[0] = i; } } b43_radio_write16(dev, 0x0052, regstack[1] | phy->minlowsigpos[0]); phy->minlowsig[1] = 0xFFFF; for (i = -4; i < 5; i += 2) { for (j = -4; j < 5; j += 2) { if (j < 0) fval = (0x0100 * i) + j + 0x0100; else fval = (0x0100 * i) + j; b43_phy_write(dev, 0x002F, fval); mls = lo_b_r15_loop(dev) / 10; if (mls < phy->minlowsig[1]) { phy->minlowsig[1] = mls; phy->minlowsigpos[1] = fval; } } } phy->minlowsigpos[1] += 0x0101; b43_phy_write(dev, 0x002F, phy->minlowsigpos[1]); if (phy->radio_ver == 0x2053) { b43_phy_write(dev, 0x000A, regstack[2]); b43_phy_write(dev, 0x002A, regstack[3]); b43_phy_write(dev, 0x0035, regstack[4]); b43_phy_write(dev, 0x0003, regstack[5]); b43_phy_write(dev, 0x0001, regstack[6]); b43_phy_write(dev, 0x0030, regstack[7]); b43_radio_write16(dev, 0x0043, regstack[8]); b43_radio_write16(dev, 0x007A, regstack[9]); b43_radio_write16(dev, 0x0052, (b43_radio_read16(dev, 0x0052) & 0x000F) | regstack[11]); b43_write16(dev, 0x03EC, regstack[10]); } b43_phy_write(dev, 0x0015, regstack[0]);}static u16 lo_measure_feedthrough(struct b43_wldev *dev, u16 lna, u16 pga, u16 trsw_rx){ struct b43_phy *phy = &dev->phy; u16 rfover; u16 feedthrough; if (phy->gmode) { lna <<= B43_PHY_RFOVERVAL_LNA_SHIFT; pga <<= B43_PHY_RFOVERVAL_PGA_SHIFT; B43_WARN_ON(lna & ~B43_PHY_RFOVERVAL_LNA); B43_WARN_ON(pga & ~B43_PHY_RFOVERVAL_PGA);/*FIXME This assertion fails B43_WARN_ON(trsw_rx & ~(B43_PHY_RFOVERVAL_TRSWRX | B43_PHY_RFOVERVAL_BW));*/ trsw_rx &= (B43_PHY_RFOVERVAL_TRSWRX | B43_PHY_RFOVERVAL_BW); /* Construct the RF Override Value */ rfover = B43_PHY_RFOVERVAL_UNK; rfover |= pga; rfover |= lna; rfover |= trsw_rx; if ((dev->dev->bus->sprom.r1.boardflags_lo & B43_BFL_EXTLNA) && phy->rev > 6) rfover |= B43_PHY_RFOVERVAL_EXTLNA; b43_phy_write(dev, B43_PHY_PGACTL, 0xE300); b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); udelay(10); rfover |= B43_PHY_RFOVERVAL_BW_LBW; b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); udelay(10); rfover |= B43_PHY_RFOVERVAL_BW_LPF; b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover); udelay(10); b43_phy_write(dev, B43_PHY_PGACTL, 0xF300); } else { pga |= B43_PHY_PGACTL_UNKNOWN; b43_phy_write(dev, B43_PHY_PGACTL, pga); udelay(10); pga |= B43_PHY_PGACTL_LOWBANDW; b43_phy_write(dev, B43_PHY_PGACTL, pga); udelay(10); pga |= B43_PHY_PGACTL_LPF; b43_phy_write(dev, B43_PHY_PGACTL, pga); } udelay(21); feedthrough = b43_phy_read(dev, B43_PHY_LO_LEAKAGE); /* This is a good place to check if we need to relax a bit, * as this is the main function called regularly * in the LO calibration. */ cond_resched(); return feedthrough;}/* TXCTL Register and Value Table. * Returns the "TXCTL Register". * "value" is the "TXCTL Value". * "pad_mix_gain" is the PAD Mixer Gain. */static u16 lo_txctl_register_table(struct b43_wldev *dev, u16 * value, u16 * pad_mix_gain){ struct b43_phy *phy = &dev->phy; u16 reg, v, padmix; if (phy->type == B43_PHYTYPE_B) { v = 0x30; if (phy->radio_rev <= 5) { reg = 0x43; padmix = 0; } else { reg = 0x52; padmix = 5; } } else { if (phy->rev >= 2 && phy->radio_rev == 8) { reg = 0x43; v = 0x10; padmix = 2; } else { reg = 0x52; v = 0x30; padmix = 5; } } if (value) *value = v; if (pad_mix_gain) *pad_mix_gain = padmix; return reg;}static void lo_measure_txctl_values(struct b43_wldev *dev){ struct b43_phy *phy = &dev->phy; struct b43_txpower_lo_control *lo = phy->lo_control; u16 reg, mask; u16 trsw_rx, pga; u16 radio_pctl_reg; static const u8 tx_bias_values[] = { 0x09, 0x08, 0x0A, 0x01, 0x00, 0x02, 0x05, 0x04, 0x06, }; static const u8 tx_magn_values[] = { 0x70, 0x40, }; if (!has_loopback_gain(phy)) { radio_pctl_reg = 6; trsw_rx = 2; pga = 0; } else { int lb_gain; /* Loopback gain (in dB) */ trsw_rx = 0; lb_gain = phy->max_lb_gain / 2; if (lb_gain > 10) { radio_pctl_reg = 0; pga = abs(10 - lb_gain) / 6; pga = limit_value(pga, 0, 15); } else { int cmp_val; int tmp; pga = 0; cmp_val = 0x24; if ((phy->rev >= 2) && (phy->radio_ver == 0x2050) && (phy->radio_rev == 8)) cmp_val = 0x3C; tmp = lb_gain; if ((10 - lb_gain) < cmp_val) tmp = (10 - lb_gain); if (tmp < 0) tmp += 6; else tmp += 3; cmp_val /= 4; tmp /= 4; if (tmp >= cmp_val) radio_pctl_reg = cmp_val; else radio_pctl_reg = tmp; } } b43_radio_write16(dev, 0x43, (b43_radio_read16(dev, 0x43) & 0xFFF0) | radio_pctl_reg); b43_phy_set_baseband_attenuation(dev, 2); reg = lo_txctl_register_table(dev, &mask, NULL); mask = ~mask; b43_radio_write16(dev, reg, b43_radio_read16(dev, reg) & mask); if (has_tx_magnification(phy)) { int i, j; int feedthrough; int min_feedth = 0xFFFF; u8 tx_magn, tx_bias; for (i = 0; i < ARRAY_SIZE(tx_magn_values); i++) { tx_magn = tx_magn_values[i]; b43_radio_write16(dev, 0x52, (b43_radio_read16(dev, 0x52) & 0xFF0F) | tx_magn); for (j = 0; j < ARRAY_SIZE(tx_bias_values); j++) { tx_bias = tx_bias_values[j]; b43_radio_write16(dev, 0x52, (b43_radio_read16(dev, 0x52) & 0xFFF0) | tx_bias); feedthrough = lo_measure_feedthrough(dev, 0, pga, trsw_rx);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -