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