b43: N-PHY: prepare for rev3+ channel tables
[safe/jmp/linux-2.6] / drivers / net / wireless / b43 / phy_n.c
index 671fbcf..34f80c4 100644 (file)
@@ -64,8 +64,30 @@ enum b43_nphy_rf_sequence {
        B43_RFSEQ_UPDATE_GAINU,
 };
 
+static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
+                                       u8 *events, u8 *delays, u8 length);
 static void b43_nphy_force_rf_sequence(struct b43_wldev *dev,
                                       enum b43_nphy_rf_sequence seq);
+static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
+                                               u16 value, u8 core, bool off);
+static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
+                                               u16 value, u8 core);
+static int nphy_channel_switch(struct b43_wldev *dev, unsigned int channel);
+
+static inline bool b43_empty_chanspec(struct b43_chanspec *chanspec)
+{
+       return !chanspec->channel && !chanspec->sideband &&
+               !chanspec->b_width && !chanspec->b_freq;
+}
+
+static inline bool b43_eq_chanspecs(struct b43_chanspec *chanspec1,
+                                       struct b43_chanspec *chanspec2)
+{
+       return (chanspec1->channel == chanspec2->channel &&
+               chanspec1->sideband == chanspec2->sideband &&
+               chanspec1->b_width == chanspec2->b_width &&
+               chanspec1->b_freq == chanspec2->b_freq);
+}
 
 void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
 {//TODO
@@ -82,34 +104,44 @@ static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev,
 }
 
 static void b43_chantab_radio_upload(struct b43_wldev *dev,
-                                    const struct b43_nphy_channeltab_entry *e)
-{
-       b43_radio_write16(dev, B2055_PLL_REF, e->radio_pll_ref);
-       b43_radio_write16(dev, B2055_RF_PLLMOD0, e->radio_rf_pllmod0);
-       b43_radio_write16(dev, B2055_RF_PLLMOD1, e->radio_rf_pllmod1);
-       b43_radio_write16(dev, B2055_VCO_CAPTAIL, e->radio_vco_captail);
-       b43_radio_write16(dev, B2055_VCO_CAL1, e->radio_vco_cal1);
-       b43_radio_write16(dev, B2055_VCO_CAL2, e->radio_vco_cal2);
-       b43_radio_write16(dev, B2055_PLL_LFC1, e->radio_pll_lfc1);
-       b43_radio_write16(dev, B2055_PLL_LFR1, e->radio_pll_lfr1);
-       b43_radio_write16(dev, B2055_PLL_LFC2, e->radio_pll_lfc2);
-       b43_radio_write16(dev, B2055_LGBUF_CENBUF, e->radio_lgbuf_cenbuf);
-       b43_radio_write16(dev, B2055_LGEN_TUNE1, e->radio_lgen_tune1);
-       b43_radio_write16(dev, B2055_LGEN_TUNE2, e->radio_lgen_tune2);
-       b43_radio_write16(dev, B2055_C1_LGBUF_ATUNE, e->radio_c1_lgbuf_atune);
-       b43_radio_write16(dev, B2055_C1_LGBUF_GTUNE, e->radio_c1_lgbuf_gtune);
-       b43_radio_write16(dev, B2055_C1_RX_RFR1, e->radio_c1_rx_rfr1);
-       b43_radio_write16(dev, B2055_C1_TX_PGAPADTN, e->radio_c1_tx_pgapadtn);
-       b43_radio_write16(dev, B2055_C1_TX_MXBGTRIM, e->radio_c1_tx_mxbgtrim);
-       b43_radio_write16(dev, B2055_C2_LGBUF_ATUNE, e->radio_c2_lgbuf_atune);
-       b43_radio_write16(dev, B2055_C2_LGBUF_GTUNE, e->radio_c2_lgbuf_gtune);
-       b43_radio_write16(dev, B2055_C2_RX_RFR1, e->radio_c2_rx_rfr1);
-       b43_radio_write16(dev, B2055_C2_TX_PGAPADTN, e->radio_c2_tx_pgapadtn);
-       b43_radio_write16(dev, B2055_C2_TX_MXBGTRIM, e->radio_c2_tx_mxbgtrim);
+                               const struct b43_nphy_channeltab_entry_rev2 *e)
+{
+       b43_radio_write(dev, B2055_PLL_REF, e->radio_pll_ref);
+       b43_radio_write(dev, B2055_RF_PLLMOD0, e->radio_rf_pllmod0);
+       b43_radio_write(dev, B2055_RF_PLLMOD1, e->radio_rf_pllmod1);
+       b43_radio_write(dev, B2055_VCO_CAPTAIL, e->radio_vco_captail);
+       b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */
+
+       b43_radio_write(dev, B2055_VCO_CAL1, e->radio_vco_cal1);
+       b43_radio_write(dev, B2055_VCO_CAL2, e->radio_vco_cal2);
+       b43_radio_write(dev, B2055_PLL_LFC1, e->radio_pll_lfc1);
+       b43_radio_write(dev, B2055_PLL_LFR1, e->radio_pll_lfr1);
+       b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */
+
+       b43_radio_write(dev, B2055_PLL_LFC2, e->radio_pll_lfc2);
+       b43_radio_write(dev, B2055_LGBUF_CENBUF, e->radio_lgbuf_cenbuf);
+       b43_radio_write(dev, B2055_LGEN_TUNE1, e->radio_lgen_tune1);
+       b43_radio_write(dev, B2055_LGEN_TUNE2, e->radio_lgen_tune2);
+       b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */
+
+       b43_radio_write(dev, B2055_C1_LGBUF_ATUNE, e->radio_c1_lgbuf_atune);
+       b43_radio_write(dev, B2055_C1_LGBUF_GTUNE, e->radio_c1_lgbuf_gtune);
+       b43_radio_write(dev, B2055_C1_RX_RFR1, e->radio_c1_rx_rfr1);
+       b43_radio_write(dev, B2055_C1_TX_PGAPADTN, e->radio_c1_tx_pgapadtn);
+       b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */
+
+       b43_radio_write(dev, B2055_C1_TX_MXBGTRIM, e->radio_c1_tx_mxbgtrim);
+       b43_radio_write(dev, B2055_C2_LGBUF_ATUNE, e->radio_c2_lgbuf_atune);
+       b43_radio_write(dev, B2055_C2_LGBUF_GTUNE, e->radio_c2_lgbuf_gtune);
+       b43_radio_write(dev, B2055_C2_RX_RFR1, e->radio_c2_rx_rfr1);
+       b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */
+
+       b43_radio_write(dev, B2055_C2_TX_PGAPADTN, e->radio_c2_tx_pgapadtn);
+       b43_radio_write(dev, B2055_C2_TX_MXBGTRIM, e->radio_c2_tx_mxbgtrim);
 }
 
 static void b43_chantab_phy_upload(struct b43_wldev *dev,
-                                  const struct b43_nphy_channeltab_entry *e)
+                                  const struct b43_phy_n_sfo_cfg *e)
 {
        b43_phy_write(dev, B43_NPHY_BW1A, e->phy_bw1a);
        b43_phy_write(dev, B43_NPHY_BW2, e->phy_bw2);
@@ -124,34 +156,20 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
        //TODO
 }
 
-/* Tune the hardware to a new channel. */
-static int nphy_channel_switch(struct b43_wldev *dev, unsigned int channel)
-{
-       const struct b43_nphy_channeltab_entry *tabent;
 
-       tabent = b43_nphy_get_chantabent(dev, channel);
-       if (!tabent)
-               return -ESRCH;
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2055Setup */
+static void b43_radio_2055_setup(struct b43_wldev *dev,
+                               const struct b43_nphy_channeltab_entry_rev2 *e)
+{
+       B43_WARN_ON(dev->phy.rev >= 3);
 
-       //FIXME enable/disable band select upper20 in RXCTL
-       if (0 /*FIXME 5Ghz*/)
-               b43_radio_maskset(dev, B2055_MASTER1, 0xFF8F, 0x20);
-       else
-               b43_radio_maskset(dev, B2055_MASTER1, 0xFF8F, 0x50);
-       b43_chantab_radio_upload(dev, tabent);
+       b43_chantab_radio_upload(dev, e);
        udelay(50);
-       b43_radio_write16(dev, B2055_VCO_CAL10, 5);
-       b43_radio_write16(dev, B2055_VCO_CAL10, 45);
-       b43_radio_write16(dev, B2055_VCO_CAL10, 65);
+       b43_radio_write(dev, B2055_VCO_CAL10, 5);
+       b43_radio_write(dev, B2055_VCO_CAL10, 45);
+       b43_read32(dev, B43_MMIO_MACCTL); /* flush writes */
+       b43_radio_write(dev, B2055_VCO_CAL10, 65);
        udelay(300);
-       if (0 /*FIXME 5Ghz*/)
-               b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ);
-       else
-               b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ);
-       b43_chantab_phy_upload(dev, tabent);
-       b43_nphy_tx_power_fix(dev);
-
-       return 0;
 }
 
 static void b43_radio_init2055_pre(struct b43_wldev *dev)
@@ -167,52 +185,64 @@ static void b43_radio_init2055_pre(struct b43_wldev *dev)
 
 static void b43_radio_init2055_post(struct b43_wldev *dev)
 {
+       struct b43_phy_n *nphy = dev->phy.n;
        struct ssb_sprom *sprom = &(dev->dev->bus->sprom);
        struct ssb_boardinfo *binfo = &(dev->dev->bus->boardinfo);
        int i;
        u16 val;
+       bool workaround = false;
+
+       if (sprom->revision < 4)
+               workaround = (binfo->vendor != PCI_VENDOR_ID_BROADCOM ||
+                               binfo->type != 0x46D ||
+                               binfo->rev < 0x41);
+       else
+               workaround = ((sprom->boardflags_hi & B43_BFH_NOPA) == 0);
 
        b43_radio_mask(dev, B2055_MASTER1, 0xFFF3);
-       msleep(1);
-       if ((sprom->revision != 4) ||
-          !(sprom->boardflags_hi & B43_BFH_RSSIINV)) {
-               if ((binfo->vendor != PCI_VENDOR_ID_BROADCOM) ||
-                   (binfo->type != 0x46D) ||
-                   (binfo->rev < 0x41)) {
-                       b43_radio_mask(dev, B2055_C1_RX_BB_REG, 0x7F);
-                       b43_radio_mask(dev, B2055_C1_RX_BB_REG, 0x7F);
-                       msleep(1);
-               }
+       if (workaround) {
+               b43_radio_mask(dev, B2055_C1_RX_BB_REG, 0x7F);
+               b43_radio_mask(dev, B2055_C2_RX_BB_REG, 0x7F);
        }
-       b43_radio_maskset(dev, B2055_RRCCAL_NOPTSEL, 0x3F, 0x2C);
-       msleep(1);
-       b43_radio_write16(dev, B2055_CAL_MISC, 0x3C);
-       msleep(1);
+       b43_radio_maskset(dev, B2055_RRCCAL_NOPTSEL, 0xFFC0, 0x2C);
+       b43_radio_write(dev, B2055_CAL_MISC, 0x3C);
        b43_radio_mask(dev, B2055_CAL_MISC, 0xFFBE);
-       msleep(1);
        b43_radio_set(dev, B2055_CAL_LPOCTL, 0x80);
-       msleep(1);
        b43_radio_set(dev, B2055_CAL_MISC, 0x1);
        msleep(1);
        b43_radio_set(dev, B2055_CAL_MISC, 0x40);
-       msleep(1);
-       for (i = 0; i < 100; i++) {
-               val = b43_radio_read16(dev, B2055_CAL_COUT2);
-               if (val & 0x80)
+       for (i = 0; i < 200; i++) {
+               val = b43_radio_read(dev, B2055_CAL_COUT2);
+               if (val & 0x80) {
+                       i = 0;
                        break;
+               }
                udelay(10);
        }
-       msleep(1);
+       if (i)
+               b43err(dev->wl, "radio post init timeout\n");
        b43_radio_mask(dev, B2055_CAL_LPOCTL, 0xFF7F);
-       msleep(1);
        nphy_channel_switch(dev, dev->phy.channel);
-       b43_radio_write16(dev, B2055_C1_RX_BB_LPF, 0x9);
-       b43_radio_write16(dev, B2055_C2_RX_BB_LPF, 0x9);
-       b43_radio_write16(dev, B2055_C1_RX_BB_MIDACHP, 0x83);
-       b43_radio_write16(dev, B2055_C2_RX_BB_MIDACHP, 0x83);
+       b43_radio_write(dev, B2055_C1_RX_BB_LPF, 0x9);
+       b43_radio_write(dev, B2055_C2_RX_BB_LPF, 0x9);
+       b43_radio_write(dev, B2055_C1_RX_BB_MIDACHP, 0x83);
+       b43_radio_write(dev, B2055_C2_RX_BB_MIDACHP, 0x83);
+       b43_radio_maskset(dev, B2055_C1_LNA_GAINBST, 0xFFF8, 0x6);
+       b43_radio_maskset(dev, B2055_C2_LNA_GAINBST, 0xFFF8, 0x6);
+       if (!nphy->gain_boost) {
+               b43_radio_set(dev, B2055_C1_RX_RFSPC1, 0x2);
+               b43_radio_set(dev, B2055_C2_RX_RFSPC1, 0x2);
+       } else {
+               b43_radio_mask(dev, B2055_C1_RX_RFSPC1, 0xFFFD);
+               b43_radio_mask(dev, B2055_C2_RX_RFSPC1, 0xFFFD);
+       }
+       udelay(2);
 }
 
-/* Initialize a Broadcom 2055 N-radio */
+/*
+ * Initialize a Broadcom 2055 N-radio
+ * http://bcm-v4.sipsolutions.net/802.11/Radio/2055/Init
+ */
 static void b43_radio_init2055(struct b43_wldev *dev)
 {
        b43_radio_init2055_pre(dev);
@@ -223,16 +253,15 @@ static void b43_radio_init2055(struct b43_wldev *dev)
        b43_radio_init2055_post(dev);
 }
 
-void b43_nphy_radio_turn_on(struct b43_wldev *dev)
+/*
+ * Initialize a Broadcom 2056 N-radio
+ * http://bcm-v4.sipsolutions.net/802.11/Radio/2056/Init
+ */
+static void b43_radio_init2056(struct b43_wldev *dev)
 {
-       b43_radio_init2055(dev);
+       /* TODO */
 }
 
-void b43_nphy_radio_turn_off(struct b43_wldev *dev)
-{
-       b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
-                    ~B43_NPHY_RFCTL_CMD_EN);
-}
 
 /*
  * Upload the N-PHY tables.
@@ -246,110 +275,6 @@ static void b43_nphy_tables_init(struct b43_wldev *dev)
                b43_nphy_rev3plus_tables_init(dev);
 }
 
-static void b43_nphy_workarounds(struct b43_wldev *dev)
-{
-       struct b43_phy *phy = &dev->phy;
-       unsigned int i;
-
-       b43_phy_set(dev, B43_NPHY_IQFLIP,
-                   B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
-       if (1 /* FIXME band is 2.4GHz */) {
-               b43_phy_set(dev, B43_NPHY_CLASSCTL,
-                           B43_NPHY_CLASSCTL_CCKEN);
-       } else {
-               b43_phy_mask(dev, B43_NPHY_CLASSCTL,
-                            ~B43_NPHY_CLASSCTL_CCKEN);
-       }
-       b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8);
-       b43_phy_write(dev, B43_NPHY_TXFRAMEDELAY, 8);
-
-       /* Fixup some tables */
-       b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0xA);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0xA);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x800);
-       b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x800);
-
-       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
-       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
-       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
-       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
-
-       //TODO set RF sequence
-
-       /* Set narrowband clip threshold */
-       b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 66);
-       b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 66);
-
-       /* Set wideband clip 2 threshold */
-       b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES,
-                       ~B43_NPHY_C1_CLIPWBTHRES_CLIP2,
-                       21 << B43_NPHY_C1_CLIPWBTHRES_CLIP2_SHIFT);
-       b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES,
-                       ~B43_NPHY_C2_CLIPWBTHRES_CLIP2,
-                       21 << B43_NPHY_C2_CLIPWBTHRES_CLIP2_SHIFT);
-
-       /* Set Clip 2 detect */
-       b43_phy_set(dev, B43_NPHY_C1_CGAINI,
-                   B43_NPHY_C1_CGAINI_CL2DETECT);
-       b43_phy_set(dev, B43_NPHY_C2_CGAINI,
-                   B43_NPHY_C2_CGAINI_CL2DETECT);
-
-       if (0 /*FIXME*/) {
-               /* Set dwell lengths */
-               b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 43);
-               b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 43);
-               b43_phy_write(dev, B43_NPHY_W1CLIP1_DWELL_LEN, 9);
-               b43_phy_write(dev, B43_NPHY_W1CLIP2_DWELL_LEN, 9);
-
-               /* Set gain backoff */
-               b43_phy_maskset(dev, B43_NPHY_C1_CGAINI,
-                               ~B43_NPHY_C1_CGAINI_GAINBKOFF,
-                               1 << B43_NPHY_C1_CGAINI_GAINBKOFF_SHIFT);
-               b43_phy_maskset(dev, B43_NPHY_C2_CGAINI,
-                               ~B43_NPHY_C2_CGAINI_GAINBKOFF,
-                               1 << B43_NPHY_C2_CGAINI_GAINBKOFF_SHIFT);
-
-               /* Set HPVGA2 index */
-               b43_phy_maskset(dev, B43_NPHY_C1_INITGAIN,
-                               ~B43_NPHY_C1_INITGAIN_HPVGA2,
-                               6 << B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT);
-               b43_phy_maskset(dev, B43_NPHY_C2_INITGAIN,
-                               ~B43_NPHY_C2_INITGAIN_HPVGA2,
-                               6 << B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT);
-
-               //FIXME verify that the specs really mean to use autoinc here.
-               for (i = 0; i < 3; i++)
-                       b43_ntab_write(dev, B43_NTAB16(7, 0x106) + i, 0x673);
-       }
-
-       /* Set minimum gain value */
-       b43_phy_maskset(dev, B43_NPHY_C1_MINMAX_GAIN,
-                       ~B43_NPHY_C1_MINGAIN,
-                       23 << B43_NPHY_C1_MINGAIN_SHIFT);
-       b43_phy_maskset(dev, B43_NPHY_C2_MINMAX_GAIN,
-                       ~B43_NPHY_C2_MINGAIN,
-                       23 << B43_NPHY_C2_MINGAIN_SHIFT);
-
-       if (phy->rev < 2) {
-               b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL,
-                            ~B43_NPHY_SCRAM_SIGCTL_SCM);
-       }
-
-       /* Set phase track alpha and beta */
-       b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125);
-       b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3);
-       b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105);
-       b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E);
-       b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD);
-       b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20);
-}
-
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PA%20override */
 static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable)
 {
@@ -600,8 +525,8 @@ static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core)
                b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0007);
        }
 
-       /* TODO: Call N PHY RF Ctrl Intc Override with 2, 0, 3 as arguments */
-       /* TODO: Call N PHY RF Intc Override with 8, 0, 3, 0 as arguments */
+       b43_nphy_rf_control_intc_override(dev, 2, 0, 3);
+       b43_nphy_rf_control_override(dev, 8, 0, 3, false);
        b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
 
        if (core == 0) {
@@ -611,9 +536,8 @@ static void b43_nphy_rx_cal_phy_setup(struct b43_wldev *dev, u8 core)
                rxval = 4;
                txval = 2;
        }
-
-       /* TODO: Call N PHY RF Ctrl Intc Override with 1, rxval, (core + 1) */
-       /* TODO: Call N PHY RF Ctrl Intc Override with 1, txval, (2 - core) */
+       b43_nphy_rf_control_intc_override(dev, 1, rxval, (core + 1));
+       b43_nphy_rf_control_intc_override(dev, 1, txval, (2 - core));
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */
@@ -745,6 +669,41 @@ static void b43_nphy_read_clip_detection(struct b43_wldev *dev, u16 *clip_st)
        clip_st[1] = b43_phy_read(dev, B43_NPHY_C2_CLIP1THRES);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SuperSwitchInit */
+static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init)
+{
+       if (dev->phy.rev >= 3) {
+               if (!init)
+                       return;
+               if (0 /* FIXME */) {
+                       b43_ntab_write(dev, B43_NTAB16(9, 2), 0x211);
+                       b43_ntab_write(dev, B43_NTAB16(9, 3), 0x222);
+                       b43_ntab_write(dev, B43_NTAB16(9, 8), 0x144);
+                       b43_ntab_write(dev, B43_NTAB16(9, 12), 0x188);
+               }
+       } else {
+               b43_phy_write(dev, B43_NPHY_GPIO_LOOEN, 0);
+               b43_phy_write(dev, B43_NPHY_GPIO_HIOEN, 0);
+
+               ssb_chipco_gpio_control(&dev->dev->bus->chipco, 0xFC00,
+                                       0xFC00);
+               b43_write32(dev, B43_MMIO_MACCTL,
+                       b43_read32(dev, B43_MMIO_MACCTL) &
+                       ~B43_MACCTL_GPOUTSMSK);
+               b43_write16(dev, B43_MMIO_GPIO_MASK,
+                       b43_read16(dev, B43_MMIO_GPIO_MASK) | 0xFC00);
+               b43_write16(dev, B43_MMIO_GPIO_CONTROL,
+                       b43_read16(dev, B43_MMIO_GPIO_CONTROL) & ~0xFC00);
+
+               if (init) {
+                       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
+                       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
+                       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
+                       b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
+               }
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/classifier */
 static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val)
 {
@@ -816,6 +775,505 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev)
                b43_nphy_stay_in_carrier_search(dev, 0);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SpurWar */
+static void b43_nphy_spur_workaround(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       u8 channel = nphy->radio_chanspec.channel;
+       int tone[2] = { 57, 58 };
+       u32 noise[2] = { 0x3FF, 0x3FF };
+
+       B43_WARN_ON(dev->phy.rev < 3);
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       if (nphy->gband_spurwar_en) {
+               /* TODO: N PHY Adjust Analog Pfbw (7) */
+               if (channel == 11 && dev->phy.is_40mhz)
+                       ; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/
+               else
+                       ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
+               /* TODO: N PHY Adjust CRS Min Power (0x1E) */
+       }
+
+       if (nphy->aband_spurwar_en) {
+               if (channel == 54) {
+                       tone[0] = 0x20;
+                       noise[0] = 0x25F;
+               } else if (channel == 38 || channel == 102 || channel == 118) {
+                       if (0 /* FIXME */) {
+                               tone[0] = 0x20;
+                               noise[0] = 0x21F;
+                       } else {
+                               tone[0] = 0;
+                               noise[0] = 0;
+                       }
+               } else if (channel == 134) {
+                       tone[0] = 0x20;
+                       noise[0] = 0x21F;
+               } else if (channel == 151) {
+                       tone[0] = 0x10;
+                       noise[0] = 0x23F;
+               } else if (channel == 153 || channel == 161) {
+                       tone[0] = 0x30;
+                       noise[0] = 0x23F;
+               } else {
+                       tone[0] = 0;
+                       noise[0] = 0;
+               }
+
+               if (!tone[0] && !noise[0])
+                       ; /* TODO: N PHY Adjust Min Noise Var(1, tone, noise)*/
+               else
+                       ; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
+       }
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/AdjustLnaGainTbl */
+static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       u8 i;
+       s16 tmp;
+       u16 data[4];
+       s16 gain[2];
+       u16 minmax[2];
+       u16 lna_gain[4] = { -2, 10, 19, 25 };
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       if (nphy->gain_boost) {
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+                       gain[0] = 6;
+                       gain[1] = 6;
+               } else {
+                       tmp = 40370 - 315 * nphy->radio_chanspec.channel;
+                       gain[0] = ((tmp >> 13) + ((tmp >> 12) & 1));
+                       tmp = 23242 - 224 * nphy->radio_chanspec.channel;
+                       gain[1] = ((tmp >> 13) + ((tmp >> 12) & 1));
+               }
+       } else {
+               gain[0] = 0;
+               gain[1] = 0;
+       }
+
+       for (i = 0; i < 2; i++) {
+               if (nphy->elna_gain_config) {
+                       data[0] = 19 + gain[i];
+                       data[1] = 25 + gain[i];
+                       data[2] = 25 + gain[i];
+                       data[3] = 25 + gain[i];
+               } else {
+                       data[0] = lna_gain[0] + gain[i];
+                       data[1] = lna_gain[1] + gain[i];
+                       data[2] = lna_gain[2] + gain[i];
+                       data[3] = lna_gain[3] + gain[i];
+               }
+               b43_ntab_write_bulk(dev, B43_NTAB16(10, 8), 4, data);
+
+               minmax[i] = 23 + gain[i];
+       }
+
+       b43_phy_maskset(dev, B43_NPHY_C1_MINMAX_GAIN, ~B43_NPHY_C1_MINGAIN,
+                               minmax[0] << B43_NPHY_C1_MINGAIN_SHIFT);
+       b43_phy_maskset(dev, B43_NPHY_C2_MINMAX_GAIN, ~B43_NPHY_C2_MINGAIN,
+                               minmax[1] << B43_NPHY_C2_MINGAIN_SHIFT);
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */
+static void b43_nphy_gain_crtl_workarounds(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+       u8 i, j;
+       u8 code;
+
+       /* TODO: for PHY >= 3
+       s8 *lna1_gain, *lna2_gain;
+       u8 *gain_db, *gain_bits;
+       u16 *rfseq_init;
+       u8 lpf_gain[6] = { 0x00, 0x06, 0x0C, 0x12, 0x12, 0x12 };
+       u8 lpf_bits[6] = { 0, 1, 2, 3, 3, 3 };
+       */
+
+       u8 rfseq_events[3] = { 6, 8, 7 };
+       u8 rfseq_delays[3] = { 10, 30, 1 };
+
+       if (dev->phy.rev >= 3) {
+               /* TODO */
+       } else {
+               /* Set Clip 2 detect */
+               b43_phy_set(dev, B43_NPHY_C1_CGAINI,
+                               B43_NPHY_C1_CGAINI_CL2DETECT);
+               b43_phy_set(dev, B43_NPHY_C2_CGAINI,
+                               B43_NPHY_C2_CGAINI_CL2DETECT);
+
+               /* Set narrowband clip threshold */
+               b43_phy_set(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84);
+               b43_phy_set(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84);
+
+               if (!dev->phy.is_40mhz) {
+                       /* Set dwell lengths */
+                       b43_phy_set(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B);
+                       b43_phy_set(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B);
+                       b43_phy_set(dev, B43_NPHY_W1CLIP1_DWELL_LEN, 0x0009);
+                       b43_phy_set(dev, B43_NPHY_W1CLIP2_DWELL_LEN, 0x0009);
+               }
+
+               /* Set wideband clip 2 threshold */
+               b43_phy_maskset(dev, B43_NPHY_C1_CLIPWBTHRES,
+                               ~B43_NPHY_C1_CLIPWBTHRES_CLIP2,
+                               21);
+               b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES,
+                               ~B43_NPHY_C2_CLIPWBTHRES_CLIP2,
+                               21);
+
+               if (!dev->phy.is_40mhz) {
+                       b43_phy_maskset(dev, B43_NPHY_C1_CGAINI,
+                               ~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1);
+                       b43_phy_maskset(dev, B43_NPHY_C2_CGAINI,
+                               ~B43_NPHY_C2_CGAINI_GAINBKOFF, 0x1);
+                       b43_phy_maskset(dev, B43_NPHY_C1_CCK_CGAINI,
+                               ~B43_NPHY_C1_CCK_CGAINI_GAINBKOFF, 0x1);
+                       b43_phy_maskset(dev, B43_NPHY_C2_CCK_CGAINI,
+                               ~B43_NPHY_C2_CCK_CGAINI_GAINBKOFF, 0x1);
+               }
+
+               b43_phy_set(dev, B43_NPHY_CCK_SHIFTB_REF, 0x809C);
+
+               if (nphy->gain_boost) {
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ &&
+                           dev->phy.is_40mhz)
+                               code = 4;
+                       else
+                               code = 5;
+               } else {
+                       code = dev->phy.is_40mhz ? 6 : 7;
+               }
+
+               /* Set HPVGA2 index */
+               b43_phy_maskset(dev, B43_NPHY_C1_INITGAIN,
+                               ~B43_NPHY_C1_INITGAIN_HPVGA2,
+                               code << B43_NPHY_C1_INITGAIN_HPVGA2_SHIFT);
+               b43_phy_maskset(dev, B43_NPHY_C2_INITGAIN,
+                               ~B43_NPHY_C2_INITGAIN_HPVGA2,
+                               code << B43_NPHY_C2_INITGAIN_HPVGA2_SHIFT);
+
+               b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06);
+               b43_phy_write(dev, B43_NPHY_TABLE_DATALO,
+                                       (code << 8 | 0x7C));
+               b43_phy_write(dev, B43_NPHY_TABLE_DATALO,
+                                       (code << 8 | 0x7C));
+
+               b43_nphy_adjust_lna_gain_table(dev);
+
+               if (nphy->elna_gain_config) {
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0808);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1);
+
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x0C08);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x1);
+
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D06);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO,
+                                       (code << 8 | 0x74));
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO,
+                                       (code << 8 | 0x74));
+               }
+
+               if (dev->phy.rev == 2) {
+                       for (i = 0; i < 4; i++) {
+                               b43_phy_write(dev, B43_NPHY_TABLE_ADDR,
+                                               (0x0400 * i) + 0x0020);
+                               for (j = 0; j < 21; j++)
+                                       b43_phy_write(dev,
+                                               B43_NPHY_TABLE_DATALO, 3 * j);
+                       }
+
+                       b43_nphy_set_rf_sequence(dev, 5,
+                                       rfseq_events, rfseq_delays, 3);
+                       b43_phy_maskset(dev, B43_NPHY_OVER_DGAIN1,
+                               (u16)~B43_NPHY_OVER_DGAIN_CCKDGECV,
+                               0x5A << B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT);
+
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+                               b43_phy_maskset(dev, B43_PHY_N(0xC5D),
+                                               0xFF80, 4);
+               }
+       }
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */
+static void b43_nphy_workarounds(struct b43_wldev *dev)
+{
+       struct ssb_bus *bus = dev->dev->bus;
+       struct b43_phy *phy = &dev->phy;
+       struct b43_phy_n *nphy = phy->n;
+
+       u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 };
+       u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 };
+
+       u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 };
+       u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 };
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+               b43_nphy_classifier(dev, 1, 0);
+       else
+               b43_nphy_classifier(dev, 1, 1);
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       b43_phy_set(dev, B43_NPHY_IQFLIP,
+                   B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
+
+       if (dev->phy.rev >= 3) {
+               /* TODO */
+       } else {
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ &&
+                   nphy->band5g_pwrgain) {
+                       b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8);
+                       b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8);
+               } else {
+                       b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8);
+                       b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8);
+               }
+
+               /* TODO: convert to b43_ntab_write? */
+               b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2000);
+               b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x000A);
+               b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2010);
+               b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x000A);
+               b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2002);
+               b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0xCDAA);
+               b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2012);
+               b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0xCDAA);
+
+               if (dev->phy.rev < 2) {
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2008);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0000);
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2018);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0000);
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2007);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x7AAB);
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2017);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x7AAB);
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2006);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0800);
+                       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2016);
+                       b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0800);
+               }
+
+               b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
+               b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
+               b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
+               b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
+
+               if (bus->sprom.boardflags2_lo & 0x100 &&
+                   bus->boardinfo.type == 0x8B) {
+                       delays1[0] = 0x1;
+                       delays1[5] = 0x14;
+               }
+               b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7);
+               b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7);
+
+               b43_nphy_gain_crtl_workarounds(dev);
+
+               if (dev->phy.rev < 2) {
+                       if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2)
+                               ; /*TODO: b43_mhf(dev, 2, 0x0010, 0x0010, 3);*/
+               } else if (dev->phy.rev == 2) {
+                       b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
+                       b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0);
+               }
+
+               if (dev->phy.rev < 2)
+                       b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL,
+                                       ~B43_NPHY_SCRAM_SIGCTL_SCM);
+
+               /* Set phase track alpha and beta */
+               b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125);
+               b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3);
+               b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105);
+               b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E);
+               b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD);
+               b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20);
+
+               b43_phy_mask(dev, B43_NPHY_PIL_DW1,
+                               (u16)~B43_NPHY_PIL_DW_64QAM);
+               b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5);
+               b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4);
+               b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00);
+
+               if (dev->phy.rev == 2)
+                       b43_phy_set(dev, B43_NPHY_FINERX2_CGC,
+                                       B43_NPHY_FINERX2_CGC_DECGC);
+       }
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */
+static int b43_nphy_load_samples(struct b43_wldev *dev,
+                                       struct b43_c32 *samples, u16 len) {
+       struct b43_phy_n *nphy = dev->phy.n;
+       u16 i;
+       u32 *data;
+
+       data = kzalloc(len * sizeof(u32), GFP_KERNEL);
+       if (!data) {
+               b43err(dev->wl, "allocation for samples loading failed\n");
+               return -ENOMEM;
+       }
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       for (i = 0; i < len; i++) {
+               data[i] = (samples[i].i & 0x3FF << 10);
+               data[i] |= samples[i].q & 0x3FF;
+       }
+       b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data);
+
+       kfree(data);
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+       return 0;
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */
+static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
+                                       bool test)
+{
+       int i;
+       u16 bw, len, rot, angle;
+       struct b43_c32 *samples;
+
+
+       bw = (dev->phy.is_40mhz) ? 40 : 20;
+       len = bw << 3;
+
+       if (test) {
+               if (b43_phy_read(dev, B43_NPHY_BBCFG) & B43_NPHY_BBCFG_RSTRX)
+                       bw = 82;
+               else
+                       bw = 80;
+
+               if (dev->phy.is_40mhz)
+                       bw <<= 1;
+
+               len = bw << 1;
+       }
+
+       samples = kzalloc(len * sizeof(struct b43_c32), GFP_KERNEL);
+       if (!samples) {
+               b43err(dev->wl, "allocation for samples generation failed\n");
+               return 0;
+       }
+       rot = (((freq * 36) / bw) << 16) / 100;
+       angle = 0;
+
+       for (i = 0; i < len; i++) {
+               samples[i] = b43_cordic(angle);
+               angle += rot;
+               samples[i].q = CORDIC_CONVERT(samples[i].q * max);
+               samples[i].i = CORDIC_CONVERT(samples[i].i * max);
+       }
+
+       i = b43_nphy_load_samples(dev, samples, len);
+       kfree(samples);
+       return (i < 0) ? 0 : len;
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */
+static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
+                                       u16 wait, bool iqmode, bool dac_test)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+       int i;
+       u16 seq_mode;
+       u32 tmp;
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, true);
+
+       if ((nphy->bb_mult_save & 0x80000000) == 0) {
+               tmp = b43_ntab_read(dev, B43_NTAB16(15, 87));
+               nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
+       }
+
+       if (!dev->phy.is_40mhz)
+               tmp = 0x6464;
+       else
+               tmp = 0x4747;
+       b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, false);
+
+       b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1));
+
+       if (loops != 0xFFFF)
+               b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, (loops - 1));
+       else
+               b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, loops);
+
+       b43_phy_write(dev, B43_NPHY_SAMP_WAITCNT, wait);
+
+       seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE);
+
+       b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER);
+       if (iqmode) {
+               b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF);
+               b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000);
+       } else {
+               if (dac_test)
+                       b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5);
+               else
+                       b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1);
+       }
+       for (i = 0; i < 100; i++) {
+               if (b43_phy_read(dev, B43_NPHY_RFSEQST) & 1) {
+                       i = 0;
+                       break;
+               }
+               udelay(10);
+       }
+       if (i)
+               b43err(dev->wl, "run samples timeout\n");
+
+       b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
+}
+
+/*
+ * Transmits a known value for LO calibration
+ * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone
+ */
+static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val,
+                               bool iqmode, bool dac_test)
+{
+       u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test);
+       if (samp == 0)
+               return -1;
+       b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test);
+       return 0;
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlCoefSetup */
 static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev)
 {
@@ -829,8 +1287,7 @@ static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev)
        if (nphy->hang_avoid)
                b43_nphy_stay_in_carrier_search(dev, true);
 
-       /* TODO: Read an N PHY Table with ID 15, length 7, offset 80,
-               width 16, and data pointer buffer */
+       b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
 
        for (i = 0; i < 2; i++) {
                tmp = ((buffer[i * 2] & 0x3FF) << 10) |
@@ -883,6 +1340,31 @@ static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev)
                b43_nphy_stay_in_carrier_search(dev, false);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRfSeq */
+static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
+                                       u8 *events, u8 *delays, u8 length)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+       u8 i;
+       u8 end = (dev->phy.rev >= 3) ? 0x1F : 0x0F;
+       u16 offset1 = cmd << 4;
+       u16 offset2 = offset1 + 0x80;
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, true);
+
+       b43_ntab_write_bulk(dev, B43_NTAB8(7, offset1), length, events);
+       b43_ntab_write_bulk(dev, B43_NTAB8(7, offset2), length, delays);
+
+       for (i = length; i < 16; i++) {
+               b43_ntab_write(dev, B43_NTAB8(7, offset1 + i), end);
+               b43_ntab_write(dev, B43_NTAB8(7, offset2 + i), 1);
+       }
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, false);
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ForceRFSeq */
 static void b43_nphy_force_rf_sequence(struct b43_wldev *dev,
                                       enum b43_nphy_rf_sequence seq)
@@ -913,6 +1395,180 @@ ok:
        b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */
+static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
+                                               u16 value, u8 core, bool off)
+{
+       int i;
+       u8 index = fls(field);
+       u8 addr, en_addr, val_addr;
+       /* we expect only one bit set */
+       B43_WARN_ON(field & (~(1 << (index - 1))));
+
+       if (dev->phy.rev >= 3) {
+               const struct nphy_rf_control_override_rev3 *rf_ctrl;
+               for (i = 0; i < 2; i++) {
+                       if (index == 0 || index == 16) {
+                               b43err(dev->wl,
+                                       "Unsupported RF Ctrl Override call\n");
+                               return;
+                       }
+
+                       rf_ctrl = &tbl_rf_control_override_rev3[index - 1];
+                       en_addr = B43_PHY_N((i == 0) ?
+                               rf_ctrl->en_addr0 : rf_ctrl->en_addr1);
+                       val_addr = B43_PHY_N((i == 0) ?
+                               rf_ctrl->val_addr0 : rf_ctrl->val_addr1);
+
+                       if (off) {
+                               b43_phy_mask(dev, en_addr, ~(field));
+                               b43_phy_mask(dev, val_addr,
+                                               ~(rf_ctrl->val_mask));
+                       } else {
+                               if (core == 0 || ((1 << core) & i) != 0) {
+                                       b43_phy_set(dev, en_addr, field);
+                                       b43_phy_maskset(dev, val_addr,
+                                               ~(rf_ctrl->val_mask),
+                                               (value << rf_ctrl->val_shift));
+                               }
+                       }
+               }
+       } else {
+               const struct nphy_rf_control_override_rev2 *rf_ctrl;
+               if (off) {
+                       b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~(field));
+                       value = 0;
+               } else {
+                       b43_phy_set(dev, B43_NPHY_RFCTL_OVER, field);
+               }
+
+               for (i = 0; i < 2; i++) {
+                       if (index <= 1 || index == 16) {
+                               b43err(dev->wl,
+                                       "Unsupported RF Ctrl Override call\n");
+                               return;
+                       }
+
+                       if (index == 2 || index == 10 ||
+                           (index >= 13 && index <= 15)) {
+                               core = 1;
+                       }
+
+                       rf_ctrl = &tbl_rf_control_override_rev2[index - 2];
+                       addr = B43_PHY_N((i == 0) ?
+                               rf_ctrl->addr0 : rf_ctrl->addr1);
+
+                       if ((core & (1 << i)) != 0)
+                               b43_phy_maskset(dev, addr, ~(rf_ctrl->bmask),
+                                               (value << rf_ctrl->shift));
+
+                       b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1);
+                       b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
+                                       B43_NPHY_RFCTL_CMD_START);
+                       udelay(1);
+                       b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, 0xFFFE);
+               }
+       }
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */
+static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
+                                               u16 value, u8 core)
+{
+       u8 i, j;
+       u16 reg, tmp, val;
+
+       B43_WARN_ON(dev->phy.rev < 3);
+       B43_WARN_ON(field > 4);
+
+       for (i = 0; i < 2; i++) {
+               if ((core == 1 && i == 1) || (core == 2 && !i))
+                       continue;
+
+               reg = (i == 0) ?
+                       B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2;
+               b43_phy_mask(dev, reg, 0xFBFF);
+
+               switch (field) {
+               case 0:
+                       b43_phy_write(dev, reg, 0);
+                       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
+                       break;
+               case 1:
+                       if (!i) {
+                               b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1,
+                                               0xFC3F, (value << 6));
+                               b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1,
+                                               0xFFFE, 1);
+                               b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
+                                               B43_NPHY_RFCTL_CMD_START);
+                               for (j = 0; j < 100; j++) {
+                                       if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START) {
+                                               j = 0;
+                                               break;
+                                       }
+                                       udelay(10);
+                               }
+                               if (j)
+                                       b43err(dev->wl,
+                                               "intc override timeout\n");
+                               b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1,
+                                               0xFFFE);
+                       } else {
+                               b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2,
+                                               0xFC3F, (value << 6));
+                               b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
+                                               0xFFFE, 1);
+                               b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
+                                               B43_NPHY_RFCTL_CMD_RXTX);
+                               for (j = 0; j < 100; j++) {
+                                       if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX) {
+                                               j = 0;
+                                               break;
+                                       }
+                                       udelay(10);
+                               }
+                               if (j)
+                                       b43err(dev->wl,
+                                               "intc override timeout\n");
+                               b43_phy_mask(dev, B43_NPHY_RFCTL_OVER,
+                                               0xFFFE);
+                       }
+                       break;
+               case 2:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0020;
+                               val = value << 5;
+                       } else {
+                               tmp = 0x0010;
+                               val = value << 4;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       break;
+               case 3:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0001;
+                               val = value;
+                       } else {
+                               tmp = 0x0004;
+                               val = value << 2;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       break;
+               case 4:
+                       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                               tmp = 0x0002;
+                               val = value << 1;
+                       } else {
+                               tmp = 0x0008;
+                               val = value << 3;
+                       }
+                       b43_phy_maskset(dev, reg, ~tmp, val);
+                       break;
+               }
+       }
+}
+
 static void b43_nphy_bphy_init(struct b43_wldev *dev)
 {
        unsigned int i;
@@ -992,66 +1648,151 @@ static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale,
                b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TSSI, tmp);
 }
 
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */
-static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
+static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
 {
        u16 val;
 
-       if (dev->phy.rev >= 3) {
-               /* TODO */
-       } else {
-               if (type < 3)
-                       val = 0;
-               else if (type == 6)
-                       val = 1;
-               else if (type == 3)
-                       val = 2;
-               else
-                       val = 3;
+       if (type < 3)
+               val = 0;
+       else if (type == 6)
+               val = 1;
+       else if (type == 3)
+               val = 2;
+       else
+               val = 3;
+
+       val = (val << 12) | (val << 14);
+       b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val);
+       b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val);
 
-               val = (val << 12) | (val << 14);
-               b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val);
-               b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val);
+       if (type < 3) {
+               b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF,
+                               (type + 1) << 4);
+               b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF,
+                               (type + 1) << 4);
+       }
 
+       /* TODO use some definitions */
+       if (code == 0) {
+               b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF, 0);
                if (type < 3) {
-                       b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF,
-                                       (type + 1) << 4);
-                       b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF,
-                                       (type + 1) << 4);
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFEC7, 0);
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xEFDC, 0);
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFFFE, 0);
+                       udelay(20);
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xFFFE, 0);
                }
+       } else {
+               b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF,
+                               0x3000);
+               if (type < 3) {
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD,
+                                       0xFEC7, 0x0180);
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
+                                       0xEFDC, (code << 1 | 0x1021));
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFFFE, 0x1);
+                       udelay(20);
+                       b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xFFFE, 0);
+               }
+       }
+}
+
+static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+       u8 i;
+       u16 reg, val;
+
+       if (code == 0) {
+               b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, 0xFDFF);
+               b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, 0xFDFF);
+               b43_phy_mask(dev, B43_NPHY_AFECTL_C1, 0xFCFF);
+               b43_phy_mask(dev, B43_NPHY_AFECTL_C2, 0xFCFF);
+               b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S0, 0xFFDF);
+               b43_phy_mask(dev, B43_NPHY_TXF_40CO_B32S1, 0xFFDF);
+               b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0xFFC3);
+               b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0xFFC3);
+       } else {
+               for (i = 0; i < 2; i++) {
+                       if ((code == 1 && i == 1) || (code == 2 && !i))
+                               continue;
+
+                       reg = (i == 0) ?
+                               B43_NPHY_AFECTL_OVER1 : B43_NPHY_AFECTL_OVER;
+                       b43_phy_maskset(dev, reg, 0xFDFF, 0x0200);
 
-               /* TODO use some definitions */
-               if (code == 0) {
-                       b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF, 0);
-                       if (type < 3) {
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD,
-                                               0xFEC7, 0);
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
-                                               0xEFDC, 0);
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD,
-                                               0xFFFE, 0);
-                               udelay(20);
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
-                                               0xFFFE, 0);
-                       }
-               } else {
-                       b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF,
-                                       0x3000);
                        if (type < 3) {
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD,
-                                               0xFEC7, 0x0180);
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
-                                               0xEFDC, (code << 1 | 0x1021));
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD,
-                                               0xFFFE, 0x0001);
-                               udelay(20);
-                               b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
-                                               0xFFFE, 0);
+                               reg = (i == 0) ?
+                                       B43_NPHY_AFECTL_C1 :
+                                       B43_NPHY_AFECTL_C2;
+                               b43_phy_maskset(dev, reg, 0xFCFF, 0);
+
+                               reg = (i == 0) ?
+                                       B43_NPHY_RFCTL_LUT_TRSW_UP1 :
+                                       B43_NPHY_RFCTL_LUT_TRSW_UP2;
+                               b43_phy_maskset(dev, reg, 0xFFC3, 0);
+
+                               if (type == 0)
+                                       val = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 4 : 8;
+                               else if (type == 1)
+                                       val = 16;
+                               else
+                                       val = 32;
+                               b43_phy_set(dev, reg, val);
+
+                               reg = (i == 0) ?
+                                       B43_NPHY_TXF_40CO_B1S0 :
+                                       B43_NPHY_TXF_40CO_B32S1;
+                               b43_phy_set(dev, reg, 0x0020);
+                       } else {
+                               if (type == 6)
+                                       val = 0x0100;
+                               else if (type == 3)
+                                       val = 0x0200;
+                               else
+                                       val = 0x0300;
+
+                               reg = (i == 0) ?
+                                       B43_NPHY_AFECTL_C1 :
+                                       B43_NPHY_AFECTL_C2;
+
+                               b43_phy_maskset(dev, reg, 0xFCFF, val);
+                               b43_phy_maskset(dev, reg, 0xF3FF, val << 2);
+
+                               if (type != 3 && type != 6) {
+                                       enum ieee80211_band band =
+                                               b43_current_band(dev->wl);
+
+                                       if ((nphy->ipa2g_on &&
+                                               band == IEEE80211_BAND_2GHZ) ||
+                                               (nphy->ipa5g_on &&
+                                               band == IEEE80211_BAND_5GHZ))
+                                               val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
+                                       else
+                                               val = 0x11;
+                                       reg = (i == 0) ? 0x2000 : 0x3000;
+                                       reg |= B2055_PADDRV;
+                                       b43_radio_write16(dev, reg, val);
+
+                                       reg = (i == 0) ?
+                                               B43_NPHY_AFECTL_OVER1 :
+                                               B43_NPHY_AFECTL_OVER;
+                                       b43_phy_set(dev, reg, 0x0200);
+                               }
                        }
                }
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */
+static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
+{
+       if (dev->phy.rev >= 3)
+               b43_nphy_rev3_rssi_select(dev, code, type);
+       else
+               b43_nphy_rev2_rssi_select(dev, code, type);
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRssi2055Vcm */
 static void b43_nphy_set_rssi_2055_vcm(struct b43_wldev *dev, u8 type, u8 *buf)
 {
@@ -1340,12 +2081,12 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev)
        u16 *rssical_phy_regs = NULL;
 
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
-               if (!nphy->rssical_chanspec_2G)
+               if (b43_empty_chanspec(&nphy->rssical_chanspec_2G))
                        return;
                rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G;
                rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G;
        } else {
-               if (!nphy->rssical_chanspec_5G)
+               if (b43_empty_chanspec(&nphy->rssical_chanspec_5G))
                        return;
                rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G;
                rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G;
@@ -1394,9 +2135,60 @@ static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev)
 {
        struct b43_phy_n *nphy = dev->phy.n;
        u16 *save = nphy->tx_rx_cal_radio_saveregs;
+       u16 tmp;
+       u8 offset, i;
 
        if (dev->phy.rev >= 3) {
-               /* TODO */
+           for (i = 0; i < 2; i++) {
+               tmp = (i == 0) ? 0x2000 : 0x3000;
+               offset = i * 11;
+
+               save[offset + 0] = b43_radio_read16(dev, B2055_CAL_RVARCTL);
+               save[offset + 1] = b43_radio_read16(dev, B2055_CAL_LPOCTL);
+               save[offset + 2] = b43_radio_read16(dev, B2055_CAL_TS);
+               save[offset + 3] = b43_radio_read16(dev, B2055_CAL_RCCALRTS);
+               save[offset + 4] = b43_radio_read16(dev, B2055_CAL_RCALRTS);
+               save[offset + 5] = b43_radio_read16(dev, B2055_PADDRV);
+               save[offset + 6] = b43_radio_read16(dev, B2055_XOCTL1);
+               save[offset + 7] = b43_radio_read16(dev, B2055_XOCTL2);
+               save[offset + 8] = b43_radio_read16(dev, B2055_XOREGUL);
+               save[offset + 9] = b43_radio_read16(dev, B2055_XOMISC);
+               save[offset + 10] = b43_radio_read16(dev, B2055_PLL_LFC1);
+
+               if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+                       b43_radio_write16(dev, tmp | B2055_CAL_RVARCTL, 0x0A);
+                       b43_radio_write16(dev, tmp | B2055_CAL_LPOCTL, 0x40);
+                       b43_radio_write16(dev, tmp | B2055_CAL_TS, 0x55);
+                       b43_radio_write16(dev, tmp | B2055_CAL_RCCALRTS, 0);
+                       b43_radio_write16(dev, tmp | B2055_CAL_RCALRTS, 0);
+                       if (nphy->ipa5g_on) {
+                               b43_radio_write16(dev, tmp | B2055_PADDRV, 4);
+                               b43_radio_write16(dev, tmp | B2055_XOCTL1, 1);
+                       } else {
+                               b43_radio_write16(dev, tmp | B2055_PADDRV, 0);
+                               b43_radio_write16(dev, tmp | B2055_XOCTL1, 0x2F);
+                       }
+                       b43_radio_write16(dev, tmp | B2055_XOCTL2, 0);
+               } else {
+                       b43_radio_write16(dev, tmp | B2055_CAL_RVARCTL, 0x06);
+                       b43_radio_write16(dev, tmp | B2055_CAL_LPOCTL, 0x40);
+                       b43_radio_write16(dev, tmp | B2055_CAL_TS, 0x55);
+                       b43_radio_write16(dev, tmp | B2055_CAL_RCCALRTS, 0);
+                       b43_radio_write16(dev, tmp | B2055_CAL_RCALRTS, 0);
+                       b43_radio_write16(dev, tmp | B2055_XOCTL1, 0);
+                       if (nphy->ipa2g_on) {
+                               b43_radio_write16(dev, tmp | B2055_PADDRV, 6);
+                               b43_radio_write16(dev, tmp | B2055_XOCTL2,
+                                       (dev->phy.rev < 5) ? 0x11 : 0x01);
+                       } else {
+                               b43_radio_write16(dev, tmp | B2055_PADDRV, 0);
+                               b43_radio_write16(dev, tmp | B2055_XOCTL2, 0);
+                       }
+               }
+               b43_radio_write16(dev, tmp | B2055_XOREGUL, 0);
+               b43_radio_write16(dev, tmp | B2055_XOMISC, 0);
+               b43_radio_write16(dev, tmp | B2055_PLL_LFC1, 0);
+           }
        } else {
                save[0] = b43_radio_read16(dev, B2055_C1_TX_RF_IQCAL1);
                b43_radio_write16(dev, B2055_C1_TX_RF_IQCAL1, 0x29);
@@ -1493,6 +2285,43 @@ static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core)
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ExtPaSetTxDigiFilts */
+static void b43_nphy_ext_pa_set_tx_dig_filters(struct b43_wldev *dev)
+{
+       int i;
+       for (i = 0; i < 15; i++)
+               b43_phy_write(dev, B43_PHY_N(0x2C5 + i),
+                               tbl_tx_filter_coef_rev4[2][i]);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IpaSetTxDigiFilts */
+static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev)
+{
+       int i, j;
+       /* B43_NPHY_TXF_20CO_S0A1, B43_NPHY_TXF_40CO_S0A1, unknown */
+       u16 offset[] = { 0x186, 0x195, 0x2C5 };
+
+       for (i = 0; i < 3; i++)
+               for (j = 0; j < 15; j++)
+                       b43_phy_write(dev, B43_PHY_N(offset[i] + j),
+                                       tbl_tx_filter_coef_rev4[i][j]);
+
+       if (dev->phy.is_40mhz) {
+               for (j = 0; j < 15; j++)
+                       b43_phy_write(dev, B43_PHY_N(offset[0] + j),
+                                       tbl_tx_filter_coef_rev4[3][j]);
+       } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+               for (j = 0; j < 15; j++)
+                       b43_phy_write(dev, B43_PHY_N(offset[0] + j),
+                                       tbl_tx_filter_coef_rev4[5][j]);
+       }
+
+       if (dev->phy.channel == 14)
+               for (j = 0; j < 15; j++)
+                       b43_phy_write(dev, B43_PHY_N(offset[0] + j),
+                                       tbl_tx_filter_coef_rev4[6][j]);
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */
 static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
 {
@@ -1507,8 +2336,7 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
 
                if (nphy->hang_avoid)
                        b43_nphy_stay_in_carrier_search(dev, true);
-               /* TODO: Read an N PHY Table with ID 7, length 2,
-                       offset 0x110, width 16, and curr_gain */
+               b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, curr_gain);
                if (nphy->hang_avoid)
                        b43_nphy_stay_in_carrier_search(dev, false);
 
@@ -1626,22 +2454,21 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
                b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x0600);
 
                regs[4] = b43_phy_read(dev, B43_NPHY_BBCFG);
-               b43_phy_mask(dev, B43_NPHY_BBCFG, ~B43_NPHY_BBCFG_RSTRX);
+               b43_phy_mask(dev, B43_NPHY_BBCFG, (u16)~B43_NPHY_BBCFG_RSTRX);
 
-               /* TODO: Read an N PHY Table with ID 8, length 1, offset 3,
-                       width 16, and data pointing to tmp */
+               tmp = b43_ntab_read(dev, B43_NTAB16(8, 3));
                regs[5] = tmp;
                b43_ntab_write(dev, B43_NTAB16(8, 3), 0);
-               /* TODO: Read an N PHY Table with ID 8, length 1, offset 19,
-                       width 16, and data pointing to tmp */
+
+               tmp = b43_ntab_read(dev, B43_NTAB16(8, 19));
                regs[6] = tmp;
                b43_ntab_write(dev, B43_NTAB16(8, 19), 0);
                regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
                regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
 
-               /* TODO: Call N PHY RF Ctrl Intc Override with 2, 1, 3 */
-               /* TODO: Call N PHY RF Ctrl Intc Override with 1, 2, 1 */
-               /* TODO: Call N PHY RF Ctrl Intc Override with 1, 8, 2 */
+               b43_nphy_rf_control_intc_override(dev, 2, 1, 3);
+               b43_nphy_rf_control_intc_override(dev, 1, 2, 1);
+               b43_nphy_rf_control_intc_override(dev, 1, 8, 2);
 
                regs[9] = b43_phy_read(dev, B43_NPHY_PAPD_EN0);
                regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1);
@@ -1653,13 +2480,11 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
                tmp = b43_phy_read(dev, B43_NPHY_AFECTL_OVER);
                regs[2] = tmp;
                b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp | 0x3000);
-               /* TODO: Read an N PHY Table with ID 8, length 1, offset 2,
-                       width 16, and data pointing to tmp */
+               tmp = b43_ntab_read(dev, B43_NTAB16(8, 2));
                regs[3] = tmp;
                tmp |= 0x2000;
                b43_ntab_write(dev, B43_NTAB16(8, 2), tmp);
-               /* TODO: Read an N PHY Table with ID 8, length 1, offset 18,
-                       width 16, and data pointer tmp */
+               tmp = b43_ntab_read(dev, B43_NTAB16(8, 18));
                regs[4] = tmp;
                tmp |= 0x2000;
                b43_ntab_write(dev, B43_NTAB16(8, 18), tmp);
@@ -1674,6 +2499,55 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev)
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */
+static void b43_nphy_save_cal(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       struct b43_phy_n_iq_comp *rxcal_coeffs = NULL;
+       u16 *txcal_radio_regs = NULL;
+       struct b43_chanspec *iqcal_chanspec;
+       u16 *table = NULL;
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 1);
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G;
+               txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G;
+               iqcal_chanspec = &nphy->iqcal_chanspec_2G;
+               table = nphy->cal_cache.txcal_coeffs_2G;
+       } else {
+               rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G;
+               txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G;
+               iqcal_chanspec = &nphy->iqcal_chanspec_5G;
+               table = nphy->cal_cache.txcal_coeffs_5G;
+       }
+
+       b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs);
+       /* TODO use some definitions */
+       if (dev->phy.rev >= 3) {
+               txcal_radio_regs[0] = b43_radio_read(dev, 0x2021);
+               txcal_radio_regs[1] = b43_radio_read(dev, 0x2022);
+               txcal_radio_regs[2] = b43_radio_read(dev, 0x3021);
+               txcal_radio_regs[3] = b43_radio_read(dev, 0x3022);
+               txcal_radio_regs[4] = b43_radio_read(dev, 0x2023);
+               txcal_radio_regs[5] = b43_radio_read(dev, 0x2024);
+               txcal_radio_regs[6] = b43_radio_read(dev, 0x3023);
+               txcal_radio_regs[7] = b43_radio_read(dev, 0x3024);
+       } else {
+               txcal_radio_regs[0] = b43_radio_read(dev, 0x8B);
+               txcal_radio_regs[1] = b43_radio_read(dev, 0xBA);
+               txcal_radio_regs[2] = b43_radio_read(dev, 0x8D);
+               txcal_radio_regs[3] = b43_radio_read(dev, 0xBC);
+       }
+       *iqcal_chanspec = nphy->radio_chanspec;
+       b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 8, table);
+
+       if (nphy->hang_avoid)
+               b43_nphy_stay_in_carrier_search(dev, 0);
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */
 static void b43_nphy_restore_cal(struct b43_wldev *dev)
 {
@@ -1688,12 +2562,12 @@ static void b43_nphy_restore_cal(struct b43_wldev *dev)
        struct b43_phy_n_iq_comp *rxcal_coeffs = NULL;
 
        if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
-               if (nphy->iqcal_chanspec_2G == 0)
+               if (b43_empty_chanspec(&nphy->iqcal_chanspec_2G))
                        return;
                table = nphy->cal_cache.txcal_coeffs_2G;
                loft = &nphy->cal_cache.txcal_coeffs_2G[5];
        } else {
-               if (nphy->iqcal_chanspec_5G == 0)
+               if (b43_empty_chanspec(&nphy->iqcal_chanspec_5G))
                        return;
                table = nphy->cal_cache.txcal_coeffs_5G;
                loft = &nphy->cal_cache.txcal_coeffs_5G[5];
@@ -1771,8 +2645,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                nphy->hang_avoid = 0;
        }
 
-       /* TODO: Read an N PHY Table with ID 7, length 2, offset 0x110,
-               width 16, and data pointer save */
+       b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, save);
 
        for (i = 0; i < 2; i++) {
                b43_nphy_iq_cal_gain_params(dev, i, target, &params[i]);
@@ -1788,22 +2661,31 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                (dev->phy.rev == 5 && nphy->ipa2g_on &&
                b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ);
        if (phy6or5x) {
-               /* TODO */
+               if (dev->phy.is_40mhz) {
+                       b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18,
+                                       tbl_tx_iqlo_cal_loft_ladder_40);
+                       b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18,
+                                       tbl_tx_iqlo_cal_iqimb_ladder_40);
+               } else {
+                       b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18,
+                                       tbl_tx_iqlo_cal_loft_ladder_20);
+                       b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18,
+                                       tbl_tx_iqlo_cal_iqimb_ladder_20);
+               }
        }
 
        b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
 
-       if (1 /* FIXME: the band width is 20 MHz */)
+       if (!dev->phy.is_40mhz)
                freq = 2500;
        else
                freq = 5000;
 
        if (nphy->mphase_cal_phase_id > 2)
-               ;/* TODO: Call N PHY Run Samples with (band width * 8),
-                       0xFFFF, 0, 1, 0 as arguments */
+               b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8,
+                                       0xFFFF, 0, true, false);
        else
-               ;/* TODO: Call N PHY TX Tone with freq, 250, 1, 0 as arguments
-                       and save result as error */
+               error = b43_nphy_tx_tone(dev, freq, 250, true, false);
 
        if (error == 0) {
                if (nphy->mphase_cal_phase_id > 2) {
@@ -1877,9 +2759,8 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                        b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDNNUM, tmp);
 
                        if (type == 1 || type == 3 || type == 4) {
-                               /* TODO: Read an N PHY Table with ID 15,
-                                       length 1, offset 69 + core,
-                                       width 16, and data pointer buffer */
+                               buffer[0] = b43_ntab_read(dev,
+                                               B43_NTAB16(15, 69 + core));
                                diq_start = buffer[0];
                                buffer[0] = 0;
                                b43_ntab_write(dev, B43_NTAB16(15, 69 + core),
@@ -1894,9 +2775,8 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                                udelay(10);
                        }
 
-                       /* TODO: Read an N PHY Table with ID 15,
-                               length table_length, offset 96, width 16,
-                               and data pointer buffer */
+                       b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length,
+                                               buffer);
                        b43_ntab_write_bulk(dev, B43_NTAB16(15, 64), length,
                                                buffer);
 
@@ -1911,8 +2791,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
 
                if (!mphase || nphy->mphase_cal_phase_id == last) {
                        b43_ntab_write_bulk(dev, B43_NTAB16(15, 96), 4, buffer);
-                       /* TODO: Read an N PHY Table with ID 15, length 4,
-                               offset 80, width 16, and data pointer buffer */
+                       b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 4, buffer);
                        if (dev->phy.rev < 3) {
                                buffer[0] = 0;
                                buffer[1] = 0;
@@ -1930,19 +2809,16 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
                        length = 11;
                        if (dev->phy.rev < 3)
                                length -= 2;
-                       /* TODO: Read an N PHY Table with ID 15, length length,
-                               offset 96, width 16, and data pointer
-                               nphy->txiqlocal_bestc */
+                       b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length,
+                                               nphy->txiqlocal_bestc);
                        nphy->txiqlocal_coeffsvalid = true;
-                       /* TODO: Set nphy->txiqlocal_chanspec to
-                               the current channel */
+                       nphy->txiqlocal_chanspec = nphy->radio_chanspec;
                } else {
                        length = 11;
                        if (dev->phy.rev < 3)
                                length -= 2;
-                       /* TODO: Read an N PHY Table with ID 5, length length,
-                               offset 96, width 16, and data pointer
-                               nphy->mphase_txcal_bestcoeffs */
+                       b43_ntab_read_bulk(dev, B43_NTAB16(15, 96), length,
+                                               nphy->mphase_txcal_bestcoeffs);
                }
 
                b43_nphy_stop_playback(dev);
@@ -1963,6 +2839,40 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
        return error;
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ReapplyTxCalCoeffs */
+static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+       u8 i;
+       u16 buffer[7];
+       bool equal = true;
+
+       if (!nphy->txiqlocal_coeffsvalid ||
+           b43_eq_chanspecs(&nphy->txiqlocal_chanspec, &nphy->radio_chanspec))
+               return;
+
+       b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
+       for (i = 0; i < 4; i++) {
+               if (buffer[i] != nphy->txiqlocal_bestc[i]) {
+                       equal = false;
+                       break;
+               }
+       }
+
+       if (!equal) {
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 80), 4,
+                                       nphy->txiqlocal_bestc);
+               for (i = 0; i < 4; i++)
+                       buffer[i] = 0;
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 88), 4,
+                                       buffer);
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 85), 2,
+                                       &nphy->txiqlocal_bestc[5]);
+               b43_ntab_write_bulk(dev, B43_NTAB16(15, 93), 2,
+                                       &nphy->txiqlocal_bestc[5]);
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */
 static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
                        struct nphy_txgains target, u8 type, bool debug)
@@ -1981,7 +2891,7 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
        u16 lna[3] = { 3, 3, 1 };
        u16 hpf1[3] = { 7, 2, 0 };
        u16 hpf2[3] = { 2, 0, 0 };
-       u32 power[3];
+       u32 power[3] = { };
        u16 gain_save[2];
        u16 cal_gain[2];
        struct nphy_iqcal_params cal_params[2];
@@ -1993,9 +2903,8 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
        b43_nphy_stay_in_carrier_search(dev, 1);
 
        if (dev->phy.rev < 2)
-               ;/* TODO: Call N PHY Reapply TX Cal Coeffs */
-       /* TODO: Read an N PHY Table with ID 7, length 2, offset 0x110,
-               width 16, and data gain_save */
+               b43_nphy_reapply_tx_cal_coeffs(dev);
+       b43_ntab_read_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save);
        for (i = 0; i < 2; i++) {
                b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]);
                cal_gain[i] = cal_params[i].cal_gain;
@@ -2087,19 +2996,19 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
 
                        tmp[0] = ((cur_hpf2 << 8) | (cur_hpf1 << 4) |
                                        (cur_lna << 2));
-                       /* TODO:Call N PHY RF Ctrl Override with 0x400, tmp[0],
-                               3, 0 as arguments */
-                       /* TODO: Call N PHY Force RF Seq with 2 as argument */
+                       b43_nphy_rf_control_override(dev, 0x400, tmp[0], 3,
+                                                                       false);
+                       b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
                        b43_nphy_stop_playback(dev);
 
                        if (playtone) {
-                               /* TODO: Call N PHY TX Tone with 4000,
-                                       (nphy_rxcalparams & 0xffff), 0, 0
-                                       as arguments and save result as ret */
+                               ret = b43_nphy_tx_tone(dev, 4000,
+                                               (nphy->rxcalparams & 0xFFFF),
+                                               false, false);
                                playtone = false;
                        } else {
-                               /* TODO: Call N PHY Run Samples with 160,
-                                       0xFFFF, 0, 0, 0 as arguments */
+                               b43_nphy_run_samples(dev, 160, 0xFFFF, 0,
+                                                       false, false);
                        }
 
                        if (ret == 0) {
@@ -2136,7 +3045,7 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
                        break;
        }
 
-       /* TODO: Call N PHY RF Ctrl Override with 0x400, 0, 3, 1 as arguments*/
+       b43_nphy_rf_control_override(dev, 0x400, 0, 3, true);
        b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
        b43_ntab_write_bulk(dev, B43_NTAB16(7, 0x110), 2, gain_save);
 
@@ -2247,9 +3156,9 @@ int b43_phy_initn(struct b43_wldev *dev)
                b43_phy_set(dev, B43_NPHY_PAPD_EN1, 0x1);
                b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ1, 0x007F,
                                nphy->papd_epsilon_offset[1] << 7);
-               /* TODO N PHY IPA Set TX Dig Filters */
+               b43_nphy_int_pa_set_tx_dig_filters(dev);
        } else if (phy->rev >= 5) {
-               /* TODO N PHY Ext PA Set TX Dig Filters */
+               b43_nphy_ext_pa_set_tx_dig_filters(dev);
        }
 
        b43_nphy_workarounds(dev);
@@ -2294,9 +3203,11 @@ int b43_phy_initn(struct b43_wldev *dev)
        do_rssi_cal = false;
        if (phy->rev >= 3) {
                if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
-                       do_rssi_cal = (nphy->rssical_chanspec_2G == 0);
+                       do_rssi_cal =
+                               b43_empty_chanspec(&nphy->rssical_chanspec_2G);
                else
-                       do_rssi_cal = (nphy->rssical_chanspec_5G == 0);
+                       do_rssi_cal =
+                               b43_empty_chanspec(&nphy->rssical_chanspec_5G);
 
                if (do_rssi_cal)
                        b43_nphy_rssi_cal(dev);
@@ -2308,9 +3219,9 @@ int b43_phy_initn(struct b43_wldev *dev)
 
        if (!((nphy->measure_hold & 0x6) != 0)) {
                if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
-                       do_cal = (nphy->iqcal_chanspec_2G == 0);
+                       do_cal = b43_empty_chanspec(&nphy->iqcal_chanspec_2G);
                else
-                       do_cal = (nphy->iqcal_chanspec_5G == 0);
+                       do_cal = b43_empty_chanspec(&nphy->iqcal_chanspec_5G);
 
                if (nphy->mute)
                        do_cal = false;
@@ -2319,7 +3230,7 @@ int b43_phy_initn(struct b43_wldev *dev)
                        target = b43_nphy_get_tx_gains(dev);
 
                        if (nphy->antsel_type == 2)
-                               ;/*TODO NPHY Superswitch Init with argument 1*/
+                               b43_nphy_superswitch_init(dev, true);
                        if (nphy->perical != 2) {
                                b43_nphy_rssi_cal(dev);
                                if (phy->rev >= 3) {
@@ -2336,7 +3247,7 @@ int b43_phy_initn(struct b43_wldev *dev)
 
        if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) {
                if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0)
-                       ;/* Call N PHY Save Cal */
+                       b43_nphy_save_cal(dev);
                else if (nphy->mphase_cal_phase_id == 0)
                        ;/* N PHY Periodic Calibration with argument 3 */
        } else {
@@ -2350,12 +3261,140 @@ int b43_phy_initn(struct b43_wldev *dev)
        if (phy->rev >= 3 && phy->rev <= 6)
                b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014);
        b43_nphy_tx_lp_fbw(dev);
-       /* TODO N PHY Spur Workaround */
+       if (phy->rev >= 3)
+               b43_nphy_spur_workaround(dev);
 
        b43err(dev->wl, "IEEE 802.11n devices are not supported, yet.\n");
        return 0;
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ChanspecSetup */
+static void b43_nphy_chanspec_setup(struct b43_wldev *dev,
+                               const struct b43_phy_n_sfo_cfg *e,
+                               struct b43_chanspec chanspec)
+{
+       struct b43_phy *phy = &dev->phy;
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       u16 tmp;
+       u32 tmp32;
+
+       tmp = b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ;
+       if (chanspec.b_freq == 1 && tmp == 0) {
+               tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR);
+               b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4);
+               b43_phy_set(dev, B43_PHY_B_BBCFG, 0xC000);
+               b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32);
+               b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ);
+       } else if (chanspec.b_freq == 1) {
+               b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ);
+               tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR);
+               b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4);
+               b43_phy_mask(dev, B43_PHY_B_BBCFG, (u16)~0xC000);
+               b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32);
+       }
+
+       b43_chantab_phy_upload(dev, e);
+
+       tmp = chanspec.channel;
+       if (chanspec.b_freq == 1)
+               tmp |= 0x0100;
+       if (chanspec.b_width == 3)
+               tmp |= 0x0200;
+       b43_shm_write16(dev, B43_SHM_SHARED, 0xA0, tmp);
+
+       if (nphy->radio_chanspec.channel == 14) {
+               b43_nphy_classifier(dev, 2, 0);
+               b43_phy_set(dev, B43_PHY_B_TEST, 0x0800);
+       } else {
+               b43_nphy_classifier(dev, 2, 2);
+               if (chanspec.b_freq == 2)
+                       b43_phy_mask(dev, B43_PHY_B_TEST, ~0x840);
+       }
+
+       if (nphy->txpwrctrl)
+               b43_nphy_tx_power_fix(dev);
+
+       if (dev->phy.rev < 3)
+               b43_nphy_adjust_lna_gain_table(dev);
+
+       b43_nphy_tx_lp_fbw(dev);
+
+       if (dev->phy.rev >= 3 && 0) {
+               /* TODO */
+       }
+
+       b43_phy_write(dev, B43_NPHY_NDATAT_DUP40, 0x3830);
+
+       if (phy->rev >= 3)
+               b43_nphy_spur_workaround(dev);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetChanspec */
+static int b43_nphy_set_chanspec(struct b43_wldev *dev,
+                                       struct b43_chanspec chanspec)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       const struct b43_nphy_channeltab_entry_rev2 *tabent_r2;
+       const struct b43_nphy_channeltab_entry_rev3 *tabent_r3;
+
+       u8 tmp;
+       u8 channel = chanspec.channel;
+
+       if (dev->phy.rev >= 3) {
+               /* TODO */
+               tabent_r3 = NULL;
+               if (!tabent_r3)
+                       return -ESRCH;
+       } else {
+               tabent_r2 = b43_nphy_get_chantabent_rev2(dev, channel);
+               if (!tabent_r2)
+                       return -ESRCH;
+       }
+
+       nphy->radio_chanspec = chanspec;
+
+       if (chanspec.b_width != nphy->b_width)
+               ; /* TODO: BMAC BW Set (chanspec.b_width) */
+
+       /* TODO: use defines */
+       if (chanspec.b_width == 3) {
+               if (chanspec.sideband == 2)
+                       b43_phy_set(dev, B43_NPHY_RXCTL,
+                                       B43_NPHY_RXCTL_BSELU20);
+               else
+                       b43_phy_mask(dev, B43_NPHY_RXCTL,
+                                       ~B43_NPHY_RXCTL_BSELU20);
+       }
+
+       if (dev->phy.rev >= 3) {
+               tmp = (chanspec.b_freq == 1) ? 4 : 0;
+               b43_radio_maskset(dev, 0x08, 0xFFFB, tmp);
+               /* TODO: PHY Radio2056 Setup (dev, tabent_r3); */
+               b43_nphy_chanspec_setup(dev, &(tabent_r3->phy_regs), chanspec);
+       } else {
+               tmp = (chanspec.b_freq == 1) ? 0x0020 : 0x0050;
+               b43_radio_maskset(dev, B2055_MASTER1, 0xFF8F, tmp);
+               b43_radio_2055_setup(dev, tabent_r2);
+               b43_nphy_chanspec_setup(dev, &(tabent_r2->phy_regs), chanspec);
+       }
+
+       return 0;
+}
+
+/* Tune the hardware to a new channel */
+static int nphy_channel_switch(struct b43_wldev *dev, unsigned int channel)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       struct b43_chanspec chanspec;
+       chanspec = nphy->radio_chanspec;
+       chanspec.channel = channel;
+
+       return b43_nphy_set_chanspec(dev, chanspec);
+}
+
 static int b43_nphy_op_allocate(struct b43_wldev *dev)
 {
        struct b43_phy_n *nphy;
@@ -2444,9 +3483,43 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
        b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/Radio/Switch%20Radio */
 static void b43_nphy_op_software_rfkill(struct b43_wldev *dev,
                                        bool blocked)
-{//TODO
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
+               b43err(dev->wl, "MAC not suspended\n");
+
+       if (blocked) {
+               b43_phy_mask(dev, B43_NPHY_RFCTL_CMD,
+                               ~B43_NPHY_RFCTL_CMD_CHIP0PU);
+               if (dev->phy.rev >= 3) {
+                       b43_radio_mask(dev, 0x09, ~0x2);
+
+                       b43_radio_write(dev, 0x204D, 0);
+                       b43_radio_write(dev, 0x2053, 0);
+                       b43_radio_write(dev, 0x2058, 0);
+                       b43_radio_write(dev, 0x205E, 0);
+                       b43_radio_mask(dev, 0x2062, ~0xF0);
+                       b43_radio_write(dev, 0x2064, 0);
+
+                       b43_radio_write(dev, 0x304D, 0);
+                       b43_radio_write(dev, 0x3053, 0);
+                       b43_radio_write(dev, 0x3058, 0);
+                       b43_radio_write(dev, 0x305E, 0);
+                       b43_radio_mask(dev, 0x3062, ~0xF0);
+                       b43_radio_write(dev, 0x3064, 0);
+               }
+       } else {
+               if (dev->phy.rev >= 3) {
+                       b43_radio_init2056(dev);
+                       b43_nphy_set_chanspec(dev, nphy->radio_chanspec);
+               } else {
+                       b43_radio_init2055(dev);
+               }
+       }
 }
 
 static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on)