1423e3ce3SKalle Valo /*
2423e3ce3SKalle Valo 
3423e3ce3SKalle Valo   Broadcom B43legacy wireless driver
4423e3ce3SKalle Valo 
5423e3ce3SKalle Valo   Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
6423e3ce3SKalle Valo 		     Stefano Brivio <stefano.brivio@polimi.it>
7423e3ce3SKalle Valo 		     Michael Buesch <m@bues.ch>
8423e3ce3SKalle Valo 		     Danny van Dyk <kugelfang@gentoo.org>
9423e3ce3SKalle Valo      Andreas Jaggi <andreas.jaggi@waterwave.ch>
10423e3ce3SKalle Valo   Copyright (c) 2007 Larry Finger <Larry.Finger@lwfinger.net>
11423e3ce3SKalle Valo 
12423e3ce3SKalle Valo   Some parts of the code in this file are derived from the ipw2200
13423e3ce3SKalle Valo   driver  Copyright(c) 2003 - 2004 Intel Corporation.
14423e3ce3SKalle Valo 
15423e3ce3SKalle Valo   This program is free software; you can redistribute it and/or modify
16423e3ce3SKalle Valo   it under the terms of the GNU General Public License as published by
17423e3ce3SKalle Valo   the Free Software Foundation; either version 2 of the License, or
18423e3ce3SKalle Valo   (at your option) any later version.
19423e3ce3SKalle Valo 
20423e3ce3SKalle Valo   This program is distributed in the hope that it will be useful,
21423e3ce3SKalle Valo   but WITHOUT ANY WARRANTY; without even the implied warranty of
22423e3ce3SKalle Valo   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23423e3ce3SKalle Valo   GNU General Public License for more details.
24423e3ce3SKalle Valo 
25423e3ce3SKalle Valo   You should have received a copy of the GNU General Public License
26423e3ce3SKalle Valo   along with this program; see the file COPYING.  If not, write to
27423e3ce3SKalle Valo   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
28423e3ce3SKalle Valo   Boston, MA 02110-1301, USA.
29423e3ce3SKalle Valo 
30423e3ce3SKalle Valo */
31423e3ce3SKalle Valo 
32423e3ce3SKalle Valo #include <linux/delay.h>
33423e3ce3SKalle Valo #include <linux/pci.h>
34423e3ce3SKalle Valo #include <linux/sched.h>
35423e3ce3SKalle Valo #include <linux/slab.h>
36423e3ce3SKalle Valo #include <linux/types.h>
37423e3ce3SKalle Valo 
38423e3ce3SKalle Valo #include "b43legacy.h"
39423e3ce3SKalle Valo #include "phy.h"
40423e3ce3SKalle Valo #include "main.h"
41423e3ce3SKalle Valo #include "radio.h"
42423e3ce3SKalle Valo #include "ilt.h"
43423e3ce3SKalle Valo 
44423e3ce3SKalle Valo 
45423e3ce3SKalle Valo static const s8 b43legacy_tssi2dbm_b_table[] = {
46423e3ce3SKalle Valo 	0x4D, 0x4C, 0x4B, 0x4A,
47423e3ce3SKalle Valo 	0x4A, 0x49, 0x48, 0x47,
48423e3ce3SKalle Valo 	0x47, 0x46, 0x45, 0x45,
49423e3ce3SKalle Valo 	0x44, 0x43, 0x42, 0x42,
50423e3ce3SKalle Valo 	0x41, 0x40, 0x3F, 0x3E,
51423e3ce3SKalle Valo 	0x3D, 0x3C, 0x3B, 0x3A,
52423e3ce3SKalle Valo 	0x39, 0x38, 0x37, 0x36,
53423e3ce3SKalle Valo 	0x35, 0x34, 0x32, 0x31,
54423e3ce3SKalle Valo 	0x30, 0x2F, 0x2D, 0x2C,
55423e3ce3SKalle Valo 	0x2B, 0x29, 0x28, 0x26,
56423e3ce3SKalle Valo 	0x25, 0x23, 0x21, 0x1F,
57423e3ce3SKalle Valo 	0x1D, 0x1A, 0x17, 0x14,
58423e3ce3SKalle Valo 	0x10, 0x0C, 0x06, 0x00,
59423e3ce3SKalle Valo 	  -7,   -7,   -7,   -7,
60423e3ce3SKalle Valo 	  -7,   -7,   -7,   -7,
61423e3ce3SKalle Valo 	  -7,   -7,   -7,   -7,
62423e3ce3SKalle Valo };
63423e3ce3SKalle Valo 
64423e3ce3SKalle Valo static const s8 b43legacy_tssi2dbm_g_table[] = {
65423e3ce3SKalle Valo 	 77,  77,  77,  76,
66423e3ce3SKalle Valo 	 76,  76,  75,  75,
67423e3ce3SKalle Valo 	 74,  74,  73,  73,
68423e3ce3SKalle Valo 	 73,  72,  72,  71,
69423e3ce3SKalle Valo 	 71,  70,  70,  69,
70423e3ce3SKalle Valo 	 68,  68,  67,  67,
71423e3ce3SKalle Valo 	 66,  65,  65,  64,
72423e3ce3SKalle Valo 	 63,  63,  62,  61,
73423e3ce3SKalle Valo 	 60,  59,  58,  57,
74423e3ce3SKalle Valo 	 56,  55,  54,  53,
75423e3ce3SKalle Valo 	 52,  50,  49,  47,
76423e3ce3SKalle Valo 	 45,  43,  40,  37,
77423e3ce3SKalle Valo 	 33,  28,  22,  14,
78423e3ce3SKalle Valo 	  5,  -7, -20, -20,
79423e3ce3SKalle Valo 	-20, -20, -20, -20,
80423e3ce3SKalle Valo 	-20, -20, -20, -20,
81423e3ce3SKalle Valo };
82423e3ce3SKalle Valo 
83423e3ce3SKalle Valo static void b43legacy_phy_initg(struct b43legacy_wldev *dev);
84423e3ce3SKalle Valo 
85423e3ce3SKalle Valo 
86423e3ce3SKalle Valo static inline
87423e3ce3SKalle Valo void b43legacy_voluntary_preempt(void)
88423e3ce3SKalle Valo {
89423e3ce3SKalle Valo 	B43legacy_BUG_ON(!(!in_atomic() && !in_irq() &&
90423e3ce3SKalle Valo 			  !in_interrupt() && !irqs_disabled()));
91423e3ce3SKalle Valo #ifndef CONFIG_PREEMPT
92423e3ce3SKalle Valo 	cond_resched();
93423e3ce3SKalle Valo #endif /* CONFIG_PREEMPT */
94423e3ce3SKalle Valo }
95423e3ce3SKalle Valo 
96423e3ce3SKalle Valo /* Lock the PHY registers against concurrent access from the microcode.
97423e3ce3SKalle Valo  * This lock is nonrecursive. */
98423e3ce3SKalle Valo void b43legacy_phy_lock(struct b43legacy_wldev *dev)
99423e3ce3SKalle Valo {
100423e3ce3SKalle Valo #if B43legacy_DEBUG
101423e3ce3SKalle Valo 	B43legacy_WARN_ON(dev->phy.phy_locked);
102423e3ce3SKalle Valo 	dev->phy.phy_locked = 1;
103423e3ce3SKalle Valo #endif
104423e3ce3SKalle Valo 
105423e3ce3SKalle Valo 	if (dev->dev->id.revision < 3) {
106423e3ce3SKalle Valo 		b43legacy_mac_suspend(dev);
107423e3ce3SKalle Valo 	} else {
108423e3ce3SKalle Valo 		if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP))
109423e3ce3SKalle Valo 			b43legacy_power_saving_ctl_bits(dev, -1, 1);
110423e3ce3SKalle Valo 	}
111423e3ce3SKalle Valo }
112423e3ce3SKalle Valo 
113423e3ce3SKalle Valo void b43legacy_phy_unlock(struct b43legacy_wldev *dev)
114423e3ce3SKalle Valo {
115423e3ce3SKalle Valo #if B43legacy_DEBUG
116423e3ce3SKalle Valo 	B43legacy_WARN_ON(!dev->phy.phy_locked);
117423e3ce3SKalle Valo 	dev->phy.phy_locked = 0;
118423e3ce3SKalle Valo #endif
119423e3ce3SKalle Valo 
120423e3ce3SKalle Valo 	if (dev->dev->id.revision < 3) {
121423e3ce3SKalle Valo 		b43legacy_mac_enable(dev);
122423e3ce3SKalle Valo 	} else {
123423e3ce3SKalle Valo 		if (!b43legacy_is_mode(dev->wl, NL80211_IFTYPE_AP))
124423e3ce3SKalle Valo 			b43legacy_power_saving_ctl_bits(dev, -1, -1);
125423e3ce3SKalle Valo 	}
126423e3ce3SKalle Valo }
127423e3ce3SKalle Valo 
128423e3ce3SKalle Valo u16 b43legacy_phy_read(struct b43legacy_wldev *dev, u16 offset)
129423e3ce3SKalle Valo {
130423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset);
131423e3ce3SKalle Valo 	return b43legacy_read16(dev, B43legacy_MMIO_PHY_DATA);
132423e3ce3SKalle Valo }
133423e3ce3SKalle Valo 
134423e3ce3SKalle Valo void b43legacy_phy_write(struct b43legacy_wldev *dev, u16 offset, u16 val)
135423e3ce3SKalle Valo {
136423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_PHY_CONTROL, offset);
137423e3ce3SKalle Valo 	mmiowb();
138423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_PHY_DATA, val);
139423e3ce3SKalle Valo }
140423e3ce3SKalle Valo 
141423e3ce3SKalle Valo void b43legacy_phy_calibrate(struct b43legacy_wldev *dev)
142423e3ce3SKalle Valo {
143423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
144423e3ce3SKalle Valo 
145423e3ce3SKalle Valo 	b43legacy_read32(dev, B43legacy_MMIO_MACCTL); /* Dummy read. */
146423e3ce3SKalle Valo 	if (phy->calibrated)
147423e3ce3SKalle Valo 		return;
148423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_G && phy->rev == 1) {
149423e3ce3SKalle Valo 		b43legacy_wireless_core_reset(dev, 0);
150423e3ce3SKalle Valo 		b43legacy_phy_initg(dev);
151423e3ce3SKalle Valo 		b43legacy_wireless_core_reset(dev, B43legacy_TMSLOW_GMODE);
152423e3ce3SKalle Valo 	}
153423e3ce3SKalle Valo 	phy->calibrated = 1;
154423e3ce3SKalle Valo }
155423e3ce3SKalle Valo 
156423e3ce3SKalle Valo /* initialize B PHY power control
157423e3ce3SKalle Valo  * as described in http://bcm-specs.sipsolutions.net/InitPowerControl
158423e3ce3SKalle Valo  */
159423e3ce3SKalle Valo static void b43legacy_phy_init_pctl(struct b43legacy_wldev *dev)
160423e3ce3SKalle Valo {
161423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
162423e3ce3SKalle Valo 	u16 saved_batt = 0;
163423e3ce3SKalle Valo 	u16 saved_ratt = 0;
164423e3ce3SKalle Valo 	u16 saved_txctl1 = 0;
165423e3ce3SKalle Valo 	int must_reset_txpower = 0;
166423e3ce3SKalle Valo 
167423e3ce3SKalle Valo 	B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B ||
168423e3ce3SKalle Valo 			  phy->type == B43legacy_PHYTYPE_G));
169423e3ce3SKalle Valo 	if (is_bcm_board_vendor(dev) &&
170423e3ce3SKalle Valo 	    (dev->dev->bus->boardinfo.type == 0x0416))
171423e3ce3SKalle Valo 		return;
172423e3ce3SKalle Valo 
173423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0028, 0x8018);
174423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03E6, b43legacy_read16(dev, 0x03E6) & 0xFFDF);
175423e3ce3SKalle Valo 
176423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_G) {
177423e3ce3SKalle Valo 		if (!phy->gmode)
178423e3ce3SKalle Valo 			return;
179423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x047A, 0xC111);
180423e3ce3SKalle Valo 	}
181423e3ce3SKalle Valo 	if (phy->savedpctlreg != 0xFFFF)
182423e3ce3SKalle Valo 		return;
183423e3ce3SKalle Valo #ifdef CONFIG_B43LEGACY_DEBUG
184423e3ce3SKalle Valo 	if (phy->manual_txpower_control)
185423e3ce3SKalle Valo 		return;
186423e3ce3SKalle Valo #endif
187423e3ce3SKalle Valo 
188423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_B &&
189423e3ce3SKalle Valo 	    phy->rev >= 2 &&
190423e3ce3SKalle Valo 	    phy->radio_ver == 0x2050)
191423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0076,
192423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x0076)
193423e3ce3SKalle Valo 					| 0x0084);
194423e3ce3SKalle Valo 	else {
195423e3ce3SKalle Valo 		saved_batt = phy->bbatt;
196423e3ce3SKalle Valo 		saved_ratt = phy->rfatt;
197423e3ce3SKalle Valo 		saved_txctl1 = phy->txctl1;
198423e3ce3SKalle Valo 		if ((phy->radio_rev >= 6) && (phy->radio_rev <= 8)
199423e3ce3SKalle Valo 		    && /*FIXME: incomplete specs for 5 < revision < 9 */ 0)
200423e3ce3SKalle Valo 			b43legacy_radio_set_txpower_bg(dev, 0xB, 0x1F, 0);
201423e3ce3SKalle Valo 		else
202423e3ce3SKalle Valo 			b43legacy_radio_set_txpower_bg(dev, 0xB, 9, 0);
203423e3ce3SKalle Valo 		must_reset_txpower = 1;
204423e3ce3SKalle Valo 	}
205423e3ce3SKalle Valo 	b43legacy_dummy_transmission(dev);
206423e3ce3SKalle Valo 
207423e3ce3SKalle Valo 	phy->savedpctlreg = b43legacy_phy_read(dev, B43legacy_PHY_G_PCTL);
208423e3ce3SKalle Valo 
209423e3ce3SKalle Valo 	if (must_reset_txpower)
210423e3ce3SKalle Valo 		b43legacy_radio_set_txpower_bg(dev, saved_batt, saved_ratt,
211423e3ce3SKalle Valo 					       saved_txctl1);
212423e3ce3SKalle Valo 	else
213423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0076, b43legacy_radio_read16(dev,
214423e3ce3SKalle Valo 					0x0076) & 0xFF7B);
215423e3ce3SKalle Valo 	b43legacy_radio_clear_tssi(dev);
216423e3ce3SKalle Valo }
217423e3ce3SKalle Valo 
218423e3ce3SKalle Valo static void b43legacy_phy_agcsetup(struct b43legacy_wldev *dev)
219423e3ce3SKalle Valo {
220423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
221423e3ce3SKalle Valo 	u16 offset = 0x0000;
222423e3ce3SKalle Valo 
223423e3ce3SKalle Valo 	if (phy->rev == 1)
224423e3ce3SKalle Valo 		offset = 0x4C00;
225423e3ce3SKalle Valo 
226423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset, 0x00FE);
227423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset + 1, 0x000D);
228423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset + 2, 0x0013);
229423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset + 3, 0x0019);
230423e3ce3SKalle Valo 
231423e3ce3SKalle Valo 	if (phy->rev == 1) {
232423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x1800, 0x2710);
233423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x1801, 0x9B83);
234423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x1802, 0x9B83);
235423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x1803, 0x0F8D);
236423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0455, 0x0004);
237423e3ce3SKalle Valo 	}
238423e3ce3SKalle Valo 
239423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A5, (b43legacy_phy_read(dev, 0x04A5)
240423e3ce3SKalle Valo 					  & 0x00FF) | 0x5700);
241423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A)
242423e3ce3SKalle Valo 					  & 0xFF80) | 0x000F);
243423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x041A, (b43legacy_phy_read(dev, 0x041A)
244423e3ce3SKalle Valo 					  & 0xC07F) | 0x2B80);
245423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x048C, (b43legacy_phy_read(dev, 0x048C)
246423e3ce3SKalle Valo 					  & 0xF0FF) | 0x0300);
247423e3ce3SKalle Valo 
248423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A,
249423e3ce3SKalle Valo 				b43legacy_radio_read16(dev, 0x007A)
250423e3ce3SKalle Valo 				| 0x0008);
251423e3ce3SKalle Valo 
252423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0)
253423e3ce3SKalle Valo 			    & 0xFFF0) | 0x0008);
254423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A1, (b43legacy_phy_read(dev, 0x04A1)
255423e3ce3SKalle Valo 			    & 0xF0FF) | 0x0600);
256423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A2, (b43legacy_phy_read(dev, 0x04A2)
257423e3ce3SKalle Valo 			    & 0xF0FF) | 0x0700);
258423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A0, (b43legacy_phy_read(dev, 0x04A0)
259423e3ce3SKalle Valo 			    & 0xF0FF) | 0x0100);
260423e3ce3SKalle Valo 
261423e3ce3SKalle Valo 	if (phy->rev == 1)
262423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A2,
263423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A2)
264423e3ce3SKalle Valo 				    & 0xFFF0) | 0x0007);
265423e3ce3SKalle Valo 
266423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488)
267423e3ce3SKalle Valo 			    & 0xFF00) | 0x001C);
268423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0488, (b43legacy_phy_read(dev, 0x0488)
269423e3ce3SKalle Valo 			    & 0xC0FF) | 0x0200);
270423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496)
271423e3ce3SKalle Valo 			    & 0xFF00) | 0x001C);
272423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489)
273423e3ce3SKalle Valo 			    & 0xFF00) | 0x0020);
274423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0489, (b43legacy_phy_read(dev, 0x0489)
275423e3ce3SKalle Valo 			    & 0xC0FF) | 0x0200);
276423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0482, (b43legacy_phy_read(dev, 0x0482)
277423e3ce3SKalle Valo 			    & 0xFF00) | 0x002E);
278423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0496, (b43legacy_phy_read(dev, 0x0496)
279423e3ce3SKalle Valo 			    & 0x00FF) | 0x1A00);
280423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481)
281423e3ce3SKalle Valo 			    & 0xFF00) | 0x0028);
282423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0481, (b43legacy_phy_read(dev, 0x0481)
283423e3ce3SKalle Valo 			    & 0x00FF) | 0x2C00);
284423e3ce3SKalle Valo 
285423e3ce3SKalle Valo 	if (phy->rev == 1) {
286423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0430, 0x092B);
287423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x041B,
288423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x041B)
289423e3ce3SKalle Valo 				    & 0xFFE1) | 0x0002);
290423e3ce3SKalle Valo 	} else {
291423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x041B,
292423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x041B) & 0xFFE1);
293423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x041F, 0x287A);
294423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0420,
295423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0420)
296423e3ce3SKalle Valo 				    & 0xFFF0) | 0x0004);
297423e3ce3SKalle Valo 	}
298423e3ce3SKalle Valo 
299423e3ce3SKalle Valo 	if (phy->rev > 2) {
300423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0422, 0x287A);
301423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0420,
302423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0420)
303423e3ce3SKalle Valo 				    & 0x0FFF) | 0x3000);
304423e3ce3SKalle Valo 	}
305423e3ce3SKalle Valo 
306423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A8, (b43legacy_phy_read(dev, 0x04A8)
307423e3ce3SKalle Valo 			    & 0x8080) | 0x7874);
308423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x048E, 0x1C00);
309423e3ce3SKalle Valo 
310423e3ce3SKalle Valo 	if (phy->rev == 1) {
311423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AB,
312423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04AB)
313423e3ce3SKalle Valo 				    & 0xF0FF) | 0x0600);
314423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x048B, 0x005E);
315423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x048C,
316423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x048C) & 0xFF00)
317423e3ce3SKalle Valo 				    | 0x001E);
318423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x048D, 0x0002);
319423e3ce3SKalle Valo 	}
320423e3ce3SKalle Valo 
321423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset + 0x0800, 0);
322423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset + 0x0801, 7);
323423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset + 0x0802, 16);
324423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, offset + 0x0803, 28);
325423e3ce3SKalle Valo 
326423e3ce3SKalle Valo 	if (phy->rev >= 6) {
327423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0426,
328423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0426) & 0xFFFC));
329423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0426,
330423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0426) & 0xEFFF));
331423e3ce3SKalle Valo 	}
332423e3ce3SKalle Valo }
333423e3ce3SKalle Valo 
334423e3ce3SKalle Valo static void b43legacy_phy_setupg(struct b43legacy_wldev *dev)
335423e3ce3SKalle Valo {
336423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
337423e3ce3SKalle Valo 	u16 i;
338423e3ce3SKalle Valo 
339423e3ce3SKalle Valo 	B43legacy_BUG_ON(phy->type != B43legacy_PHYTYPE_G);
340423e3ce3SKalle Valo 	if (phy->rev == 1) {
341423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0406, 0x4F19);
342423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
343423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev,
344423e3ce3SKalle Valo 				    B43legacy_PHY_G_CRS) & 0xFC3F) | 0x0340);
345423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x042C, 0x005A);
346423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0427, 0x001A);
347423e3ce3SKalle Valo 
348423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_FINEFREQG_SIZE; i++)
349423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x5800 + i,
350423e3ce3SKalle Valo 					    b43legacy_ilt_finefreqg[i]);
351423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_NOISEG1_SIZE; i++)
352423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1800 + i,
353423e3ce3SKalle Valo 					    b43legacy_ilt_noiseg1[i]);
354423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_ROTOR_SIZE; i++)
355423e3ce3SKalle Valo 			b43legacy_ilt_write32(dev, 0x2000 + i,
356423e3ce3SKalle Valo 					      b43legacy_ilt_rotor[i]);
357423e3ce3SKalle Valo 	} else {
358423e3ce3SKalle Valo 		/* nrssi values are signed 6-bit values. Why 0x7654 here? */
359423e3ce3SKalle Valo 		b43legacy_nrssi_hw_write(dev, 0xBA98, (s16)0x7654);
360423e3ce3SKalle Valo 
361423e3ce3SKalle Valo 		if (phy->rev == 2) {
362423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C0, 0x1861);
363423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C1, 0x0271);
364423e3ce3SKalle Valo 		} else if (phy->rev > 2) {
365423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C0, 0x0098);
366423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C1, 0x0070);
367423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C9, 0x0080);
368423e3ce3SKalle Valo 		}
369423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev,
370423e3ce3SKalle Valo 				    0x042B) | 0x800);
371423e3ce3SKalle Valo 
372423e3ce3SKalle Valo 		for (i = 0; i < 64; i++)
373423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x4000 + i, i);
374423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_NOISEG2_SIZE; i++)
375423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1800 + i,
376423e3ce3SKalle Valo 					    b43legacy_ilt_noiseg2[i]);
377423e3ce3SKalle Valo 	}
378423e3ce3SKalle Valo 
379423e3ce3SKalle Valo 	if (phy->rev <= 2)
380423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++)
381423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1400 + i,
382423e3ce3SKalle Valo 					    b43legacy_ilt_noisescaleg1[i]);
383423e3ce3SKalle Valo 	else if ((phy->rev >= 7) && (b43legacy_phy_read(dev, 0x0449) & 0x0200))
384423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++)
385423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1400 + i,
386423e3ce3SKalle Valo 					    b43legacy_ilt_noisescaleg3[i]);
387423e3ce3SKalle Valo 	else
388423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_NOISESCALEG_SIZE; i++)
389423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1400 + i,
390423e3ce3SKalle Valo 					    b43legacy_ilt_noisescaleg2[i]);
391423e3ce3SKalle Valo 
392423e3ce3SKalle Valo 	if (phy->rev == 2)
393423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++)
394423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x5000 + i,
395423e3ce3SKalle Valo 					    b43legacy_ilt_sigmasqr1[i]);
396423e3ce3SKalle Valo 	else if ((phy->rev > 2) && (phy->rev <= 8))
397423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_SIGMASQR_SIZE; i++)
398423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x5000 + i,
399423e3ce3SKalle Valo 					    b43legacy_ilt_sigmasqr2[i]);
400423e3ce3SKalle Valo 
401423e3ce3SKalle Valo 	if (phy->rev == 1) {
402423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_ILT_RETARD_SIZE; i++)
403423e3ce3SKalle Valo 			b43legacy_ilt_write32(dev, 0x2400 + i,
404423e3ce3SKalle Valo 					      b43legacy_ilt_retard[i]);
405423e3ce3SKalle Valo 		for (i = 4; i < 20; i++)
406423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x5400 + i, 0x0020);
407423e3ce3SKalle Valo 		b43legacy_phy_agcsetup(dev);
408423e3ce3SKalle Valo 
409423e3ce3SKalle Valo 		if (is_bcm_board_vendor(dev) &&
410423e3ce3SKalle Valo 		    (dev->dev->bus->boardinfo.type == 0x0416) &&
411423e3ce3SKalle Valo 		    (dev->dev->bus->sprom.board_rev == 0x0017))
412423e3ce3SKalle Valo 			return;
413423e3ce3SKalle Valo 
414423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x5001, 0x0002);
415423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x5002, 0x0001);
416423e3ce3SKalle Valo 	} else {
417423e3ce3SKalle Valo 		for (i = 0; i <= 0x20; i++)
418423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1000 + i, 0x0820);
419423e3ce3SKalle Valo 		b43legacy_phy_agcsetup(dev);
420423e3ce3SKalle Valo 		b43legacy_phy_read(dev, 0x0400); /* dummy read */
421423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0403, 0x1000);
422423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x3C02, 0x000F);
423423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x3C03, 0x0014);
424423e3ce3SKalle Valo 
425423e3ce3SKalle Valo 		if (is_bcm_board_vendor(dev) &&
426423e3ce3SKalle Valo 		    (dev->dev->bus->boardinfo.type == 0x0416) &&
427423e3ce3SKalle Valo 		    (dev->dev->bus->sprom.board_rev == 0x0017))
428423e3ce3SKalle Valo 			return;
429423e3ce3SKalle Valo 
430423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x0401, 0x0002);
431423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, 0x0402, 0x0001);
432423e3ce3SKalle Valo 	}
433423e3ce3SKalle Valo }
434423e3ce3SKalle Valo 
435423e3ce3SKalle Valo /* Initialize the APHY portion of a GPHY. */
436423e3ce3SKalle Valo static void b43legacy_phy_inita(struct b43legacy_wldev *dev)
437423e3ce3SKalle Valo {
438423e3ce3SKalle Valo 
439423e3ce3SKalle Valo 	might_sleep();
440423e3ce3SKalle Valo 
441423e3ce3SKalle Valo 	b43legacy_phy_setupg(dev);
442423e3ce3SKalle Valo 	if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_PACTRL)
443423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x046E, 0x03CF);
444423e3ce3SKalle Valo }
445423e3ce3SKalle Valo 
446423e3ce3SKalle Valo static void b43legacy_phy_initb2(struct b43legacy_wldev *dev)
447423e3ce3SKalle Valo {
448423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
449423e3ce3SKalle Valo 	u16 offset;
450423e3ce3SKalle Valo 	int val;
451423e3ce3SKalle Valo 
452423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03EC, 0x3F22);
453423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0020, 0x301C);
454423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0026, 0x0000);
455423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0030, 0x00C6);
456423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0088, 0x3E00);
457423e3ce3SKalle Valo 	val = 0x3C3D;
458423e3ce3SKalle Valo 	for (offset = 0x0089; offset < 0x00A7; offset++) {
459423e3ce3SKalle Valo 		b43legacy_phy_write(dev, offset, val);
460423e3ce3SKalle Valo 		val -= 0x0202;
461423e3ce3SKalle Valo 	}
462423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x03E4, 0x3000);
463423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, phy->channel, 0);
464423e3ce3SKalle Valo 	if (phy->radio_ver != 0x2050) {
465423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0075, 0x0080);
466423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0079, 0x0081);
467423e3ce3SKalle Valo 	}
468423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0020);
469423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0023);
470423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050) {
471423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0050, 0x0020);
472423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005A, 0x0070);
473423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005B, 0x007B);
474423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005C, 0x00B0);
475423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A, 0x000F);
476423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0038, 0x0677);
477423e3ce3SKalle Valo 		b43legacy_radio_init2050(dev);
478423e3ce3SKalle Valo 	}
479423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0014, 0x0080);
480423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0032, 0x00CA);
481423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0032, 0x00CC);
482423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0035, 0x07C2);
483423e3ce3SKalle Valo 	b43legacy_phy_lo_b_measure(dev);
484423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0026, 0xCC00);
485423e3ce3SKalle Valo 	if (phy->radio_ver != 0x2050)
486423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0026, 0xCE00);
487423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1000);
488423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002A, 0x88A3);
489423e3ce3SKalle Valo 	if (phy->radio_ver != 0x2050)
490423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, 0x88C2);
491423e3ce3SKalle Valo 	b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
492423e3ce3SKalle Valo 	b43legacy_phy_init_pctl(dev);
493423e3ce3SKalle Valo }
494423e3ce3SKalle Valo 
495423e3ce3SKalle Valo static void b43legacy_phy_initb4(struct b43legacy_wldev *dev)
496423e3ce3SKalle Valo {
497423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
498423e3ce3SKalle Valo 	u16 offset;
499423e3ce3SKalle Valo 	u16 val;
500423e3ce3SKalle Valo 
501423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03EC, 0x3F22);
502423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0020, 0x301C);
503423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0026, 0x0000);
504423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0030, 0x00C6);
505423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0088, 0x3E00);
506423e3ce3SKalle Valo 	val = 0x3C3D;
507423e3ce3SKalle Valo 	for (offset = 0x0089; offset < 0x00A7; offset++) {
508423e3ce3SKalle Valo 		b43legacy_phy_write(dev, offset, val);
509423e3ce3SKalle Valo 		val -= 0x0202;
510423e3ce3SKalle Valo 	}
511423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x03E4, 0x3000);
512423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, phy->channel, 0);
513423e3ce3SKalle Valo 	if (phy->radio_ver != 0x2050) {
514423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0075, 0x0080);
515423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0079, 0x0081);
516423e3ce3SKalle Valo 	}
517423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0020);
518423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0023);
519423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050) {
520423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0050, 0x0020);
521423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005A, 0x0070);
522423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005B, 0x007B);
523423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005C, 0x00B0);
524423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A, 0x000F);
525423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0038, 0x0677);
526423e3ce3SKalle Valo 		b43legacy_radio_init2050(dev);
527423e3ce3SKalle Valo 	}
528423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0014, 0x0080);
529423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0032, 0x00CA);
530423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050)
531423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0032, 0x00E0);
532423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0035, 0x07C2);
533423e3ce3SKalle Valo 
534423e3ce3SKalle Valo 	b43legacy_phy_lo_b_measure(dev);
535423e3ce3SKalle Valo 
536423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0026, 0xCC00);
537423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050)
538423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0026, 0xCE00);
539423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x1100);
540423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002A, 0x88A3);
541423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050)
542423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, 0x88C2);
543423e3ce3SKalle Valo 	b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
544423e3ce3SKalle Valo 	if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI) {
545423e3ce3SKalle Valo 		b43legacy_calc_nrssi_slope(dev);
546423e3ce3SKalle Valo 		b43legacy_calc_nrssi_threshold(dev);
547423e3ce3SKalle Valo 	}
548423e3ce3SKalle Valo 	b43legacy_phy_init_pctl(dev);
549423e3ce3SKalle Valo }
550423e3ce3SKalle Valo 
551423e3ce3SKalle Valo static void b43legacy_phy_initb5(struct b43legacy_wldev *dev)
552423e3ce3SKalle Valo {
553423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
554423e3ce3SKalle Valo 	u16 offset;
555423e3ce3SKalle Valo 	u16 value;
556423e3ce3SKalle Valo 	u8 old_channel;
557423e3ce3SKalle Valo 
558423e3ce3SKalle Valo 	if (phy->analog == 1)
559423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
560423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
561423e3ce3SKalle Valo 					| 0x0050);
562423e3ce3SKalle Valo 	if (!is_bcm_board_vendor(dev) &&
563423e3ce3SKalle Valo 	    (dev->dev->bus->boardinfo.type != 0x0416)) {
564423e3ce3SKalle Valo 		value = 0x2120;
565423e3ce3SKalle Valo 		for (offset = 0x00A8 ; offset < 0x00C7; offset++) {
566423e3ce3SKalle Valo 			b43legacy_phy_write(dev, offset, value);
567423e3ce3SKalle Valo 			value += 0x0202;
568423e3ce3SKalle Valo 		}
569423e3ce3SKalle Valo 	}
570423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0035,
571423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x0035) & 0xF0FF)
572423e3ce3SKalle Valo 			    | 0x0700);
573423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050)
574423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0038, 0x0667);
575423e3ce3SKalle Valo 
576423e3ce3SKalle Valo 	if (phy->gmode) {
577423e3ce3SKalle Valo 		if (phy->radio_ver == 0x2050) {
578423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x007A,
579423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
580423e3ce3SKalle Valo 					| 0x0020);
581423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x0051,
582423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x0051)
583423e3ce3SKalle Valo 					| 0x0004);
584423e3ce3SKalle Valo 		}
585423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO, 0x0000);
586423e3ce3SKalle Valo 
587423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0802, b43legacy_phy_read(dev, 0x0802)
588423e3ce3SKalle Valo 				    | 0x0100);
589423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x042B, b43legacy_phy_read(dev, 0x042B)
590423e3ce3SKalle Valo 				    | 0x2000);
591423e3ce3SKalle Valo 
592423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x001C, 0x186A);
593423e3ce3SKalle Valo 
594423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0013, (b43legacy_phy_read(dev,
595423e3ce3SKalle Valo 				    0x0013) & 0x00FF) | 0x1900);
596423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0035, (b43legacy_phy_read(dev,
597423e3ce3SKalle Valo 				    0x0035) & 0xFFC0) | 0x0064);
598423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
599423e3ce3SKalle Valo 				    0x005D) & 0xFF80) | 0x000A);
600423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x5B, 0x0000);
601423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x5C, 0x0000);
602423e3ce3SKalle Valo 	}
603423e3ce3SKalle Valo 
604423e3ce3SKalle Valo 	if (dev->bad_frames_preempt)
605423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
606423e3ce3SKalle Valo 				    b43legacy_phy_read(dev,
607423e3ce3SKalle Valo 				    B43legacy_PHY_RADIO_BITFIELD) | (1 << 12));
608423e3ce3SKalle Valo 
609423e3ce3SKalle Valo 	if (phy->analog == 1) {
610423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0026, 0xCE00);
611423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0021, 0x3763);
612423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0022, 0x1BC3);
613423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0023, 0x06F9);
614423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0024, 0x037E);
615423e3ce3SKalle Valo 	} else
616423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0026, 0xCC00);
617423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0030, 0x00C6);
618423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03EC, 0x3F22);
619423e3ce3SKalle Valo 
620423e3ce3SKalle Valo 	if (phy->analog == 1)
621423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0020, 0x3E1C);
622423e3ce3SKalle Valo 	else
623423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0020, 0x301C);
624423e3ce3SKalle Valo 
625423e3ce3SKalle Valo 	if (phy->analog == 0)
626423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E4, 0x3000);
627423e3ce3SKalle Valo 
628423e3ce3SKalle Valo 	old_channel = (phy->channel == 0xFF) ? 1 : phy->channel;
629423e3ce3SKalle Valo 	/* Force to channel 7, even if not supported. */
630423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, 7, 0);
631423e3ce3SKalle Valo 
632423e3ce3SKalle Valo 	if (phy->radio_ver != 0x2050) {
633423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0075, 0x0080);
634423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0079, 0x0081);
635423e3ce3SKalle Valo 	}
636423e3ce3SKalle Valo 
637423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0020);
638423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0023);
639423e3ce3SKalle Valo 
640423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050) {
641423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0050, 0x0020);
642423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005A, 0x0070);
643423e3ce3SKalle Valo 	}
644423e3ce3SKalle Valo 
645423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x005B, 0x007B);
646423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x005C, 0x00B0);
647423e3ce3SKalle Valo 
648423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A, b43legacy_radio_read16(dev,
649423e3ce3SKalle Valo 				0x007A) | 0x0007);
650423e3ce3SKalle Valo 
651423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, old_channel, 0);
652423e3ce3SKalle Valo 
653423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0014, 0x0080);
654423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0032, 0x00CA);
655423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002A, 0x88A3);
656423e3ce3SKalle Valo 
657423e3ce3SKalle Valo 	b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
658423e3ce3SKalle Valo 
659423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050)
660423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005D, 0x000D);
661423e3ce3SKalle Valo 
662423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03E4, (b43legacy_read16(dev, 0x03E4) &
663423e3ce3SKalle Valo 			  0xFFC0) | 0x0004);
664423e3ce3SKalle Valo }
665423e3ce3SKalle Valo 
666423e3ce3SKalle Valo static void b43legacy_phy_initb6(struct b43legacy_wldev *dev)
667423e3ce3SKalle Valo {
668423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
669423e3ce3SKalle Valo 	u16 offset;
670423e3ce3SKalle Valo 	u16 val;
671423e3ce3SKalle Valo 	u8 old_channel;
672423e3ce3SKalle Valo 
673423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x003E, 0x817A);
674423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A,
675423e3ce3SKalle Valo 				(b43legacy_radio_read16(dev, 0x007A) | 0x0058));
676423e3ce3SKalle Valo 	if (phy->radio_rev == 4 ||
677423e3ce3SKalle Valo 	     phy->radio_rev == 5) {
678423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0051, 0x0037);
679423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, 0x0070);
680423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0053, 0x00B3);
681423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0054, 0x009B);
682423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005A, 0x0088);
683423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005B, 0x0088);
684423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005D, 0x0088);
685423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005E, 0x0088);
686423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007D, 0x0088);
687423e3ce3SKalle Valo 		b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
688423e3ce3SKalle Valo 				      B43legacy_UCODEFLAGS_OFFSET,
689423e3ce3SKalle Valo 				      (b43legacy_shm_read32(dev,
690423e3ce3SKalle Valo 				      B43legacy_SHM_SHARED,
691423e3ce3SKalle Valo 				      B43legacy_UCODEFLAGS_OFFSET)
692423e3ce3SKalle Valo 				      | 0x00000200));
693423e3ce3SKalle Valo 	}
694423e3ce3SKalle Valo 	if (phy->radio_rev == 8) {
695423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0051, 0x0000);
696423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, 0x0040);
697423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0053, 0x00B7);
698423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0054, 0x0098);
699423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005A, 0x0088);
700423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005B, 0x006B);
701423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005C, 0x000F);
702423e3ce3SKalle Valo 		if (dev->dev->bus->sprom.boardflags_lo & 0x8000) {
703423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x005D, 0x00FA);
704423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x005E, 0x00D8);
705423e3ce3SKalle Valo 		} else {
706423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x005D, 0x00F5);
707423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x005E, 0x00B8);
708423e3ce3SKalle Valo 		}
709423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0073, 0x0003);
710423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007D, 0x00A8);
711423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007C, 0x0001);
712423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007E, 0x0008);
713423e3ce3SKalle Valo 	}
714423e3ce3SKalle Valo 	val = 0x1E1F;
715423e3ce3SKalle Valo 	for (offset = 0x0088; offset < 0x0098; offset++) {
716423e3ce3SKalle Valo 		b43legacy_phy_write(dev, offset, val);
717423e3ce3SKalle Valo 		val -= 0x0202;
718423e3ce3SKalle Valo 	}
719423e3ce3SKalle Valo 	val = 0x3E3F;
720423e3ce3SKalle Valo 	for (offset = 0x0098; offset < 0x00A8; offset++) {
721423e3ce3SKalle Valo 		b43legacy_phy_write(dev, offset, val);
722423e3ce3SKalle Valo 		val -= 0x0202;
723423e3ce3SKalle Valo 	}
724423e3ce3SKalle Valo 	val = 0x2120;
725423e3ce3SKalle Valo 	for (offset = 0x00A8; offset < 0x00C8; offset++) {
726423e3ce3SKalle Valo 		b43legacy_phy_write(dev, offset, (val & 0x3F3F));
727423e3ce3SKalle Valo 		val += 0x0202;
728423e3ce3SKalle Valo 	}
729423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_G) {
730423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
731423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A) |
732423e3ce3SKalle Valo 					0x0020);
733423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0051,
734423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x0051) |
735423e3ce3SKalle Valo 					0x0004);
736423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0802,
737423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0802) | 0x0100);
738423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x042B,
739423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x042B) | 0x2000);
740423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x5B, 0x0000);
741423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x5C, 0x0000);
742423e3ce3SKalle Valo 	}
743423e3ce3SKalle Valo 
744423e3ce3SKalle Valo 	old_channel = phy->channel;
745423e3ce3SKalle Valo 	if (old_channel >= 8)
746423e3ce3SKalle Valo 		b43legacy_radio_selectchannel(dev, 1, 0);
747423e3ce3SKalle Valo 	else
748423e3ce3SKalle Valo 		b43legacy_radio_selectchannel(dev, 13, 0);
749423e3ce3SKalle Valo 
750423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0020);
751423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0050, 0x0023);
752423e3ce3SKalle Valo 	udelay(40);
753423e3ce3SKalle Valo 	if (phy->radio_rev < 6 || phy->radio_rev == 8) {
754423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007C,
755423e3ce3SKalle Valo 					(b43legacy_radio_read16(dev, 0x007C)
756423e3ce3SKalle Valo 					| 0x0002));
757423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0050, 0x0020);
758423e3ce3SKalle Valo 	}
759423e3ce3SKalle Valo 	if (phy->radio_rev <= 2) {
760423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0050, 0x0020);
761423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005A, 0x0070);
762423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005B, 0x007B);
763423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005C, 0x00B0);
764423e3ce3SKalle Valo 	}
765423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A,
766423e3ce3SKalle Valo 				(b43legacy_radio_read16(dev,
767423e3ce3SKalle Valo 				0x007A) & 0x00F8) | 0x0007);
768423e3ce3SKalle Valo 
769423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, old_channel, 0);
770423e3ce3SKalle Valo 
771423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0014, 0x0200);
772423e3ce3SKalle Valo 	if (phy->radio_rev >= 6)
773423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, 0x88C2);
774423e3ce3SKalle Valo 	else
775423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, 0x8AC0);
776423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0038, 0x0668);
777423e3ce3SKalle Valo 	b43legacy_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF);
778423e3ce3SKalle Valo 	if (phy->radio_rev == 4 || phy->radio_rev == 5)
779423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005D, (b43legacy_phy_read(dev,
780423e3ce3SKalle Valo 				    0x005D) & 0xFF80) | 0x0003);
781423e3ce3SKalle Valo 	if (phy->radio_rev <= 2)
782423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005D, 0x000D);
783423e3ce3SKalle Valo 
784423e3ce3SKalle Valo 	if (phy->analog == 4) {
785423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E4, 0x0009);
786423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x61, b43legacy_phy_read(dev, 0x61)
787423e3ce3SKalle Valo 				    & 0xFFF);
788423e3ce3SKalle Valo 	} else
789423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0002, (b43legacy_phy_read(dev,
790423e3ce3SKalle Valo 				    0x0002) & 0xFFC0) | 0x0004);
791423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_G)
792423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E6, 0x0);
793423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_B) {
794423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E6, 0x8140);
795423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0016, 0x0410);
796423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0017, 0x0820);
797423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0062, 0x0007);
798423e3ce3SKalle Valo 		b43legacy_radio_init2050(dev);
799423e3ce3SKalle Valo 		b43legacy_phy_lo_g_measure(dev);
800423e3ce3SKalle Valo 		if (dev->dev->bus->sprom.boardflags_lo &
801423e3ce3SKalle Valo 		    B43legacy_BFL_RSSI) {
802423e3ce3SKalle Valo 			b43legacy_calc_nrssi_slope(dev);
803423e3ce3SKalle Valo 			b43legacy_calc_nrssi_threshold(dev);
804423e3ce3SKalle Valo 		}
805423e3ce3SKalle Valo 		b43legacy_phy_init_pctl(dev);
806423e3ce3SKalle Valo 	}
807423e3ce3SKalle Valo }
808423e3ce3SKalle Valo 
809423e3ce3SKalle Valo static void b43legacy_calc_loopback_gain(struct b43legacy_wldev *dev)
810423e3ce3SKalle Valo {
811423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
812423e3ce3SKalle Valo 	u16 backup_phy[15] = {0};
813423e3ce3SKalle Valo 	u16 backup_radio[3];
814423e3ce3SKalle Valo 	u16 backup_bband;
815423e3ce3SKalle Valo 	u16 i;
816423e3ce3SKalle Valo 	u16 loop1_cnt;
817423e3ce3SKalle Valo 	u16 loop1_done;
818423e3ce3SKalle Valo 	u16 loop1_omitted;
819423e3ce3SKalle Valo 	u16 loop2_done;
820423e3ce3SKalle Valo 
821423e3ce3SKalle Valo 	backup_phy[0] = b43legacy_phy_read(dev, 0x0429);
822423e3ce3SKalle Valo 	backup_phy[1] = b43legacy_phy_read(dev, 0x0001);
823423e3ce3SKalle Valo 	backup_phy[2] = b43legacy_phy_read(dev, 0x0811);
824423e3ce3SKalle Valo 	backup_phy[3] = b43legacy_phy_read(dev, 0x0812);
825423e3ce3SKalle Valo 	if (phy->rev != 1) {
826423e3ce3SKalle Valo 		backup_phy[4] = b43legacy_phy_read(dev, 0x0814);
827423e3ce3SKalle Valo 		backup_phy[5] = b43legacy_phy_read(dev, 0x0815);
828423e3ce3SKalle Valo 	}
829423e3ce3SKalle Valo 	backup_phy[6] = b43legacy_phy_read(dev, 0x005A);
830423e3ce3SKalle Valo 	backup_phy[7] = b43legacy_phy_read(dev, 0x0059);
831423e3ce3SKalle Valo 	backup_phy[8] = b43legacy_phy_read(dev, 0x0058);
832423e3ce3SKalle Valo 	backup_phy[9] = b43legacy_phy_read(dev, 0x000A);
833423e3ce3SKalle Valo 	backup_phy[10] = b43legacy_phy_read(dev, 0x0003);
834423e3ce3SKalle Valo 	backup_phy[11] = b43legacy_phy_read(dev, 0x080F);
835423e3ce3SKalle Valo 	backup_phy[12] = b43legacy_phy_read(dev, 0x0810);
836423e3ce3SKalle Valo 	backup_phy[13] = b43legacy_phy_read(dev, 0x002B);
837423e3ce3SKalle Valo 	backup_phy[14] = b43legacy_phy_read(dev, 0x0015);
838423e3ce3SKalle Valo 	b43legacy_phy_read(dev, 0x002D); /* dummy read */
839423e3ce3SKalle Valo 	backup_bband = phy->bbatt;
840423e3ce3SKalle Valo 	backup_radio[0] = b43legacy_radio_read16(dev, 0x0052);
841423e3ce3SKalle Valo 	backup_radio[1] = b43legacy_radio_read16(dev, 0x0043);
842423e3ce3SKalle Valo 	backup_radio[2] = b43legacy_radio_read16(dev, 0x007A);
843423e3ce3SKalle Valo 
844423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0429,
845423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0429) & 0x3FFF);
846423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0001,
847423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0001) & 0x8000);
848423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811,
849423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0811) | 0x0002);
850423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812,
851423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0812) & 0xFFFD);
852423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811,
853423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0811) | 0x0001);
854423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812,
855423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0812) & 0xFFFE);
856423e3ce3SKalle Valo 	if (phy->rev != 1) {
857423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814,
858423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0814) | 0x0001);
859423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815,
860423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0815) & 0xFFFE);
861423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814,
862423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0814) | 0x0002);
863423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815,
864423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0815) & 0xFFFD);
865423e3ce3SKalle Valo 	}
866423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811, b43legacy_phy_read(dev, 0x0811) |
867423e3ce3SKalle Valo 			    0x000C);
868423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812, b43legacy_phy_read(dev, 0x0812) |
869423e3ce3SKalle Valo 			    0x000C);
870423e3ce3SKalle Valo 
871423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811, (b43legacy_phy_read(dev, 0x0811)
872423e3ce3SKalle Valo 			    & 0xFFCF) | 0x0030);
873423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812, (b43legacy_phy_read(dev, 0x0812)
874423e3ce3SKalle Valo 			    & 0xFFCF) | 0x0010);
875423e3ce3SKalle Valo 
876423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x005A, 0x0780);
877423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0059, 0xC810);
878423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0058, 0x000D);
879423e3ce3SKalle Valo 	if (phy->analog == 0)
880423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0003, 0x0122);
881423e3ce3SKalle Valo 	else
882423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x000A,
883423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x000A)
884423e3ce3SKalle Valo 				    | 0x2000);
885423e3ce3SKalle Valo 	if (phy->rev != 1) {
886423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814,
887423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0814) | 0x0004);
888423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815,
889423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0815) & 0xFFFB);
890423e3ce3SKalle Valo 	}
891423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0003,
892423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x0003)
893423e3ce3SKalle Valo 			     & 0xFF9F) | 0x0040);
894423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050 && phy->radio_rev == 2) {
895423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, 0x0000);
896423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043,
897423e3ce3SKalle Valo 					(b43legacy_radio_read16(dev, 0x0043)
898423e3ce3SKalle Valo 					 & 0xFFF0) | 0x0009);
899423e3ce3SKalle Valo 		loop1_cnt = 9;
900423e3ce3SKalle Valo 	} else if (phy->radio_rev == 8) {
901423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043, 0x000F);
902423e3ce3SKalle Valo 		loop1_cnt = 15;
903423e3ce3SKalle Valo 	} else
904423e3ce3SKalle Valo 		loop1_cnt = 0;
905423e3ce3SKalle Valo 
906423e3ce3SKalle Valo 	b43legacy_phy_set_baseband_attenuation(dev, 11);
907423e3ce3SKalle Valo 
908423e3ce3SKalle Valo 	if (phy->rev >= 3)
909423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x080F, 0xC020);
910423e3ce3SKalle Valo 	else
911423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x080F, 0x8020);
912423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0810, 0x0000);
913423e3ce3SKalle Valo 
914423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002B,
915423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x002B)
916423e3ce3SKalle Valo 			     & 0xFFC0) | 0x0001);
917423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002B,
918423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x002B)
919423e3ce3SKalle Valo 			     & 0xC0FF) | 0x0800);
920423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811,
921423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0811) | 0x0100);
922423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812,
923423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0812) & 0xCFFF);
924423e3ce3SKalle Valo 	if (dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_EXTLNA) {
925423e3ce3SKalle Valo 		if (phy->rev >= 7) {
926423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0811,
927423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x0811)
928423e3ce3SKalle Valo 					    | 0x0800);
929423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
930423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x0812)
931423e3ce3SKalle Valo 					    | 0x8000);
932423e3ce3SKalle Valo 		}
933423e3ce3SKalle Valo 	}
934423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A,
935423e3ce3SKalle Valo 				b43legacy_radio_read16(dev, 0x007A)
936423e3ce3SKalle Valo 				& 0x00F7);
937423e3ce3SKalle Valo 
938423e3ce3SKalle Valo 	for (i = 0; i < loop1_cnt; i++) {
939423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043, loop1_cnt);
940423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812,
941423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0812)
942423e3ce3SKalle Valo 				     & 0xF0FF) | (i << 8));
943423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015,
944423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0015)
945423e3ce3SKalle Valo 				     & 0x0FFF) | 0xA000);
946423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015,
947423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0015)
948423e3ce3SKalle Valo 				     & 0x0FFF) | 0xF000);
949423e3ce3SKalle Valo 		udelay(20);
950423e3ce3SKalle Valo 		if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC)
951423e3ce3SKalle Valo 			break;
952423e3ce3SKalle Valo 	}
953423e3ce3SKalle Valo 	loop1_done = i;
954423e3ce3SKalle Valo 	loop1_omitted = loop1_cnt - loop1_done;
955423e3ce3SKalle Valo 
956423e3ce3SKalle Valo 	loop2_done = 0;
957423e3ce3SKalle Valo 	if (loop1_done >= 8) {
958423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812,
959423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0812)
960423e3ce3SKalle Valo 				    | 0x0030);
961423e3ce3SKalle Valo 		for (i = loop1_done - 8; i < 16; i++) {
962423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
963423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0812)
964423e3ce3SKalle Valo 					     & 0xF0FF) | (i << 8));
965423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0015,
966423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0015)
967423e3ce3SKalle Valo 					     & 0x0FFF) | 0xA000);
968423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0015,
969423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0015)
970423e3ce3SKalle Valo 					     & 0x0FFF) | 0xF000);
971423e3ce3SKalle Valo 			udelay(20);
972423e3ce3SKalle Valo 			if (b43legacy_phy_read(dev, 0x002D) >= 0x0DFC)
973423e3ce3SKalle Valo 				break;
974423e3ce3SKalle Valo 		}
975423e3ce3SKalle Valo 	}
976423e3ce3SKalle Valo 
977423e3ce3SKalle Valo 	if (phy->rev != 1) {
978423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814, backup_phy[4]);
979423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815, backup_phy[5]);
980423e3ce3SKalle Valo 	}
981423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x005A, backup_phy[6]);
982423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0059, backup_phy[7]);
983423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0058, backup_phy[8]);
984423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x000A, backup_phy[9]);
985423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0003, backup_phy[10]);
986423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x080F, backup_phy[11]);
987423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0810, backup_phy[12]);
988423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002B, backup_phy[13]);
989423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0015, backup_phy[14]);
990423e3ce3SKalle Valo 
991423e3ce3SKalle Valo 	b43legacy_phy_set_baseband_attenuation(dev, backup_bband);
992423e3ce3SKalle Valo 
993423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0052, backup_radio[0]);
994423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0043, backup_radio[1]);
995423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A, backup_radio[2]);
996423e3ce3SKalle Valo 
997423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811, backup_phy[2] | 0x0003);
998423e3ce3SKalle Valo 	udelay(10);
999423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811, backup_phy[2]);
1000423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812, backup_phy[3]);
1001423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0429, backup_phy[0]);
1002423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0001, backup_phy[1]);
1003423e3ce3SKalle Valo 
1004423e3ce3SKalle Valo 	phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11;
1005423e3ce3SKalle Valo 	phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2;
1006423e3ce3SKalle Valo }
1007423e3ce3SKalle Valo 
1008423e3ce3SKalle Valo static void b43legacy_phy_initg(struct b43legacy_wldev *dev)
1009423e3ce3SKalle Valo {
1010423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1011423e3ce3SKalle Valo 	u16 tmp;
1012423e3ce3SKalle Valo 
1013423e3ce3SKalle Valo 	if (phy->rev == 1)
1014423e3ce3SKalle Valo 		b43legacy_phy_initb5(dev);
1015423e3ce3SKalle Valo 	else
1016423e3ce3SKalle Valo 		b43legacy_phy_initb6(dev);
1017423e3ce3SKalle Valo 	if (phy->rev >= 2 && phy->gmode)
1018423e3ce3SKalle Valo 		b43legacy_phy_inita(dev);
1019423e3ce3SKalle Valo 
1020423e3ce3SKalle Valo 	if (phy->rev >= 2) {
1021423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814, 0x0000);
1022423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815, 0x0000);
1023423e3ce3SKalle Valo 	}
1024423e3ce3SKalle Valo 	if (phy->rev == 2) {
1025423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0811, 0x0000);
1026423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0x00C0);
1027423e3ce3SKalle Valo 	}
1028423e3ce3SKalle Valo 	if (phy->rev > 5) {
1029423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0811, 0x0400);
1030423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0x00C0);
1031423e3ce3SKalle Valo 	}
1032423e3ce3SKalle Valo 	if (phy->gmode) {
1033423e3ce3SKalle Valo 		tmp = b43legacy_phy_read(dev, 0x0400) & 0xFF;
1034423e3ce3SKalle Valo 		if (tmp == 3) {
1035423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C2, 0x1816);
1036423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C3, 0x8606);
1037423e3ce3SKalle Valo 		}
1038423e3ce3SKalle Valo 		if (tmp == 4 || tmp == 5) {
1039423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C2, 0x1816);
1040423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C3, 0x8006);
1041423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04CC,
1042423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev,
1043423e3ce3SKalle Valo 					     0x04CC) & 0x00FF) |
1044423e3ce3SKalle Valo 					     0x1F00);
1045423e3ce3SKalle Valo 		}
1046423e3ce3SKalle Valo 		if (phy->rev >= 2)
1047423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x047E, 0x0078);
1048423e3ce3SKalle Valo 	}
1049423e3ce3SKalle Valo 	if (phy->radio_rev == 8) {
1050423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0801, b43legacy_phy_read(dev, 0x0801)
1051423e3ce3SKalle Valo 				    | 0x0080);
1052423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x043E, b43legacy_phy_read(dev, 0x043E)
1053423e3ce3SKalle Valo 				    | 0x0004);
1054423e3ce3SKalle Valo 	}
1055423e3ce3SKalle Valo 	if (phy->rev >= 2 && phy->gmode)
1056423e3ce3SKalle Valo 		b43legacy_calc_loopback_gain(dev);
1057423e3ce3SKalle Valo 	if (phy->radio_rev != 8) {
1058423e3ce3SKalle Valo 		if (phy->initval == 0xFFFF)
1059423e3ce3SKalle Valo 			phy->initval = b43legacy_radio_init2050(dev);
1060423e3ce3SKalle Valo 		else
1061423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x0078, phy->initval);
1062423e3ce3SKalle Valo 	}
1063423e3ce3SKalle Valo 	if (phy->txctl2 == 0xFFFF)
1064423e3ce3SKalle Valo 		b43legacy_phy_lo_g_measure(dev);
1065423e3ce3SKalle Valo 	else {
1066423e3ce3SKalle Valo 		if (phy->radio_ver == 0x2050 && phy->radio_rev == 8)
1067423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x0052,
1068423e3ce3SKalle Valo 						(phy->txctl1 << 4) |
1069423e3ce3SKalle Valo 						phy->txctl2);
1070423e3ce3SKalle Valo 		else
1071423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x0052,
1072423e3ce3SKalle Valo 						(b43legacy_radio_read16(dev,
1073423e3ce3SKalle Valo 						 0x0052) & 0xFFF0) |
1074423e3ce3SKalle Valo 						 phy->txctl1);
1075423e3ce3SKalle Valo 		if (phy->rev >= 6)
1076423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0036,
1077423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0036)
1078423e3ce3SKalle Valo 					     & 0x0FFF) | (phy->txctl2 << 12));
1079423e3ce3SKalle Valo 		if (dev->dev->bus->sprom.boardflags_lo &
1080423e3ce3SKalle Valo 		    B43legacy_BFL_PACTRL)
1081423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002E, 0x8075);
1082423e3ce3SKalle Valo 		else
1083423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002E, 0x807F);
1084423e3ce3SKalle Valo 		if (phy->rev < 2)
1085423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002F, 0x0101);
1086423e3ce3SKalle Valo 		else
1087423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002F, 0x0202);
1088423e3ce3SKalle Valo 	}
1089423e3ce3SKalle Valo 	if (phy->gmode) {
1090423e3ce3SKalle Valo 		b43legacy_phy_lo_adjust(dev, 0);
1091423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x080F, 0x8078);
1092423e3ce3SKalle Valo 	}
1093423e3ce3SKalle Valo 
1094423e3ce3SKalle Valo 	if (!(dev->dev->bus->sprom.boardflags_lo & B43legacy_BFL_RSSI)) {
1095423e3ce3SKalle Valo 		/* The specs state to update the NRSSI LT with
1096423e3ce3SKalle Valo 		 * the value 0x7FFFFFFF here. I think that is some weird
1097423e3ce3SKalle Valo 		 * compiler optimization in the original driver.
1098423e3ce3SKalle Valo 		 * Essentially, what we do here is resetting all NRSSI LT
1099423e3ce3SKalle Valo 		 * entries to -32 (see the clamp_val() in nrssi_hw_update())
1100423e3ce3SKalle Valo 		 */
1101423e3ce3SKalle Valo 		b43legacy_nrssi_hw_update(dev, 0xFFFF);
1102423e3ce3SKalle Valo 		b43legacy_calc_nrssi_threshold(dev);
1103423e3ce3SKalle Valo 	} else if (phy->gmode || phy->rev >= 2) {
1104423e3ce3SKalle Valo 		if (phy->nrssi[0] == -1000) {
1105423e3ce3SKalle Valo 			B43legacy_WARN_ON(phy->nrssi[1] != -1000);
1106423e3ce3SKalle Valo 			b43legacy_calc_nrssi_slope(dev);
1107423e3ce3SKalle Valo 		} else {
1108423e3ce3SKalle Valo 			B43legacy_WARN_ON(phy->nrssi[1] == -1000);
1109423e3ce3SKalle Valo 			b43legacy_calc_nrssi_threshold(dev);
1110423e3ce3SKalle Valo 		}
1111423e3ce3SKalle Valo 	}
1112423e3ce3SKalle Valo 	if (phy->radio_rev == 8)
1113423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0805, 0x3230);
1114423e3ce3SKalle Valo 	b43legacy_phy_init_pctl(dev);
1115423e3ce3SKalle Valo 	if (dev->dev->bus->chip_id == 0x4306
1116423e3ce3SKalle Valo 	    && dev->dev->bus->chip_package == 2) {
1117423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0429,
1118423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0429) & 0xBFFF);
1119423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04C3,
1120423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x04C3) & 0x7FFF);
1121423e3ce3SKalle Valo 	}
1122423e3ce3SKalle Valo }
1123423e3ce3SKalle Valo 
1124423e3ce3SKalle Valo static u16 b43legacy_phy_lo_b_r15_loop(struct b43legacy_wldev *dev)
1125423e3ce3SKalle Valo {
1126423e3ce3SKalle Valo 	int i;
1127423e3ce3SKalle Valo 	u16 ret = 0;
1128423e3ce3SKalle Valo 	unsigned long flags;
1129423e3ce3SKalle Valo 
1130423e3ce3SKalle Valo 	local_irq_save(flags);
1131423e3ce3SKalle Valo 	for (i = 0; i < 10; i++) {
1132423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xAFA0);
1133423e3ce3SKalle Valo 		udelay(1);
1134423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xEFA0);
1135423e3ce3SKalle Valo 		udelay(10);
1136423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xFFA0);
1137423e3ce3SKalle Valo 		udelay(40);
1138423e3ce3SKalle Valo 		ret += b43legacy_phy_read(dev, 0x002C);
1139423e3ce3SKalle Valo 	}
1140423e3ce3SKalle Valo 	local_irq_restore(flags);
1141423e3ce3SKalle Valo 	b43legacy_voluntary_preempt();
1142423e3ce3SKalle Valo 
1143423e3ce3SKalle Valo 	return ret;
1144423e3ce3SKalle Valo }
1145423e3ce3SKalle Valo 
1146423e3ce3SKalle Valo void b43legacy_phy_lo_b_measure(struct b43legacy_wldev *dev)
1147423e3ce3SKalle Valo {
1148423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1149423e3ce3SKalle Valo 	u16 regstack[12] = { 0 };
1150423e3ce3SKalle Valo 	u16 mls;
1151423e3ce3SKalle Valo 	u16 fval;
1152423e3ce3SKalle Valo 	int i;
1153423e3ce3SKalle Valo 	int j;
1154423e3ce3SKalle Valo 
1155423e3ce3SKalle Valo 	regstack[0] = b43legacy_phy_read(dev, 0x0015);
1156423e3ce3SKalle Valo 	regstack[1] = b43legacy_radio_read16(dev, 0x0052) & 0xFFF0;
1157423e3ce3SKalle Valo 
1158423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2053) {
1159423e3ce3SKalle Valo 		regstack[2] = b43legacy_phy_read(dev, 0x000A);
1160423e3ce3SKalle Valo 		regstack[3] = b43legacy_phy_read(dev, 0x002A);
1161423e3ce3SKalle Valo 		regstack[4] = b43legacy_phy_read(dev, 0x0035);
1162423e3ce3SKalle Valo 		regstack[5] = b43legacy_phy_read(dev, 0x0003);
1163423e3ce3SKalle Valo 		regstack[6] = b43legacy_phy_read(dev, 0x0001);
1164423e3ce3SKalle Valo 		regstack[7] = b43legacy_phy_read(dev, 0x0030);
1165423e3ce3SKalle Valo 
1166423e3ce3SKalle Valo 		regstack[8] = b43legacy_radio_read16(dev, 0x0043);
1167423e3ce3SKalle Valo 		regstack[9] = b43legacy_radio_read16(dev, 0x007A);
1168423e3ce3SKalle Valo 		regstack[10] = b43legacy_read16(dev, 0x03EC);
1169423e3ce3SKalle Valo 		regstack[11] = b43legacy_radio_read16(dev, 0x0052) & 0x00F0;
1170423e3ce3SKalle Valo 
1171423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0030, 0x00FF);
1172423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03EC, 0x3F3F);
1173423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0035, regstack[4] & 0xFF7F);
1174423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0);
1175423e3ce3SKalle Valo 	}
1176423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0015, 0xB000);
1177423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002B, 0x0004);
1178423e3ce3SKalle Valo 
1179423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2053) {
1180423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002B, 0x0203);
1181423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, 0x08A3);
1182423e3ce3SKalle Valo 	}
1183423e3ce3SKalle Valo 
1184423e3ce3SKalle Valo 	phy->minlowsig[0] = 0xFFFF;
1185423e3ce3SKalle Valo 
1186423e3ce3SKalle Valo 	for (i = 0; i < 4; i++) {
1187423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, regstack[1] | i);
1188423e3ce3SKalle Valo 		b43legacy_phy_lo_b_r15_loop(dev);
1189423e3ce3SKalle Valo 	}
1190423e3ce3SKalle Valo 	for (i = 0; i < 10; i++) {
1191423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, regstack[1] | i);
1192423e3ce3SKalle Valo 		mls = b43legacy_phy_lo_b_r15_loop(dev) / 10;
1193423e3ce3SKalle Valo 		if (mls < phy->minlowsig[0]) {
1194423e3ce3SKalle Valo 			phy->minlowsig[0] = mls;
1195423e3ce3SKalle Valo 			phy->minlowsigpos[0] = i;
1196423e3ce3SKalle Valo 		}
1197423e3ce3SKalle Valo 	}
1198423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0052, regstack[1]
1199423e3ce3SKalle Valo 				| phy->minlowsigpos[0]);
1200423e3ce3SKalle Valo 
1201423e3ce3SKalle Valo 	phy->minlowsig[1] = 0xFFFF;
1202423e3ce3SKalle Valo 
1203423e3ce3SKalle Valo 	for (i = -4; i < 5; i += 2) {
1204423e3ce3SKalle Valo 		for (j = -4; j < 5; j += 2) {
1205423e3ce3SKalle Valo 			if (j < 0)
1206423e3ce3SKalle Valo 				fval = (0x0100 * i) + j + 0x0100;
1207423e3ce3SKalle Valo 			else
1208423e3ce3SKalle Valo 				fval = (0x0100 * i) + j;
1209423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002F, fval);
1210423e3ce3SKalle Valo 			mls = b43legacy_phy_lo_b_r15_loop(dev) / 10;
1211423e3ce3SKalle Valo 			if (mls < phy->minlowsig[1]) {
1212423e3ce3SKalle Valo 				phy->minlowsig[1] = mls;
1213423e3ce3SKalle Valo 				phy->minlowsigpos[1] = fval;
1214423e3ce3SKalle Valo 			}
1215423e3ce3SKalle Valo 		}
1216423e3ce3SKalle Valo 	}
1217423e3ce3SKalle Valo 	phy->minlowsigpos[1] += 0x0101;
1218423e3ce3SKalle Valo 
1219423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002F, phy->minlowsigpos[1]);
1220423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2053) {
1221423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x000A, regstack[2]);
1222423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, regstack[3]);
1223423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0035, regstack[4]);
1224423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0003, regstack[5]);
1225423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0001, regstack[6]);
1226423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0030, regstack[7]);
1227423e3ce3SKalle Valo 
1228423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043, regstack[8]);
1229423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A, regstack[9]);
1230423e3ce3SKalle Valo 
1231423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052,
1232423e3ce3SKalle Valo 					(b43legacy_radio_read16(dev, 0x0052)
1233423e3ce3SKalle Valo 					& 0x000F) | regstack[11]);
1234423e3ce3SKalle Valo 
1235423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03EC, regstack[10]);
1236423e3ce3SKalle Valo 	}
1237423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0015, regstack[0]);
1238423e3ce3SKalle Valo }
1239423e3ce3SKalle Valo 
1240423e3ce3SKalle Valo static inline
1241423e3ce3SKalle Valo u16 b43legacy_phy_lo_g_deviation_subval(struct b43legacy_wldev *dev,
1242423e3ce3SKalle Valo 					u16 control)
1243423e3ce3SKalle Valo {
1244423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1245423e3ce3SKalle Valo 	u16 ret;
1246423e3ce3SKalle Valo 	unsigned long flags;
1247423e3ce3SKalle Valo 
1248423e3ce3SKalle Valo 	local_irq_save(flags);
1249423e3ce3SKalle Valo 	if (phy->gmode) {
1250423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x15, 0xE300);
1251423e3ce3SKalle Valo 		control <<= 8;
1252423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, control | 0x00B0);
1253423e3ce3SKalle Valo 		udelay(5);
1254423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, control | 0x00B2);
1255423e3ce3SKalle Valo 		udelay(2);
1256423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, control | 0x00B3);
1257423e3ce3SKalle Valo 		udelay(4);
1258423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xF300);
1259423e3ce3SKalle Valo 		udelay(8);
1260423e3ce3SKalle Valo 	} else {
1261423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, control | 0xEFA0);
1262423e3ce3SKalle Valo 		udelay(2);
1263423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, control | 0xEFE0);
1264423e3ce3SKalle Valo 		udelay(4);
1265423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, control | 0xFFE0);
1266423e3ce3SKalle Valo 		udelay(8);
1267423e3ce3SKalle Valo 	}
1268423e3ce3SKalle Valo 	ret = b43legacy_phy_read(dev, 0x002D);
1269423e3ce3SKalle Valo 	local_irq_restore(flags);
1270423e3ce3SKalle Valo 	b43legacy_voluntary_preempt();
1271423e3ce3SKalle Valo 
1272423e3ce3SKalle Valo 	return ret;
1273423e3ce3SKalle Valo }
1274423e3ce3SKalle Valo 
1275423e3ce3SKalle Valo static u32 b43legacy_phy_lo_g_singledeviation(struct b43legacy_wldev *dev,
1276423e3ce3SKalle Valo 					      u16 control)
1277423e3ce3SKalle Valo {
1278423e3ce3SKalle Valo 	int i;
1279423e3ce3SKalle Valo 	u32 ret = 0;
1280423e3ce3SKalle Valo 
1281423e3ce3SKalle Valo 	for (i = 0; i < 8; i++)
1282423e3ce3SKalle Valo 		ret += b43legacy_phy_lo_g_deviation_subval(dev, control);
1283423e3ce3SKalle Valo 
1284423e3ce3SKalle Valo 	return ret;
1285423e3ce3SKalle Valo }
1286423e3ce3SKalle Valo 
1287423e3ce3SKalle Valo /* Write the LocalOscillator CONTROL */
1288423e3ce3SKalle Valo static inline
1289423e3ce3SKalle Valo void b43legacy_lo_write(struct b43legacy_wldev *dev,
1290423e3ce3SKalle Valo 			struct b43legacy_lopair *pair)
1291423e3ce3SKalle Valo {
1292423e3ce3SKalle Valo 	u16 value;
1293423e3ce3SKalle Valo 
1294423e3ce3SKalle Valo 	value = (u8)(pair->low);
1295423e3ce3SKalle Valo 	value |= ((u8)(pair->high)) << 8;
1296423e3ce3SKalle Valo 
1297423e3ce3SKalle Valo #ifdef CONFIG_B43LEGACY_DEBUG
1298423e3ce3SKalle Valo 	/* Sanity check. */
1299423e3ce3SKalle Valo 	if (pair->low < -8 || pair->low > 8 ||
1300423e3ce3SKalle Valo 	    pair->high < -8 || pair->high > 8) {
1301423e3ce3SKalle Valo 		b43legacydbg(dev->wl,
1302423e3ce3SKalle Valo 		       "WARNING: Writing invalid LOpair "
1303423e3ce3SKalle Valo 		       "(low: %d, high: %d)\n",
1304423e3ce3SKalle Valo 		       pair->low, pair->high);
1305423e3ce3SKalle Valo 		dump_stack();
1306423e3ce3SKalle Valo 	}
1307423e3ce3SKalle Valo #endif
1308423e3ce3SKalle Valo 
1309423e3ce3SKalle Valo 	b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, value);
1310423e3ce3SKalle Valo }
1311423e3ce3SKalle Valo 
1312423e3ce3SKalle Valo static inline
1313423e3ce3SKalle Valo struct b43legacy_lopair *b43legacy_find_lopair(struct b43legacy_wldev *dev,
1314423e3ce3SKalle Valo 					       u16 bbatt,
1315423e3ce3SKalle Valo 					       u16 rfatt,
1316423e3ce3SKalle Valo 					       u16 tx)
1317423e3ce3SKalle Valo {
1318423e3ce3SKalle Valo 	static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 };
1319423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1320423e3ce3SKalle Valo 
1321423e3ce3SKalle Valo 	if (bbatt > 6)
1322423e3ce3SKalle Valo 		bbatt = 6;
1323423e3ce3SKalle Valo 	B43legacy_WARN_ON(rfatt >= 10);
1324423e3ce3SKalle Valo 
1325423e3ce3SKalle Valo 	if (tx == 3)
1326423e3ce3SKalle Valo 		return b43legacy_get_lopair(phy, rfatt, bbatt);
1327423e3ce3SKalle Valo 	return b43legacy_get_lopair(phy, dict[rfatt], bbatt);
1328423e3ce3SKalle Valo }
1329423e3ce3SKalle Valo 
1330423e3ce3SKalle Valo static inline
1331423e3ce3SKalle Valo struct b43legacy_lopair *b43legacy_current_lopair(struct b43legacy_wldev *dev)
1332423e3ce3SKalle Valo {
1333423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1334423e3ce3SKalle Valo 
1335423e3ce3SKalle Valo 	return b43legacy_find_lopair(dev, phy->bbatt,
1336423e3ce3SKalle Valo 				     phy->rfatt, phy->txctl1);
1337423e3ce3SKalle Valo }
1338423e3ce3SKalle Valo 
1339423e3ce3SKalle Valo /* Adjust B/G LO */
1340423e3ce3SKalle Valo void b43legacy_phy_lo_adjust(struct b43legacy_wldev *dev, int fixed)
1341423e3ce3SKalle Valo {
1342423e3ce3SKalle Valo 	struct b43legacy_lopair *pair;
1343423e3ce3SKalle Valo 
1344423e3ce3SKalle Valo 	if (fixed) {
1345423e3ce3SKalle Valo 		/* Use fixed values. Only for initialization. */
1346423e3ce3SKalle Valo 		pair = b43legacy_find_lopair(dev, 2, 3, 0);
1347423e3ce3SKalle Valo 	} else
1348423e3ce3SKalle Valo 		pair = b43legacy_current_lopair(dev);
1349423e3ce3SKalle Valo 	b43legacy_lo_write(dev, pair);
1350423e3ce3SKalle Valo }
1351423e3ce3SKalle Valo 
1352423e3ce3SKalle Valo static void b43legacy_phy_lo_g_measure_txctl2(struct b43legacy_wldev *dev)
1353423e3ce3SKalle Valo {
1354423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1355423e3ce3SKalle Valo 	u16 txctl2 = 0;
1356423e3ce3SKalle Valo 	u16 i;
1357423e3ce3SKalle Valo 	u32 smallest;
1358423e3ce3SKalle Valo 	u32 tmp;
1359423e3ce3SKalle Valo 
1360423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0052, 0x0000);
1361423e3ce3SKalle Valo 	udelay(10);
1362423e3ce3SKalle Valo 	smallest = b43legacy_phy_lo_g_singledeviation(dev, 0);
1363423e3ce3SKalle Valo 	for (i = 0; i < 16; i++) {
1364423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, i);
1365423e3ce3SKalle Valo 		udelay(10);
1366423e3ce3SKalle Valo 		tmp = b43legacy_phy_lo_g_singledeviation(dev, 0);
1367423e3ce3SKalle Valo 		if (tmp < smallest) {
1368423e3ce3SKalle Valo 			smallest = tmp;
1369423e3ce3SKalle Valo 			txctl2 = i;
1370423e3ce3SKalle Valo 		}
1371423e3ce3SKalle Valo 	}
1372423e3ce3SKalle Valo 	phy->txctl2 = txctl2;
1373423e3ce3SKalle Valo }
1374423e3ce3SKalle Valo 
1375423e3ce3SKalle Valo static
1376423e3ce3SKalle Valo void b43legacy_phy_lo_g_state(struct b43legacy_wldev *dev,
1377423e3ce3SKalle Valo 			      const struct b43legacy_lopair *in_pair,
1378423e3ce3SKalle Valo 			      struct b43legacy_lopair *out_pair,
1379423e3ce3SKalle Valo 			      u16 r27)
1380423e3ce3SKalle Valo {
1381423e3ce3SKalle Valo 	static const struct b43legacy_lopair transitions[8] = {
1382423e3ce3SKalle Valo 		{ .high =  1,  .low =  1, },
1383423e3ce3SKalle Valo 		{ .high =  1,  .low =  0, },
1384423e3ce3SKalle Valo 		{ .high =  1,  .low = -1, },
1385423e3ce3SKalle Valo 		{ .high =  0,  .low = -1, },
1386423e3ce3SKalle Valo 		{ .high = -1,  .low = -1, },
1387423e3ce3SKalle Valo 		{ .high = -1,  .low =  0, },
1388423e3ce3SKalle Valo 		{ .high = -1,  .low =  1, },
1389423e3ce3SKalle Valo 		{ .high =  0,  .low =  1, },
1390423e3ce3SKalle Valo 	};
1391423e3ce3SKalle Valo 	struct b43legacy_lopair lowest_transition = {
1392423e3ce3SKalle Valo 		.high = in_pair->high,
1393423e3ce3SKalle Valo 		.low = in_pair->low,
1394423e3ce3SKalle Valo 	};
1395423e3ce3SKalle Valo 	struct b43legacy_lopair tmp_pair;
1396423e3ce3SKalle Valo 	struct b43legacy_lopair transition;
1397423e3ce3SKalle Valo 	int i = 12;
1398423e3ce3SKalle Valo 	int state = 0;
1399423e3ce3SKalle Valo 	int found_lower;
1400423e3ce3SKalle Valo 	int j;
1401423e3ce3SKalle Valo 	int begin;
1402423e3ce3SKalle Valo 	int end;
1403423e3ce3SKalle Valo 	u32 lowest_deviation;
1404423e3ce3SKalle Valo 	u32 tmp;
1405423e3ce3SKalle Valo 
1406423e3ce3SKalle Valo 	/* Note that in_pair and out_pair can point to the same pair.
1407423e3ce3SKalle Valo 	 * Be careful. */
1408423e3ce3SKalle Valo 
1409423e3ce3SKalle Valo 	b43legacy_lo_write(dev, &lowest_transition);
1410423e3ce3SKalle Valo 	lowest_deviation = b43legacy_phy_lo_g_singledeviation(dev, r27);
1411423e3ce3SKalle Valo 	do {
1412423e3ce3SKalle Valo 		found_lower = 0;
1413423e3ce3SKalle Valo 		B43legacy_WARN_ON(!(state >= 0 && state <= 8));
1414423e3ce3SKalle Valo 		if (state == 0) {
1415423e3ce3SKalle Valo 			begin = 1;
1416423e3ce3SKalle Valo 			end = 8;
1417423e3ce3SKalle Valo 		} else if (state % 2 == 0) {
1418423e3ce3SKalle Valo 			begin = state - 1;
1419423e3ce3SKalle Valo 			end = state + 1;
1420423e3ce3SKalle Valo 		} else {
1421423e3ce3SKalle Valo 			begin = state - 2;
1422423e3ce3SKalle Valo 			end = state + 2;
1423423e3ce3SKalle Valo 		}
1424423e3ce3SKalle Valo 		if (begin < 1)
1425423e3ce3SKalle Valo 			begin += 8;
1426423e3ce3SKalle Valo 		if (end > 8)
1427423e3ce3SKalle Valo 			end -= 8;
1428423e3ce3SKalle Valo 
1429423e3ce3SKalle Valo 		j = begin;
1430423e3ce3SKalle Valo 		tmp_pair.high = lowest_transition.high;
1431423e3ce3SKalle Valo 		tmp_pair.low = lowest_transition.low;
1432423e3ce3SKalle Valo 		while (1) {
1433423e3ce3SKalle Valo 			B43legacy_WARN_ON(!(j >= 1 && j <= 8));
1434423e3ce3SKalle Valo 			transition.high = tmp_pair.high +
1435423e3ce3SKalle Valo 					  transitions[j - 1].high;
1436423e3ce3SKalle Valo 			transition.low = tmp_pair.low + transitions[j - 1].low;
1437423e3ce3SKalle Valo 			if ((abs(transition.low) < 9)
1438423e3ce3SKalle Valo 			     && (abs(transition.high) < 9)) {
1439423e3ce3SKalle Valo 				b43legacy_lo_write(dev, &transition);
1440423e3ce3SKalle Valo 				tmp = b43legacy_phy_lo_g_singledeviation(dev,
1441423e3ce3SKalle Valo 								       r27);
1442423e3ce3SKalle Valo 				if (tmp < lowest_deviation) {
1443423e3ce3SKalle Valo 					lowest_deviation = tmp;
1444423e3ce3SKalle Valo 					state = j;
1445423e3ce3SKalle Valo 					found_lower = 1;
1446423e3ce3SKalle Valo 
1447423e3ce3SKalle Valo 					lowest_transition.high =
1448423e3ce3SKalle Valo 								transition.high;
1449423e3ce3SKalle Valo 					lowest_transition.low = transition.low;
1450423e3ce3SKalle Valo 				}
1451423e3ce3SKalle Valo 			}
1452423e3ce3SKalle Valo 			if (j == end)
1453423e3ce3SKalle Valo 				break;
1454423e3ce3SKalle Valo 			if (j == 8)
1455423e3ce3SKalle Valo 				j = 1;
1456423e3ce3SKalle Valo 			else
1457423e3ce3SKalle Valo 				j++;
1458423e3ce3SKalle Valo 		}
1459423e3ce3SKalle Valo 	} while (i-- && found_lower);
1460423e3ce3SKalle Valo 
1461423e3ce3SKalle Valo 	out_pair->high = lowest_transition.high;
1462423e3ce3SKalle Valo 	out_pair->low = lowest_transition.low;
1463423e3ce3SKalle Valo }
1464423e3ce3SKalle Valo 
1465423e3ce3SKalle Valo /* Set the baseband attenuation value on chip. */
1466423e3ce3SKalle Valo void b43legacy_phy_set_baseband_attenuation(struct b43legacy_wldev *dev,
1467423e3ce3SKalle Valo 					    u16 bbatt)
1468423e3ce3SKalle Valo {
1469423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1470423e3ce3SKalle Valo 	u16 value;
1471423e3ce3SKalle Valo 
1472423e3ce3SKalle Valo 	if (phy->analog == 0) {
1473423e3ce3SKalle Valo 		value = (b43legacy_read16(dev, 0x03E6) & 0xFFF0);
1474423e3ce3SKalle Valo 		value |= (bbatt & 0x000F);
1475423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E6, value);
1476423e3ce3SKalle Valo 		return;
1477423e3ce3SKalle Valo 	}
1478423e3ce3SKalle Valo 
1479423e3ce3SKalle Valo 	if (phy->analog > 1) {
1480423e3ce3SKalle Valo 		value = b43legacy_phy_read(dev, 0x0060) & 0xFFC3;
1481423e3ce3SKalle Valo 		value |= (bbatt << 2) & 0x003C;
1482423e3ce3SKalle Valo 	} else {
1483423e3ce3SKalle Valo 		value = b43legacy_phy_read(dev, 0x0060) & 0xFF87;
1484423e3ce3SKalle Valo 		value |= (bbatt << 3) & 0x0078;
1485423e3ce3SKalle Valo 	}
1486423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0060, value);
1487423e3ce3SKalle Valo }
1488423e3ce3SKalle Valo 
1489423e3ce3SKalle Valo /* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */
1490423e3ce3SKalle Valo void b43legacy_phy_lo_g_measure(struct b43legacy_wldev *dev)
1491423e3ce3SKalle Valo {
1492423e3ce3SKalle Valo 	static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 };
1493423e3ce3SKalle Valo 	const int is_initializing = (b43legacy_status(dev)
1494423e3ce3SKalle Valo 				     < B43legacy_STAT_STARTED);
1495423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1496423e3ce3SKalle Valo 	u16 h;
1497423e3ce3SKalle Valo 	u16 i;
1498423e3ce3SKalle Valo 	u16 oldi = 0;
1499423e3ce3SKalle Valo 	u16 j;
1500423e3ce3SKalle Valo 	struct b43legacy_lopair control;
1501423e3ce3SKalle Valo 	struct b43legacy_lopair *tmp_control;
1502423e3ce3SKalle Valo 	u16 tmp;
1503423e3ce3SKalle Valo 	u16 regstack[16] = { 0 };
1504423e3ce3SKalle Valo 	u8 oldchannel;
1505423e3ce3SKalle Valo 
1506423e3ce3SKalle Valo 	/* XXX: What are these? */
1507423e3ce3SKalle Valo 	u8 r27 = 0;
1508423e3ce3SKalle Valo 	u16 r31;
1509423e3ce3SKalle Valo 
1510423e3ce3SKalle Valo 	oldchannel = phy->channel;
1511423e3ce3SKalle Valo 	/* Setup */
1512423e3ce3SKalle Valo 	if (phy->gmode) {
1513423e3ce3SKalle Valo 		regstack[0] = b43legacy_phy_read(dev, B43legacy_PHY_G_CRS);
1514423e3ce3SKalle Valo 		regstack[1] = b43legacy_phy_read(dev, 0x0802);
1515423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]
1516423e3ce3SKalle Valo 				    & 0x7FFF);
1517423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC);
1518423e3ce3SKalle Valo 	}
1519423e3ce3SKalle Valo 	regstack[3] = b43legacy_read16(dev, 0x03E2);
1520423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03E2, regstack[3] | 0x8000);
1521423e3ce3SKalle Valo 	regstack[4] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
1522423e3ce3SKalle Valo 	regstack[5] = b43legacy_phy_read(dev, 0x15);
1523423e3ce3SKalle Valo 	regstack[6] = b43legacy_phy_read(dev, 0x2A);
1524423e3ce3SKalle Valo 	regstack[7] = b43legacy_phy_read(dev, 0x35);
1525423e3ce3SKalle Valo 	regstack[8] = b43legacy_phy_read(dev, 0x60);
1526423e3ce3SKalle Valo 	regstack[9] = b43legacy_radio_read16(dev, 0x43);
1527423e3ce3SKalle Valo 	regstack[10] = b43legacy_radio_read16(dev, 0x7A);
1528423e3ce3SKalle Valo 	regstack[11] = b43legacy_radio_read16(dev, 0x52);
1529423e3ce3SKalle Valo 	if (phy->gmode) {
1530423e3ce3SKalle Valo 		regstack[12] = b43legacy_phy_read(dev, 0x0811);
1531423e3ce3SKalle Valo 		regstack[13] = b43legacy_phy_read(dev, 0x0812);
1532423e3ce3SKalle Valo 		regstack[14] = b43legacy_phy_read(dev, 0x0814);
1533423e3ce3SKalle Valo 		regstack[15] = b43legacy_phy_read(dev, 0x0815);
1534423e3ce3SKalle Valo 	}
1535423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, 6, 0);
1536423e3ce3SKalle Valo 	if (phy->gmode) {
1537423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]
1538423e3ce3SKalle Valo 				    & 0x7FFF);
1539423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0802, regstack[1] & 0xFFFC);
1540423e3ce3SKalle Valo 		b43legacy_dummy_transmission(dev);
1541423e3ce3SKalle Valo 	}
1542423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0043, 0x0006);
1543423e3ce3SKalle Valo 
1544423e3ce3SKalle Valo 	b43legacy_phy_set_baseband_attenuation(dev, 2);
1545423e3ce3SKalle Valo 
1546423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, 0x0000);
1547423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002E, 0x007F);
1548423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x080F, 0x0078);
1549423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0035, regstack[7] & ~(1 << 7));
1550423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A, regstack[10] & 0xFFF0);
1551423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002B, 0x0203);
1552423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002A, 0x08A3);
1553423e3ce3SKalle Valo 	if (phy->gmode) {
1554423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814, regstack[14] | 0x0003);
1555423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815, regstack[15] & 0xFFFC);
1556423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0811, 0x01B3);
1557423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, 0x00B2);
1558423e3ce3SKalle Valo 	}
1559423e3ce3SKalle Valo 	if (is_initializing)
1560423e3ce3SKalle Valo 		b43legacy_phy_lo_g_measure_txctl2(dev);
1561423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x080F, 0x8078);
1562423e3ce3SKalle Valo 
1563423e3ce3SKalle Valo 	/* Measure */
1564423e3ce3SKalle Valo 	control.low = 0;
1565423e3ce3SKalle Valo 	control.high = 0;
1566423e3ce3SKalle Valo 	for (h = 0; h < 10; h++) {
1567423e3ce3SKalle Valo 		/* Loop over each possible RadioAttenuation (0-9) */
1568423e3ce3SKalle Valo 		i = pairorder[h];
1569423e3ce3SKalle Valo 		if (is_initializing) {
1570423e3ce3SKalle Valo 			if (i == 3) {
1571423e3ce3SKalle Valo 				control.low = 0;
1572423e3ce3SKalle Valo 				control.high = 0;
1573423e3ce3SKalle Valo 			} else if (((i % 2 == 1) && (oldi % 2 == 1)) ||
1574423e3ce3SKalle Valo 				  ((i % 2 == 0) && (oldi % 2 == 0))) {
1575423e3ce3SKalle Valo 				tmp_control = b43legacy_get_lopair(phy, oldi,
1576423e3ce3SKalle Valo 								   0);
1577423e3ce3SKalle Valo 				memcpy(&control, tmp_control, sizeof(control));
1578423e3ce3SKalle Valo 			} else {
1579423e3ce3SKalle Valo 				tmp_control = b43legacy_get_lopair(phy, 3, 0);
1580423e3ce3SKalle Valo 				memcpy(&control, tmp_control, sizeof(control));
1581423e3ce3SKalle Valo 			}
1582423e3ce3SKalle Valo 		}
1583423e3ce3SKalle Valo 		/* Loop over each possible BasebandAttenuation/2 */
1584423e3ce3SKalle Valo 		for (j = 0; j < 4; j++) {
1585423e3ce3SKalle Valo 			if (is_initializing) {
1586423e3ce3SKalle Valo 				tmp = i * 2 + j;
1587423e3ce3SKalle Valo 				r27 = 0;
1588423e3ce3SKalle Valo 				r31 = 0;
1589423e3ce3SKalle Valo 				if (tmp > 14) {
1590423e3ce3SKalle Valo 					r31 = 1;
1591423e3ce3SKalle Valo 					if (tmp > 17)
1592423e3ce3SKalle Valo 						r27 = 1;
1593423e3ce3SKalle Valo 					if (tmp > 19)
1594423e3ce3SKalle Valo 						r27 = 2;
1595423e3ce3SKalle Valo 				}
1596423e3ce3SKalle Valo 			} else {
1597423e3ce3SKalle Valo 				tmp_control = b43legacy_get_lopair(phy, i,
1598423e3ce3SKalle Valo 								   j * 2);
1599423e3ce3SKalle Valo 				if (!tmp_control->used)
1600423e3ce3SKalle Valo 					continue;
1601423e3ce3SKalle Valo 				memcpy(&control, tmp_control, sizeof(control));
1602423e3ce3SKalle Valo 				r27 = 3;
1603423e3ce3SKalle Valo 				r31 = 0;
1604423e3ce3SKalle Valo 			}
1605423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x43, i);
1606423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x52, phy->txctl2);
1607423e3ce3SKalle Valo 			udelay(10);
1608423e3ce3SKalle Valo 			b43legacy_voluntary_preempt();
1609423e3ce3SKalle Valo 
1610423e3ce3SKalle Valo 			b43legacy_phy_set_baseband_attenuation(dev, j * 2);
1611423e3ce3SKalle Valo 
1612423e3ce3SKalle Valo 			tmp = (regstack[10] & 0xFFF0);
1613423e3ce3SKalle Valo 			if (r31)
1614423e3ce3SKalle Valo 				tmp |= 0x0008;
1615423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x007A, tmp);
1616423e3ce3SKalle Valo 
1617423e3ce3SKalle Valo 			tmp_control = b43legacy_get_lopair(phy, i, j * 2);
1618423e3ce3SKalle Valo 			b43legacy_phy_lo_g_state(dev, &control, tmp_control,
1619423e3ce3SKalle Valo 						 r27);
1620423e3ce3SKalle Valo 		}
1621423e3ce3SKalle Valo 		oldi = i;
1622423e3ce3SKalle Valo 	}
1623423e3ce3SKalle Valo 	/* Loop over each possible RadioAttenuation (10-13) */
1624423e3ce3SKalle Valo 	for (i = 10; i < 14; i++) {
1625423e3ce3SKalle Valo 		/* Loop over each possible BasebandAttenuation/2 */
1626423e3ce3SKalle Valo 		for (j = 0; j < 4; j++) {
1627423e3ce3SKalle Valo 			if (is_initializing) {
1628423e3ce3SKalle Valo 				tmp_control = b43legacy_get_lopair(phy, i - 9,
1629423e3ce3SKalle Valo 								 j * 2);
1630423e3ce3SKalle Valo 				memcpy(&control, tmp_control, sizeof(control));
1631423e3ce3SKalle Valo 				/* FIXME: The next line is wrong, as the
1632423e3ce3SKalle Valo 				 * following if statement can never trigger. */
1633423e3ce3SKalle Valo 				tmp = (i - 9) * 2 + j - 5;
1634423e3ce3SKalle Valo 				r27 = 0;
1635423e3ce3SKalle Valo 				r31 = 0;
1636423e3ce3SKalle Valo 				if (tmp > 14) {
1637423e3ce3SKalle Valo 					r31 = 1;
1638423e3ce3SKalle Valo 					if (tmp > 17)
1639423e3ce3SKalle Valo 						r27 = 1;
1640423e3ce3SKalle Valo 					if (tmp > 19)
1641423e3ce3SKalle Valo 						r27 = 2;
1642423e3ce3SKalle Valo 				}
1643423e3ce3SKalle Valo 			} else {
1644423e3ce3SKalle Valo 				tmp_control = b43legacy_get_lopair(phy, i - 9,
1645423e3ce3SKalle Valo 								   j * 2);
1646423e3ce3SKalle Valo 				if (!tmp_control->used)
1647423e3ce3SKalle Valo 					continue;
1648423e3ce3SKalle Valo 				memcpy(&control, tmp_control, sizeof(control));
1649423e3ce3SKalle Valo 				r27 = 3;
1650423e3ce3SKalle Valo 				r31 = 0;
1651423e3ce3SKalle Valo 			}
1652423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x43, i - 9);
1653423e3ce3SKalle Valo 			/* FIXME: shouldn't txctl1 be zero in the next line
1654423e3ce3SKalle Valo 			 * and 3 in the loop above? */
1655423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x52,
1656423e3ce3SKalle Valo 					      phy->txctl2
1657423e3ce3SKalle Valo 					      | (3/*txctl1*/ << 4));
1658423e3ce3SKalle Valo 			udelay(10);
1659423e3ce3SKalle Valo 			b43legacy_voluntary_preempt();
1660423e3ce3SKalle Valo 
1661423e3ce3SKalle Valo 			b43legacy_phy_set_baseband_attenuation(dev, j * 2);
1662423e3ce3SKalle Valo 
1663423e3ce3SKalle Valo 			tmp = (regstack[10] & 0xFFF0);
1664423e3ce3SKalle Valo 			if (r31)
1665423e3ce3SKalle Valo 				tmp |= 0x0008;
1666423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x7A, tmp);
1667423e3ce3SKalle Valo 
1668423e3ce3SKalle Valo 			tmp_control = b43legacy_get_lopair(phy, i, j * 2);
1669423e3ce3SKalle Valo 			b43legacy_phy_lo_g_state(dev, &control, tmp_control,
1670423e3ce3SKalle Valo 						 r27);
1671423e3ce3SKalle Valo 		}
1672423e3ce3SKalle Valo 	}
1673423e3ce3SKalle Valo 
1674423e3ce3SKalle Valo 	/* Restoration */
1675423e3ce3SKalle Valo 	if (phy->gmode) {
1676423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xE300);
1677423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA0);
1678423e3ce3SKalle Valo 		udelay(5);
1679423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA2);
1680423e3ce3SKalle Valo 		udelay(2);
1681423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, (r27 << 8) | 0xA3);
1682423e3ce3SKalle Valo 		b43legacy_voluntary_preempt();
1683423e3ce3SKalle Valo 	} else
1684423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, r27 | 0xEFA0);
1685423e3ce3SKalle Valo 	b43legacy_phy_lo_adjust(dev, is_initializing);
1686423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002E, 0x807F);
1687423e3ce3SKalle Valo 	if (phy->gmode)
1688423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002F, 0x0202);
1689423e3ce3SKalle Valo 	else
1690423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002F, 0x0101);
1691423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, regstack[4]);
1692423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0015, regstack[5]);
1693423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002A, regstack[6]);
1694423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0035, regstack[7]);
1695423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0060, regstack[8]);
1696423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0043, regstack[9]);
1697423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A, regstack[10]);
1698423e3ce3SKalle Valo 	regstack[11] &= 0x00F0;
1699423e3ce3SKalle Valo 	regstack[11] |= (b43legacy_radio_read16(dev, 0x52) & 0x000F);
1700423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x52, regstack[11]);
1701423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03E2, regstack[3]);
1702423e3ce3SKalle Valo 	if (phy->gmode) {
1703423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0811, regstack[12]);
1704423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812, regstack[13]);
1705423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814, regstack[14]);
1706423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815, regstack[15]);
1707423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS, regstack[0]);
1708423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0802, regstack[1]);
1709423e3ce3SKalle Valo 	}
1710423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, oldchannel, 1);
1711423e3ce3SKalle Valo 
1712423e3ce3SKalle Valo #ifdef CONFIG_B43LEGACY_DEBUG
1713423e3ce3SKalle Valo 	{
1714423e3ce3SKalle Valo 		/* Sanity check for all lopairs. */
1715423e3ce3SKalle Valo 		for (i = 0; i < B43legacy_LO_COUNT; i++) {
1716423e3ce3SKalle Valo 			tmp_control = phy->_lo_pairs + i;
1717423e3ce3SKalle Valo 			if (tmp_control->low < -8 || tmp_control->low > 8 ||
1718423e3ce3SKalle Valo 			    tmp_control->high < -8 || tmp_control->high > 8)
1719423e3ce3SKalle Valo 				b43legacywarn(dev->wl,
1720423e3ce3SKalle Valo 				       "WARNING: Invalid LOpair (low: %d, high:"
1721423e3ce3SKalle Valo 				       " %d, index: %d)\n",
1722423e3ce3SKalle Valo 				       tmp_control->low, tmp_control->high, i);
1723423e3ce3SKalle Valo 		}
1724423e3ce3SKalle Valo 	}
1725423e3ce3SKalle Valo #endif /* CONFIG_B43LEGACY_DEBUG */
1726423e3ce3SKalle Valo }
1727423e3ce3SKalle Valo 
1728423e3ce3SKalle Valo static
1729423e3ce3SKalle Valo void b43legacy_phy_lo_mark_current_used(struct b43legacy_wldev *dev)
1730423e3ce3SKalle Valo {
1731423e3ce3SKalle Valo 	struct b43legacy_lopair *pair;
1732423e3ce3SKalle Valo 
1733423e3ce3SKalle Valo 	pair = b43legacy_current_lopair(dev);
1734423e3ce3SKalle Valo 	pair->used = 1;
1735423e3ce3SKalle Valo }
1736423e3ce3SKalle Valo 
1737423e3ce3SKalle Valo void b43legacy_phy_lo_mark_all_unused(struct b43legacy_wldev *dev)
1738423e3ce3SKalle Valo {
1739423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1740423e3ce3SKalle Valo 	struct b43legacy_lopair *pair;
1741423e3ce3SKalle Valo 	int i;
1742423e3ce3SKalle Valo 
1743423e3ce3SKalle Valo 	for (i = 0; i < B43legacy_LO_COUNT; i++) {
1744423e3ce3SKalle Valo 		pair = phy->_lo_pairs + i;
1745423e3ce3SKalle Valo 		pair->used = 0;
1746423e3ce3SKalle Valo 	}
1747423e3ce3SKalle Valo }
1748423e3ce3SKalle Valo 
1749423e3ce3SKalle Valo /* http://bcm-specs.sipsolutions.net/EstimatePowerOut
1750423e3ce3SKalle Valo  * This function converts a TSSI value to dBm in Q5.2
1751423e3ce3SKalle Valo  */
1752423e3ce3SKalle Valo static s8 b43legacy_phy_estimate_power_out(struct b43legacy_wldev *dev, s8 tssi)
1753423e3ce3SKalle Valo {
1754423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1755423e3ce3SKalle Valo 	s8 dbm = 0;
1756423e3ce3SKalle Valo 	s32 tmp;
1757423e3ce3SKalle Valo 
1758423e3ce3SKalle Valo 	tmp = phy->idle_tssi;
1759423e3ce3SKalle Valo 	tmp += tssi;
1760423e3ce3SKalle Valo 	tmp -= phy->savedpctlreg;
1761423e3ce3SKalle Valo 
1762423e3ce3SKalle Valo 	switch (phy->type) {
1763423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B:
1764423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
1765423e3ce3SKalle Valo 		tmp = clamp_val(tmp, 0x00, 0x3F);
1766423e3ce3SKalle Valo 		dbm = phy->tssi2dbm[tmp];
1767423e3ce3SKalle Valo 		break;
1768423e3ce3SKalle Valo 	default:
1769423e3ce3SKalle Valo 		B43legacy_BUG_ON(1);
1770423e3ce3SKalle Valo 	}
1771423e3ce3SKalle Valo 
1772423e3ce3SKalle Valo 	return dbm;
1773423e3ce3SKalle Valo }
1774423e3ce3SKalle Valo 
1775423e3ce3SKalle Valo /* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
1776423e3ce3SKalle Valo void b43legacy_phy_xmitpower(struct b43legacy_wldev *dev)
1777423e3ce3SKalle Valo {
1778423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1779423e3ce3SKalle Valo 	u16 tmp;
1780423e3ce3SKalle Valo 	u16 txpower;
1781423e3ce3SKalle Valo 	s8 v0;
1782423e3ce3SKalle Valo 	s8 v1;
1783423e3ce3SKalle Valo 	s8 v2;
1784423e3ce3SKalle Valo 	s8 v3;
1785423e3ce3SKalle Valo 	s8 average;
1786423e3ce3SKalle Valo 	int max_pwr;
1787423e3ce3SKalle Valo 	s16 desired_pwr;
1788423e3ce3SKalle Valo 	s16 estimated_pwr;
1789423e3ce3SKalle Valo 	s16 pwr_adjust;
1790423e3ce3SKalle Valo 	s16 radio_att_delta;
1791423e3ce3SKalle Valo 	s16 baseband_att_delta;
1792423e3ce3SKalle Valo 	s16 radio_attenuation;
1793423e3ce3SKalle Valo 	s16 baseband_attenuation;
1794423e3ce3SKalle Valo 
1795423e3ce3SKalle Valo 	if (phy->savedpctlreg == 0xFFFF)
1796423e3ce3SKalle Valo 		return;
1797423e3ce3SKalle Valo 	if ((dev->dev->bus->boardinfo.type == 0x0416) &&
1798423e3ce3SKalle Valo 	    is_bcm_board_vendor(dev))
1799423e3ce3SKalle Valo 		return;
1800423e3ce3SKalle Valo #ifdef CONFIG_B43LEGACY_DEBUG
1801423e3ce3SKalle Valo 	if (phy->manual_txpower_control)
1802423e3ce3SKalle Valo 		return;
1803423e3ce3SKalle Valo #endif
1804423e3ce3SKalle Valo 
1805423e3ce3SKalle Valo 	B43legacy_BUG_ON(!(phy->type == B43legacy_PHYTYPE_B ||
1806423e3ce3SKalle Valo 			 phy->type == B43legacy_PHYTYPE_G));
1807423e3ce3SKalle Valo 	tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0058);
1808423e3ce3SKalle Valo 	v0 = (s8)(tmp & 0x00FF);
1809423e3ce3SKalle Valo 	v1 = (s8)((tmp & 0xFF00) >> 8);
1810423e3ce3SKalle Valo 	tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005A);
1811423e3ce3SKalle Valo 	v2 = (s8)(tmp & 0x00FF);
1812423e3ce3SKalle Valo 	v3 = (s8)((tmp & 0xFF00) >> 8);
1813423e3ce3SKalle Valo 	tmp = 0;
1814423e3ce3SKalle Valo 
1815423e3ce3SKalle Valo 	if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) {
1816423e3ce3SKalle Valo 		tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED,
1817423e3ce3SKalle Valo 					 0x0070);
1818423e3ce3SKalle Valo 		v0 = (s8)(tmp & 0x00FF);
1819423e3ce3SKalle Valo 		v1 = (s8)((tmp & 0xFF00) >> 8);
1820423e3ce3SKalle Valo 		tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED,
1821423e3ce3SKalle Valo 					 0x0072);
1822423e3ce3SKalle Valo 		v2 = (s8)(tmp & 0x00FF);
1823423e3ce3SKalle Valo 		v3 = (s8)((tmp & 0xFF00) >> 8);
1824423e3ce3SKalle Valo 		if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F)
1825423e3ce3SKalle Valo 			return;
1826423e3ce3SKalle Valo 		v0 = (v0 + 0x20) & 0x3F;
1827423e3ce3SKalle Valo 		v1 = (v1 + 0x20) & 0x3F;
1828423e3ce3SKalle Valo 		v2 = (v2 + 0x20) & 0x3F;
1829423e3ce3SKalle Valo 		v3 = (v3 + 0x20) & 0x3F;
1830423e3ce3SKalle Valo 		tmp = 1;
1831423e3ce3SKalle Valo 	}
1832423e3ce3SKalle Valo 	b43legacy_radio_clear_tssi(dev);
1833423e3ce3SKalle Valo 
1834423e3ce3SKalle Valo 	average = (v0 + v1 + v2 + v3 + 2) / 4;
1835423e3ce3SKalle Valo 
1836423e3ce3SKalle Valo 	if (tmp && (b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x005E)
1837423e3ce3SKalle Valo 	    & 0x8))
1838423e3ce3SKalle Valo 		average -= 13;
1839423e3ce3SKalle Valo 
1840423e3ce3SKalle Valo 	estimated_pwr = b43legacy_phy_estimate_power_out(dev, average);
1841423e3ce3SKalle Valo 
1842423e3ce3SKalle Valo 	max_pwr = dev->dev->bus->sprom.maxpwr_bg;
1843423e3ce3SKalle Valo 
1844423e3ce3SKalle Valo 	if ((dev->dev->bus->sprom.boardflags_lo
1845423e3ce3SKalle Valo 	     & B43legacy_BFL_PACTRL) &&
1846423e3ce3SKalle Valo 	    (phy->type == B43legacy_PHYTYPE_G))
1847423e3ce3SKalle Valo 		max_pwr -= 0x3;
1848423e3ce3SKalle Valo 	if (unlikely(max_pwr <= 0)) {
1849423e3ce3SKalle Valo 		b43legacywarn(dev->wl, "Invalid max-TX-power value in SPROM."
1850423e3ce3SKalle Valo 			"\n");
1851423e3ce3SKalle Valo 		max_pwr = 74; /* fake it */
1852423e3ce3SKalle Valo 		dev->dev->bus->sprom.maxpwr_bg = max_pwr;
1853423e3ce3SKalle Valo 	}
1854423e3ce3SKalle Valo 
1855423e3ce3SKalle Valo 	/* Use regulatory information to get the maximum power.
1856423e3ce3SKalle Valo 	 * In the absence of such data from mac80211, we will use 20 dBm, which
1857423e3ce3SKalle Valo 	 * is the value for the EU, US, Canada, and most of the world.
1858423e3ce3SKalle Valo 	 * The regulatory maximum is reduced by the antenna gain (from sprom)
1859423e3ce3SKalle Valo 	 * and 1.5 dBm (a safety factor??). The result is in Q5.2 format
1860423e3ce3SKalle Valo 	 * which accounts for the factor of 4 */
1861423e3ce3SKalle Valo #define REG_MAX_PWR 20
1862423e3ce3SKalle Valo 	max_pwr = min(REG_MAX_PWR * 4
1863423e3ce3SKalle Valo 		      - dev->dev->bus->sprom.antenna_gain.a0
1864423e3ce3SKalle Valo 		      - 0x6, max_pwr);
1865423e3ce3SKalle Valo 
1866423e3ce3SKalle Valo 	/* find the desired power in Q5.2 - power_level is in dBm
1867423e3ce3SKalle Valo 	 * and limit it - max_pwr is already in Q5.2 */
1868423e3ce3SKalle Valo 	desired_pwr = clamp_val(phy->power_level << 2, 0, max_pwr);
1869423e3ce3SKalle Valo 	if (b43legacy_debug(dev, B43legacy_DBG_XMITPOWER))
1870423e3ce3SKalle Valo 		b43legacydbg(dev->wl, "Current TX power output: " Q52_FMT
1871423e3ce3SKalle Valo 		       " dBm, Desired TX power output: " Q52_FMT
1872423e3ce3SKalle Valo 		       " dBm\n", Q52_ARG(estimated_pwr),
1873423e3ce3SKalle Valo 		       Q52_ARG(desired_pwr));
1874423e3ce3SKalle Valo 	/* Check if we need to adjust the current power. The factor of 2 is
1875423e3ce3SKalle Valo 	 * for damping */
1876423e3ce3SKalle Valo 	pwr_adjust = (desired_pwr - estimated_pwr) / 2;
1877423e3ce3SKalle Valo 	/* RF attenuation delta
1878423e3ce3SKalle Valo 	 * The minus sign is because lower attenuation => more power */
1879423e3ce3SKalle Valo 	radio_att_delta = -(pwr_adjust + 7) >> 3;
1880423e3ce3SKalle Valo 	/* Baseband attenuation delta */
1881423e3ce3SKalle Valo 	baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta);
1882423e3ce3SKalle Valo 	/* Do we need to adjust anything? */
1883423e3ce3SKalle Valo 	if ((radio_att_delta == 0) && (baseband_att_delta == 0)) {
1884423e3ce3SKalle Valo 		b43legacy_phy_lo_mark_current_used(dev);
1885423e3ce3SKalle Valo 		return;
1886423e3ce3SKalle Valo 	}
1887423e3ce3SKalle Valo 
1888423e3ce3SKalle Valo 	/* Calculate the new attenuation values. */
1889423e3ce3SKalle Valo 	baseband_attenuation = phy->bbatt;
1890423e3ce3SKalle Valo 	baseband_attenuation += baseband_att_delta;
1891423e3ce3SKalle Valo 	radio_attenuation = phy->rfatt;
1892423e3ce3SKalle Valo 	radio_attenuation += radio_att_delta;
1893423e3ce3SKalle Valo 
1894423e3ce3SKalle Valo 	/* Get baseband and radio attenuation values into permitted ranges.
1895423e3ce3SKalle Valo 	 * baseband 0-11, radio 0-9.
1896423e3ce3SKalle Valo 	 * Radio attenuation affects power level 4 times as much as baseband.
1897423e3ce3SKalle Valo 	 */
1898423e3ce3SKalle Valo 	if (radio_attenuation < 0) {
1899423e3ce3SKalle Valo 		baseband_attenuation -= (4 * -radio_attenuation);
1900423e3ce3SKalle Valo 		radio_attenuation = 0;
1901423e3ce3SKalle Valo 	} else if (radio_attenuation > 9) {
1902423e3ce3SKalle Valo 		baseband_attenuation += (4 * (radio_attenuation - 9));
1903423e3ce3SKalle Valo 		radio_attenuation = 9;
1904423e3ce3SKalle Valo 	} else {
1905423e3ce3SKalle Valo 		while (baseband_attenuation < 0 && radio_attenuation > 0) {
1906423e3ce3SKalle Valo 			baseband_attenuation += 4;
1907423e3ce3SKalle Valo 			radio_attenuation--;
1908423e3ce3SKalle Valo 		}
1909423e3ce3SKalle Valo 		while (baseband_attenuation > 11 && radio_attenuation < 9) {
1910423e3ce3SKalle Valo 			baseband_attenuation -= 4;
1911423e3ce3SKalle Valo 			radio_attenuation++;
1912423e3ce3SKalle Valo 		}
1913423e3ce3SKalle Valo 	}
1914423e3ce3SKalle Valo 	baseband_attenuation = clamp_val(baseband_attenuation, 0, 11);
1915423e3ce3SKalle Valo 
1916423e3ce3SKalle Valo 	txpower = phy->txctl1;
1917423e3ce3SKalle Valo 	if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) {
1918423e3ce3SKalle Valo 		if (radio_attenuation <= 1) {
1919423e3ce3SKalle Valo 			if (txpower == 0) {
1920423e3ce3SKalle Valo 				txpower = 3;
1921423e3ce3SKalle Valo 				radio_attenuation += 2;
1922423e3ce3SKalle Valo 				baseband_attenuation += 2;
1923423e3ce3SKalle Valo 			} else if (dev->dev->bus->sprom.boardflags_lo
1924423e3ce3SKalle Valo 				   & B43legacy_BFL_PACTRL) {
1925423e3ce3SKalle Valo 				baseband_attenuation += 4 *
1926423e3ce3SKalle Valo 						     (radio_attenuation - 2);
1927423e3ce3SKalle Valo 				radio_attenuation = 2;
1928423e3ce3SKalle Valo 			}
1929423e3ce3SKalle Valo 		} else if (radio_attenuation > 4 && txpower != 0) {
1930423e3ce3SKalle Valo 			txpower = 0;
1931423e3ce3SKalle Valo 			if (baseband_attenuation < 3) {
1932423e3ce3SKalle Valo 				radio_attenuation -= 3;
1933423e3ce3SKalle Valo 				baseband_attenuation += 2;
1934423e3ce3SKalle Valo 			} else {
1935423e3ce3SKalle Valo 				radio_attenuation -= 2;
1936423e3ce3SKalle Valo 				baseband_attenuation -= 2;
1937423e3ce3SKalle Valo 			}
1938423e3ce3SKalle Valo 		}
1939423e3ce3SKalle Valo 	}
1940423e3ce3SKalle Valo 	/* Save the control values */
1941423e3ce3SKalle Valo 	phy->txctl1 = txpower;
1942423e3ce3SKalle Valo 	baseband_attenuation = clamp_val(baseband_attenuation, 0, 11);
1943423e3ce3SKalle Valo 	radio_attenuation = clamp_val(radio_attenuation, 0, 9);
1944423e3ce3SKalle Valo 	phy->rfatt = radio_attenuation;
1945423e3ce3SKalle Valo 	phy->bbatt = baseband_attenuation;
1946423e3ce3SKalle Valo 
1947423e3ce3SKalle Valo 	/* Adjust the hardware */
1948423e3ce3SKalle Valo 	b43legacy_phy_lock(dev);
1949423e3ce3SKalle Valo 	b43legacy_radio_lock(dev);
1950423e3ce3SKalle Valo 	b43legacy_radio_set_txpower_bg(dev, baseband_attenuation,
1951423e3ce3SKalle Valo 				       radio_attenuation, txpower);
1952423e3ce3SKalle Valo 	b43legacy_phy_lo_mark_current_used(dev);
1953423e3ce3SKalle Valo 	b43legacy_radio_unlock(dev);
1954423e3ce3SKalle Valo 	b43legacy_phy_unlock(dev);
1955423e3ce3SKalle Valo }
1956423e3ce3SKalle Valo 
1957423e3ce3SKalle Valo static inline
1958423e3ce3SKalle Valo s32 b43legacy_tssi2dbm_ad(s32 num, s32 den)
1959423e3ce3SKalle Valo {
1960423e3ce3SKalle Valo 	if (num < 0)
1961423e3ce3SKalle Valo 		return num/den;
1962423e3ce3SKalle Valo 	else
1963423e3ce3SKalle Valo 		return (num+den/2)/den;
1964423e3ce3SKalle Valo }
1965423e3ce3SKalle Valo 
1966423e3ce3SKalle Valo static inline
1967423e3ce3SKalle Valo s8 b43legacy_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2)
1968423e3ce3SKalle Valo {
1969423e3ce3SKalle Valo 	s32 m1;
1970423e3ce3SKalle Valo 	s32 m2;
1971423e3ce3SKalle Valo 	s32 f = 256;
1972423e3ce3SKalle Valo 	s32 q;
1973423e3ce3SKalle Valo 	s32 delta;
1974423e3ce3SKalle Valo 	s8 i = 0;
1975423e3ce3SKalle Valo 
1976423e3ce3SKalle Valo 	m1 = b43legacy_tssi2dbm_ad(16 * pab0 + index * pab1, 32);
1977423e3ce3SKalle Valo 	m2 = max(b43legacy_tssi2dbm_ad(32768 + index * pab2, 256), 1);
1978423e3ce3SKalle Valo 	do {
1979423e3ce3SKalle Valo 		if (i > 15)
1980423e3ce3SKalle Valo 			return -EINVAL;
1981423e3ce3SKalle Valo 		q = b43legacy_tssi2dbm_ad(f * 4096 -
1982423e3ce3SKalle Valo 					  b43legacy_tssi2dbm_ad(m2 * f, 16) *
1983423e3ce3SKalle Valo 					  f, 2048);
1984423e3ce3SKalle Valo 		delta = abs(q - f);
1985423e3ce3SKalle Valo 		f = q;
1986423e3ce3SKalle Valo 		i++;
1987423e3ce3SKalle Valo 	} while (delta >= 2);
1988423e3ce3SKalle Valo 	entry[index] = clamp_val(b43legacy_tssi2dbm_ad(m1 * f, 8192),
1989423e3ce3SKalle Valo 				   -127, 128);
1990423e3ce3SKalle Valo 	return 0;
1991423e3ce3SKalle Valo }
1992423e3ce3SKalle Valo 
1993423e3ce3SKalle Valo /* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */
1994423e3ce3SKalle Valo int b43legacy_phy_init_tssi2dbm_table(struct b43legacy_wldev *dev)
1995423e3ce3SKalle Valo {
1996423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1997423e3ce3SKalle Valo 	s16 pab0;
1998423e3ce3SKalle Valo 	s16 pab1;
1999423e3ce3SKalle Valo 	s16 pab2;
2000423e3ce3SKalle Valo 	u8 idx;
2001423e3ce3SKalle Valo 	s8 *dyn_tssi2dbm;
2002423e3ce3SKalle Valo 
2003423e3ce3SKalle Valo 	B43legacy_WARN_ON(!(phy->type == B43legacy_PHYTYPE_B ||
2004423e3ce3SKalle Valo 			  phy->type == B43legacy_PHYTYPE_G));
2005423e3ce3SKalle Valo 	pab0 = (s16)(dev->dev->bus->sprom.pa0b0);
2006423e3ce3SKalle Valo 	pab1 = (s16)(dev->dev->bus->sprom.pa0b1);
2007423e3ce3SKalle Valo 	pab2 = (s16)(dev->dev->bus->sprom.pa0b2);
2008423e3ce3SKalle Valo 
2009423e3ce3SKalle Valo 	if ((dev->dev->bus->chip_id == 0x4301) && (phy->radio_ver != 0x2050)) {
2010423e3ce3SKalle Valo 		phy->idle_tssi = 0x34;
2011423e3ce3SKalle Valo 		phy->tssi2dbm = b43legacy_tssi2dbm_b_table;
2012423e3ce3SKalle Valo 		return 0;
2013423e3ce3SKalle Valo 	}
2014423e3ce3SKalle Valo 
2015423e3ce3SKalle Valo 	if (pab0 != 0 && pab1 != 0 && pab2 != 0 &&
2016423e3ce3SKalle Valo 	    pab0 != -1 && pab1 != -1 && pab2 != -1) {
2017423e3ce3SKalle Valo 		/* The pabX values are set in SPROM. Use them. */
2018423e3ce3SKalle Valo 		if ((s8)dev->dev->bus->sprom.itssi_bg != 0 &&
2019423e3ce3SKalle Valo 		    (s8)dev->dev->bus->sprom.itssi_bg != -1)
2020423e3ce3SKalle Valo 			phy->idle_tssi = (s8)(dev->dev->bus->sprom.
2021423e3ce3SKalle Valo 					  itssi_bg);
2022423e3ce3SKalle Valo 		else
2023423e3ce3SKalle Valo 			phy->idle_tssi = 62;
2024423e3ce3SKalle Valo 		dyn_tssi2dbm = kmalloc(64, GFP_KERNEL);
2025423e3ce3SKalle Valo 		if (dyn_tssi2dbm == NULL) {
2026423e3ce3SKalle Valo 			b43legacyerr(dev->wl, "Could not allocate memory "
2027423e3ce3SKalle Valo 			       "for tssi2dbm table\n");
2028423e3ce3SKalle Valo 			return -ENOMEM;
2029423e3ce3SKalle Valo 		}
2030423e3ce3SKalle Valo 		for (idx = 0; idx < 64; idx++)
2031423e3ce3SKalle Valo 			if (b43legacy_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0,
2032423e3ce3SKalle Valo 						     pab1, pab2)) {
2033423e3ce3SKalle Valo 				phy->tssi2dbm = NULL;
2034423e3ce3SKalle Valo 				b43legacyerr(dev->wl, "Could not generate "
2035423e3ce3SKalle Valo 				       "tssi2dBm table\n");
2036423e3ce3SKalle Valo 				kfree(dyn_tssi2dbm);
2037423e3ce3SKalle Valo 				return -ENODEV;
2038423e3ce3SKalle Valo 			}
2039423e3ce3SKalle Valo 		phy->tssi2dbm = dyn_tssi2dbm;
2040423e3ce3SKalle Valo 		phy->dyn_tssi_tbl = 1;
2041423e3ce3SKalle Valo 	} else {
2042423e3ce3SKalle Valo 		/* pabX values not set in SPROM. */
2043423e3ce3SKalle Valo 		switch (phy->type) {
2044423e3ce3SKalle Valo 		case B43legacy_PHYTYPE_B:
2045423e3ce3SKalle Valo 			phy->idle_tssi = 0x34;
2046423e3ce3SKalle Valo 			phy->tssi2dbm = b43legacy_tssi2dbm_b_table;
2047423e3ce3SKalle Valo 			break;
2048423e3ce3SKalle Valo 		case B43legacy_PHYTYPE_G:
2049423e3ce3SKalle Valo 			phy->idle_tssi = 0x34;
2050423e3ce3SKalle Valo 			phy->tssi2dbm = b43legacy_tssi2dbm_g_table;
2051423e3ce3SKalle Valo 			break;
2052423e3ce3SKalle Valo 		}
2053423e3ce3SKalle Valo 	}
2054423e3ce3SKalle Valo 
2055423e3ce3SKalle Valo 	return 0;
2056423e3ce3SKalle Valo }
2057423e3ce3SKalle Valo 
2058423e3ce3SKalle Valo int b43legacy_phy_init(struct b43legacy_wldev *dev)
2059423e3ce3SKalle Valo {
2060423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
2061423e3ce3SKalle Valo 	int err = -ENODEV;
2062423e3ce3SKalle Valo 
2063423e3ce3SKalle Valo 	switch (phy->type) {
2064423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B:
2065423e3ce3SKalle Valo 		switch (phy->rev) {
2066423e3ce3SKalle Valo 		case 2:
2067423e3ce3SKalle Valo 			b43legacy_phy_initb2(dev);
2068423e3ce3SKalle Valo 			err = 0;
2069423e3ce3SKalle Valo 			break;
2070423e3ce3SKalle Valo 		case 4:
2071423e3ce3SKalle Valo 			b43legacy_phy_initb4(dev);
2072423e3ce3SKalle Valo 			err = 0;
2073423e3ce3SKalle Valo 			break;
2074423e3ce3SKalle Valo 		case 5:
2075423e3ce3SKalle Valo 			b43legacy_phy_initb5(dev);
2076423e3ce3SKalle Valo 			err = 0;
2077423e3ce3SKalle Valo 			break;
2078423e3ce3SKalle Valo 		case 6:
2079423e3ce3SKalle Valo 			b43legacy_phy_initb6(dev);
2080423e3ce3SKalle Valo 			err = 0;
2081423e3ce3SKalle Valo 			break;
2082423e3ce3SKalle Valo 		}
2083423e3ce3SKalle Valo 		break;
2084423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
2085423e3ce3SKalle Valo 		b43legacy_phy_initg(dev);
2086423e3ce3SKalle Valo 		err = 0;
2087423e3ce3SKalle Valo 		break;
2088423e3ce3SKalle Valo 	}
2089423e3ce3SKalle Valo 	if (err)
2090423e3ce3SKalle Valo 		b43legacyerr(dev->wl, "Unknown PHYTYPE found\n");
2091423e3ce3SKalle Valo 
2092423e3ce3SKalle Valo 	return err;
2093423e3ce3SKalle Valo }
2094423e3ce3SKalle Valo 
2095423e3ce3SKalle Valo void b43legacy_phy_set_antenna_diversity(struct b43legacy_wldev *dev)
2096423e3ce3SKalle Valo {
2097423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
2098423e3ce3SKalle Valo 	u16 antennadiv;
2099423e3ce3SKalle Valo 	u16 offset;
2100423e3ce3SKalle Valo 	u16 value;
2101423e3ce3SKalle Valo 	u32 ucodeflags;
2102423e3ce3SKalle Valo 
2103423e3ce3SKalle Valo 	antennadiv = phy->antenna_diversity;
2104423e3ce3SKalle Valo 
2105423e3ce3SKalle Valo 	if (antennadiv == 0xFFFF)
2106423e3ce3SKalle Valo 		antennadiv = 3;
2107423e3ce3SKalle Valo 	B43legacy_WARN_ON(antennadiv > 3);
2108423e3ce3SKalle Valo 
2109423e3ce3SKalle Valo 	ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
2110423e3ce3SKalle Valo 					  B43legacy_UCODEFLAGS_OFFSET);
2111423e3ce3SKalle Valo 	b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
2112423e3ce3SKalle Valo 			      B43legacy_UCODEFLAGS_OFFSET,
2113423e3ce3SKalle Valo 			      ucodeflags & ~B43legacy_UCODEFLAG_AUTODIV);
2114423e3ce3SKalle Valo 
2115423e3ce3SKalle Valo 	switch (phy->type) {
2116423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
2117423e3ce3SKalle Valo 		offset = 0x0400;
2118423e3ce3SKalle Valo 
2119423e3ce3SKalle Valo 		if (antennadiv == 2)
2120423e3ce3SKalle Valo 			value = (3/*automatic*/ << 7);
2121423e3ce3SKalle Valo 		else
2122423e3ce3SKalle Valo 			value = (antennadiv << 7);
2123423e3ce3SKalle Valo 		b43legacy_phy_write(dev, offset + 1,
2124423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, offset + 1)
2125423e3ce3SKalle Valo 				    & 0x7E7F) | value);
2126423e3ce3SKalle Valo 
2127423e3ce3SKalle Valo 		if (antennadiv >= 2) {
2128423e3ce3SKalle Valo 			if (antennadiv == 2)
2129423e3ce3SKalle Valo 				value = (antennadiv << 7);
2130423e3ce3SKalle Valo 			else
2131423e3ce3SKalle Valo 				value = (0/*force0*/ << 7);
2132423e3ce3SKalle Valo 			b43legacy_phy_write(dev, offset + 0x2B,
2133423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev,
2134423e3ce3SKalle Valo 					    offset + 0x2B)
2135423e3ce3SKalle Valo 					    & 0xFEFF) | value);
2136423e3ce3SKalle Valo 		}
2137423e3ce3SKalle Valo 
2138423e3ce3SKalle Valo 		if (phy->type == B43legacy_PHYTYPE_G) {
2139423e3ce3SKalle Valo 			if (antennadiv >= 2)
2140423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x048C,
2141423e3ce3SKalle Valo 						    b43legacy_phy_read(dev,
2142423e3ce3SKalle Valo 						    0x048C) | 0x2000);
2143423e3ce3SKalle Valo 			else
2144423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x048C,
2145423e3ce3SKalle Valo 						    b43legacy_phy_read(dev,
2146423e3ce3SKalle Valo 						    0x048C) & ~0x2000);
2147423e3ce3SKalle Valo 			if (phy->rev >= 2) {
2148423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0461,
2149423e3ce3SKalle Valo 						    b43legacy_phy_read(dev,
2150423e3ce3SKalle Valo 						    0x0461) | 0x0010);
2151423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x04AD,
2152423e3ce3SKalle Valo 						    (b43legacy_phy_read(dev,
2153423e3ce3SKalle Valo 						    0x04AD)
2154423e3ce3SKalle Valo 						    & 0x00FF) | 0x0015);
2155423e3ce3SKalle Valo 				if (phy->rev == 2)
2156423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x0427,
2157423e3ce3SKalle Valo 							    0x0008);
2158423e3ce3SKalle Valo 				else
2159423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x0427,
2160423e3ce3SKalle Valo 						(b43legacy_phy_read(dev, 0x0427)
2161423e3ce3SKalle Valo 						 & 0x00FF) | 0x0008);
2162423e3ce3SKalle Valo 			} else if (phy->rev >= 6)
2163423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x049B, 0x00DC);
2164423e3ce3SKalle Valo 		} else {
2165423e3ce3SKalle Valo 			if (phy->rev < 3)
2166423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x002B,
2167423e3ce3SKalle Valo 						    (b43legacy_phy_read(dev,
2168423e3ce3SKalle Valo 						    0x002B) & 0x00FF)
2169423e3ce3SKalle Valo 						    | 0x0024);
2170423e3ce3SKalle Valo 			else {
2171423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0061,
2172423e3ce3SKalle Valo 						    b43legacy_phy_read(dev,
2173423e3ce3SKalle Valo 						    0x0061) | 0x0010);
2174423e3ce3SKalle Valo 				if (phy->rev == 3) {
2175423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x0093,
2176423e3ce3SKalle Valo 							    0x001D);
2177423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x0027,
2178423e3ce3SKalle Valo 							    0x0008);
2179423e3ce3SKalle Valo 				} else {
2180423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x0093,
2181423e3ce3SKalle Valo 							    0x003A);
2182423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x0027,
2183423e3ce3SKalle Valo 						(b43legacy_phy_read(dev, 0x0027)
2184423e3ce3SKalle Valo 						 & 0x00FF) | 0x0008);
2185423e3ce3SKalle Valo 				}
2186423e3ce3SKalle Valo 			}
2187423e3ce3SKalle Valo 		}
2188423e3ce3SKalle Valo 		break;
2189423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B:
2190423e3ce3SKalle Valo 		if (dev->dev->id.revision == 2)
2191423e3ce3SKalle Valo 			value = (3/*automatic*/ << 7);
2192423e3ce3SKalle Valo 		else
2193423e3ce3SKalle Valo 			value = (antennadiv << 7);
2194423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x03E2,
2195423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x03E2)
2196423e3ce3SKalle Valo 				    & 0xFE7F) | value);
2197423e3ce3SKalle Valo 		break;
2198423e3ce3SKalle Valo 	default:
2199423e3ce3SKalle Valo 		B43legacy_WARN_ON(1);
2200423e3ce3SKalle Valo 	}
2201423e3ce3SKalle Valo 
2202423e3ce3SKalle Valo 	if (antennadiv >= 2) {
2203423e3ce3SKalle Valo 		ucodeflags = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
2204423e3ce3SKalle Valo 						  B43legacy_UCODEFLAGS_OFFSET);
2205423e3ce3SKalle Valo 		b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
2206423e3ce3SKalle Valo 				      B43legacy_UCODEFLAGS_OFFSET,
2207423e3ce3SKalle Valo 				      ucodeflags | B43legacy_UCODEFLAG_AUTODIV);
2208423e3ce3SKalle Valo 	}
2209423e3ce3SKalle Valo 
2210423e3ce3SKalle Valo 	phy->antenna_diversity = antennadiv;
2211423e3ce3SKalle Valo }
2212423e3ce3SKalle Valo 
2213423e3ce3SKalle Valo /* Set the PowerSavingControlBits.
2214423e3ce3SKalle Valo  * Bitvalues:
2215423e3ce3SKalle Valo  *   0  => unset the bit
2216423e3ce3SKalle Valo  *   1  => set the bit
2217423e3ce3SKalle Valo  *   -1 => calculate the bit
2218423e3ce3SKalle Valo  */
2219423e3ce3SKalle Valo void b43legacy_power_saving_ctl_bits(struct b43legacy_wldev *dev,
2220423e3ce3SKalle Valo 				     int bit25, int bit26)
2221423e3ce3SKalle Valo {
2222423e3ce3SKalle Valo 	int i;
2223423e3ce3SKalle Valo 	u32 status;
2224423e3ce3SKalle Valo 
2225423e3ce3SKalle Valo /* FIXME: Force 25 to off and 26 to on for now: */
2226423e3ce3SKalle Valo bit25 = 0;
2227423e3ce3SKalle Valo bit26 = 1;
2228423e3ce3SKalle Valo 
2229423e3ce3SKalle Valo 	if (bit25 == -1) {
2230423e3ce3SKalle Valo 		/* TODO: If powersave is not off and FIXME is not set and we
2231423e3ce3SKalle Valo 		 *	are not in adhoc and thus is not an AP and we arei
2232423e3ce3SKalle Valo 		 *	associated, set bit 25 */
2233423e3ce3SKalle Valo 	}
2234423e3ce3SKalle Valo 	if (bit26 == -1) {
2235423e3ce3SKalle Valo 		/* TODO: If the device is awake or this is an AP, or we are
2236423e3ce3SKalle Valo 		 *	scanning, or FIXME, or we are associated, or FIXME,
2237423e3ce3SKalle Valo 		 *	or the latest PS-Poll packet sent was successful,
2238423e3ce3SKalle Valo 		 *	set bit26  */
2239423e3ce3SKalle Valo 	}
2240423e3ce3SKalle Valo 	status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
2241423e3ce3SKalle Valo 	if (bit25)
2242423e3ce3SKalle Valo 		status |= B43legacy_MACCTL_HWPS;
2243423e3ce3SKalle Valo 	else
2244423e3ce3SKalle Valo 		status &= ~B43legacy_MACCTL_HWPS;
2245423e3ce3SKalle Valo 	if (bit26)
2246423e3ce3SKalle Valo 		status |= B43legacy_MACCTL_AWAKE;
2247423e3ce3SKalle Valo 	else
2248423e3ce3SKalle Valo 		status &= ~B43legacy_MACCTL_AWAKE;
2249423e3ce3SKalle Valo 	b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
2250423e3ce3SKalle Valo 	if (bit26 && dev->dev->id.revision >= 5) {
2251423e3ce3SKalle Valo 		for (i = 0; i < 100; i++) {
2252423e3ce3SKalle Valo 			if (b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
2253423e3ce3SKalle Valo 						 0x0040) != 4)
2254423e3ce3SKalle Valo 				break;
2255423e3ce3SKalle Valo 			udelay(10);
2256423e3ce3SKalle Valo 		}
2257423e3ce3SKalle Valo 	}
2258423e3ce3SKalle Valo }
2259