diff mbox

[v2] b43: LP-PHY: Begin implementing calibration & software RFKILL support

Message ID 4A99C4BB.7080806@gmail.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Gábor Stefanik Aug. 30, 2009, 12:15 a.m. UTC
This implements the following calibration functions:
-Set TX IQCC
-Set TX Power by Index
-PR41573 workaround
-Calc RX IQ Comp
-PHY Cordic
-Run Samples
-Start/Stop TX Tone
-part of PAPD Cal TX Power
-RX I/Q Calibration
-The basic structure of the periodic calibration wrapper

Software RFKILL (required by calibration) is also implemented in
this round.

Signed-off-by: Gábor Stefanik <netrolller.3d@gmail.com>
---
V2: Fix a mistake I made in the PHY Cordic routine.

 drivers/net/wireless/b43/main.c   |    2 +-
 drivers/net/wireless/b43/main.h   |    2 +
 drivers/net/wireless/b43/phy_lp.c |  686 ++++++++++++++++++++++++++++++++-----
 drivers/net/wireless/b43/phy_lp.h |   11 +-
 4 files changed, 613 insertions(+), 88 deletions(-)

Comments

Michael Buesch Aug. 30, 2009, 10:28 a.m. UTC | #1
On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>  {
>  	struct b43_phy_lp *lpphy = dev->phy.lp;
> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>  		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
>  				    saved_tab_size, saved_tab);
>  	}
> +	b43_put_phy_into_reset(dev);

Are you sure you really want this?
This function completely disables the PHY on the backplane and keeps the physical
PHY reset pin asserted (even after return from the function).
So the PHY will physically be powered down from this point on. The following
PHY accesses could even hang the machine, because the PHY won't respond to
register accesses anymore.

We currently only use this function on A/G Multi-PHY devices to permanently
hard-disable the PHY that's not used.

> +	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFF8);
> +	lpphy_table_init(dev); //FIXME is table init needed?
> +	lpphy_baseband_init(dev);
> +	b43_lpphy_op_software_rfkill(dev, false);
> +	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
> +	if (dev->phy.rev < 2) {
> +		b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140),
> +				     saved_tab_size, saved_tab);
> +	} else {
> +		b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140),
> +				     saved_tab_size, saved_tab);
> +	}
> +	b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel);
> +	lpphy->tssi_npt = tssi_npt;
> +	lpphy->tssi_idx = tssi_idx;
> +	lpphy_set_analog_filter(dev, lpphy->channel);
> +	if (tx_pwr_idx_over != -1)
> +		lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over);
> +	if (lpphy->rc_cap)
> +		lpphy_set_rc_cap(dev);
> +	b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna);
> +	lpphy_set_tx_power_control(dev, txpctl_mode);
> +	kfree(saved_tab);
> +}
Larry Finger Aug. 30, 2009, 3:10 p.m. UTC | #2
Michael Buesch wrote:
> On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
>>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>>  {
>>  	struct b43_phy_lp *lpphy = dev->phy.lp;
>> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>>  		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
>>  				    saved_tab_size, saved_tab);
>>  	}
>> +	b43_put_phy_into_reset(dev);
> 
> Are you sure you really want this?
> This function completely disables the PHY on the backplane and keeps the physical
> PHY reset pin asserted (even after return from the function).
> So the PHY will physically be powered down from this point on. The following
> PHY accesses could even hang the machine, because the PHY won't respond to
> register accesses anymore.
> 
> We currently only use this function on A/G Multi-PHY devices to permanently
> hard-disable the PHY that's not used.

The PHY reset routine in
http://bcm-v4.sipsolutions.net/802.11/PHY/Reset, which I just updated
for the latest N PHY changes, appears to be a different routine than
b43_put_phy_into_reset(). The names are confusing.

Larry
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michael Buesch Aug. 30, 2009, 3:55 p.m. UTC | #3
On Sunday 30 August 2009 17:10:23 Larry Finger wrote:
> Michael Buesch wrote:
> > On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
> >>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> >>  {
> >>  	struct b43_phy_lp *lpphy = dev->phy.lp;
> >> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> >>  		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
> >>  				    saved_tab_size, saved_tab);
> >>  	}
> >> +	b43_put_phy_into_reset(dev);
> > 
> > Are you sure you really want this?
> > This function completely disables the PHY on the backplane and keeps the physical
> > PHY reset pin asserted (even after return from the function).
> > So the PHY will physically be powered down from this point on. The following
> > PHY accesses could even hang the machine, because the PHY won't respond to
> > register accesses anymore.
> > 
> > We currently only use this function on A/G Multi-PHY devices to permanently
> > hard-disable the PHY that's not used.
> 
> The PHY reset routine in
> http://bcm-v4.sipsolutions.net/802.11/PHY/Reset, which I just updated
> for the latest N PHY changes, appears to be a different routine than
> b43_put_phy_into_reset(). The names are confusing.

b43_put_phy_into_reset() is opencoded in the specifications in various init
routines. There's no separate specs page for that function.
But I think the code is straightforward and easy to understand.
John W. Linville Aug. 31, 2009, 5:53 p.m. UTC | #4
On Sun, Aug 30, 2009 at 05:55:40PM +0200, Michael Buesch wrote:
> On Sunday 30 August 2009 17:10:23 Larry Finger wrote:
> > Michael Buesch wrote:
> > > On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
> > >>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> > >>  {
> > >>  	struct b43_phy_lp *lpphy = dev->phy.lp;
> > >> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> > >>  		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
> > >>  				    saved_tab_size, saved_tab);
> > >>  	}
> > >> +	b43_put_phy_into_reset(dev);
> > > 
> > > Are you sure you really want this?
> > > This function completely disables the PHY on the backplane and keeps the physical
> > > PHY reset pin asserted (even after return from the function).
> > > So the PHY will physically be powered down from this point on. The following
> > > PHY accesses could even hang the machine, because the PHY won't respond to
> > > register accesses anymore.
> > > 
> > > We currently only use this function on A/G Multi-PHY devices to permanently
> > > hard-disable the PHY that's not used.
> > 
> > The PHY reset routine in
> > http://bcm-v4.sipsolutions.net/802.11/PHY/Reset, which I just updated
> > for the latest N PHY changes, appears to be a different routine than
> > b43_put_phy_into_reset(). The names are confusing.
> 
> b43_put_phy_into_reset() is opencoded in the specifications in various init
> routines. There's no separate specs page for that function.
> But I think the code is straightforward and easy to understand.

So is this patch right or not?  Should I hold onto it for 2.6.33
(i.e. after the 2.6.32 merge window)?

John
Michael Buesch Aug. 31, 2009, 7:17 p.m. UTC | #5
On Monday 31 August 2009 19:53:31 John W. Linville wrote:
> On Sun, Aug 30, 2009 at 05:55:40PM +0200, Michael Buesch wrote:
> > On Sunday 30 August 2009 17:10:23 Larry Finger wrote:
> > > Michael Buesch wrote:
> > > > On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
> > > >>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> > > >>  {
> > > >>  	struct b43_phy_lp *lpphy = dev->phy.lp;
> > > >> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> > > >>  		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
> > > >>  				    saved_tab_size, saved_tab);
> > > >>  	}
> > > >> +	b43_put_phy_into_reset(dev);
> > > > 
> > > > Are you sure you really want this?
> > > > This function completely disables the PHY on the backplane and keeps the physical
> > > > PHY reset pin asserted (even after return from the function).
> > > > So the PHY will physically be powered down from this point on. The following
> > > > PHY accesses could even hang the machine, because the PHY won't respond to
> > > > register accesses anymore.
> > > > 
> > > > We currently only use this function on A/G Multi-PHY devices to permanently
> > > > hard-disable the PHY that's not used.
> > > 
> > > The PHY reset routine in
> > > http://bcm-v4.sipsolutions.net/802.11/PHY/Reset, which I just updated
> > > for the latest N PHY changes, appears to be a different routine than
> > > b43_put_phy_into_reset(). The names are confusing.
> > 
> > b43_put_phy_into_reset() is opencoded in the specifications in various init
> > routines. There's no separate specs page for that function.
> > But I think the code is straightforward and easy to understand.
> 
> So is this patch right or not?  Should I hold onto it for 2.6.33
> (i.e. after the 2.6.32 merge window)?

I'm pretty sure it's incorrect.
Larry Finger Aug. 31, 2009, 7:23 p.m. UTC | #6
Michael Buesch wrote:
> On Monday 31 August 2009 19:53:31 John W. Linville wrote:
>> On Sun, Aug 30, 2009 at 05:55:40PM +0200, Michael Buesch wrote:
>>> On Sunday 30 August 2009 17:10:23 Larry Finger wrote:
>>>> Michael Buesch wrote:
>>>>> On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
>>>>>>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>>>>>>  {
>>>>>>  	struct b43_phy_lp *lpphy = dev->phy.lp;
>>>>>> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>>>>>>  		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
>>>>>>  				    saved_tab_size, saved_tab);
>>>>>>  	}
>>>>>> +	b43_put_phy_into_reset(dev);
>>>>> Are you sure you really want this?
>>>>> This function completely disables the PHY on the backplane and keeps the physical
>>>>> PHY reset pin asserted (even after return from the function).
>>>>> So the PHY will physically be powered down from this point on. The following
>>>>> PHY accesses could even hang the machine, because the PHY won't respond to
>>>>> register accesses anymore.
>>>>>
>>>>> We currently only use this function on A/G Multi-PHY devices to permanently
>>>>> hard-disable the PHY that's not used.
>>>> The PHY reset routine in
>>>> http://bcm-v4.sipsolutions.net/802.11/PHY/Reset, which I just updated
>>>> for the latest N PHY changes, appears to be a different routine than
>>>> b43_put_phy_into_reset(). The names are confusing.
>>> b43_put_phy_into_reset() is opencoded in the specifications in various init
>>> routines. There's no separate specs page for that function.
>>> But I think the code is straightforward and easy to understand.
>> So is this patch right or not?  Should I hold onto it for 2.6.33
>> (i.e. after the 2.6.32 merge window)?
> 
> I'm pretty sure it's incorrect.

I agree.

Larry
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gábor Stefanik Aug. 31, 2009, 7:38 p.m. UTC | #7
On Mon, Aug 31, 2009 at 9:17 PM, Michael Buesch<mb@bu3sch.de> wrote:
> On Monday 31 August 2009 19:53:31 John W. Linville wrote:
>> On Sun, Aug 30, 2009 at 05:55:40PM +0200, Michael Buesch wrote:
>> > On Sunday 30 August 2009 17:10:23 Larry Finger wrote:
>> > > Michael Buesch wrote:
>> > > > On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
>> > > >>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>> > > >>  {
>> > > >>        struct b43_phy_lp *lpphy = dev->phy.lp;
>> > > >> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
>> > > >>                b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
>> > > >>                                    saved_tab_size, saved_tab);
>> > > >>        }
>> > > >> +      b43_put_phy_into_reset(dev);
>> > > >
>> > > > Are you sure you really want this?
>> > > > This function completely disables the PHY on the backplane and keeps the physical
>> > > > PHY reset pin asserted (even after return from the function).
>> > > > So the PHY will physically be powered down from this point on. The following
>> > > > PHY accesses could even hang the machine, because the PHY won't respond to
>> > > > register accesses anymore.
>> > > >
>> > > > We currently only use this function on A/G Multi-PHY devices to permanently
>> > > > hard-disable the PHY that's not used.
>> > >
>> > > The PHY reset routine in
>> > > http://bcm-v4.sipsolutions.net/802.11/PHY/Reset, which I just updated
>> > > for the latest N PHY changes, appears to be a different routine than
>> > > b43_put_phy_into_reset(). The names are confusing.
>> >
>> > b43_put_phy_into_reset() is opencoded in the specifications in various init
>> > routines. There's no separate specs page for that function.
>> > But I think the code is straightforward and easy to understand.
>>
>> So is this patch right or not?  Should I hold onto it for 2.6.33
>> (i.e. after the 2.6.32 merge window)?
>
> I'm pretty sure it's incorrect.
>
> --
> Greetings, Michael.
>

Do we have the correct reset routine implemented somewhere, or is it a
new routine to add?
Michael Buesch Sept. 1, 2009, 12:55 p.m. UTC | #8
On Monday 31 August 2009 21:38:28 Gábor Stefanik wrote:
> On Mon, Aug 31, 2009 at 9:17 PM, Michael Buesch<mb@bu3sch.de> wrote:
> > On Monday 31 August 2009 19:53:31 John W. Linville wrote:
> >> On Sun, Aug 30, 2009 at 05:55:40PM +0200, Michael Buesch wrote:
> >> > On Sunday 30 August 2009 17:10:23 Larry Finger wrote:
> >> > > Michael Buesch wrote:
> >> > > > On Sunday 30 August 2009 02:15:55 Gábor Stefanik wrote:
> >> > > >>  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> >> > > >>  {
> >> > > >>        struct b43_phy_lp *lpphy = dev->phy.lp;
> >> > > >> @@ -1357,28 +1488,440 @@ static void lpphy_pr41573_workaround(struct b43_wldev *dev)
> >> > > >>                b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
> >> > > >>                                    saved_tab_size, saved_tab);
> >> > > >>        }
> >> > > >> +      b43_put_phy_into_reset(dev);
> >> > > >
> >> > > > Are you sure you really want this?
> >> > > > This function completely disables the PHY on the backplane and keeps the physical
> >> > > > PHY reset pin asserted (even after return from the function).
> >> > > > So the PHY will physically be powered down from this point on. The following
> >> > > > PHY accesses could even hang the machine, because the PHY won't respond to
> >> > > > register accesses anymore.
> >> > > >
> >> > > > We currently only use this function on A/G Multi-PHY devices to permanently
> >> > > > hard-disable the PHY that's not used.
> >> > >
> >> > > The PHY reset routine in
> >> > > http://bcm-v4.sipsolutions.net/802.11/PHY/Reset, which I just updated
> >> > > for the latest N PHY changes, appears to be a different routine than
> >> > > b43_put_phy_into_reset(). The names are confusing.
> >> >
> >> > b43_put_phy_into_reset() is opencoded in the specifications in various init
> >> > routines. There's no separate specs page for that function.
> >> > But I think the code is straightforward and easy to understand.
> >>
> >> So is this patch right or not?  Should I hold onto it for 2.6.33
> >> (i.e. after the 2.6.32 merge window)?
> >
> > I'm pretty sure it's incorrect.
> >
> > --
> > Greetings, Michael.
> >
> 
> Do we have the correct reset routine implemented somewhere, or is it a
> new routine to add?
> 

We opencode something similar (N stuff and so on not included) in wireless_core_reset.
So we should probably implement http://bcm-v4.sipsolutions.net/802.11/PHY/Reset
in a separate function and also call that from wireless_core_reset (and your workaround
code).
diff mbox

Patch

diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index f2c5b2d..59bee02 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -3345,7 +3345,7 @@  static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
 	mutex_unlock(&wl->mutex);
 }
 
-static void b43_put_phy_into_reset(struct b43_wldev *dev)
+void b43_put_phy_into_reset(struct b43_wldev *dev)
 {
 	struct ssb_device *sdev = dev->dev;
 	u32 tmslow;
diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h
index 0406e06..fdbea9a 100644
--- a/drivers/net/wireless/b43/main.h
+++ b/drivers/net/wireless/b43/main.h
@@ -129,6 +129,8 @@  void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags);
 
 void b43_controller_restart(struct b43_wldev *dev, const char *reason);
 
+void b43_put_phy_into_reset(struct b43_wldev *dev);
+
 #define B43_PS_ENABLED	(1 << 0)	/* Force enable hardware power saving */
 #define B43_PS_DISABLED	(1 << 1)	/* Force disable hardware power saving */
 #define B43_PS_AWAKE	(1 << 2)	/* Force device awake */
diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c
index 5fff30a..c13385b 100644
--- a/drivers/net/wireless/b43/phy_lp.c
+++ b/drivers/net/wireless/b43/phy_lp.c
@@ -67,6 +67,7 @@  static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev)
 	struct b43_phy_lp *lpphy = phy->lp;
 
 	memset(lpphy, 0, sizeof(*lpphy));
+	lpphy->antenna = B43_ANTENNA_DEFAULT;
 
 	//TODO
 }
@@ -751,11 +752,17 @@  static void lpphy_clear_deaf(struct b43_wldev *dev, bool user)
 	}
 }
 
+static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx)
+{
+	u16 trsw = (tx << 1) | rx;
+	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw);
+	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
+}
+
 static void lpphy_disable_crs(struct b43_wldev *dev, bool user)
 {
 	lpphy_set_deaf(dev, user);
-	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x1);
-	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
+	lpphy_set_trsw_over(dev, false, true);
 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB);
 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4);
 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7);
@@ -790,6 +797,60 @@  static void lpphy_restore_crs(struct b43_wldev *dev, bool user)
 
 struct lpphy_tx_gains { u16 gm, pga, pad, dac; };
 
+static void lpphy_disable_rx_gain_override(struct b43_wldev *dev)
+{
+	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE);
+	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF);
+	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF);
+	if (dev->phy.rev >= 2) {
+		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
+		if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF);
+			b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7);
+		}
+	} else {
+		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF);
+	}
+}
+
+static void lpphy_enable_rx_gain_override(struct b43_wldev *dev)
+{
+	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1);
+	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
+	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
+	if (dev->phy.rev >= 2) {
+		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
+		if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400);
+			b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8);
+		}
+	} else {
+		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200);
+	}
+}
+
+static void lpphy_disable_tx_gain_override(struct b43_wldev *dev)
+{
+	if (dev->phy.rev < 2)
+		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
+	else {
+		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F);
+		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF);
+	}
+	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF);
+}
+
+static void lpphy_enable_tx_gain_override(struct b43_wldev *dev)
+{
+	if (dev->phy.rev < 2)
+		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
+	else {
+		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80);
+		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000);
+	}
+	b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40);
+}
+
 static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev)
 {
 	struct lpphy_tx_gains gains;
@@ -819,6 +880,17 @@  static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac)
 	b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl);
 }
 
+static u16 lpphy_get_pa_gain(struct b43_wldev *dev)
+{
+	return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F;
+}
+
+static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain)
+{
+	b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6);
+	b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8);
+}
+
 static void lpphy_set_tx_gains(struct b43_wldev *dev,
 			       struct lpphy_tx_gains gains)
 {
@@ -829,8 +901,7 @@  static void lpphy_set_tx_gains(struct b43_wldev *dev,
 		b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
 				0xF800, rf_gain);
 	} else {
-		pa_gain = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x1FC0;
-		pa_gain <<= 2;
+		pa_gain = lpphy_get_pa_gain(dev);
 		b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
 			      (gains.pga << 8) | gains.gm);
 		b43_phy_maskset(dev, B43_PHY_OFDM(0xFB),
@@ -841,13 +912,7 @@  static void lpphy_set_tx_gains(struct b43_wldev *dev,
 				0x8000, gains.pad | pa_gain);
 	}
 	lpphy_set_dac_gain(dev, gains.dac);
-	if (dev->phy.rev < 2) {
-		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF, 1 << 8);
-	} else {
-		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F, 1 << 7);
-		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF, 1 << 14);
-	}
-	b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF, 1 << 6);
+	lpphy_enable_tx_gain_override(dev);
 }
 
 static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain)
@@ -887,38 +952,6 @@  static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain)
 	}
 }
 
-static void lpphy_disable_rx_gain_override(struct b43_wldev *dev)
-{
-	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE);
-	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF);
-	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF);
-	if (dev->phy.rev >= 2) {
-		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
-		if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
-			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF);
-			b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7);
-		}
-	} else {
-		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF);
-	}
-}
-
-static void lpphy_enable_rx_gain_override(struct b43_wldev *dev)
-{
-	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1);
-	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
-	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
-	if (dev->phy.rev >= 2) {
-		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
-		if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
-			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400);
-			b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8);
-		}
-	} else {
-		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200);
-	}
-}
-
 static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain)
 {
 	if (dev->phy.rev < 2)
@@ -1003,8 +1036,7 @@  static int lpphy_loopback(struct b43_wldev *dev)
 
 	memset(&iq_est, 0, sizeof(iq_est));
 
-	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x3);
-	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
+	lpphy_set_trsw_over(dev, true, true);
 	b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1);
 	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
@@ -1126,7 +1158,7 @@  static void lpphy_set_tx_power_control(struct b43_wldev *dev,
 			b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM,
 					0x8FFF, ((u16)lpphy->tssi_npt << 16));
 			//TODO Set "TSSI Transmit Count" variable to total transmitted frame count
-			//TODO Disable TX gain override
+			lpphy_disable_tx_gain_override(dev);
 			lpphy->tx_pwr_idx_over = -1;
 		}
 	}
@@ -1312,15 +1344,73 @@  static void lpphy_calibrate_rc(struct b43_wldev *dev)
 	}
 }
 
+static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
+{
+	if (dev->phy.rev >= 2)
+		return; // rev2+ doesn't support antenna diversity
+
+	if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1))
+		return;
+
+	b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP);
+
+	b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2);
+	b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1);
+
+	b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP);
+
+	dev->phy.lp->antenna = antenna;
+}
+
+static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b)
+{
+	u16 tmp[2];
+
+	tmp[0] = a;
+	tmp[1] = b;
+	b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp);
+}
+
 static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index)
 {
 	struct b43_phy_lp *lpphy = dev->phy.lp;
+	struct lpphy_tx_gains gains;
+	u32 iq_comp, tx_gain, coeff, rf_power;
 
 	lpphy->tx_pwr_idx_over = index;
+	lpphy_read_tx_pctl_mode_from_hardware(dev);
 	if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF)
 		lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW);
-
-	//TODO
+	if (dev->phy.rev >= 2) {
+		iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320));
+		tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192));
+		gains.pad = (tx_gain >> 16) & 0xFF;
+		gains.gm = tx_gain & 0xFF;
+		gains.pga = (tx_gain >> 8) & 0xFF;
+		gains.dac = (iq_comp >> 28) & 0xFF;
+		lpphy_set_tx_gains(dev, gains);
+	} else {
+		iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320));
+		tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192));
+		b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
+				0xF800, (tx_gain >> 4) & 0x7FFF);
+		lpphy_set_dac_gain(dev, tx_gain & 0x7);
+		lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F);
+	}
+	lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF);
+	lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF);
+	if (dev->phy.rev >= 2) {
+		coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448));
+	} else {
+		coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448));
+	}
+	b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF);
+	if (dev->phy.rev >= 2) {
+		rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576));
+		b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00,
+				rf_power & 0xFFFF);//SPEC FIXME mask & set != 0
+	}
+	lpphy_enable_tx_gain_override(dev);
 }
 
 static void lpphy_btcoex_override(struct b43_wldev *dev)
@@ -1329,6 +1419,47 @@  static void lpphy_btcoex_override(struct b43_wldev *dev)
 	b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF);
 }
 
+static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
+					 bool blocked)
+{
+	//TODO check MAC control register
+	if (blocked) {
+		if (dev->phy.rev >= 2) {
+			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF);
+			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
+			b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF);
+			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF);
+			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808);
+		} else {
+			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF);
+			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
+			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF);
+			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018);
+		}
+	} else {
+		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF);
+		if (dev->phy.rev >= 2)
+			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7);
+		else
+			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7);
+	}
+}
+
+/* This was previously called lpphy_japan_filter */
+static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel)
+{
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter!
+
+	if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific?
+		b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9);
+		if ((dev->phy.rev == 1) && (lpphy->rc_cap))
+			lpphy_set_rc_cap(dev);
+	} else {
+		b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F);
+	}
+}
+
 static void lpphy_pr41573_workaround(struct b43_wldev *dev)
 {
 	struct b43_phy_lp *lpphy = dev->phy.lp;
@@ -1357,28 +1488,440 @@  static void lpphy_pr41573_workaround(struct b43_wldev *dev)
 		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
 				    saved_tab_size, saved_tab);
 	}
+	b43_put_phy_into_reset(dev);
+	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFF8);
+	lpphy_table_init(dev); //FIXME is table init needed?
+	lpphy_baseband_init(dev);
+	b43_lpphy_op_software_rfkill(dev, false);
+	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
+	if (dev->phy.rev < 2) {
+		b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140),
+				     saved_tab_size, saved_tab);
+	} else {
+		b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140),
+				     saved_tab_size, saved_tab);
+	}
+	b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel);
+	lpphy->tssi_npt = tssi_npt;
+	lpphy->tssi_idx = tssi_idx;
+	lpphy_set_analog_filter(dev, lpphy->channel);
+	if (tx_pwr_idx_over != -1)
+		lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over);
+	if (lpphy->rc_cap)
+		lpphy_set_rc_cap(dev);
+	b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna);
+	lpphy_set_tx_power_control(dev, txpctl_mode);
+	kfree(saved_tab);
+}
+
+struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; };
+
+static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = {
+	{ .chan = 1, .c1 = -66, .c0 = 15, },
+	{ .chan = 2, .c1 = -66, .c0 = 15, },
+	{ .chan = 3, .c1 = -66, .c0 = 15, },
+	{ .chan = 4, .c1 = -66, .c0 = 15, },
+	{ .chan = 5, .c1 = -66, .c0 = 15, },
+	{ .chan = 6, .c1 = -66, .c0 = 15, },
+	{ .chan = 7, .c1 = -66, .c0 = 14, },
+	{ .chan = 8, .c1 = -66, .c0 = 14, },
+	{ .chan = 9, .c1 = -66, .c0 = 14, },
+	{ .chan = 10, .c1 = -66, .c0 = 14, },
+	{ .chan = 11, .c1 = -66, .c0 = 14, },
+	{ .chan = 12, .c1 = -66, .c0 = 13, },
+	{ .chan = 13, .c1 = -66, .c0 = 13, },
+	{ .chan = 14, .c1 = -66, .c0 = 13, },
+};
+
+static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = {
+	{ .chan = 1, .c1 = -64, .c0 = 13, },
+	{ .chan = 2, .c1 = -64, .c0 = 13, },
+	{ .chan = 3, .c1 = -64, .c0 = 13, },
+	{ .chan = 4, .c1 = -64, .c0 = 13, },
+	{ .chan = 5, .c1 = -64, .c0 = 12, },
+	{ .chan = 6, .c1 = -64, .c0 = 12, },
+	{ .chan = 7, .c1 = -64, .c0 = 12, },
+	{ .chan = 8, .c1 = -64, .c0 = 12, },
+	{ .chan = 9, .c1 = -64, .c0 = 12, },
+	{ .chan = 10, .c1 = -64, .c0 = 11, },
+	{ .chan = 11, .c1 = -64, .c0 = 11, },
+	{ .chan = 12, .c1 = -64, .c0 = 11, },
+	{ .chan = 13, .c1 = -64, .c0 = 11, },
+	{ .chan = 14, .c1 = -64, .c0 = 10, },
+	{ .chan = 34, .c1 = -62, .c0 = 24, },
+	{ .chan = 38, .c1 = -62, .c0 = 24, },
+	{ .chan = 42, .c1 = -62, .c0 = 24, },
+	{ .chan = 46, .c1 = -62, .c0 = 23, },
+	{ .chan = 36, .c1 = -62, .c0 = 24, },
+	{ .chan = 40, .c1 = -62, .c0 = 24, },
+	{ .chan = 44, .c1 = -62, .c0 = 23, },
+	{ .chan = 48, .c1 = -62, .c0 = 23, },
+	{ .chan = 52, .c1 = -62, .c0 = 23, },
+	{ .chan = 56, .c1 = -62, .c0 = 22, },
+	{ .chan = 60, .c1 = -62, .c0 = 22, },
+	{ .chan = 64, .c1 = -62, .c0 = 22, },
+	{ .chan = 100, .c1 = -62, .c0 = 16, },
+	{ .chan = 104, .c1 = -62, .c0 = 16, },
+	{ .chan = 108, .c1 = -62, .c0 = 15, },
+	{ .chan = 112, .c1 = -62, .c0 = 14, },
+	{ .chan = 116, .c1 = -62, .c0 = 14, },
+	{ .chan = 120, .c1 = -62, .c0 = 13, },
+	{ .chan = 124, .c1 = -62, .c0 = 12, },
+	{ .chan = 128, .c1 = -62, .c0 = 12, },
+	{ .chan = 132, .c1 = -62, .c0 = 12, },
+	{ .chan = 136, .c1 = -62, .c0 = 11, },
+	{ .chan = 140, .c1 = -62, .c0 = 10, },
+	{ .chan = 149, .c1 = -61, .c0 = 9, },
+	{ .chan = 153, .c1 = -61, .c0 = 9, },
+	{ .chan = 157, .c1 = -61, .c0 = 9, },
+	{ .chan = 161, .c1 = -61, .c0 = 8, },
+	{ .chan = 165, .c1 = -61, .c0 = 8, },
+	{ .chan = 184, .c1 = -62, .c0 = 25, },
+	{ .chan = 188, .c1 = -62, .c0 = 25, },
+	{ .chan = 192, .c1 = -62, .c0 = 25, },
+	{ .chan = 196, .c1 = -62, .c0 = 25, },
+	{ .chan = 200, .c1 = -62, .c0 = 25, },
+	{ .chan = 204, .c1 = -62, .c0 = 25, },
+	{ .chan = 208, .c1 = -62, .c0 = 25, },
+	{ .chan = 212, .c1 = -62, .c0 = 25, },
+	{ .chan = 216, .c1 = -62, .c0 = 26, },
+};
+
+static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = {
+	.chan = 0,
+	.c1 = -64,
+	.c0 = 0,
+};
+
+static u8 lpphy_nbits(s32 val)
+{
+	u32 tmp = abs(val);
+	u8 nbits = 0;
+
+	while (tmp != 0) {
+		nbits++;
+		tmp >>= 1;
+	}
+
+	return nbits;
+}
+
+static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples)
+{
+	struct lpphy_iq_est iq_est;
+	u16 c0, c1;
+	int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret;
+
+	c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S);
+	c0 = c1 >> 8;
+	c1 |= 0xFF;
+
+	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0);
+	b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF);
+
+	ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est);
+	if (!ret)
+		goto out;
+
+	prod = iq_est.iq_prod;
+	ipwr = iq_est.i_pwr;
+	qpwr = iq_est.q_pwr;
+
+	if (ipwr + qpwr < 2) {
+		ret = 0;
+		goto out;
+	}
+
+	prod_msb = lpphy_nbits(prod);
+	q_msb = lpphy_nbits(qpwr);
+	tmp1 = prod_msb - 20;
+
+	if (tmp1 >= 0) {
+		tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) /
+			(ipwr >> tmp1);
+	} else {
+		tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) /
+			(ipwr << -tmp1);
+	}
+
+	tmp2 = q_msb - 11;
+
+	if (tmp2 >= 0)
+		tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2);
+	else
+		tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2);
+
+	tmp4 -= tmp3 * tmp3;
+	tmp4 = -int_sqrt(tmp4);
+
+	c0 = tmp3 >> 3;
+	c1 = tmp4 >> 4;
+
+out:
+	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1);
+	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8);
+	return ret;
+}
+
+/* Complex number using 2 32-bit signed integers */
+typedef struct {s32 i, q;} lpphy_c32;
+
+static lpphy_c32 lpphy_cordic(int theta)
+{
+	u32 arctg[] = { 2949120, 1740967, 919879, 466945, 234379, 117304,
+		      58666, 29335, 14668, 7334, 3667, 1833, 917, 458,
+		      229, 115, 57, 29, };
+	int i, tmp, signx = 1, angle = 0;
+	lpphy_c32 ret = { .i = 39797, .q = 0, };
+
+	theta = clamp_t(int, theta, -180, 180);
+
+	if (theta > 90) {
+		theta -= 180;
+		signx = -1;
+	} else if (theta < -90) {
+		theta += 180;
+		signx = -1;
+	}
+
+	for (i = 0; i <= 17; i++) {
+		if (theta > angle) {
+			tmp = ret.i - (ret.q >> i);
+			ret.q += ret.i >> i;
+			ret.i = tmp;
+			angle += arctg[i];
+		} else {
+			tmp = ret.i + (ret.q >> i);
+			ret.q -= ret.i >> i;
+			ret.i = tmp;
+			angle -= arctg[i];
+		}
+	}
+
+	ret.i *= signx;
+	ret.q *= signx;
+
+	return ret;
+}
+
+static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops,
+			      u16 wait)
+{
+	b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL,
+			0xFFC0, samples - 1);
+	if (loops != 0xFFFF)
+		loops--;
+	b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops);
+	b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6);
+	b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1);
+}
+
+//SPEC FIXME what does a negative freq mean?
+static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
+{
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	u16 buf[64];
+	int i, samples, angle = 0, rotation = (9 * freq) / 500;
+	lpphy_c32 sample;
+
+	lpphy->tx_tone_freq = freq;
+
+	if (freq) {
+		/* Find i for which abs(freq) integrally divides 20000 * i */
+		for (i = 1; samples * abs(freq) != 20000 * i; i++) {
+			samples = (20000 * i) / abs(freq);
+			if(B43_WARN_ON(samples > 63))
+				return;
+		}
+	} else {
+		samples = 2;
+	}
+
+	for (i = 0; i < samples; i++) {
+		sample = lpphy_cordic(angle);
+		angle += rotation;
+		buf[i] = ((sample.i * max) & 0xFF) << 8;
+		buf[i] |= (sample.q * max) & 0xFF;
+	}
+
+	b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf);
+
+	lpphy_run_samples(dev, samples, 0xFFFF, 0);
+}
+
+static void lpphy_stop_tx_tone(struct b43_wldev *dev)
+{
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	int i;
+
+	lpphy->tx_tone_freq = 0;
+
+	b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000);
+	for (i = 0; i < 31; i++) {
+		if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1))
+			break;
+		udelay(100);
+	}
+}
+
+
+static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains,
+			   int mode, bool useindex, u8 index)
+{
 	//TODO
+}
 
-	kfree(saved_tab);
+static void lpphy_papd_cal_txpwr(struct b43_wldev *dev)
+{
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	struct ssb_bus *bus = dev->dev->bus;
+	struct lpphy_tx_gains gains, oldgains;
+	int old_txpctl, old_afe_ovr, old_rf, old_bbmult;
+
+	lpphy_read_tx_pctl_mode_from_hardware(dev);
+	old_txpctl = lpphy->txpctl_mode;
+	old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;
+	if (old_afe_ovr)
+		oldgains = lpphy_get_tx_gains(dev);
+	old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF;
+	old_bbmult = lpphy_get_bb_mult(dev);
+
+	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
+
+	if (bus->chip_id == 0x4325 && bus->chip_rev == 0)
+		lpphy_papd_cal(dev, gains, 0, 1, 30);
+	else
+		lpphy_papd_cal(dev, gains, 0, 1, 65);
+
+	if (old_afe_ovr)
+		lpphy_set_tx_gains(dev, oldgains);
+	lpphy_set_bb_mult(dev, old_bbmult);
+	lpphy_set_tx_power_control(dev, old_txpctl);
+	b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf);
+}
+
+static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx,
+			    bool rx, bool pa, struct lpphy_tx_gains *gains)
+{
+	struct b43_phy_lp *lpphy = dev->phy.lp;
+	struct ssb_bus *bus = dev->dev->bus;
+	const struct lpphy_rx_iq_comp *iqcomp = NULL;
+	struct lpphy_tx_gains nogains, oldgains;
+	u16 tmp;
+	int i, ret;
+
+	memset(&nogains, 0, sizeof(nogains));
+	memset(&oldgains, 0, sizeof(oldgains));
+
+	if (bus->chip_id == 0x5354) {
+		for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) {
+			if (lpphy_5354_iq_table[i].chan == lpphy->channel) {
+				iqcomp = &lpphy_5354_iq_table[i];
+			}
+		}
+	} else if (dev->phy.rev >= 2) {
+		iqcomp = &lpphy_rev2plus_iq_comp;
+	} else {
+		for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) {
+			if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) {
+				iqcomp = &lpphy_rev0_1_iq_table[i];
+			}
+		}
+	}
+
+	if (B43_WARN_ON(!iqcomp))
+		return 0;
+
+	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1);
+	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S,
+			0x00FF, iqcomp->c0 << 8);
+
+	if (noise) {
+		tx = true;
+		rx = false;
+		pa = false;
+	}
+
+	lpphy_set_trsw_over(dev, tx, rx);
+
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
+		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
+				0xFFF7, pa << 3);
+	} else {
+		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20);
+		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
+				0xFFDF, pa << 5);
+	}
+
+	tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;
+
+	if (noise)
+		lpphy_set_rx_gain(dev, 0x2D5D);
+	else {
+		if (tmp)
+			oldgains = lpphy_get_tx_gains(dev);
+		if (!gains)
+			gains = &nogains;
+		lpphy_set_tx_gains(dev, *gains);
+	}
+
+	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
+	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
+	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
+	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800);
+	lpphy_set_deaf(dev, false);
+	if (noise)
+		ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0);
+	else {
+		lpphy_start_tx_tone(dev, 4000, 100);
+		ret = lpphy_calc_rx_iq_comp(dev, 0x4000);
+		lpphy_stop_tx_tone(dev);
+	}
+	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC);
+	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7);
+	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF);
+	if (!noise) {
+		if (tmp)
+			lpphy_set_tx_gains(dev, oldgains);
+		else
+			lpphy_disable_tx_gain_override(dev);
+	}
+	lpphy_disable_rx_gain_override(dev);
+	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
+	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF);
+	return ret;
 }
 
 static void lpphy_calibration(struct b43_wldev *dev)
 {
 	struct b43_phy_lp *lpphy = dev->phy.lp;
 	enum b43_lpphy_txpctl_mode saved_pctl_mode;
+	bool full_cal = false;
+
+	if (lpphy->full_calib_chan != lpphy->channel) {
+		full_cal = true;
+		lpphy->full_calib_chan = lpphy->channel;
+	}
 
 	b43_mac_suspend(dev);
 
 	lpphy_btcoex_override(dev);
+	if (dev->phy.rev >= 2)
+		lpphy_save_dig_flt_state(dev);
 	lpphy_read_tx_pctl_mode_from_hardware(dev);
 	saved_pctl_mode = lpphy->txpctl_mode;
 	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
 	//TODO Perform transmit power table I/Q LO calibration
 	if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF))
 		lpphy_pr41573_workaround(dev);
-	//TODO If a full calibration has not been performed on this channel yet, perform PAPD TX-power calibration
+	if ((dev->phy.rev >= 2) && full_cal) {
+		lpphy_papd_cal_txpwr(dev);
+	}
 	lpphy_set_tx_power_control(dev, saved_pctl_mode);
-	//TODO Perform I/Q calibration with a single control value set
+	if (dev->phy.rev >= 2)
+		lpphy_restore_dig_flt_state(dev);
+	lpphy_rx_iq_cal(dev, true, true, false, false, NULL);
 
 	b43_mac_enable(dev);
 }
@@ -1532,12 +2075,6 @@  static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
 	b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
 }
 
-static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
-					 bool blocked)
-{
-	//TODO
-}
-
 struct b206x_channel {
 	u8 channel;
 	u16 freq;
@@ -2003,22 +2540,6 @@  static int lpphy_b2062_tune(struct b43_wldev *dev,
 	return err;
 }
 
-
-/* This was previously called lpphy_japan_filter */
-static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel)
-{
-	struct b43_phy_lp *lpphy = dev->phy.lp;
-	u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter!
-
-	if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific?
-		b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9);
-		if ((dev->phy.rev == 1) && (lpphy->rc_cap))
-			lpphy_set_rc_cap(dev);
-	} else {
-		b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F);
-	}
-}
-
 static void lpphy_b2063_vco_calib(struct b43_wldev *dev)
 {
 	u16 tmp;
@@ -2203,18 +2724,6 @@  static int b43_lpphy_op_init(struct b43_wldev *dev)
 	return 0;
 }
 
-static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
-{
-	if (dev->phy.rev >= 2)
-		return; // rev2+ doesn't support antenna diversity
-
-	if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1))
-		return;
-
-	b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2);
-	b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1);
-}
-
 static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev)
 {
 	//TODO
@@ -2227,6 +2736,11 @@  static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev,
 	return B43_TXPWR_RES_DONE;
 }
 
+static void b43_lpphy_op_pwork_15sec(struct b43_wldev *dev)
+{
+	//TODO
+}
+
 const struct b43_phy_operations b43_phyops_lp = {
 	.allocate		= b43_lpphy_op_allocate,
 	.free			= b43_lpphy_op_free,
@@ -2244,4 +2758,6 @@  const struct b43_phy_operations b43_phyops_lp = {
 	.set_rx_antenna		= b43_lpphy_op_set_rx_antenna,
 	.recalc_txpower		= b43_lpphy_op_recalc_txpower,
 	.adjust_txpower		= b43_lpphy_op_adjust_txpower,
+	.pwork_15sec		= b43_lpphy_op_pwork_15sec,
+	.pwork_60sec		= lpphy_calibration,
 };
diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/b43/phy_lp.h
index c3232c1..62737f7 100644
--- a/drivers/net/wireless/b43/phy_lp.h
+++ b/drivers/net/wireless/b43/phy_lp.h
@@ -286,6 +286,7 @@ 
 #define B43_LPPHY_TR_LOOKUP_6			B43_PHY_OFDM(0xC8) /* TR Lookup 6 */
 #define B43_LPPHY_TR_LOOKUP_7			B43_PHY_OFDM(0xC9) /* TR Lookup 7 */
 #define B43_LPPHY_TR_LOOKUP_8			B43_PHY_OFDM(0xCA) /* TR Lookup 8 */
+#define B43_LPPHY_RF_PWR_OVERRIDE		B43_PHY_OFDM(0xD3) /* RF power override */
 
 
 
@@ -871,12 +872,12 @@  struct b43_phy_lp {
 	u8 rssi_gs;
 
 	/* RC cap */
-	u8 rc_cap; /* FIXME initial value? */
+	u8 rc_cap;
 	/* BX arch */
 	u8 bx_arch;
 
 	/* Full calibration channel */
-	u8 full_calib_chan; /* FIXME initial value? */
+	u8 full_calib_chan;
 
 	/* Transmit iqlocal best coeffs */
 	bool tx_iqloc_best_coeffs_valid;
@@ -891,6 +892,12 @@  struct b43_phy_lp {
 
 	/* The channel we are tuned to */
 	u8 channel;
+
+	/* The active antenna diversity mode */
+	int antenna;
+
+	/* Frequency of the active TX tone */
+	int tx_tone_freq;
 };
 
 enum tssi_mux_mode {