158619b14SKalle Valo /*
258619b14SKalle Valo 
358619b14SKalle Valo   Broadcom B43 wireless driver
458619b14SKalle Valo   IEEE 802.11a/g LP-PHY driver
558619b14SKalle Valo 
658619b14SKalle Valo   Copyright (c) 2008-2009 Michael Buesch <m@bues.ch>
758619b14SKalle Valo   Copyright (c) 2009 Gábor Stefanik <netrolller.3d@gmail.com>
858619b14SKalle Valo 
958619b14SKalle Valo   This program is free software; you can redistribute it and/or modify
1058619b14SKalle Valo   it under the terms of the GNU General Public License as published by
1158619b14SKalle Valo   the Free Software Foundation; either version 2 of the License, or
1258619b14SKalle Valo   (at your option) any later version.
1358619b14SKalle Valo 
1458619b14SKalle Valo   This program is distributed in the hope that it will be useful,
1558619b14SKalle Valo   but WITHOUT ANY WARRANTY; without even the implied warranty of
1658619b14SKalle Valo   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1758619b14SKalle Valo   GNU General Public License for more details.
1858619b14SKalle Valo 
1958619b14SKalle Valo   You should have received a copy of the GNU General Public License
2058619b14SKalle Valo   along with this program; see the file COPYING.  If not, write to
2158619b14SKalle Valo   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
2258619b14SKalle Valo   Boston, MA 02110-1301, USA.
2358619b14SKalle Valo 
2458619b14SKalle Valo */
2558619b14SKalle Valo 
26d5a43355SPriit Laes #include <linux/cordic.h>
2758619b14SKalle Valo #include <linux/slab.h>
2858619b14SKalle Valo 
2958619b14SKalle Valo #include "b43.h"
3058619b14SKalle Valo #include "main.h"
3158619b14SKalle Valo #include "phy_lp.h"
3258619b14SKalle Valo #include "phy_common.h"
3358619b14SKalle Valo #include "tables_lpphy.h"
3458619b14SKalle Valo 
3558619b14SKalle Valo 
3658619b14SKalle Valo static inline u16 channel2freq_lp(u8 channel)
3758619b14SKalle Valo {
3858619b14SKalle Valo 	if (channel < 14)
3958619b14SKalle Valo 		return (2407 + 5 * channel);
4058619b14SKalle Valo 	else if (channel == 14)
4158619b14SKalle Valo 		return 2484;
4258619b14SKalle Valo 	else if (channel < 184)
4358619b14SKalle Valo 		return (5000 + 5 * channel);
4458619b14SKalle Valo 	else
4558619b14SKalle Valo 		return (4000 + 5 * channel);
4658619b14SKalle Valo }
4758619b14SKalle Valo 
4858619b14SKalle Valo static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev)
4958619b14SKalle Valo {
5057fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
5158619b14SKalle Valo 		return 1;
5258619b14SKalle Valo 	return 36;
5358619b14SKalle Valo }
5458619b14SKalle Valo 
5558619b14SKalle Valo static int b43_lpphy_op_allocate(struct b43_wldev *dev)
5658619b14SKalle Valo {
5758619b14SKalle Valo 	struct b43_phy_lp *lpphy;
5858619b14SKalle Valo 
5958619b14SKalle Valo 	lpphy = kzalloc(sizeof(*lpphy), GFP_KERNEL);
6058619b14SKalle Valo 	if (!lpphy)
6158619b14SKalle Valo 		return -ENOMEM;
6258619b14SKalle Valo 	dev->phy.lp = lpphy;
6358619b14SKalle Valo 
6458619b14SKalle Valo 	return 0;
6558619b14SKalle Valo }
6658619b14SKalle Valo 
6758619b14SKalle Valo static void b43_lpphy_op_prepare_structs(struct b43_wldev *dev)
6858619b14SKalle Valo {
6958619b14SKalle Valo 	struct b43_phy *phy = &dev->phy;
7058619b14SKalle Valo 	struct b43_phy_lp *lpphy = phy->lp;
7158619b14SKalle Valo 
7258619b14SKalle Valo 	memset(lpphy, 0, sizeof(*lpphy));
7358619b14SKalle Valo 	lpphy->antenna = B43_ANTENNA_DEFAULT;
7458619b14SKalle Valo 
7558619b14SKalle Valo 	//TODO
7658619b14SKalle Valo }
7758619b14SKalle Valo 
7858619b14SKalle Valo static void b43_lpphy_op_free(struct b43_wldev *dev)
7958619b14SKalle Valo {
8058619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
8158619b14SKalle Valo 
8258619b14SKalle Valo 	kfree(lpphy);
8358619b14SKalle Valo 	dev->phy.lp = NULL;
8458619b14SKalle Valo }
8558619b14SKalle Valo 
8658619b14SKalle Valo /* http://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */
8758619b14SKalle Valo static void lpphy_read_band_sprom(struct b43_wldev *dev)
8858619b14SKalle Valo {
8958619b14SKalle Valo 	struct ssb_sprom *sprom = dev->dev->bus_sprom;
9058619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
9158619b14SKalle Valo 	u16 cckpo, maxpwr;
9258619b14SKalle Valo 	u32 ofdmpo;
9358619b14SKalle Valo 	int i;
9458619b14SKalle Valo 
9557fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
9658619b14SKalle Valo 		lpphy->tx_isolation_med_band = sprom->tri2g;
9758619b14SKalle Valo 		lpphy->bx_arch = sprom->bxa2g;
9858619b14SKalle Valo 		lpphy->rx_pwr_offset = sprom->rxpo2g;
9958619b14SKalle Valo 		lpphy->rssi_vf = sprom->rssismf2g;
10058619b14SKalle Valo 		lpphy->rssi_vc = sprom->rssismc2g;
10158619b14SKalle Valo 		lpphy->rssi_gs = sprom->rssisav2g;
10258619b14SKalle Valo 		lpphy->txpa[0] = sprom->pa0b0;
10358619b14SKalle Valo 		lpphy->txpa[1] = sprom->pa0b1;
10458619b14SKalle Valo 		lpphy->txpa[2] = sprom->pa0b2;
10558619b14SKalle Valo 		maxpwr = sprom->maxpwr_bg;
10658619b14SKalle Valo 		lpphy->max_tx_pwr_med_band = maxpwr;
10758619b14SKalle Valo 		cckpo = sprom->cck2gpo;
10858619b14SKalle Valo 		if (cckpo) {
10958619b14SKalle Valo 			ofdmpo = sprom->ofdm2gpo;
11058619b14SKalle Valo 			for (i = 0; i < 4; i++) {
11158619b14SKalle Valo 				lpphy->tx_max_rate[i] =
11258619b14SKalle Valo 					maxpwr - (ofdmpo & 0xF) * 2;
11358619b14SKalle Valo 				ofdmpo >>= 4;
11458619b14SKalle Valo 			}
11558619b14SKalle Valo 			ofdmpo = sprom->ofdm2gpo;
11658619b14SKalle Valo 			for (i = 4; i < 15; i++) {
11758619b14SKalle Valo 				lpphy->tx_max_rate[i] =
11858619b14SKalle Valo 					maxpwr - (ofdmpo & 0xF) * 2;
11958619b14SKalle Valo 				ofdmpo >>= 4;
12058619b14SKalle Valo 			}
12158619b14SKalle Valo 		} else {
12258619b14SKalle Valo 			u8 opo = sprom->opo;
12358619b14SKalle Valo 			for (i = 0; i < 4; i++)
12458619b14SKalle Valo 				lpphy->tx_max_rate[i] = maxpwr;
12558619b14SKalle Valo 			for (i = 4; i < 15; i++)
12658619b14SKalle Valo 				lpphy->tx_max_rate[i] = maxpwr - opo;
12758619b14SKalle Valo 		}
12858619b14SKalle Valo 	} else { /* 5GHz */
12958619b14SKalle Valo 		lpphy->tx_isolation_low_band = sprom->tri5gl;
13058619b14SKalle Valo 		lpphy->tx_isolation_med_band = sprom->tri5g;
13158619b14SKalle Valo 		lpphy->tx_isolation_hi_band = sprom->tri5gh;
13258619b14SKalle Valo 		lpphy->bx_arch = sprom->bxa5g;
13358619b14SKalle Valo 		lpphy->rx_pwr_offset = sprom->rxpo5g;
13458619b14SKalle Valo 		lpphy->rssi_vf = sprom->rssismf5g;
13558619b14SKalle Valo 		lpphy->rssi_vc = sprom->rssismc5g;
13658619b14SKalle Valo 		lpphy->rssi_gs = sprom->rssisav5g;
13758619b14SKalle Valo 		lpphy->txpa[0] = sprom->pa1b0;
13858619b14SKalle Valo 		lpphy->txpa[1] = sprom->pa1b1;
13958619b14SKalle Valo 		lpphy->txpa[2] = sprom->pa1b2;
14058619b14SKalle Valo 		lpphy->txpal[0] = sprom->pa1lob0;
14158619b14SKalle Valo 		lpphy->txpal[1] = sprom->pa1lob1;
14258619b14SKalle Valo 		lpphy->txpal[2] = sprom->pa1lob2;
14358619b14SKalle Valo 		lpphy->txpah[0] = sprom->pa1hib0;
14458619b14SKalle Valo 		lpphy->txpah[1] = sprom->pa1hib1;
14558619b14SKalle Valo 		lpphy->txpah[2] = sprom->pa1hib2;
14658619b14SKalle Valo 		maxpwr = sprom->maxpwr_al;
14758619b14SKalle Valo 		ofdmpo = sprom->ofdm5glpo;
14858619b14SKalle Valo 		lpphy->max_tx_pwr_low_band = maxpwr;
14958619b14SKalle Valo 		for (i = 4; i < 12; i++) {
15058619b14SKalle Valo 			lpphy->tx_max_ratel[i] = maxpwr - (ofdmpo & 0xF) * 2;
15158619b14SKalle Valo 			ofdmpo >>= 4;
15258619b14SKalle Valo 		}
15358619b14SKalle Valo 		maxpwr = sprom->maxpwr_a;
15458619b14SKalle Valo 		ofdmpo = sprom->ofdm5gpo;
15558619b14SKalle Valo 		lpphy->max_tx_pwr_med_band = maxpwr;
15658619b14SKalle Valo 		for (i = 4; i < 12; i++) {
15758619b14SKalle Valo 			lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2;
15858619b14SKalle Valo 			ofdmpo >>= 4;
15958619b14SKalle Valo 		}
16058619b14SKalle Valo 		maxpwr = sprom->maxpwr_ah;
16158619b14SKalle Valo 		ofdmpo = sprom->ofdm5ghpo;
16258619b14SKalle Valo 		lpphy->max_tx_pwr_hi_band = maxpwr;
16358619b14SKalle Valo 		for (i = 4; i < 12; i++) {
16458619b14SKalle Valo 			lpphy->tx_max_rateh[i] = maxpwr - (ofdmpo & 0xF) * 2;
16558619b14SKalle Valo 			ofdmpo >>= 4;
16658619b14SKalle Valo 		}
16758619b14SKalle Valo 	}
16858619b14SKalle Valo }
16958619b14SKalle Valo 
17058619b14SKalle Valo static void lpphy_adjust_gain_table(struct b43_wldev *dev, u32 freq)
17158619b14SKalle Valo {
17258619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
17358619b14SKalle Valo 	u16 temp[3];
17458619b14SKalle Valo 	u16 isolation;
17558619b14SKalle Valo 
17658619b14SKalle Valo 	B43_WARN_ON(dev->phy.rev >= 2);
17758619b14SKalle Valo 
17857fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
17958619b14SKalle Valo 		isolation = lpphy->tx_isolation_med_band;
18058619b14SKalle Valo 	else if (freq <= 5320)
18158619b14SKalle Valo 		isolation = lpphy->tx_isolation_low_band;
18258619b14SKalle Valo 	else if (freq <= 5700)
18358619b14SKalle Valo 		isolation = lpphy->tx_isolation_med_band;
18458619b14SKalle Valo 	else
18558619b14SKalle Valo 		isolation = lpphy->tx_isolation_hi_band;
18658619b14SKalle Valo 
18758619b14SKalle Valo 	temp[0] = ((isolation - 26) / 12) << 12;
18858619b14SKalle Valo 	temp[1] = temp[0] + 0x1000;
18958619b14SKalle Valo 	temp[2] = temp[0] + 0x2000;
19058619b14SKalle Valo 
19158619b14SKalle Valo 	b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), 3, temp);
19258619b14SKalle Valo 	b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), 3, temp);
19358619b14SKalle Valo }
19458619b14SKalle Valo 
19558619b14SKalle Valo static void lpphy_table_init(struct b43_wldev *dev)
19658619b14SKalle Valo {
19758619b14SKalle Valo 	u32 freq = channel2freq_lp(b43_lpphy_op_get_default_chan(dev));
19858619b14SKalle Valo 
19958619b14SKalle Valo 	if (dev->phy.rev < 2)
20058619b14SKalle Valo 		lpphy_rev0_1_table_init(dev);
20158619b14SKalle Valo 	else
20258619b14SKalle Valo 		lpphy_rev2plus_table_init(dev);
20358619b14SKalle Valo 
20458619b14SKalle Valo 	lpphy_init_tx_gain_table(dev);
20558619b14SKalle Valo 
20658619b14SKalle Valo 	if (dev->phy.rev < 2)
20758619b14SKalle Valo 		lpphy_adjust_gain_table(dev, freq);
20858619b14SKalle Valo }
20958619b14SKalle Valo 
21058619b14SKalle Valo static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev)
21158619b14SKalle Valo {
21258619b14SKalle Valo 	struct ssb_bus *bus = dev->dev->sdev->bus;
21358619b14SKalle Valo 	struct ssb_sprom *sprom = dev->dev->bus_sprom;
21458619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
21558619b14SKalle Valo 	u16 tmp, tmp2;
21658619b14SKalle Valo 
21758619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_DAC_CTL, 0xF7FF);
21858619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0);
21958619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0);
22058619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0);
22158619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0);
22258619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_AFE_DAC_CTL, 0x0004);
22358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x0078);
22458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800);
22558619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x0016);
22658619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_0, 0xFFF8, 0x0004);
22758619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5400);
22858619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2400);
22958619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100);
23058619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0x0006);
23158619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RX_RADIO_CTL, 0xFFFE);
23258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x0005);
23358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0x0180);
23458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x3C00);
23558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFFF0, 0x0005);
23658619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_GAIN_MISMATCH_LIMIT, 0xFFC0, 0x001A);
23758619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0x00B3);
23858619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00);
23958619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB,
24058619b14SKalle Valo 			0xFF00, lpphy->rx_pwr_offset);
24158619b14SKalle Valo 	if ((sprom->boardflags_lo & B43_BFL_FEM) &&
24257fbcce3SJohannes Berg 	   ((b43_current_band(dev->wl) == NL80211_BAND_5GHZ) ||
24358619b14SKalle Valo 	   (sprom->boardflags_hi & B43_BFH_PAREF))) {
24458619b14SKalle Valo 		ssb_pmu_set_ldo_voltage(&bus->chipco, LDO_PAREF, 0x28);
24558619b14SKalle Valo 		ssb_pmu_set_ldo_paref(&bus->chipco, true);
24658619b14SKalle Valo 		if (dev->phy.rev == 0) {
24758619b14SKalle Valo 			b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT,
24858619b14SKalle Valo 					0xFFCF, 0x0010);
24958619b14SKalle Valo 		}
25058619b14SKalle Valo 		b43_lptab_write(dev, B43_LPTAB16(11, 7), 60);
25158619b14SKalle Valo 	} else {
25258619b14SKalle Valo 		ssb_pmu_set_ldo_paref(&bus->chipco, false);
25358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT,
25458619b14SKalle Valo 				0xFFCF, 0x0020);
25558619b14SKalle Valo 		b43_lptab_write(dev, B43_LPTAB16(11, 7), 100);
25658619b14SKalle Valo 	}
25758619b14SKalle Valo 	tmp = lpphy->rssi_vf | lpphy->rssi_vc << 4 | 0xA000;
25858619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, tmp);
25958619b14SKalle Valo 	if (sprom->boardflags_hi & B43_BFH_RSSIINV)
26058619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x0AAA);
26158619b14SKalle Valo 	else
26258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x02AA);
26358619b14SKalle Valo 	b43_lptab_write(dev, B43_LPTAB16(11, 1), 24);
26458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RX_RADIO_CTL,
26558619b14SKalle Valo 			0xFFF9, (lpphy->bx_arch << 1));
26658619b14SKalle Valo 	if (dev->phy.rev == 1 &&
26758619b14SKalle Valo 	   (sprom->boardflags_hi & B43_BFH_FEM_BT)) {
26858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A);
26958619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0x3F00, 0x0900);
27058619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A);
27158619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00);
27258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x000A);
27358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0400);
27458619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x000A);
27558619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0B00);
27658619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xFFC0, 0x000A);
27758619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xC0FF, 0x0900);
27858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xFFC0, 0x000A);
27958619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xC0FF, 0x0B00);
28058619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xFFC0, 0x000A);
28158619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xC0FF, 0x0900);
28258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xFFC0, 0x000A);
28358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xC0FF, 0x0B00);
28457fbcce3SJohannes Berg 	} else if (b43_current_band(dev->wl) == NL80211_BAND_5GHZ ||
28558619b14SKalle Valo 		   (dev->dev->board_type == SSB_BOARD_BU4312) ||
28658619b14SKalle Valo 		   (dev->phy.rev == 0 && (sprom->boardflags_lo & B43_BFL_FEM))) {
28758619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0001);
28858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0400);
28958619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0001);
29058619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0500);
29158619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002);
29258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0800);
29358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002);
29458619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0A00);
29558619b14SKalle Valo 	} else if (dev->phy.rev == 1 ||
29658619b14SKalle Valo 		  (sprom->boardflags_lo & B43_BFL_FEM)) {
29758619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0004);
29858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0800);
29958619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0004);
30058619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0C00);
30158619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002);
30258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0100);
30358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002);
30458619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0300);
30558619b14SKalle Valo 	} else {
30658619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A);
30758619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0900);
30858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A);
30958619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00);
31058619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0006);
31158619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0500);
31258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0006);
31358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0700);
31458619b14SKalle Valo 	}
31558619b14SKalle Valo 	if (dev->phy.rev == 1 && (sprom->boardflags_hi & B43_BFH_PAREF)) {
31658619b14SKalle Valo 		b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_5, B43_LPPHY_TR_LOOKUP_1);
31758619b14SKalle Valo 		b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_6, B43_LPPHY_TR_LOOKUP_2);
31858619b14SKalle Valo 		b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_7, B43_LPPHY_TR_LOOKUP_3);
31958619b14SKalle Valo 		b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_8, B43_LPPHY_TR_LOOKUP_4);
32058619b14SKalle Valo 	}
32158619b14SKalle Valo 	if ((sprom->boardflags_hi & B43_BFH_FEM_BT) &&
32258619b14SKalle Valo 	    (dev->dev->chip_id == 0x5354) &&
32358619b14SKalle Valo 	    (dev->dev->chip_pkg == SSB_CHIPPACK_BCM4712S)) {
32458619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0006);
32558619b14SKalle Valo 		b43_phy_write(dev, B43_LPPHY_GPIO_SELECT, 0x0005);
32658619b14SKalle Valo 		b43_phy_write(dev, B43_LPPHY_GPIO_OUTEN, 0xFFFF);
32758619b14SKalle Valo 		//FIXME the Broadcom driver caches & delays this HF write!
32858619b14SKalle Valo 		b43_hf_write(dev, b43_hf_read(dev) | B43_HF_PR45960W);
32958619b14SKalle Valo 	}
33057fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
33158619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x8000);
33258619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0040);
33358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0xA400);
33458619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0x0B00);
33558619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x0007);
33658619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFF8, 0x0003);
33758619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFC7, 0x0020);
33858619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF);
33958619b14SKalle Valo 	} else { /* 5GHz */
34058619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0x7FFF);
34158619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFBF);
34258619b14SKalle Valo 	}
34358619b14SKalle Valo 	if (dev->phy.rev == 1) {
34458619b14SKalle Valo 		tmp = b43_phy_read(dev, B43_LPPHY_CLIPCTRTHRESH);
34558619b14SKalle Valo 		tmp2 = (tmp & 0x03E0) >> 5;
34658619b14SKalle Valo 		tmp2 |= tmp2 << 5;
34758619b14SKalle Valo 		b43_phy_write(dev, B43_LPPHY_4C3, tmp2);
34858619b14SKalle Valo 		tmp = b43_phy_read(dev, B43_LPPHY_GAINDIRECTMISMATCH);
34958619b14SKalle Valo 		tmp2 = (tmp & 0x1F00) >> 8;
35058619b14SKalle Valo 		tmp2 |= tmp2 << 5;
35158619b14SKalle Valo 		b43_phy_write(dev, B43_LPPHY_4C4, tmp2);
35258619b14SKalle Valo 		tmp = b43_phy_read(dev, B43_LPPHY_VERYLOWGAINDB);
35358619b14SKalle Valo 		tmp2 = tmp & 0x00FF;
35458619b14SKalle Valo 		tmp2 |= tmp << 8;
35558619b14SKalle Valo 		b43_phy_write(dev, B43_LPPHY_4C5, tmp2);
35658619b14SKalle Valo 	}
35758619b14SKalle Valo }
35858619b14SKalle Valo 
35958619b14SKalle Valo static void lpphy_save_dig_flt_state(struct b43_wldev *dev)
36058619b14SKalle Valo {
36158619b14SKalle Valo 	static const u16 addr[] = {
36258619b14SKalle Valo 		B43_PHY_OFDM(0xC1),
36358619b14SKalle Valo 		B43_PHY_OFDM(0xC2),
36458619b14SKalle Valo 		B43_PHY_OFDM(0xC3),
36558619b14SKalle Valo 		B43_PHY_OFDM(0xC4),
36658619b14SKalle Valo 		B43_PHY_OFDM(0xC5),
36758619b14SKalle Valo 		B43_PHY_OFDM(0xC6),
36858619b14SKalle Valo 		B43_PHY_OFDM(0xC7),
36958619b14SKalle Valo 		B43_PHY_OFDM(0xC8),
37058619b14SKalle Valo 		B43_PHY_OFDM(0xCF),
37158619b14SKalle Valo 	};
37258619b14SKalle Valo 
37358619b14SKalle Valo 	static const u16 coefs[] = {
37458619b14SKalle Valo 		0xDE5E, 0xE832, 0xE331, 0x4D26,
37558619b14SKalle Valo 		0x0026, 0x1420, 0x0020, 0xFE08,
37658619b14SKalle Valo 		0x0008,
37758619b14SKalle Valo 	};
37858619b14SKalle Valo 
37958619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
38058619b14SKalle Valo 	int i;
38158619b14SKalle Valo 
38258619b14SKalle Valo 	for (i = 0; i < ARRAY_SIZE(addr); i++) {
38358619b14SKalle Valo 		lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]);
38458619b14SKalle Valo 		b43_phy_write(dev, addr[i], coefs[i]);
38558619b14SKalle Valo 	}
38658619b14SKalle Valo }
38758619b14SKalle Valo 
38858619b14SKalle Valo static void lpphy_restore_dig_flt_state(struct b43_wldev *dev)
38958619b14SKalle Valo {
39058619b14SKalle Valo 	static const u16 addr[] = {
39158619b14SKalle Valo 		B43_PHY_OFDM(0xC1),
39258619b14SKalle Valo 		B43_PHY_OFDM(0xC2),
39358619b14SKalle Valo 		B43_PHY_OFDM(0xC3),
39458619b14SKalle Valo 		B43_PHY_OFDM(0xC4),
39558619b14SKalle Valo 		B43_PHY_OFDM(0xC5),
39658619b14SKalle Valo 		B43_PHY_OFDM(0xC6),
39758619b14SKalle Valo 		B43_PHY_OFDM(0xC7),
39858619b14SKalle Valo 		B43_PHY_OFDM(0xC8),
39958619b14SKalle Valo 		B43_PHY_OFDM(0xCF),
40058619b14SKalle Valo 	};
40158619b14SKalle Valo 
40258619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
40358619b14SKalle Valo 	int i;
40458619b14SKalle Valo 
40558619b14SKalle Valo 	for (i = 0; i < ARRAY_SIZE(addr); i++)
40658619b14SKalle Valo 		b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]);
40758619b14SKalle Valo }
40858619b14SKalle Valo 
40958619b14SKalle Valo static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev)
41058619b14SKalle Valo {
41158619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
41258619b14SKalle Valo 
41358619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_DAC_CTL, 0x50);
41458619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0x8800);
41558619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0);
41658619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0);
41758619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0);
41858619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0);
41958619b14SKalle Valo 	b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0);
42058619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0);
42158619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10);
42258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4);
42358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200);
42458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F);
42558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40);
42658619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_PREAMBLECONFIRMTO, 0xFF00, 0x2);
42758619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000);
42858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000);
42958619b14SKalle Valo 	b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1);
43058619b14SKalle Valo 	if (dev->dev->board_rev >= 0x18) {
43158619b14SKalle Valo 		b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC);
43258619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14);
43358619b14SKalle Valo 	} else {
43458619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10);
43558619b14SKalle Valo 	}
43658619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4);
43758619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100);
43858619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48);
43958619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0xFF00, 0x46);
44058619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xE4), 0xFF00, 0x10);
44158619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_PWR_THRESH1, 0xFFF0, 0x9);
44258619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_GAINDIRECTMISMATCH, ~0xF);
44358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5500);
44458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0xA0);
44558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xE0FF, 0x300);
44658619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2A00);
44758619b14SKalle Valo 	if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
44858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100);
44958619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xA);
45058619b14SKalle Valo 	} else {
45158619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x1E00);
45258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0xD);
45358619b14SKalle Valo 	}
45458619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFFE0, 0x1F);
45558619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC);
45658619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0xFF00, 0x19);
45758619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0x03FF, 0x3C00);
45858619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xFE), 0xFC1F, 0x3E0);
45958619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xFF), 0xFFE0, 0xC);
46058619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0x100), 0x00FF, 0x1900);
46158619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800);
46258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12);
46358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000);
46458619b14SKalle Valo 
46558619b14SKalle Valo 	if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
46658619b14SKalle Valo 		b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0);
46758619b14SKalle Valo 		b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40);
46858619b14SKalle Valo 	}
46958619b14SKalle Valo 
47057fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
47158619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40);
47258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0xB00);
47358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x6);
47458619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0x9D00);
47558619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0xFF00, 0xA1);
47658619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF);
47758619b14SKalle Valo 	} else /* 5GHz */
47858619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x40);
47958619b14SKalle Valo 
48058619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0xB3);
48158619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00);
48258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, 0xFF00, lpphy->rx_pwr_offset);
48358619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RESET_CTL, 0x44);
48458619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RESET_CTL, 0x80);
48558619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, 0xA954);
48658619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1,
48758619b14SKalle Valo 		      0x2000 | ((u16)lpphy->rssi_gs << 10) |
48858619b14SKalle Valo 		      ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf);
48958619b14SKalle Valo 
49058619b14SKalle Valo 	if ((dev->dev->chip_id == 0x4325) && (dev->dev->chip_rev == 0)) {
49158619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C);
49258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800);
49358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400);
49458619b14SKalle Valo 	}
49558619b14SKalle Valo 
49658619b14SKalle Valo 	lpphy_save_dig_flt_state(dev);
49758619b14SKalle Valo }
49858619b14SKalle Valo 
49958619b14SKalle Valo static void lpphy_baseband_init(struct b43_wldev *dev)
50058619b14SKalle Valo {
50158619b14SKalle Valo 	lpphy_table_init(dev);
50258619b14SKalle Valo 	if (dev->phy.rev >= 2)
50358619b14SKalle Valo 		lpphy_baseband_rev2plus_init(dev);
50458619b14SKalle Valo 	else
50558619b14SKalle Valo 		lpphy_baseband_rev0_1_init(dev);
50658619b14SKalle Valo }
50758619b14SKalle Valo 
50858619b14SKalle Valo struct b2062_freqdata {
50958619b14SKalle Valo 	u16 freq;
51058619b14SKalle Valo 	u8 data[6];
51158619b14SKalle Valo };
51258619b14SKalle Valo 
51358619b14SKalle Valo /* Initialize the 2062 radio. */
51458619b14SKalle Valo static void lpphy_2062_init(struct b43_wldev *dev)
51558619b14SKalle Valo {
51658619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
51758619b14SKalle Valo 	struct ssb_bus *bus = dev->dev->sdev->bus;
51858619b14SKalle Valo 	u32 crystalfreq, tmp, ref;
51958619b14SKalle Valo 	unsigned int i;
52058619b14SKalle Valo 	const struct b2062_freqdata *fd = NULL;
52158619b14SKalle Valo 
52258619b14SKalle Valo 	static const struct b2062_freqdata freqdata_tab[] = {
52358619b14SKalle Valo 		{ .freq = 12000, .data[0] =  6, .data[1] =  6, .data[2] =  6,
52458619b14SKalle Valo 				 .data[3] =  6, .data[4] = 10, .data[5] =  6, },
52558619b14SKalle Valo 		{ .freq = 13000, .data[0] =  4, .data[1] =  4, .data[2] =  4,
52658619b14SKalle Valo 				 .data[3] =  4, .data[4] = 11, .data[5] =  7, },
52758619b14SKalle Valo 		{ .freq = 14400, .data[0] =  3, .data[1] =  3, .data[2] =  3,
52858619b14SKalle Valo 				 .data[3] =  3, .data[4] = 12, .data[5] =  7, },
52958619b14SKalle Valo 		{ .freq = 16200, .data[0] =  3, .data[1] =  3, .data[2] =  3,
53058619b14SKalle Valo 				 .data[3] =  3, .data[4] = 13, .data[5] =  8, },
53158619b14SKalle Valo 		{ .freq = 18000, .data[0] =  2, .data[1] =  2, .data[2] =  2,
53258619b14SKalle Valo 				 .data[3] =  2, .data[4] = 14, .data[5] =  8, },
53358619b14SKalle Valo 		{ .freq = 19200, .data[0] =  1, .data[1] =  1, .data[2] =  1,
53458619b14SKalle Valo 				 .data[3] =  1, .data[4] = 14, .data[5] =  9, },
53558619b14SKalle Valo 	};
53658619b14SKalle Valo 
53758619b14SKalle Valo 	b2062_upload_init_table(dev);
53858619b14SKalle Valo 
53958619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_CTL3, 0);
54058619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_CTL4, 0);
54158619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_CTL5, 0);
54258619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_CTL6, 0);
54358619b14SKalle Valo 	b43_radio_write(dev, B2062_N_PDN_CTL0, 0x40);
54458619b14SKalle Valo 	b43_radio_write(dev, B2062_N_PDN_CTL0, 0);
54558619b14SKalle Valo 	b43_radio_write(dev, B2062_N_CALIB_TS, 0x10);
54658619b14SKalle Valo 	b43_radio_write(dev, B2062_N_CALIB_TS, 0);
54758619b14SKalle Valo 	if (dev->phy.rev > 0) {
54858619b14SKalle Valo 		b43_radio_write(dev, B2062_S_BG_CTL1,
54958619b14SKalle Valo 			(b43_radio_read(dev, B2062_N_COMM2) >> 1) | 0x80);
55058619b14SKalle Valo 	}
55157fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
55258619b14SKalle Valo 		b43_radio_set(dev, B2062_N_TSSI_CTL0, 0x1);
55358619b14SKalle Valo 	else
55458619b14SKalle Valo 		b43_radio_mask(dev, B2062_N_TSSI_CTL0, ~0x1);
55558619b14SKalle Valo 
55658619b14SKalle Valo 	/* Get the crystal freq, in Hz. */
55758619b14SKalle Valo 	crystalfreq = bus->chipco.pmu.crystalfreq * 1000;
55858619b14SKalle Valo 
55958619b14SKalle Valo 	B43_WARN_ON(!(bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU));
56058619b14SKalle Valo 	B43_WARN_ON(crystalfreq == 0);
56158619b14SKalle Valo 
56258619b14SKalle Valo 	if (crystalfreq <= 30000000) {
56358619b14SKalle Valo 		lpphy->pdiv = 1;
56458619b14SKalle Valo 		b43_radio_mask(dev, B2062_S_RFPLL_CTL1, 0xFFFB);
56558619b14SKalle Valo 	} else {
56658619b14SKalle Valo 		lpphy->pdiv = 2;
56758619b14SKalle Valo 		b43_radio_set(dev, B2062_S_RFPLL_CTL1, 0x4);
56858619b14SKalle Valo 	}
56958619b14SKalle Valo 
57058619b14SKalle Valo 	tmp = (((800000000 * lpphy->pdiv + crystalfreq) /
57158619b14SKalle Valo 	      (2 * crystalfreq)) - 8) & 0xFF;
57258619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL7, tmp);
57358619b14SKalle Valo 
57458619b14SKalle Valo 	tmp = (((100 * crystalfreq + 16000000 * lpphy->pdiv) /
57558619b14SKalle Valo 	      (32000000 * lpphy->pdiv)) - 1) & 0xFF;
57658619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL18, tmp);
57758619b14SKalle Valo 
57858619b14SKalle Valo 	tmp = (((2 * crystalfreq + 1000000 * lpphy->pdiv) /
57958619b14SKalle Valo 	      (2000000 * lpphy->pdiv)) - 1) & 0xFF;
58058619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL19, tmp);
58158619b14SKalle Valo 
58258619b14SKalle Valo 	ref = (1000 * lpphy->pdiv + 2 * crystalfreq) / (2000 * lpphy->pdiv);
58358619b14SKalle Valo 	ref &= 0xFFFF;
58458619b14SKalle Valo 	for (i = 0; i < ARRAY_SIZE(freqdata_tab); i++) {
58558619b14SKalle Valo 		if (ref < freqdata_tab[i].freq) {
58658619b14SKalle Valo 			fd = &freqdata_tab[i];
58758619b14SKalle Valo 			break;
58858619b14SKalle Valo 		}
58958619b14SKalle Valo 	}
59058619b14SKalle Valo 	if (!fd)
59158619b14SKalle Valo 		fd = &freqdata_tab[ARRAY_SIZE(freqdata_tab) - 1];
59258619b14SKalle Valo 	b43dbg(dev->wl, "b2062: Using crystal tab entry %u kHz.\n",
59358619b14SKalle Valo 	       fd->freq); /* FIXME: Keep this printk until the code is fully debugged. */
59458619b14SKalle Valo 
59558619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL8,
59658619b14SKalle Valo 			((u16)(fd->data[1]) << 4) | fd->data[0]);
59758619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL9,
59858619b14SKalle Valo 			((u16)(fd->data[3]) << 4) | fd->data[2]);
59958619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL10, fd->data[4]);
60058619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL11, fd->data[5]);
60158619b14SKalle Valo }
60258619b14SKalle Valo 
60358619b14SKalle Valo /* Initialize the 2063 radio. */
60458619b14SKalle Valo static void lpphy_2063_init(struct b43_wldev *dev)
60558619b14SKalle Valo {
60658619b14SKalle Valo 	b2063_upload_init_table(dev);
60758619b14SKalle Valo 	b43_radio_write(dev, B2063_LOGEN_SP5, 0);
60858619b14SKalle Valo 	b43_radio_set(dev, B2063_COMM8, 0x38);
60958619b14SKalle Valo 	b43_radio_write(dev, B2063_REG_SP1, 0x56);
61058619b14SKalle Valo 	b43_radio_mask(dev, B2063_RX_BB_CTL2, ~0x2);
61158619b14SKalle Valo 	b43_radio_write(dev, B2063_PA_SP7, 0);
61258619b14SKalle Valo 	b43_radio_write(dev, B2063_TX_RF_SP6, 0x20);
61358619b14SKalle Valo 	b43_radio_write(dev, B2063_TX_RF_SP9, 0x40);
61458619b14SKalle Valo 	if (dev->phy.rev == 2) {
61558619b14SKalle Valo 		b43_radio_write(dev, B2063_PA_SP3, 0xa0);
61658619b14SKalle Valo 		b43_radio_write(dev, B2063_PA_SP4, 0xa0);
61758619b14SKalle Valo 		b43_radio_write(dev, B2063_PA_SP2, 0x18);
61858619b14SKalle Valo 	} else {
61958619b14SKalle Valo 		b43_radio_write(dev, B2063_PA_SP3, 0x20);
62058619b14SKalle Valo 		b43_radio_write(dev, B2063_PA_SP2, 0x20);
62158619b14SKalle Valo 	}
62258619b14SKalle Valo }
62358619b14SKalle Valo 
62458619b14SKalle Valo struct lpphy_stx_table_entry {
62558619b14SKalle Valo 	u16 phy_offset;
62658619b14SKalle Valo 	u16 phy_shift;
62758619b14SKalle Valo 	u16 rf_addr;
62858619b14SKalle Valo 	u16 rf_shift;
62958619b14SKalle Valo 	u16 mask;
63058619b14SKalle Valo };
63158619b14SKalle Valo 
63258619b14SKalle Valo static const struct lpphy_stx_table_entry lpphy_stx_table[] = {
63358619b14SKalle Valo 	{ .phy_offset = 2, .phy_shift = 6, .rf_addr = 0x3d, .rf_shift = 3, .mask = 0x01, },
63458619b14SKalle Valo 	{ .phy_offset = 1, .phy_shift = 12, .rf_addr = 0x4c, .rf_shift = 1, .mask = 0x01, },
63558619b14SKalle Valo 	{ .phy_offset = 1, .phy_shift = 8, .rf_addr = 0x50, .rf_shift = 0, .mask = 0x7f, },
63658619b14SKalle Valo 	{ .phy_offset = 0, .phy_shift = 8, .rf_addr = 0x44, .rf_shift = 0, .mask = 0xff, },
63758619b14SKalle Valo 	{ .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4a, .rf_shift = 0, .mask = 0xff, },
63858619b14SKalle Valo 	{ .phy_offset = 0, .phy_shift = 4, .rf_addr = 0x4d, .rf_shift = 0, .mask = 0xff, },
63958619b14SKalle Valo 	{ .phy_offset = 1, .phy_shift = 4, .rf_addr = 0x4e, .rf_shift = 0, .mask = 0xff, },
64058619b14SKalle Valo 	{ .phy_offset = 0, .phy_shift = 12, .rf_addr = 0x4f, .rf_shift = 0, .mask = 0x0f, },
64158619b14SKalle Valo 	{ .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4f, .rf_shift = 4, .mask = 0x0f, },
64258619b14SKalle Valo 	{ .phy_offset = 3, .phy_shift = 0, .rf_addr = 0x49, .rf_shift = 0, .mask = 0x0f, },
64358619b14SKalle Valo 	{ .phy_offset = 4, .phy_shift = 3, .rf_addr = 0x46, .rf_shift = 4, .mask = 0x07, },
64458619b14SKalle Valo 	{ .phy_offset = 3, .phy_shift = 15, .rf_addr = 0x46, .rf_shift = 0, .mask = 0x01, },
64558619b14SKalle Valo 	{ .phy_offset = 4, .phy_shift = 0, .rf_addr = 0x46, .rf_shift = 1, .mask = 0x07, },
64658619b14SKalle Valo 	{ .phy_offset = 3, .phy_shift = 8, .rf_addr = 0x48, .rf_shift = 4, .mask = 0x07, },
64758619b14SKalle Valo 	{ .phy_offset = 3, .phy_shift = 11, .rf_addr = 0x48, .rf_shift = 0, .mask = 0x0f, },
64858619b14SKalle Valo 	{ .phy_offset = 3, .phy_shift = 4, .rf_addr = 0x49, .rf_shift = 4, .mask = 0x0f, },
64958619b14SKalle Valo 	{ .phy_offset = 2, .phy_shift = 15, .rf_addr = 0x45, .rf_shift = 0, .mask = 0x01, },
65058619b14SKalle Valo 	{ .phy_offset = 5, .phy_shift = 13, .rf_addr = 0x52, .rf_shift = 4, .mask = 0x07, },
65158619b14SKalle Valo 	{ .phy_offset = 6, .phy_shift = 0, .rf_addr = 0x52, .rf_shift = 7, .mask = 0x01, },
65258619b14SKalle Valo 	{ .phy_offset = 5, .phy_shift = 3, .rf_addr = 0x41, .rf_shift = 5, .mask = 0x07, },
65358619b14SKalle Valo 	{ .phy_offset = 5, .phy_shift = 6, .rf_addr = 0x41, .rf_shift = 0, .mask = 0x0f, },
65458619b14SKalle Valo 	{ .phy_offset = 5, .phy_shift = 10, .rf_addr = 0x42, .rf_shift = 5, .mask = 0x07, },
65558619b14SKalle Valo 	{ .phy_offset = 4, .phy_shift = 15, .rf_addr = 0x42, .rf_shift = 0, .mask = 0x01, },
65658619b14SKalle Valo 	{ .phy_offset = 5, .phy_shift = 0, .rf_addr = 0x42, .rf_shift = 1, .mask = 0x07, },
65758619b14SKalle Valo 	{ .phy_offset = 4, .phy_shift = 11, .rf_addr = 0x43, .rf_shift = 4, .mask = 0x0f, },
65858619b14SKalle Valo 	{ .phy_offset = 4, .phy_shift = 7, .rf_addr = 0x43, .rf_shift = 0, .mask = 0x0f, },
65958619b14SKalle Valo 	{ .phy_offset = 4, .phy_shift = 6, .rf_addr = 0x45, .rf_shift = 1, .mask = 0x01, },
66058619b14SKalle Valo 	{ .phy_offset = 2, .phy_shift = 7, .rf_addr = 0x40, .rf_shift = 4, .mask = 0x0f, },
66158619b14SKalle Valo 	{ .phy_offset = 2, .phy_shift = 11, .rf_addr = 0x40, .rf_shift = 0, .mask = 0x0f, },
66258619b14SKalle Valo };
66358619b14SKalle Valo 
66458619b14SKalle Valo static void lpphy_sync_stx(struct b43_wldev *dev)
66558619b14SKalle Valo {
66658619b14SKalle Valo 	const struct lpphy_stx_table_entry *e;
66758619b14SKalle Valo 	unsigned int i;
66858619b14SKalle Valo 	u16 tmp;
66958619b14SKalle Valo 
67058619b14SKalle Valo 	for (i = 0; i < ARRAY_SIZE(lpphy_stx_table); i++) {
67158619b14SKalle Valo 		e = &lpphy_stx_table[i];
67258619b14SKalle Valo 		tmp = b43_radio_read(dev, e->rf_addr);
67358619b14SKalle Valo 		tmp >>= e->rf_shift;
67458619b14SKalle Valo 		tmp <<= e->phy_shift;
67558619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0xF2 + e->phy_offset),
67658619b14SKalle Valo 				~(e->mask << e->phy_shift), tmp);
67758619b14SKalle Valo 	}
67858619b14SKalle Valo }
67958619b14SKalle Valo 
68058619b14SKalle Valo static void lpphy_radio_init(struct b43_wldev *dev)
68158619b14SKalle Valo {
68258619b14SKalle Valo 	/* The radio is attached through the 4wire bus. */
68358619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_FOURWIRE_CTL, 0x2);
68458619b14SKalle Valo 	udelay(1);
68558619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_FOURWIRE_CTL, 0xFFFD);
68658619b14SKalle Valo 	udelay(1);
68758619b14SKalle Valo 
68858619b14SKalle Valo 	if (dev->phy.radio_ver == 0x2062) {
68958619b14SKalle Valo 		lpphy_2062_init(dev);
69058619b14SKalle Valo 	} else {
69158619b14SKalle Valo 		lpphy_2063_init(dev);
69258619b14SKalle Valo 		lpphy_sync_stx(dev);
69358619b14SKalle Valo 		b43_phy_write(dev, B43_PHY_OFDM(0xF0), 0x5F80);
69458619b14SKalle Valo 		b43_phy_write(dev, B43_PHY_OFDM(0xF1), 0);
69558619b14SKalle Valo 		if (dev->dev->chip_id == 0x4325) {
69658619b14SKalle Valo 			// TODO SSB PMU recalibration
69758619b14SKalle Valo 		}
69858619b14SKalle Valo 	}
69958619b14SKalle Valo }
70058619b14SKalle Valo 
70158619b14SKalle Valo struct lpphy_iq_est { u32 iq_prod, i_pwr, q_pwr; };
70258619b14SKalle Valo 
70358619b14SKalle Valo static void lpphy_set_rc_cap(struct b43_wldev *dev)
70458619b14SKalle Valo {
70558619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
70658619b14SKalle Valo 
70758619b14SKalle Valo 	u8 rc_cap = (lpphy->rc_cap & 0x1F) >> 1;
70858619b14SKalle Valo 
70958619b14SKalle Valo 	if (dev->phy.rev == 1) //FIXME check channel 14!
71058619b14SKalle Valo 		rc_cap = min_t(u8, rc_cap + 5, 15);
71158619b14SKalle Valo 
71258619b14SKalle Valo 	b43_radio_write(dev, B2062_N_RXBB_CALIB2,
71358619b14SKalle Valo 			max_t(u8, lpphy->rc_cap - 4, 0x80));
71458619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_CTL_A, rc_cap | 0x80);
71558619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RXG_CNT16,
71658619b14SKalle Valo 			((lpphy->rc_cap & 0x1F) >> 2) | 0x80);
71758619b14SKalle Valo }
71858619b14SKalle Valo 
71958619b14SKalle Valo static u8 lpphy_get_bb_mult(struct b43_wldev *dev)
72058619b14SKalle Valo {
72158619b14SKalle Valo 	return (b43_lptab_read(dev, B43_LPTAB16(0, 87)) & 0xFF00) >> 8;
72258619b14SKalle Valo }
72358619b14SKalle Valo 
72458619b14SKalle Valo static void lpphy_set_bb_mult(struct b43_wldev *dev, u8 bb_mult)
72558619b14SKalle Valo {
72658619b14SKalle Valo 	b43_lptab_write(dev, B43_LPTAB16(0, 87), (u16)bb_mult << 8);
72758619b14SKalle Valo }
72858619b14SKalle Valo 
72958619b14SKalle Valo static void lpphy_set_deaf(struct b43_wldev *dev, bool user)
73058619b14SKalle Valo {
73158619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
73258619b14SKalle Valo 
73358619b14SKalle Valo 	if (user)
73458619b14SKalle Valo 		lpphy->crs_usr_disable = true;
73558619b14SKalle Valo 	else
73658619b14SKalle Valo 		lpphy->crs_sys_disable = true;
73758619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x80);
73858619b14SKalle Valo }
73958619b14SKalle Valo 
74058619b14SKalle Valo static void lpphy_clear_deaf(struct b43_wldev *dev, bool user)
74158619b14SKalle Valo {
74258619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
74358619b14SKalle Valo 
74458619b14SKalle Valo 	if (user)
74558619b14SKalle Valo 		lpphy->crs_usr_disable = false;
74658619b14SKalle Valo 	else
74758619b14SKalle Valo 		lpphy->crs_sys_disable = false;
74858619b14SKalle Valo 
74958619b14SKalle Valo 	if (!lpphy->crs_usr_disable && !lpphy->crs_sys_disable) {
75057fbcce3SJohannes Berg 		if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
75158619b14SKalle Valo 			b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL,
75258619b14SKalle Valo 					0xFF1F, 0x60);
75358619b14SKalle Valo 		else
75458619b14SKalle Valo 			b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL,
75558619b14SKalle Valo 					0xFF1F, 0x20);
75658619b14SKalle Valo 	}
75758619b14SKalle Valo }
75858619b14SKalle Valo 
75958619b14SKalle Valo static void lpphy_set_trsw_over(struct b43_wldev *dev, bool tx, bool rx)
76058619b14SKalle Valo {
76158619b14SKalle Valo 	u16 trsw = (tx << 1) | rx;
76258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, trsw);
76358619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3);
76458619b14SKalle Valo }
76558619b14SKalle Valo 
76658619b14SKalle Valo static void lpphy_disable_crs(struct b43_wldev *dev, bool user)
76758619b14SKalle Valo {
76858619b14SKalle Valo 	lpphy_set_deaf(dev, user);
76958619b14SKalle Valo 	lpphy_set_trsw_over(dev, false, true);
77058619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB);
77158619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4);
77258619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7);
77358619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
77458619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x10);
77558619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
77658619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFDF);
77758619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20);
77858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFBF);
77958619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
78058619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x7);
78158619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x38);
78258619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F);
78358619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x100);
78458619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFDFF);
78558619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL0, 0);
78658619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL1, 1);
78758619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL2, 0x20);
78858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF);
78958619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xF7FF);
79058619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0);
79158619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, 0x45AF);
79258619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0x3FF);
79358619b14SKalle Valo }
79458619b14SKalle Valo 
79558619b14SKalle Valo static void lpphy_restore_crs(struct b43_wldev *dev, bool user)
79658619b14SKalle Valo {
79758619b14SKalle Valo 	lpphy_clear_deaf(dev, user);
79858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFF80);
79958619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFC00);
80058619b14SKalle Valo }
80158619b14SKalle Valo 
80258619b14SKalle Valo struct lpphy_tx_gains { u16 gm, pga, pad, dac; };
80358619b14SKalle Valo 
80458619b14SKalle Valo static void lpphy_disable_rx_gain_override(struct b43_wldev *dev)
80558619b14SKalle Valo {
80658619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE);
80758619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF);
80858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF);
80958619b14SKalle Valo 	if (dev->phy.rev >= 2) {
81058619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
81157fbcce3SJohannes Berg 		if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
81258619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF);
81358619b14SKalle Valo 			b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7);
81458619b14SKalle Valo 		}
81558619b14SKalle Valo 	} else {
81658619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF);
81758619b14SKalle Valo 	}
81858619b14SKalle Valo }
81958619b14SKalle Valo 
82058619b14SKalle Valo static void lpphy_enable_rx_gain_override(struct b43_wldev *dev)
82158619b14SKalle Valo {
82258619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1);
82358619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10);
82458619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40);
82558619b14SKalle Valo 	if (dev->phy.rev >= 2) {
82658619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
82757fbcce3SJohannes Berg 		if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
82858619b14SKalle Valo 			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400);
82958619b14SKalle Valo 			b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8);
83058619b14SKalle Valo 		}
83158619b14SKalle Valo 	} else {
83258619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200);
83358619b14SKalle Valo 	}
83458619b14SKalle Valo }
83558619b14SKalle Valo 
83658619b14SKalle Valo static void lpphy_disable_tx_gain_override(struct b43_wldev *dev)
83758619b14SKalle Valo {
83858619b14SKalle Valo 	if (dev->phy.rev < 2)
83958619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF);
84058619b14SKalle Valo 	else {
84158619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F);
84258619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF);
84358619b14SKalle Valo 	}
84458619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF);
84558619b14SKalle Valo }
84658619b14SKalle Valo 
84758619b14SKalle Valo static void lpphy_enable_tx_gain_override(struct b43_wldev *dev)
84858619b14SKalle Valo {
84958619b14SKalle Valo 	if (dev->phy.rev < 2)
85058619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100);
85158619b14SKalle Valo 	else {
85258619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x80);
85358619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x4000);
85458619b14SKalle Valo 	}
85558619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x40);
85658619b14SKalle Valo }
85758619b14SKalle Valo 
85858619b14SKalle Valo static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev)
85958619b14SKalle Valo {
86058619b14SKalle Valo 	struct lpphy_tx_gains gains;
86158619b14SKalle Valo 	u16 tmp;
86258619b14SKalle Valo 
86358619b14SKalle Valo 	gains.dac = (b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0x380) >> 7;
86458619b14SKalle Valo 	if (dev->phy.rev < 2) {
86558619b14SKalle Valo 		tmp = b43_phy_read(dev,
86658619b14SKalle Valo 				   B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL) & 0x7FF;
86758619b14SKalle Valo 		gains.gm = tmp & 0x0007;
86858619b14SKalle Valo 		gains.pga = (tmp & 0x0078) >> 3;
86958619b14SKalle Valo 		gains.pad = (tmp & 0x780) >> 7;
87058619b14SKalle Valo 	} else {
87158619b14SKalle Valo 		tmp = b43_phy_read(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL);
87258619b14SKalle Valo 		gains.pad = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0xFF;
87358619b14SKalle Valo 		gains.gm = tmp & 0xFF;
87458619b14SKalle Valo 		gains.pga = (tmp >> 8) & 0xFF;
87558619b14SKalle Valo 	}
87658619b14SKalle Valo 
87758619b14SKalle Valo 	return gains;
87858619b14SKalle Valo }
87958619b14SKalle Valo 
88058619b14SKalle Valo static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac)
88158619b14SKalle Valo {
88258619b14SKalle Valo 	u16 ctl = b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0xC7F;
88358619b14SKalle Valo 	ctl |= dac << 7;
88458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl);
88558619b14SKalle Valo }
88658619b14SKalle Valo 
88758619b14SKalle Valo static u16 lpphy_get_pa_gain(struct b43_wldev *dev)
88858619b14SKalle Valo {
88958619b14SKalle Valo 	return b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x7F;
89058619b14SKalle Valo }
89158619b14SKalle Valo 
89258619b14SKalle Valo static void lpphy_set_pa_gain(struct b43_wldev *dev, u16 gain)
89358619b14SKalle Valo {
89458619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), 0xE03F, gain << 6);
89558619b14SKalle Valo 	b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), 0x80FF, gain << 8);
89658619b14SKalle Valo }
89758619b14SKalle Valo 
89858619b14SKalle Valo static void lpphy_set_tx_gains(struct b43_wldev *dev,
89958619b14SKalle Valo 			       struct lpphy_tx_gains gains)
90058619b14SKalle Valo {
90158619b14SKalle Valo 	u16 rf_gain, pa_gain;
90258619b14SKalle Valo 
90358619b14SKalle Valo 	if (dev->phy.rev < 2) {
90458619b14SKalle Valo 		rf_gain = (gains.pad << 7) | (gains.pga << 3) | gains.gm;
90558619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
90658619b14SKalle Valo 				0xF800, rf_gain);
90758619b14SKalle Valo 	} else {
90858619b14SKalle Valo 		pa_gain = lpphy_get_pa_gain(dev);
90958619b14SKalle Valo 		b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
91058619b14SKalle Valo 			      (gains.pga << 8) | gains.gm);
91158619b14SKalle Valo 		/*
91258619b14SKalle Valo 		 * SPEC FIXME The spec calls for (pa_gain << 8) here, but that
91358619b14SKalle Valo 		 * conflicts with the spec for set_pa_gain! Vendor driver bug?
91458619b14SKalle Valo 		 */
91558619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0xFB),
91658619b14SKalle Valo 				0x8000, gains.pad | (pa_gain << 6));
91758619b14SKalle Valo 		b43_phy_write(dev, B43_PHY_OFDM(0xFC),
91858619b14SKalle Valo 			      (gains.pga << 8) | gains.gm);
91958619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0xFD),
92058619b14SKalle Valo 				0x8000, gains.pad | (pa_gain << 8));
92158619b14SKalle Valo 	}
92258619b14SKalle Valo 	lpphy_set_dac_gain(dev, gains.dac);
92358619b14SKalle Valo 	lpphy_enable_tx_gain_override(dev);
92458619b14SKalle Valo }
92558619b14SKalle Valo 
92658619b14SKalle Valo static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain)
92758619b14SKalle Valo {
92858619b14SKalle Valo 	u16 trsw = gain & 0x1;
92958619b14SKalle Valo 	u16 lna = (gain & 0xFFFC) | ((gain & 0xC) >> 2);
93058619b14SKalle Valo 	u16 ext_lna = (gain & 2) >> 1;
93158619b14SKalle Valo 
93258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw);
93358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
93458619b14SKalle Valo 			0xFBFF, ext_lna << 10);
93558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
93658619b14SKalle Valo 			0xF7FF, ext_lna << 11);
93758619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, lna);
93858619b14SKalle Valo }
93958619b14SKalle Valo 
94058619b14SKalle Valo static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain)
94158619b14SKalle Valo {
94258619b14SKalle Valo 	u16 low_gain = gain & 0xFFFF;
94358619b14SKalle Valo 	u16 high_gain = (gain >> 16) & 0xF;
94458619b14SKalle Valo 	u16 ext_lna = (gain >> 21) & 0x1;
94558619b14SKalle Valo 	u16 trsw = ~(gain >> 20) & 0x1;
94658619b14SKalle Valo 	u16 tmp;
94758619b14SKalle Valo 
94858619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw);
94958619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
95058619b14SKalle Valo 			0xFDFF, ext_lna << 9);
95158619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
95258619b14SKalle Valo 			0xFBFF, ext_lna << 10);
95358619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, low_gain);
95458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF0, high_gain);
95557fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
95658619b14SKalle Valo 		tmp = (gain >> 2) & 0x3;
95758619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL,
95858619b14SKalle Valo 				0xE7FF, tmp<<11);
95958619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0xE6), 0xFFE7, tmp << 3);
96058619b14SKalle Valo 	}
96158619b14SKalle Valo }
96258619b14SKalle Valo 
96358619b14SKalle Valo static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain)
96458619b14SKalle Valo {
96558619b14SKalle Valo 	if (dev->phy.rev < 2)
96658619b14SKalle Valo 		lpphy_rev0_1_set_rx_gain(dev, gain);
96758619b14SKalle Valo 	else
96858619b14SKalle Valo 		lpphy_rev2plus_set_rx_gain(dev, gain);
96958619b14SKalle Valo 	lpphy_enable_rx_gain_override(dev);
97058619b14SKalle Valo }
97158619b14SKalle Valo 
97258619b14SKalle Valo static void lpphy_set_rx_gain_by_index(struct b43_wldev *dev, u16 idx)
97358619b14SKalle Valo {
97458619b14SKalle Valo 	u32 gain = b43_lptab_read(dev, B43_LPTAB16(12, idx));
97558619b14SKalle Valo 	lpphy_set_rx_gain(dev, gain);
97658619b14SKalle Valo }
97758619b14SKalle Valo 
97858619b14SKalle Valo static void lpphy_stop_ddfs(struct b43_wldev *dev)
97958619b14SKalle Valo {
98058619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFD);
98158619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xFFDF);
98258619b14SKalle Valo }
98358619b14SKalle Valo 
98458619b14SKalle Valo static void lpphy_run_ddfs(struct b43_wldev *dev, int i_on, int q_on,
98558619b14SKalle Valo 			   int incr1, int incr2, int scale_idx)
98658619b14SKalle Valo {
98758619b14SKalle Valo 	lpphy_stop_ddfs(dev);
98858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0xFF80);
98958619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0x80FF);
99058619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0xFF80, incr1);
99158619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0x80FF, incr2 << 8);
99258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF7, i_on << 3);
99358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFEF, q_on << 4);
99458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFF9F, scale_idx << 5);
99558619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFB);
99658619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_AFE_DDFS, 0x2);
99758619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x20);
99858619b14SKalle Valo }
99958619b14SKalle Valo 
100058619b14SKalle Valo static bool lpphy_rx_iq_est(struct b43_wldev *dev, u16 samples, u8 time,
100158619b14SKalle Valo 			   struct lpphy_iq_est *iq_est)
100258619b14SKalle Valo {
100358619b14SKalle Valo 	int i;
100458619b14SKalle Valo 
100558619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFF7);
100658619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_IQ_NUM_SMPLS_ADDR, samples);
100758619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFF00, time);
100858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFEFF);
100958619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0x200);
101058619b14SKalle Valo 
101158619b14SKalle Valo 	for (i = 0; i < 500; i++) {
101258619b14SKalle Valo 		if (!(b43_phy_read(dev,
101358619b14SKalle Valo 				B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200))
101458619b14SKalle Valo 			break;
101558619b14SKalle Valo 		msleep(1);
101658619b14SKalle Valo 	}
101758619b14SKalle Valo 
101858619b14SKalle Valo 	if ((b43_phy_read(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) {
101958619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8);
102058619b14SKalle Valo 		return false;
102158619b14SKalle Valo 	}
102258619b14SKalle Valo 
102358619b14SKalle Valo 	iq_est->iq_prod = b43_phy_read(dev, B43_LPPHY_IQ_ACC_HI_ADDR);
102458619b14SKalle Valo 	iq_est->iq_prod <<= 16;
102558619b14SKalle Valo 	iq_est->iq_prod |= b43_phy_read(dev, B43_LPPHY_IQ_ACC_LO_ADDR);
102658619b14SKalle Valo 
102758619b14SKalle Valo 	iq_est->i_pwr = b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR);
102858619b14SKalle Valo 	iq_est->i_pwr <<= 16;
102958619b14SKalle Valo 	iq_est->i_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR);
103058619b14SKalle Valo 
103158619b14SKalle Valo 	iq_est->q_pwr = b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR);
103258619b14SKalle Valo 	iq_est->q_pwr <<= 16;
103358619b14SKalle Valo 	iq_est->q_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR);
103458619b14SKalle Valo 
103558619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8);
103658619b14SKalle Valo 	return true;
103758619b14SKalle Valo }
103858619b14SKalle Valo 
103958619b14SKalle Valo static int lpphy_loopback(struct b43_wldev *dev)
104058619b14SKalle Valo {
104158619b14SKalle Valo 	struct lpphy_iq_est iq_est;
104258619b14SKalle Valo 	int i, index = -1;
104358619b14SKalle Valo 	u32 tmp;
104458619b14SKalle Valo 
104558619b14SKalle Valo 	memset(&iq_est, 0, sizeof(iq_est));
104658619b14SKalle Valo 
104758619b14SKalle Valo 	lpphy_set_trsw_over(dev, true, true);
104858619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1);
104958619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
105058619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
105158619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800);
105258619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
105358619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x8);
105458619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_CTL_A, 0x80);
105558619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x80);
105658619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x80);
105758619b14SKalle Valo 	for (i = 0; i < 32; i++) {
105858619b14SKalle Valo 		lpphy_set_rx_gain_by_index(dev, i);
105958619b14SKalle Valo 		lpphy_run_ddfs(dev, 1, 1, 5, 5, 0);
106058619b14SKalle Valo 		if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est)))
106158619b14SKalle Valo 			continue;
106258619b14SKalle Valo 		tmp = (iq_est.i_pwr + iq_est.q_pwr) / 1000;
106358619b14SKalle Valo 		if ((tmp > 4000) && (tmp < 10000)) {
106458619b14SKalle Valo 			index = i;
106558619b14SKalle Valo 			break;
106658619b14SKalle Valo 		}
106758619b14SKalle Valo 	}
106858619b14SKalle Valo 	lpphy_stop_ddfs(dev);
106958619b14SKalle Valo 	return index;
107058619b14SKalle Valo }
107158619b14SKalle Valo 
107258619b14SKalle Valo /* Fixed-point division algorithm using only integer math. */
107358619b14SKalle Valo static u32 lpphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision)
107458619b14SKalle Valo {
107558619b14SKalle Valo 	u32 quotient, remainder;
107658619b14SKalle Valo 
107758619b14SKalle Valo 	if (divisor == 0)
107858619b14SKalle Valo 		return 0;
107958619b14SKalle Valo 
108058619b14SKalle Valo 	quotient = dividend / divisor;
108158619b14SKalle Valo 	remainder = dividend % divisor;
108258619b14SKalle Valo 
108358619b14SKalle Valo 	while (precision > 0) {
108458619b14SKalle Valo 		quotient <<= 1;
108558619b14SKalle Valo 		if (remainder << 1 >= divisor) {
108658619b14SKalle Valo 			quotient++;
108758619b14SKalle Valo 			remainder = (remainder << 1) - divisor;
108858619b14SKalle Valo 		}
108958619b14SKalle Valo 		precision--;
109058619b14SKalle Valo 	}
109158619b14SKalle Valo 
109258619b14SKalle Valo 	if (remainder << 1 >= divisor)
109358619b14SKalle Valo 		quotient++;
109458619b14SKalle Valo 
109558619b14SKalle Valo 	return quotient;
109658619b14SKalle Valo }
109758619b14SKalle Valo 
109858619b14SKalle Valo /* Read the TX power control mode from hardware. */
109958619b14SKalle Valo static void lpphy_read_tx_pctl_mode_from_hardware(struct b43_wldev *dev)
110058619b14SKalle Valo {
110158619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
110258619b14SKalle Valo 	u16 ctl;
110358619b14SKalle Valo 
110458619b14SKalle Valo 	ctl = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_CMD);
110558619b14SKalle Valo 	switch (ctl & B43_LPPHY_TX_PWR_CTL_CMD_MODE) {
110658619b14SKalle Valo 	case B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF:
110758619b14SKalle Valo 		lpphy->txpctl_mode = B43_LPPHY_TXPCTL_OFF;
110858619b14SKalle Valo 		break;
110958619b14SKalle Valo 	case B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW:
111058619b14SKalle Valo 		lpphy->txpctl_mode = B43_LPPHY_TXPCTL_SW;
111158619b14SKalle Valo 		break;
111258619b14SKalle Valo 	case B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW:
111358619b14SKalle Valo 		lpphy->txpctl_mode = B43_LPPHY_TXPCTL_HW;
111458619b14SKalle Valo 		break;
111558619b14SKalle Valo 	default:
111658619b14SKalle Valo 		lpphy->txpctl_mode = B43_LPPHY_TXPCTL_UNKNOWN;
111758619b14SKalle Valo 		B43_WARN_ON(1);
111858619b14SKalle Valo 		break;
111958619b14SKalle Valo 	}
112058619b14SKalle Valo }
112158619b14SKalle Valo 
112258619b14SKalle Valo /* Set the TX power control mode in hardware. */
112358619b14SKalle Valo static void lpphy_write_tx_pctl_mode_to_hardware(struct b43_wldev *dev)
112458619b14SKalle Valo {
112558619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
112658619b14SKalle Valo 	u16 ctl;
112758619b14SKalle Valo 
112858619b14SKalle Valo 	switch (lpphy->txpctl_mode) {
112958619b14SKalle Valo 	case B43_LPPHY_TXPCTL_OFF:
113058619b14SKalle Valo 		ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF;
113158619b14SKalle Valo 		break;
113258619b14SKalle Valo 	case B43_LPPHY_TXPCTL_HW:
113358619b14SKalle Valo 		ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_HW;
113458619b14SKalle Valo 		break;
113558619b14SKalle Valo 	case B43_LPPHY_TXPCTL_SW:
113658619b14SKalle Valo 		ctl = B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW;
113758619b14SKalle Valo 		break;
113858619b14SKalle Valo 	default:
113958619b14SKalle Valo 		ctl = 0;
114058619b14SKalle Valo 		B43_WARN_ON(1);
114158619b14SKalle Valo 	}
114258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
114358619b14SKalle Valo 			~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF, ctl);
114458619b14SKalle Valo }
114558619b14SKalle Valo 
114658619b14SKalle Valo static void lpphy_set_tx_power_control(struct b43_wldev *dev,
114758619b14SKalle Valo 				       enum b43_lpphy_txpctl_mode mode)
114858619b14SKalle Valo {
114958619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
115058619b14SKalle Valo 	enum b43_lpphy_txpctl_mode oldmode;
115158619b14SKalle Valo 
115258619b14SKalle Valo 	lpphy_read_tx_pctl_mode_from_hardware(dev);
115358619b14SKalle Valo 	oldmode = lpphy->txpctl_mode;
115458619b14SKalle Valo 	if (oldmode == mode)
115558619b14SKalle Valo 		return;
115658619b14SKalle Valo 	lpphy->txpctl_mode = mode;
115758619b14SKalle Valo 
115858619b14SKalle Valo 	if (oldmode == B43_LPPHY_TXPCTL_HW) {
115958619b14SKalle Valo 		//TODO Update TX Power NPT
116058619b14SKalle Valo 		//TODO Clear all TX Power offsets
116158619b14SKalle Valo 	} else {
116258619b14SKalle Valo 		if (mode == B43_LPPHY_TXPCTL_HW) {
116358619b14SKalle Valo 			//TODO Recalculate target TX power
116458619b14SKalle Valo 			b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
116558619b14SKalle Valo 					0xFF80, lpphy->tssi_idx);
116658619b14SKalle Valo 			b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM,
116758619b14SKalle Valo 					0x8FFF, ((u16)lpphy->tssi_npt << 16));
116858619b14SKalle Valo 			//TODO Set "TSSI Transmit Count" variable to total transmitted frame count
116958619b14SKalle Valo 			lpphy_disable_tx_gain_override(dev);
117058619b14SKalle Valo 			lpphy->tx_pwr_idx_over = -1;
117158619b14SKalle Valo 		}
117258619b14SKalle Valo 	}
117358619b14SKalle Valo 	if (dev->phy.rev >= 2) {
117458619b14SKalle Valo 		if (mode == B43_LPPHY_TXPCTL_HW)
117558619b14SKalle Valo 			b43_phy_set(dev, B43_PHY_OFDM(0xD0), 0x2);
117658619b14SKalle Valo 		else
117758619b14SKalle Valo 			b43_phy_mask(dev, B43_PHY_OFDM(0xD0), 0xFFFD);
117858619b14SKalle Valo 	}
117958619b14SKalle Valo 	lpphy_write_tx_pctl_mode_to_hardware(dev);
118058619b14SKalle Valo }
118158619b14SKalle Valo 
118258619b14SKalle Valo static int b43_lpphy_op_switch_channel(struct b43_wldev *dev,
118358619b14SKalle Valo 				       unsigned int new_channel);
118458619b14SKalle Valo 
118558619b14SKalle Valo static void lpphy_rev0_1_rc_calib(struct b43_wldev *dev)
118658619b14SKalle Valo {
118758619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
118858619b14SKalle Valo 	struct lpphy_iq_est iq_est;
118958619b14SKalle Valo 	struct lpphy_tx_gains tx_gains;
119058619b14SKalle Valo 	static const u32 ideal_pwr_table[21] = {
119158619b14SKalle Valo 		0x10000, 0x10557, 0x10e2d, 0x113e0, 0x10f22, 0x0ff64,
119258619b14SKalle Valo 		0x0eda2, 0x0e5d4, 0x0efd1, 0x0fbe8, 0x0b7b8, 0x04b35,
119358619b14SKalle Valo 		0x01a5e, 0x00a0b, 0x00444, 0x001fd, 0x000ff, 0x00088,
119458619b14SKalle Valo 		0x0004c, 0x0002c, 0x0001a,
119558619b14SKalle Valo 	};
119658619b14SKalle Valo 	bool old_txg_ovr;
119758619b14SKalle Valo 	u8 old_bbmult;
119858619b14SKalle Valo 	u16 old_rf_ovr, old_rf_ovrval, old_afe_ovr, old_afe_ovrval,
119958619b14SKalle Valo 	    old_rf2_ovr, old_rf2_ovrval, old_phy_ctl;
120058619b14SKalle Valo 	enum b43_lpphy_txpctl_mode old_txpctl;
120158619b14SKalle Valo 	u32 normal_pwr, ideal_pwr, mean_sq_pwr, tmp = 0, mean_sq_pwr_min = 0;
120258619b14SKalle Valo 	int loopback, i, j, inner_sum, err;
120358619b14SKalle Valo 
120458619b14SKalle Valo 	memset(&iq_est, 0, sizeof(iq_est));
120558619b14SKalle Valo 
120658619b14SKalle Valo 	err = b43_lpphy_op_switch_channel(dev, 7);
120758619b14SKalle Valo 	if (err) {
120858619b14SKalle Valo 		b43dbg(dev->wl,
120958619b14SKalle Valo 		       "RC calib: Failed to switch to channel 7, error = %d\n",
121058619b14SKalle Valo 		       err);
121158619b14SKalle Valo 	}
121258619b14SKalle Valo 	old_txg_ovr = !!(b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40);
121358619b14SKalle Valo 	old_bbmult = lpphy_get_bb_mult(dev);
121458619b14SKalle Valo 	if (old_txg_ovr)
121558619b14SKalle Valo 		tx_gains = lpphy_get_tx_gains(dev);
121658619b14SKalle Valo 	old_rf_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_0);
121758619b14SKalle Valo 	old_rf_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_VAL_0);
121858619b14SKalle Valo 	old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR);
121958619b14SKalle Valo 	old_afe_ovrval = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVRVAL);
122058619b14SKalle Valo 	old_rf2_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2);
122158619b14SKalle Valo 	old_rf2_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2_VAL);
122258619b14SKalle Valo 	old_phy_ctl = b43_phy_read(dev, B43_LPPHY_LP_PHY_CTL);
122358619b14SKalle Valo 	lpphy_read_tx_pctl_mode_from_hardware(dev);
122458619b14SKalle Valo 	old_txpctl = lpphy->txpctl_mode;
122558619b14SKalle Valo 
122658619b14SKalle Valo 	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
122758619b14SKalle Valo 	lpphy_disable_crs(dev, true);
122858619b14SKalle Valo 	loopback = lpphy_loopback(dev);
122958619b14SKalle Valo 	if (loopback == -1)
123058619b14SKalle Valo 		goto finish;
123158619b14SKalle Valo 	lpphy_set_rx_gain_by_index(dev, loopback);
123258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFFBF, 0x40);
123358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFF8, 0x1);
123458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFC7, 0x8);
123558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F, 0xC0);
123658619b14SKalle Valo 	for (i = 128; i <= 159; i++) {
123758619b14SKalle Valo 		b43_radio_write(dev, B2062_N_RXBB_CALIB2, i);
123858619b14SKalle Valo 		inner_sum = 0;
123958619b14SKalle Valo 		for (j = 5; j <= 25; j++) {
124058619b14SKalle Valo 			lpphy_run_ddfs(dev, 1, 1, j, j, 0);
124158619b14SKalle Valo 			if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est)))
124258619b14SKalle Valo 				goto finish;
124358619b14SKalle Valo 			mean_sq_pwr = iq_est.i_pwr + iq_est.q_pwr;
124458619b14SKalle Valo 			if (j == 5)
124558619b14SKalle Valo 				tmp = mean_sq_pwr;
124658619b14SKalle Valo 			ideal_pwr = ((ideal_pwr_table[j-5] >> 3) + 1) >> 1;
124758619b14SKalle Valo 			normal_pwr = lpphy_qdiv_roundup(mean_sq_pwr, tmp, 12);
124858619b14SKalle Valo 			mean_sq_pwr = ideal_pwr - normal_pwr;
124958619b14SKalle Valo 			mean_sq_pwr *= mean_sq_pwr;
125058619b14SKalle Valo 			inner_sum += mean_sq_pwr;
125158619b14SKalle Valo 			if ((i == 128) || (inner_sum < mean_sq_pwr_min)) {
125258619b14SKalle Valo 				lpphy->rc_cap = i;
125358619b14SKalle Valo 				mean_sq_pwr_min = inner_sum;
125458619b14SKalle Valo 			}
125558619b14SKalle Valo 		}
125658619b14SKalle Valo 	}
125758619b14SKalle Valo 	lpphy_stop_ddfs(dev);
125858619b14SKalle Valo 
125958619b14SKalle Valo finish:
126058619b14SKalle Valo 	lpphy_restore_crs(dev, true);
126158619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, old_rf_ovrval);
126258619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, old_rf_ovr);
126358619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, old_afe_ovrval);
126458619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, old_afe_ovr);
126558619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, old_rf2_ovrval);
126658619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, old_rf2_ovr);
126758619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_LP_PHY_CTL, old_phy_ctl);
126858619b14SKalle Valo 
126958619b14SKalle Valo 	lpphy_set_bb_mult(dev, old_bbmult);
127058619b14SKalle Valo 	if (old_txg_ovr) {
127158619b14SKalle Valo 		/*
127258619b14SKalle Valo 		 * SPEC FIXME: The specs say "get_tx_gains" here, which is
127358619b14SKalle Valo 		 * illogical. According to lwfinger, vendor driver v4.150.10.5
127458619b14SKalle Valo 		 * has a Set here, while v4.174.64.19 has a Get - regression in
127558619b14SKalle Valo 		 * the vendor driver? This should be tested this once the code
127658619b14SKalle Valo 		 * is testable.
127758619b14SKalle Valo 		 */
127858619b14SKalle Valo 		lpphy_set_tx_gains(dev, tx_gains);
127958619b14SKalle Valo 	}
128058619b14SKalle Valo 	lpphy_set_tx_power_control(dev, old_txpctl);
128158619b14SKalle Valo 	if (lpphy->rc_cap)
128258619b14SKalle Valo 		lpphy_set_rc_cap(dev);
128358619b14SKalle Valo }
128458619b14SKalle Valo 
128558619b14SKalle Valo static void lpphy_rev2plus_rc_calib(struct b43_wldev *dev)
128658619b14SKalle Valo {
128758619b14SKalle Valo 	struct ssb_bus *bus = dev->dev->sdev->bus;
128858619b14SKalle Valo 	u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
128958619b14SKalle Valo 	u8 tmp = b43_radio_read(dev, B2063_RX_BB_SP8) & 0xFF;
129058619b14SKalle Valo 	int i;
129158619b14SKalle Valo 
129258619b14SKalle Valo 	b43_radio_write(dev, B2063_RX_BB_SP8, 0x0);
129358619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E);
129458619b14SKalle Valo 	b43_radio_mask(dev, B2063_PLL_SP1, 0xF7);
129558619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C);
129658619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x15);
129758619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x70);
129858619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x52);
129958619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1);
130058619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7D);
130158619b14SKalle Valo 
130258619b14SKalle Valo 	for (i = 0; i < 10000; i++) {
130358619b14SKalle Valo 		if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)
130458619b14SKalle Valo 			break;
130558619b14SKalle Valo 		msleep(1);
130658619b14SKalle Valo 	}
130758619b14SKalle Valo 
130858619b14SKalle Valo 	if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2))
130958619b14SKalle Valo 		b43_radio_write(dev, B2063_RX_BB_SP8, tmp);
131058619b14SKalle Valo 
131158619b14SKalle Valo 	tmp = b43_radio_read(dev, B2063_TX_BB_SP3) & 0xFF;
131258619b14SKalle Valo 
131358619b14SKalle Valo 	b43_radio_write(dev, B2063_TX_BB_SP3, 0x0);
131458619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E);
131558619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C);
131658619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x55);
131758619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x76);
131858619b14SKalle Valo 
131958619b14SKalle Valo 	if (crystal_freq == 24000000) {
132058619b14SKalle Valo 		b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0xFC);
132158619b14SKalle Valo 		b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x0);
132258619b14SKalle Valo 	} else {
132358619b14SKalle Valo 		b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x13);
132458619b14SKalle Valo 		b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1);
132558619b14SKalle Valo 	}
132658619b14SKalle Valo 
132758619b14SKalle Valo 	b43_radio_write(dev, B2063_PA_SP7, 0x7D);
132858619b14SKalle Valo 
132958619b14SKalle Valo 	for (i = 0; i < 10000; i++) {
133058619b14SKalle Valo 		if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)
133158619b14SKalle Valo 			break;
133258619b14SKalle Valo 		msleep(1);
133358619b14SKalle Valo 	}
133458619b14SKalle Valo 
133558619b14SKalle Valo 	if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2))
133658619b14SKalle Valo 		b43_radio_write(dev, B2063_TX_BB_SP3, tmp);
133758619b14SKalle Valo 
133858619b14SKalle Valo 	b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E);
133958619b14SKalle Valo }
134058619b14SKalle Valo 
134158619b14SKalle Valo static void lpphy_calibrate_rc(struct b43_wldev *dev)
134258619b14SKalle Valo {
134358619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
134458619b14SKalle Valo 
134558619b14SKalle Valo 	if (dev->phy.rev >= 2) {
134658619b14SKalle Valo 		lpphy_rev2plus_rc_calib(dev);
134758619b14SKalle Valo 	} else if (!lpphy->rc_cap) {
134857fbcce3SJohannes Berg 		if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ)
134958619b14SKalle Valo 			lpphy_rev0_1_rc_calib(dev);
135058619b14SKalle Valo 	} else {
135158619b14SKalle Valo 		lpphy_set_rc_cap(dev);
135258619b14SKalle Valo 	}
135358619b14SKalle Valo }
135458619b14SKalle Valo 
135558619b14SKalle Valo static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna)
135658619b14SKalle Valo {
135758619b14SKalle Valo 	if (dev->phy.rev >= 2)
135858619b14SKalle Valo 		return; // rev2+ doesn't support antenna diversity
135958619b14SKalle Valo 
136058619b14SKalle Valo 	if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1))
136158619b14SKalle Valo 		return;
136258619b14SKalle Valo 
136358619b14SKalle Valo 	b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_ANTDIVHELP);
136458619b14SKalle Valo 
136558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2);
136658619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1);
136758619b14SKalle Valo 
136858619b14SKalle Valo 	b43_hf_write(dev, b43_hf_read(dev) | B43_HF_ANTDIVHELP);
136958619b14SKalle Valo 
137058619b14SKalle Valo 	dev->phy.lp->antenna = antenna;
137158619b14SKalle Valo }
137258619b14SKalle Valo 
137358619b14SKalle Valo static void lpphy_set_tx_iqcc(struct b43_wldev *dev, u16 a, u16 b)
137458619b14SKalle Valo {
137558619b14SKalle Valo 	u16 tmp[2];
137658619b14SKalle Valo 
137758619b14SKalle Valo 	tmp[0] = a;
137858619b14SKalle Valo 	tmp[1] = b;
137958619b14SKalle Valo 	b43_lptab_write_bulk(dev, B43_LPTAB16(0, 80), 2, tmp);
138058619b14SKalle Valo }
138158619b14SKalle Valo 
138258619b14SKalle Valo static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index)
138358619b14SKalle Valo {
138458619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
138558619b14SKalle Valo 	struct lpphy_tx_gains gains;
138658619b14SKalle Valo 	u32 iq_comp, tx_gain, coeff, rf_power;
138758619b14SKalle Valo 
138858619b14SKalle Valo 	lpphy->tx_pwr_idx_over = index;
138958619b14SKalle Valo 	lpphy_read_tx_pctl_mode_from_hardware(dev);
139058619b14SKalle Valo 	if (lpphy->txpctl_mode != B43_LPPHY_TXPCTL_OFF)
139158619b14SKalle Valo 		lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_SW);
139258619b14SKalle Valo 	if (dev->phy.rev >= 2) {
139358619b14SKalle Valo 		iq_comp = b43_lptab_read(dev, B43_LPTAB32(7, index + 320));
139458619b14SKalle Valo 		tx_gain = b43_lptab_read(dev, B43_LPTAB32(7, index + 192));
139558619b14SKalle Valo 		gains.pad = (tx_gain >> 16) & 0xFF;
139658619b14SKalle Valo 		gains.gm = tx_gain & 0xFF;
139758619b14SKalle Valo 		gains.pga = (tx_gain >> 8) & 0xFF;
139858619b14SKalle Valo 		gains.dac = (iq_comp >> 28) & 0xFF;
139958619b14SKalle Valo 		lpphy_set_tx_gains(dev, gains);
140058619b14SKalle Valo 	} else {
140158619b14SKalle Valo 		iq_comp = b43_lptab_read(dev, B43_LPTAB32(10, index + 320));
140258619b14SKalle Valo 		tx_gain = b43_lptab_read(dev, B43_LPTAB32(10, index + 192));
140358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL,
140458619b14SKalle Valo 				0xF800, (tx_gain >> 4) & 0x7FFF);
140558619b14SKalle Valo 		lpphy_set_dac_gain(dev, tx_gain & 0x7);
140658619b14SKalle Valo 		lpphy_set_pa_gain(dev, (tx_gain >> 24) & 0x7F);
140758619b14SKalle Valo 	}
140858619b14SKalle Valo 	lpphy_set_bb_mult(dev, (iq_comp >> 20) & 0xFF);
140958619b14SKalle Valo 	lpphy_set_tx_iqcc(dev, (iq_comp >> 10) & 0x3FF, iq_comp & 0x3FF);
141058619b14SKalle Valo 	if (dev->phy.rev >= 2) {
141158619b14SKalle Valo 		coeff = b43_lptab_read(dev, B43_LPTAB32(7, index + 448));
141258619b14SKalle Valo 	} else {
141358619b14SKalle Valo 		coeff = b43_lptab_read(dev, B43_LPTAB32(10, index + 448));
141458619b14SKalle Valo 	}
141558619b14SKalle Valo 	b43_lptab_write(dev, B43_LPTAB16(0, 85), coeff & 0xFFFF);
141658619b14SKalle Valo 	if (dev->phy.rev >= 2) {
141758619b14SKalle Valo 		rf_power = b43_lptab_read(dev, B43_LPTAB32(7, index + 576));
141858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00,
141958619b14SKalle Valo 				rf_power & 0xFFFF);//SPEC FIXME mask & set != 0
142058619b14SKalle Valo 	}
142158619b14SKalle Valo 	lpphy_enable_tx_gain_override(dev);
142258619b14SKalle Valo }
142358619b14SKalle Valo 
142458619b14SKalle Valo static void lpphy_btcoex_override(struct b43_wldev *dev)
142558619b14SKalle Valo {
142658619b14SKalle Valo 	b43_write16(dev, B43_MMIO_BTCOEX_CTL, 0x3);
142758619b14SKalle Valo 	b43_write16(dev, B43_MMIO_BTCOEX_TXCTL, 0xFF);
142858619b14SKalle Valo }
142958619b14SKalle Valo 
143058619b14SKalle Valo static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev,
143158619b14SKalle Valo 					 bool blocked)
143258619b14SKalle Valo {
143358619b14SKalle Valo 	//TODO check MAC control register
143458619b14SKalle Valo 	if (blocked) {
143558619b14SKalle Valo 		if (dev->phy.rev >= 2) {
143658619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x83FF);
143758619b14SKalle Valo 			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
143858619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0x80FF);
143958619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xDFFF);
144058619b14SKalle Valo 			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0808);
144158619b14SKalle Valo 		} else {
144258619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xE0FF);
144358619b14SKalle Valo 			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1F00);
144458619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFCFF);
144558619b14SKalle Valo 			b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x0018);
144658619b14SKalle Valo 		}
144758619b14SKalle Valo 	} else {
144858619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xE0FF);
144958619b14SKalle Valo 		if (dev->phy.rev >= 2)
145058619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xF7F7);
145158619b14SKalle Valo 		else
145258619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFFE7);
145358619b14SKalle Valo 	}
145458619b14SKalle Valo }
145558619b14SKalle Valo 
145658619b14SKalle Valo /* This was previously called lpphy_japan_filter */
145758619b14SKalle Valo static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel)
145858619b14SKalle Valo {
145958619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
146058619b14SKalle Valo 	u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter!
146158619b14SKalle Valo 
146258619b14SKalle Valo 	if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific?
146358619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9);
146458619b14SKalle Valo 		if ((dev->phy.rev == 1) && (lpphy->rc_cap))
146558619b14SKalle Valo 			lpphy_set_rc_cap(dev);
146658619b14SKalle Valo 	} else {
146758619b14SKalle Valo 		b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F);
146858619b14SKalle Valo 	}
146958619b14SKalle Valo }
147058619b14SKalle Valo 
147158619b14SKalle Valo static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode)
147258619b14SKalle Valo {
147358619b14SKalle Valo 	if (mode != TSSI_MUX_EXT) {
147458619b14SKalle Valo 		b43_radio_set(dev, B2063_PA_SP1, 0x2);
147558619b14SKalle Valo 		b43_phy_set(dev, B43_PHY_OFDM(0xF3), 0x1000);
147658619b14SKalle Valo 		b43_radio_write(dev, B2063_PA_CTL10, 0x51);
147758619b14SKalle Valo 		if (mode == TSSI_MUX_POSTPA) {
147858619b14SKalle Valo 			b43_radio_mask(dev, B2063_PA_SP1, 0xFFFE);
147958619b14SKalle Valo 			b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFC7);
148058619b14SKalle Valo 		} else {
148158619b14SKalle Valo 			b43_radio_maskset(dev, B2063_PA_SP1, 0xFFFE, 0x1);
148258619b14SKalle Valo 			b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVRVAL,
148358619b14SKalle Valo 					0xFFC7, 0x20);
148458619b14SKalle Valo 		}
148558619b14SKalle Valo 	} else {
148658619b14SKalle Valo 		B43_WARN_ON(1);
148758619b14SKalle Valo 	}
148858619b14SKalle Valo }
148958619b14SKalle Valo 
149058619b14SKalle Valo static void lpphy_tx_pctl_init_hw(struct b43_wldev *dev)
149158619b14SKalle Valo {
149258619b14SKalle Valo 	u16 tmp;
149358619b14SKalle Valo 	int i;
149458619b14SKalle Valo 
149558619b14SKalle Valo 	//SPEC TODO Call LP PHY Clear TX Power offsets
149658619b14SKalle Valo 	for (i = 0; i < 64; i++) {
149758619b14SKalle Valo 		if (dev->phy.rev >= 2)
149858619b14SKalle Valo 			b43_lptab_write(dev, B43_LPTAB32(7, i + 1), i);
149958619b14SKalle Valo 		else
150058619b14SKalle Valo 			b43_lptab_write(dev, B43_LPTAB32(10, i + 1), i);
150158619b14SKalle Valo 	}
150258619b14SKalle Valo 
150358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xFF00, 0xFF);
150458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, 0x5000);
150558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0xFFC0, 0x1F);
150658619b14SKalle Valo 	if (dev->phy.rev < 2) {
150758619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xEFFF);
150858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xDFFF, 0x2000);
150958619b14SKalle Valo 	} else {
151058619b14SKalle Valo 		b43_phy_mask(dev, B43_PHY_OFDM(0x103), 0xFFFE);
151158619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFFB, 0x4);
151258619b14SKalle Valo 		b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFEF, 0x10);
151358619b14SKalle Valo 		b43_radio_maskset(dev, B2063_IQ_CALIB_CTL2, 0xF3, 0x1);
151458619b14SKalle Valo 		lpphy_set_tssi_mux(dev, TSSI_MUX_POSTPA);
151558619b14SKalle Valo 	}
151658619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0x7FFF, 0x8000);
151758619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xFF);
151858619b14SKalle Valo 	b43_phy_write(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xA);
151958619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
152058619b14SKalle Valo 			~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF,
152158619b14SKalle Valo 			B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF);
152258619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xF8FF);
152358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD,
152458619b14SKalle Valo 			~B43_LPPHY_TX_PWR_CTL_CMD_MODE & 0xFFFF,
152558619b14SKalle Valo 			B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW);
152658619b14SKalle Valo 
152758619b14SKalle Valo 	if (dev->phy.rev < 2) {
152858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF, 0x1000);
152958619b14SKalle Valo 		b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xEFFF);
153058619b14SKalle Valo 	} else {
153158619b14SKalle Valo 		lpphy_set_tx_power_by_index(dev, 0x7F);
153258619b14SKalle Valo 	}
153358619b14SKalle Valo 
153458619b14SKalle Valo 	b43_dummy_transmission(dev, true, true);
153558619b14SKalle Valo 
153658619b14SKalle Valo 	tmp = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_STAT);
153758619b14SKalle Valo 	if (tmp & 0x8000) {
153858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI,
153958619b14SKalle Valo 				0xFFC0, (tmp & 0xFF) - 32);
154058619b14SKalle Valo 	}
154158619b14SKalle Valo 
154258619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF);
154358619b14SKalle Valo 
154458619b14SKalle Valo 	// (SPEC?) TODO Set "Target TX frequency" variable to 0
154558619b14SKalle Valo 	// SPEC FIXME "Set BB Multiplier to 0xE000" impossible - bb_mult is u8!
154658619b14SKalle Valo }
154758619b14SKalle Valo 
154858619b14SKalle Valo static void lpphy_tx_pctl_init_sw(struct b43_wldev *dev)
154958619b14SKalle Valo {
155058619b14SKalle Valo 	struct lpphy_tx_gains gains;
155158619b14SKalle Valo 
155257fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
155358619b14SKalle Valo 		gains.gm = 4;
155458619b14SKalle Valo 		gains.pad = 12;
155558619b14SKalle Valo 		gains.pga = 12;
155658619b14SKalle Valo 		gains.dac = 0;
155758619b14SKalle Valo 	} else {
155858619b14SKalle Valo 		gains.gm = 7;
155958619b14SKalle Valo 		gains.pad = 14;
156058619b14SKalle Valo 		gains.pga = 15;
156158619b14SKalle Valo 		gains.dac = 0;
156258619b14SKalle Valo 	}
156358619b14SKalle Valo 	lpphy_set_tx_gains(dev, gains);
156458619b14SKalle Valo 	lpphy_set_bb_mult(dev, 150);
156558619b14SKalle Valo }
156658619b14SKalle Valo 
156758619b14SKalle Valo /* Initialize TX power control */
156858619b14SKalle Valo static void lpphy_tx_pctl_init(struct b43_wldev *dev)
156958619b14SKalle Valo {
157058619b14SKalle Valo 	if (0/*FIXME HWPCTL capable */) {
157158619b14SKalle Valo 		lpphy_tx_pctl_init_hw(dev);
157258619b14SKalle Valo 	} else { /* This device is only software TX power control capable. */
157358619b14SKalle Valo 		lpphy_tx_pctl_init_sw(dev);
157458619b14SKalle Valo 	}
157558619b14SKalle Valo }
157658619b14SKalle Valo 
157758619b14SKalle Valo static void lpphy_pr41573_workaround(struct b43_wldev *dev)
157858619b14SKalle Valo {
157958619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
158058619b14SKalle Valo 	u32 *saved_tab;
158158619b14SKalle Valo 	const unsigned int saved_tab_size = 256;
158258619b14SKalle Valo 	enum b43_lpphy_txpctl_mode txpctl_mode;
158358619b14SKalle Valo 	s8 tx_pwr_idx_over;
158458619b14SKalle Valo 	u16 tssi_npt, tssi_idx;
158558619b14SKalle Valo 
158658619b14SKalle Valo 	saved_tab = kcalloc(saved_tab_size, sizeof(saved_tab[0]), GFP_KERNEL);
158758619b14SKalle Valo 	if (!saved_tab) {
158858619b14SKalle Valo 		b43err(dev->wl, "PR41573 failed. Out of memory!\n");
158958619b14SKalle Valo 		return;
159058619b14SKalle Valo 	}
159158619b14SKalle Valo 
159258619b14SKalle Valo 	lpphy_read_tx_pctl_mode_from_hardware(dev);
159358619b14SKalle Valo 	txpctl_mode = lpphy->txpctl_mode;
159458619b14SKalle Valo 	tx_pwr_idx_over = lpphy->tx_pwr_idx_over;
159558619b14SKalle Valo 	tssi_npt = lpphy->tssi_npt;
159658619b14SKalle Valo 	tssi_idx = lpphy->tssi_idx;
159758619b14SKalle Valo 
159858619b14SKalle Valo 	if (dev->phy.rev < 2) {
159958619b14SKalle Valo 		b43_lptab_read_bulk(dev, B43_LPTAB32(10, 0x140),
160058619b14SKalle Valo 				    saved_tab_size, saved_tab);
160158619b14SKalle Valo 	} else {
160258619b14SKalle Valo 		b43_lptab_read_bulk(dev, B43_LPTAB32(7, 0x140),
160358619b14SKalle Valo 				    saved_tab_size, saved_tab);
160458619b14SKalle Valo 	}
160558619b14SKalle Valo 	//FIXME PHY reset
160658619b14SKalle Valo 	lpphy_table_init(dev); //FIXME is table init needed?
160758619b14SKalle Valo 	lpphy_baseband_init(dev);
160858619b14SKalle Valo 	lpphy_tx_pctl_init(dev);
160958619b14SKalle Valo 	b43_lpphy_op_software_rfkill(dev, false);
161058619b14SKalle Valo 	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
161158619b14SKalle Valo 	if (dev->phy.rev < 2) {
161258619b14SKalle Valo 		b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0x140),
161358619b14SKalle Valo 				     saved_tab_size, saved_tab);
161458619b14SKalle Valo 	} else {
161558619b14SKalle Valo 		b43_lptab_write_bulk(dev, B43_LPTAB32(7, 0x140),
161658619b14SKalle Valo 				     saved_tab_size, saved_tab);
161758619b14SKalle Valo 	}
161858619b14SKalle Valo 	b43_write16(dev, B43_MMIO_CHANNEL, lpphy->channel);
161958619b14SKalle Valo 	lpphy->tssi_npt = tssi_npt;
162058619b14SKalle Valo 	lpphy->tssi_idx = tssi_idx;
162158619b14SKalle Valo 	lpphy_set_analog_filter(dev, lpphy->channel);
162258619b14SKalle Valo 	if (tx_pwr_idx_over != -1)
162358619b14SKalle Valo 		lpphy_set_tx_power_by_index(dev, tx_pwr_idx_over);
162458619b14SKalle Valo 	if (lpphy->rc_cap)
162558619b14SKalle Valo 		lpphy_set_rc_cap(dev);
162658619b14SKalle Valo 	b43_lpphy_op_set_rx_antenna(dev, lpphy->antenna);
162758619b14SKalle Valo 	lpphy_set_tx_power_control(dev, txpctl_mode);
162858619b14SKalle Valo 	kfree(saved_tab);
162958619b14SKalle Valo }
163058619b14SKalle Valo 
163158619b14SKalle Valo struct lpphy_rx_iq_comp { u8 chan; s8 c1, c0; };
163258619b14SKalle Valo 
163358619b14SKalle Valo static const struct lpphy_rx_iq_comp lpphy_5354_iq_table[] = {
163458619b14SKalle Valo 	{ .chan = 1, .c1 = -66, .c0 = 15, },
163558619b14SKalle Valo 	{ .chan = 2, .c1 = -66, .c0 = 15, },
163658619b14SKalle Valo 	{ .chan = 3, .c1 = -66, .c0 = 15, },
163758619b14SKalle Valo 	{ .chan = 4, .c1 = -66, .c0 = 15, },
163858619b14SKalle Valo 	{ .chan = 5, .c1 = -66, .c0 = 15, },
163958619b14SKalle Valo 	{ .chan = 6, .c1 = -66, .c0 = 15, },
164058619b14SKalle Valo 	{ .chan = 7, .c1 = -66, .c0 = 14, },
164158619b14SKalle Valo 	{ .chan = 8, .c1 = -66, .c0 = 14, },
164258619b14SKalle Valo 	{ .chan = 9, .c1 = -66, .c0 = 14, },
164358619b14SKalle Valo 	{ .chan = 10, .c1 = -66, .c0 = 14, },
164458619b14SKalle Valo 	{ .chan = 11, .c1 = -66, .c0 = 14, },
164558619b14SKalle Valo 	{ .chan = 12, .c1 = -66, .c0 = 13, },
164658619b14SKalle Valo 	{ .chan = 13, .c1 = -66, .c0 = 13, },
164758619b14SKalle Valo 	{ .chan = 14, .c1 = -66, .c0 = 13, },
164858619b14SKalle Valo };
164958619b14SKalle Valo 
165058619b14SKalle Valo static const struct lpphy_rx_iq_comp lpphy_rev0_1_iq_table[] = {
165158619b14SKalle Valo 	{ .chan = 1, .c1 = -64, .c0 = 13, },
165258619b14SKalle Valo 	{ .chan = 2, .c1 = -64, .c0 = 13, },
165358619b14SKalle Valo 	{ .chan = 3, .c1 = -64, .c0 = 13, },
165458619b14SKalle Valo 	{ .chan = 4, .c1 = -64, .c0 = 13, },
165558619b14SKalle Valo 	{ .chan = 5, .c1 = -64, .c0 = 12, },
165658619b14SKalle Valo 	{ .chan = 6, .c1 = -64, .c0 = 12, },
165758619b14SKalle Valo 	{ .chan = 7, .c1 = -64, .c0 = 12, },
165858619b14SKalle Valo 	{ .chan = 8, .c1 = -64, .c0 = 12, },
165958619b14SKalle Valo 	{ .chan = 9, .c1 = -64, .c0 = 12, },
166058619b14SKalle Valo 	{ .chan = 10, .c1 = -64, .c0 = 11, },
166158619b14SKalle Valo 	{ .chan = 11, .c1 = -64, .c0 = 11, },
166258619b14SKalle Valo 	{ .chan = 12, .c1 = -64, .c0 = 11, },
166358619b14SKalle Valo 	{ .chan = 13, .c1 = -64, .c0 = 11, },
166458619b14SKalle Valo 	{ .chan = 14, .c1 = -64, .c0 = 10, },
166558619b14SKalle Valo 	{ .chan = 34, .c1 = -62, .c0 = 24, },
166658619b14SKalle Valo 	{ .chan = 38, .c1 = -62, .c0 = 24, },
166758619b14SKalle Valo 	{ .chan = 42, .c1 = -62, .c0 = 24, },
166858619b14SKalle Valo 	{ .chan = 46, .c1 = -62, .c0 = 23, },
166958619b14SKalle Valo 	{ .chan = 36, .c1 = -62, .c0 = 24, },
167058619b14SKalle Valo 	{ .chan = 40, .c1 = -62, .c0 = 24, },
167158619b14SKalle Valo 	{ .chan = 44, .c1 = -62, .c0 = 23, },
167258619b14SKalle Valo 	{ .chan = 48, .c1 = -62, .c0 = 23, },
167358619b14SKalle Valo 	{ .chan = 52, .c1 = -62, .c0 = 23, },
167458619b14SKalle Valo 	{ .chan = 56, .c1 = -62, .c0 = 22, },
167558619b14SKalle Valo 	{ .chan = 60, .c1 = -62, .c0 = 22, },
167658619b14SKalle Valo 	{ .chan = 64, .c1 = -62, .c0 = 22, },
167758619b14SKalle Valo 	{ .chan = 100, .c1 = -62, .c0 = 16, },
167858619b14SKalle Valo 	{ .chan = 104, .c1 = -62, .c0 = 16, },
167958619b14SKalle Valo 	{ .chan = 108, .c1 = -62, .c0 = 15, },
168058619b14SKalle Valo 	{ .chan = 112, .c1 = -62, .c0 = 14, },
168158619b14SKalle Valo 	{ .chan = 116, .c1 = -62, .c0 = 14, },
168258619b14SKalle Valo 	{ .chan = 120, .c1 = -62, .c0 = 13, },
168358619b14SKalle Valo 	{ .chan = 124, .c1 = -62, .c0 = 12, },
168458619b14SKalle Valo 	{ .chan = 128, .c1 = -62, .c0 = 12, },
168558619b14SKalle Valo 	{ .chan = 132, .c1 = -62, .c0 = 12, },
168658619b14SKalle Valo 	{ .chan = 136, .c1 = -62, .c0 = 11, },
168758619b14SKalle Valo 	{ .chan = 140, .c1 = -62, .c0 = 10, },
168858619b14SKalle Valo 	{ .chan = 149, .c1 = -61, .c0 = 9, },
168958619b14SKalle Valo 	{ .chan = 153, .c1 = -61, .c0 = 9, },
169058619b14SKalle Valo 	{ .chan = 157, .c1 = -61, .c0 = 9, },
169158619b14SKalle Valo 	{ .chan = 161, .c1 = -61, .c0 = 8, },
169258619b14SKalle Valo 	{ .chan = 165, .c1 = -61, .c0 = 8, },
169358619b14SKalle Valo 	{ .chan = 184, .c1 = -62, .c0 = 25, },
169458619b14SKalle Valo 	{ .chan = 188, .c1 = -62, .c0 = 25, },
169558619b14SKalle Valo 	{ .chan = 192, .c1 = -62, .c0 = 25, },
169658619b14SKalle Valo 	{ .chan = 196, .c1 = -62, .c0 = 25, },
169758619b14SKalle Valo 	{ .chan = 200, .c1 = -62, .c0 = 25, },
169858619b14SKalle Valo 	{ .chan = 204, .c1 = -62, .c0 = 25, },
169958619b14SKalle Valo 	{ .chan = 208, .c1 = -62, .c0 = 25, },
170058619b14SKalle Valo 	{ .chan = 212, .c1 = -62, .c0 = 25, },
170158619b14SKalle Valo 	{ .chan = 216, .c1 = -62, .c0 = 26, },
170258619b14SKalle Valo };
170358619b14SKalle Valo 
170458619b14SKalle Valo static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = {
170558619b14SKalle Valo 	.chan = 0,
170658619b14SKalle Valo 	.c1 = -64,
170758619b14SKalle Valo 	.c0 = 0,
170858619b14SKalle Valo };
170958619b14SKalle Valo 
171058619b14SKalle Valo static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples)
171158619b14SKalle Valo {
171258619b14SKalle Valo 	struct lpphy_iq_est iq_est;
171358619b14SKalle Valo 	u16 c0, c1;
171458619b14SKalle Valo 	int prod, ipwr, qpwr, prod_msb, q_msb, tmp1, tmp2, tmp3, tmp4, ret;
171558619b14SKalle Valo 
171658619b14SKalle Valo 	c1 = b43_phy_read(dev, B43_LPPHY_RX_COMP_COEFF_S);
171758619b14SKalle Valo 	c0 = c1 >> 8;
171858619b14SKalle Valo 	c1 |= 0xFF;
171958619b14SKalle Valo 
172058619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, 0x00C0);
172158619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF);
172258619b14SKalle Valo 
172358619b14SKalle Valo 	ret = lpphy_rx_iq_est(dev, samples, 32, &iq_est);
172458619b14SKalle Valo 	if (!ret)
172558619b14SKalle Valo 		goto out;
172658619b14SKalle Valo 
172758619b14SKalle Valo 	prod = iq_est.iq_prod;
172858619b14SKalle Valo 	ipwr = iq_est.i_pwr;
172958619b14SKalle Valo 	qpwr = iq_est.q_pwr;
173058619b14SKalle Valo 
173158619b14SKalle Valo 	if (ipwr + qpwr < 2) {
173258619b14SKalle Valo 		ret = 0;
173358619b14SKalle Valo 		goto out;
173458619b14SKalle Valo 	}
173558619b14SKalle Valo 
173658619b14SKalle Valo 	prod_msb = fls(abs(prod));
173758619b14SKalle Valo 	q_msb = fls(abs(qpwr));
173858619b14SKalle Valo 	tmp1 = prod_msb - 20;
173958619b14SKalle Valo 
174058619b14SKalle Valo 	if (tmp1 >= 0) {
174158619b14SKalle Valo 		tmp3 = ((prod << (30 - prod_msb)) + (ipwr >> (1 + tmp1))) /
174258619b14SKalle Valo 			(ipwr >> tmp1);
174358619b14SKalle Valo 	} else {
174458619b14SKalle Valo 		tmp3 = ((prod << (30 - prod_msb)) + (ipwr << (-1 - tmp1))) /
174558619b14SKalle Valo 			(ipwr << -tmp1);
174658619b14SKalle Valo 	}
174758619b14SKalle Valo 
174858619b14SKalle Valo 	tmp2 = q_msb - 11;
174958619b14SKalle Valo 
175058619b14SKalle Valo 	if (tmp2 >= 0)
175158619b14SKalle Valo 		tmp4 = (qpwr << (31 - q_msb)) / (ipwr >> tmp2);
175258619b14SKalle Valo 	else
175358619b14SKalle Valo 		tmp4 = (qpwr << (31 - q_msb)) / (ipwr << -tmp2);
175458619b14SKalle Valo 
175558619b14SKalle Valo 	tmp4 -= tmp3 * tmp3;
175658619b14SKalle Valo 	tmp4 = -int_sqrt(tmp4);
175758619b14SKalle Valo 
175858619b14SKalle Valo 	c0 = tmp3 >> 3;
175958619b14SKalle Valo 	c1 = tmp4 >> 4;
176058619b14SKalle Valo 
176158619b14SKalle Valo out:
176258619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, c1);
176358619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0x00FF, c0 << 8);
176458619b14SKalle Valo 	return ret;
176558619b14SKalle Valo }
176658619b14SKalle Valo 
176758619b14SKalle Valo static void lpphy_run_samples(struct b43_wldev *dev, u16 samples, u16 loops,
176858619b14SKalle Valo 			      u16 wait)
176958619b14SKalle Valo {
177058619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL,
177158619b14SKalle Valo 			0xFFC0, samples - 1);
177258619b14SKalle Valo 	if (loops != 0xFFFF)
177358619b14SKalle Valo 		loops--;
177458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000, loops);
177558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_SMPL_PLAY_BUFFER_CTL, 0x3F, wait << 6);
177658619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_A_PHY_CTL_ADDR, 0x1);
177758619b14SKalle Valo }
177858619b14SKalle Valo 
177958619b14SKalle Valo //SPEC FIXME what does a negative freq mean?
178058619b14SKalle Valo static void lpphy_start_tx_tone(struct b43_wldev *dev, s32 freq, u16 max)
178158619b14SKalle Valo {
178258619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
178358619b14SKalle Valo 	u16 buf[64];
1784d5a43355SPriit Laes 	int i, samples = 0, theta = 0;
178558619b14SKalle Valo 	int rotation = (((36 * freq) / 20) << 16) / 100;
1786d5a43355SPriit Laes 	struct cordic_iq sample;
178758619b14SKalle Valo 
178858619b14SKalle Valo 	lpphy->tx_tone_freq = freq;
178958619b14SKalle Valo 
179058619b14SKalle Valo 	if (freq) {
179158619b14SKalle Valo 		/* Find i for which abs(freq) integrally divides 20000 * i */
179258619b14SKalle Valo 		for (i = 1; samples * abs(freq) != 20000 * i; i++) {
179358619b14SKalle Valo 			samples = (20000 * i) / abs(freq);
179458619b14SKalle Valo 			if(B43_WARN_ON(samples > 63))
179558619b14SKalle Valo 				return;
179658619b14SKalle Valo 		}
179758619b14SKalle Valo 	} else {
179858619b14SKalle Valo 		samples = 2;
179958619b14SKalle Valo 	}
180058619b14SKalle Valo 
180158619b14SKalle Valo 	for (i = 0; i < samples; i++) {
1802d5a43355SPriit Laes 		sample = cordic_calc_iq(CORDIC_FIXED(theta));
1803d5a43355SPriit Laes 		theta += rotation;
1804d5a43355SPriit Laes 		buf[i] = CORDIC_FLOAT((sample.i * max) & 0xFF) << 8;
1805d5a43355SPriit Laes 		buf[i] |= CORDIC_FLOAT((sample.q * max) & 0xFF);
180658619b14SKalle Valo 	}
180758619b14SKalle Valo 
180858619b14SKalle Valo 	b43_lptab_write_bulk(dev, B43_LPTAB16(5, 0), samples, buf);
180958619b14SKalle Valo 
181058619b14SKalle Valo 	lpphy_run_samples(dev, samples, 0xFFFF, 0);
181158619b14SKalle Valo }
181258619b14SKalle Valo 
181358619b14SKalle Valo static void lpphy_stop_tx_tone(struct b43_wldev *dev)
181458619b14SKalle Valo {
181558619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
181658619b14SKalle Valo 	int i;
181758619b14SKalle Valo 
181858619b14SKalle Valo 	lpphy->tx_tone_freq = 0;
181958619b14SKalle Valo 
182058619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_SMPL_PLAY_COUNT, 0xF000);
182158619b14SKalle Valo 	for (i = 0; i < 31; i++) {
182258619b14SKalle Valo 		if (!(b43_phy_read(dev, B43_LPPHY_A_PHY_CTL_ADDR) & 0x1))
182358619b14SKalle Valo 			break;
182458619b14SKalle Valo 		udelay(100);
182558619b14SKalle Valo 	}
182658619b14SKalle Valo }
182758619b14SKalle Valo 
182858619b14SKalle Valo 
182958619b14SKalle Valo static void lpphy_papd_cal(struct b43_wldev *dev, struct lpphy_tx_gains gains,
183058619b14SKalle Valo 			   int mode, bool useindex, u8 index)
183158619b14SKalle Valo {
183258619b14SKalle Valo 	//TODO
183358619b14SKalle Valo }
183458619b14SKalle Valo 
183558619b14SKalle Valo static void lpphy_papd_cal_txpwr(struct b43_wldev *dev)
183658619b14SKalle Valo {
183758619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
1838d825db34SArnd Bergmann 	struct lpphy_tx_gains oldgains;
183958619b14SKalle Valo 	int old_txpctl, old_afe_ovr, old_rf, old_bbmult;
184058619b14SKalle Valo 
184158619b14SKalle Valo 	lpphy_read_tx_pctl_mode_from_hardware(dev);
184258619b14SKalle Valo 	old_txpctl = lpphy->txpctl_mode;
184358619b14SKalle Valo 	old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;
184458619b14SKalle Valo 	if (old_afe_ovr)
184558619b14SKalle Valo 		oldgains = lpphy_get_tx_gains(dev);
184658619b14SKalle Valo 	old_rf = b43_phy_read(dev, B43_LPPHY_RF_PWR_OVERRIDE) & 0xFF;
184758619b14SKalle Valo 	old_bbmult = lpphy_get_bb_mult(dev);
184858619b14SKalle Valo 
184958619b14SKalle Valo 	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
185058619b14SKalle Valo 
185158619b14SKalle Valo 	if (dev->dev->chip_id == 0x4325 && dev->dev->chip_rev == 0)
1852d825db34SArnd Bergmann 		lpphy_papd_cal(dev, oldgains, 0, 1, 30);
185358619b14SKalle Valo 	else
1854d825db34SArnd Bergmann 		lpphy_papd_cal(dev, oldgains, 0, 1, 65);
185558619b14SKalle Valo 
185658619b14SKalle Valo 	if (old_afe_ovr)
185758619b14SKalle Valo 		lpphy_set_tx_gains(dev, oldgains);
185858619b14SKalle Valo 	lpphy_set_bb_mult(dev, old_bbmult);
185958619b14SKalle Valo 	lpphy_set_tx_power_control(dev, old_txpctl);
186058619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RF_PWR_OVERRIDE, 0xFF00, old_rf);
186158619b14SKalle Valo }
186258619b14SKalle Valo 
186358619b14SKalle Valo static int lpphy_rx_iq_cal(struct b43_wldev *dev, bool noise, bool tx,
186458619b14SKalle Valo 			    bool rx, bool pa, struct lpphy_tx_gains *gains)
186558619b14SKalle Valo {
186658619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
186758619b14SKalle Valo 	const struct lpphy_rx_iq_comp *iqcomp = NULL;
186858619b14SKalle Valo 	struct lpphy_tx_gains nogains, oldgains;
186958619b14SKalle Valo 	u16 tmp;
187058619b14SKalle Valo 	int i, ret;
187158619b14SKalle Valo 
187258619b14SKalle Valo 	memset(&nogains, 0, sizeof(nogains));
187358619b14SKalle Valo 	memset(&oldgains, 0, sizeof(oldgains));
187458619b14SKalle Valo 
187558619b14SKalle Valo 	if (dev->dev->chip_id == 0x5354) {
187658619b14SKalle Valo 		for (i = 0; i < ARRAY_SIZE(lpphy_5354_iq_table); i++) {
187758619b14SKalle Valo 			if (lpphy_5354_iq_table[i].chan == lpphy->channel) {
187858619b14SKalle Valo 				iqcomp = &lpphy_5354_iq_table[i];
187958619b14SKalle Valo 			}
188058619b14SKalle Valo 		}
188158619b14SKalle Valo 	} else if (dev->phy.rev >= 2) {
188258619b14SKalle Valo 		iqcomp = &lpphy_rev2plus_iq_comp;
188358619b14SKalle Valo 	} else {
188458619b14SKalle Valo 		for (i = 0; i < ARRAY_SIZE(lpphy_rev0_1_iq_table); i++) {
188558619b14SKalle Valo 			if (lpphy_rev0_1_iq_table[i].chan == lpphy->channel) {
188658619b14SKalle Valo 				iqcomp = &lpphy_rev0_1_iq_table[i];
188758619b14SKalle Valo 			}
188858619b14SKalle Valo 		}
188958619b14SKalle Valo 	}
189058619b14SKalle Valo 
189158619b14SKalle Valo 	if (B43_WARN_ON(!iqcomp))
189258619b14SKalle Valo 		return 0;
189358619b14SKalle Valo 
189458619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S, 0xFF00, iqcomp->c1);
189558619b14SKalle Valo 	b43_phy_maskset(dev, B43_LPPHY_RX_COMP_COEFF_S,
189658619b14SKalle Valo 			0x00FF, iqcomp->c0 << 8);
189758619b14SKalle Valo 
189858619b14SKalle Valo 	if (noise) {
189958619b14SKalle Valo 		tx = true;
190058619b14SKalle Valo 		rx = false;
190158619b14SKalle Valo 		pa = false;
190258619b14SKalle Valo 	}
190358619b14SKalle Valo 
190458619b14SKalle Valo 	lpphy_set_trsw_over(dev, tx, rx);
190558619b14SKalle Valo 
190657fbcce3SJohannes Berg 	if (b43_current_band(dev->wl) == NL80211_BAND_2GHZ) {
190758619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8);
190858619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
190958619b14SKalle Valo 				0xFFF7, pa << 3);
191058619b14SKalle Valo 	} else {
191158619b14SKalle Valo 		b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20);
191258619b14SKalle Valo 		b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0,
191358619b14SKalle Valo 				0xFFDF, pa << 5);
191458619b14SKalle Valo 	}
191558619b14SKalle Valo 
191658619b14SKalle Valo 	tmp = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40;
191758619b14SKalle Valo 
191858619b14SKalle Valo 	if (noise)
191958619b14SKalle Valo 		lpphy_set_rx_gain(dev, 0x2D5D);
192058619b14SKalle Valo 	else {
192158619b14SKalle Valo 		if (tmp)
192258619b14SKalle Valo 			oldgains = lpphy_get_tx_gains(dev);
192358619b14SKalle Valo 		if (!gains)
192458619b14SKalle Valo 			gains = &nogains;
192558619b14SKalle Valo 		lpphy_set_tx_gains(dev, *gains);
192658619b14SKalle Valo 	}
192758619b14SKalle Valo 
192858619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
192958619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE);
193058619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800);
193158619b14SKalle Valo 	b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800);
193258619b14SKalle Valo 	lpphy_set_deaf(dev, false);
193358619b14SKalle Valo 	if (noise)
193458619b14SKalle Valo 		ret = lpphy_calc_rx_iq_comp(dev, 0xFFF0);
193558619b14SKalle Valo 	else {
193658619b14SKalle Valo 		lpphy_start_tx_tone(dev, 4000, 100);
193758619b14SKalle Valo 		ret = lpphy_calc_rx_iq_comp(dev, 0x4000);
193858619b14SKalle Valo 		lpphy_stop_tx_tone(dev);
193958619b14SKalle Valo 	}
194058619b14SKalle Valo 	lpphy_clear_deaf(dev, false);
194158619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFC);
194258619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFF7);
194358619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFDF);
194458619b14SKalle Valo 	if (!noise) {
194558619b14SKalle Valo 		if (tmp)
194658619b14SKalle Valo 			lpphy_set_tx_gains(dev, oldgains);
194758619b14SKalle Valo 		else
194858619b14SKalle Valo 			lpphy_disable_tx_gain_override(dev);
194958619b14SKalle Valo 	}
195058619b14SKalle Valo 	lpphy_disable_rx_gain_override(dev);
195158619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFFE);
195258619b14SKalle Valo 	b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xF7FF);
195358619b14SKalle Valo 	return ret;
195458619b14SKalle Valo }
195558619b14SKalle Valo 
195658619b14SKalle Valo static void lpphy_calibration(struct b43_wldev *dev)
195758619b14SKalle Valo {
195858619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
195958619b14SKalle Valo 	enum b43_lpphy_txpctl_mode saved_pctl_mode;
196058619b14SKalle Valo 	bool full_cal = false;
196158619b14SKalle Valo 
196258619b14SKalle Valo 	if (lpphy->full_calib_chan != lpphy->channel) {
196358619b14SKalle Valo 		full_cal = true;
196458619b14SKalle Valo 		lpphy->full_calib_chan = lpphy->channel;
196558619b14SKalle Valo 	}
196658619b14SKalle Valo 
196758619b14SKalle Valo 	b43_mac_suspend(dev);
196858619b14SKalle Valo 
196958619b14SKalle Valo 	lpphy_btcoex_override(dev);
197058619b14SKalle Valo 	if (dev->phy.rev >= 2)
197158619b14SKalle Valo 		lpphy_save_dig_flt_state(dev);
197258619b14SKalle Valo 	lpphy_read_tx_pctl_mode_from_hardware(dev);
197358619b14SKalle Valo 	saved_pctl_mode = lpphy->txpctl_mode;
197458619b14SKalle Valo 	lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF);
197558619b14SKalle Valo 	//TODO Perform transmit power table I/Q LO calibration
197658619b14SKalle Valo 	if ((dev->phy.rev == 0) && (saved_pctl_mode != B43_LPPHY_TXPCTL_OFF))
197758619b14SKalle Valo 		lpphy_pr41573_workaround(dev);
197858619b14SKalle Valo 	if ((dev->phy.rev >= 2) && full_cal) {
197958619b14SKalle Valo 		lpphy_papd_cal_txpwr(dev);
198058619b14SKalle Valo 	}
198158619b14SKalle Valo 	lpphy_set_tx_power_control(dev, saved_pctl_mode);
198258619b14SKalle Valo 	if (dev->phy.rev >= 2)
198358619b14SKalle Valo 		lpphy_restore_dig_flt_state(dev);
198458619b14SKalle Valo 	lpphy_rx_iq_cal(dev, true, true, false, false, NULL);
198558619b14SKalle Valo 
198658619b14SKalle Valo 	b43_mac_enable(dev);
198758619b14SKalle Valo }
198858619b14SKalle Valo 
198958619b14SKalle Valo static void b43_lpphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
199058619b14SKalle Valo 				 u16 set)
199158619b14SKalle Valo {
199258619b14SKalle Valo 	b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg);
199358619b14SKalle Valo 	b43_write16(dev, B43_MMIO_PHY_DATA,
199458619b14SKalle Valo 		    (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
199558619b14SKalle Valo }
199658619b14SKalle Valo 
199758619b14SKalle Valo static u16 b43_lpphy_op_radio_read(struct b43_wldev *dev, u16 reg)
199858619b14SKalle Valo {
199958619b14SKalle Valo 	/* Register 1 is a 32-bit register. */
200058619b14SKalle Valo 	B43_WARN_ON(reg == 1);
200158619b14SKalle Valo 	/* LP-PHY needs a special bit set for read access */
200258619b14SKalle Valo 	if (dev->phy.rev < 2) {
200358619b14SKalle Valo 		if (reg != 0x4001)
200458619b14SKalle Valo 			reg |= 0x100;
200558619b14SKalle Valo 	} else
200658619b14SKalle Valo 		reg |= 0x200;
200758619b14SKalle Valo 
200858619b14SKalle Valo 	b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg);
200958619b14SKalle Valo 	return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW);
201058619b14SKalle Valo }
201158619b14SKalle Valo 
201258619b14SKalle Valo static void b43_lpphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value)
201358619b14SKalle Valo {
201458619b14SKalle Valo 	/* Register 1 is a 32-bit register. */
201558619b14SKalle Valo 	B43_WARN_ON(reg == 1);
201658619b14SKalle Valo 
201758619b14SKalle Valo 	b43_write16f(dev, B43_MMIO_RADIO_CONTROL, reg);
201858619b14SKalle Valo 	b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value);
201958619b14SKalle Valo }
202058619b14SKalle Valo 
202158619b14SKalle Valo struct b206x_channel {
202258619b14SKalle Valo 	u8 channel;
202358619b14SKalle Valo 	u16 freq;
202458619b14SKalle Valo 	u8 data[12];
202558619b14SKalle Valo };
202658619b14SKalle Valo 
202758619b14SKalle Valo static const struct b206x_channel b2062_chantbl[] = {
202858619b14SKalle Valo 	{ .channel = 1, .freq = 2412, .data[0] = 0xFF, .data[1] = 0xFF,
202958619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
203058619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
203158619b14SKalle Valo 	{ .channel = 2, .freq = 2417, .data[0] = 0xFF, .data[1] = 0xFF,
203258619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
203358619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
203458619b14SKalle Valo 	{ .channel = 3, .freq = 2422, .data[0] = 0xFF, .data[1] = 0xFF,
203558619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
203658619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
203758619b14SKalle Valo 	{ .channel = 4, .freq = 2427, .data[0] = 0xFF, .data[1] = 0xFF,
203858619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
203958619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
204058619b14SKalle Valo 	{ .channel = 5, .freq = 2432, .data[0] = 0xFF, .data[1] = 0xFF,
204158619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
204258619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
204358619b14SKalle Valo 	{ .channel = 6, .freq = 2437, .data[0] = 0xFF, .data[1] = 0xFF,
204458619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
204558619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
204658619b14SKalle Valo 	{ .channel = 7, .freq = 2442, .data[0] = 0xFF, .data[1] = 0xFF,
204758619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
204858619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
204958619b14SKalle Valo 	{ .channel = 8, .freq = 2447, .data[0] = 0xFF, .data[1] = 0xFF,
205058619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
205158619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
205258619b14SKalle Valo 	{ .channel = 9, .freq = 2452, .data[0] = 0xFF, .data[1] = 0xFF,
205358619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
205458619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
205558619b14SKalle Valo 	{ .channel = 10, .freq = 2457, .data[0] = 0xFF, .data[1] = 0xFF,
205658619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
205758619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
205858619b14SKalle Valo 	{ .channel = 11, .freq = 2462, .data[0] = 0xFF, .data[1] = 0xFF,
205958619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
206058619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
206158619b14SKalle Valo 	{ .channel = 12, .freq = 2467, .data[0] = 0xFF, .data[1] = 0xFF,
206258619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
206358619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
206458619b14SKalle Valo 	{ .channel = 13, .freq = 2472, .data[0] = 0xFF, .data[1] = 0xFF,
206558619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
206658619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
206758619b14SKalle Valo 	{ .channel = 14, .freq = 2484, .data[0] = 0xFF, .data[1] = 0xFF,
206858619b14SKalle Valo 	  .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32,
206958619b14SKalle Valo 	  .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, },
207058619b14SKalle Valo 	{ .channel = 34, .freq = 5170, .data[0] = 0x00, .data[1] = 0x22,
207158619b14SKalle Valo 	  .data[2] = 0x20, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77,
207258619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
207358619b14SKalle Valo 	{ .channel = 38, .freq = 5190, .data[0] = 0x00, .data[1] = 0x11,
207458619b14SKalle Valo 	  .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
207558619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
207658619b14SKalle Valo 	{ .channel = 42, .freq = 5210, .data[0] = 0x00, .data[1] = 0x11,
207758619b14SKalle Valo 	  .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
207858619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
207958619b14SKalle Valo 	{ .channel = 46, .freq = 5230, .data[0] = 0x00, .data[1] = 0x00,
208058619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
208158619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
208258619b14SKalle Valo 	{ .channel = 36, .freq = 5180, .data[0] = 0x00, .data[1] = 0x11,
208358619b14SKalle Valo 	  .data[2] = 0x20, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
208458619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
208558619b14SKalle Valo 	{ .channel = 40, .freq = 5200, .data[0] = 0x00, .data[1] = 0x11,
208658619b14SKalle Valo 	  .data[2] = 0x10, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77,
208758619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
208858619b14SKalle Valo 	{ .channel = 44, .freq = 5220, .data[0] = 0x00, .data[1] = 0x11,
208958619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
209058619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
209158619b14SKalle Valo 	{ .channel = 48, .freq = 5240, .data[0] = 0x00, .data[1] = 0x00,
209258619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
209358619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
209458619b14SKalle Valo 	{ .channel = 52, .freq = 5260, .data[0] = 0x00, .data[1] = 0x00,
209558619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
209658619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
209758619b14SKalle Valo 	{ .channel = 56, .freq = 5280, .data[0] = 0x00, .data[1] = 0x00,
209858619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77,
209958619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
210058619b14SKalle Valo 	{ .channel = 60, .freq = 5300, .data[0] = 0x00, .data[1] = 0x00,
210158619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x63, .data[4] = 0x3C, .data[5] = 0x77,
210258619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
210358619b14SKalle Valo 	{ .channel = 64, .freq = 5320, .data[0] = 0x00, .data[1] = 0x00,
210458619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x62, .data[4] = 0x3C, .data[5] = 0x77,
210558619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
210658619b14SKalle Valo 	{ .channel = 100, .freq = 5500, .data[0] = 0x00, .data[1] = 0x00,
210758619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x30, .data[4] = 0x3C, .data[5] = 0x77,
210858619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
210958619b14SKalle Valo 	{ .channel = 104, .freq = 5520, .data[0] = 0x00, .data[1] = 0x00,
211058619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77,
211158619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
211258619b14SKalle Valo 	{ .channel = 108, .freq = 5540, .data[0] = 0x00, .data[1] = 0x00,
211358619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77,
211458619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
211558619b14SKalle Valo 	{ .channel = 112, .freq = 5560, .data[0] = 0x00, .data[1] = 0x00,
211658619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77,
211758619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
211858619b14SKalle Valo 	{ .channel = 116, .freq = 5580, .data[0] = 0x00, .data[1] = 0x00,
211958619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x10, .data[4] = 0x3C, .data[5] = 0x77,
212058619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
212158619b14SKalle Valo 	{ .channel = 120, .freq = 5600, .data[0] = 0x00, .data[1] = 0x00,
212258619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
212358619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
212458619b14SKalle Valo 	{ .channel = 124, .freq = 5620, .data[0] = 0x00, .data[1] = 0x00,
212558619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
212658619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
212758619b14SKalle Valo 	{ .channel = 128, .freq = 5640, .data[0] = 0x00, .data[1] = 0x00,
212858619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
212958619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
213058619b14SKalle Valo 	{ .channel = 132, .freq = 5660, .data[0] = 0x00, .data[1] = 0x00,
213158619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
213258619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
213358619b14SKalle Valo 	{ .channel = 136, .freq = 5680, .data[0] = 0x00, .data[1] = 0x00,
213458619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
213558619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
213658619b14SKalle Valo 	{ .channel = 140, .freq = 5700, .data[0] = 0x00, .data[1] = 0x00,
213758619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
213858619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
213958619b14SKalle Valo 	{ .channel = 149, .freq = 5745, .data[0] = 0x00, .data[1] = 0x00,
214058619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
214158619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
214258619b14SKalle Valo 	{ .channel = 153, .freq = 5765, .data[0] = 0x00, .data[1] = 0x00,
214358619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
214458619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
214558619b14SKalle Valo 	{ .channel = 157, .freq = 5785, .data[0] = 0x00, .data[1] = 0x00,
214658619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
214758619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
214858619b14SKalle Valo 	{ .channel = 161, .freq = 5805, .data[0] = 0x00, .data[1] = 0x00,
214958619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
215058619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
215158619b14SKalle Valo 	{ .channel = 165, .freq = 5825, .data[0] = 0x00, .data[1] = 0x00,
215258619b14SKalle Valo 	  .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77,
215358619b14SKalle Valo 	  .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, },
215458619b14SKalle Valo 	{ .channel = 184, .freq = 4920, .data[0] = 0x55, .data[1] = 0x77,
215558619b14SKalle Valo 	  .data[2] = 0x90, .data[3] = 0xF7, .data[4] = 0x3C, .data[5] = 0x77,
215658619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, },
215758619b14SKalle Valo 	{ .channel = 188, .freq = 4940, .data[0] = 0x44, .data[1] = 0x77,
215858619b14SKalle Valo 	  .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77,
215958619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, },
216058619b14SKalle Valo 	{ .channel = 192, .freq = 4960, .data[0] = 0x44, .data[1] = 0x66,
216158619b14SKalle Valo 	  .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77,
216258619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, },
216358619b14SKalle Valo 	{ .channel = 196, .freq = 4980, .data[0] = 0x33, .data[1] = 0x66,
216458619b14SKalle Valo 	  .data[2] = 0x70, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77,
216558619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, },
216658619b14SKalle Valo 	{ .channel = 200, .freq = 5000, .data[0] = 0x22, .data[1] = 0x55,
216758619b14SKalle Valo 	  .data[2] = 0x60, .data[3] = 0xD7, .data[4] = 0x3C, .data[5] = 0x77,
216858619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, },
216958619b14SKalle Valo 	{ .channel = 204, .freq = 5020, .data[0] = 0x22, .data[1] = 0x55,
217058619b14SKalle Valo 	  .data[2] = 0x60, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77,
217158619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, },
217258619b14SKalle Valo 	{ .channel = 208, .freq = 5040, .data[0] = 0x22, .data[1] = 0x44,
217358619b14SKalle Valo 	  .data[2] = 0x50, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77,
217458619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, },
217558619b14SKalle Valo 	{ .channel = 212, .freq = 5060, .data[0] = 0x11, .data[1] = 0x44,
217658619b14SKalle Valo 	  .data[2] = 0x50, .data[3] = 0xA5, .data[4] = 0x3C, .data[5] = 0x77,
217758619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
217858619b14SKalle Valo 	{ .channel = 216, .freq = 5080, .data[0] = 0x00, .data[1] = 0x44,
217958619b14SKalle Valo 	  .data[2] = 0x40, .data[3] = 0xB6, .data[4] = 0x3C, .data[5] = 0x77,
218058619b14SKalle Valo 	  .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, },
218158619b14SKalle Valo };
218258619b14SKalle Valo 
218358619b14SKalle Valo static const struct b206x_channel b2063_chantbl[] = {
218458619b14SKalle Valo 	{ .channel = 1, .freq = 2412, .data[0] = 0x6F, .data[1] = 0x3C,
218558619b14SKalle Valo 	  .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
218658619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
218758619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
218858619b14SKalle Valo 	{ .channel = 2, .freq = 2417, .data[0] = 0x6F, .data[1] = 0x3C,
218958619b14SKalle Valo 	  .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
219058619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
219158619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
219258619b14SKalle Valo 	{ .channel = 3, .freq = 2422, .data[0] = 0x6F, .data[1] = 0x3C,
219358619b14SKalle Valo 	  .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
219458619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
219558619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
219658619b14SKalle Valo 	{ .channel = 4, .freq = 2427, .data[0] = 0x6F, .data[1] = 0x2C,
219758619b14SKalle Valo 	  .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
219858619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
219958619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
220058619b14SKalle Valo 	{ .channel = 5, .freq = 2432, .data[0] = 0x6F, .data[1] = 0x2C,
220158619b14SKalle Valo 	  .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
220258619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
220358619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
220458619b14SKalle Valo 	{ .channel = 6, .freq = 2437, .data[0] = 0x6F, .data[1] = 0x2C,
220558619b14SKalle Valo 	  .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
220658619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
220758619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
220858619b14SKalle Valo 	{ .channel = 7, .freq = 2442, .data[0] = 0x6F, .data[1] = 0x2C,
220958619b14SKalle Valo 	  .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
221058619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
221158619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
221258619b14SKalle Valo 	{ .channel = 8, .freq = 2447, .data[0] = 0x6F, .data[1] = 0x2C,
221358619b14SKalle Valo 	  .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
221458619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
221558619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
221658619b14SKalle Valo 	{ .channel = 9, .freq = 2452, .data[0] = 0x6F, .data[1] = 0x1C,
221758619b14SKalle Valo 	  .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
221858619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
221958619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
222058619b14SKalle Valo 	{ .channel = 10, .freq = 2457, .data[0] = 0x6F, .data[1] = 0x1C,
222158619b14SKalle Valo 	  .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
222258619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
222358619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
222458619b14SKalle Valo 	{ .channel = 11, .freq = 2462, .data[0] = 0x6E, .data[1] = 0x1C,
222558619b14SKalle Valo 	  .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
222658619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
222758619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
222858619b14SKalle Valo 	{ .channel = 12, .freq = 2467, .data[0] = 0x6E, .data[1] = 0x1C,
222958619b14SKalle Valo 	  .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
223058619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
223158619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
223258619b14SKalle Valo 	{ .channel = 13, .freq = 2472, .data[0] = 0x6E, .data[1] = 0x1C,
223358619b14SKalle Valo 	  .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
223458619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
223558619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
223658619b14SKalle Valo 	{ .channel = 14, .freq = 2484, .data[0] = 0x6E, .data[1] = 0x0C,
223758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05,
223858619b14SKalle Valo 	  .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80,
223958619b14SKalle Valo 	  .data[10] = 0x80, .data[11] = 0x70, },
224058619b14SKalle Valo 	{ .channel = 34, .freq = 5170, .data[0] = 0x6A, .data[1] = 0x0C,
224158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x02, .data[5] = 0x05,
224258619b14SKalle Valo 	  .data[6] = 0x0D, .data[7] = 0x0D, .data[8] = 0x77, .data[9] = 0x80,
224358619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
224458619b14SKalle Valo 	{ .channel = 36, .freq = 5180, .data[0] = 0x6A, .data[1] = 0x0C,
224558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x05,
224658619b14SKalle Valo 	  .data[6] = 0x0D, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80,
224758619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
224858619b14SKalle Valo 	{ .channel = 38, .freq = 5190, .data[0] = 0x6A, .data[1] = 0x0C,
224958619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04,
225058619b14SKalle Valo 	  .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80,
225158619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
225258619b14SKalle Valo 	{ .channel = 40, .freq = 5200, .data[0] = 0x69, .data[1] = 0x0C,
225358619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04,
225458619b14SKalle Valo 	  .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70,
225558619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
225658619b14SKalle Valo 	{ .channel = 42, .freq = 5210, .data[0] = 0x69, .data[1] = 0x0C,
225758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04,
225858619b14SKalle Valo 	  .data[6] = 0x0B, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70,
225958619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
226058619b14SKalle Valo 	{ .channel = 44, .freq = 5220, .data[0] = 0x69, .data[1] = 0x0C,
226158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x04,
226258619b14SKalle Valo 	  .data[6] = 0x0B, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60,
226358619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
226458619b14SKalle Valo 	{ .channel = 46, .freq = 5230, .data[0] = 0x69, .data[1] = 0x0C,
226558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03,
226658619b14SKalle Valo 	  .data[6] = 0x0A, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60,
226758619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
226858619b14SKalle Valo 	{ .channel = 48, .freq = 5240, .data[0] = 0x69, .data[1] = 0x0C,
226958619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03,
227058619b14SKalle Valo 	  .data[6] = 0x0A, .data[7] = 0x0A, .data[8] = 0x77, .data[9] = 0x60,
227158619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
227258619b14SKalle Valo 	{ .channel = 52, .freq = 5260, .data[0] = 0x68, .data[1] = 0x0C,
227358619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x02,
227458619b14SKalle Valo 	  .data[6] = 0x09, .data[7] = 0x09, .data[8] = 0x77, .data[9] = 0x60,
227558619b14SKalle Valo 	  .data[10] = 0x20, .data[11] = 0x00, },
227658619b14SKalle Valo 	{ .channel = 56, .freq = 5280, .data[0] = 0x68, .data[1] = 0x0C,
227758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01,
227858619b14SKalle Valo 	  .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50,
227958619b14SKalle Valo 	  .data[10] = 0x10, .data[11] = 0x00, },
228058619b14SKalle Valo 	{ .channel = 60, .freq = 5300, .data[0] = 0x68, .data[1] = 0x0C,
228158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01,
228258619b14SKalle Valo 	  .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50,
228358619b14SKalle Valo 	  .data[10] = 0x10, .data[11] = 0x00, },
228458619b14SKalle Valo 	{ .channel = 64, .freq = 5320, .data[0] = 0x67, .data[1] = 0x0C,
228558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
228658619b14SKalle Valo 	  .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50,
228758619b14SKalle Valo 	  .data[10] = 0x10, .data[11] = 0x00, },
228858619b14SKalle Valo 	{ .channel = 100, .freq = 5500, .data[0] = 0x64, .data[1] = 0x0C,
228958619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
229058619b14SKalle Valo 	  .data[6] = 0x02, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20,
229158619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
229258619b14SKalle Valo 	{ .channel = 104, .freq = 5520, .data[0] = 0x64, .data[1] = 0x0C,
229358619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
229458619b14SKalle Valo 	  .data[6] = 0x01, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20,
229558619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
229658619b14SKalle Valo 	{ .channel = 108, .freq = 5540, .data[0] = 0x63, .data[1] = 0x0C,
229758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
229858619b14SKalle Valo 	  .data[6] = 0x01, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10,
229958619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
230058619b14SKalle Valo 	{ .channel = 112, .freq = 5560, .data[0] = 0x63, .data[1] = 0x0C,
230158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
230258619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10,
230358619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
230458619b14SKalle Valo 	{ .channel = 116, .freq = 5580, .data[0] = 0x62, .data[1] = 0x0C,
230558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
230658619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10,
230758619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
230858619b14SKalle Valo 	{ .channel = 120, .freq = 5600, .data[0] = 0x62, .data[1] = 0x0C,
230958619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
231058619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
231158619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
231258619b14SKalle Valo 	{ .channel = 124, .freq = 5620, .data[0] = 0x62, .data[1] = 0x0C,
231358619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
231458619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
231558619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
231658619b14SKalle Valo 	{ .channel = 128, .freq = 5640, .data[0] = 0x61, .data[1] = 0x0C,
231758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
231858619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
231958619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
232058619b14SKalle Valo 	{ .channel = 132, .freq = 5660, .data[0] = 0x61, .data[1] = 0x0C,
232158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
232258619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
232358619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
232458619b14SKalle Valo 	{ .channel = 136, .freq = 5680, .data[0] = 0x61, .data[1] = 0x0C,
232558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
232658619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
232758619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
232858619b14SKalle Valo 	{ .channel = 140, .freq = 5700, .data[0] = 0x60, .data[1] = 0x0C,
232958619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
233058619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
233158619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
233258619b14SKalle Valo 	{ .channel = 149, .freq = 5745, .data[0] = 0x60, .data[1] = 0x0C,
233358619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
233458619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
233558619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
233658619b14SKalle Valo 	{ .channel = 153, .freq = 5765, .data[0] = 0x60, .data[1] = 0x0C,
233758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
233858619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
233958619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
234058619b14SKalle Valo 	{ .channel = 157, .freq = 5785, .data[0] = 0x60, .data[1] = 0x0C,
234158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
234258619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
234358619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
234458619b14SKalle Valo 	{ .channel = 161, .freq = 5805, .data[0] = 0x60, .data[1] = 0x0C,
234558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
234658619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
234758619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
234858619b14SKalle Valo 	{ .channel = 165, .freq = 5825, .data[0] = 0x60, .data[1] = 0x0C,
234958619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00,
235058619b14SKalle Valo 	  .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00,
235158619b14SKalle Valo 	  .data[10] = 0x00, .data[11] = 0x00, },
235258619b14SKalle Valo 	{ .channel = 184, .freq = 4920, .data[0] = 0x6E, .data[1] = 0x0C,
235358619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0E,
235458619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xC0,
235558619b14SKalle Valo 	  .data[10] = 0x50, .data[11] = 0x00, },
235658619b14SKalle Valo 	{ .channel = 188, .freq = 4940, .data[0] = 0x6E, .data[1] = 0x0C,
235758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0D,
235858619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0,
235958619b14SKalle Valo 	  .data[10] = 0x50, .data[11] = 0x00, },
236058619b14SKalle Valo 	{ .channel = 192, .freq = 4960, .data[0] = 0x6E, .data[1] = 0x0C,
236158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C,
236258619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0,
236358619b14SKalle Valo 	  .data[10] = 0x50, .data[11] = 0x00, },
236458619b14SKalle Valo 	{ .channel = 196, .freq = 4980, .data[0] = 0x6D, .data[1] = 0x0C,
236558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C,
236658619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0,
236758619b14SKalle Valo 	  .data[10] = 0x40, .data[11] = 0x00, },
236858619b14SKalle Valo 	{ .channel = 200, .freq = 5000, .data[0] = 0x6D, .data[1] = 0x0C,
236958619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0B,
237058619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0,
237158619b14SKalle Valo 	  .data[10] = 0x40, .data[11] = 0x00, },
237258619b14SKalle Valo 	{ .channel = 204, .freq = 5020, .data[0] = 0x6D, .data[1] = 0x0C,
237358619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0A,
237458619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0,
237558619b14SKalle Valo 	  .data[10] = 0x40, .data[11] = 0x00, },
237658619b14SKalle Valo 	{ .channel = 208, .freq = 5040, .data[0] = 0x6C, .data[1] = 0x0C,
237758619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x07, .data[5] = 0x09,
237858619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90,
237958619b14SKalle Valo 	  .data[10] = 0x40, .data[11] = 0x00, },
238058619b14SKalle Valo 	{ .channel = 212, .freq = 5060, .data[0] = 0x6C, .data[1] = 0x0C,
238158619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x06, .data[5] = 0x08,
238258619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90,
238358619b14SKalle Valo 	  .data[10] = 0x40, .data[11] = 0x00, },
238458619b14SKalle Valo 	{ .channel = 216, .freq = 5080, .data[0] = 0x6C, .data[1] = 0x0C,
238558619b14SKalle Valo 	  .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x05, .data[5] = 0x08,
238658619b14SKalle Valo 	  .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90,
238758619b14SKalle Valo 	  .data[10] = 0x40, .data[11] = 0x00, },
238858619b14SKalle Valo };
238958619b14SKalle Valo 
239058619b14SKalle Valo static void lpphy_b2062_reset_pll_bias(struct b43_wldev *dev)
239158619b14SKalle Valo {
239258619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0xFF);
239358619b14SKalle Valo 	udelay(20);
239458619b14SKalle Valo 	if (dev->dev->chip_id == 0x5354) {
239558619b14SKalle Valo 		b43_radio_write(dev, B2062_N_COMM1, 4);
239658619b14SKalle Valo 		b43_radio_write(dev, B2062_S_RFPLL_CTL2, 4);
239758619b14SKalle Valo 	} else {
239858619b14SKalle Valo 		b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0);
239958619b14SKalle Valo 	}
240058619b14SKalle Valo 	udelay(5);
240158619b14SKalle Valo }
240258619b14SKalle Valo 
240358619b14SKalle Valo static void lpphy_b2062_vco_calib(struct b43_wldev *dev)
240458619b14SKalle Valo {
240558619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x42);
240658619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x62);
240758619b14SKalle Valo 	udelay(200);
240858619b14SKalle Valo }
240958619b14SKalle Valo 
241058619b14SKalle Valo static int lpphy_b2062_tune(struct b43_wldev *dev,
241158619b14SKalle Valo 			    unsigned int channel)
241258619b14SKalle Valo {
241358619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
241458619b14SKalle Valo 	struct ssb_bus *bus = dev->dev->sdev->bus;
241558619b14SKalle Valo 	const struct b206x_channel *chandata = NULL;
241658619b14SKalle Valo 	u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
241758619b14SKalle Valo 	u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9;
241858619b14SKalle Valo 	int i, err = 0;
241958619b14SKalle Valo 
242058619b14SKalle Valo 	for (i = 0; i < ARRAY_SIZE(b2062_chantbl); i++) {
242158619b14SKalle Valo 		if (b2062_chantbl[i].channel == channel) {
242258619b14SKalle Valo 			chandata = &b2062_chantbl[i];
242358619b14SKalle Valo 			break;
242458619b14SKalle Valo 		}
242558619b14SKalle Valo 	}
242658619b14SKalle Valo 
242758619b14SKalle Valo 	if (B43_WARN_ON(!chandata))
242858619b14SKalle Valo 		return -EINVAL;
242958619b14SKalle Valo 
243058619b14SKalle Valo 	b43_radio_set(dev, B2062_S_RFPLL_CTL14, 0x04);
243158619b14SKalle Valo 	b43_radio_write(dev, B2062_N_LGENA_TUNE0, chandata->data[0]);
243258619b14SKalle Valo 	b43_radio_write(dev, B2062_N_LGENA_TUNE2, chandata->data[1]);
243358619b14SKalle Valo 	b43_radio_write(dev, B2062_N_LGENA_TUNE3, chandata->data[2]);
243458619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_TUNE, chandata->data[3]);
243558619b14SKalle Valo 	b43_radio_write(dev, B2062_S_LGENG_CTL1, chandata->data[4]);
243658619b14SKalle Valo 	b43_radio_write(dev, B2062_N_LGENA_CTL5, chandata->data[5]);
243758619b14SKalle Valo 	b43_radio_write(dev, B2062_N_LGENA_CTL6, chandata->data[6]);
243858619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_PGA, chandata->data[7]);
243958619b14SKalle Valo 	b43_radio_write(dev, B2062_N_TX_PAD, chandata->data[8]);
244058619b14SKalle Valo 
244158619b14SKalle Valo 	tmp1 = crystal_freq / 1000;
244258619b14SKalle Valo 	tmp2 = lpphy->pdiv * 1000;
244358619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xCC);
244458619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0x07);
244558619b14SKalle Valo 	lpphy_b2062_reset_pll_bias(dev);
244658619b14SKalle Valo 	tmp3 = tmp2 * channel2freq_lp(channel);
244758619b14SKalle Valo 	if (channel2freq_lp(channel) < 4000)
244858619b14SKalle Valo 		tmp3 *= 2;
244958619b14SKalle Valo 	tmp4 = 48 * tmp1;
245058619b14SKalle Valo 	tmp6 = tmp3 / tmp4;
245158619b14SKalle Valo 	tmp7 = tmp3 % tmp4;
245258619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL26, tmp6);
245358619b14SKalle Valo 	tmp5 = tmp7 * 0x100;
245458619b14SKalle Valo 	tmp6 = tmp5 / tmp4;
245558619b14SKalle Valo 	tmp7 = tmp5 % tmp4;
245658619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL27, tmp6);
245758619b14SKalle Valo 	tmp5 = tmp7 * 0x100;
245858619b14SKalle Valo 	tmp6 = tmp5 / tmp4;
245958619b14SKalle Valo 	tmp7 = tmp5 % tmp4;
246058619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL28, tmp6);
246158619b14SKalle Valo 	tmp5 = tmp7 * 0x100;
246258619b14SKalle Valo 	tmp6 = tmp5 / tmp4;
246358619b14SKalle Valo 	tmp7 = tmp5 % tmp4;
246458619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL29, tmp6 + ((2 * tmp7) / tmp4));
246558619b14SKalle Valo 	tmp8 = b43_radio_read(dev, B2062_S_RFPLL_CTL19);
246658619b14SKalle Valo 	tmp9 = ((2 * tmp3 * (tmp8 + 1)) + (3 * tmp1)) / (6 * tmp1);
246758619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL23, (tmp9 >> 8) + 16);
246858619b14SKalle Valo 	b43_radio_write(dev, B2062_S_RFPLL_CTL24, tmp9 & 0xFF);
246958619b14SKalle Valo 
247058619b14SKalle Valo 	lpphy_b2062_vco_calib(dev);
247158619b14SKalle Valo 	if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) {
247258619b14SKalle Valo 		b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xFC);
247358619b14SKalle Valo 		b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0);
247458619b14SKalle Valo 		lpphy_b2062_reset_pll_bias(dev);
247558619b14SKalle Valo 		lpphy_b2062_vco_calib(dev);
247658619b14SKalle Valo 		if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10)
247758619b14SKalle Valo 			err = -EIO;
247858619b14SKalle Valo 	}
247958619b14SKalle Valo 
248058619b14SKalle Valo 	b43_radio_mask(dev, B2062_S_RFPLL_CTL14, ~0x04);
248158619b14SKalle Valo 	return err;
248258619b14SKalle Valo }
248358619b14SKalle Valo 
248458619b14SKalle Valo static void lpphy_b2063_vco_calib(struct b43_wldev *dev)
248558619b14SKalle Valo {
248658619b14SKalle Valo 	u16 tmp;
248758619b14SKalle Valo 
248858619b14SKalle Valo 	b43_radio_mask(dev, B2063_PLL_SP1, ~0x40);
248958619b14SKalle Valo 	tmp = b43_radio_read(dev, B2063_PLL_JTAG_CALNRST) & 0xF8;
249058619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp);
249158619b14SKalle Valo 	udelay(1);
249258619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x4);
249358619b14SKalle Valo 	udelay(1);
249458619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x6);
249558619b14SKalle Valo 	udelay(1);
249658619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x7);
249758619b14SKalle Valo 	udelay(300);
249858619b14SKalle Valo 	b43_radio_set(dev, B2063_PLL_SP1, 0x40);
249958619b14SKalle Valo }
250058619b14SKalle Valo 
250158619b14SKalle Valo static int lpphy_b2063_tune(struct b43_wldev *dev,
250258619b14SKalle Valo 			    unsigned int channel)
250358619b14SKalle Valo {
250458619b14SKalle Valo 	struct ssb_bus *bus = dev->dev->sdev->bus;
250558619b14SKalle Valo 
250658619b14SKalle Valo 	static const struct b206x_channel *chandata = NULL;
250758619b14SKalle Valo 	u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000;
250858619b14SKalle Valo 	u32 freqref, vco_freq, val1, val2, val3, timeout, timeoutref, count;
250958619b14SKalle Valo 	u16 old_comm15, scale;
251058619b14SKalle Valo 	u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
251158619b14SKalle Valo 	int i, div = (crystal_freq <= 26000000 ? 1 : 2);
251258619b14SKalle Valo 
251358619b14SKalle Valo 	for (i = 0; i < ARRAY_SIZE(b2063_chantbl); i++) {
251458619b14SKalle Valo 		if (b2063_chantbl[i].channel == channel) {
251558619b14SKalle Valo 			chandata = &b2063_chantbl[i];
251658619b14SKalle Valo 			break;
251758619b14SKalle Valo 		}
251858619b14SKalle Valo 	}
251958619b14SKalle Valo 
252058619b14SKalle Valo 	if (B43_WARN_ON(!chandata))
252158619b14SKalle Valo 		return -EINVAL;
252258619b14SKalle Valo 
252358619b14SKalle Valo 	b43_radio_write(dev, B2063_LOGEN_VCOBUF1, chandata->data[0]);
252458619b14SKalle Valo 	b43_radio_write(dev, B2063_LOGEN_MIXER2, chandata->data[1]);
252558619b14SKalle Valo 	b43_radio_write(dev, B2063_LOGEN_BUF2, chandata->data[2]);
252658619b14SKalle Valo 	b43_radio_write(dev, B2063_LOGEN_RCCR1, chandata->data[3]);
252758619b14SKalle Valo 	b43_radio_write(dev, B2063_A_RX_1ST3, chandata->data[4]);
252858619b14SKalle Valo 	b43_radio_write(dev, B2063_A_RX_2ND1, chandata->data[5]);
252958619b14SKalle Valo 	b43_radio_write(dev, B2063_A_RX_2ND4, chandata->data[6]);
253058619b14SKalle Valo 	b43_radio_write(dev, B2063_A_RX_2ND7, chandata->data[7]);
253158619b14SKalle Valo 	b43_radio_write(dev, B2063_A_RX_PS6, chandata->data[8]);
253258619b14SKalle Valo 	b43_radio_write(dev, B2063_TX_RF_CTL2, chandata->data[9]);
253358619b14SKalle Valo 	b43_radio_write(dev, B2063_TX_RF_CTL5, chandata->data[10]);
253458619b14SKalle Valo 	b43_radio_write(dev, B2063_PA_CTL11, chandata->data[11]);
253558619b14SKalle Valo 
253658619b14SKalle Valo 	old_comm15 = b43_radio_read(dev, B2063_COMM15);
253758619b14SKalle Valo 	b43_radio_set(dev, B2063_COMM15, 0x1E);
253858619b14SKalle Valo 
253958619b14SKalle Valo 	if (chandata->freq > 4000) /* spec says 2484, but 4000 is safer */
254058619b14SKalle Valo 		vco_freq = chandata->freq << 1;
254158619b14SKalle Valo 	else
254258619b14SKalle Valo 		vco_freq = chandata->freq << 2;
254358619b14SKalle Valo 
254458619b14SKalle Valo 	freqref = crystal_freq * 3;
254558619b14SKalle Valo 	val1 = lpphy_qdiv_roundup(crystal_freq, 1000000, 16);
254658619b14SKalle Valo 	val2 = lpphy_qdiv_roundup(crystal_freq, 1000000 * div, 16);
254758619b14SKalle Valo 	val3 = lpphy_qdiv_roundup(vco_freq, 3, 16);
254858619b14SKalle Valo 	timeout = ((((8 * crystal_freq) / (div * 5000000)) + 1) >> 1) - 1;
254958619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB3, 0x2);
255058619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB6,
255158619b14SKalle Valo 			  0xFFF8, timeout >> 2);
255258619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7,
255358619b14SKalle Valo 			  0xFF9F,timeout << 5);
255458619b14SKalle Valo 
255558619b14SKalle Valo 	timeoutref = ((((8 * crystal_freq) / (div * (timeout + 1))) +
255658619b14SKalle Valo 						999999) / 1000000) + 1;
255758619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB5, timeoutref);
255858619b14SKalle Valo 
255958619b14SKalle Valo 	count = lpphy_qdiv_roundup(val3, val2 + 16, 16);
256058619b14SKalle Valo 	count *= (timeout + 1) * (timeoutref + 1);
256158619b14SKalle Valo 	count--;
256258619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7,
256358619b14SKalle Valo 						0xF0, count >> 8);
256458619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB8, count & 0xFF);
256558619b14SKalle Valo 
256658619b14SKalle Valo 	tmp1 = ((val3 * 62500) / freqref) << 4;
256758619b14SKalle Valo 	tmp2 = ((val3 * 62500) % freqref) << 4;
256858619b14SKalle Valo 	while (tmp2 >= freqref) {
256958619b14SKalle Valo 		tmp1++;
257058619b14SKalle Valo 		tmp2 -= freqref;
257158619b14SKalle Valo 	}
257258619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG1, 0xFFE0, tmp1 >> 4);
257358619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFE0F, tmp1 << 4);
257458619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFFF0, tmp1 >> 16);
257558619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG3, (tmp2 >> 8) & 0xFF);
257658619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG4, tmp2 & 0xFF);
257758619b14SKalle Valo 
257858619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF1, 0xB9);
257958619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF2, 0x88);
258058619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF3, 0x28);
258158619b14SKalle Valo 	b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF4, 0x63);
258258619b14SKalle Valo 
258358619b14SKalle Valo 	tmp3 = ((41 * (val3 - 3000)) /1200) + 27;
258458619b14SKalle Valo 	tmp4 = lpphy_qdiv_roundup(132000 * tmp1, 8451, 16);
258558619b14SKalle Valo 
258658619b14SKalle Valo 	if ((tmp4 + tmp3 - 1) / tmp3 > 60) {
258758619b14SKalle Valo 		scale = 1;
258858619b14SKalle Valo 		tmp5 = ((tmp4 + tmp3) / (tmp3 << 1)) - 8;
258958619b14SKalle Valo 	} else {
259058619b14SKalle Valo 		scale = 0;
259158619b14SKalle Valo 		tmp5 = ((tmp4 + (tmp3 >> 1)) / tmp3) - 8;
259258619b14SKalle Valo 	}
259358619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFC0, tmp5);
259458619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFBF, scale << 6);
259558619b14SKalle Valo 
259658619b14SKalle Valo 	tmp6 = lpphy_qdiv_roundup(100 * val1, val3, 16);
259758619b14SKalle Valo 	tmp6 *= (tmp5 * 8) * (scale + 1);
259858619b14SKalle Valo 	if (tmp6 > 150)
259958619b14SKalle Valo 		tmp6 = 0;
260058619b14SKalle Valo 
260158619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFE0, tmp6);
260258619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFDF, scale << 5);
260358619b14SKalle Valo 
260458619b14SKalle Valo 	b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFFFB, 0x4);
260558619b14SKalle Valo 	if (crystal_freq > 26000000)
260658619b14SKalle Valo 		b43_radio_set(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0x2);
260758619b14SKalle Valo 	else
260858619b14SKalle Valo 		b43_radio_mask(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFD);
260958619b14SKalle Valo 
261058619b14SKalle Valo 	if (val1 == 45)
261158619b14SKalle Valo 		b43_radio_set(dev, B2063_PLL_JTAG_PLL_VCO1, 0x2);
261258619b14SKalle Valo 	else
261358619b14SKalle Valo 		b43_radio_mask(dev, B2063_PLL_JTAG_PLL_VCO1, 0xFD);
261458619b14SKalle Valo 
261558619b14SKalle Valo 	b43_radio_set(dev, B2063_PLL_SP2, 0x3);
261658619b14SKalle Valo 	udelay(1);
261758619b14SKalle Valo 	b43_radio_mask(dev, B2063_PLL_SP2, 0xFFFC);
261858619b14SKalle Valo 	lpphy_b2063_vco_calib(dev);
261958619b14SKalle Valo 	b43_radio_write(dev, B2063_COMM15, old_comm15);
262058619b14SKalle Valo 
262158619b14SKalle Valo 	return 0;
262258619b14SKalle Valo }
262358619b14SKalle Valo 
262458619b14SKalle Valo static int b43_lpphy_op_switch_channel(struct b43_wldev *dev,
262558619b14SKalle Valo 				       unsigned int new_channel)
262658619b14SKalle Valo {
262758619b14SKalle Valo 	struct b43_phy_lp *lpphy = dev->phy.lp;
262858619b14SKalle Valo 	int err;
262958619b14SKalle Valo 
263058619b14SKalle Valo 	if (dev->phy.radio_ver == 0x2063) {
263158619b14SKalle Valo 		err = lpphy_b2063_tune(dev, new_channel);
263258619b14SKalle Valo 		if (err)
263358619b14SKalle Valo 			return err;
263458619b14SKalle Valo 	} else {
263558619b14SKalle Valo 		err = lpphy_b2062_tune(dev, new_channel);
263658619b14SKalle Valo 		if (err)
263758619b14SKalle Valo 			return err;
263858619b14SKalle Valo 		lpphy_set_analog_filter(dev, new_channel);
263958619b14SKalle Valo 		lpphy_adjust_gain_table(dev, channel2freq_lp(new_channel));
264058619b14SKalle Valo 	}
264158619b14SKalle Valo 
264258619b14SKalle Valo 	lpphy->channel = new_channel;
264358619b14SKalle Valo 	b43_write16(dev, B43_MMIO_CHANNEL, new_channel);
264458619b14SKalle Valo 
264558619b14SKalle Valo 	return 0;
264658619b14SKalle Valo }
264758619b14SKalle Valo 
264858619b14SKalle Valo static int b43_lpphy_op_init(struct b43_wldev *dev)
264958619b14SKalle Valo {
265058619b14SKalle Valo 	int err;
265158619b14SKalle Valo 
265258619b14SKalle Valo 	if (dev->dev->bus_type != B43_BUS_SSB) {
265358619b14SKalle Valo 		b43err(dev->wl, "LP-PHY is supported only on SSB!\n");
265458619b14SKalle Valo 		return -EOPNOTSUPP;
265558619b14SKalle Valo 	}
265658619b14SKalle Valo 
265758619b14SKalle Valo 	lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs?
265858619b14SKalle Valo 	lpphy_baseband_init(dev);
265958619b14SKalle Valo 	lpphy_radio_init(dev);
266058619b14SKalle Valo 	lpphy_calibrate_rc(dev);
266158619b14SKalle Valo 	err = b43_lpphy_op_switch_channel(dev, 7);
266258619b14SKalle Valo 	if (err) {
266358619b14SKalle Valo 		b43dbg(dev->wl, "Switch to channel 7 failed, error = %d.\n",
266458619b14SKalle Valo 		       err);
266558619b14SKalle Valo 	}
266658619b14SKalle Valo 	lpphy_tx_pctl_init(dev);
266758619b14SKalle Valo 	lpphy_calibration(dev);
266858619b14SKalle Valo 	//TODO ACI init
266958619b14SKalle Valo 
267058619b14SKalle Valo 	return 0;
267158619b14SKalle Valo }
267258619b14SKalle Valo 
267358619b14SKalle Valo static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev)
267458619b14SKalle Valo {
267558619b14SKalle Valo 	//TODO
267658619b14SKalle Valo }
267758619b14SKalle Valo 
267858619b14SKalle Valo static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev,
267958619b14SKalle Valo 							 bool ignore_tssi)
268058619b14SKalle Valo {
268158619b14SKalle Valo 	//TODO
268258619b14SKalle Valo 	return B43_TXPWR_RES_DONE;
268358619b14SKalle Valo }
268458619b14SKalle Valo 
268558619b14SKalle Valo static void b43_lpphy_op_switch_analog(struct b43_wldev *dev, bool on)
268658619b14SKalle Valo {
268758619b14SKalle Valo        if (on) {
268858619b14SKalle Valo                b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVR, 0xfff8);
268958619b14SKalle Valo        } else {
269058619b14SKalle Valo                b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0x0007);
269158619b14SKalle Valo                b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 0x0007);
269258619b14SKalle Valo        }
269358619b14SKalle Valo }
269458619b14SKalle Valo 
269558619b14SKalle Valo static void b43_lpphy_op_pwork_15sec(struct b43_wldev *dev)
269658619b14SKalle Valo {
269758619b14SKalle Valo 	//TODO
269858619b14SKalle Valo }
269958619b14SKalle Valo 
270058619b14SKalle Valo const struct b43_phy_operations b43_phyops_lp = {
270158619b14SKalle Valo 	.allocate		= b43_lpphy_op_allocate,
270258619b14SKalle Valo 	.free			= b43_lpphy_op_free,
270358619b14SKalle Valo 	.prepare_structs	= b43_lpphy_op_prepare_structs,
270458619b14SKalle Valo 	.init			= b43_lpphy_op_init,
270558619b14SKalle Valo 	.phy_maskset		= b43_lpphy_op_maskset,
270658619b14SKalle Valo 	.radio_read		= b43_lpphy_op_radio_read,
270758619b14SKalle Valo 	.radio_write		= b43_lpphy_op_radio_write,
270858619b14SKalle Valo 	.software_rfkill	= b43_lpphy_op_software_rfkill,
270958619b14SKalle Valo 	.switch_analog		= b43_lpphy_op_switch_analog,
271058619b14SKalle Valo 	.switch_channel		= b43_lpphy_op_switch_channel,
271158619b14SKalle Valo 	.get_default_chan	= b43_lpphy_op_get_default_chan,
271258619b14SKalle Valo 	.set_rx_antenna		= b43_lpphy_op_set_rx_antenna,
271358619b14SKalle Valo 	.recalc_txpower		= b43_lpphy_op_recalc_txpower,
271458619b14SKalle Valo 	.adjust_txpower		= b43_lpphy_op_adjust_txpower,
271558619b14SKalle Valo 	.pwork_15sec		= b43_lpphy_op_pwork_15sec,
271658619b14SKalle Valo 	.pwork_60sec		= lpphy_calibration,
271758619b14SKalle Valo };
2718