Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/broadcom/b43/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 95 kB image not shown  

Quelle  phy_lp.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*

  Broadcom B43 wireless driver
  IEEE 802.11a/g LP-PHY driver

  Copyright (c) 2008-2009 Michael Buesch <m@bues.ch>
  Copyright (c) 2009 Gábor Stefanik <netrolller.3d@gmail.com>


*/


#include <linux/cordic.h>
#include <linux/slab.h>

#include "b43.h"
#include "main.h"
#include "phy_lp.h"
#include "phy_common.h"
#include "tables_lpphy.h"


static inline u16 channel2freq_lp(u8 channel)
{
 if (channel < 14)
  return (2407 + 5 * channel);
 else if (channel == 14)
  return 2484;
 else if (channel < 184)
  return (5000 + 5 * channel);
 else
  return (4000 + 5 * channel);
}

static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev)
{
 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
  return 1;
 return 36;
}

static int b43_lpphy_op_allocate(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy;

 lpphy = kzalloc(sizeof(*lpphy), GFP_KERNEL);
 if (!lpphy)
  return -ENOMEM;
 dev->phy.lp = lpphy;

 return 0;
}

static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev)
{
 struct b43_phy *phy = &dev->phy;
 struct b43_phy_lp *lpphy = phy->lp;

 memset(lpphy, 0, sizeof(*lpphy));
 lpphy->antenna = B43_ANTENNA_DEFAULT;

 //TODO
}

static void b43_lpphy_op_free(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;

 kfree(lpphy);
 dev->phy.lp = NULL;
}

/* https://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */
static void lpphy_read_band_sprom(struct b43_wldev *dev)
{
 struct ssb_sprom *sprom = dev->dev->bus_sprom;
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u16 cckpo, maxpwr;
 u32 ofdmpo;
 int i;

 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
  lpphy->tx_isolation_med_band = sprom->tri2g;
  lpphy->bx_arch = sprom->bxa2g;
  lpphy->rx_pwr_offset = sprom->rxpo2g;
  lpphy->rssi_vf = sprom->rssismf2g;
  lpphy->rssi_vc = sprom->rssismc2g;
  lpphy->rssi_gs = sprom->rssisav2g;
  lpphy->txpa[0] = sprom->pa0b0;
  lpphy->txpa[1] = sprom->pa0b1;
  lpphy->txpa[2] = sprom->pa0b2;
  maxpwr = sprom->maxpwr_bg;
  lpphy->max_tx_pwr_med_band = maxpwr;
  cckpo = sprom->cck2gpo;
  if (cckpo) {
   ofdmpo = sprom->ofdm2gpo;
   for (i = 0; i < 4; i++) {
    lpphy->tx_max_rate[i] =
     maxpwr - (ofdmpo & 0xF) * 2;
    ofdmpo >>= 4;
   }
   ofdmpo = sprom->ofdm2gpo;
   for (i = 4; i < 15; i++) {
    lpphy->tx_max_rate[i] =
     maxpwr - (ofdmpo & 0xF) * 2;
    ofdmpo >>= 4;
   }
  } else {
   u8 opo = sprom->opo;
   for (i = 0; i < 4; i++)
    lpphy->tx_max_rate[i] = maxpwr;
   for (i = 4; i < 15; i++)
    lpphy->tx_max_rate[i] = maxpwr - opo;
  }
 } else { /* 5GHz */
  lpphy->tx_isolation_low_band = sprom->tri5gl;
  lpphy->tx_isolation_med_band = sprom->tri5g;
  lpphy->tx_isolation_hi_band = sprom->tri5gh;
  lpphy->bx_arch = sprom->bxa5g;
  lpphy->rx_pwr_offset = sprom->rxpo5g;
  lpphy->rssi_vf = sprom->rssismf5g;
  lpphy->rssi_vc = sprom->rssismc5g;
  lpphy->rssi_gs = sprom->rssisav5g;
  lpphy->txpa[0] = sprom->pa1b0;
  lpphy->txpa[1] = sprom->pa1b1;
  lpphy->txpa[2] = sprom->pa1b2;
  lpphy->txpal[0] = sprom->pa1lob0;
  lpphy->txpal[1] = sprom->pa1lob1;
  lpphy->txpal[2] = sprom->pa1lob2;
  lpphy->txpah[0] = sprom->pa1hib0;
  lpphy->txpah[1] = sprom->pa1hib1;
  lpphy->txpah[2] = sprom->pa1hib2;
  maxpwr = sprom->maxpwr_al;
  ofdmpo = sprom->ofdm5glpo;
  lpphy->max_tx_pwr_low_band = maxpwr;
  for (i = 4; i < 12; i++) {
   lpphy->tx_max_ratel[i] = maxpwr - (ofdmpo & 0xF) * 2;
   ofdmpo >>= 4;
  }
  maxpwr = sprom->maxpwr_a;
  ofdmpo = sprom->ofdm5gpo;
  lpphy->max_tx_pwr_med_band = maxpwr;
  for (i = 4; i < 12; i++) {
   lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2;
   ofdmpo >>= 4;
  }
  maxpwr = sprom->maxpwr_ah;
  ofdmpo = sprom->ofdm5ghpo;
  lpphy->max_tx_pwr_hi_band = maxpwr;
  for (i = 4; i < 12; i++) {
   lpphy->tx_max_rateh[i] = maxpwr - (ofdmpo & 0xF) * 2;
   ofdmpo >>= 4;
  }
 }
}

static void lpphy_adjust_gain_table(struct b43_wldev *dev, u32 freq)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u16 temp[3];
 u16 isolation;

 B43_WARN_ON(dev->phy.rev >= 2);

 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
  isolation = lpphy->tx_isolation_med_band;
 else if (freq <= 5320)
  isolation = lpphy->tx_isolation_low_band;
 else if (freq <= 5700)
  isolation = lpphy->tx_isolation_med_band;
 else
  isolation = lpphy->tx_isolation_hi_band;

 temp[0] = ((isolation - 26) / 12) << 12;
 temp[1] = temp[0] + 0x1000;
 temp[2] = temp[0] + 0x2000;

 b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), 3, temp);
 b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), 3, temp);
}

static void lpphy_table_init(struct b43_wldev *dev)
{
 u32 freq = channel2freq_lp(b43_lpphy_op_get_default_chan(dev));

 if (dev->phy.rev < 2)
  lpphy_rev0_1_table_init(dev);
 else
  lpphy_rev2plus_table_init(dev);

 lpphy_init_tx_gain_table(dev);

 if (dev->phy.rev < 2)
  lpphy_adjust_gain_table(dev, freq);
}

static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
{
 struct ssb_bus *bus = dev->dev->sdev->bus;
 struct ssb_sprom *sprom = dev->dev->bus_sprom;
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u16 tmp, tmp2;

 b43_phy_mask(dev, B43_LPPHY_AFE_DAC_CTL, 0xF7FF);
 b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0);
 b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0);
 b43_phy_set(dev, B43_LPPHY_AFE_DAC_CTL, 0x0004);
 b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x0078);
 b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800);
 b43_phy_write(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x0016);
 b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_0, 0xFFF8, 0x0004);
 b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5400);
 b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2400);
 b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100);
 b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0x0006);
 b43_phy_mask(dev, B43_LPPHY_RX_RADIO_CTL, 0xFFFE);
 b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x0005);
 b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0x0180);
 b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x3C00);
 b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFFF0, 0x0005);
 b43_phy_maskset(dev, B43_LPPHY_GAIN_MISMATCH_LIMIT, 0xFFC0, 0x001A);
 b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0x00B3);
 b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00);
 b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB,
   0xFF00, lpphy->rx_pwr_offset);
 if ((sprom->boardflags_lo & B43_BFL_FEM) &&
    ((b43_current_band(dev->wl) == NL80211_BAND_5GHZ) ||
    (sprom->boardflags_hi & B43_BFH_PAREF))) {
  ssb_pmu_set_ldo_voltage(&bus->chipco, LDO_PAREF, 0x28);
  ssb_pmu_set_ldo_paref(&bus->chipco, true);
  if (dev->phy.rev == 0) {
   b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT,
     0xFFCF, 0x0010);
  }
  b43_lptab_write(dev, B43_LPTAB16(11, 7), 60);
 } else {
  ssb_pmu_set_ldo_paref(&bus->chipco, false);
  b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT,
    0xFFCF, 0x0020);
  b43_lptab_write(dev, B43_LPTAB16(11, 7), 100);
 }
 tmp = lpphy->rssi_vf | lpphy->rssi_vc << 4 | 0xA000;
 b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, tmp);
 if (sprom->boardflags_hi & B43_BFH_RSSIINV)
  b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x0AAA);
 else
  b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x02AA);
 b43_lptab_write(dev, B43_LPTAB16(11, 1), 24);
 b43_phy_maskset(dev, B43_LPPHY_RX_RADIO_CTL,
   0xFFF9, (lpphy->bx_arch << 1));
 if (dev->phy.rev == 1 &&
    (sprom->boardflags_hi & B43_BFH_FEM_BT)) {
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0x3F00, 0x0900);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0400);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0B00);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xC0FF, 0x0900);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xC0FF, 0x0B00);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xC0FF, 0x0900);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xC0FF, 0x0B00);
 } else if (b43_current_band(dev->wl) == NL80211_BAND_5GHZ ||
     (dev->dev->board_type == SSB_BOARD_BU4312) ||
     (dev->phy.rev == 0 && (sprom->boardflags_lo & B43_BFL_FEM))) {
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0001);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0400);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0001);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0500);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0800);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0A00);
 } else if (dev->phy.rev == 1 ||
    (sprom->boardflags_lo & B43_BFL_FEM)) {
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0004);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0800);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0004);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0C00);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0100);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0300);
 } else {
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0900);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0006);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0500);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0006);
  b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0700);
 }
 if (dev->phy.rev == 1 && (sprom->boardflags_hi & B43_BFH_PAREF)) {
  b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_5, B43_LPPHY_TR_LOOKUP_1);
  b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_6, B43_LPPHY_TR_LOOKUP_2);
  b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_7, B43_LPPHY_TR_LOOKUP_3);
  b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_8, B43_LPPHY_TR_LOOKUP_4);
 }
 if ((sprom->boardflags_hi & B43_BFH_FEM_BT) &&
     (dev->dev->chip_id == 0x5354) &&
     (dev->dev->chip_pkg == SSB_CHIPPACK_BCM4712S)) {
  b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0006);
  b43_phy_write(dev, B43_LPPHY_GPIO_SELECT, 0x0005);
  b43_phy_write(dev, B43_LPPHY_GPIO_OUTEN, 0xFFFF);
  //FIXME the Broadcom driver caches & delays this HF write!
  b43_hf_write(dev, b43_hf_read(dev) | B43_HF_PR45960W);
 }
 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
  b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x8000);
  b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0040);
  b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0xA400);
  b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0x0B00);
  b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x0007);
  b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFF8, 0x0003);
  b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFC7, 0x0020);
  b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF);
 } else { /* 5GHz */
  b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0x7FFF);
  b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFBF);
 }
 if (dev->phy.rev == 1) {
  tmp = b43_phy_read(dev, B43_LPPHY_CLIPCTRTHRESH);
  tmp2 = (tmp & 0x03E0) >> 5;
  tmp2 |= tmp2 << 5;
  b43_phy_write(dev, B43_LPPHY_4C3, tmp2);
  tmp = b43_phy_read(dev, B43_LPPHY_GAINDIRECTMISMATCH);
  tmp2 = (tmp & 0x1F00) >> 8;
  tmp2 |= tmp2 << 5;
  b43_phy_write(dev, B43_LPPHY_4C4, tmp2);
  tmp = b43_phy_read(dev, B43_LPPHY_VERYLOWGAINDB);
  tmp2 = tmp & 0x00FF;
  tmp2 |= tmp << 8;
  b43_phy_write(dev, B43_LPPHY_4C5, tmp2);
 }
}

static void lpphy_save_dig_flt_state(struct b43_wldev *dev)
{
 static const u16 addr[] = {
  B43_PHY_OFDM(0xC1),
  B43_PHY_OFDM(0xC2),
  B43_PHY_OFDM(0xC3),
  B43_PHY_OFDM(0xC4),
  B43_PHY_OFDM(0xC5),
  B43_PHY_OFDM(0xC6),
  B43_PHY_OFDM(0xC7),
  B43_PHY_OFDM(0xC8),
  B43_PHY_OFDM(0xCF),
 };

 static const u16 coefs[] = {
  0xDE5E, 0xE832, 0xE331, 0x4D26,
  0x0026, 0x1420, 0x0020, 0xFE08,
  0x0008,
 };

 struct b43_phy_lp *lpphy = dev->phy.lp;
 int i;

 for (i = 0; i < ARRAY_SIZE(addr); i++) {
  lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]);
  b43_phy_write(dev, addr[i], coefs[i]);
 }
}

static void lpphy_restore_dig_flt_state(struct b43_wldev *dev)
{
 static const u16 addr[] = {
  B43_PHY_OFDM(0xC1),
  B43_PHY_OFDM(0xC2),
  B43_PHY_OFDM(0xC3),
  B43_PHY_OFDM(0xC4),
  B43_PHY_OFDM(0xC5),
  B43_PHY_OFDM(0xC6),
  B43_PHY_OFDM(0xC7),
  B43_PHY_OFDM(0xC8),
  B43_PHY_OFDM(0xCF),
 };

 struct b43_phy_lp *lpphy = dev->phy.lp;
 int i;

 for (i = 0; i < ARRAY_SIZE(addr); i++)
  b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]);
}

static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;

 b43_phy_write(dev, B43_LPPHY_AFE_DAC_CTL, 0x50);
 b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0x8800);
 b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0);
 b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0);
 b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0);
 b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0);
 b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10);
 b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4);
 b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200);
 b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F);
 b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40);
 b43_phy_maskset(dev, B43_LPPHY_PREAMBLECONFIRMTO, 0xFF00, 0x2);
 b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000);
 b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000);
 b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1);
 if (dev->dev->board_rev >= 0x18) {
  b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC);
  b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14);
 } else {
  b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10);
 }
 b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4);
 b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100);
 b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48);
 b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0xFF00, 0x46);
 b43_phy_maskset(dev, B43_PHY_OFDM(0xE4), 0xFF00, 0x10);
 b43_phy_maskset(dev, B43_LPPHY_PWR_THRESH1, 0xFFF0, 0x9);
 b43_phy_mask(dev, B43_LPPHY_GAINDIRECTMISMATCH, ~0xF);
 b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5500);
 b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0xA0);
 b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xE0FF, 0x300);
 b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2A00);
 if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
  b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100);
  b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xA);
 } else {
  b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x1E00);
  b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xD);
 }
 b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFFE0, 0x1F);
 b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC);
 b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0xFF00, 0x19);
 b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0x03FF, 0x3C00);
 b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFC1F, 0x3E0);
 b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC);
 b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0x00FF, 0x1900);
 b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800);
 b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12);
 b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000);

 if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
  b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0);
  b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40);
 }

 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
  b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40);
  b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0xB00);
  b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x6);
  b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0x9D00);
  b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0xFF00, 0xA1);
  b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF);
 } else /* 5GHz */
  b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x40);

 b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0xB3);
 b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00);
 b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, 0xFF00, lpphy->rx_pwr_offset);
 b43_phy_set(dev, B43_LPPHY_RESET_CTL, 0x44);
 b43_phy_write(dev, B43_LPPHY_RESET_CTL, 0x80);
 b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, 0xA954);
 b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1,
        0x2000 | ((u16)lpphy->rssi_gs << 10) |
        ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf);

 if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
  b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C);
  b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800);
  b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400);
 }

 lpphy_save_dig_flt_state(dev);
}

static void lpphy_baseband_init(struct b43_wldev *dev)
{
 lpphy_table_init(dev);
 if (dev->phy.rev >= 2)
  lpphy_baseband_rev2plus_init(dev);
 else
  lpphy_baseband_rev0_1_init(dev);
}

struct b2062_freqdata {
 u16 freq;
 u8 data[6];
};

/* Initialize the 2062 radio. */
static void lpphy_2062_init(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 struct ssb_bus *bus = dev->dev->sdev->bus;
 u32 crystalfreq, tmp, ref;
 unsigned int i;
 const struct b2062_freqdata *fd = NULL;

 static const struct b2062_freqdata freqdata_tab[] = {
  { .freq = 12000, .data[0] =  6, .data[1] =  6, .data[2] =  6,
     .data[3] =  6, .data[4] = 10, .data[5] =  6, },
  { .freq = 13000, .data[0] =  4, .data[1] =  4, .data[2] =  4,
     .data[3] =  4, .data[4] = 11, .data[5] =  7, },
  { .freq = 14400, .data[0] =  3, .data[1] =  3, .data[2] =  3,
     .data[3] =  3, .data[4] = 12, .data[5] =  7, },
  { .freq = 16200, .data[0] =  3, .data[1] =  3, .data[2] =  3,
     .data[3] =  3, .data[4] = 13, .data[5] =  8, },
  { .freq = 18000, .data[0] =  2, .data[1] =  2, .data[2] =  2,
     .data[3] =  2, .data[4] = 14, .data[5] =  8, },
  { .freq = 19200, .data[0] =  1, .data[1] =  1, .data[2] =  1,
     .data[3] =  1, .data[4] = 14, .data[5] =  9, },
 };

 b2062_upload_init_table(dev);

 b43_radio_write(dev, B2062_N_TX_CTL3, 0);
 b43_radio_write(dev, B2062_N_TX_CTL4, 0);
 b43_radio_write(dev, B2062_N_TX_CTL5, 0);
 b43_radio_write(dev, B2062_N_TX_CTL6, 0);
 b43_radio_write(dev, B2062_N_PDN_CTL0, 0x40);
 b43_radio_write(dev, B2062_N_PDN_CTL0, 0);
 b43_radio_write(dev, B2062_N_CALIB_TS, 0x10);
 b43_radio_write(dev, B2062_N_CALIB_TS, 0);
 if (dev->phy.rev > 0) {
  b43_radio_write(dev, B2062_S_BG_CTL1,
   (b43_radio_read(dev, B2062_N_COMM2) >> 1) | 0x80);
 }
 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
  b43_radio_set(dev, B2062_N_TSSI_CTL0, 0x1);
 else
  b43_radio_mask(dev, B2062_N_TSSI_CTL0, ~0x1);

 /* Get the crystal freq, in Hz. */
 crystalfreq = bus->chipco.pmu.crystalfreq * 1000;

 B43_WARN_ON(!(bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU));
 B43_WARN_ON(crystalfreq == 0);

 if (crystalfreq <= 30000000) {
  lpphy->pdiv = 1;
  b43_radio_mask(dev, B2062_S_RFPLL_CTL1, 0xFFFB);
 } else {
  lpphy->pdiv = 2;
  b43_radio_set(dev, B2062_S_RFPLL_CTL1, 0x4);
 }

 tmp = (((800000000 * lpphy->pdiv + crystalfreq) /
       (2 * crystalfreq)) - 8) & 0xFF;
 b43_radio_write(dev, B2062_S_RFPLL_CTL7, tmp);

 tmp = (((100 * crystalfreq + 16000000 * lpphy->pdiv) /
       (32000000 * lpphy->pdiv)) - 1) & 0xFF;
 b43_radio_write(dev, B2062_S_RFPLL_CTL18, tmp);

 tmp = (((2 * crystalfreq + 1000000 * lpphy->pdiv) /
       (2000000 * lpphy->pdiv)) - 1) & 0xFF;
 b43_radio_write(dev, B2062_S_RFPLL_CTL19, tmp);

 ref = (1000 * lpphy->pdiv + 2 * crystalfreq) / (2000 * lpphy->pdiv);
 ref &= 0xFFFF;
 for (i = 0; i < ARRAY_SIZE(freqdata_tab); i++) {
  if (ref < freqdata_tab[i].freq) {
   fd = &freqdata_tab[i];
   break;
  }
 }
 if (!fd)
  fd = &freqdata_tab[ARRAY_SIZE(freqdata_tab) - 1];
 b43dbg(dev->wl, "b2062: Using crystal tab entry %u kHz.\n",
        fd->freq); /* FIXME: Keep this printk until the code is fully debugged. */

 b43_radio_write(dev, B2062_S_RFPLL_CTL8,
   ((u16)(fd->data[1]) << 4) | fd->data[0]);
 b43_radio_write(dev, B2062_S_RFPLL_CTL9,
   ((u16)(fd->data[3]) << 4) | fd->data[2]);
 b43_radio_write(dev, B2062_S_RFPLL_CTL10, fd->data[4]);
 b43_radio_write(dev, B2062_S_RFPLL_CTL11, fd->data[5]);
}

/* Initialize the 2063 radio. */
static void lpphy_2063_init(struct b43_wldev *dev)
{
 b2063_upload_init_table(dev);
 b43_radio_write(dev, B2063_LOGEN_SP5, 0);
 b43_radio_set(dev, B2063_COMM8, 0x38);
 b43_radio_write(dev, B2063_REG_SP1, 0x56);
 b43_radio_mask(dev, B2063_RX_BB_CTL2, ~0x2);
 b43_radio_write(dev, B2063_PA_SP7, 0);
 b43_radio_write(dev, B2063_TX_RF_SP6, 0x20);
 b43_radio_write(dev, B2063_TX_RF_SP9, 0x40);
 if (dev->phy.rev == 2) {
  b43_radio_write(dev, B2063_PA_SP3, 0xa0);
  b43_radio_write(dev, B2063_PA_SP4, 0xa0);
  b43_radio_write(dev, B2063_PA_SP2, 0x18);
 } else {
  b43_radio_write(dev, B2063_PA_SP3, 0x20);
  b43_radio_write(dev, B2063_PA_SP2, 0x20);
 }
}

struct lpphy_stx_table_entry {
 u16 phy_offset;
 u16 phy_shift;
 u16 rf_addr;
 u16 rf_shift;
 u16 mask;
};

static const struct lpphy_stx_table_entry lpphy_stx_table[] = {
 { .phy_offset = 2, .phy_shift = 6, .rf_addr = 0x3d, .rf_shift = 3, .mask = 0x01, },
 { .phy_offset = 1, .phy_shift = 12, .rf_addr = 0x4c, .rf_shift = 1, .mask = 0x01, },
 { .phy_offset = 1, .phy_shift = 8, .rf_addr = 0x50, .rf_shift = 0, .mask = 0x7f, },
 { .phy_offset = 0, .phy_shift = 8, .rf_addr = 0x44, .rf_shift = 0, .mask = 0xff, },
 { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4a, .rf_shift = 0, .mask = 0xff, },
 { .phy_offset = 0, .phy_shift = 4, .rf_addr = 0x4d, .rf_shift = 0, .mask = 0xff, },
 { .phy_offset = 1, .phy_shift = 4, .rf_addr = 0x4e, .rf_shift = 0, .mask = 0xff, },
 { .phy_offset = 0, .phy_shift = 12, .rf_addr = 0x4f, .rf_shift = 0, .mask = 0x0f, },
 { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4f, .rf_shift = 4, .mask = 0x0f, },
 { .phy_offset = 3, .phy_shift = 0, .rf_addr = 0x49, .rf_shift = 0, .mask = 0x0f, },
 { .phy_offset = 4, .phy_shift = 3, .rf_addr = 0x46, .rf_shift = 4, .mask = 0x07, },
 { .phy_offset = 3, .phy_shift = 15, .rf_addr = 0x46, .rf_shift = 0, .mask = 0x01, },
 { .phy_offset = 4, .phy_shift = 0, .rf_addr = 0x46, .rf_shift = 1, .mask = 0x07, },
 { .phy_offset = 3, .phy_shift = 8, .rf_addr = 0x48, .rf_shift = 4, .mask = 0x07, },
 { .phy_offset = 3, .phy_shift = 11, .rf_addr = 0x48, .rf_shift = 0, .mask = 0x0f, },
 { .phy_offset = 3, .phy_shift = 4, .rf_addr = 0x49, .rf_shift = 4, .mask = 0x0f, },
 { .phy_offset = 2, .phy_shift = 15, .rf_addr = 0x45, .rf_shift = 0, .mask = 0x01, },
 { .phy_offset = 5, .phy_shift = 13, .rf_addr = 0x52, .rf_shift = 4, .mask = 0x07, },
 { .phy_offset = 6, .phy_shift = 0, .rf_addr = 0x52, .rf_shift = 7, .mask = 0x01, },
 { .phy_offset = 5, .phy_shift = 3, .rf_addr = 0x41, .rf_shift = 5, .mask = 0x07, },
 { .phy_offset = 5, .phy_shift = 6, .rf_addr = 0x41, .rf_shift = 0, .mask = 0x0f, },
 { .phy_offset = 5, .phy_shift = 10, .rf_addr = 0x42, .rf_shift = 5, .mask = 0x07, },
 { .phy_offset = 4, .phy_shift = 15, .rf_addr = 0x42, .rf_shift = 0, .mask = 0x01, },
 { .phy_offset = 5, .phy_shift = 0, .rf_addr = 0x42, .rf_shift = 1, .mask = 0x07, },
 { .phy_offset = 4, .phy_shift = 11, .rf_addr = 0x43, .rf_shift = 4, .mask = 0x0f, },
 { .phy_offset = 4, .phy_shift = 7, .rf_addr = 0x43, .rf_shift = 0, .mask = 0x0f, },
 { .phy_offset = 4, .phy_shift = 6, .rf_addr = 0x45, .rf_shift = 1, .mask = 0x01, },
 { .phy_offset = 2, .phy_shift = 7, .rf_addr = 0x40, .rf_shift = 4, .mask = 0x0f, },
 { .phy_offset = 2, .phy_shift = 11, .rf_addr = 0x40, .rf_shift = 0, .mask = 0x0f, },
};

static void lpphy_sync_stx(struct b43_wldev *dev)
{
 const struct lpphy_stx_table_entry *e;
 unsigned int i;
 u16 tmp;

 for (i = 0; i < ARRAY_SIZE(lpphy_stx_table); i++) {
  e = &lpphy_stx_table[i];
  tmp = b43_radio_read(dev, e->rf_addr);
  tmp >>= e->rf_shift;
  tmp <<= e->phy_shift;
  b43_phy_maskset(dev, B43_PHY_OFDM(0xF2 + e->phy_offset),
    ~(e->mask << e->phy_shift), tmp);
 }
}

static void lpphy_radio_init(struct b43_wldev *dev)
{
 /* The radio is attached through the 4wire bus. */
 b43_phy_set(dev, B43_LPPHY_FOURWIRE_CTL, 0x2);
 udelay(1);
 b43_phy_mask(dev, B43_LPPHY_FOURWIRE_CTL, 0xFFFD);
 udelay(1);

 if (dev->phy.radio_ver == 0x2062) {
  lpphy_2062_init(dev);
 } else {
  lpphy_2063_init(dev);
  lpphy_sync_stx(dev);
  b43_phy_write(dev, B43_PHY_OFDM(0xF0), 0x5F80);
  b43_phy_write(dev, B43_PHY_OFDM(0xF1), 0);
  if (dev->dev->chip_id == 0x4325) {
   // TODO SSB PMU recalibration
  }
 }
}

struct lpphy_iq_est { u32 iq_prod, i_pwr, q_pwr; };

static void lpphy_set_rc_cap(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;

 u8 rc_cap = (lpphy->rc_cap & 0x1F) >> 1;

 if (dev->phy.rev == 1) //FIXME check channel 14!
  rc_cap = min_t(u8, rc_cap + 5, 15);

 b43_radio_write(dev, B2062_N_RXBB_CALIB2,
   max_t(u8, lpphy->rc_cap - 4, 0x80));
 b43_radio_write(dev, B2062_N_TX_CTL_A, rc_cap | 0x80);
 b43_radio_write(dev, B2062_S_RXG_CNT16,
   ((lpphy->rc_cap & 0x1F) >> 2) | 0x80);
}

static u8 lpphy_get_bb_mult(struct b43_wldev *dev)
{
 return (b43_lptab_read(dev, B43_LPTAB16(0, 87)) & 0xFF00) >> 8;
}

static void lpphy_set_bb_mult(struct b43_wldev *dev, u8 bb_mult)
{
 b43_lptab_write(dev, B43_LPTAB16(0, 87), (u16)bb_mult << 8);
}

static void lpphy_set_deaf(struct b43_wldev *dev, bool user)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;

 if (user)
  lpphy->crs_usr_disable = true;
 else
  lpphy->crs_sys_disable = true;
 b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x80);
}

static void lpphy_clear_deaf(struct b43_wldev *dev, bool user)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;

 if (user)
  lpphy->crs_usr_disable = false;
 else
  lpphy->crs_sys_disable = false;

 if (!lpphy->crs_usr_disable && !lpphy->crs_sys_disable) {
  if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
   b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL,
     0xFF1F, 0x60);
  else
   b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL,
     0xFF1F, 0x20);
 }
}

static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx)
{
 u16 trsw = (tx << 1) | rx;
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
}

static void lpphy_disable_crs(struct b43_wldev *dev, bool user)
{
 lpphy_set_deaf(dev, user);
 lpphy_set_trsw_over(dev, falsetrue);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x10);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFDF);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFBF);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x7);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x38);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x100);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFDFF);
 b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL0, 0);
 b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL1, 1);
 b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL2, 0x20);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xF7FF);
 b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0);
 b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, 0x45AF);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0x3FF);
}

static void lpphy_restore_crs(struct b43_wldev *dev, bool user)
{
 lpphy_clear_deaf(dev, user);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFF80);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFC00);
}

struct lpphy_tx_gains { u16 gm, pga, pad, dac; };

static void lpphy_disable_rx_gain_override(struct b43_wldev *dev)
{
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF);
 if (dev->phy.rev >= 2) {
  b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
  if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
   b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF);
   b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7);
  }
 } else {
  b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF);
 }
}

static void lpphy_enable_rx_gain_override(struct b43_wldev *dev)
{
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
 if (dev->phy.rev >= 2) {
  b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
  if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
   b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400);
   b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8);
  }
 } else {
  b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200);
 }
}

static void lpphy_disable_tx_gain_override(struct b43_wldev *dev)
{
 if (dev->phy.rev < 2)
  b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
 else {
  b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F);
  b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF);
 }
 b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF);
}

static void lpphy_enable_tx_gain_override(struct b43_wldev *dev)
{
 if (dev->phy.rev < 2)
  b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
 else {
  b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80);
  b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000);
 }
 b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40);
}

static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev)
{
 struct lpphy_tx_gains gains;
 u16 tmp;

 gains.dac = (b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0x380) >> 7;
 if (dev->phy.rev < 2) {
  tmp = b43_phy_read(dev,
       B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL) & 0x7FF;
  gains.gm = tmp & 0x0007;
  gains.pga = (tmp & 0x0078) >> 3;
  gains.pad = (tmp & 0x780) >> 7;
 } else {
  tmp = b43_phy_read(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL);
  gains.pad = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0xFF;
  gains.gm = tmp & 0xFF;
  gains.pga = (tmp >> 8) & 0xFF;
 }

 return gains;
}

static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac)
{
 u16 ctl = b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0xC7F;
 ctl |= dac << 7;
 b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl);
}

static u16 lpphy_get_pa_gain(struct b43_wldev *dev)
{
 return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F;
}

static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain)
{
 b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6);
 b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8);
}

static void lpphy_set_tx_gains(struct b43_wldev *dev,
          struct lpphy_tx_gains gains)
{
 u16 rf_gain, pa_gain;

 if (dev->phy.rev < 2) {
  rf_gain = (gains.pad << 7) | (gains.pga << 3) | gains.gm;
  b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
    0xF800, rf_gain);
 } else {
  pa_gain = lpphy_get_pa_gain(dev);
  b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
         (gains.pga << 8) | gains.gm);
  /*
 * SPEC FIXME The spec calls for (pa_gain << 8) here, but that
 * conflicts with the spec for set_pa_gain! Vendor driver bug?
 */

  b43_phy_maskset(dev, B43_PHY_OFDM(0xFB),
    0x8000, gains.pad | (pa_gain << 6));
  b43_phy_write(dev, B43_PHY_OFDM(0xFC),
         (gains.pga << 8) | gains.gm);
  b43_phy_maskset(dev, B43_PHY_OFDM(0xFD),
    0x8000, gains.pad | (pa_gain << 8));
 }
 lpphy_set_dac_gain(dev, gains.dac);
 lpphy_enable_tx_gain_override(dev);
}

static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain)
{
 u16 trsw = gain & 0x1;
 u16 lna = (gain & 0xFFFC) | ((gain & 0xC) >> 2);
 u16 ext_lna = (gain & 2) >> 1;

 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw);
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
   0xFBFF, ext_lna << 10);
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
   0xF7FF, ext_lna << 11);
 b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, lna);
}

static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain)
{
 u16 low_gain = gain & 0xFFFF;
 u16 high_gain = (gain >> 16) & 0xF;
 u16 ext_lna = (gain >> 21) & 0x1;
 u16 trsw = ~(gain >> 20) & 0x1;
 u16 tmp;

 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw);
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
   0xFDFF, ext_lna << 9);
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
   0xFBFF, ext_lna << 10);
 b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, low_gain);
 b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF0, high_gain);
 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
  tmp = (gain >> 2) & 0x3;
  b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
    0xE7FF, tmp<<11);
  b43_phy_maskset(dev, B43_PHY_OFDM(0xE6), 0xFFE7, tmp << 3);
 }
}

static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain)
{
 if (dev->phy.rev < 2)
  lpphy_rev0_1_set_rx_gain(dev, gain);
 else
  lpphy_rev2plus_set_rx_gain(dev, gain);
 lpphy_enable_rx_gain_override(dev);
}

static void lpphy_set_rx_gain_by_index(struct b43_wldev *dev, u16 idx)
{
 u32 gain = b43_lptab_read(dev, B43_LPTAB16(12, idx));
 lpphy_set_rx_gain(dev, gain);
}

static void lpphy_stop_ddfs(struct b43_wldev *dev)
{
 b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFD);
 b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xFFDF);
}

static void lpphy_run_ddfs(struct b43_wldev *dev, int i_on, int q_on,
      int incr1, int incr2, int scale_idx)
{
 lpphy_stop_ddfs(dev);
 b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0xFF80);
 b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0x80FF);
 b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0xFF80, incr1);
 b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0x80FF, incr2 << 8);
 b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF7, i_on << 3);
 b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFEF, q_on << 4);
 b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFF9F, scale_idx << 5);
 b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFB);
 b43_phy_set(dev, B43_LPPHY_AFE_DDFS, 0x2);
 b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x20);
}

static bool lpphy_rx_iq_est(struct b43_wldev *dev, u16 samples, u8 time,
      struct lpphy_iq_est *iq_est)
{
 int i;

 b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFF7);
 b43_phy_write(dev, B43_LPPHY_IQ_NUM_SMPLS_ADDR, samples);
 b43_phy_maskset(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFF00, time);
 b43_phy_mask(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFEFF);
 b43_phy_set(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0x200);

 for (i = 0; i < 500; i++) {
  if (!(b43_phy_read(dev,
    B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200))
   break;
  msleep(1);
 }

 if ((b43_phy_read(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) {
  b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8);
  return false;
 }

 iq_est->iq_prod = b43_phy_read(dev, B43_LPPHY_IQ_ACC_HI_ADDR);
 iq_est->iq_prod <<= 16;
 iq_est->iq_prod |= b43_phy_read(dev, B43_LPPHY_IQ_ACC_LO_ADDR);

 iq_est->i_pwr = b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR);
 iq_est->i_pwr <<= 16;
 iq_est->i_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR);

 iq_est->q_pwr = b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR);
 iq_est->q_pwr <<= 16;
 iq_est->q_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR);

 b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8);
 return true;
}

static int lpphy_loopback(struct b43_wldev *dev)
{
 struct lpphy_iq_est iq_est;
 int i, index = -1;
 u32 tmp;

 memset(&iq_est, 0, sizeof(iq_est));

 lpphy_set_trsw_over(dev, truetrue);
 b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1);
 b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x8);
 b43_radio_write(dev, B2062_N_TX_CTL_A, 0x80);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x80);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x80);
 for (i = 0; i < 32; i++) {
  lpphy_set_rx_gain_by_index(dev, i);
  lpphy_run_ddfs(dev, 1, 1, 5, 5, 0);
  if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est)))
   continue;
  tmp = (iq_est.i_pwr + iq_est.q_pwr) / 1000;
  if ((tmp > 4000) && (tmp < 10000)) {
   index = i;
   break;
  }
 }
 lpphy_stop_ddfs(dev);
 return index;
}

/* Fixed-point division algorithm using only integer math. */
static u32 lpphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision)
{
 u32 quotient, remainder;

 if (divisor == 0)
  return 0;

 quotient = dividend / divisor;
 remainder = dividend % divisor;

 while (precision > 0) {
  quotient <<= 1;
  if (remainder << 1 >= divisor) {
   quotient++;
   remainder = (remainder << 1) - divisor;
  }
  precision--;
 }

 if (remainder << 1 >= divisor)
  quotient++;

 return quotient;
}

/* Read the TX power control mode from hardware. */
static void lpphy_read_tx_pctl_mode_from_hardware(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u16 ctl;

 ctl = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_CMD);
 switch (ctl & B43_LPPHY_TX_PWR_CTL_CMD_MODE) {
 case B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF:
  lpphy->txpctl_mode = B43_LPPHY_TXPCTL_OFF;
  break;
 case B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW:
  lpphy->txpctl_mode = B43_LPPHY_TXPCTL_SW;
  break;
 case B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW:
  lpphy->txpctl_mode = B43_LPPHY_TXPCTL_HW;
  break;
 default:
  lpphy->txpctl_mode = B43_LPPHY_TXPCTL_UNKNOWN;
  B43_WARN_ON(1);
  break;
 }
}

/* Set the TX power control mode in hardware. */
static void lpphy_write_tx_pctl_mode_to_hardware(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u16 ctl;

 switch (lpphy->txpctl_mode) {
 case B43_LPPHY_TXPCTL_OFF:
  ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF;
  break;
 case B43_LPPHY_TXPCTL_HW:
  ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW;
  break;
 case B43_LPPHY_TXPCTL_SW:
  ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW;
  break;
 default:
  ctl = 0;
  B43_WARN_ON(1);
 }
 b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
   ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, ctl);
}

static void lpphy_set_tx_power_control(struct b43_wldev *dev,
           enum b43_lpphy_txpctl_mode mode)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 enum b43_lpphy_txpctl_mode oldmode;

 lpphy_read_tx_pctl_mode_from_hardware(dev);
 oldmode = lpphy->txpctl_mode;
 if (oldmode == mode)
  return;
 lpphy->txpctl_mode = mode;

 if (oldmode == B43_LPPHY_TXPCTL_HW) {
  //TODO Update TX Power NPT
  //TODO Clear all TX Power offsets
 } else {
  if (mode == B43_LPPHY_TXPCTL_HW) {
   //TODO Recalculate target TX power
   b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
     0xFF80, lpphy->tssi_idx);
   b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM,
     0x8FFF, ((u16)lpphy->tssi_npt << 16));
   //TODO Set "TSSI Transmit Count" variable to total transmitted frame count
   lpphy_disable_tx_gain_override(dev);
   lpphy->tx_pwr_idx_over = -1;
  }
 }
 if (dev->phy.rev >= 2) {
  if (mode == B43_LPPHY_TXPCTL_HW)
   b43_phy_set(dev, B43_PHY_OFDM(0xD0), 0x2);
  else
   b43_phy_mask(dev, B43_PHY_OFDM(0xD0), 0xFFFD);
 }
 lpphy_write_tx_pctl_mode_to_hardware(dev);
}

static int b43_lpphy_op_switch_channel(struct b43_wldev *dev,
           unsigned int new_channel);

static void lpphy_rev0_1_rc_calib(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 struct lpphy_iq_est iq_est;
 struct lpphy_tx_gains tx_gains;
 static const u32 ideal_pwr_table[21] = {
  0x10000, 0x10557, 0x10e2d, 0x113e0, 0x10f22, 0x0ff64,
  0x0eda2, 0x0e5d4, 0x0efd1, 0x0fbe8, 0x0b7b8, 0x04b35,
  0x01a5e, 0x00a0b, 0x00444, 0x001fd, 0x000ff, 0x00088,
  0x0004c, 0x0002c, 0x0001a,
 };
 bool old_txg_ovr;
 u8 old_bbmult;
 u16 old_rf_ovr, old_rf_ovrval, old_afe_ovr, old_afe_ovrval,
     old_rf2_ovr, old_rf2_ovrval, old_phy_ctl;
 enum b43_lpphy_txpctl_mode old_txpctl;
 u32 normal_pwr, ideal_pwr, mean_sq_pwr, tmp = 0, mean_sq_pwr_min = 0;
 int loopback, i, j, inner_sum, err;

 memset(&iq_est, 0, sizeof(iq_est));

 err = b43_lpphy_op_switch_channel(dev, 7);
 if (err) {
  b43dbg(dev->wl,
         "RC calib: Failed to switch to channel 7, error = %d\n",
         err);
 }
 old_txg_ovr = !!(b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40);
 old_bbmult = lpphy_get_bb_mult(dev);
 if (old_txg_ovr)
  tx_gains = lpphy_get_tx_gains(dev);
 old_rf_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_0);
 old_rf_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_VAL_0);
 old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR);
 old_afe_ovrval = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVRVAL);
 old_rf2_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2);
 old_rf2_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2_VAL);
 old_phy_ctl = b43_phy_read(dev, B43_LPPHY_LP_PHY_CTL);
 lpphy_read_tx_pctl_mode_from_hardware(dev);
 old_txpctl = lpphy->txpctl_mode;

 lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
 lpphy_disable_crs(dev, true);
 loopback = lpphy_loopback(dev);
 if (loopback == -1)
  goto finish;
 lpphy_set_rx_gain_by_index(dev, loopback);
 b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFFBF, 0x40);
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFF8, 0x1);
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFC7, 0x8);
 b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F, 0xC0);
 for (i = 128; i <= 159; i++) {
  b43_radio_write(dev, B2062_N_RXBB_CALIB2, i);
  inner_sum = 0;
  for (j = 5; j <= 25; j++) {
   lpphy_run_ddfs(dev, 1, 1, j, j, 0);
   if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est)))
    goto finish;
   mean_sq_pwr = iq_est.i_pwr + iq_est.q_pwr;
   if (j == 5)
    tmp = mean_sq_pwr;
   ideal_pwr = ((ideal_pwr_table[j-5] >> 3) + 1) >> 1;
   normal_pwr = lpphy_qdiv_roundup(mean_sq_pwr, tmp, 12);
   mean_sq_pwr = ideal_pwr - normal_pwr;
   mean_sq_pwr *= mean_sq_pwr;
   inner_sum += mean_sq_pwr;
   if ((i == 128) || (inner_sum < mean_sq_pwr_min)) {
    lpphy->rc_cap = i;
    mean_sq_pwr_min = inner_sum;
   }
  }
 }
 lpphy_stop_ddfs(dev);

finish:
 lpphy_restore_crs(dev, true);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, old_rf_ovrval);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, old_rf_ovr);
 b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, old_afe_ovrval);
 b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, old_afe_ovr);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, old_rf2_ovrval);
 b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, old_rf2_ovr);
 b43_phy_write(dev, B43_LPPHY_LP_PHY_CTL, old_phy_ctl);

 lpphy_set_bb_mult(dev, old_bbmult);
 if (old_txg_ovr) {
  /*
 * SPEC FIXME: The specs say "get_tx_gains" here, which is
 * illogical. According to lwfinger, vendor driver v4.150.10.5
 * has a Set here, while v4.174.64.19 has a Get - regression in
 * the vendor driver? This should be tested this once the code
 * is testable.
 */

  lpphy_set_tx_gains(dev, tx_gains);
 }
 lpphy_set_tx_power_control(dev, old_txpctl);
 if (lpphy->rc_cap)
  lpphy_set_rc_cap(dev);
}

static void lpphy_rev2plus_rc_calib(struct b43_wldev *dev)
{
 struct ssb_bus *bus = dev->dev->sdev->bus;
 u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
 u8 tmp = b43_radio_read(dev, B2063_RX_BB_SP8) & 0xFF;
 int i;

 b43_radio_write(dev, B2063_RX_BB_SP8, 0x0);
 b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E);
 b43_radio_mask(dev, B2063_PLL_SP1, 0xF7);
 b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C);
 b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x15);
 b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x70);
 b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x52);
 b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1);
 b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7D);

 for (i = 0; i < 10000; i++) {
  if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)
   break;
  msleep(1);
 }

 if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2))
  b43_radio_write(dev, B2063_RX_BB_SP8, tmp);

 tmp = b43_radio_read(dev, B2063_TX_BB_SP3) & 0xFF;

 b43_radio_write(dev, B2063_TX_BB_SP3, 0x0);
 b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E);
 b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C);
 b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x55);
 b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x76);

 if (crystal_freq == 24000000) {
  b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0xFC);
  b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x0);
 } else {
  b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x13);
  b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1);
 }

 b43_radio_write(dev, B2063_PA_SP7, 0x7D);

 for (i = 0; i < 10000; i++) {
  if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)
   break;
  msleep(1);
 }

 if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2))
  b43_radio_write(dev, B2063_TX_BB_SP3, tmp);

 b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E);
}

static void lpphy_calibrate_rc(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;

 if (dev->phy.rev >= 2) {
  lpphy_rev2plus_rc_calib(dev);
 } else if (!lpphy->rc_cap) {
  if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
   lpphy_rev0_1_rc_calib(dev);
 } else {
  lpphy_set_rc_cap(dev);
 }
}

static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
{
 if (dev->phy.rev >= 2)
  return// rev2+ doesn't support antenna diversity

 if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1))
  return;

 b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP);

 b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2);
 b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1);

 b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP);

 dev->phy.lp->antenna = antenna;
}

static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b)
{
 u16 tmp[2];

 tmp[0] = a;
 tmp[1] = b;
 b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp);
}

static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 struct lpphy_tx_gains gains;
 u32 iq_comp, tx_gain, coeff, rf_power;

 lpphy->tx_pwr_idx_over = index;
 lpphy_read_tx_pctl_mode_from_hardware(dev);
 if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF)
  lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW);
 if (dev->phy.rev >= 2) {
  iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320));
  tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192));
  gains.pad = (tx_gain >> 16) & 0xFF;
  gains.gm = tx_gain & 0xFF;
  gains.pga = (tx_gain >> 8) & 0xFF;
  gains.dac = (iq_comp >> 28) & 0xFF;
  lpphy_set_tx_gains(dev, gains);
 } else {
  iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320));
  tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192));
  b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
    0xF800, (tx_gain >> 4) & 0x7FFF);
  lpphy_set_dac_gain(dev, tx_gain & 0x7);
  lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F);
 }
 lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF);
 lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF);
 if (dev->phy.rev >= 2) {
  coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448));
 } else {
  coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448));
 }
 b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF);
 if (dev->phy.rev >= 2) {
  rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576));
  b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00,
    rf_power & 0xFFFF);//SPEC FIXME mask & set != 0
 }
 lpphy_enable_tx_gain_override(dev);
}

static void lpphy_btcoex_override(struct b43_wldev *dev)
{
 b43_write16(dev, B43_MMIO_BTCOEX_CTL, 0x3);
 b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF);
}

static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
      bool blocked)
{
 //TODO check MAC control register
 if (blocked) {
  if (dev->phy.rev >= 2) {
   b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF);
   b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
   b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF);
   b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF);
   b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808);
  } else {
   b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF);
   b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
   b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF);
   b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018);
  }
 } else {
  b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF);
  if (dev->phy.rev >= 2)
   b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7);
  else
   b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7);
 }
}

/* This was previously called lpphy_japan_filter */
static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter!

 if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific?
  b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9);
  if ((dev->phy.rev == 1) && (lpphy->rc_cap))
   lpphy_set_rc_cap(dev);
 } else {
  b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F);
 }
}

static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode)
{
 if (mode != TSSI_MUX_EXT) {
  b43_radio_set(dev, B2063_PA_SP1, 0x2);
  b43_phy_set(dev, B43_PHY_OFDM(0xF3), 0x1000);
  b43_radio_write(dev, B2063_PA_CTL10, 0x51);
  if (mode == TSSI_MUX_POSTPA) {
   b43_radio_mask(dev, B2063_PA_SP1, 0xFFFE);
   b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFC7);
  } else {
   b43_radio_maskset(dev, B2063_PA_SP1, 0xFFFE, 0x1);
   b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVRVAL,
     0xFFC7, 0x20);
  }
 } else {
  B43_WARN_ON(1);
 }
}

static void lpphy_tx_pctl_init_hw(struct b43_wldev *dev)
{
 u16 tmp;
 int i;

 //SPEC TODO Call LP PHY Clear TX Power offsets
 for (i = 0; i < 64; i++) {
  if (dev->phy.rev >= 2)
   b43_lptab_write(dev, B43_LPTAB32(7, i + 1), i);
  else
   b43_lptab_write(dev, B43_LPTAB32(10, i + 1), i);
 }

 b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xFF00, 0xFF);
 b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, 0x5000);
 b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0xFFC0, 0x1F);
 if (dev->phy.rev < 2) {
  b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xEFFF);
  b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xDFFF, 0x2000);
 } else {
  b43_phy_mask(dev, B43_PHY_OFDM(0x103), 0xFFFE);
  b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFFB, 0x4);
  b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFEF, 0x10);
  b43_radio_maskset(dev, B2063_IQ_CALIB_CTL2, 0xF3, 0x1);
  lpphy_set_tssi_mux(dev, TSSI_MUX_POSTPA);
 }
 b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0x7FFF, 0x8000);
 b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xFF);
 b43_phy_write(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xA);
 b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
   ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF,
   B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF);
 b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xF8FF);
 b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
   ~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF,
   B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW);

 if (dev->phy.rev < 2) {
  b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF, 0x1000);
  b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xEFFF);
 } else {
  lpphy_set_tx_power_by_index(dev, 0x7F);
 }

 b43_dummy_transmission(dev, truetrue);

 tmp = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_STAT);
 if (tmp & 0x8000) {
  b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI,
    0xFFC0, (tmp & 0xFF) - 32);
 }

 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF);

 // (SPEC?) TODO Set "Target TX frequency" variable to 0
 // SPEC FIXME "Set BB Multiplier to 0xE000" impossible - bb_mult is u8!
}

static void lpphy_tx_pctl_init_sw(struct b43_wldev *dev)
{
 struct lpphy_tx_gains gains;

 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
  gains.gm = 4;
  gains.pad = 12;
  gains.pga = 12;
  gains.dac = 0;
 } else {
  gains.gm = 7;
  gains.pad = 14;
  gains.pga = 15;
  gains.dac = 0;
 }
 lpphy_set_tx_gains(dev, gains);
 lpphy_set_bb_mult(dev, 150);
}

/* Initialize TX power control */
static void lpphy_tx_pctl_init(struct b43_wldev *dev)
{
 if (0/*FIXME HWPCTL capable */) {
  lpphy_tx_pctl_init_hw(dev);
 } else { /* This device is only software TX power control capable. */
  lpphy_tx_pctl_init_sw(dev);
 }
}

static void lpphy_pr41573_workaround(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u32 *saved_tab;
 const unsigned int saved_tab_size = 256;
 enum b43_lpphy_txpctl_mode txpctl_mode;
 s8 tx_pwr_idx_over;
 u16 tssi_npt, tssi_idx;

 saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL);
 if (!saved_tab) {
  b43err(dev->wl, "PR41573 failed. Out of memory!\n");
  return;
 }

 lpphy_read_tx_pctl_mode_from_hardware(dev);
 txpctl_mode = lpphy->txpctl_mode;
 tx_pwr_idx_over = lpphy->tx_pwr_idx_over;
 tssi_npt = lpphy->tssi_npt;
 tssi_idx = lpphy->tssi_idx;

 if (dev->phy.rev < 2) {
  b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140),
        saved_tab_size, saved_tab);
 } else {
  b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
        saved_tab_size, saved_tab);
 }
 //FIXME PHY reset
 lpphy_table_init(dev); //FIXME is table init needed?
 lpphy_baseband_init(dev);
 lpphy_tx_pctl_init(dev);
 b43_lpphy_op_software_rfkill(dev, false);
 lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
 if (dev->phy.rev < 2) {
  b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140),
         saved_tab_size, saved_tab);
 } else {
  b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140),
         saved_tab_size, saved_tab);
 }
 b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel);
 lpphy->tssi_npt = tssi_npt;
 lpphy->tssi_idx = tssi_idx;
 lpphy_set_analog_filter(dev, lpphy->channel);
 if (tx_pwr_idx_over != -1)
  lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over);
 if (lpphy->rc_cap)
  lpphy_set_rc_cap(dev);
 b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna);
 lpphy_set_tx_power_control(dev, txpctl_mode);
 kfree(saved_tab);
}

struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; };

static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = {
 { .chan = 1, .c1 = -66, .c0 = 15, },
 { .chan = 2, .c1 = -66, .c0 = 15, },
 { .chan = 3, .c1 = -66, .c0 = 15, },
 { .chan = 4, .c1 = -66, .c0 = 15, },
 { .chan = 5, .c1 = -66, .c0 = 15, },
 { .chan = 6, .c1 = -66, .c0 = 15, },
 { .chan = 7, .c1 = -66, .c0 = 14, },
 { .chan = 8, .c1 = -66, .c0 = 14, },
 { .chan = 9, .c1 = -66, .c0 = 14, },
 { .chan = 10, .c1 = -66, .c0 = 14, },
 { .chan = 11, .c1 = -66, .c0 = 14, },
 { .chan = 12, .c1 = -66, .c0 = 13, },
 { .chan = 13, .c1 = -66, .c0 = 13, },
 { .chan = 14, .c1 = -66, .c0 = 13, },
};

static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = {
 { .chan = 1, .c1 = -64, .c0 = 13, },
 { .chan = 2, .c1 = -64, .c0 = 13, },
 { .chan = 3, .c1 = -64, .c0 = 13, },
 { .chan = 4, .c1 = -64, .c0 = 13, },
 { .chan = 5, .c1 = -64, .c0 = 12, },
 { .chan = 6, .c1 = -64, .c0 = 12, },
 { .chan = 7, .c1 = -64, .c0 = 12, },
 { .chan = 8, .c1 = -64, .c0 = 12, },
 { .chan = 9, .c1 = -64, .c0 = 12, },
 { .chan = 10, .c1 = -64, .c0 = 11, },
 { .chan = 11, .c1 = -64, .c0 = 11, },
 { .chan = 12, .c1 = -64, .c0 = 11, },
 { .chan = 13, .c1 = -64, .c0 = 11, },
 { .chan = 14, .c1 = -64, .c0 = 10, },
 { .chan = 34, .c1 = -62, .c0 = 24, },
 { .chan = 38, .c1 = -62, .c0 = 24, },
 { .chan = 42, .c1 = -62, .c0 = 24, },
 { .chan = 46, .c1 = -62, .c0 = 23, },
 { .chan = 36, .c1 = -62, .c0 = 24, },
 { .chan = 40, .c1 = -62, .c0 = 24, },
 { .chan = 44, .c1 = -62, .c0 = 23, },
 { .chan = 48, .c1 = -62, .c0 = 23, },
 { .chan = 52, .c1 = -62, .c0 = 23, },
 { .chan = 56, .c1 = -62, .c0 = 22, },
 { .chan = 60, .c1 = -62, .c0 = 22, },
 { .chan = 64, .c1 = -62, .c0 = 22, },
 { .chan = 100, .c1 = -62, .c0 = 16, },
 { .chan = 104, .c1 = -62, .c0 = 16, },
 { .chan = 108, .c1 = -62, .c0 = 15, },
 { .chan = 112, .c1 = -62, .c0 = 14, },
 { .chan = 116, .c1 = -62, .c0 = 14, },
 { .chan = 120, .c1 = -62, .c0 = 13, },
 { .chan = 124, .c1 = -62, .c0 = 12, },
 { .chan = 128, .c1 = -62, .c0 = 12, },
 { .chan = 132, .c1 = -62, .c0 = 12, },
 { .chan = 136, .c1 = -62, .c0 = 11, },
 { .chan = 140, .c1 = -62, .c0 = 10, },
 { .chan = 149, .c1 = -61, .c0 = 9, },
 { .chan = 153, .c1 = -61, .c0 = 9, },
 { .chan = 157, .c1 = -61, .c0 = 9, },
 { .chan = 161, .c1 = -61, .c0 = 8, },
 { .chan = 165, .c1 = -61, .c0 = 8, },
 { .chan = 184, .c1 = -62, .c0 = 25, },
 { .chan = 188, .c1 = -62, .c0 = 25, },
 { .chan = 192, .c1 = -62, .c0 = 25, },
 { .chan = 196, .c1 = -62, .c0 = 25, },
 { .chan = 200, .c1 = -62, .c0 = 25, },
 { .chan = 204, .c1 = -62, .c0 = 25, },
 { .chan = 208, .c1 = -62, .c0 = 25, },
 { .chan = 212, .c1 = -62, .c0 = 25, },
 { .chan = 216, .c1 = -62, .c0 = 26, },
};

static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = {
 .chan = 0,
 .c1 = -64,
 .c0 = 0,
};

static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples)
{
 struct lpphy_iq_est iq_est;
 u16 c0, c1;
 int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret;

 c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S);
 c0 = c1 >> 8;
 c1 |= 0xFF;

 b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0);
 b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF);

 ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est);
 if (!ret)
  goto out;

 prod = iq_est.iq_prod;
 ipwr = iq_est.i_pwr;
 qpwr = iq_est.q_pwr;

 if (ipwr + qpwr < 2) {
  ret = 0;
  goto out;
 }

 prod_msb = fls(abs(prod));
 q_msb = fls(abs(qpwr));
 tmp1 = prod_msb - 20;

 if (tmp1 >= 0) {
  tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) /
   (ipwr >> tmp1);
 } else {
  tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) /
   (ipwr << -tmp1);
 }

 tmp2 = q_msb - 11;

 if (tmp2 >= 0)
  tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2);
 else
  tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2);

 tmp4 -= tmp3 * tmp3;
 tmp4 = -int_sqrt(tmp4);

 c0 = tmp3 >> 3;
 c1 = tmp4 >> 4;

out:
 b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1);
 b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8);
 return ret;
}

static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops,
         u16 wait)
{
 b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL,
   0xFFC0, samples - 1);
 if (loops != 0xFFFF)
  loops--;
 b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops);
 b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6);
 b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1);
}

//SPEC FIXME what does a negative freq mean?
static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 u16 buf[64];
 int i, samples = 0, theta = 0;
 int rotation = (((36 * freq) / 20) << 16) / 100;
 struct cordic_iq sample;

 lpphy->tx_tone_freq = freq;

 if (freq) {
  /* Find i for which abs(freq) integrally divides 20000 * i */
  for (i = 1; samples * abs(freq) != 20000 * i; i++) {
   samples = (20000 * i) / abs(freq);
   if(B43_WARN_ON(samples > 63))
    return;
  }
 } else {
  samples = 2;
 }

 for (i = 0; i < samples; i++) {
  sample = cordic_calc_iq(CORDIC_FIXED(theta));
  theta += rotation;
  buf[i] = CORDIC_FLOAT((sample.i * max) & 0xFF) << 8;
  buf[i] |= CORDIC_FLOAT((sample.q * max) & 0xFF);
 }

 b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf);

 lpphy_run_samples(dev, samples, 0xFFFF, 0);
}

static void lpphy_stop_tx_tone(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 int i;

 lpphy->tx_tone_freq = 0;

 b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000);
 for (i = 0; i < 31; i++) {
  if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1))
   break;
  udelay(100);
 }
}


static void lpphy_papd_cal_txpwr(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 struct lpphy_tx_gains oldgains;
 int old_txpctl, old_afe_ovr, old_rf, old_bbmult;

 lpphy_read_tx_pctl_mode_from_hardware(dev);
 old_txpctl = lpphy->txpctl_mode;
 old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;
 if (old_afe_ovr)
  oldgains = lpphy_get_tx_gains(dev);
 old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF;
 old_bbmult = lpphy_get_bb_mult(dev);

 lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);

 if (old_afe_ovr)
  lpphy_set_tx_gains(dev, oldgains);
 lpphy_set_bb_mult(dev, old_bbmult);
 lpphy_set_tx_power_control(dev, old_txpctl);
 b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf);
}

static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx,
       bool rx, bool pa, struct lpphy_tx_gains *gains)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 const struct lpphy_rx_iq_comp *iqcomp = NULL;
 struct lpphy_tx_gains nogains, oldgains;
 u16 tmp;
 int i, ret;

 memset(&nogains, 0, sizeof(nogains));
 memset(&oldgains, 0, sizeof(oldgains));

 if (dev->dev->chip_id == 0x5354) {
  for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) {
   if (lpphy_5354_iq_table[i].chan == lpphy->channel) {
    iqcomp = &lpphy_5354_iq_table[i];
   }
  }
 } else if (dev->phy.rev >= 2) {
  iqcomp = &lpphy_rev2plus_iq_comp;
 } else {
  for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) {
   if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) {
    iqcomp = &lpphy_rev0_1_iq_table[i];
   }
  }
 }

 if (B43_WARN_ON(!iqcomp))
  return 0;

 b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1);
 b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S,
   0x00FF, iqcomp->c0 << 8);

 if (noise) {
  tx = true;
  rx = false;
  pa = false;
 }

 lpphy_set_trsw_over(dev, tx, rx);

 if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
  b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
  b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
    0xFFF7, pa << 3);
 } else {
  b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20);
  b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
    0xFFDF, pa << 5);
 }

 tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;

 if (noise)
  lpphy_set_rx_gain(dev, 0x2D5D);
 else {
  if (tmp)
   oldgains = lpphy_get_tx_gains(dev);
  if (!gains)
   gains = &nogains;
  lpphy_set_tx_gains(dev, *gains);
 }

 b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
 b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
 b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800);
 lpphy_set_deaf(dev, false);
 if (noise)
  ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0);
 else {
  lpphy_start_tx_tone(dev, 4000, 100);
  ret = lpphy_calc_rx_iq_comp(dev, 0x4000);
  lpphy_stop_tx_tone(dev);
 }
 lpphy_clear_deaf(dev, false);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7);
 b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF);
 if (!noise) {
  if (tmp)
   lpphy_set_tx_gains(dev, oldgains);
  else
   lpphy_disable_tx_gain_override(dev);
 }
 lpphy_disable_rx_gain_override(dev);
 b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
 b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF);
 return ret;
}

static void lpphy_calibration(struct b43_wldev *dev)
{
 struct b43_phy_lp *lpphy = dev->phy.lp;
 enum b43_lpphy_txpctl_mode saved_pctl_mode;
 bool full_cal = false;

 if (lpphy->full_calib_chan != lpphy->channel) {
  full_cal = true;
  lpphy->full_calib_chan = lpphy->channel;
 }

 b43_mac_suspend(dev);

 lpphy_btcoex_override(dev);
 if (dev->phy.rev >= 2)
  lpphy_save_dig_flt_state(dev);
 lpphy_read_tx_pctl_mode_from_hardware(dev);
 saved_pctl_mode = lpphy->txpctl_mode;
 lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
 //TODO Perform transmit power table I/Q LO calibration
 if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF))
  lpphy_pr41573_workaround(dev);
 if ((dev->phy.rev >= 2) && full_cal) {
  lpphy_papd_cal_txpwr(dev);
 }
 lpphy_set_tx_power_control(dev, saved_pctl_mode);
 if (dev->phy.rev >= 2)
  lpphy_restore_dig_flt_state(dev);
 lpphy_rx_iq_cal(dev, truetruefalsefalse, NULL);

 b43_mac_enable(dev);
}

static void b43_lpphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
     u16 set)
{
 b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg);
 b43_write16(dev, B43_MMIO_PHY_DATA,
      (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
}

static u16 b43_lpphy_op_radio_read(struct b43_wldev *dev, u16 reg)
{
 /* Register 1 is a 32-bit register. */
 B43_WARN_ON(reg == 1);
 /* LP-PHY needs a special bit set for read access */
 if (dev->phy.rev < 2) {
  if (reg != 0x4001)
   reg |= 0x100;
 } else
  reg |= 0x200;

 b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg);
 return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
}

static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
{
 /* Register 1 is a 32-bit register. */
 B43_WARN_ON(reg == 1);

 b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg);
 b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
}

struct b206x_channel {
 u8 channel;
 u16 freq;
 u8 data[12];
};

static const struct b206x_channel b2062_chantbl[] = {
 { .channel = 1, .freq = 2412, .data[0] = 0xFF, .data[1] = 0xFF,
   .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
   .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
 { .channel = 2, .freq = 2417, .data[0] = 0xFF, .data[1] = 0xFF,
   .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
   .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
 { .channel = 3, .freq = 2422, .data[0] = 0xFF, .data[1] = 0xFF,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=94 G=95

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.