Skip to content
Snippets Groups Projects
phy_n.c 94.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2006);
    			b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0800);
    			b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x2016);
    			b43_phy_write(dev, B43_NPHY_TABLE_DATALO, 0x0800);
    		}
    
    		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
    		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
    		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
    		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
    
    		if (bus->sprom.boardflags2_lo & 0x100 &&
    		    bus->boardinfo.type == 0x8B) {
    			delays1[0] = 0x1;
    			delays1[5] = 0x14;
    		}
    
    		b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7);
    		b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7);
    
    		b43_nphy_gain_crtl_workarounds(dev);
    
    
    		if (dev->phy.rev < 2) {
    			if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2)
    				; /*TODO: b43_mhf(dev, 2, 0x0010, 0x0010, 3);*/
    		} else if (dev->phy.rev == 2) {
    			b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
    			b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0);
    		}
    
    		if (dev->phy.rev < 2)
    			b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL,
    					~B43_NPHY_SCRAM_SIGCTL_SCM);
    
    		/* Set phase track alpha and beta */
    		b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125);
    		b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3);
    		b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105);
    		b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E);
    		b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD);
    		b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20);
    
    		b43_phy_mask(dev, B43_NPHY_PIL_DW1,
    				(u16)~B43_NPHY_PIL_DW_64QAM);
    		b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5);
    		b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4);
    		b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00);
    
    		if (dev->phy.rev == 2)
    			b43_phy_set(dev, B43_NPHY_FINERX2_CGC,
    					B43_NPHY_FINERX2_CGC_DECGC);
    	}
    
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, 0);
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/LoadSampleTable */
    static int b43_nphy_load_samples(struct b43_wldev *dev,
    					struct b43_c32 *samples, u16 len) {
    	struct b43_phy_n *nphy = dev->phy.n;
    	u16 i;
    	u32 *data;
    
    	data = kzalloc(len * sizeof(u32), GFP_KERNEL);
    	if (!data) {
    		b43err(dev->wl, "allocation for samples loading failed\n");
    		return -ENOMEM;
    	}
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, 1);
    
    	for (i = 0; i < len; i++) {
    		data[i] = (samples[i].i & 0x3FF << 10);
    		data[i] |= samples[i].q & 0x3FF;
    	}
    	b43_ntab_write_bulk(dev, B43_NTAB32(17, 0), len, data);
    
    	kfree(data);
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, 0);
    	return 0;
    }
    
    
    Rafał Miłecki's avatar
    Rafał Miłecki committed
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GenLoadSamples */
    static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
    					bool test)
    {
    	int i;
    
    	u16 bw, len, rot, angle;
    
    Rafał Miłecki's avatar
    Rafał Miłecki committed
    
    	bw = (dev->phy.is_40mhz) ? 40 : 20;
    	len = bw << 3;
    
    	if (test) {
    		if (b43_phy_read(dev, B43_NPHY_BBCFG) & B43_NPHY_BBCFG_RSTRX)
    			bw = 82;
    		else
    			bw = 80;
    
    		if (dev->phy.is_40mhz)
    			bw <<= 1;
    
    		len = bw << 1;
    	}
    
    
    	samples = kzalloc(len * sizeof(struct b43_c32), GFP_KERNEL);
    
    	if (!samples) {
    		b43err(dev->wl, "allocation for samples generation failed\n");
    		return 0;
    	}
    
    Rafał Miłecki's avatar
    Rafał Miłecki committed
    	rot = (((freq * 36) / bw) << 16) / 100;
    	angle = 0;
    
    
    	for (i = 0; i < len; i++) {
    		samples[i] = b43_cordic(angle);
    		angle += rot;
    		samples[i].q = CORDIC_CONVERT(samples[i].q * max);
    		samples[i].i = CORDIC_CONVERT(samples[i].i * max);
    
    	i = b43_nphy_load_samples(dev, samples, len);
    
    	return (i < 0) ? 0 : len;
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */
    static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
    					u16 wait, bool iqmode, bool dac_test)
    {
    	struct b43_phy_n *nphy = dev->phy.n;
    	int i;
    	u16 seq_mode;
    	u32 tmp;
    
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, true);
    
    	if ((nphy->bb_mult_save & 0x80000000) == 0) {
    		tmp = b43_ntab_read(dev, B43_NTAB16(15, 87));
    		nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
    	}
    
    	if (!dev->phy.is_40mhz)
    		tmp = 0x6464;
    	else
    		tmp = 0x4747;
    	b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
    
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, false);
    
    	b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1));
    
    	if (loops != 0xFFFF)
    		b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, (loops - 1));
    	else
    		b43_phy_write(dev, B43_NPHY_SAMP_LOOPCNT, loops);
    
    	b43_phy_write(dev, B43_NPHY_SAMP_WAITCNT, wait);
    
    	seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE);
    
    	b43_phy_set(dev, B43_NPHY_RFSEQMODE, B43_NPHY_RFSEQMODE_CAOVER);
    	if (iqmode) {
    		b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF);
    		b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000);
    	} else {
    		if (dac_test)
    			b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5);
    		else
    			b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1);
    	}
    	for (i = 0; i < 100; i++) {
    		if (b43_phy_read(dev, B43_NPHY_RFSEQST) & 1) {
    			i = 0;
    			break;
    		}
    		udelay(10);
    	}
    	if (i)
    		b43err(dev->wl, "run samples timeout\n");
    
    	b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
    }
    
    
    Rafał Miłecki's avatar
    Rafał Miłecki committed
    /*
     * Transmits a known value for LO calibration
     * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone
     */
    static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val,
    				bool iqmode, bool dac_test)
    {
    	u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test);
    	if (samp == 0)
    		return -1;
    	b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test);
    	return 0;
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlCoefSetup */
    static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev)
    {
    	struct b43_phy_n *nphy = dev->phy.n;
    	int i, j;
    	u32 tmp;
    	u32 cur_real, cur_imag, real_part, imag_part;
    
    	u16 buffer[7];
    
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, true);
    
    
    	b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
    
    
    	for (i = 0; i < 2; i++) {
    		tmp = ((buffer[i * 2] & 0x3FF) << 10) |
    			(buffer[i * 2 + 1] & 0x3FF);
    		b43_phy_write(dev, B43_NPHY_TABLE_ADDR,
    				(((i + 26) << 10) | 320));
    		for (j = 0; j < 128; j++) {
    			b43_phy_write(dev, B43_NPHY_TABLE_DATAHI,
    					((tmp >> 16) & 0xFFFF));
    			b43_phy_write(dev, B43_NPHY_TABLE_DATALO,
    					(tmp & 0xFFFF));
    		}
    	}
    
    	for (i = 0; i < 2; i++) {
    		tmp = buffer[5 + i];
    		real_part = (tmp >> 8) & 0xFF;
    		imag_part = (tmp & 0xFF);
    		b43_phy_write(dev, B43_NPHY_TABLE_ADDR,
    				(((i + 26) << 10) | 448));
    
    		if (dev->phy.rev >= 3) {
    			cur_real = real_part;
    			cur_imag = imag_part;
    			tmp = ((cur_real & 0xFF) << 8) | (cur_imag & 0xFF);
    		}
    
    		for (j = 0; j < 128; j++) {
    			if (dev->phy.rev < 3) {
    				cur_real = (real_part * loscale[j] + 128) >> 8;
    				cur_imag = (imag_part * loscale[j] + 128) >> 8;
    				tmp = ((cur_real & 0xFF) << 8) |
    					(cur_imag & 0xFF);
    			}
    			b43_phy_write(dev, B43_NPHY_TABLE_DATAHI,
    					((tmp >> 16) & 0xFFFF));
    			b43_phy_write(dev, B43_NPHY_TABLE_DATALO,
    					(tmp & 0xFFFF));
    		}
    	}
    
    	if (dev->phy.rev >= 3) {
    		b43_shm_write16(dev, B43_SHM_SHARED,
    				B43_SHM_SH_NPHY_TXPWR_INDX0, 0xFFFF);
    		b43_shm_write16(dev, B43_SHM_SHARED,
    				B43_SHM_SH_NPHY_TXPWR_INDX1, 0xFFFF);
    	}
    
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, false);
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRfSeq */
    static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
    					u8 *events, u8 *delays, u8 length)
    {
    	struct b43_phy_n *nphy = dev->phy.n;
    	u8 i;
    	u8 end = (dev->phy.rev >= 3) ? 0x1F : 0x0F;
    	u16 offset1 = cmd << 4;
    	u16 offset2 = offset1 + 0x80;
    
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, true);
    
    	b43_ntab_write_bulk(dev, B43_NTAB8(7, offset1), length, events);
    	b43_ntab_write_bulk(dev, B43_NTAB8(7, offset2), length, delays);
    
    	for (i = length; i < 16; i++) {
    		b43_ntab_write(dev, B43_NTAB8(7, offset1 + i), end);
    		b43_ntab_write(dev, B43_NTAB8(7, offset2 + i), 1);
    	}
    
    	if (nphy->hang_avoid)
    		b43_nphy_stay_in_carrier_search(dev, false);
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ForceRFSeq */
    
    static void b43_nphy_force_rf_sequence(struct b43_wldev *dev,
    				       enum b43_nphy_rf_sequence seq)
    {
    	static const u16 trigger[] = {
    		[B43_RFSEQ_RX2TX]		= B43_NPHY_RFSEQTR_RX2TX,
    		[B43_RFSEQ_TX2RX]		= B43_NPHY_RFSEQTR_TX2RX,
    		[B43_RFSEQ_RESET2RX]		= B43_NPHY_RFSEQTR_RST2RX,
    		[B43_RFSEQ_UPDATE_GAINH]	= B43_NPHY_RFSEQTR_UPGH,
    		[B43_RFSEQ_UPDATE_GAINL]	= B43_NPHY_RFSEQTR_UPGL,
    		[B43_RFSEQ_UPDATE_GAINU]	= B43_NPHY_RFSEQTR_UPGU,
    	};
    	int i;
    
    	u16 seq_mode = b43_phy_read(dev, B43_NPHY_RFSEQMODE);
    
    
    	B43_WARN_ON(seq >= ARRAY_SIZE(trigger));
    
    	b43_phy_set(dev, B43_NPHY_RFSEQMODE,
    		    B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER);
    	b43_phy_set(dev, B43_NPHY_RFSEQTR, trigger[seq]);
    	for (i = 0; i < 200; i++) {
    		if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & trigger[seq]))
    			goto ok;
    		msleep(1);
    	}
    	b43err(dev->wl, "RF sequence status timeout\n");
    ok:
    
    	b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode);
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */
    static void b43_nphy_rf_control_override(struct b43_wldev *dev, u16 field,
    						u16 value, u8 core, bool off)
    {
    	int i;
    	u8 index = fls(field);
    	u8 addr, en_addr, val_addr;
    	/* we expect only one bit set */
    
    	B43_WARN_ON(field & (~(1 << (index - 1))));
    
    
    	if (dev->phy.rev >= 3) {
    		const struct nphy_rf_control_override_rev3 *rf_ctrl;
    		for (i = 0; i < 2; i++) {
    			if (index == 0 || index == 16) {
    				b43err(dev->wl,
    					"Unsupported RF Ctrl Override call\n");
    				return;
    			}
    
    			rf_ctrl = &tbl_rf_control_override_rev3[index - 1];
    			en_addr = B43_PHY_N((i == 0) ?
    				rf_ctrl->en_addr0 : rf_ctrl->en_addr1);
    			val_addr = B43_PHY_N((i == 0) ?
    				rf_ctrl->val_addr0 : rf_ctrl->val_addr1);
    
    			if (off) {
    				b43_phy_mask(dev, en_addr, ~(field));
    				b43_phy_mask(dev, val_addr,
    						~(rf_ctrl->val_mask));
    			} else {
    				if (core == 0 || ((1 << core) & i) != 0) {
    					b43_phy_set(dev, en_addr, field);
    					b43_phy_maskset(dev, val_addr,
    						~(rf_ctrl->val_mask),
    						(value << rf_ctrl->val_shift));
    				}
    			}
    		}
    	} else {
    		const struct nphy_rf_control_override_rev2 *rf_ctrl;
    		if (off) {
    			b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~(field));
    			value = 0;
    		} else {
    			b43_phy_set(dev, B43_NPHY_RFCTL_OVER, field);
    		}
    
    		for (i = 0; i < 2; i++) {
    			if (index <= 1 || index == 16) {
    				b43err(dev->wl,
    					"Unsupported RF Ctrl Override call\n");
    				return;
    			}
    
    			if (index == 2 || index == 10 ||
    			    (index >= 13 && index <= 15)) {
    				core = 1;
    			}
    
    			rf_ctrl = &tbl_rf_control_override_rev2[index - 2];
    			addr = B43_PHY_N((i == 0) ?
    				rf_ctrl->addr0 : rf_ctrl->addr1);
    
    			if ((core & (1 << i)) != 0)
    				b43_phy_maskset(dev, addr, ~(rf_ctrl->bmask),
    						(value << rf_ctrl->shift));
    
    			b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1);
    			b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
    					B43_NPHY_RFCTL_CMD_START);
    			udelay(1);
    			b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, 0xFFFE);
    		}
    	}
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */
    static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
    						u16 value, u8 core)
    {
    	u8 i, j;
    	u16 reg, tmp, val;
    
    	B43_WARN_ON(dev->phy.rev < 3);
    	B43_WARN_ON(field > 4);
    
    	for (i = 0; i < 2; i++) {
    		if ((core == 1 && i == 1) || (core == 2 && !i))
    			continue;
    
    		reg = (i == 0) ?
    			B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2;
    		b43_phy_mask(dev, reg, 0xFBFF);
    
    		switch (field) {
    		case 0:
    			b43_phy_write(dev, reg, 0);
    			b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
    			break;
    		case 1:
    			if (!i) {
    				b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC1,
    						0xFC3F, (value << 6));
    				b43_phy_maskset(dev, B43_NPHY_TXF_40CO_B1S1,
    						0xFFFE, 1);
    				b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
    						B43_NPHY_RFCTL_CMD_START);
    				for (j = 0; j < 100; j++) {
    					if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_START) {
    						j = 0;
    						break;
    					}
    					udelay(10);
    				}
    				if (j)
    					b43err(dev->wl,
    						"intc override timeout\n");
    				b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1,
    						0xFFFE);
    			} else {
    				b43_phy_maskset(dev, B43_NPHY_RFCTL_INTC2,
    						0xFC3F, (value << 6));
    				b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
    						0xFFFE, 1);
    				b43_phy_set(dev, B43_NPHY_RFCTL_CMD,
    						B43_NPHY_RFCTL_CMD_RXTX);
    				for (j = 0; j < 100; j++) {
    					if (b43_phy_read(dev, B43_NPHY_RFCTL_CMD) & B43_NPHY_RFCTL_CMD_RXTX) {
    						j = 0;
    						break;
    					}
    					udelay(10);
    				}
    				if (j)
    					b43err(dev->wl,
    						"intc override timeout\n");
    				b43_phy_mask(dev, B43_NPHY_RFCTL_OVER,
    						0xFFFE);
    			}
    			break;
    		case 2:
    			if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
    				tmp = 0x0020;
    				val = value << 5;
    			} else {
    				tmp = 0x0010;
    				val = value << 4;
    			}
    			b43_phy_maskset(dev, reg, ~tmp, val);
    			break;
    		case 3:
    			if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
    				tmp = 0x0001;
    				val = value;
    			} else {
    				tmp = 0x0004;
    				val = value << 2;
    			}
    			b43_phy_maskset(dev, reg, ~tmp, val);
    			break;
    		case 4:
    			if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
    				tmp = 0x0002;
    				val = value << 1;
    			} else {
    				tmp = 0x0008;
    				val = value << 3;
    			}
    			b43_phy_maskset(dev, reg, ~tmp, val);
    			break;
    		}
    	}
    }
    
    
    static void b43_nphy_bphy_init(struct b43_wldev *dev)
    {
    	unsigned int i;
    	u16 val;
    
    	val = 0x1E1F;
    	for (i = 0; i < 14; i++) {
    		b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val);
    		val -= 0x202;
    	}
    	val = 0x3E3F;
    	for (i = 0; i < 16; i++) {
    		b43_phy_write(dev, B43_PHY_N_BMODE(0x97 + i), val);
    		val -= 0x202;
    	}
    	b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668);
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ScaleOffsetRssi */
    static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale,
    				       s8 offset, u8 core, u8 rail, u8 type)
    {
    	u16 tmp;
    	bool core1or5 = (core == 1) || (core == 5);
    	bool core2or5 = (core == 2) || (core == 5);
    
    	offset = clamp_val(offset, -32, 31);
    	tmp = ((scale & 0x3F) << 8) | (offset & 0x3F);
    
    	if (core1or5 && (rail == 0) && (type == 2))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, tmp);
    	if (core1or5 && (rail == 1) && (type == 2))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, tmp);
    	if (core2or5 && (rail == 0) && (type == 2))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, tmp);
    	if (core2or5 && (rail == 1) && (type == 2))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, tmp);
    	if (core1or5 && (rail == 0) && (type == 0))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, tmp);
    	if (core1or5 && (rail == 1) && (type == 0))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, tmp);
    	if (core2or5 && (rail == 0) && (type == 0))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, tmp);
    	if (core2or5 && (rail == 1) && (type == 0))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, tmp);
    	if (core1or5 && (rail == 0) && (type == 1))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, tmp);
    	if (core1or5 && (rail == 1) && (type == 1))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, tmp);
    	if (core2or5 && (rail == 0) && (type == 1))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, tmp);
    	if (core2or5 && (rail == 1) && (type == 1))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, tmp);
    	if (core1or5 && (rail == 0) && (type == 6))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TBD, tmp);
    	if (core1or5 && (rail == 1) && (type == 6))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TBD, tmp);
    	if (core2or5 && (rail == 0) && (type == 6))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TBD, tmp);
    	if (core2or5 && (rail == 1) && (type == 6))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TBD, tmp);
    	if (core1or5 && (rail == 0) && (type == 3))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0I_PWRDET, tmp);
    	if (core1or5 && (rail == 1) && (type == 3))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_PWRDET, tmp);
    	if (core2or5 && (rail == 0) && (type == 3))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1I_PWRDET, tmp);
    	if (core2or5 && (rail == 1) && (type == 3))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_PWRDET, tmp);
    	if (core1or5 && (type == 4))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TSSI, tmp);
    	if (core2or5 && (type == 4))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TSSI, tmp);
    	if (core1or5 && (type == 5))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TSSI, tmp);
    	if (core2or5 && (type == 5))
    		b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TSSI, tmp);
    }
    
    
    static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
    
    	if (type < 3)
    		val = 0;
    	else if (type == 6)
    		val = 1;
    	else if (type == 3)
    		val = 2;
    	else
    		val = 3;
    
    	val = (val << 12) | (val << 14);
    	b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val);
    	b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val);
    
    	if (type < 3) {
    		b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF,
    				(type + 1) << 4);
    		b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF,
    				(type + 1) << 4);
    	}
    
    	/* TODO use some definitions */
    	if (code == 0) {
    		b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF, 0);
    
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFEC7, 0);
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xEFDC, 0);
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFFFE, 0);
    			udelay(20);
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xFFFE, 0);
    
    	} else {
    		b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF,
    				0x3000);
    		if (type < 3) {
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD,
    					0xFEC7, 0x0180);
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER,
    					0xEFDC, (code << 1 | 0x1021));
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, 0xFFFE, 0x1);
    			udelay(20);
    			b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, 0xFFFE, 0);
    
    static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
    {
    
    	struct b43_phy_n *nphy = dev->phy.n;
    	u8 i;
    	u16 reg, val;
    
    	if (code == 0) {
    		b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, 0xFDFF);
    		b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, 0xFDFF);
    		b43_phy_mask(dev, B43_NPHY_AFECTL_C1, 0xFCFF);
    		b43_phy_mask(dev, B43_NPHY_AFECTL_C2, 0xFCFF);
    		b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S0, 0xFFDF);
    		b43_phy_mask(dev, B43_NPHY_TXF_40CO_B32S1, 0xFFDF);
    		b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0xFFC3);
    		b43_phy_mask(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0xFFC3);
    	} else {
    		for (i = 0; i < 2; i++) {
    			if ((code == 1 && i == 1) || (code == 2 && !i))
    				continue;
    
    			reg = (i == 0) ?
    				B43_NPHY_AFECTL_OVER1 : B43_NPHY_AFECTL_OVER;
    			b43_phy_maskset(dev, reg, 0xFDFF, 0x0200);
    
    			if (type < 3) {
    				reg = (i == 0) ?
    					B43_NPHY_AFECTL_C1 :
    					B43_NPHY_AFECTL_C2;
    				b43_phy_maskset(dev, reg, 0xFCFF, 0);
    
    				reg = (i == 0) ?
    					B43_NPHY_RFCTL_LUT_TRSW_UP1 :
    					B43_NPHY_RFCTL_LUT_TRSW_UP2;
    				b43_phy_maskset(dev, reg, 0xFFC3, 0);
    
    				if (type == 0)
    					val = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? 4 : 8;
    				else if (type == 1)
    					val = 16;
    				else
    					val = 32;
    				b43_phy_set(dev, reg, val);
    
    				reg = (i == 0) ?
    					B43_NPHY_TXF_40CO_B1S0 :
    					B43_NPHY_TXF_40CO_B32S1;
    				b43_phy_set(dev, reg, 0x0020);
    			} else {
    				if (type == 6)
    					val = 0x0100;
    				else if (type == 3)
    					val = 0x0200;
    				else
    					val = 0x0300;
    
    				reg = (i == 0) ?
    					B43_NPHY_AFECTL_C1 :
    					B43_NPHY_AFECTL_C2;
    
    				b43_phy_maskset(dev, reg, 0xFCFF, val);
    				b43_phy_maskset(dev, reg, 0xF3FF, val << 2);
    
    				if (type != 3 && type != 6) {
    					enum ieee80211_band band =
    						b43_current_band(dev->wl);
    
    					if ((nphy->ipa2g_on &&
    						band == IEEE80211_BAND_2GHZ) ||
    						(nphy->ipa5g_on &&
    						band == IEEE80211_BAND_5GHZ))
    						val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
    					else
    						val = 0x11;
    					reg = (i == 0) ? 0x2000 : 0x3000;
    					reg |= B2055_PADDRV;
    					b43_radio_write16(dev, reg, val);
    
    					reg = (i == 0) ?
    						B43_NPHY_AFECTL_OVER1 :
    						B43_NPHY_AFECTL_OVER;
    					b43_phy_set(dev, reg, 0x0200);
    				}
    			}
    		}
    	}
    
    }
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */
    static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
    {
    	if (dev->phy.rev >= 3)
    		b43_nphy_rev3_rssi_select(dev, code, type);
    	else
    		b43_nphy_rev2_rssi_select(dev, code, type);
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRssi2055Vcm */
    static void b43_nphy_set_rssi_2055_vcm(struct b43_wldev *dev, u8 type, u8 *buf)
    {
    	int i;
    	for (i = 0; i < 2; i++) {
    		if (type == 2) {
    			if (i == 0) {
    				b43_radio_maskset(dev, B2055_C1_B0NB_RSSIVCM,
    						  0xFC, buf[0]);
    				b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5,
    						  0xFC, buf[1]);
    			} else {
    				b43_radio_maskset(dev, B2055_C2_B0NB_RSSIVCM,
    						  0xFC, buf[2 * i]);
    				b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5,
    						  0xFC, buf[2 * i + 1]);
    			}
    		} else {
    			if (i == 0)
    				b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5,
    						  0xF3, buf[0] << 2);
    			else
    				b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5,
    						  0xF3, buf[2 * i + 1] << 2);
    		}
    	}
    }
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PollRssi */
    static int b43_nphy_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf,
    				u8 nsamp)
    {
    	int i;
    	int out;
    	u16 save_regs_phy[9];
    	u16 s[2];
    
    	if (dev->phy.rev >= 3) {
    		save_regs_phy[0] = b43_phy_read(dev,
    						B43_NPHY_RFCTL_LUT_TRSW_UP1);
    		save_regs_phy[1] = b43_phy_read(dev,
    						B43_NPHY_RFCTL_LUT_TRSW_UP2);
    		save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_C1);
    		save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_AFECTL_C2);
    		save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1);
    		save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER);
    		save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0);
    		save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1);
    	}
    
    	b43_nphy_rssi_select(dev, 5, type);
    
    	if (dev->phy.rev < 2) {
    		save_regs_phy[8] = b43_phy_read(dev, B43_NPHY_GPIO_SEL);
    		b43_phy_write(dev, B43_NPHY_GPIO_SEL, 5);
    	}
    
    	for (i = 0; i < 4; i++)
    		buf[i] = 0;
    
    	for (i = 0; i < nsamp; i++) {
    		if (dev->phy.rev < 2) {
    			s[0] = b43_phy_read(dev, B43_NPHY_GPIO_LOOUT);
    			s[1] = b43_phy_read(dev, B43_NPHY_GPIO_HIOUT);
    		} else {
    			s[0] = b43_phy_read(dev, B43_NPHY_RSSI1);
    			s[1] = b43_phy_read(dev, B43_NPHY_RSSI2);
    		}
    
    		buf[0] += ((s8)((s[0] & 0x3F) << 2)) >> 2;
    		buf[1] += ((s8)(((s[0] >> 8) & 0x3F) << 2)) >> 2;
    		buf[2] += ((s8)((s[1] & 0x3F) << 2)) >> 2;
    		buf[3] += ((s8)(((s[1] >> 8) & 0x3F) << 2)) >> 2;
    	}
    	out = (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 |
    		(buf[2] & 0xFF) << 8 | (buf[3] & 0xFF);
    
    	if (dev->phy.rev < 2)
    		b43_phy_write(dev, B43_NPHY_GPIO_SEL, save_regs_phy[8]);
    
    	if (dev->phy.rev >= 3) {
    		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1,
    				save_regs_phy[0]);
    		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2,
    				save_regs_phy[1]);
    		b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[2]);
    		b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[3]);
    		b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, save_regs_phy[4]);
    		b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]);
    		b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]);
    		b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]);
    	}
    
    	return out;
    }
    
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */
    static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, u8 type)
    
    	int i, j;
    	u8 state[4];
    	u8 code, val;
    	u16 class, override;
    	u8 regs_save_radio[2];
    	u16 regs_save_phy[2];
    	s8 offset[4];
    
    	u16 clip_state[2];
    	u16 clip_off[2] = { 0xFFFF, 0xFFFF };
    	s32 results_min[4] = { };
    	u8 vcm_final[4] = { };
    	s32 results[4][4] = { };
    	s32 miniq[4][2] = { };
    
    	if (type == 2) {
    		code = 0;
    		val = 6;
    	} else if (type < 2) {
    		code = 25;
    		val = 4;
    	} else {
    		B43_WARN_ON(1);
    		return;
    	}
    
    	class = b43_nphy_classifier(dev, 0, 0);
    	b43_nphy_classifier(dev, 7, 4);
    	b43_nphy_read_clip_detection(dev, clip_state);
    	b43_nphy_write_clip_detection(dev, clip_off);
    
    	if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
    		override = 0x140;
    	else
    		override = 0x110;
    
    	regs_save_phy[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1);
    	regs_save_radio[0] = b43_radio_read16(dev, B2055_C1_PD_RXTX);
    	b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, override);
    	b43_radio_write16(dev, B2055_C1_PD_RXTX, val);
    
    	regs_save_phy[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2);
    	regs_save_radio[1] = b43_radio_read16(dev, B2055_C2_PD_RXTX);
    	b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, override);
    	b43_radio_write16(dev, B2055_C2_PD_RXTX, val);
    
    	state[0] = b43_radio_read16(dev, B2055_C1_PD_RSSIMISC) & 0x07;
    	state[1] = b43_radio_read16(dev, B2055_C2_PD_RSSIMISC) & 0x07;
    	b43_radio_mask(dev, B2055_C1_PD_RSSIMISC, 0xF8);
    	b43_radio_mask(dev, B2055_C2_PD_RSSIMISC, 0xF8);
    	state[2] = b43_radio_read16(dev, B2055_C1_SP_RSSI) & 0x07;
    	state[3] = b43_radio_read16(dev, B2055_C2_SP_RSSI) & 0x07;
    
    	b43_nphy_rssi_select(dev, 5, type);
    	b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 0, type);
    	b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 1, type);
    
    	for (i = 0; i < 4; i++) {
    		u8 tmp[4];
    		for (j = 0; j < 4; j++)
    			tmp[j] = i;
    		if (type != 1)
    			b43_nphy_set_rssi_2055_vcm(dev, type, tmp);
    		b43_nphy_poll_rssi(dev, type, results[i], 8);
    		if (type < 2)
    			for (j = 0; j < 2; j++)
    				miniq[i][j] = min(results[i][2 * j],
    						results[i][2 * j + 1]);
    	}
    
    	for (i = 0; i < 4; i++) {
    		s32 mind = 40;
    		u8 minvcm = 0;
    		s32 minpoll = 249;
    		s32 curr;
    		for (j = 0; j < 4; j++) {
    			if (type == 2)
    				curr = abs(results[j][i]);
    			else
    				curr = abs(miniq[j][i / 2] - code * 8);
    
    			if (curr < mind) {
    				mind = curr;
    				minvcm = j;
    			}
    
    			if (results[j][i] < minpoll)
    				minpoll = results[j][i];
    		}
    		results_min[i] = minpoll;
    		vcm_final[i] = minvcm;
    	}
    
    	if (type != 1)
    		b43_nphy_set_rssi_2055_vcm(dev, type, vcm_final);
    
    	for (i = 0; i < 4; i++) {
    		offset[i] = (code * 8) - results[vcm_final[i]][i];
    
    		if (offset[i] < 0)
    			offset[i] = -((abs(offset[i]) + 4) / 8);
    		else
    			offset[i] = (offset[i] + 4) / 8;
    
    		if (results_min[i] == 248)
    			offset[i] = code - 32;
    
    		if (i % 2 == 0)
    			b43_nphy_scale_offset_rssi(dev, 0, offset[i], 1, 0,
    							type);
    		else
    			b43_nphy_scale_offset_rssi(dev, 0, offset[i], 2, 1,
    							type);
    	}
    
    	b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[0]);
    	b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[1]);
    
    	switch (state[2]) {
    	case 1:
    		b43_nphy_rssi_select(dev, 1, 2);
    		break;
    	case 4:
    		b43_nphy_rssi_select(dev, 1, 0);
    		break;
    	case 2:
    		b43_nphy_rssi_select(dev, 1, 1);
    		break;
    	default:
    		b43_nphy_rssi_select(dev, 1, 1);
    		break;
    	}
    
    	switch (state[3]) {
    	case 1:
    		b43_nphy_rssi_select(dev, 2, 2);
    		break;
    	case 4:
    		b43_nphy_rssi_select(dev, 2, 0);
    		break;
    	default:
    		b43_nphy_rssi_select(dev, 2, 1);
    		break;
    	}
    
    	b43_nphy_rssi_select(dev, 0, type);
    
    	b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs_save_phy[0]);
    	b43_radio_write16(dev, B2055_C1_PD_RXTX, regs_save_radio[0]);
    	b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs_save_phy[1]);
    	b43_radio_write16(dev, B2055_C2_PD_RXTX, regs_save_radio[1]);
    
    	b43_nphy_classifier(dev, 7, class);
    	b43_nphy_write_clip_detection(dev, clip_state);
    
    }
    
    /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */
    static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
    {
    	/* TODO */
    }
    
    /*
     * RSSI Calibration
     * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal
     */
    static void b43_nphy_rssi_cal(struct b43_wldev *dev)
    {
    	if (dev->phy.rev >= 3) {
    		b43_nphy_rev3_rssi_cal(dev);
    	} else {
    		b43_nphy_rev2_rssi_cal(dev, 2);
    		b43_nphy_rev2_rssi_cal(dev, 0);
    		b43_nphy_rev2_rssi_cal(dev, 1);
    	}
    
    /*
     * Restore RSSI Calibration
     * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreRssiCal
     */