b43: N-PHY: implement restoring general configuration
[safe/jmp/linux-2.6] / drivers / net / wireless / b43 / phy_n.c
index 30b9dac..68c1582 100644 (file)
@@ -28,6 +28,7 @@
 #include "b43.h"
 #include "phy_n.h"
 #include "tables_nphy.h"
+#include "main.h"
 
 struct nphy_txgains {
        u16 txgm[2];
@@ -343,6 +344,40 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
        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)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+       enum ieee80211_band band;
+       u16 tmp;
+
+       if (!enable) {
+               nphy->rfctrl_intc1_save = b43_phy_read(dev,
+                                                      B43_NPHY_RFCTL_INTC1);
+               nphy->rfctrl_intc2_save = b43_phy_read(dev,
+                                                      B43_NPHY_RFCTL_INTC2);
+               band = b43_current_band(dev->wl);
+               if (dev->phy.rev >= 3) {
+                       if (band == IEEE80211_BAND_5GHZ)
+                               tmp = 0x600;
+                       else
+                               tmp = 0x480;
+               } else {
+                       if (band == IEEE80211_BAND_5GHZ)
+                               tmp = 0x180;
+                       else
+                               tmp = 0x120;
+               }
+               b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp);
+               b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp);
+       } else {
+               b43_phy_write(dev, B43_NPHY_RFCTL_INTC1,
+                               nphy->rfctrl_intc1_save);
+               b43_phy_write(dev, B43_NPHY_RFCTL_INTC2,
+                               nphy->rfctrl_intc2_save);
+       }
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
 static void b43_nphy_bmac_clock_fgc(struct b43_wldev *dev, bool force)
 {
@@ -373,6 +408,97 @@ static void b43_nphy_reset_cca(struct b43_wldev *dev)
        /* TODO: N PHY Force RF Seq with argument 2 */
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqCoeffs */
+static void b43_nphy_rx_iq_coeffs(struct b43_wldev *dev, bool write,
+                                       struct b43_phy_n_iq_comp *pcomp)
+{
+       if (write) {
+               b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPA0, pcomp->a0);
+               b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPB0, pcomp->b0);
+               b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPA1, pcomp->a1);
+               b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPB1, pcomp->b1);
+       } else {
+               pcomp->a0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPA0);
+               pcomp->b0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPB0);
+               pcomp->a1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPA1);
+               pcomp->b1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPB1);
+       }
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxIqWar */
+static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev)
+{
+       u16 array[4];
+       int i;
+
+       b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C50);
+       for (i = 0; i < 4; i++)
+               array[i] = b43_phy_read(dev, B43_NPHY_TABLE_DATALO);
+
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW2, array[2]);
+       b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW3, array[3]);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */
+static void b43_nphy_write_clip_detection(struct b43_wldev *dev, u16 *clip_st)
+{
+       b43_phy_write(dev, B43_NPHY_C1_CLIP1THRES, clip_st[0]);
+       b43_phy_write(dev, B43_NPHY_C2_CLIP1THRES, clip_st[1]);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */
+static void b43_nphy_read_clip_detection(struct b43_wldev *dev, u16 *clip_st)
+{
+       clip_st[0] = b43_phy_read(dev, B43_NPHY_C1_CLIP1THRES);
+       clip_st[1] = b43_phy_read(dev, B43_NPHY_C2_CLIP1THRES);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/classifier */
+static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val)
+{
+       u16 tmp;
+
+       if (dev->dev->id.revision == 16)
+               b43_mac_suspend(dev);
+
+       tmp = b43_phy_read(dev, B43_NPHY_CLASSCTL);
+       tmp &= (B43_NPHY_CLASSCTL_CCKEN | B43_NPHY_CLASSCTL_OFDMEN |
+               B43_NPHY_CLASSCTL_WAITEDEN);
+       tmp &= ~mask;
+       tmp |= (val & mask);
+       b43_phy_maskset(dev, B43_NPHY_CLASSCTL, 0xFFF8, tmp);
+
+       if (dev->dev->id.revision == 16)
+               b43_mac_enable(dev);
+
+       return tmp;
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/carriersearch */
+static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable)
+{
+       struct b43_phy *phy = &dev->phy;
+       struct b43_phy_n *nphy = phy->n;
+
+       if (enable) {
+               u16 clip[] = { 0xFFFF, 0xFFFF };
+               if (nphy->deaf_count++ == 0) {
+                       nphy->classifier_state = b43_nphy_classifier(dev, 0, 0);
+                       b43_nphy_classifier(dev, 0x7, 0);
+                       b43_nphy_read_clip_detection(dev, nphy->clip_state);
+                       b43_nphy_write_clip_detection(dev, clip);
+               }
+               b43_nphy_reset_cca(dev);
+       } else {
+               if (--nphy->deaf_count == 0) {
+                       b43_nphy_classifier(dev, 0x7, nphy->classifier_state);
+                       b43_nphy_write_clip_detection(dev, nphy->clip_state);
+               }
+       }
+}
+
 enum b43_nphy_rf_sequence {
        B43_RFSEQ_RX2TX,
        B43_RFSEQ_TX2RX,
@@ -429,10 +555,380 @@ static void b43_nphy_bphy_init(struct b43_wldev *dev)
        b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ScaleOffsetRssi */
+static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale,
+                                      s8 offset, u8 core, u8 rail, u8 type)
+{
+       u16 tmp;
+       bool core1or5 = (core == 1) || (core == 5);
+       bool core2or5 = (core == 2) || (core == 5);
+
+       offset = clamp_val(offset, -32, 31);
+       tmp = ((scale & 0x3F) << 8) | (offset & 0x3F);
+
+       if (core1or5 && (rail == 0) && (type == 2))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, tmp);
+       if (core1or5 && (rail == 1) && (type == 2))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, tmp);
+       if (core2or5 && (rail == 0) && (type == 2))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, tmp);
+       if (core2or5 && (rail == 1) && (type == 2))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, tmp);
+       if (core1or5 && (rail == 0) && (type == 0))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, tmp);
+       if (core1or5 && (rail == 1) && (type == 0))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, tmp);
+       if (core2or5 && (rail == 0) && (type == 0))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, tmp);
+       if (core2or5 && (rail == 1) && (type == 0))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, tmp);
+       if (core1or5 && (rail == 0) && (type == 1))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, tmp);
+       if (core1or5 && (rail == 1) && (type == 1))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, tmp);
+       if (core2or5 && (rail == 0) && (type == 1))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, tmp);
+       if (core2or5 && (rail == 1) && (type == 1))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, tmp);
+       if (core1or5 && (rail == 0) && (type == 6))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TBD, tmp);
+       if (core1or5 && (rail == 1) && (type == 6))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TBD, tmp);
+       if (core2or5 && (rail == 0) && (type == 6))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TBD, tmp);
+       if (core2or5 && (rail == 1) && (type == 6))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TBD, tmp);
+       if (core1or5 && (rail == 0) && (type == 3))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0I_PWRDET, tmp);
+       if (core1or5 && (rail == 1) && (type == 3))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_PWRDET, tmp);
+       if (core2or5 && (rail == 0) && (type == 3))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1I_PWRDET, tmp);
+       if (core2or5 && (rail == 1) && (type == 3))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_PWRDET, tmp);
+       if (core1or5 && (type == 4))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TSSI, tmp);
+       if (core2or5 && (type == 4))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TSSI, tmp);
+       if (core1or5 && (type == 5))
+               b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TSSI, tmp);
+       if (core2or5 && (type == 5))
+               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)
+{
+       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;
+
+               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_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);
+                       }
+               }
+       }
+}
+
+/* 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)
+{
+       int i;
+       for (i = 0; i < 2; i++) {
+               if (type == 2) {
+                       if (i == 0) {
+                               b43_radio_maskset(dev, B2055_C1_B0NB_RSSIVCM,
+                                                 0xFC, buf[0]);
+                               b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5,
+                                                 0xFC, buf[1]);
+                       } else {
+                               b43_radio_maskset(dev, B2055_C2_B0NB_RSSIVCM,
+                                                 0xFC, buf[2 * i]);
+                               b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5,
+                                                 0xFC, buf[2 * i + 1]);
+                       }
+               } else {
+                       if (i == 0)
+                               b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5,
+                                                 0xF3, buf[0] << 2);
+                       else
+                               b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5,
+                                                 0xF3, buf[2 * i + 1] << 2);
+               }
+       }
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PollRssi */
+static int b43_nphy_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf,
+                               u8 nsamp)
+{
+       int i;
+       int out;
+       u16 save_regs_phy[9];
+       u16 s[2];
+
+       if (dev->phy.rev >= 3) {
+               save_regs_phy[0] = b43_phy_read(dev,
+                                               B43_NPHY_RFCTL_LUT_TRSW_UP1);
+               save_regs_phy[1] = b43_phy_read(dev,
+                                               B43_NPHY_RFCTL_LUT_TRSW_UP2);
+               save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_C1);
+               save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_AFECTL_C2);
+               save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1);
+               save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER);
+               save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0);
+               save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1);
+       }
+
+       b43_nphy_rssi_select(dev, 5, type);
+
+       if (dev->phy.rev < 2) {
+               save_regs_phy[8] = b43_phy_read(dev, B43_NPHY_GPIO_SEL);
+               b43_phy_write(dev, B43_NPHY_GPIO_SEL, 5);
+       }
+
+       for (i = 0; i < 4; i++)
+               buf[i] = 0;
+
+       for (i = 0; i < nsamp; i++) {
+               if (dev->phy.rev < 2) {
+                       s[0] = b43_phy_read(dev, B43_NPHY_GPIO_LOOUT);
+                       s[1] = b43_phy_read(dev, B43_NPHY_GPIO_HIOUT);
+               } else {
+                       s[0] = b43_phy_read(dev, B43_NPHY_RSSI1);
+                       s[1] = b43_phy_read(dev, B43_NPHY_RSSI2);
+               }
+
+               buf[0] += ((s8)((s[0] & 0x3F) << 2)) >> 2;
+               buf[1] += ((s8)(((s[0] >> 8) & 0x3F) << 2)) >> 2;
+               buf[2] += ((s8)((s[1] & 0x3F) << 2)) >> 2;
+               buf[3] += ((s8)(((s[1] >> 8) & 0x3F) << 2)) >> 2;
+       }
+       out = (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 |
+               (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF);
+
+       if (dev->phy.rev < 2)
+               b43_phy_write(dev, B43_NPHY_GPIO_SEL, save_regs_phy[8]);
+
+       if (dev->phy.rev >= 3) {
+               b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1,
+                               save_regs_phy[0]);
+               b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2,
+                               save_regs_phy[1]);
+               b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[2]);
+               b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[3]);
+               b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, save_regs_phy[4]);
+               b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]);
+               b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]);
+               b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]);
+       }
+
+       return out;
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */
 static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, u8 type)
 {
-       /* TODO */
+       int i, j;
+       u8 state[4];
+       u8 code, val;
+       u16 class, override;
+       u8 regs_save_radio[2];
+       u16 regs_save_phy[2];
+       s8 offset[4];
+
+       u16 clip_state[2];
+       u16 clip_off[2] = { 0xFFFF, 0xFFFF };
+       s32 results_min[4] = { };
+       u8 vcm_final[4] = { };
+       s32 results[4][4] = { };
+       s32 miniq[4][2] = { };
+
+       if (type == 2) {
+               code = 0;
+               val = 6;
+       } else if (type < 2) {
+               code = 25;
+               val = 4;
+       } else {
+               B43_WARN_ON(1);
+               return;
+       }
+
+       class = b43_nphy_classifier(dev, 0, 0);
+       b43_nphy_classifier(dev, 7, 4);
+       b43_nphy_read_clip_detection(dev, clip_state);
+       b43_nphy_write_clip_detection(dev, clip_off);
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+               override = 0x140;
+       else
+               override = 0x110;
+
+       regs_save_phy[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
+       regs_save_radio[0] = b43_radio_read16(dev, B2055_C1_PD_RXTX);
+       b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, override);
+       b43_radio_write16(dev, B2055_C1_PD_RXTX, val);
+
+       regs_save_phy[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
+       regs_save_radio[1] = b43_radio_read16(dev, B2055_C2_PD_RXTX);
+       b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, override);
+       b43_radio_write16(dev, B2055_C2_PD_RXTX, val);
+
+       state[0] = b43_radio_read16(dev, B2055_C1_PD_RSSIMISC) & 0x07;
+       state[1] = b43_radio_read16(dev, B2055_C2_PD_RSSIMISC) & 0x07;
+       b43_radio_mask(dev, B2055_C1_PD_RSSIMISC, 0xF8);
+       b43_radio_mask(dev, B2055_C2_PD_RSSIMISC, 0xF8);
+       state[2] = b43_radio_read16(dev, B2055_C1_SP_RSSI) & 0x07;
+       state[3] = b43_radio_read16(dev, B2055_C2_SP_RSSI) & 0x07;
+
+       b43_nphy_rssi_select(dev, 5, type);
+       b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 0, type);
+       b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 1, type);
+
+       for (i = 0; i < 4; i++) {
+               u8 tmp[4];
+               for (j = 0; j < 4; j++)
+                       tmp[j] = i;
+               if (type != 1)
+                       b43_nphy_set_rssi_2055_vcm(dev, type, tmp);
+               b43_nphy_poll_rssi(dev, type, results[i], 8);
+               if (type < 2)
+                       for (j = 0; j < 2; j++)
+                               miniq[i][j] = min(results[i][2 * j],
+                                               results[i][2 * j + 1]);
+       }
+
+       for (i = 0; i < 4; i++) {
+               s32 mind = 40;
+               u8 minvcm = 0;
+               s32 minpoll = 249;
+               s32 curr;
+               for (j = 0; j < 4; j++) {
+                       if (type == 2)
+                               curr = abs(results[j][i]);
+                       else
+                               curr = abs(miniq[j][i / 2] - code * 8);
+
+                       if (curr < mind) {
+                               mind = curr;
+                               minvcm = j;
+                       }
+
+                       if (results[j][i] < minpoll)
+                               minpoll = results[j][i];
+               }
+               results_min[i] = minpoll;
+               vcm_final[i] = minvcm;
+       }
+
+       if (type != 1)
+               b43_nphy_set_rssi_2055_vcm(dev, type, vcm_final);
+
+       for (i = 0; i < 4; i++) {
+               offset[i] = (code * 8) - results[vcm_final[i]][i];
+
+               if (offset[i] < 0)
+                       offset[i] = -((abs(offset[i]) + 4) / 8);
+               else
+                       offset[i] = (offset[i] + 4) / 8;
+
+               if (results_min[i] == 248)
+                       offset[i] = code - 32;
+
+               if (i % 2 == 0)
+                       b43_nphy_scale_offset_rssi(dev, 0, offset[i], 1, 0,
+                                                       type);
+               else
+                       b43_nphy_scale_offset_rssi(dev, 0, offset[i], 2, 1,
+                                                       type);
+       }
+
+       b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[0]);
+       b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[1]);
+
+       switch (state[2]) {
+       case 1:
+               b43_nphy_rssi_select(dev, 1, 2);
+               break;
+       case 4:
+               b43_nphy_rssi_select(dev, 1, 0);
+               break;
+       case 2:
+               b43_nphy_rssi_select(dev, 1, 1);
+               break;
+       default:
+               b43_nphy_rssi_select(dev, 1, 1);
+               break;
+       }
+
+       switch (state[3]) {
+       case 1:
+               b43_nphy_rssi_select(dev, 2, 2);
+               break;
+       case 4:
+               b43_nphy_rssi_select(dev, 2, 0);
+               break;
+       default:
+               b43_nphy_rssi_select(dev, 2, 1);
+               break;
+       }
+
+       b43_nphy_rssi_select(dev, 0, type);
+
+       b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs_save_phy[0]);
+       b43_radio_write16(dev, B2055_C1_PD_RXTX, regs_save_radio[0]);
+       b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs_save_phy[1]);
+       b43_radio_write16(dev, B2055_C2_PD_RXTX, regs_save_radio[1]);
+
+       b43_nphy_classifier(dev, 7, class);
+       b43_nphy_write_clip_detection(dev, clip_state);
 }
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */
@@ -457,6 +953,139 @@ static void b43_nphy_rssi_cal(struct b43_wldev *dev)
 }
 
 /*
+ * Restore RSSI Calibration
+ * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreRssiCal
+ */
+static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       u16 *rssical_radio_regs = NULL;
+       u16 *rssical_phy_regs = NULL;
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               if (!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)
+                       return;
+               rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G;
+               rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G;
+       }
+
+       /* TODO use some definitions */
+       b43_radio_maskset(dev, 0x602B, 0xE3, rssical_radio_regs[0]);
+       b43_radio_maskset(dev, 0x702B, 0xE3, rssical_radio_regs[1]);
+
+       b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, rssical_phy_regs[0]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, rssical_phy_regs[1]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, rssical_phy_regs[2]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, rssical_phy_regs[3]);
+
+       b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, rssical_phy_regs[4]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, rssical_phy_regs[5]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, rssical_phy_regs[6]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, rssical_phy_regs[7]);
+
+       b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, rssical_phy_regs[8]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, rssical_phy_regs[9]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, rssical_phy_regs[10]);
+       b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]);
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */
+static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
+{
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               if (dev->phy.rev >= 6) {
+                       /* TODO If the chip is 47162
+                               return txpwrctrl_tx_gain_ipa_rev5 */
+                       return txpwrctrl_tx_gain_ipa_rev6;
+               } else if (dev->phy.rev >= 5) {
+                       return txpwrctrl_tx_gain_ipa_rev5;
+               } else {
+                       return txpwrctrl_tx_gain_ipa;
+               }
+       } else {
+               return txpwrctrl_tx_gain_ipa_5g;
+       }
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */
+static void b43_nphy_restore_cal(struct b43_wldev *dev)
+{
+       struct b43_phy_n *nphy = dev->phy.n;
+
+       u16 coef[4];
+       u16 *loft = NULL;
+       u16 *table = NULL;
+
+       int i;
+       u16 *txcal_radio_regs = NULL;
+       struct b43_phy_n_iq_comp *rxcal_coeffs = NULL;
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               if (nphy->iqcal_chanspec_2G == 0)
+                       return;
+               table = nphy->cal_cache.txcal_coeffs_2G;
+               loft = &nphy->cal_cache.txcal_coeffs_2G[5];
+       } else {
+               if (nphy->iqcal_chanspec_5G == 0)
+                       return;
+               table = nphy->cal_cache.txcal_coeffs_5G;
+               loft = &nphy->cal_cache.txcal_coeffs_5G[5];
+       }
+
+       /* TODO: Write an N PHY table with ID 15, length 4, offset 80,
+               width 16, and data from table */
+
+       for (i = 0; i < 4; i++) {
+               if (dev->phy.rev >= 3)
+                       table[i] = coef[i];
+               else
+                       coef[i] = 0;
+       }
+
+       /* TODO: Write an N PHY table with ID 15, length 4, offset 88,
+               width 16, and data from coef */
+       /* TODO: Write an N PHY table with ID 15, length 2, offset 85,
+               width 16 and data from loft */
+       /* TODO: Write an N PHY table with ID 15, length 2, offset 93,
+               width 16 and data from loft */
+
+       if (dev->phy.rev < 2)
+               b43_nphy_tx_iq_workaround(dev);
+
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+               txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G;
+               rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G;
+       } else {
+               txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G;
+               rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G;
+       }
+
+       /* TODO use some definitions */
+       if (dev->phy.rev >= 3) {
+               b43_radio_write(dev, 0x2021, txcal_radio_regs[0]);
+               b43_radio_write(dev, 0x2022, txcal_radio_regs[1]);
+               b43_radio_write(dev, 0x3021, txcal_radio_regs[2]);
+               b43_radio_write(dev, 0x3022, txcal_radio_regs[3]);
+               b43_radio_write(dev, 0x2023, txcal_radio_regs[4]);
+               b43_radio_write(dev, 0x2024, txcal_radio_regs[5]);
+               b43_radio_write(dev, 0x3023, txcal_radio_regs[6]);
+               b43_radio_write(dev, 0x3024, txcal_radio_regs[7]);
+       } else {
+               b43_radio_write(dev, 0x8B, txcal_radio_regs[0]);
+               b43_radio_write(dev, 0xBA, txcal_radio_regs[1]);
+               b43_radio_write(dev, 0x8D, txcal_radio_regs[2]);
+               b43_radio_write(dev, 0xBC, txcal_radio_regs[3]);
+       }
+       b43_nphy_rx_iq_coeffs(dev, true, rxcal_coeffs);
+}
+
+/*
  * Init N-PHY
  * http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N
  */
@@ -558,13 +1187,13 @@ int b43_phy_initn(struct b43_wldev *dev)
 
        /* TODO N PHY MAC PHY Clock Set with argument 1 */
 
-       /* b43_nphy_pa_override(dev, false); */
+       b43_nphy_pa_override(dev, false);
        b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX);
        b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
-       /* b43_nphy_pa_override(dev, true); */
+       b43_nphy_pa_override(dev, true);
 
-       /* b43_nphy_classifier(dev, 0, 0); */
-       /* b43_nphy_read_clip_detection(dev, clip); */
+       b43_nphy_classifier(dev, 0, 0);
+       b43_nphy_read_clip_detection(dev, clip);
        tx_pwr_state = nphy->txpwrctrl;
        /* TODO N PHY TX power control with argument 0
                (turning off power control) */
@@ -594,7 +1223,7 @@ int b43_phy_initn(struct b43_wldev *dev)
                if (do_rssi_cal)
                        b43_nphy_rssi_cal(dev);
                else
-                       ;/* b43_nphy_restore_rssi_cal(dev); */
+                       b43_nphy_restore_rssi_cal(dev);
        } else {
                b43_nphy_rssi_cal(dev);
        }
@@ -614,7 +1243,7 @@ int b43_phy_initn(struct b43_wldev *dev)
                        if (nphy->antsel_type == 2)
                                ;/*TODO NPHY Superswitch Init with argument 1*/
                        if (nphy->perical != 2) {
-                               /* b43_nphy_rssi_cal(dev); */
+                               b43_nphy_rssi_cal(dev);
                                if (phy->rev >= 3) {
                                        nphy->cal_orig_pwr_idx[0] =
                                            nphy->txpwrindex[0].index_internal;