1e4d6b7951812d98417feb10784e400e253caf633Michael Buesch/*
2e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
3e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  Broadcom B43 wireless driver
4e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
5e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  G PHY LO (LocalOscillator) Measuring and Control routines
6e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
7e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
81f21ad2a4f7f66855dae600ddd635ff5fb299bbdStefano Brivio  Copyright (c) 2005, 2006 Stefano Brivio <stefano.brivio@polimi.it>
9eb032b9837a958e21ca000358a5bde5e17192ddbMichael Buesch  Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
10e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org>
11e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch>
12e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
13e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  This program is free software; you can redistribute it and/or modify
14e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  it under the terms of the GNU General Public License as published by
15e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  the Free Software Foundation; either version 2 of the License, or
16e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  (at your option) any later version.
17e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
18e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  This program is distributed in the hope that it will be useful,
19e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  but WITHOUT ANY WARRANTY; without even the implied warranty of
20e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  GNU General Public License for more details.
22e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
23e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  You should have received a copy of the GNU General Public License
24e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  along with this program; see the file COPYING.  If not, write to
25e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
26e4d6b7951812d98417feb10784e400e253caf633Michael Buesch  Boston, MA 02110-1301, USA.
27e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
28e4d6b7951812d98417feb10784e400e253caf633Michael Buesch*/
29e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
30e4d6b7951812d98417feb10784e400e253caf633Michael Buesch#include "b43.h"
31e4d6b7951812d98417feb10784e400e253caf633Michael Buesch#include "lo.h"
32ef1a628d83fc0423c36e773281162be790503168Michael Buesch#include "phy_g.h"
33e4d6b7951812d98417feb10784e400e253caf633Michael Buesch#include "main.h"
34e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
35e4d6b7951812d98417feb10784e400e253caf633Michael Buesch#include <linux/delay.h>
36e4d6b7951812d98417feb10784e400e253caf633Michael Buesch#include <linux/sched.h>
375a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
38e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
39e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
4099da185a72ba685a5aaf49dff6a5fe83885112e4John Daikerstatic struct b43_lo_calib *b43_find_lo_calib(struct b43_txpower_lo_control *lo,
4199da185a72ba685a5aaf49dff6a5fe83885112e4John Daiker					      const struct b43_bbatt *bbatt,
42f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch					       const struct b43_rfatt *rfatt)
43f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch{
44f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_lo_calib *c;
45f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
46f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	list_for_each_entry(c, &lo->calib_list, list) {
47f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (!b43_compare_bbatt(&c->bbatt, bbatt))
48f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			continue;
49f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (!b43_compare_rfatt(&c->rfatt, rfatt))
50f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			continue;
51f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return c;
52f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	}
53e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
54f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	return NULL;
55f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch}
56e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
57e4d6b7951812d98417feb10784e400e253caf633Michael Buesch/* Write the LocalOscillator Control (adjust) value-pair. */
58e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic void b43_lo_write(struct b43_wldev *dev, struct b43_loctl *control)
59e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
60e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
61e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 value;
62e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
63e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (B43_DEBUG) {
64e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (unlikely(abs(control->i) > 16 || abs(control->q) > 16)) {
65e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			b43dbg(dev->wl, "Invalid LO control pair "
66e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			       "(I: %d, Q: %d)\n", control->i, control->q);
67e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			dump_stack();
68e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			return;
69e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
70e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
71f4440e8a47e216adfe32d96a35bef6d13d498b58Michael Buesch	B43_WARN_ON(phy->type != B43_PHYTYPE_G);
72e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
73e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	value = (u8) (control->q);
74e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	value |= ((u8) (control->i)) << 8;
75f4440e8a47e216adfe32d96a35bef6d13d498b58Michael Buesch	b43_phy_write(dev, B43_PHY_LO_CTL, value);
76e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
77e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
78e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic u16 lo_measure_feedthrough(struct b43_wldev *dev,
79e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				  u16 lna, u16 pga, u16 trsw_rx)
80e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
81e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
82e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 rfover;
83e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 feedthrough;
84e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
85e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->gmode) {
86e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		lna <<= B43_PHY_RFOVERVAL_LNA_SHIFT;
87e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		pga <<= B43_PHY_RFOVERVAL_PGA_SHIFT;
88e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
89e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		B43_WARN_ON(lna & ~B43_PHY_RFOVERVAL_LNA);
90e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		B43_WARN_ON(pga & ~B43_PHY_RFOVERVAL_PGA);
91e4d6b7951812d98417feb10784e400e253caf633Michael Buesch/*FIXME This assertion fails		B43_WARN_ON(trsw_rx & ~(B43_PHY_RFOVERVAL_TRSWRX |
92e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				    B43_PHY_RFOVERVAL_BW));
93e4d6b7951812d98417feb10784e400e253caf633Michael Buesch*/
94e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		trsw_rx &= (B43_PHY_RFOVERVAL_TRSWRX | B43_PHY_RFOVERVAL_BW);
95e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
96e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		/* Construct the RF Override Value */
97e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		rfover = B43_PHY_RFOVERVAL_UNK;
98e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		rfover |= pga;
99e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		rfover |= lna;
100e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		rfover |= trsw_rx;
1010581483afe1a6f90f828b36111b05a70c162137dRafał Miłecki		if ((dev->dev->bus_sprom->boardflags_lo & B43_BFL_EXTLNA)
10295de2841aad971867851b59c0c5253ecc2e19832Larry Finger		    && phy->rev > 6)
103e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			rfover |= B43_PHY_RFOVERVAL_EXTLNA;
104e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
105e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_PGACTL, 0xE300);
106e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover);
107e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		udelay(10);
108e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		rfover |= B43_PHY_RFOVERVAL_BW_LBW;
109e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover);
110e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		udelay(10);
111e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		rfover |= B43_PHY_RFOVERVAL_BW_LPF;
112e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVERVAL, rfover);
113e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		udelay(10);
114e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_PGACTL, 0xF300);
115e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
116e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		pga |= B43_PHY_PGACTL_UNKNOWN;
117e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_PGACTL, pga);
118e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		udelay(10);
119e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		pga |= B43_PHY_PGACTL_LOWBANDW;
120e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_PGACTL, pga);
121e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		udelay(10);
122e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		pga |= B43_PHY_PGACTL_LPF;
123e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_PGACTL, pga);
124e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
125e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	udelay(21);
126e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	feedthrough = b43_phy_read(dev, B43_PHY_LO_LEAKAGE);
127e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
128e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	/* This is a good place to check if we need to relax a bit,
129e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	 * as this is the main function called regularly
130e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	 * in the LO calibration. */
131e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	cond_resched();
132e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
133e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	return feedthrough;
134e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
135e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
136e4d6b7951812d98417feb10784e400e253caf633Michael Buesch/* TXCTL Register and Value Table.
137e4d6b7951812d98417feb10784e400e253caf633Michael Buesch * Returns the "TXCTL Register".
138e4d6b7951812d98417feb10784e400e253caf633Michael Buesch * "value" is the "TXCTL Value".
139e4d6b7951812d98417feb10784e400e253caf633Michael Buesch * "pad_mix_gain" is the PAD Mixer Gain.
140e4d6b7951812d98417feb10784e400e253caf633Michael Buesch */
141e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic u16 lo_txctl_register_table(struct b43_wldev *dev,
14299da185a72ba685a5aaf49dff6a5fe83885112e4John Daiker				   u16 *value, u16 *pad_mix_gain)
143e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
144e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
145e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 reg, v, padmix;
146e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
147e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_B) {
148e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		v = 0x30;
149e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (phy->radio_rev <= 5) {
150e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			reg = 0x43;
151e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			padmix = 0;
152e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else {
153e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			reg = 0x52;
154e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			padmix = 5;
155e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
156e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
157e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (phy->rev >= 2 && phy->radio_rev == 8) {
158e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			reg = 0x43;
159e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			v = 0x10;
160e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			padmix = 2;
161e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else {
162e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			reg = 0x52;
163e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			v = 0x30;
164e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			padmix = 5;
165e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
166e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
167e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (value)
168e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		*value = v;
169e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (pad_mix_gain)
170e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		*pad_mix_gain = padmix;
171e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
172e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	return reg;
173e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
174e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
175e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic void lo_measure_txctl_values(struct b43_wldev *dev)
176e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
177e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
178ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
179ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_txpower_lo_control *lo = gphy->lo_control;
180e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 reg, mask;
181e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 trsw_rx, pga;
182e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 radio_pctl_reg;
183e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
184e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	static const u8 tx_bias_values[] = {
185e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		0x09, 0x08, 0x0A, 0x01, 0x00,
186e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		0x02, 0x05, 0x04, 0x06,
187e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	};
188e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	static const u8 tx_magn_values[] = {
189e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		0x70, 0x40,
190e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	};
191e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
192e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (!has_loopback_gain(phy)) {
193e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		radio_pctl_reg = 6;
194e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		trsw_rx = 2;
195e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		pga = 0;
196e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
197e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		int lb_gain;	/* Loopback gain (in dB) */
198e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
199e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		trsw_rx = 0;
200ef1a628d83fc0423c36e773281162be790503168Michael Buesch		lb_gain = gphy->max_lb_gain / 2;
201e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (lb_gain > 10) {
202e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			radio_pctl_reg = 0;
203e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			pga = abs(10 - lb_gain) / 6;
204cdbf0846e2dd5f122f3910d0e2a305fab337744bHarvey Harrison			pga = clamp_val(pga, 0, 15);
205e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else {
206e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			int cmp_val;
207e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			int tmp;
208e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
209e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			pga = 0;
210e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			cmp_val = 0x24;
211e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if ((phy->rev >= 2) &&
212e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			    (phy->radio_ver == 0x2050) && (phy->radio_rev == 8))
213e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				cmp_val = 0x3C;
214e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			tmp = lb_gain;
215e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if ((10 - lb_gain) < cmp_val)
216e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				tmp = (10 - lb_gain);
217e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (tmp < 0)
218e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				tmp += 6;
219e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			else
220e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				tmp += 3;
221e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			cmp_val /= 4;
222e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			tmp /= 4;
223e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (tmp >= cmp_val)
224e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				radio_pctl_reg = cmp_val;
225e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			else
226e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				radio_pctl_reg = tmp;
227e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
228e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
2295f9724dd94d63e26edb02d9f6a4ce1ce35737f14Michael Buesch	b43_radio_maskset(dev, 0x43, 0xFFF0, radio_pctl_reg);
230ef1a628d83fc0423c36e773281162be790503168Michael Buesch	b43_gphy_set_baseband_attenuation(dev, 2);
231e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
232e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	reg = lo_txctl_register_table(dev, &mask, NULL);
233e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	mask = ~mask;
2343718582a663e035af5d2634cd537a012e20cdb3fMichael Buesch	b43_radio_mask(dev, reg, mask);
235e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
236e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (has_tx_magnification(phy)) {
237e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		int i, j;
238e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		int feedthrough;
239e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		int min_feedth = 0xFFFF;
240e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		u8 tx_magn, tx_bias;
241e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
242e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		for (i = 0; i < ARRAY_SIZE(tx_magn_values); i++) {
243e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			tx_magn = tx_magn_values[i];
2445f9724dd94d63e26edb02d9f6a4ce1ce35737f14Michael Buesch			b43_radio_maskset(dev, 0x52, 0xFF0F, tx_magn);
245e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			for (j = 0; j < ARRAY_SIZE(tx_bias_values); j++) {
246e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				tx_bias = tx_bias_values[j];
2475f9724dd94d63e26edb02d9f6a4ce1ce35737f14Michael Buesch				b43_radio_maskset(dev, 0x52, 0xFFF0, tx_bias);
248e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				feedthrough =
249e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				    lo_measure_feedthrough(dev, 0, pga,
250e4d6b7951812d98417feb10784e400e253caf633Michael Buesch							   trsw_rx);
251e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				if (feedthrough < min_feedth) {
252e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					lo->tx_bias = tx_bias;
253e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					lo->tx_magn = tx_magn;
254e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					min_feedth = feedthrough;
255e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				}
256e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				if (lo->tx_bias == 0)
257e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					break;
258e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			}
259e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			b43_radio_write16(dev, 0x52,
260e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					  (b43_radio_read16(dev, 0x52)
261e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					   & 0xFF00) | lo->tx_bias | lo->
262e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					  tx_magn);
263e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
264e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
265e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		lo->tx_magn = 0;
266e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		lo->tx_bias = 0;
2673718582a663e035af5d2634cd537a012e20cdb3fMichael Buesch		b43_radio_mask(dev, 0x52, 0xFFF0);	/* TX bias == 0 */
268e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
269f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	lo->txctl_measured_time = jiffies;
270e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
271e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
272e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic void lo_read_power_vector(struct b43_wldev *dev)
273e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
274e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
275ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
276ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_txpower_lo_control *lo = gphy->lo_control;
277f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	int i;
278e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u64 tmp;
279e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u64 power_vector = 0;
280e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
281e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	for (i = 0; i < 8; i += 2) {
282e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		tmp = b43_shm_read16(dev, B43_SHM_SHARED, 0x310 + i);
283e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		power_vector |= (tmp << (i * 8));
284e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		/* Clear the vector on the device. */
285e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_shm_write16(dev, B43_SHM_SHARED, 0x310 + i, 0);
286e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
287e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (power_vector)
288e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		lo->power_vector = power_vector;
289f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	lo->pwr_vec_read_time = jiffies;
290e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
291e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
292e4d6b7951812d98417feb10784e400e253caf633Michael Buesch/* 802.11/LO/GPHY/MeasuringGains */
293e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic void lo_measure_gain_values(struct b43_wldev *dev,
294e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				   s16 max_rx_gain, int use_trsw_rx)
295e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
296e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
297ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
298e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 tmp;
299e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
300e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (max_rx_gain < 0)
301e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		max_rx_gain = 0;
302e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
303e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (has_loopback_gain(phy)) {
304e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		int trsw_rx_gain;
305e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
306e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (use_trsw_rx) {
307ef1a628d83fc0423c36e773281162be790503168Michael Buesch			trsw_rx_gain = gphy->trsw_rx_gain / 2;
308e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (max_rx_gain >= trsw_rx_gain) {
309e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				trsw_rx_gain = max_rx_gain - trsw_rx_gain;
310e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			}
311e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else
312e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			trsw_rx_gain = max_rx_gain;
313e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (trsw_rx_gain < 9) {
314ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_lod_gain = 0;
315e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else {
316ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_lod_gain = 1;
317e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			trsw_rx_gain -= 8;
318e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
319cdbf0846e2dd5f122f3910d0e2a305fab337744bHarvey Harrison		trsw_rx_gain = clamp_val(trsw_rx_gain, 0, 0x2D);
320ef1a628d83fc0423c36e773281162be790503168Michael Buesch		gphy->pga_gain = trsw_rx_gain / 3;
321ef1a628d83fc0423c36e773281162be790503168Michael Buesch		if (gphy->pga_gain >= 5) {
322ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->pga_gain -= 5;
323ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_gain = 2;
324e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else
325ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_gain = 0;
326e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
327ef1a628d83fc0423c36e773281162be790503168Michael Buesch		gphy->lna_gain = 0;
328ef1a628d83fc0423c36e773281162be790503168Michael Buesch		gphy->trsw_rx_gain = 0x20;
329e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (max_rx_gain >= 0x14) {
330ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_lod_gain = 1;
331ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->pga_gain = 2;
332e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else if (max_rx_gain >= 0x12) {
333ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_lod_gain = 1;
334ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->pga_gain = 1;
335e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else if (max_rx_gain >= 0xF) {
336ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_lod_gain = 1;
337ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->pga_gain = 0;
338e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else {
339ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->lna_lod_gain = 0;
340ef1a628d83fc0423c36e773281162be790503168Michael Buesch			gphy->pga_gain = 0;
341e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
342e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
343e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
344e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	tmp = b43_radio_read16(dev, 0x7A);
345ef1a628d83fc0423c36e773281162be790503168Michael Buesch	if (gphy->lna_lod_gain == 0)
346e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		tmp &= ~0x0008;
347e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	else
348e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		tmp |= 0x0008;
349e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_radio_write16(dev, 0x7A, tmp);
350e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
351e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
352e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstruct lo_g_saved_values {
353e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u8 old_channel;
354e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
355e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	/* Core registers */
356e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 reg_3F4;
357e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 reg_3E2;
358e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
359e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	/* PHY registers */
360e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_lo_mask;
361e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_extg_01;
362e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_dacctl_hwpctl;
363e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_dacctl;
3645250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	u16 phy_cck_14;
365e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_hpwr_tssictl;
366e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_analogover;
367e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_analogoverval;
368e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_rfover;
369e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_rfoverval;
370e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_classctl;
3715250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	u16 phy_cck_3E;
372e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_crs0;
373e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_pgactl;
3745250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	u16 phy_cck_2A;
375e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 phy_syncctl;
3765250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	u16 phy_cck_30;
3775250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	u16 phy_cck_06;
378e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
379e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	/* Radio registers */
380e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 radio_43;
381e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 radio_7A;
382e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 radio_52;
383e4d6b7951812d98417feb10784e400e253caf633Michael Buesch};
384e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
385e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic void lo_measure_setup(struct b43_wldev *dev,
386e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			     struct lo_g_saved_values *sav)
387e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
3880581483afe1a6f90f828b36111b05a70c162137dRafał Miłecki	struct ssb_sprom *sprom = dev->dev->bus_sprom;
389e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
390ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
391ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_txpower_lo_control *lo = gphy->lo_control;
392e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 tmp;
393e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
394ef1a628d83fc0423c36e773281162be790503168Michael Buesch	if (b43_has_hardware_pctl(dev)) {
395e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_lo_mask = b43_phy_read(dev, B43_PHY_LO_MASK);
396e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_extg_01 = b43_phy_read(dev, B43_PHY_EXTG(0x01));
397e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_dacctl_hwpctl = b43_phy_read(dev, B43_PHY_DACCTL);
3985250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		sav->phy_cck_14 = b43_phy_read(dev, B43_PHY_CCK(0x14));
399e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_hpwr_tssictl = b43_phy_read(dev, B43_PHY_HPWR_TSSICTL);
400e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
401e59be0b5299ce327d67cfca737b839ef98e0da0eMichael Buesch		b43_phy_set(dev, B43_PHY_HPWR_TSSICTL, 0x100);
402e59be0b5299ce327d67cfca737b839ef98e0da0eMichael Buesch		b43_phy_set(dev, B43_PHY_EXTG(0x01), 0x40);
403e59be0b5299ce327d67cfca737b839ef98e0da0eMichael Buesch		b43_phy_set(dev, B43_PHY_DACCTL, 0x40);
404e59be0b5299ce327d67cfca737b839ef98e0da0eMichael Buesch		b43_phy_set(dev, B43_PHY_CCK(0x14), 0x200);
405e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
406e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_B &&
407e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	    phy->radio_ver == 0x2050 && phy->radio_rev < 6) {
4085250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x16), 0x410);
4095250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x17), 0x820);
410e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
411e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->rev >= 2) {
412e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_analogover = b43_phy_read(dev, B43_PHY_ANALOGOVER);
413e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_analogoverval =
414e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		    b43_phy_read(dev, B43_PHY_ANALOGOVERVAL);
415e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_rfover = b43_phy_read(dev, B43_PHY_RFOVER);
416e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_rfoverval = b43_phy_read(dev, B43_PHY_RFOVERVAL);
417e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_classctl = b43_phy_read(dev, B43_PHY_CLASSCTL);
4185250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		sav->phy_cck_3E = b43_phy_read(dev, B43_PHY_CCK(0x3E));
419e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->phy_crs0 = b43_phy_read(dev, B43_PHY_CRS0);
420e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
421ac1ea3959f4c6694e92fe18a2ec72cfbed0c71faMichael Buesch		b43_phy_mask(dev, B43_PHY_CLASSCTL, 0xFFFC);
422ac1ea3959f4c6694e92fe18a2ec72cfbed0c71faMichael Buesch		b43_phy_mask(dev, B43_PHY_CRS0, 0x7FFF);
423e59be0b5299ce327d67cfca737b839ef98e0da0eMichael Buesch		b43_phy_set(dev, B43_PHY_ANALOGOVER, 0x0003);
424ac1ea3959f4c6694e92fe18a2ec72cfbed0c71faMichael Buesch		b43_phy_mask(dev, B43_PHY_ANALOGOVERVAL, 0xFFFC);
425e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (phy->type == B43_PHYTYPE_G) {
426e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if ((phy->rev >= 7) &&
42795de2841aad971867851b59c0c5253ecc2e19832Larry Finger			    (sprom->boardflags_lo & B43_BFL_EXTLNA)) {
428e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				b43_phy_write(dev, B43_PHY_RFOVER, 0x933);
429e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			} else {
430e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				b43_phy_write(dev, B43_PHY_RFOVER, 0x133);
431e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			}
432e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} else {
433e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			b43_phy_write(dev, B43_PHY_RFOVER, 0);
434e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
4355250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x3E), 0);
436e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
437e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	sav->reg_3F4 = b43_read16(dev, 0x3F4);
438e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	sav->reg_3E2 = b43_read16(dev, 0x3E2);
439e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	sav->radio_43 = b43_radio_read16(dev, 0x43);
440e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	sav->radio_7A = b43_radio_read16(dev, 0x7A);
441e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	sav->phy_pgactl = b43_phy_read(dev, B43_PHY_PGACTL);
4425250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	sav->phy_cck_2A = b43_phy_read(dev, B43_PHY_CCK(0x2A));
443e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	sav->phy_syncctl = b43_phy_read(dev, B43_PHY_SYNCCTL);
444e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	sav->phy_dacctl = b43_phy_read(dev, B43_PHY_DACCTL);
445e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
446e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (!has_tx_magnification(phy)) {
447e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->radio_52 = b43_radio_read16(dev, 0x52);
448e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		sav->radio_52 &= 0x00F0;
449e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
450e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_B) {
4515250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		sav->phy_cck_30 = b43_phy_read(dev, B43_PHY_CCK(0x30));
4525250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		sav->phy_cck_06 = b43_phy_read(dev, B43_PHY_CCK(0x06));
4535250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x30), 0x00FF);
4545250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x06), 0x3F3F);
455e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
456e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_write16(dev, 0x3E2, b43_read16(dev, 0x3E2)
457e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			    | 0x8000);
458e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
459e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_write16(dev, 0x3F4, b43_read16(dev, 0x3F4)
460e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		    & 0xF000);
461e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
462e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	tmp =
4635250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	    (phy->type == B43_PHYTYPE_G) ? B43_PHY_LO_MASK : B43_PHY_CCK(0x2E);
464e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_phy_write(dev, tmp, 0x007F);
465e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
466e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	tmp = sav->phy_syncctl;
467e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_phy_write(dev, B43_PHY_SYNCCTL, tmp & 0xFF7F);
468e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	tmp = sav->radio_7A;
469e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_radio_write16(dev, 0x007A, tmp & 0xFFF0);
470e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
4715250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	b43_phy_write(dev, B43_PHY_CCK(0x2A), 0x8A3);
472e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_G ||
473e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	    (phy->type == B43_PHYTYPE_B &&
474e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	     phy->radio_ver == 0x2050 && phy->radio_rev >= 6)) {
4755250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x1003);
476e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else
4775250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x0802);
478e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->rev >= 2)
4792f19c287fecb958eb68b1c2199b4dd6e00ba4276Gábor Stefanik		b43_dummy_transmission(dev, false, true);
480ef1a628d83fc0423c36e773281162be790503168Michael Buesch	b43_gphy_channel_switch(dev, 6, 0);
481e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_radio_read16(dev, 0x51);	/* dummy read */
482e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_G)
4835250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x2F), 0);
484f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
485f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	/* Re-measure the txctl values, if needed. */
486f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (time_before(lo->txctl_measured_time,
487f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			jiffies - B43_LO_TXCTL_EXPIRE))
488e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		lo_measure_txctl_values(dev);
489f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
490e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_G && phy->rev >= 3) {
491e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_LO_MASK, 0xC078);
492e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
493e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (phy->type == B43_PHYTYPE_B)
4945250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch			b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078);
495e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		else
496e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			b43_phy_write(dev, B43_PHY_LO_MASK, 0x8078);
497e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
498e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
499e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
500e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic void lo_measure_restore(struct b43_wldev *dev,
501e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			       struct lo_g_saved_values *sav)
502e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
503e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
504ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
505e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 tmp;
506e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
507e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->rev >= 2) {
508e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_PGACTL, 0xE300);
509ef1a628d83fc0423c36e773281162be790503168Michael Buesch		tmp = (gphy->pga_gain << 8);
510e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA0);
511e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		udelay(5);
512e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA2);
513e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		udelay(2);
514e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVERVAL, tmp | 0xA3);
515e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
516ef1a628d83fc0423c36e773281162be790503168Michael Buesch		tmp = (gphy->pga_gain | 0xEFA0);
517e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_PGACTL, tmp);
518e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
519e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_G) {
520e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (phy->rev >= 3)
5215250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch			b43_phy_write(dev, B43_PHY_CCK(0x2E), 0xC078);
522e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		else
5235250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch			b43_phy_write(dev, B43_PHY_CCK(0x2E), 0x8078);
524e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (phy->rev >= 2)
5255250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch			b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0202);
526e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		else
5275250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch			b43_phy_write(dev, B43_PHY_CCK(0x2F), 0x0101);
528e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
529e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_write16(dev, 0x3F4, sav->reg_3F4);
530e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_phy_write(dev, B43_PHY_PGACTL, sav->phy_pgactl);
5315250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch	b43_phy_write(dev, B43_PHY_CCK(0x2A), sav->phy_cck_2A);
532e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_phy_write(dev, B43_PHY_SYNCCTL, sav->phy_syncctl);
533e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl);
534e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_radio_write16(dev, 0x43, sav->radio_43);
535e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_radio_write16(dev, 0x7A, sav->radio_7A);
536e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (!has_tx_magnification(phy)) {
537e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		tmp = sav->radio_52;
5385f9724dd94d63e26edb02d9f6a4ce1ce35737f14Michael Buesch		b43_radio_maskset(dev, 0x52, 0xFF0F, tmp);
539e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
540e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	b43_write16(dev, 0x3E2, sav->reg_3E2);
541e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->type == B43_PHYTYPE_B &&
542e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	    phy->radio_ver == 0x2050 && phy->radio_rev <= 5) {
5435250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x30), sav->phy_cck_30);
5445250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x06), sav->phy_cck_06);
545e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
546e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (phy->rev >= 2) {
547e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_ANALOGOVER, sav->phy_analogover);
548e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_ANALOGOVERVAL,
549e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			      sav->phy_analogoverval);
550e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_CLASSCTL, sav->phy_classctl);
551e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVER, sav->phy_rfover);
552e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_RFOVERVAL, sav->phy_rfoverval);
5535250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x3E), sav->phy_cck_3E);
554e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_CRS0, sav->phy_crs0);
555e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
556ef1a628d83fc0423c36e773281162be790503168Michael Buesch	if (b43_has_hardware_pctl(dev)) {
557e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		tmp = (sav->phy_lo_mask & 0xBFFF);
558e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_LO_MASK, tmp);
559e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_EXTG(0x01), sav->phy_extg_01);
560e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_DACCTL, sav->phy_dacctl_hwpctl);
5615250703e3144e50fbeceb4d1fc01ea2fd159fd4aMichael Buesch		b43_phy_write(dev, B43_PHY_CCK(0x14), sav->phy_cck_14);
562e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_phy_write(dev, B43_PHY_HPWR_TSSICTL, sav->phy_hpwr_tssictl);
563e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
564ef1a628d83fc0423c36e773281162be790503168Michael Buesch	b43_gphy_channel_switch(dev, sav->old_channel, 1);
565e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
566e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
567e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstruct b43_lo_g_statemachine {
568e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int current_state;
569e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int nr_measured;
570e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int state_val_multiplier;
571e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 lowest_feedth;
572e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_loctl min_loctl;
573e4d6b7951812d98417feb10784e400e253caf633Michael Buesch};
574e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
575e4d6b7951812d98417feb10784e400e253caf633Michael Buesch/* Loop over each possible value in this state. */
576e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic int lo_probe_possible_loctls(struct b43_wldev *dev,
577e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				    struct b43_loctl *probe_loctl,
578e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				    struct b43_lo_g_statemachine *d)
579e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
580e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
581ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
582e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_loctl test_loctl;
583e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_loctl orig_loctl;
584e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_loctl prev_loctl = {
585e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		.i = -100,
586e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		.q = -100,
587e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	};
588e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int i;
589e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int begin, end;
590e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int found_lower = 0;
591e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 feedth;
592e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
593e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	static const struct b43_loctl modifiers[] = {
594e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = 1,.q = 1,},
595e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = 1,.q = 0,},
596e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = 1,.q = -1,},
597e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = 0,.q = -1,},
598e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = -1,.q = -1,},
599e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = -1,.q = 0,},
600e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = -1,.q = 1,},
601e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		{.i = 0,.q = 1,},
602e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	};
603e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
604e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (d->current_state == 0) {
605e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		begin = 1;
606e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		end = 8;
607e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else if (d->current_state % 2 == 0) {
608e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		begin = d->current_state - 1;
609e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		end = d->current_state + 1;
610e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} else {
611e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		begin = d->current_state - 2;
612e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		end = d->current_state + 2;
613e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
614e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (begin < 1)
615e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		begin += 8;
616e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	if (end > 8)
617e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		end -= 8;
618e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
619e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	memcpy(&orig_loctl, probe_loctl, sizeof(struct b43_loctl));
620e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	i = begin;
621e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	d->current_state = i;
622e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	while (1) {
623e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		B43_WARN_ON(!(i >= 1 && i <= 8));
624e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		memcpy(&test_loctl, &orig_loctl, sizeof(struct b43_loctl));
625e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		test_loctl.i += modifiers[i - 1].i * d->state_val_multiplier;
626e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		test_loctl.q += modifiers[i - 1].q * d->state_val_multiplier;
627e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if ((test_loctl.i != prev_loctl.i ||
628e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		     test_loctl.q != prev_loctl.q) &&
629e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		    (abs(test_loctl.i) <= 16 && abs(test_loctl.q) <= 16)) {
630e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			b43_lo_write(dev, &test_loctl);
631ef1a628d83fc0423c36e773281162be790503168Michael Buesch			feedth = lo_measure_feedthrough(dev, gphy->lna_gain,
632ef1a628d83fc0423c36e773281162be790503168Michael Buesch							gphy->pga_gain,
633ef1a628d83fc0423c36e773281162be790503168Michael Buesch							gphy->trsw_rx_gain);
634e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (feedth < d->lowest_feedth) {
635e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				memcpy(probe_loctl, &test_loctl,
636e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				       sizeof(struct b43_loctl));
637e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				found_lower = 1;
638e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				d->lowest_feedth = feedth;
639e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				if ((d->nr_measured < 2) &&
640f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch				    !has_loopback_gain(phy))
641e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					break;
642e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			}
643e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
644e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		memcpy(&prev_loctl, &test_loctl, sizeof(prev_loctl));
645e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (i == end)
646e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			break;
647e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (i == 8)
648e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			i = 1;
649e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		else
650e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			i++;
651e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		d->current_state = i;
652e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
653e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
654e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	return found_lower;
655e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
656e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
657e4d6b7951812d98417feb10784e400e253caf633Michael Bueschstatic void lo_probe_loctls_statemachine(struct b43_wldev *dev,
658e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					 struct b43_loctl *loctl,
659e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					 int *max_rx_gain)
660e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
661e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
662ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
663e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_lo_g_statemachine d;
664e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 feedth;
665e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int found_lower;
666e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_loctl probe_loctl;
667e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int max_repeat = 1, repeat_cnt = 0;
668e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
669e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	d.nr_measured = 0;
670e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	d.state_val_multiplier = 1;
671f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (has_loopback_gain(phy))
672e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		d.state_val_multiplier = 3;
673e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
674e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	memcpy(&d.min_loctl, loctl, sizeof(struct b43_loctl));
675f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (has_loopback_gain(phy))
676e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		max_repeat = 4;
677e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	do {
678e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		b43_lo_write(dev, &d.min_loctl);
679ef1a628d83fc0423c36e773281162be790503168Michael Buesch		feedth = lo_measure_feedthrough(dev, gphy->lna_gain,
680ef1a628d83fc0423c36e773281162be790503168Michael Buesch						gphy->pga_gain,
681ef1a628d83fc0423c36e773281162be790503168Michael Buesch						gphy->trsw_rx_gain);
682f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (feedth < 0x258) {
683e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (feedth >= 0x12C)
684e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				*max_rx_gain += 6;
685e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			else
686e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				*max_rx_gain += 3;
687ef1a628d83fc0423c36e773281162be790503168Michael Buesch			feedth = lo_measure_feedthrough(dev, gphy->lna_gain,
688ef1a628d83fc0423c36e773281162be790503168Michael Buesch							gphy->pga_gain,
689ef1a628d83fc0423c36e773281162be790503168Michael Buesch							gphy->trsw_rx_gain);
690e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
691e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		d.lowest_feedth = feedth;
692e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
693e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		d.current_state = 0;
694e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		do {
695e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			B43_WARN_ON(!
696e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				    (d.current_state >= 0
697e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				     && d.current_state <= 8));
698e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			memcpy(&probe_loctl, &d.min_loctl,
699e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			       sizeof(struct b43_loctl));
700e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			found_lower =
701e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			    lo_probe_possible_loctls(dev, &probe_loctl, &d);
702e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (!found_lower)
703e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				break;
704e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if ((probe_loctl.i == d.min_loctl.i) &&
705e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			    (probe_loctl.q == d.min_loctl.q))
706e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				break;
707e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			memcpy(&d.min_loctl, &probe_loctl,
708e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			       sizeof(struct b43_loctl));
709e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			d.nr_measured++;
710e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		} while (d.nr_measured < 24);
711e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		memcpy(loctl, &d.min_loctl, sizeof(struct b43_loctl));
712e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
713e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		if (has_loopback_gain(phy)) {
714e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (d.lowest_feedth > 0x1194)
715e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				*max_rx_gain -= 6;
716e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			else if (d.lowest_feedth < 0x5DC)
717e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				*max_rx_gain += 3;
718e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			if (repeat_cnt == 0) {
719e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				if (d.lowest_feedth <= 0x5DC) {
720e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					d.state_val_multiplier = 1;
721e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					repeat_cnt++;
722e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				} else
723e4d6b7951812d98417feb10784e400e253caf633Michael Buesch					d.state_val_multiplier = 2;
724e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			} else if (repeat_cnt == 2)
725e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				d.state_val_multiplier = 1;
726e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		}
727e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		lo_measure_gain_values(dev, *max_rx_gain,
728e4d6b7951812d98417feb10784e400e253caf633Michael Buesch				       has_loopback_gain(phy));
729e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	} while (++repeat_cnt < max_repeat);
730e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
731e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
732f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Bueschstatic
73399da185a72ba685a5aaf49dff6a5fe83885112e4John Daikerstruct b43_lo_calib *b43_calibrate_lo_setting(struct b43_wldev *dev,
73499da185a72ba685a5aaf49dff6a5fe83885112e4John Daiker					      const struct b43_bbatt *bbatt,
73599da185a72ba685a5aaf49dff6a5fe83885112e4John Daiker					      const struct b43_rfatt *rfatt)
736e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
737e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
738ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
739e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_loctl loctl = {
740e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		.i = 0,
741e4d6b7951812d98417feb10784e400e253caf633Michael Buesch		.q = 0,
742e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	};
743e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	int max_rx_gain;
744f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_lo_calib *cal;
745f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct lo_g_saved_values uninitialized_var(saved_regs);
746e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	/* Values from the "TXCTL Register and Value Table" */
747e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 txctl_reg;
748e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 txctl_value;
749e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	u16 pad_mix_gain;
750e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
751f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	saved_regs.old_channel = phy->channel;
752f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_mac_suspend(dev);
753f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	lo_measure_setup(dev, &saved_regs);
754e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
755e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	txctl_reg = lo_txctl_register_table(dev, &txctl_value, &pad_mix_gain);
756e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
7575f9724dd94d63e26edb02d9f6a4ce1ce35737f14Michael Buesch	b43_radio_maskset(dev, 0x43, 0xFFF0, rfatt->att);
7585f9724dd94d63e26edb02d9f6a4ce1ce35737f14Michael Buesch	b43_radio_maskset(dev, txctl_reg, ~txctl_value, (rfatt->with_padmix ? txctl_value :0));
759e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
760f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	max_rx_gain = rfatt->att * 2;
761f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	max_rx_gain += bbatt->att / 2;
762f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (rfatt->with_padmix)
763f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		max_rx_gain -= pad_mix_gain;
764f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (has_loopback_gain(phy))
765ef1a628d83fc0423c36e773281162be790503168Michael Buesch		max_rx_gain += gphy->max_lb_gain;
766f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	lo_measure_gain_values(dev, max_rx_gain,
767f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			       has_loopback_gain(phy));
768f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
769ef1a628d83fc0423c36e773281162be790503168Michael Buesch	b43_gphy_set_baseband_attenuation(dev, bbatt->att);
770f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain);
771f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
772f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	lo_measure_restore(dev, &saved_regs);
773f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_mac_enable(dev);
774f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
775f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (b43_debug(dev, B43_DBG_LO)) {
776f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		b43dbg(dev->wl, "LO: Calibrated for BB(%u), RF(%u,%u) "
777f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		       "=> I=%d Q=%d\n",
778f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		       bbatt->att, rfatt->att, rfatt->with_padmix,
779f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		       loctl.i, loctl.q);
780e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
781e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
782f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	cal = kmalloc(sizeof(*cal), GFP_KERNEL);
783f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!cal) {
784f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		b43warn(dev->wl, "LO calib: out of memory\n");
785f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return NULL;
786e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
787f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	memcpy(&cal->bbatt, bbatt, sizeof(*bbatt));
788f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	memcpy(&cal->rfatt, rfatt, sizeof(*rfatt));
789f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	memcpy(&cal->ctl, &loctl, sizeof(loctl));
790f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	cal->calib_time = jiffies;
791f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	INIT_LIST_HEAD(&cal->list);
792f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
793f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	return cal;
794e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
795e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
796f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch/* Get a calibrated LO setting for the given attenuation values.
797f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch * Might return a NULL pointer under OOM! */
798f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Bueschstatic
79999da185a72ba685a5aaf49dff6a5fe83885112e4John Daikerstruct b43_lo_calib *b43_get_calib_lo_settings(struct b43_wldev *dev,
80099da185a72ba685a5aaf49dff6a5fe83885112e4John Daiker					       const struct b43_bbatt *bbatt,
80199da185a72ba685a5aaf49dff6a5fe83885112e4John Daiker					       const struct b43_rfatt *rfatt)
802e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
803ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_txpower_lo_control *lo = dev->phy.g->lo_control;
804f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_lo_calib *c;
805f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
806f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	c = b43_find_lo_calib(lo, bbatt, rfatt);
807f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (c)
808f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return c;
809f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	/* Not in the list of calibrated LO settings.
810f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	 * Calibrate it now. */
811f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	c = b43_calibrate_lo_setting(dev, bbatt, rfatt);
812f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!c)
813f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return NULL;
814f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	list_add(&c->list, &lo->calib_list);
815f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
816f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	return c;
817e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
818e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
819f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Bueschvoid b43_gphy_dc_lt_init(struct b43_wldev *dev, bool update_all)
820e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
821e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_phy *phy = &dev->phy;
822ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
823ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_txpower_lo_control *lo = gphy->lo_control;
824f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	int i;
825f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	int rf_offset, bb_offset;
826f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	const struct b43_rfatt *rfatt;
827f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	const struct b43_bbatt *bbatt;
828f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	u64 power_vector;
8293db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell	bool table_changed = false;
830e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
831f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	BUILD_BUG_ON(B43_DC_LT_SIZE != 32);
832f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	B43_WARN_ON(lo->rfatt_list.len * lo->bbatt_list.len > 64);
833e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
834f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	power_vector = lo->power_vector;
835f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!update_all && !power_vector)
836f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return; /* Nothing to do. */
837f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
838f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	/* Suspend the MAC now to avoid continuous suspend/enable
839f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	 * cycles in the loop. */
840f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_mac_suspend(dev);
841f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
842f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	for (i = 0; i < B43_DC_LT_SIZE * 2; i++) {
843f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		struct b43_lo_calib *cal;
844f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		int idx;
845f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		u16 val;
846f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
847f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (!update_all && !(power_vector & (((u64)1ULL) << i)))
848f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			continue;
849f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/* Update the table entry for this power_vector bit.
850f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		 * The table rows are RFatt entries and columns are BBatt. */
851f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		bb_offset = i / lo->rfatt_list.len;
852f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		rf_offset = i % lo->rfatt_list.len;
853f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		bbatt = &(lo->bbatt_list.list[bb_offset]);
854f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		rfatt = &(lo->rfatt_list.list[rf_offset]);
855f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
856f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		cal = b43_calibrate_lo_setting(dev, bbatt, rfatt);
857f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (!cal) {
858f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			b43warn(dev->wl, "LO: Could not "
859f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch				"calibrate DC table entry\n");
860f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			continue;
861f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		}
862f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/*FIXME: Is Q really in the low nibble? */
863f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		val = (u8)(cal->ctl.q);
864f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		val |= ((u8)(cal->ctl.i)) << 4;
865f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		kfree(cal);
866f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
867f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/* Get the index into the hardware DC LT. */
868f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		idx = i / 2;
869f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/* Change the table in memory. */
870f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (i % 2) {
871f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			/* Change the high byte. */
872f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			lo->dc_lt[idx] = (lo->dc_lt[idx] & 0x00FF)
873f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch					 | ((val & 0x00FF) << 8);
874f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		} else {
875f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			/* Change the low byte. */
876f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			lo->dc_lt[idx] = (lo->dc_lt[idx] & 0xFF00)
877f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch					 | (val & 0x00FF);
878f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		}
8793db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell		table_changed = true;
880e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	}
881f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (table_changed) {
882f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/* The table changed in memory. Update the hardware table. */
883f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		for (i = 0; i < B43_DC_LT_SIZE; i++)
884f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			b43_phy_write(dev, 0x3A0 + i, lo->dc_lt[i]);
885f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	}
886f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_mac_enable(dev);
887e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
888e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
889f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch/* Fixup the RF attenuation value for the case where we are
890f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch * using the PAD mixer. */
891f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Bueschstatic inline void b43_lo_fixup_rfatt(struct b43_rfatt *rf)
892e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
893f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!rf->with_padmix)
894f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return;
895f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if ((rf->att != 1) && (rf->att != 2) && (rf->att != 3))
896f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		rf->att = 4;
897e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
898e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
899e4d6b7951812d98417feb10784e400e253caf633Michael Bueschvoid b43_lo_g_adjust(struct b43_wldev *dev)
900e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
901ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = dev->phy.g;
902f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_lo_calib *cal;
903e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_rfatt rf;
904e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
905ef1a628d83fc0423c36e773281162be790503168Michael Buesch	memcpy(&rf, &gphy->rfatt, sizeof(rf));
906f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_lo_fixup_rfatt(&rf);
907e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
908ef1a628d83fc0423c36e773281162be790503168Michael Buesch	cal = b43_get_calib_lo_settings(dev, &gphy->bbatt, &rf);
909f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!cal)
910f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return;
911f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_lo_write(dev, &cal->ctl);
912e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
913e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
914e4d6b7951812d98417feb10784e400e253caf633Michael Bueschvoid b43_lo_g_adjust_to(struct b43_wldev *dev,
915e4d6b7951812d98417feb10784e400e253caf633Michael Buesch			u16 rfatt, u16 bbatt, u16 tx_control)
916e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
917e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_rfatt rf;
918e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	struct b43_bbatt bb;
919f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_lo_calib *cal;
920e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
921e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	memset(&rf, 0, sizeof(rf));
922e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	memset(&bb, 0, sizeof(bb));
923e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	rf.att = rfatt;
924e4d6b7951812d98417feb10784e400e253caf633Michael Buesch	bb.att = bbatt;
925f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_lo_fixup_rfatt(&rf);
926f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	cal = b43_get_calib_lo_settings(dev, &bb, &rf);
927f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!cal)
928f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return;
929f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	b43_lo_write(dev, &cal->ctl);
930e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
931e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
932f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch/* Periodic LO maintanance work */
933f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Bueschvoid b43_lo_g_maintanance_work(struct b43_wldev *dev)
934e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
935f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_phy *phy = &dev->phy;
936ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_phy_g *gphy = phy->g;
937ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_txpower_lo_control *lo = gphy->lo_control;
938f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	unsigned long now;
939f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	unsigned long expire;
940f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_lo_calib *cal, *tmp;
9413db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell	bool current_item_expired = false;
942f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	bool hwpctl;
943f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
944f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!lo)
945f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return;
946f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	now = jiffies;
947ef1a628d83fc0423c36e773281162be790503168Michael Buesch	hwpctl = b43_has_hardware_pctl(dev);
948f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
949f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (hwpctl) {
950f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/* Read the power vector and update it, if needed. */
951f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		expire = now - B43_LO_PWRVEC_EXPIRE;
952f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (time_before(lo->pwr_vec_read_time, expire)) {
953f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			lo_read_power_vector(dev);
954f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			b43_gphy_dc_lt_init(dev, 0);
955f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		}
956f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		//FIXME Recalc the whole DC table from time to time?
957f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	}
958f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch
959f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (hwpctl)
960f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return;
961f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	/* Search for expired LO settings. Remove them.
962f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	 * Recalibrate the current setting, if expired. */
963f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	expire = now - B43_LO_CALIB_EXPIRE;
964f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) {
965f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (!time_before(cal->calib_time, expire))
966f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			continue;
967f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/* This item expired. */
968ef1a628d83fc0423c36e773281162be790503168Michael Buesch		if (b43_compare_bbatt(&cal->bbatt, &gphy->bbatt) &&
969ef1a628d83fc0423c36e773281162be790503168Michael Buesch		    b43_compare_rfatt(&cal->rfatt, &gphy->rfatt)) {
970f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			B43_WARN_ON(current_item_expired);
9713db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell			current_item_expired = true;
972f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		}
973f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (b43_debug(dev, B43_DBG_LO)) {
974f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			b43dbg(dev->wl, "LO: Item BB(%u), RF(%u,%u), "
975f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			       "I=%d, Q=%d expired\n",
976f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			       cal->bbatt.att, cal->rfatt.att,
977f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			       cal->rfatt.with_padmix,
978f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			       cal->ctl.i, cal->ctl.q);
979f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		}
980f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		list_del(&cal->list);
981f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		kfree(cal);
982f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	}
983f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (current_item_expired || unlikely(list_empty(&lo->calib_list))) {
984f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		/* Recalibrate currently used LO setting. */
985f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (b43_debug(dev, B43_DBG_LO))
986f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			b43dbg(dev->wl, "LO: Recalibrating current LO setting\n");
987ef1a628d83fc0423c36e773281162be790503168Michael Buesch		cal = b43_calibrate_lo_setting(dev, &gphy->bbatt, &gphy->rfatt);
988f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		if (cal) {
989f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			list_add(&cal->list, &lo->calib_list);
990f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			b43_lo_write(dev, &cal->ctl);
991f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		} else
992f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch			b43warn(dev->wl, "Failed to recalibrate current LO setting\n");
993f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	}
994e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
995e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
996f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Bueschvoid b43_lo_g_cleanup(struct b43_wldev *dev)
997e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
998ef1a628d83fc0423c36e773281162be790503168Michael Buesch	struct b43_txpower_lo_control *lo = dev->phy.g->lo_control;
999f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	struct b43_lo_calib *cal, *tmp;
1000e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
1001f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	if (!lo)
1002f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		return;
1003f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	list_for_each_entry_safe(cal, tmp, &lo->calib_list, list) {
1004f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		list_del(&cal->list);
1005f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		kfree(cal);
1006f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	}
1007e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
1008e4d6b7951812d98417feb10784e400e253caf633Michael Buesch
1009f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch/* LO Initialization */
1010f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Bueschvoid b43_lo_g_init(struct b43_wldev *dev)
1011e4d6b7951812d98417feb10784e400e253caf633Michael Buesch{
1012ef1a628d83fc0423c36e773281162be790503168Michael Buesch	if (b43_has_hardware_pctl(dev)) {
1013f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		lo_read_power_vector(dev);
1014f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch		b43_gphy_dc_lt_init(dev, 1);
1015f5eda47f45e90dfa38e25d569b9ac84ba94f8301Michael Buesch	}
1016e4d6b7951812d98417feb10784e400e253caf633Michael Buesch}
1017