]> git.karo-electronics.de Git - linux-beck.git/commitdiff
b43: LP-PHY: Implement channel switching for rev2+/B2063 radio
authorGábor Stefanik <netrolller.3d@gmail.com>
Thu, 13 Aug 2009 20:46:30 +0000 (22:46 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 20 Aug 2009 15:35:50 +0000 (11:35 -0400)
Rev.2+/B2063 will now hopefully show some signs of life, though
it won't work at full performance, as calibration is still missing.

Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/b43/phy_lp.c

index 996b7eccfaf876d1666d6f73d7efb820e64dde47..86da8281d9295c9b8166ae2e7760b9c7f1b775c3 100644 (file)
 #include "tables_lpphy.h"
 
 
+static inline u16 channel2freq_lp(u8 channel)
+{
+       if (channel < 14)
+               return (2407 + 5 * channel);
+       else if (channel == 14)
+               return 2484;
+       else if (channel < 184)
+               return (5000 + 5 * channel);
+       else
+               return (4000 + 5 * channel);
+}
+
+static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev)
+{
+       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+               return 1;
+       return 36;
+}
+
 static int b43_lpphy_op_allocate(struct b43_wldev *dev)
 {
        struct b43_phy_lp *lpphy;
@@ -142,10 +161,9 @@ static void lpphy_read_band_sprom(struct b43_wldev *dev)
        }
 }
 
-static void lpphy_adjust_gain_table(struct b43_wldev *dev)
+static void lpphy_adjust_gain_table(struct b43_wldev *dev, u32 freq)
 {
        struct b43_phy_lp *lpphy = dev->phy.lp;
-       u32 freq = dev->wl->hw->conf.channel->center_freq;
        u16 temp[3];
        u16 isolation;
 
@@ -170,6 +188,8 @@ static void lpphy_adjust_gain_table(struct b43_wldev *dev)
 
 static void lpphy_table_init(struct b43_wldev *dev)
 {
+       u32 freq = channel2freq_lp(b43_lpphy_op_get_default_chan(dev));
+
        if (dev->phy.rev < 2)
                lpphy_rev0_1_table_init(dev);
        else
@@ -178,7 +198,7 @@ static void lpphy_table_init(struct b43_wldev *dev)
        lpphy_init_tx_gain_table(dev);
 
        if (dev->phy.rev < 2)
-               lpphy_adjust_gain_table(dev);
+               lpphy_adjust_gain_table(dev, freq);
 }
 
 static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
@@ -1363,20 +1383,6 @@ static void lpphy_tx_pctl_init(struct b43_wldev *dev)
        }
 }
 
-static int b43_lpphy_op_init(struct b43_wldev *dev)
-{
-       lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs?
-       lpphy_baseband_init(dev);
-       lpphy_radio_init(dev);
-       lpphy_calibrate_rc(dev);
-       //TODO set channel
-       lpphy_tx_pctl_init(dev);
-       lpphy_calibration(dev);
-       //TODO ACI init
-
-       return 0;
-}
-
 static u16 b43_lpphy_op_read(struct b43_wldev *dev, u16 reg)
 {
        b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
@@ -1419,18 +1425,392 @@ static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
        //TODO
 }
 
+struct b206x_channel {
+       u8 channel;
+       u16 freq;
+       u8 data[12];
+};
+
+static const struct b206x_channel b2063_chantbl[] = {
+       { .channel = 1, .freq = 2412, .data[0] = 0x6F, .data[1] = 0x3C,
+         .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 2, .freq = 2417, .data[0] = 0x6F, .data[1] = 0x3C,
+         .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 3, .freq = 2422, .data[0] = 0x6F, .data[1] = 0x3C,
+         .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 4, .freq = 2427, .data[0] = 0x6F, .data[1] = 0x2C,
+         .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 5, .freq = 2432, .data[0] = 0x6F, .data[1] = 0x2C,
+         .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 6, .freq = 2437, .data[0] = 0x6F, .data[1] = 0x2C,
+         .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 7, .freq = 2442, .data[0] = 0x6F, .data[1] = 0x2C,
+         .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 8, .freq = 2447, .data[0] = 0x6F, .data[1] = 0x2C,
+         .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 9, .freq = 2452, .data[0] = 0x6F, .data[1] = 0x1C,
+         .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 10, .freq = 2457, .data[0] = 0x6F, .data[1] = 0x1C,
+         .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 11, .freq = 2462, .data[0] = 0x6E, .data[1] = 0x1C,
+         .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 12, .freq = 2467, .data[0] = 0x6E, .data[1] = 0x1C,
+         .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 13, .freq = 2472, .data[0] = 0x6E, .data[1] = 0x1C,
+         .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 14, .freq = 2484, .data[0] = 0x6E, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
+         .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x80, .data[11] = 0x70, },
+       { .channel = 34, .freq = 5170, .data[0] = 0x6A, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x02, .data[5] = 0x05,
+         .data[6] = 0x0D, .data[7] = 0x0D, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 36, .freq = 5180, .data[0] = 0x6A, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x05,
+         .data[6] = 0x0D, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 38, .freq = 5190, .data[0] = 0x6A, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04,
+         .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 40, .freq = 5200, .data[0] = 0x69, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04,
+         .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 42, .freq = 5210, .data[0] = 0x69, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04,
+         .data[6] = 0x0B, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 44, .freq = 5220, .data[0] = 0x69, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x04,
+         .data[6] = 0x0B, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 46, .freq = 5230, .data[0] = 0x69, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03,
+         .data[6] = 0x0A, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 48, .freq = 5240, .data[0] = 0x69, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03,
+         .data[6] = 0x0A, .data[7] = 0x0A, .data[8] = 0x77, .data[9] = 0x60,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 52, .freq = 5260, .data[0] = 0x68, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x02,
+         .data[6] = 0x09, .data[7] = 0x09, .data[8] = 0x77, .data[9] = 0x60,
+         .data[10] = 0x20, .data[11] = 0x00, },
+       { .channel = 56, .freq = 5280, .data[0] = 0x68, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01,
+         .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50,
+         .data[10] = 0x10, .data[11] = 0x00, },
+       { .channel = 60, .freq = 5300, .data[0] = 0x68, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01,
+         .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50,
+         .data[10] = 0x10, .data[11] = 0x00, },
+       { .channel = 64, .freq = 5320, .data[0] = 0x67, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50,
+         .data[10] = 0x10, .data[11] = 0x00, },
+       { .channel = 100, .freq = 5500, .data[0] = 0x64, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x02, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 104, .freq = 5520, .data[0] = 0x64, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x01, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 108, .freq = 5540, .data[0] = 0x63, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x01, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 112, .freq = 5560, .data[0] = 0x63, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 116, .freq = 5580, .data[0] = 0x62, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 120, .freq = 5600, .data[0] = 0x62, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 124, .freq = 5620, .data[0] = 0x62, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 128, .freq = 5640, .data[0] = 0x61, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 132, .freq = 5660, .data[0] = 0x61, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 136, .freq = 5680, .data[0] = 0x61, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 140, .freq = 5700, .data[0] = 0x60, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 149, .freq = 5745, .data[0] = 0x60, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 153, .freq = 5765, .data[0] = 0x60, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 157, .freq = 5785, .data[0] = 0x60, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 161, .freq = 5805, .data[0] = 0x60, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 165, .freq = 5825, .data[0] = 0x60, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
+         .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
+         .data[10] = 0x00, .data[11] = 0x00, },
+       { .channel = 184, .freq = 4920, .data[0] = 0x6E, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0E,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xC0,
+         .data[10] = 0x50, .data[11] = 0x00, },
+       { .channel = 188, .freq = 4940, .data[0] = 0x6E, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0D,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0,
+         .data[10] = 0x50, .data[11] = 0x00, },
+       { .channel = 192, .freq = 4960, .data[0] = 0x6E, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0,
+         .data[10] = 0x50, .data[11] = 0x00, },
+       { .channel = 196, .freq = 4980, .data[0] = 0x6D, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0,
+         .data[10] = 0x40, .data[11] = 0x00, },
+       { .channel = 200, .freq = 5000, .data[0] = 0x6D, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0B,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0,
+         .data[10] = 0x40, .data[11] = 0x00, },
+       { .channel = 204, .freq = 5020, .data[0] = 0x6D, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0A,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0,
+         .data[10] = 0x40, .data[11] = 0x00, },
+       { .channel = 208, .freq = 5040, .data[0] = 0x6C, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x07, .data[5] = 0x09,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90,
+         .data[10] = 0x40, .data[11] = 0x00, },
+       { .channel = 212, .freq = 5060, .data[0] = 0x6C, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x06, .data[5] = 0x08,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90,
+         .data[10] = 0x40, .data[11] = 0x00, },
+       { .channel = 216, .freq = 5080, .data[0] = 0x6C, .data[1] = 0x0C,
+         .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x05, .data[5] = 0x08,
+         .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90,
+         .data[10] = 0x40, .data[11] = 0x00, },
+};
+
+static void lpphy_b2062_tune(struct b43_wldev *dev,
+                            unsigned int channel)
+{
+       //TODO
+}
+
+static void lpphy_b2063_vco_calib(struct b43_wldev *dev)
+{
+       u16 tmp;
+
+       b43_phy_mask(dev, B2063_PLL_SP1, ~0x40);
+       tmp = b43_phy_read(dev, B2063_PLL_JTAG_CALNRST) & 0xF8;
+       b43_phy_write(dev, B2063_PLL_JTAG_CALNRST, tmp);
+       udelay(1);
+       b43_phy_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x4);
+       udelay(1);
+       b43_phy_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x6);
+       udelay(1);
+       b43_phy_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x7);
+       udelay(300);
+       b43_phy_set(dev, B2063_PLL_SP1, 0x40);
+}
+
+static void lpphy_b2063_tune(struct b43_wldev *dev,
+                            unsigned int channel)
+{
+       struct ssb_bus *bus = dev->dev->bus;
+
+       static const struct b206x_channel *chandata = NULL;
+       u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
+       u32 freqref, vco_freq, val1, val2, val3, timeout, timeoutref, count;
+       u16 old_comm15, scale;
+       u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
+       int i, div = (crystal_freq <= 26000000 ? 1 : 2);
+
+       for (i = 0; i < ARRAY_SIZE(b2063_chantbl); i++) {
+               if (b2063_chantbl[i].channel == channel) {
+                       chandata = &b2063_chantbl[i];
+                       break;
+               }
+       }
+
+       if (B43_WARN_ON(!chandata))
+               return;
+
+       b43_radio_write(dev, B2063_LOGEN_VCOBUF1, chandata->data[0]);
+       b43_radio_write(dev, B2063_LOGEN_MIXER2, chandata->data[1]);
+       b43_radio_write(dev, B2063_LOGEN_BUF2, chandata->data[2]);
+       b43_radio_write(dev, B2063_LOGEN_RCCR1, chandata->data[3]);
+       b43_radio_write(dev, B2063_A_RX_1ST3, chandata->data[4]);
+       b43_radio_write(dev, B2063_A_RX_2ND1, chandata->data[5]);
+       b43_radio_write(dev, B2063_A_RX_2ND4, chandata->data[6]);
+       b43_radio_write(dev, B2063_A_RX_2ND7, chandata->data[7]);
+       b43_radio_write(dev, B2063_A_RX_PS6, chandata->data[8]);
+       b43_radio_write(dev, B2063_TX_RF_CTL2, chandata->data[9]);
+       b43_radio_write(dev, B2063_TX_RF_CTL5, chandata->data[10]);
+       b43_radio_write(dev, B2063_PA_CTL11, chandata->data[11]);
+
+       old_comm15 = b43_radio_read(dev, B2063_COMM15);
+       b43_radio_set(dev, B2063_COMM15, 0x1E);
+
+       if (chandata->freq > 4000) /* spec says 2484, but 4000 is safer */
+               vco_freq = chandata->freq << 1;
+       else
+               vco_freq = chandata->freq << 2;
+
+       freqref = crystal_freq * 3;
+       val1 = lpphy_qdiv_roundup(crystal_freq, 1000000, 16);
+       val2 = lpphy_qdiv_roundup(crystal_freq, 1000000 * div, 16);
+       val3 = lpphy_qdiv_roundup(vco_freq, 3, 16);
+       timeout = ((((8 * crystal_freq) / (div * 5000000)) + 1) >> 1) - 1;
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB3, 0x2);
+       b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB6,
+                         0xFFF8, timeout >> 2);
+       b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7,
+                         0xFF9F,timeout << 5);
+
+       timeoutref = ((((8 * crystal_freq) / (div * (timeout + 1))) +
+                                               999999) / 1000000) + 1;
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB5, timeoutref);
+
+       count = lpphy_qdiv_roundup(val3, val2 + 16, 16);
+       count *= (timeout + 1) * (timeoutref + 1);
+       count--;
+       b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7,
+                                               0xF0, count >> 8);
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB8, count & 0xFF);
+
+       tmp1 = ((val3 * 62500) / freqref) << 4;
+       tmp2 = ((val3 * 62500) % freqref) << 4;
+       while (tmp2 >= freqref) {
+               tmp1++;
+               tmp2 -= freqref;
+       }
+       b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG1, 0xFFE0, tmp1 >> 4);
+       b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFE0F, tmp1 << 4);
+       b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFFF0, tmp1 >> 16);
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG3, (tmp2 >> 8) & 0xFF);
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG4, tmp2 & 0xFF);
+
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF1, 0xB9);
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF2, 0x88);
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF3, 0x28);
+       b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF4, 0x63);
+
+       tmp3 = ((41 * (val3 - 3000)) /1200) + 27;
+       tmp4 = lpphy_qdiv_roundup(132000 * tmp1, 8451, 16);
+
+       if ((tmp4 + tmp3 - 1) / tmp3 > 60) {
+               scale = 1;
+               tmp5 = ((tmp4 + tmp3) / (tmp3 << 1)) - 8;
+       } else {
+               scale = 0;
+               tmp5 = ((tmp4 + (tmp3 >> 1)) / tmp3) - 8;
+       }
+       b43_phy_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFC0, tmp5);
+       b43_phy_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFBF, scale << 6);
+
+       tmp6 = lpphy_qdiv_roundup(100 * val1, val3, 16);
+       tmp6 *= (tmp5 * 8) * (scale + 1);
+       if (tmp6 > 150)
+               tmp6 = 0;
+
+       b43_phy_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFE0, tmp6);
+       b43_phy_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFDF, scale << 5);
+
+       b43_phy_maskset(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFFFB, 0x4);
+       if (crystal_freq > 26000000)
+               b43_phy_set(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0x2);
+       else
+               b43_phy_mask(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFD);
+
+       if (val1 == 45)
+               b43_phy_set(dev, B2063_PLL_JTAG_PLL_VCO1, 0x2);
+       else
+               b43_phy_mask(dev, B2063_PLL_JTAG_PLL_VCO1, 0xFD);
+
+       b43_phy_set(dev, B2063_PLL_SP2, 0x3);
+       udelay(1);
+       b43_phy_mask(dev, B2063_PLL_SP2, 0xFFFC);
+       lpphy_b2063_vco_calib(dev);
+       b43_radio_write(dev, B2063_COMM15, old_comm15);
+}
+
 static int b43_lpphy_op_switch_channel(struct b43_wldev *dev,
                                       unsigned int new_channel)
 {
-       //TODO
+       b43_write16(dev, B43_MMIO_CHANNEL, new_channel);
+
+       if (dev->phy.radio_ver == 0x2063) {
+               lpphy_b2063_tune(dev, new_channel);
+       } else {
+               lpphy_b2062_tune(dev, new_channel);
+               //TODO Japan filter
+       }
+
+       lpphy_adjust_gain_table(dev, channel2freq_lp(new_channel));
+
        return 0;
 }
 
-static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev)
+static int b43_lpphy_op_init(struct b43_wldev *dev)
 {
-       if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
-               return 1;
-       return 36;
+       lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs?
+       lpphy_baseband_init(dev);
+       lpphy_radio_init(dev);
+       lpphy_calibrate_rc(dev);
+       b43_lpphy_op_switch_channel(dev, b43_lpphy_op_get_default_chan(dev));
+       lpphy_tx_pctl_init(dev);
+       lpphy_calibration(dev);
+       //TODO ACI init
+
+       return 0;
 }
 
 static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)