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 
21423e3ce3SKalle Valo #include "b43legacy.h"
22423e3ce3SKalle Valo #include "main.h"
23423e3ce3SKalle Valo #include "phy.h"
24423e3ce3SKalle Valo #include "radio.h"
25423e3ce3SKalle Valo #include "ilt.h"
26423e3ce3SKalle Valo 
27423e3ce3SKalle Valo 
28423e3ce3SKalle Valo /* Table for b43legacy_radio_calibrationvalue() */
29423e3ce3SKalle Valo static const u16 rcc_table[16] = {
30423e3ce3SKalle Valo 	0x0002, 0x0003, 0x0001, 0x000F,
31423e3ce3SKalle Valo 	0x0006, 0x0007, 0x0005, 0x000F,
32423e3ce3SKalle Valo 	0x000A, 0x000B, 0x0009, 0x000F,
33423e3ce3SKalle Valo 	0x000E, 0x000F, 0x000D, 0x000F,
34423e3ce3SKalle Valo };
35423e3ce3SKalle Valo 
36423e3ce3SKalle Valo /* Reverse the bits of a 4bit value.
37423e3ce3SKalle Valo  * Example:  1101 is flipped 1011
38423e3ce3SKalle Valo  */
flip_4bit(u16 value)39423e3ce3SKalle Valo static u16 flip_4bit(u16 value)
40423e3ce3SKalle Valo {
41423e3ce3SKalle Valo 	u16 flipped = 0x0000;
42423e3ce3SKalle Valo 
43423e3ce3SKalle Valo 	B43legacy_BUG_ON(!((value & ~0x000F) == 0x0000));
44423e3ce3SKalle Valo 
45423e3ce3SKalle Valo 	flipped |= (value & 0x0001) << 3;
46423e3ce3SKalle Valo 	flipped |= (value & 0x0002) << 1;
47423e3ce3SKalle Valo 	flipped |= (value & 0x0004) >> 1;
48423e3ce3SKalle Valo 	flipped |= (value & 0x0008) >> 3;
49423e3ce3SKalle Valo 
50423e3ce3SKalle Valo 	return flipped;
51423e3ce3SKalle Valo }
52423e3ce3SKalle Valo 
53423e3ce3SKalle Valo /* Get the freq, as it has to be written to the device. */
54423e3ce3SKalle Valo static inline
channel2freq_bg(u8 channel)55423e3ce3SKalle Valo u16 channel2freq_bg(u8 channel)
56423e3ce3SKalle Valo {
57423e3ce3SKalle Valo 	/* Frequencies are given as frequencies_bg[index] + 2.4GHz
58423e3ce3SKalle Valo 	 * Starting with channel 1
59423e3ce3SKalle Valo 	 */
60423e3ce3SKalle Valo 	static const u16 frequencies_bg[14] = {
61423e3ce3SKalle Valo 		12, 17, 22, 27,
62423e3ce3SKalle Valo 		32, 37, 42, 47,
63423e3ce3SKalle Valo 		52, 57, 62, 67,
64423e3ce3SKalle Valo 		72, 84,
65423e3ce3SKalle Valo 	};
66423e3ce3SKalle Valo 
67423e3ce3SKalle Valo 	if (unlikely(channel < 1 || channel > 14)) {
68423e3ce3SKalle Valo 		printk(KERN_INFO "b43legacy: Channel %d is out of range\n",
69423e3ce3SKalle Valo 				  channel);
70423e3ce3SKalle Valo 		dump_stack();
71423e3ce3SKalle Valo 		return 2412;
72423e3ce3SKalle Valo 	}
73423e3ce3SKalle Valo 
74423e3ce3SKalle Valo 	return frequencies_bg[channel - 1];
75423e3ce3SKalle Valo }
76423e3ce3SKalle Valo 
b43legacy_radio_lock(struct b43legacy_wldev * dev)77423e3ce3SKalle Valo void b43legacy_radio_lock(struct b43legacy_wldev *dev)
78423e3ce3SKalle Valo {
79423e3ce3SKalle Valo 	u32 status;
80423e3ce3SKalle Valo 
81423e3ce3SKalle Valo 	status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
82423e3ce3SKalle Valo 	B43legacy_WARN_ON(status & B43legacy_MACCTL_RADIOLOCK);
83423e3ce3SKalle Valo 	status |= B43legacy_MACCTL_RADIOLOCK;
84423e3ce3SKalle Valo 	b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
85423e3ce3SKalle Valo 	udelay(10);
86423e3ce3SKalle Valo }
87423e3ce3SKalle Valo 
b43legacy_radio_unlock(struct b43legacy_wldev * dev)88423e3ce3SKalle Valo void b43legacy_radio_unlock(struct b43legacy_wldev *dev)
89423e3ce3SKalle Valo {
90423e3ce3SKalle Valo 	u32 status;
91423e3ce3SKalle Valo 
92423e3ce3SKalle Valo 	b43legacy_read16(dev, B43legacy_MMIO_PHY_VER); /* dummy read */
93423e3ce3SKalle Valo 	status = b43legacy_read32(dev, B43legacy_MMIO_MACCTL);
94423e3ce3SKalle Valo 	B43legacy_WARN_ON(!(status & B43legacy_MACCTL_RADIOLOCK));
95423e3ce3SKalle Valo 	status &= ~B43legacy_MACCTL_RADIOLOCK;
96423e3ce3SKalle Valo 	b43legacy_write32(dev, B43legacy_MMIO_MACCTL, status);
97423e3ce3SKalle Valo }
98423e3ce3SKalle Valo 
b43legacy_radio_read16(struct b43legacy_wldev * dev,u16 offset)99423e3ce3SKalle Valo u16 b43legacy_radio_read16(struct b43legacy_wldev *dev, u16 offset)
100423e3ce3SKalle Valo {
101423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
102423e3ce3SKalle Valo 
103423e3ce3SKalle Valo 	switch (phy->type) {
104423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B:
105423e3ce3SKalle Valo 		if (phy->radio_ver == 0x2053) {
106423e3ce3SKalle Valo 			if (offset < 0x70)
107423e3ce3SKalle Valo 				offset += 0x80;
108423e3ce3SKalle Valo 			else if (offset < 0x80)
109423e3ce3SKalle Valo 				offset += 0x70;
110423e3ce3SKalle Valo 		} else if (phy->radio_ver == 0x2050)
111423e3ce3SKalle Valo 			offset |= 0x80;
112423e3ce3SKalle Valo 		else
113423e3ce3SKalle Valo 			B43legacy_WARN_ON(1);
114423e3ce3SKalle Valo 		break;
115423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
116423e3ce3SKalle Valo 		offset |= 0x80;
117423e3ce3SKalle Valo 		break;
118423e3ce3SKalle Valo 	default:
119423e3ce3SKalle Valo 		B43legacy_BUG_ON(1);
120423e3ce3SKalle Valo 	}
121423e3ce3SKalle Valo 
122423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset);
123423e3ce3SKalle Valo 	return b43legacy_read16(dev, B43legacy_MMIO_RADIO_DATA_LOW);
124423e3ce3SKalle Valo }
125423e3ce3SKalle Valo 
b43legacy_radio_write16(struct b43legacy_wldev * dev,u16 offset,u16 val)126423e3ce3SKalle Valo void b43legacy_radio_write16(struct b43legacy_wldev *dev, u16 offset, u16 val)
127423e3ce3SKalle Valo {
128423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_RADIO_CONTROL, offset);
129423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_RADIO_DATA_LOW, val);
130423e3ce3SKalle Valo }
131423e3ce3SKalle Valo 
b43legacy_set_all_gains(struct b43legacy_wldev * dev,s16 first,s16 second,s16 third)132423e3ce3SKalle Valo static void b43legacy_set_all_gains(struct b43legacy_wldev *dev,
133423e3ce3SKalle Valo 				  s16 first, s16 second, s16 third)
134423e3ce3SKalle Valo {
135423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
136423e3ce3SKalle Valo 	u16 i;
137423e3ce3SKalle Valo 	u16 start = 0x08;
138423e3ce3SKalle Valo 	u16 end = 0x18;
139423e3ce3SKalle Valo 	u16 offset = 0x0400;
140423e3ce3SKalle Valo 	u16 tmp;
141423e3ce3SKalle Valo 
142423e3ce3SKalle Valo 	if (phy->rev <= 1) {
143423e3ce3SKalle Valo 		offset = 0x5000;
144423e3ce3SKalle Valo 		start = 0x10;
145423e3ce3SKalle Valo 		end = 0x20;
146423e3ce3SKalle Valo 	}
147423e3ce3SKalle Valo 
148423e3ce3SKalle Valo 	for (i = 0; i < 4; i++)
149423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, offset + i, first);
150423e3ce3SKalle Valo 
151423e3ce3SKalle Valo 	for (i = start; i < end; i++)
152423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, offset + i, second);
153423e3ce3SKalle Valo 
154423e3ce3SKalle Valo 	if (third != -1) {
155423e3ce3SKalle Valo 		tmp = ((u16)third << 14) | ((u16)third << 6);
156423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A0,
157423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF)
158423e3ce3SKalle Valo 				    | tmp);
159423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A1,
160423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF)
161423e3ce3SKalle Valo 				    | tmp);
162423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A2,
163423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF)
164423e3ce3SKalle Valo 				    | tmp);
165423e3ce3SKalle Valo 	}
166423e3ce3SKalle Valo 	b43legacy_dummy_transmission(dev);
167423e3ce3SKalle Valo }
168423e3ce3SKalle Valo 
b43legacy_set_original_gains(struct b43legacy_wldev * dev)169423e3ce3SKalle Valo static void b43legacy_set_original_gains(struct b43legacy_wldev *dev)
170423e3ce3SKalle Valo {
171423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
172423e3ce3SKalle Valo 	u16 i;
173423e3ce3SKalle Valo 	u16 tmp;
174423e3ce3SKalle Valo 	u16 offset = 0x0400;
175423e3ce3SKalle Valo 	u16 start = 0x0008;
176423e3ce3SKalle Valo 	u16 end = 0x0018;
177423e3ce3SKalle Valo 
178423e3ce3SKalle Valo 	if (phy->rev <= 1) {
179423e3ce3SKalle Valo 		offset = 0x5000;
180423e3ce3SKalle Valo 		start = 0x0010;
181423e3ce3SKalle Valo 		end = 0x0020;
182423e3ce3SKalle Valo 	}
183423e3ce3SKalle Valo 
184423e3ce3SKalle Valo 	for (i = 0; i < 4; i++) {
185423e3ce3SKalle Valo 		tmp = (i & 0xFFFC);
186423e3ce3SKalle Valo 		tmp |= (i & 0x0001) << 1;
187423e3ce3SKalle Valo 		tmp |= (i & 0x0002) >> 1;
188423e3ce3SKalle Valo 
189423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, offset + i, tmp);
190423e3ce3SKalle Valo 	}
191423e3ce3SKalle Valo 
192423e3ce3SKalle Valo 	for (i = start; i < end; i++)
193423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, offset + i, i - start);
194423e3ce3SKalle Valo 
195423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A0,
196423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x04A0) & 0xBFBF)
197423e3ce3SKalle Valo 			    | 0x4040);
198423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A1,
199423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x04A1) & 0xBFBF)
200423e3ce3SKalle Valo 			    | 0x4040);
201423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x04A2,
202423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x04A2) & 0xBFBF)
203423e3ce3SKalle Valo 			    | 0x4000);
204423e3ce3SKalle Valo 	b43legacy_dummy_transmission(dev);
205423e3ce3SKalle Valo }
206423e3ce3SKalle Valo 
207423e3ce3SKalle Valo /* Synthetic PU workaround */
b43legacy_synth_pu_workaround(struct b43legacy_wldev * dev,u8 channel)208423e3ce3SKalle Valo static void b43legacy_synth_pu_workaround(struct b43legacy_wldev *dev,
209423e3ce3SKalle Valo 					  u8 channel)
210423e3ce3SKalle Valo {
211423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
212423e3ce3SKalle Valo 
213423e3ce3SKalle Valo 	might_sleep();
214423e3ce3SKalle Valo 
215423e3ce3SKalle Valo 	if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6)
216423e3ce3SKalle Valo 		/* We do not need the workaround. */
217423e3ce3SKalle Valo 		return;
218423e3ce3SKalle Valo 
219423e3ce3SKalle Valo 	if (channel <= 10)
220423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
221423e3ce3SKalle Valo 				  channel2freq_bg(channel + 4));
222423e3ce3SKalle Valo 	else
223423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
224423e3ce3SKalle Valo 				  channel2freq_bg(channel));
225423e3ce3SKalle Valo 	msleep(1);
226423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
227423e3ce3SKalle Valo 			  channel2freq_bg(channel));
228423e3ce3SKalle Valo }
229423e3ce3SKalle Valo 
b43legacy_radio_aci_detect(struct b43legacy_wldev * dev,u8 channel)230423e3ce3SKalle Valo u8 b43legacy_radio_aci_detect(struct b43legacy_wldev *dev, u8 channel)
231423e3ce3SKalle Valo {
232423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
233423e3ce3SKalle Valo 	u8 ret = 0;
234423e3ce3SKalle Valo 	u16 saved;
235423e3ce3SKalle Valo 	u16 rssi;
236423e3ce3SKalle Valo 	u16 temp;
237423e3ce3SKalle Valo 	int i;
238423e3ce3SKalle Valo 	int j = 0;
239423e3ce3SKalle Valo 
240423e3ce3SKalle Valo 	saved = b43legacy_phy_read(dev, 0x0403);
241423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, channel, 0);
242423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5);
243423e3ce3SKalle Valo 	if (phy->aci_hw_rssi)
244423e3ce3SKalle Valo 		rssi = b43legacy_phy_read(dev, 0x048A) & 0x3F;
245423e3ce3SKalle Valo 	else
246423e3ce3SKalle Valo 		rssi = saved & 0x3F;
247423e3ce3SKalle Valo 	/* clamp temp to signed 5bit */
248423e3ce3SKalle Valo 	if (rssi > 32)
249423e3ce3SKalle Valo 		rssi -= 64;
250423e3ce3SKalle Valo 	for (i = 0; i < 100; i++) {
251423e3ce3SKalle Valo 		temp = (b43legacy_phy_read(dev, 0x047F) >> 8) & 0x3F;
252423e3ce3SKalle Valo 		if (temp > 32)
253423e3ce3SKalle Valo 			temp -= 64;
254423e3ce3SKalle Valo 		if (temp < rssi)
255423e3ce3SKalle Valo 			j++;
256423e3ce3SKalle Valo 		if (j >= 20)
257423e3ce3SKalle Valo 			ret = 1;
258423e3ce3SKalle Valo 	}
259423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0403, saved);
260423e3ce3SKalle Valo 
261423e3ce3SKalle Valo 	return ret;
262423e3ce3SKalle Valo }
263423e3ce3SKalle Valo 
b43legacy_radio_aci_scan(struct b43legacy_wldev * dev)264423e3ce3SKalle Valo u8 b43legacy_radio_aci_scan(struct b43legacy_wldev *dev)
265423e3ce3SKalle Valo {
266423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
267e3ae1c77SColin Ian King 	u8 ret[13] = { 0 };
268423e3ce3SKalle Valo 	unsigned int channel = phy->channel;
269423e3ce3SKalle Valo 	unsigned int i;
270423e3ce3SKalle Valo 	unsigned int j;
271423e3ce3SKalle Valo 	unsigned int start;
272423e3ce3SKalle Valo 	unsigned int end;
273423e3ce3SKalle Valo 
274423e3ce3SKalle Valo 	if (!((phy->type == B43legacy_PHYTYPE_G) && (phy->rev > 0)))
275423e3ce3SKalle Valo 		return 0;
276423e3ce3SKalle Valo 
277423e3ce3SKalle Valo 	b43legacy_phy_lock(dev);
278423e3ce3SKalle Valo 	b43legacy_radio_lock(dev);
279423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0802,
280423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0802) & 0xFFFC);
281423e3ce3SKalle Valo 	b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
282423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
283423e3ce3SKalle Valo 			    & 0x7FFF);
284423e3ce3SKalle Valo 	b43legacy_set_all_gains(dev, 3, 8, 1);
285423e3ce3SKalle Valo 
286*c1c8380bSDan Carpenter 	start = (channel > 5) ? channel - 5 : 1;
287423e3ce3SKalle Valo 	end = (channel + 5 < 14) ? channel + 5 : 13;
288423e3ce3SKalle Valo 
289423e3ce3SKalle Valo 	for (i = start; i <= end; i++) {
290423e3ce3SKalle Valo 		if (abs(channel - i) > 2)
291423e3ce3SKalle Valo 			ret[i-1] = b43legacy_radio_aci_detect(dev, i);
292423e3ce3SKalle Valo 	}
293423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, channel, 0);
294423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0802,
295423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x0802) & 0xFFFC)
296423e3ce3SKalle Valo 			    | 0x0003);
297423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0403,
298423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0403) & 0xFFF8);
299423e3ce3SKalle Valo 	b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
300423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
301423e3ce3SKalle Valo 			    | 0x8000);
302423e3ce3SKalle Valo 	b43legacy_set_original_gains(dev);
303423e3ce3SKalle Valo 	for (i = 0; i < 13; i++) {
304423e3ce3SKalle Valo 		if (!ret[i])
305423e3ce3SKalle Valo 			continue;
306423e3ce3SKalle Valo 		end = (i + 5 < 13) ? i + 5 : 13;
307423e3ce3SKalle Valo 		for (j = i; j < end; j++)
308423e3ce3SKalle Valo 			ret[j] = 1;
309423e3ce3SKalle Valo 	}
310423e3ce3SKalle Valo 	b43legacy_radio_unlock(dev);
311423e3ce3SKalle Valo 	b43legacy_phy_unlock(dev);
312423e3ce3SKalle Valo 
313423e3ce3SKalle Valo 	return ret[channel - 1];
314423e3ce3SKalle Valo }
315423e3ce3SKalle Valo 
316140c6026SAlexander A. Klimov /* https://bcm-specs.sipsolutions.net/NRSSILookupTable */
b43legacy_nrssi_hw_write(struct b43legacy_wldev * dev,u16 offset,s16 val)317423e3ce3SKalle Valo void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val)
318423e3ce3SKalle Valo {
319423e3ce3SKalle Valo 	b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset);
320423e3ce3SKalle Valo 	b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_DATA, (u16)val);
321423e3ce3SKalle Valo }
322423e3ce3SKalle Valo 
323140c6026SAlexander A. Klimov /* https://bcm-specs.sipsolutions.net/NRSSILookupTable */
b43legacy_nrssi_hw_read(struct b43legacy_wldev * dev,u16 offset)324423e3ce3SKalle Valo s16 b43legacy_nrssi_hw_read(struct b43legacy_wldev *dev, u16 offset)
325423e3ce3SKalle Valo {
326423e3ce3SKalle Valo 	u16 val;
327423e3ce3SKalle Valo 
328423e3ce3SKalle Valo 	b43legacy_phy_write(dev, B43legacy_PHY_NRSSILT_CTRL, offset);
329423e3ce3SKalle Valo 	val = b43legacy_phy_read(dev, B43legacy_PHY_NRSSILT_DATA);
330423e3ce3SKalle Valo 
331423e3ce3SKalle Valo 	return (s16)val;
332423e3ce3SKalle Valo }
333423e3ce3SKalle Valo 
334140c6026SAlexander A. Klimov /* https://bcm-specs.sipsolutions.net/NRSSILookupTable */
b43legacy_nrssi_hw_update(struct b43legacy_wldev * dev,u16 val)335423e3ce3SKalle Valo void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val)
336423e3ce3SKalle Valo {
337423e3ce3SKalle Valo 	u16 i;
338423e3ce3SKalle Valo 	s16 tmp;
339423e3ce3SKalle Valo 
340423e3ce3SKalle Valo 	for (i = 0; i < 64; i++) {
341423e3ce3SKalle Valo 		tmp = b43legacy_nrssi_hw_read(dev, i);
342423e3ce3SKalle Valo 		tmp -= val;
343423e3ce3SKalle Valo 		tmp = clamp_val(tmp, -32, 31);
344423e3ce3SKalle Valo 		b43legacy_nrssi_hw_write(dev, i, tmp);
345423e3ce3SKalle Valo 	}
346423e3ce3SKalle Valo }
347423e3ce3SKalle Valo 
348140c6026SAlexander A. Klimov /* https://bcm-specs.sipsolutions.net/NRSSILookupTable */
b43legacy_nrssi_mem_update(struct b43legacy_wldev * dev)349423e3ce3SKalle Valo void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev)
350423e3ce3SKalle Valo {
351423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
352423e3ce3SKalle Valo 	s16 i;
353423e3ce3SKalle Valo 	s16 delta;
354423e3ce3SKalle Valo 	s32 tmp;
355423e3ce3SKalle Valo 
356423e3ce3SKalle Valo 	delta = 0x1F - phy->nrssi[0];
357423e3ce3SKalle Valo 	for (i = 0; i < 64; i++) {
358423e3ce3SKalle Valo 		tmp = (i - delta) * phy->nrssislope;
359423e3ce3SKalle Valo 		tmp /= 0x10000;
360423e3ce3SKalle Valo 		tmp += 0x3A;
361423e3ce3SKalle Valo 		tmp = clamp_val(tmp, 0, 0x3F);
362423e3ce3SKalle Valo 		phy->nrssi_lt[i] = tmp;
363423e3ce3SKalle Valo 	}
364423e3ce3SKalle Valo }
365423e3ce3SKalle Valo 
b43legacy_calc_nrssi_offset(struct b43legacy_wldev * dev)366423e3ce3SKalle Valo static void b43legacy_calc_nrssi_offset(struct b43legacy_wldev *dev)
367423e3ce3SKalle Valo {
368423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
369423e3ce3SKalle Valo 	u16 backup[20] = { 0 };
370423e3ce3SKalle Valo 	s16 v47F;
371423e3ce3SKalle Valo 	u16 i;
372423e3ce3SKalle Valo 	u16 saved = 0xFFFF;
373423e3ce3SKalle Valo 
374423e3ce3SKalle Valo 	backup[0] = b43legacy_phy_read(dev, 0x0001);
375423e3ce3SKalle Valo 	backup[1] = b43legacy_phy_read(dev, 0x0811);
376423e3ce3SKalle Valo 	backup[2] = b43legacy_phy_read(dev, 0x0812);
377423e3ce3SKalle Valo 	backup[3] = b43legacy_phy_read(dev, 0x0814);
378423e3ce3SKalle Valo 	backup[4] = b43legacy_phy_read(dev, 0x0815);
379423e3ce3SKalle Valo 	backup[5] = b43legacy_phy_read(dev, 0x005A);
380423e3ce3SKalle Valo 	backup[6] = b43legacy_phy_read(dev, 0x0059);
381423e3ce3SKalle Valo 	backup[7] = b43legacy_phy_read(dev, 0x0058);
382423e3ce3SKalle Valo 	backup[8] = b43legacy_phy_read(dev, 0x000A);
383423e3ce3SKalle Valo 	backup[9] = b43legacy_phy_read(dev, 0x0003);
384423e3ce3SKalle Valo 	backup[10] = b43legacy_radio_read16(dev, 0x007A);
385423e3ce3SKalle Valo 	backup[11] = b43legacy_radio_read16(dev, 0x0043);
386423e3ce3SKalle Valo 
387423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0429,
388423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0429) & 0x7FFF);
389423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0001,
390423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x0001) & 0x3FFF)
391423e3ce3SKalle Valo 			    | 0x4000);
392423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811,
393423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0811) | 0x000C);
394423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812,
395423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x0812) & 0xFFF3)
396423e3ce3SKalle Valo 			    | 0x0004);
397423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0802,
398423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0802) & ~(0x1 | 0x2));
399423e3ce3SKalle Valo 	if (phy->rev >= 6) {
400423e3ce3SKalle Valo 		backup[12] = b43legacy_phy_read(dev, 0x002E);
401423e3ce3SKalle Valo 		backup[13] = b43legacy_phy_read(dev, 0x002F);
402423e3ce3SKalle Valo 		backup[14] = b43legacy_phy_read(dev, 0x080F);
403423e3ce3SKalle Valo 		backup[15] = b43legacy_phy_read(dev, 0x0810);
404423e3ce3SKalle Valo 		backup[16] = b43legacy_phy_read(dev, 0x0801);
405423e3ce3SKalle Valo 		backup[17] = b43legacy_phy_read(dev, 0x0060);
406423e3ce3SKalle Valo 		backup[18] = b43legacy_phy_read(dev, 0x0014);
407423e3ce3SKalle Valo 		backup[19] = b43legacy_phy_read(dev, 0x0478);
408423e3ce3SKalle Valo 
409423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002E, 0);
410423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002F, 0);
411423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x080F, 0);
412423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0810, 0);
413423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0478,
414423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0478) | 0x0100);
415423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0801,
416423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0801) | 0x0040);
417423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0060,
418423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0060) | 0x0040);
419423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0014,
420423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0014) | 0x0200);
421423e3ce3SKalle Valo 	}
422423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A,
423423e3ce3SKalle Valo 				b43legacy_radio_read16(dev, 0x007A) | 0x0070);
424423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A,
425423e3ce3SKalle Valo 				b43legacy_radio_read16(dev, 0x007A) | 0x0080);
426423e3ce3SKalle Valo 	udelay(30);
427423e3ce3SKalle Valo 
428423e3ce3SKalle Valo 	v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
429423e3ce3SKalle Valo 	if (v47F >= 0x20)
430423e3ce3SKalle Valo 		v47F -= 0x40;
431423e3ce3SKalle Valo 	if (v47F == 31) {
432423e3ce3SKalle Valo 		for (i = 7; i >= 4; i--) {
433423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x007B, i);
434423e3ce3SKalle Valo 			udelay(20);
435423e3ce3SKalle Valo 			v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8)
436423e3ce3SKalle Valo 							 & 0x003F);
437423e3ce3SKalle Valo 			if (v47F >= 0x20)
438423e3ce3SKalle Valo 				v47F -= 0x40;
439423e3ce3SKalle Valo 			if (v47F < 31 && saved == 0xFFFF)
440423e3ce3SKalle Valo 				saved = i;
441423e3ce3SKalle Valo 		}
442423e3ce3SKalle Valo 		if (saved == 0xFFFF)
443423e3ce3SKalle Valo 			saved = 4;
444423e3ce3SKalle Valo 	} else {
445423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
446423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
447423e3ce3SKalle Valo 					& 0x007F);
448423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814,
449423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0814) | 0x0001);
450423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815,
451423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0815) & 0xFFFE);
452423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0811,
453423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0811) | 0x000C);
454423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812,
455423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0812) | 0x000C);
456423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0811,
457423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0811) | 0x0030);
458423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812,
459423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0812) | 0x0030);
460423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005A, 0x0480);
461423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0059, 0x0810);
462423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0058, 0x000D);
463423e3ce3SKalle Valo 		if (phy->analog == 0)
464423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0003, 0x0122);
465423e3ce3SKalle Valo 		else
466423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x000A,
467423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x000A)
468423e3ce3SKalle Valo 					    | 0x2000);
469423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0814,
470423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0814) | 0x0004);
471423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0815,
472423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0815) & 0xFFFB);
473423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0003,
474423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x0003) & 0xFF9F)
475423e3ce3SKalle Valo 				    | 0x0040);
476423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
477423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
478423e3ce3SKalle Valo 					| 0x000F);
479423e3ce3SKalle Valo 		b43legacy_set_all_gains(dev, 3, 0, 1);
480423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043,
481423e3ce3SKalle Valo 					(b43legacy_radio_read16(dev, 0x0043)
482423e3ce3SKalle Valo 					& 0x00F0) | 0x000F);
483423e3ce3SKalle Valo 		udelay(30);
484423e3ce3SKalle Valo 		v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
485423e3ce3SKalle Valo 		if (v47F >= 0x20)
486423e3ce3SKalle Valo 			v47F -= 0x40;
487423e3ce3SKalle Valo 		if (v47F == -32) {
488423e3ce3SKalle Valo 			for (i = 0; i < 4; i++) {
489423e3ce3SKalle Valo 				b43legacy_radio_write16(dev, 0x007B, i);
490423e3ce3SKalle Valo 				udelay(20);
491423e3ce3SKalle Valo 				v47F = (s16)((b43legacy_phy_read(dev, 0x047F) >>
492423e3ce3SKalle Valo 								 8) & 0x003F);
493423e3ce3SKalle Valo 				if (v47F >= 0x20)
494423e3ce3SKalle Valo 					v47F -= 0x40;
495423e3ce3SKalle Valo 				if (v47F > -31 && saved == 0xFFFF)
496423e3ce3SKalle Valo 					saved = i;
497423e3ce3SKalle Valo 			}
498423e3ce3SKalle Valo 			if (saved == 0xFFFF)
499423e3ce3SKalle Valo 				saved = 3;
500423e3ce3SKalle Valo 		} else
501423e3ce3SKalle Valo 			saved = 0;
502423e3ce3SKalle Valo 	}
503423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007B, saved);
504423e3ce3SKalle Valo 
505423e3ce3SKalle Valo 	if (phy->rev >= 6) {
506423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002E, backup[12]);
507423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002F, backup[13]);
508423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x080F, backup[14]);
509423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0810, backup[15]);
510423e3ce3SKalle Valo 	}
511423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0814, backup[3]);
512423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0815, backup[4]);
513423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x005A, backup[5]);
514423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0059, backup[6]);
515423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0058, backup[7]);
516423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x000A, backup[8]);
517423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0003, backup[9]);
518423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0043, backup[11]);
519423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x007A, backup[10]);
520423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0802,
521423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0802) | 0x1 | 0x2);
522423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0429,
523423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, 0x0429) | 0x8000);
524423e3ce3SKalle Valo 	b43legacy_set_original_gains(dev);
525423e3ce3SKalle Valo 	if (phy->rev >= 6) {
526423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0801, backup[16]);
527423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0060, backup[17]);
528423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0014, backup[18]);
529423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0478, backup[19]);
530423e3ce3SKalle Valo 	}
531423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0001, backup[0]);
532423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0812, backup[2]);
533423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0811, backup[1]);
534423e3ce3SKalle Valo }
535423e3ce3SKalle Valo 
b43legacy_calc_nrssi_slope(struct b43legacy_wldev * dev)536423e3ce3SKalle Valo void b43legacy_calc_nrssi_slope(struct b43legacy_wldev *dev)
537423e3ce3SKalle Valo {
538423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
539423e3ce3SKalle Valo 	u16 backup[18] = { 0 };
540423e3ce3SKalle Valo 	u16 tmp;
541423e3ce3SKalle Valo 	s16 nrssi0;
542423e3ce3SKalle Valo 	s16 nrssi1;
543423e3ce3SKalle Valo 
544423e3ce3SKalle Valo 	switch (phy->type) {
545423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B:
546423e3ce3SKalle Valo 		backup[0] = b43legacy_radio_read16(dev, 0x007A);
547423e3ce3SKalle Valo 		backup[1] = b43legacy_radio_read16(dev, 0x0052);
548423e3ce3SKalle Valo 		backup[2] = b43legacy_radio_read16(dev, 0x0043);
549423e3ce3SKalle Valo 		backup[3] = b43legacy_phy_read(dev, 0x0030);
550423e3ce3SKalle Valo 		backup[4] = b43legacy_phy_read(dev, 0x0026);
551423e3ce3SKalle Valo 		backup[5] = b43legacy_phy_read(dev, 0x0015);
552423e3ce3SKalle Valo 		backup[6] = b43legacy_phy_read(dev, 0x002A);
553423e3ce3SKalle Valo 		backup[7] = b43legacy_phy_read(dev, 0x0020);
554423e3ce3SKalle Valo 		backup[8] = b43legacy_phy_read(dev, 0x005A);
555423e3ce3SKalle Valo 		backup[9] = b43legacy_phy_read(dev, 0x0059);
556423e3ce3SKalle Valo 		backup[10] = b43legacy_phy_read(dev, 0x0058);
557423e3ce3SKalle Valo 		backup[11] = b43legacy_read16(dev, 0x03E2);
558423e3ce3SKalle Valo 		backup[12] = b43legacy_read16(dev, 0x03E6);
559423e3ce3SKalle Valo 		backup[13] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
560423e3ce3SKalle Valo 
561423e3ce3SKalle Valo 		tmp  = b43legacy_radio_read16(dev, 0x007A);
562423e3ce3SKalle Valo 		tmp &= (phy->rev >= 5) ? 0x007F : 0x000F;
563423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A, tmp);
564423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0030, 0x00FF);
565423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03EC, 0x7F7F);
566423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0026, 0x0000);
567423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015,
568423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0015) | 0x0020);
569423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, 0x08A3);
570423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
571423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
572423e3ce3SKalle Valo 					| 0x0080);
573423e3ce3SKalle Valo 
574423e3ce3SKalle Valo 		nrssi0 = (s16)b43legacy_phy_read(dev, 0x0027);
575423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
576423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
577423e3ce3SKalle Valo 					& 0x007F);
578423e3ce3SKalle Valo 		if (phy->analog >= 2)
579423e3ce3SKalle Valo 			b43legacy_write16(dev, 0x03E6, 0x0040);
580423e3ce3SKalle Valo 		else if (phy->analog == 0)
581423e3ce3SKalle Valo 			b43legacy_write16(dev, 0x03E6, 0x0122);
582423e3ce3SKalle Valo 		else
583423e3ce3SKalle Valo 			b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
584423e3ce3SKalle Valo 					  b43legacy_read16(dev,
585423e3ce3SKalle Valo 					  B43legacy_MMIO_CHANNEL_EXT) & 0x2000);
586423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0020, 0x3F3F);
587423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xF330);
588423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x005A, 0x0060);
589423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043,
590423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x0043)
591423e3ce3SKalle Valo 					& 0x00F0);
592423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005A, 0x0480);
593423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0059, 0x0810);
594423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0058, 0x000D);
595423e3ce3SKalle Valo 		udelay(20);
596423e3ce3SKalle Valo 
597423e3ce3SKalle Valo 		nrssi1 = (s16)b43legacy_phy_read(dev, 0x0027);
598423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0030, backup[3]);
599423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A, backup[0]);
600423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E2, backup[11]);
601423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0026, backup[4]);
602423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, backup[5]);
603423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x002A, backup[6]);
604423e3ce3SKalle Valo 		b43legacy_synth_pu_workaround(dev, phy->channel);
605423e3ce3SKalle Valo 		if (phy->analog != 0)
606423e3ce3SKalle Valo 			b43legacy_write16(dev, 0x03F4, backup[13]);
607423e3ce3SKalle Valo 
608423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0020, backup[7]);
609423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005A, backup[8]);
610423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0059, backup[9]);
611423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0058, backup[10]);
612423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, backup[1]);
613423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043, backup[2]);
614423e3ce3SKalle Valo 
615423e3ce3SKalle Valo 		if (nrssi0 == nrssi1)
616423e3ce3SKalle Valo 			phy->nrssislope = 0x00010000;
617423e3ce3SKalle Valo 		else
618423e3ce3SKalle Valo 			phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
619423e3ce3SKalle Valo 
620423e3ce3SKalle Valo 		if (nrssi0 <= -4) {
621423e3ce3SKalle Valo 			phy->nrssi[0] = nrssi0;
622423e3ce3SKalle Valo 			phy->nrssi[1] = nrssi1;
623423e3ce3SKalle Valo 		}
624423e3ce3SKalle Valo 		break;
625423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
626423e3ce3SKalle Valo 		if (phy->radio_rev >= 9)
627423e3ce3SKalle Valo 			return;
628423e3ce3SKalle Valo 		if (phy->radio_rev == 8)
629423e3ce3SKalle Valo 			b43legacy_calc_nrssi_offset(dev);
630423e3ce3SKalle Valo 
631423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
632423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
633423e3ce3SKalle Valo 				    & 0x7FFF);
634423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0802,
635423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0802) & 0xFFFC);
636423e3ce3SKalle Valo 		backup[7] = b43legacy_read16(dev, 0x03E2);
637423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E2,
638423e3ce3SKalle Valo 				  b43legacy_read16(dev, 0x03E2) | 0x8000);
639423e3ce3SKalle Valo 		backup[0] = b43legacy_radio_read16(dev, 0x007A);
640423e3ce3SKalle Valo 		backup[1] = b43legacy_radio_read16(dev, 0x0052);
641423e3ce3SKalle Valo 		backup[2] = b43legacy_radio_read16(dev, 0x0043);
642423e3ce3SKalle Valo 		backup[3] = b43legacy_phy_read(dev, 0x0015);
643423e3ce3SKalle Valo 		backup[4] = b43legacy_phy_read(dev, 0x005A);
644423e3ce3SKalle Valo 		backup[5] = b43legacy_phy_read(dev, 0x0059);
645423e3ce3SKalle Valo 		backup[6] = b43legacy_phy_read(dev, 0x0058);
646423e3ce3SKalle Valo 		backup[8] = b43legacy_read16(dev, 0x03E6);
647423e3ce3SKalle Valo 		backup[9] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
648423e3ce3SKalle Valo 		if (phy->rev >= 3) {
649423e3ce3SKalle Valo 			backup[10] = b43legacy_phy_read(dev, 0x002E);
650423e3ce3SKalle Valo 			backup[11] = b43legacy_phy_read(dev, 0x002F);
651423e3ce3SKalle Valo 			backup[12] = b43legacy_phy_read(dev, 0x080F);
652423e3ce3SKalle Valo 			backup[13] = b43legacy_phy_read(dev,
653423e3ce3SKalle Valo 						B43legacy_PHY_G_LO_CONTROL);
654423e3ce3SKalle Valo 			backup[14] = b43legacy_phy_read(dev, 0x0801);
655423e3ce3SKalle Valo 			backup[15] = b43legacy_phy_read(dev, 0x0060);
656423e3ce3SKalle Valo 			backup[16] = b43legacy_phy_read(dev, 0x0014);
657423e3ce3SKalle Valo 			backup[17] = b43legacy_phy_read(dev, 0x0478);
658423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002E, 0);
659423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL, 0);
660423e3ce3SKalle Valo 			switch (phy->rev) {
661423e3ce3SKalle Valo 			case 4: case 6: case 7:
662423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0478,
663423e3ce3SKalle Valo 						    b43legacy_phy_read(dev,
664423e3ce3SKalle Valo 						    0x0478) | 0x0100);
665423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0801,
666423e3ce3SKalle Valo 						    b43legacy_phy_read(dev,
667423e3ce3SKalle Valo 						    0x0801) | 0x0040);
668423e3ce3SKalle Valo 				break;
669423e3ce3SKalle Valo 			case 3: case 5:
670423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0801,
671423e3ce3SKalle Valo 						    b43legacy_phy_read(dev,
672423e3ce3SKalle Valo 						    0x0801) & 0xFFBF);
673423e3ce3SKalle Valo 				break;
674423e3ce3SKalle Valo 			}
675423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0060,
676423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x0060)
677423e3ce3SKalle Valo 					    | 0x0040);
678423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0014,
679423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x0014)
680423e3ce3SKalle Valo 					    | 0x0200);
681423e3ce3SKalle Valo 		}
682423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
683423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
684423e3ce3SKalle Valo 					| 0x0070);
685423e3ce3SKalle Valo 		b43legacy_set_all_gains(dev, 0, 8, 0);
686423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
687423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
688423e3ce3SKalle Valo 					& 0x00F7);
689423e3ce3SKalle Valo 		if (phy->rev >= 2) {
690423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0811,
691423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0811)
692423e3ce3SKalle Valo 					    & 0xFFCF) | 0x0030);
693423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
694423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0812)
695423e3ce3SKalle Valo 					    & 0xFFCF) | 0x0010);
696423e3ce3SKalle Valo 		}
697423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
698423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
699423e3ce3SKalle Valo 					| 0x0080);
700423e3ce3SKalle Valo 		udelay(20);
701423e3ce3SKalle Valo 
702423e3ce3SKalle Valo 		nrssi0 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
703423e3ce3SKalle Valo 		if (nrssi0 >= 0x0020)
704423e3ce3SKalle Valo 			nrssi0 -= 0x0040;
705423e3ce3SKalle Valo 
706423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
707423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
708423e3ce3SKalle Valo 					& 0x007F);
709423e3ce3SKalle Valo 		if (phy->analog >= 2)
710423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0003,
711423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0003)
712423e3ce3SKalle Valo 					    & 0xFF9F) | 0x0040);
713423e3ce3SKalle Valo 
714423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
715423e3ce3SKalle Valo 				  b43legacy_read16(dev,
716423e3ce3SKalle Valo 				  B43legacy_MMIO_CHANNEL_EXT) | 0x2000);
717423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A,
718423e3ce3SKalle Valo 					b43legacy_radio_read16(dev, 0x007A)
719423e3ce3SKalle Valo 					| 0x000F);
720423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xF330);
721423e3ce3SKalle Valo 		if (phy->rev >= 2) {
722423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
723423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0812)
724423e3ce3SKalle Valo 					    & 0xFFCF) | 0x0020);
725423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0811,
726423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0811)
727423e3ce3SKalle Valo 					    & 0xFFCF) | 0x0020);
728423e3ce3SKalle Valo 		}
729423e3ce3SKalle Valo 
730423e3ce3SKalle Valo 		b43legacy_set_all_gains(dev, 3, 0, 1);
731423e3ce3SKalle Valo 		if (phy->radio_rev == 8)
732423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x0043, 0x001F);
733423e3ce3SKalle Valo 		else {
734423e3ce3SKalle Valo 			tmp = b43legacy_radio_read16(dev, 0x0052) & 0xFF0F;
735423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x0052, tmp | 0x0060);
736423e3ce3SKalle Valo 			tmp = b43legacy_radio_read16(dev, 0x0043) & 0xFFF0;
737423e3ce3SKalle Valo 			b43legacy_radio_write16(dev, 0x0043, tmp | 0x0009);
738423e3ce3SKalle Valo 		}
739423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005A, 0x0480);
740423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0059, 0x0810);
741423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0058, 0x000D);
742423e3ce3SKalle Valo 		udelay(20);
743423e3ce3SKalle Valo 		nrssi1 = (s16)((b43legacy_phy_read(dev, 0x047F) >> 8) & 0x003F);
744423e3ce3SKalle Valo 		if (nrssi1 >= 0x0020)
745423e3ce3SKalle Valo 			nrssi1 -= 0x0040;
746423e3ce3SKalle Valo 		if (nrssi0 == nrssi1)
747423e3ce3SKalle Valo 			phy->nrssislope = 0x00010000;
748423e3ce3SKalle Valo 		else
749423e3ce3SKalle Valo 			phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
750423e3ce3SKalle Valo 		if (nrssi0 >= -4) {
751423e3ce3SKalle Valo 			phy->nrssi[0] = nrssi1;
752423e3ce3SKalle Valo 			phy->nrssi[1] = nrssi0;
753423e3ce3SKalle Valo 		}
754423e3ce3SKalle Valo 		if (phy->rev >= 3) {
755423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002E, backup[10]);
756423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x002F, backup[11]);
757423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x080F, backup[12]);
758423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_G_LO_CONTROL,
759423e3ce3SKalle Valo 					    backup[13]);
760423e3ce3SKalle Valo 		}
761423e3ce3SKalle Valo 		if (phy->rev >= 2) {
762423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
763423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x0812)
764423e3ce3SKalle Valo 					    & 0xFFCF);
765423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0811,
766423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x0811)
767423e3ce3SKalle Valo 					    & 0xFFCF);
768423e3ce3SKalle Valo 		}
769423e3ce3SKalle Valo 
770423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x007A, backup[0]);
771423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, backup[1]);
772423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043, backup[2]);
773423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E2, backup[7]);
774423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E6, backup[8]);
775423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[9]);
776423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, backup[3]);
777423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005A, backup[4]);
778423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0059, backup[5]);
779423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0058, backup[6]);
780423e3ce3SKalle Valo 		b43legacy_synth_pu_workaround(dev, phy->channel);
781423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0802,
782423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x0802) | 0x0003);
783423e3ce3SKalle Valo 		b43legacy_set_original_gains(dev);
784423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
785423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
786423e3ce3SKalle Valo 				    | 0x8000);
787423e3ce3SKalle Valo 		if (phy->rev >= 3) {
788423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0801, backup[14]);
789423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0060, backup[15]);
790423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0014, backup[16]);
791423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0478, backup[17]);
792423e3ce3SKalle Valo 		}
793423e3ce3SKalle Valo 		b43legacy_nrssi_mem_update(dev);
794423e3ce3SKalle Valo 		b43legacy_calc_nrssi_threshold(dev);
795423e3ce3SKalle Valo 		break;
796423e3ce3SKalle Valo 	default:
797423e3ce3SKalle Valo 		B43legacy_BUG_ON(1);
798423e3ce3SKalle Valo 	}
799423e3ce3SKalle Valo }
800423e3ce3SKalle Valo 
b43legacy_calc_nrssi_threshold(struct b43legacy_wldev * dev)801423e3ce3SKalle Valo void b43legacy_calc_nrssi_threshold(struct b43legacy_wldev *dev)
802423e3ce3SKalle Valo {
803423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
804423e3ce3SKalle Valo 	s32 threshold;
805423e3ce3SKalle Valo 	s32 a;
806423e3ce3SKalle Valo 	s32 b;
807423e3ce3SKalle Valo 	s16 tmp16;
808423e3ce3SKalle Valo 	u16 tmp_u16;
809423e3ce3SKalle Valo 
810423e3ce3SKalle Valo 	switch (phy->type) {
811423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B: {
812423e3ce3SKalle Valo 		if (phy->radio_ver != 0x2050)
813423e3ce3SKalle Valo 			return;
814423e3ce3SKalle Valo 		if (!(dev->dev->bus->sprom.boardflags_lo &
815423e3ce3SKalle Valo 		    B43legacy_BFL_RSSI))
816423e3ce3SKalle Valo 			return;
817423e3ce3SKalle Valo 
818423e3ce3SKalle Valo 		if (phy->radio_rev >= 6) {
819423e3ce3SKalle Valo 			threshold = (phy->nrssi[1] - phy->nrssi[0]) * 32;
820423e3ce3SKalle Valo 			threshold += 20 * (phy->nrssi[0] + 1);
821423e3ce3SKalle Valo 			threshold /= 40;
822423e3ce3SKalle Valo 		} else
823423e3ce3SKalle Valo 			threshold = phy->nrssi[1] - 5;
824423e3ce3SKalle Valo 
825423e3ce3SKalle Valo 		threshold = clamp_val(threshold, 0, 0x3E);
826423e3ce3SKalle Valo 		b43legacy_phy_read(dev, 0x0020); /* dummy read */
827423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0020, (((u16)threshold) << 8)
828423e3ce3SKalle Valo 				    | 0x001C);
829423e3ce3SKalle Valo 
830423e3ce3SKalle Valo 		if (phy->radio_rev >= 6) {
831423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0087, 0x0E0D);
832423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0086, 0x0C0B);
833423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0085, 0x0A09);
834423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0084, 0x0808);
835423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0083, 0x0808);
836423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0082, 0x0604);
837423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0081, 0x0302);
838423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0080, 0x0100);
839423e3ce3SKalle Valo 		}
840423e3ce3SKalle Valo 		break;
841423e3ce3SKalle Valo 	}
842423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
843423e3ce3SKalle Valo 		if (!phy->gmode ||
844423e3ce3SKalle Valo 		    !(dev->dev->bus->sprom.boardflags_lo &
845423e3ce3SKalle Valo 		    B43legacy_BFL_RSSI)) {
846423e3ce3SKalle Valo 			tmp16 = b43legacy_nrssi_hw_read(dev, 0x20);
847423e3ce3SKalle Valo 			if (tmp16 >= 0x20)
848423e3ce3SKalle Valo 				tmp16 -= 0x40;
849423e3ce3SKalle Valo 			if (tmp16 < 3)
850423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x048A,
851423e3ce3SKalle Valo 						    (b43legacy_phy_read(dev,
852423e3ce3SKalle Valo 						    0x048A) & 0xF000) | 0x09EB);
853423e3ce3SKalle Valo 			else
854423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x048A,
855423e3ce3SKalle Valo 						    (b43legacy_phy_read(dev,
856423e3ce3SKalle Valo 						    0x048A) & 0xF000) | 0x0AED);
857423e3ce3SKalle Valo 		} else {
858423e3ce3SKalle Valo 			if (phy->interfmode ==
859423e3ce3SKalle Valo 			    B43legacy_RADIO_INTERFMODE_NONWLAN) {
860423e3ce3SKalle Valo 				a = 0xE;
861423e3ce3SKalle Valo 				b = 0xA;
862423e3ce3SKalle Valo 			} else if (!phy->aci_wlan_automatic &&
863423e3ce3SKalle Valo 				    phy->aci_enable) {
864423e3ce3SKalle Valo 				a = 0x13;
865423e3ce3SKalle Valo 				b = 0x12;
866423e3ce3SKalle Valo 			} else {
867423e3ce3SKalle Valo 				a = 0xE;
868423e3ce3SKalle Valo 				b = 0x11;
869423e3ce3SKalle Valo 			}
870423e3ce3SKalle Valo 
871423e3ce3SKalle Valo 			a = a * (phy->nrssi[1] - phy->nrssi[0]);
872423e3ce3SKalle Valo 			a += (phy->nrssi[0] << 6);
873423e3ce3SKalle Valo 			if (a < 32)
874423e3ce3SKalle Valo 				a += 31;
875423e3ce3SKalle Valo 			else
876423e3ce3SKalle Valo 				a += 32;
877423e3ce3SKalle Valo 			a = a >> 6;
878423e3ce3SKalle Valo 			a = clamp_val(a, -31, 31);
879423e3ce3SKalle Valo 
880423e3ce3SKalle Valo 			b = b * (phy->nrssi[1] - phy->nrssi[0]);
881423e3ce3SKalle Valo 			b += (phy->nrssi[0] << 6);
882423e3ce3SKalle Valo 			if (b < 32)
883423e3ce3SKalle Valo 				b += 31;
884423e3ce3SKalle Valo 			else
885423e3ce3SKalle Valo 				b += 32;
886423e3ce3SKalle Valo 			b = b >> 6;
887423e3ce3SKalle Valo 			b = clamp_val(b, -31, 31);
888423e3ce3SKalle Valo 
889423e3ce3SKalle Valo 			tmp_u16 = b43legacy_phy_read(dev, 0x048A) & 0xF000;
890423e3ce3SKalle Valo 			tmp_u16 |= ((u32)b & 0x0000003F);
891423e3ce3SKalle Valo 			tmp_u16 |= (((u32)a & 0x0000003F) << 6);
892423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x048A, tmp_u16);
893423e3ce3SKalle Valo 		}
894423e3ce3SKalle Valo 		break;
895423e3ce3SKalle Valo 	default:
896423e3ce3SKalle Valo 		B43legacy_BUG_ON(1);
897423e3ce3SKalle Valo 	}
898423e3ce3SKalle Valo }
899423e3ce3SKalle Valo 
900423e3ce3SKalle Valo /* Stack implementation to save/restore values from the
901423e3ce3SKalle Valo  * interference mitigation code.
902423e3ce3SKalle Valo  * It is save to restore values in random order.
903423e3ce3SKalle Valo  */
_stack_save(u32 * _stackptr,size_t * stackidx,u8 id,u16 offset,u16 value)904423e3ce3SKalle Valo static void _stack_save(u32 *_stackptr, size_t *stackidx,
905423e3ce3SKalle Valo 			u8 id, u16 offset, u16 value)
906423e3ce3SKalle Valo {
907423e3ce3SKalle Valo 	u32 *stackptr = &(_stackptr[*stackidx]);
908423e3ce3SKalle Valo 
909423e3ce3SKalle Valo 	B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000));
910423e3ce3SKalle Valo 	B43legacy_WARN_ON(!((id & 0xF8) == 0x00));
911423e3ce3SKalle Valo 	*stackptr = offset;
912423e3ce3SKalle Valo 	*stackptr |= ((u32)id) << 13;
913423e3ce3SKalle Valo 	*stackptr |= ((u32)value) << 16;
914423e3ce3SKalle Valo 	(*stackidx)++;
915423e3ce3SKalle Valo 	B43legacy_WARN_ON(!(*stackidx < B43legacy_INTERFSTACK_SIZE));
916423e3ce3SKalle Valo }
917423e3ce3SKalle Valo 
_stack_restore(u32 * stackptr,u8 id,u16 offset)918423e3ce3SKalle Valo static u16 _stack_restore(u32 *stackptr,
919423e3ce3SKalle Valo 			  u8 id, u16 offset)
920423e3ce3SKalle Valo {
921423e3ce3SKalle Valo 	size_t i;
922423e3ce3SKalle Valo 
923423e3ce3SKalle Valo 	B43legacy_WARN_ON(!((offset & 0xE000) == 0x0000));
924423e3ce3SKalle Valo 	B43legacy_WARN_ON(!((id & 0xF8) == 0x00));
925423e3ce3SKalle Valo 	for (i = 0; i < B43legacy_INTERFSTACK_SIZE; i++, stackptr++) {
926423e3ce3SKalle Valo 		if ((*stackptr & 0x00001FFF) != offset)
927423e3ce3SKalle Valo 			continue;
928423e3ce3SKalle Valo 		if (((*stackptr & 0x00007000) >> 13) != id)
929423e3ce3SKalle Valo 			continue;
930423e3ce3SKalle Valo 		return ((*stackptr & 0xFFFF0000) >> 16);
931423e3ce3SKalle Valo 	}
932423e3ce3SKalle Valo 	B43legacy_BUG_ON(1);
933423e3ce3SKalle Valo 
934423e3ce3SKalle Valo 	return 0;
935423e3ce3SKalle Valo }
936423e3ce3SKalle Valo 
937423e3ce3SKalle Valo #define phy_stacksave(offset)					\
938423e3ce3SKalle Valo 	do {							\
939423e3ce3SKalle Valo 		_stack_save(stack, &stackidx, 0x1, (offset),	\
940423e3ce3SKalle Valo 			    b43legacy_phy_read(dev, (offset)));	\
941423e3ce3SKalle Valo 	} while (0)
942423e3ce3SKalle Valo #define phy_stackrestore(offset)				\
943423e3ce3SKalle Valo 	do {							\
944423e3ce3SKalle Valo 		b43legacy_phy_write(dev, (offset),		\
945423e3ce3SKalle Valo 				    _stack_restore(stack, 0x1,	\
946423e3ce3SKalle Valo 				    (offset)));			\
947423e3ce3SKalle Valo 	} while (0)
948423e3ce3SKalle Valo #define radio_stacksave(offset)						\
949423e3ce3SKalle Valo 	do {								\
950423e3ce3SKalle Valo 		_stack_save(stack, &stackidx, 0x2, (offset),		\
951423e3ce3SKalle Valo 			    b43legacy_radio_read16(dev, (offset)));	\
952423e3ce3SKalle Valo 	} while (0)
953423e3ce3SKalle Valo #define radio_stackrestore(offset)					\
954423e3ce3SKalle Valo 	do {								\
955423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, (offset),			\
956423e3ce3SKalle Valo 					_stack_restore(stack, 0x2,	\
957423e3ce3SKalle Valo 					(offset)));			\
958423e3ce3SKalle Valo 	} while (0)
959423e3ce3SKalle Valo #define ilt_stacksave(offset)					\
960423e3ce3SKalle Valo 	do {							\
961423e3ce3SKalle Valo 		_stack_save(stack, &stackidx, 0x3, (offset),	\
962423e3ce3SKalle Valo 			    b43legacy_ilt_read(dev, (offset)));	\
963423e3ce3SKalle Valo 	} while (0)
964423e3ce3SKalle Valo #define ilt_stackrestore(offset)				\
965423e3ce3SKalle Valo 	do {							\
966423e3ce3SKalle Valo 		b43legacy_ilt_write(dev, (offset),		\
967423e3ce3SKalle Valo 				  _stack_restore(stack, 0x3,	\
968423e3ce3SKalle Valo 						 (offset)));	\
969423e3ce3SKalle Valo 	} while (0)
970423e3ce3SKalle Valo 
971423e3ce3SKalle Valo static void
b43legacy_radio_interference_mitigation_enable(struct b43legacy_wldev * dev,int mode)972423e3ce3SKalle Valo b43legacy_radio_interference_mitigation_enable(struct b43legacy_wldev *dev,
973423e3ce3SKalle Valo 					       int mode)
974423e3ce3SKalle Valo {
975423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
976423e3ce3SKalle Valo 	u16 tmp;
977423e3ce3SKalle Valo 	u16 flipped;
978423e3ce3SKalle Valo 	u32 tmp32;
979423e3ce3SKalle Valo 	size_t stackidx = 0;
980423e3ce3SKalle Valo 	u32 *stack = phy->interfstack;
981423e3ce3SKalle Valo 
982423e3ce3SKalle Valo 	switch (mode) {
983423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_NONWLAN:
984423e3ce3SKalle Valo 		if (phy->rev != 1) {
985423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x042B,
986423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x042B)
987423e3ce3SKalle Valo 					    | 0x0800);
988423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
989423e3ce3SKalle Valo 					    b43legacy_phy_read(dev,
990423e3ce3SKalle Valo 					    B43legacy_PHY_G_CRS) & ~0x4000);
991423e3ce3SKalle Valo 			break;
992423e3ce3SKalle Valo 		}
993423e3ce3SKalle Valo 		radio_stacksave(0x0078);
994423e3ce3SKalle Valo 		tmp = (b43legacy_radio_read16(dev, 0x0078) & 0x001E);
995423e3ce3SKalle Valo 		flipped = flip_4bit(tmp);
996423e3ce3SKalle Valo 		if (flipped < 10 && flipped >= 8)
997423e3ce3SKalle Valo 			flipped = 7;
998423e3ce3SKalle Valo 		else if (flipped >= 10)
999423e3ce3SKalle Valo 			flipped -= 3;
1000423e3ce3SKalle Valo 		flipped = flip_4bit(flipped);
1001423e3ce3SKalle Valo 		flipped = (flipped << 1) | 0x0020;
1002423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0078, flipped);
1003423e3ce3SKalle Valo 
1004423e3ce3SKalle Valo 		b43legacy_calc_nrssi_threshold(dev);
1005423e3ce3SKalle Valo 
1006423e3ce3SKalle Valo 		phy_stacksave(0x0406);
1007423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0406, 0x7E28);
1008423e3ce3SKalle Valo 
1009423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x042B,
1010423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x042B) | 0x0800);
1011423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
1012423e3ce3SKalle Valo 				    b43legacy_phy_read(dev,
1013423e3ce3SKalle Valo 				    B43legacy_PHY_RADIO_BITFIELD) | 0x1000);
1014423e3ce3SKalle Valo 
1015423e3ce3SKalle Valo 		phy_stacksave(0x04A0);
1016423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A0,
1017423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A0) & 0xC0C0)
1018423e3ce3SKalle Valo 				    | 0x0008);
1019423e3ce3SKalle Valo 		phy_stacksave(0x04A1);
1020423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A1,
1021423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A1) & 0xC0C0)
1022423e3ce3SKalle Valo 				    | 0x0605);
1023423e3ce3SKalle Valo 		phy_stacksave(0x04A2);
1024423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A2,
1025423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A2) & 0xC0C0)
1026423e3ce3SKalle Valo 				    | 0x0204);
1027423e3ce3SKalle Valo 		phy_stacksave(0x04A8);
1028423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A8,
1029423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A8) & 0xC0C0)
1030423e3ce3SKalle Valo 				    | 0x0803);
1031423e3ce3SKalle Valo 		phy_stacksave(0x04AB);
1032423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AB,
1033423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04AB) & 0xC0C0)
1034423e3ce3SKalle Valo 				    | 0x0605);
1035423e3ce3SKalle Valo 
1036423e3ce3SKalle Valo 		phy_stacksave(0x04A7);
1037423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A7, 0x0002);
1038423e3ce3SKalle Valo 		phy_stacksave(0x04A3);
1039423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A3, 0x287A);
1040423e3ce3SKalle Valo 		phy_stacksave(0x04A9);
1041423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A9, 0x2027);
1042423e3ce3SKalle Valo 		phy_stacksave(0x0493);
1043423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0493, 0x32F5);
1044423e3ce3SKalle Valo 		phy_stacksave(0x04AA);
1045423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AA, 0x2027);
1046423e3ce3SKalle Valo 		phy_stacksave(0x04AC);
1047423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AC, 0x32F5);
1048423e3ce3SKalle Valo 		break;
1049423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_MANUALWLAN:
1050423e3ce3SKalle Valo 		if (b43legacy_phy_read(dev, 0x0033) & 0x0800)
1051423e3ce3SKalle Valo 			break;
1052423e3ce3SKalle Valo 
1053423e3ce3SKalle Valo 		phy->aci_enable = true;
1054423e3ce3SKalle Valo 
1055423e3ce3SKalle Valo 		phy_stacksave(B43legacy_PHY_RADIO_BITFIELD);
1056423e3ce3SKalle Valo 		phy_stacksave(B43legacy_PHY_G_CRS);
1057423e3ce3SKalle Valo 		if (phy->rev < 2)
1058423e3ce3SKalle Valo 			phy_stacksave(0x0406);
1059423e3ce3SKalle Valo 		else {
1060423e3ce3SKalle Valo 			phy_stacksave(0x04C0);
1061423e3ce3SKalle Valo 			phy_stacksave(0x04C1);
1062423e3ce3SKalle Valo 		}
1063423e3ce3SKalle Valo 		phy_stacksave(0x0033);
1064423e3ce3SKalle Valo 		phy_stacksave(0x04A7);
1065423e3ce3SKalle Valo 		phy_stacksave(0x04A3);
1066423e3ce3SKalle Valo 		phy_stacksave(0x04A9);
1067423e3ce3SKalle Valo 		phy_stacksave(0x04AA);
1068423e3ce3SKalle Valo 		phy_stacksave(0x04AC);
1069423e3ce3SKalle Valo 		phy_stacksave(0x0493);
1070423e3ce3SKalle Valo 		phy_stacksave(0x04A1);
1071423e3ce3SKalle Valo 		phy_stacksave(0x04A0);
1072423e3ce3SKalle Valo 		phy_stacksave(0x04A2);
1073423e3ce3SKalle Valo 		phy_stacksave(0x048A);
1074423e3ce3SKalle Valo 		phy_stacksave(0x04A8);
1075423e3ce3SKalle Valo 		phy_stacksave(0x04AB);
1076423e3ce3SKalle Valo 		if (phy->rev == 2) {
1077423e3ce3SKalle Valo 			phy_stacksave(0x04AD);
1078423e3ce3SKalle Valo 			phy_stacksave(0x04AE);
1079423e3ce3SKalle Valo 		} else if (phy->rev >= 3) {
1080423e3ce3SKalle Valo 			phy_stacksave(0x04AD);
1081423e3ce3SKalle Valo 			phy_stacksave(0x0415);
1082423e3ce3SKalle Valo 			phy_stacksave(0x0416);
1083423e3ce3SKalle Valo 			phy_stacksave(0x0417);
1084423e3ce3SKalle Valo 			ilt_stacksave(0x1A00 + 0x2);
1085423e3ce3SKalle Valo 			ilt_stacksave(0x1A00 + 0x3);
1086423e3ce3SKalle Valo 		}
1087423e3ce3SKalle Valo 		phy_stacksave(0x042B);
1088423e3ce3SKalle Valo 		phy_stacksave(0x048C);
1089423e3ce3SKalle Valo 
1090423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
1091423e3ce3SKalle Valo 				    b43legacy_phy_read(dev,
1092423e3ce3SKalle Valo 				    B43legacy_PHY_RADIO_BITFIELD) & ~0x1000);
1093423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
1094423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev,
1095423e3ce3SKalle Valo 				    B43legacy_PHY_G_CRS)
1096423e3ce3SKalle Valo 				    & 0xFFFC) | 0x0002);
1097423e3ce3SKalle Valo 
1098423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0033, 0x0800);
1099423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A3, 0x2027);
1100423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A9, 0x1CA8);
1101423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0493, 0x287A);
1102423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AA, 0x1CA8);
1103423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AC, 0x287A);
1104423e3ce3SKalle Valo 
1105423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A0,
1106423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A0)
1107423e3ce3SKalle Valo 				    & 0xFFC0) | 0x001A);
1108423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A7, 0x000D);
1109423e3ce3SKalle Valo 
1110423e3ce3SKalle Valo 		if (phy->rev < 2)
1111423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0406, 0xFF0D);
1112423e3ce3SKalle Valo 		else if (phy->rev == 2) {
1113423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C0, 0xFFFF);
1114423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C1, 0x00A9);
1115423e3ce3SKalle Valo 		} else {
1116423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C0, 0x00C1);
1117423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04C1, 0x0059);
1118423e3ce3SKalle Valo 		}
1119423e3ce3SKalle Valo 
1120423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A1,
1121423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A1)
1122423e3ce3SKalle Valo 				    & 0xC0FF) | 0x1800);
1123423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A1,
1124423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A1)
1125423e3ce3SKalle Valo 				    & 0xFFC0) | 0x0015);
1126423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A8,
1127423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A8)
1128423e3ce3SKalle Valo 				    & 0xCFFF) | 0x1000);
1129423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A8,
1130423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A8)
1131423e3ce3SKalle Valo 				    & 0xF0FF) | 0x0A00);
1132423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AB,
1133423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04AB)
1134423e3ce3SKalle Valo 				    & 0xCFFF) | 0x1000);
1135423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AB,
1136423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04AB)
1137423e3ce3SKalle Valo 				    & 0xF0FF) | 0x0800);
1138423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AB,
1139423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04AB)
1140423e3ce3SKalle Valo 				    & 0xFFCF) | 0x0010);
1141423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04AB,
1142423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04AB)
1143423e3ce3SKalle Valo 				    & 0xFFF0) | 0x0005);
1144423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A8,
1145423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A8)
1146423e3ce3SKalle Valo 				    & 0xFFCF) | 0x0010);
1147423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A8,
1148423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A8)
1149423e3ce3SKalle Valo 				    & 0xFFF0) | 0x0006);
1150423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A2,
1151423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A2)
1152423e3ce3SKalle Valo 				    & 0xF0FF) | 0x0800);
1153423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A0,
1154423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A0)
1155423e3ce3SKalle Valo 				    & 0xF0FF) | 0x0500);
1156423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x04A2,
1157423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x04A2)
1158423e3ce3SKalle Valo 				    & 0xFFF0) | 0x000B);
1159423e3ce3SKalle Valo 
1160423e3ce3SKalle Valo 		if (phy->rev >= 3) {
1161423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x048A,
1162423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x048A)
1163423e3ce3SKalle Valo 					    & ~0x8000);
1164423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0415,
1165423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0415)
1166423e3ce3SKalle Valo 					    & 0x8000) | 0x36D8);
1167423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0416,
1168423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0416)
1169423e3ce3SKalle Valo 					    & 0x8000) | 0x36D8);
1170423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0417,
1171423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0417)
1172423e3ce3SKalle Valo 					    & 0xFE00) | 0x016D);
1173423e3ce3SKalle Valo 		} else {
1174423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x048A,
1175423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x048A)
1176423e3ce3SKalle Valo 					    | 0x1000);
1177423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x048A,
1178423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x048A)
1179423e3ce3SKalle Valo 					    & 0x9FFF) | 0x2000);
1180423e3ce3SKalle Valo 			tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
1181423e3ce3SKalle Valo 					    B43legacy_UCODEFLAGS_OFFSET);
1182423e3ce3SKalle Valo 			if (!(tmp32 & 0x800)) {
1183423e3ce3SKalle Valo 				tmp32 |= 0x800;
1184423e3ce3SKalle Valo 				b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
1185423e3ce3SKalle Valo 					    B43legacy_UCODEFLAGS_OFFSET,
1186423e3ce3SKalle Valo 					    tmp32);
1187423e3ce3SKalle Valo 			}
1188423e3ce3SKalle Valo 		}
1189423e3ce3SKalle Valo 		if (phy->rev >= 2)
1190423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x042B,
1191423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x042B)
1192423e3ce3SKalle Valo 					    | 0x0800);
1193423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x048C,
1194423e3ce3SKalle Valo 				    (b43legacy_phy_read(dev, 0x048C)
1195423e3ce3SKalle Valo 				    & 0xF0FF) | 0x0200);
1196423e3ce3SKalle Valo 		if (phy->rev == 2) {
1197423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04AE,
1198423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x04AE)
1199423e3ce3SKalle Valo 					    & 0xFF00) | 0x007F);
1200423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04AD,
1201423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x04AD)
1202423e3ce3SKalle Valo 					    & 0x00FF) | 0x1300);
1203423e3ce3SKalle Valo 		} else if (phy->rev >= 6) {
1204423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1A00 + 0x3, 0x007F);
1205423e3ce3SKalle Valo 			b43legacy_ilt_write(dev, 0x1A00 + 0x2, 0x007F);
1206423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x04AD,
1207423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x04AD)
1208423e3ce3SKalle Valo 					    & 0x00FF);
1209423e3ce3SKalle Valo 		}
1210423e3ce3SKalle Valo 		b43legacy_calc_nrssi_slope(dev);
1211423e3ce3SKalle Valo 		break;
1212423e3ce3SKalle Valo 	default:
1213423e3ce3SKalle Valo 		B43legacy_BUG_ON(1);
1214423e3ce3SKalle Valo 	}
1215423e3ce3SKalle Valo }
1216423e3ce3SKalle Valo 
1217423e3ce3SKalle Valo static void
b43legacy_radio_interference_mitigation_disable(struct b43legacy_wldev * dev,int mode)1218423e3ce3SKalle Valo b43legacy_radio_interference_mitigation_disable(struct b43legacy_wldev *dev,
1219423e3ce3SKalle Valo 						int mode)
1220423e3ce3SKalle Valo {
1221423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1222423e3ce3SKalle Valo 	u32 tmp32;
1223423e3ce3SKalle Valo 	u32 *stack = phy->interfstack;
1224423e3ce3SKalle Valo 
1225423e3ce3SKalle Valo 	switch (mode) {
1226423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_NONWLAN:
1227423e3ce3SKalle Valo 		if (phy->rev != 1) {
1228423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x042B,
1229423e3ce3SKalle Valo 					    b43legacy_phy_read(dev, 0x042B)
1230423e3ce3SKalle Valo 					    & ~0x0800);
1231423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
1232423e3ce3SKalle Valo 					    b43legacy_phy_read(dev,
1233423e3ce3SKalle Valo 					    B43legacy_PHY_G_CRS) | 0x4000);
1234423e3ce3SKalle Valo 			break;
1235423e3ce3SKalle Valo 		}
1236423e3ce3SKalle Valo 		phy_stackrestore(0x0078);
1237423e3ce3SKalle Valo 		b43legacy_calc_nrssi_threshold(dev);
1238423e3ce3SKalle Valo 		phy_stackrestore(0x0406);
1239423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x042B,
1240423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, 0x042B) & ~0x0800);
1241423e3ce3SKalle Valo 		if (!dev->bad_frames_preempt)
1242423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_RADIO_BITFIELD,
1243423e3ce3SKalle Valo 					    b43legacy_phy_read(dev,
1244423e3ce3SKalle Valo 					    B43legacy_PHY_RADIO_BITFIELD)
1245423e3ce3SKalle Valo 					    & ~(1 << 11));
1246423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
1247423e3ce3SKalle Valo 				    b43legacy_phy_read(dev, B43legacy_PHY_G_CRS)
1248423e3ce3SKalle Valo 				    | 0x4000);
1249423e3ce3SKalle Valo 		phy_stackrestore(0x04A0);
1250423e3ce3SKalle Valo 		phy_stackrestore(0x04A1);
1251423e3ce3SKalle Valo 		phy_stackrestore(0x04A2);
1252423e3ce3SKalle Valo 		phy_stackrestore(0x04A8);
1253423e3ce3SKalle Valo 		phy_stackrestore(0x04AB);
1254423e3ce3SKalle Valo 		phy_stackrestore(0x04A7);
1255423e3ce3SKalle Valo 		phy_stackrestore(0x04A3);
1256423e3ce3SKalle Valo 		phy_stackrestore(0x04A9);
1257423e3ce3SKalle Valo 		phy_stackrestore(0x0493);
1258423e3ce3SKalle Valo 		phy_stackrestore(0x04AA);
1259423e3ce3SKalle Valo 		phy_stackrestore(0x04AC);
1260423e3ce3SKalle Valo 		break;
1261423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_MANUALWLAN:
1262423e3ce3SKalle Valo 		if (!(b43legacy_phy_read(dev, 0x0033) & 0x0800))
1263423e3ce3SKalle Valo 			break;
1264423e3ce3SKalle Valo 
1265423e3ce3SKalle Valo 		phy->aci_enable = false;
1266423e3ce3SKalle Valo 
1267423e3ce3SKalle Valo 		phy_stackrestore(B43legacy_PHY_RADIO_BITFIELD);
1268423e3ce3SKalle Valo 		phy_stackrestore(B43legacy_PHY_G_CRS);
1269423e3ce3SKalle Valo 		phy_stackrestore(0x0033);
1270423e3ce3SKalle Valo 		phy_stackrestore(0x04A3);
1271423e3ce3SKalle Valo 		phy_stackrestore(0x04A9);
1272423e3ce3SKalle Valo 		phy_stackrestore(0x0493);
1273423e3ce3SKalle Valo 		phy_stackrestore(0x04AA);
1274423e3ce3SKalle Valo 		phy_stackrestore(0x04AC);
1275423e3ce3SKalle Valo 		phy_stackrestore(0x04A0);
1276423e3ce3SKalle Valo 		phy_stackrestore(0x04A7);
1277423e3ce3SKalle Valo 		if (phy->rev >= 2) {
1278423e3ce3SKalle Valo 			phy_stackrestore(0x04C0);
1279423e3ce3SKalle Valo 			phy_stackrestore(0x04C1);
1280423e3ce3SKalle Valo 		} else
1281423e3ce3SKalle Valo 			phy_stackrestore(0x0406);
1282423e3ce3SKalle Valo 		phy_stackrestore(0x04A1);
1283423e3ce3SKalle Valo 		phy_stackrestore(0x04AB);
1284423e3ce3SKalle Valo 		phy_stackrestore(0x04A8);
1285423e3ce3SKalle Valo 		if (phy->rev == 2) {
1286423e3ce3SKalle Valo 			phy_stackrestore(0x04AD);
1287423e3ce3SKalle Valo 			phy_stackrestore(0x04AE);
1288423e3ce3SKalle Valo 		} else if (phy->rev >= 3) {
1289423e3ce3SKalle Valo 			phy_stackrestore(0x04AD);
1290423e3ce3SKalle Valo 			phy_stackrestore(0x0415);
1291423e3ce3SKalle Valo 			phy_stackrestore(0x0416);
1292423e3ce3SKalle Valo 			phy_stackrestore(0x0417);
1293423e3ce3SKalle Valo 			ilt_stackrestore(0x1A00 + 0x2);
1294423e3ce3SKalle Valo 			ilt_stackrestore(0x1A00 + 0x3);
1295423e3ce3SKalle Valo 		}
1296423e3ce3SKalle Valo 		phy_stackrestore(0x04A2);
1297423e3ce3SKalle Valo 		phy_stackrestore(0x04A8);
1298423e3ce3SKalle Valo 		phy_stackrestore(0x042B);
1299423e3ce3SKalle Valo 		phy_stackrestore(0x048C);
1300423e3ce3SKalle Valo 		tmp32 = b43legacy_shm_read32(dev, B43legacy_SHM_SHARED,
1301423e3ce3SKalle Valo 					     B43legacy_UCODEFLAGS_OFFSET);
1302423e3ce3SKalle Valo 		if (tmp32 & 0x800) {
1303423e3ce3SKalle Valo 			tmp32 &= ~0x800;
1304423e3ce3SKalle Valo 			b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
1305423e3ce3SKalle Valo 					      B43legacy_UCODEFLAGS_OFFSET,
1306423e3ce3SKalle Valo 					      tmp32);
1307423e3ce3SKalle Valo 		}
1308423e3ce3SKalle Valo 		b43legacy_calc_nrssi_slope(dev);
1309423e3ce3SKalle Valo 		break;
1310423e3ce3SKalle Valo 	default:
1311423e3ce3SKalle Valo 		B43legacy_BUG_ON(1);
1312423e3ce3SKalle Valo 	}
1313423e3ce3SKalle Valo }
1314423e3ce3SKalle Valo 
1315423e3ce3SKalle Valo #undef phy_stacksave
1316423e3ce3SKalle Valo #undef phy_stackrestore
1317423e3ce3SKalle Valo #undef radio_stacksave
1318423e3ce3SKalle Valo #undef radio_stackrestore
1319423e3ce3SKalle Valo #undef ilt_stacksave
1320423e3ce3SKalle Valo #undef ilt_stackrestore
1321423e3ce3SKalle Valo 
b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev * dev,int mode)1322423e3ce3SKalle Valo int b43legacy_radio_set_interference_mitigation(struct b43legacy_wldev *dev,
1323423e3ce3SKalle Valo 						int mode)
1324423e3ce3SKalle Valo {
1325423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1326423e3ce3SKalle Valo 	int currentmode;
1327423e3ce3SKalle Valo 
1328423e3ce3SKalle Valo 	if ((phy->type != B43legacy_PHYTYPE_G) ||
1329423e3ce3SKalle Valo 	    (phy->rev == 0) || (!phy->gmode))
1330423e3ce3SKalle Valo 		return -ENODEV;
1331423e3ce3SKalle Valo 
1332423e3ce3SKalle Valo 	phy->aci_wlan_automatic = false;
1333423e3ce3SKalle Valo 	switch (mode) {
1334423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_AUTOWLAN:
1335423e3ce3SKalle Valo 		phy->aci_wlan_automatic = true;
1336423e3ce3SKalle Valo 		if (phy->aci_enable)
1337423e3ce3SKalle Valo 			mode = B43legacy_RADIO_INTERFMODE_MANUALWLAN;
1338423e3ce3SKalle Valo 		else
1339423e3ce3SKalle Valo 			mode = B43legacy_RADIO_INTERFMODE_NONE;
1340423e3ce3SKalle Valo 		break;
1341423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_NONE:
1342423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_NONWLAN:
1343423e3ce3SKalle Valo 	case B43legacy_RADIO_INTERFMODE_MANUALWLAN:
1344423e3ce3SKalle Valo 		break;
1345423e3ce3SKalle Valo 	default:
1346423e3ce3SKalle Valo 		return -EINVAL;
1347423e3ce3SKalle Valo 	}
1348423e3ce3SKalle Valo 
1349423e3ce3SKalle Valo 	currentmode = phy->interfmode;
1350423e3ce3SKalle Valo 	if (currentmode == mode)
1351423e3ce3SKalle Valo 		return 0;
1352423e3ce3SKalle Valo 	if (currentmode != B43legacy_RADIO_INTERFMODE_NONE)
1353423e3ce3SKalle Valo 		b43legacy_radio_interference_mitigation_disable(dev,
1354423e3ce3SKalle Valo 								currentmode);
1355423e3ce3SKalle Valo 
1356423e3ce3SKalle Valo 	if (mode == B43legacy_RADIO_INTERFMODE_NONE) {
1357423e3ce3SKalle Valo 		phy->aci_enable = false;
1358423e3ce3SKalle Valo 		phy->aci_hw_rssi = false;
1359423e3ce3SKalle Valo 	} else
1360423e3ce3SKalle Valo 		b43legacy_radio_interference_mitigation_enable(dev, mode);
1361423e3ce3SKalle Valo 	phy->interfmode = mode;
1362423e3ce3SKalle Valo 
1363423e3ce3SKalle Valo 	return 0;
1364423e3ce3SKalle Valo }
1365423e3ce3SKalle Valo 
b43legacy_radio_calibrationvalue(struct b43legacy_wldev * dev)1366423e3ce3SKalle Valo u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev)
1367423e3ce3SKalle Valo {
1368423e3ce3SKalle Valo 	u16 reg;
1369423e3ce3SKalle Valo 	u16 index;
1370423e3ce3SKalle Valo 	u16 ret;
1371423e3ce3SKalle Valo 
1372423e3ce3SKalle Valo 	reg = b43legacy_radio_read16(dev, 0x0060);
1373423e3ce3SKalle Valo 	index = (reg & 0x001E) >> 1;
1374423e3ce3SKalle Valo 	ret = rcc_table[index] << 1;
1375423e3ce3SKalle Valo 	ret |= (reg & 0x0001);
1376423e3ce3SKalle Valo 	ret |= 0x0020;
1377423e3ce3SKalle Valo 
1378423e3ce3SKalle Valo 	return ret;
1379423e3ce3SKalle Valo }
1380423e3ce3SKalle Valo 
1381423e3ce3SKalle Valo #define LPD(L, P, D)    (((L) << 2) | ((P) << 1) | ((D) << 0))
b43legacy_get_812_value(struct b43legacy_wldev * dev,u8 lpd)1382423e3ce3SKalle Valo static u16 b43legacy_get_812_value(struct b43legacy_wldev *dev, u8 lpd)
1383423e3ce3SKalle Valo {
1384423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1385423e3ce3SKalle Valo 	u16 loop_or = 0;
1386423e3ce3SKalle Valo 	u16 adj_loopback_gain = phy->loopback_gain[0];
1387423e3ce3SKalle Valo 	u8 loop;
1388423e3ce3SKalle Valo 	u16 extern_lna_control;
1389423e3ce3SKalle Valo 
1390423e3ce3SKalle Valo 	if (!phy->gmode)
1391423e3ce3SKalle Valo 		return 0;
1392423e3ce3SKalle Valo 	if (!has_loopback_gain(phy)) {
1393423e3ce3SKalle Valo 		if (phy->rev < 7 || !(dev->dev->bus->sprom.boardflags_lo
1394423e3ce3SKalle Valo 		    & B43legacy_BFL_EXTLNA)) {
1395423e3ce3SKalle Valo 			switch (lpd) {
1396423e3ce3SKalle Valo 			case LPD(0, 1, 1):
1397423e3ce3SKalle Valo 				return 0x0FB2;
1398423e3ce3SKalle Valo 			case LPD(0, 0, 1):
1399423e3ce3SKalle Valo 				return 0x00B2;
1400423e3ce3SKalle Valo 			case LPD(1, 0, 1):
1401423e3ce3SKalle Valo 				return 0x30B2;
1402423e3ce3SKalle Valo 			case LPD(1, 0, 0):
1403423e3ce3SKalle Valo 				return 0x30B3;
1404423e3ce3SKalle Valo 			default:
1405423e3ce3SKalle Valo 				B43legacy_BUG_ON(1);
1406423e3ce3SKalle Valo 			}
1407423e3ce3SKalle Valo 		} else {
1408423e3ce3SKalle Valo 			switch (lpd) {
1409423e3ce3SKalle Valo 			case LPD(0, 1, 1):
1410423e3ce3SKalle Valo 				return 0x8FB2;
1411423e3ce3SKalle Valo 			case LPD(0, 0, 1):
1412423e3ce3SKalle Valo 				return 0x80B2;
1413423e3ce3SKalle Valo 			case LPD(1, 0, 1):
1414423e3ce3SKalle Valo 				return 0x20B2;
1415423e3ce3SKalle Valo 			case LPD(1, 0, 0):
1416423e3ce3SKalle Valo 				return 0x20B3;
1417423e3ce3SKalle Valo 			default:
1418423e3ce3SKalle Valo 				B43legacy_BUG_ON(1);
1419423e3ce3SKalle Valo 			}
1420423e3ce3SKalle Valo 		}
1421423e3ce3SKalle Valo 	} else {
1422423e3ce3SKalle Valo 		if (phy->radio_rev == 8)
1423423e3ce3SKalle Valo 			adj_loopback_gain += 0x003E;
1424423e3ce3SKalle Valo 		else
1425423e3ce3SKalle Valo 			adj_loopback_gain += 0x0026;
1426423e3ce3SKalle Valo 		if (adj_loopback_gain >= 0x46) {
1427423e3ce3SKalle Valo 			adj_loopback_gain -= 0x46;
1428423e3ce3SKalle Valo 			extern_lna_control = 0x3000;
1429423e3ce3SKalle Valo 		} else if (adj_loopback_gain >= 0x3A) {
1430423e3ce3SKalle Valo 			adj_loopback_gain -= 0x3A;
1431423e3ce3SKalle Valo 			extern_lna_control = 0x2000;
1432423e3ce3SKalle Valo 		} else if (adj_loopback_gain >= 0x2E) {
1433423e3ce3SKalle Valo 			adj_loopback_gain -= 0x2E;
1434423e3ce3SKalle Valo 			extern_lna_control = 0x1000;
1435423e3ce3SKalle Valo 		} else {
1436423e3ce3SKalle Valo 			adj_loopback_gain -= 0x10;
1437423e3ce3SKalle Valo 			extern_lna_control = 0x0000;
1438423e3ce3SKalle Valo 		}
1439423e3ce3SKalle Valo 		for (loop = 0; loop < 16; loop++) {
1440423e3ce3SKalle Valo 			u16 tmp = adj_loopback_gain - 6 * loop;
1441423e3ce3SKalle Valo 			if (tmp < 6)
1442423e3ce3SKalle Valo 				break;
1443423e3ce3SKalle Valo 		}
1444423e3ce3SKalle Valo 
1445423e3ce3SKalle Valo 		loop_or = (loop << 8) | extern_lna_control;
1446423e3ce3SKalle Valo 		if (phy->rev >= 7 && dev->dev->bus->sprom.boardflags_lo
1447423e3ce3SKalle Valo 		    & B43legacy_BFL_EXTLNA) {
1448423e3ce3SKalle Valo 			if (extern_lna_control)
1449423e3ce3SKalle Valo 				loop_or |= 0x8000;
1450423e3ce3SKalle Valo 			switch (lpd) {
1451423e3ce3SKalle Valo 			case LPD(0, 1, 1):
1452423e3ce3SKalle Valo 				return 0x8F92;
1453423e3ce3SKalle Valo 			case LPD(0, 0, 1):
1454423e3ce3SKalle Valo 				return (0x8092 | loop_or);
1455423e3ce3SKalle Valo 			case LPD(1, 0, 1):
1456423e3ce3SKalle Valo 				return (0x2092 | loop_or);
1457423e3ce3SKalle Valo 			case LPD(1, 0, 0):
1458423e3ce3SKalle Valo 				return (0x2093 | loop_or);
1459423e3ce3SKalle Valo 			default:
1460423e3ce3SKalle Valo 				B43legacy_BUG_ON(1);
1461423e3ce3SKalle Valo 			}
1462423e3ce3SKalle Valo 		} else {
1463423e3ce3SKalle Valo 			switch (lpd) {
1464423e3ce3SKalle Valo 			case LPD(0, 1, 1):
1465423e3ce3SKalle Valo 				return 0x0F92;
1466423e3ce3SKalle Valo 			case LPD(0, 0, 1):
1467423e3ce3SKalle Valo 			case LPD(1, 0, 1):
1468423e3ce3SKalle Valo 				return (0x0092 | loop_or);
1469423e3ce3SKalle Valo 			case LPD(1, 0, 0):
1470423e3ce3SKalle Valo 				return (0x0093 | loop_or);
1471423e3ce3SKalle Valo 			default:
1472423e3ce3SKalle Valo 				B43legacy_BUG_ON(1);
1473423e3ce3SKalle Valo 			}
1474423e3ce3SKalle Valo 		}
1475423e3ce3SKalle Valo 	}
1476423e3ce3SKalle Valo 	return 0;
1477423e3ce3SKalle Valo }
1478423e3ce3SKalle Valo 
b43legacy_radio_init2050(struct b43legacy_wldev * dev)1479423e3ce3SKalle Valo u16 b43legacy_radio_init2050(struct b43legacy_wldev *dev)
1480423e3ce3SKalle Valo {
1481423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1482423e3ce3SKalle Valo 	u16 backup[21] = { 0 };
1483423e3ce3SKalle Valo 	u16 ret;
1484423e3ce3SKalle Valo 	u16 i;
1485423e3ce3SKalle Valo 	u16 j;
1486423e3ce3SKalle Valo 	u32 tmp1 = 0;
1487423e3ce3SKalle Valo 	u32 tmp2 = 0;
1488423e3ce3SKalle Valo 
1489423e3ce3SKalle Valo 	backup[0] = b43legacy_radio_read16(dev, 0x0043);
1490423e3ce3SKalle Valo 	backup[14] = b43legacy_radio_read16(dev, 0x0051);
1491423e3ce3SKalle Valo 	backup[15] = b43legacy_radio_read16(dev, 0x0052);
1492423e3ce3SKalle Valo 	backup[1] = b43legacy_phy_read(dev, 0x0015);
1493423e3ce3SKalle Valo 	backup[16] = b43legacy_phy_read(dev, 0x005A);
1494423e3ce3SKalle Valo 	backup[17] = b43legacy_phy_read(dev, 0x0059);
1495423e3ce3SKalle Valo 	backup[18] = b43legacy_phy_read(dev, 0x0058);
1496423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_B) {
1497423e3ce3SKalle Valo 		backup[2] = b43legacy_phy_read(dev, 0x0030);
1498423e3ce3SKalle Valo 		backup[3] = b43legacy_read16(dev, 0x03EC);
1499423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0030, 0x00FF);
1500423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03EC, 0x3F3F);
1501423e3ce3SKalle Valo 	} else {
1502423e3ce3SKalle Valo 		if (phy->gmode) {
1503423e3ce3SKalle Valo 			backup[4] = b43legacy_phy_read(dev, 0x0811);
1504423e3ce3SKalle Valo 			backup[5] = b43legacy_phy_read(dev, 0x0812);
1505423e3ce3SKalle Valo 			backup[6] = b43legacy_phy_read(dev, 0x0814);
1506423e3ce3SKalle Valo 			backup[7] = b43legacy_phy_read(dev, 0x0815);
1507423e3ce3SKalle Valo 			backup[8] = b43legacy_phy_read(dev,
1508423e3ce3SKalle Valo 						       B43legacy_PHY_G_CRS);
1509423e3ce3SKalle Valo 			backup[9] = b43legacy_phy_read(dev, 0x0802);
1510423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0814,
1511423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0814)
1512423e3ce3SKalle Valo 					    | 0x0003));
1513423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0815,
1514423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0815)
1515423e3ce3SKalle Valo 					    & 0xFFFC));
1516423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
1517423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev,
1518423e3ce3SKalle Valo 					    B43legacy_PHY_G_CRS) & 0x7FFF));
1519423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0802,
1520423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0802)
1521423e3ce3SKalle Valo 					    & 0xFFFC));
1522423e3ce3SKalle Valo 			if (phy->rev > 1) { /* loopback gain enabled */
1523423e3ce3SKalle Valo 				backup[19] = b43legacy_phy_read(dev, 0x080F);
1524423e3ce3SKalle Valo 				backup[20] = b43legacy_phy_read(dev, 0x0810);
1525423e3ce3SKalle Valo 				if (phy->rev >= 3)
1526423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x080F,
1527423e3ce3SKalle Valo 							    0xC020);
1528423e3ce3SKalle Valo 				else
1529423e3ce3SKalle Valo 					b43legacy_phy_write(dev, 0x080F,
1530423e3ce3SKalle Valo 							    0x8020);
1531423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0810, 0x0000);
1532423e3ce3SKalle Valo 			}
1533423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
1534423e3ce3SKalle Valo 					    b43legacy_get_812_value(dev,
1535423e3ce3SKalle Valo 					    LPD(0, 1, 1)));
1536423e3ce3SKalle Valo 			if (phy->rev < 7 ||
1537423e3ce3SKalle Valo 			    !(dev->dev->bus->sprom.boardflags_lo
1538423e3ce3SKalle Valo 			    & B43legacy_BFL_EXTLNA))
1539423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0811, 0x01B3);
1540423e3ce3SKalle Valo 			else
1541423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0811, 0x09B3);
1542423e3ce3SKalle Valo 		}
1543423e3ce3SKalle Valo 	}
1544423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO,
1545423e3ce3SKalle Valo 			(b43legacy_read16(dev, B43legacy_MMIO_PHY_RADIO)
1546423e3ce3SKalle Valo 					  | 0x8000));
1547423e3ce3SKalle Valo 	backup[10] = b43legacy_phy_read(dev, 0x0035);
1548423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0035,
1549423e3ce3SKalle Valo 			    (b43legacy_phy_read(dev, 0x0035) & 0xFF7F));
1550423e3ce3SKalle Valo 	backup[11] = b43legacy_read16(dev, 0x03E6);
1551423e3ce3SKalle Valo 	backup[12] = b43legacy_read16(dev, B43legacy_MMIO_CHANNEL_EXT);
1552423e3ce3SKalle Valo 
1553423e3ce3SKalle Valo 	/* Initialization */
1554423e3ce3SKalle Valo 	if (phy->analog == 0)
1555423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03E6, 0x0122);
1556423e3ce3SKalle Valo 	else {
1557423e3ce3SKalle Valo 		if (phy->analog >= 2)
1558423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0003,
1559423e3ce3SKalle Valo 					    (b43legacy_phy_read(dev, 0x0003)
1560423e3ce3SKalle Valo 					    & 0xFFBF) | 0x0040);
1561423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
1562423e3ce3SKalle Valo 				  (b43legacy_read16(dev,
1563423e3ce3SKalle Valo 				  B43legacy_MMIO_CHANNEL_EXT) | 0x2000));
1564423e3ce3SKalle Valo 	}
1565423e3ce3SKalle Valo 
1566423e3ce3SKalle Valo 	ret = b43legacy_radio_calibrationvalue(dev);
1567423e3ce3SKalle Valo 
1568423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_B)
1569423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0078, 0x0026);
1570423e3ce3SKalle Valo 
1571423e3ce3SKalle Valo 	if (phy->gmode)
1572423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812,
1573423e3ce3SKalle Valo 				    b43legacy_get_812_value(dev,
1574423e3ce3SKalle Valo 				    LPD(0, 1, 1)));
1575423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0015, 0xBFAF);
1576423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x002B, 0x1403);
1577423e3ce3SKalle Valo 	if (phy->gmode)
1578423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0812,
1579423e3ce3SKalle Valo 				    b43legacy_get_812_value(dev,
1580423e3ce3SKalle Valo 				    LPD(0, 0, 1)));
1581423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0015, 0xBFA0);
1582423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0051,
1583423e3ce3SKalle Valo 				(b43legacy_radio_read16(dev, 0x0051)
1584423e3ce3SKalle Valo 				| 0x0004));
1585423e3ce3SKalle Valo 	if (phy->radio_rev == 8)
1586423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043, 0x001F);
1587423e3ce3SKalle Valo 	else {
1588423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052, 0x0000);
1589423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0043,
1590423e3ce3SKalle Valo 					(b43legacy_radio_read16(dev, 0x0043)
1591423e3ce3SKalle Valo 					& 0xFFF0) | 0x0009);
1592423e3ce3SKalle Valo 	}
1593423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0058, 0x0000);
1594423e3ce3SKalle Valo 
1595423e3ce3SKalle Valo 	for (i = 0; i < 16; i++) {
1596423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x005A, 0x0480);
1597423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0059, 0xC810);
1598423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0058, 0x000D);
1599423e3ce3SKalle Valo 		if (phy->gmode)
1600423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
1601423e3ce3SKalle Valo 					    b43legacy_get_812_value(dev,
1602423e3ce3SKalle Valo 					    LPD(1, 0, 1)));
1603423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xAFB0);
1604423e3ce3SKalle Valo 		udelay(10);
1605423e3ce3SKalle Valo 		if (phy->gmode)
1606423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
1607423e3ce3SKalle Valo 					    b43legacy_get_812_value(dev,
1608423e3ce3SKalle Valo 					    LPD(1, 0, 1)));
1609423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xEFB0);
1610423e3ce3SKalle Valo 		udelay(10);
1611423e3ce3SKalle Valo 		if (phy->gmode)
1612423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
1613423e3ce3SKalle Valo 					    b43legacy_get_812_value(dev,
1614423e3ce3SKalle Valo 					    LPD(1, 0, 0)));
1615423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xFFF0);
1616423e3ce3SKalle Valo 		udelay(20);
1617423e3ce3SKalle Valo 		tmp1 += b43legacy_phy_read(dev, 0x002D);
1618423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0058, 0x0000);
1619423e3ce3SKalle Valo 		if (phy->gmode)
1620423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812,
1621423e3ce3SKalle Valo 					    b43legacy_get_812_value(dev,
1622423e3ce3SKalle Valo 					    LPD(1, 0, 1)));
1623423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xAFB0);
1624423e3ce3SKalle Valo 	}
1625423e3ce3SKalle Valo 
1626423e3ce3SKalle Valo 	tmp1++;
1627423e3ce3SKalle Valo 	tmp1 >>= 9;
1628423e3ce3SKalle Valo 	udelay(10);
1629423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0058, 0x0000);
1630423e3ce3SKalle Valo 
1631423e3ce3SKalle Valo 	for (i = 0; i < 16; i++) {
1632423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0078, (flip_4bit(i) << 1)
1633423e3ce3SKalle Valo 					| 0x0020);
1634423e3ce3SKalle Valo 		backup[13] = b43legacy_radio_read16(dev, 0x0078);
1635423e3ce3SKalle Valo 		udelay(10);
1636423e3ce3SKalle Valo 		for (j = 0; j < 16; j++) {
1637423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x005A, 0x0D80);
1638423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0059, 0xC810);
1639423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0058, 0x000D);
1640423e3ce3SKalle Valo 			if (phy->gmode)
1641423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0812,
1642423e3ce3SKalle Valo 						    b43legacy_get_812_value(dev,
1643423e3ce3SKalle Valo 						    LPD(1, 0, 1)));
1644423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0015, 0xAFB0);
1645423e3ce3SKalle Valo 			udelay(10);
1646423e3ce3SKalle Valo 			if (phy->gmode)
1647423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0812,
1648423e3ce3SKalle Valo 						    b43legacy_get_812_value(dev,
1649423e3ce3SKalle Valo 						    LPD(1, 0, 1)));
1650423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0015, 0xEFB0);
1651423e3ce3SKalle Valo 			udelay(10);
1652423e3ce3SKalle Valo 			if (phy->gmode)
1653423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0812,
1654423e3ce3SKalle Valo 						    b43legacy_get_812_value(dev,
1655423e3ce3SKalle Valo 						    LPD(1, 0, 0)));
1656423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0015, 0xFFF0);
1657423e3ce3SKalle Valo 			udelay(10);
1658423e3ce3SKalle Valo 			tmp2 += b43legacy_phy_read(dev, 0x002D);
1659423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0058, 0x0000);
1660423e3ce3SKalle Valo 			if (phy->gmode)
1661423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0812,
1662423e3ce3SKalle Valo 						    b43legacy_get_812_value(dev,
1663423e3ce3SKalle Valo 						    LPD(1, 0, 1)));
1664423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0015, 0xAFB0);
1665423e3ce3SKalle Valo 		}
1666423e3ce3SKalle Valo 		tmp2++;
1667423e3ce3SKalle Valo 		tmp2 >>= 8;
1668423e3ce3SKalle Valo 		if (tmp1 < tmp2)
1669423e3ce3SKalle Valo 			break;
1670423e3ce3SKalle Valo 	}
1671423e3ce3SKalle Valo 
1672423e3ce3SKalle Valo 	/* Restore the registers */
1673423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0015, backup[1]);
1674423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0051, backup[14]);
1675423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0052, backup[15]);
1676423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0043, backup[0]);
1677423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x005A, backup[16]);
1678423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0059, backup[17]);
1679423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0058, backup[18]);
1680423e3ce3SKalle Valo 	b43legacy_write16(dev, 0x03E6, backup[11]);
1681423e3ce3SKalle Valo 	if (phy->analog != 0)
1682423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT, backup[12]);
1683423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0035, backup[10]);
1684423e3ce3SKalle Valo 	b43legacy_radio_selectchannel(dev, phy->channel, 1);
1685423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_B) {
1686423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0030, backup[2]);
1687423e3ce3SKalle Valo 		b43legacy_write16(dev, 0x03EC, backup[3]);
1688423e3ce3SKalle Valo 	} else {
1689423e3ce3SKalle Valo 		if (phy->gmode) {
1690423e3ce3SKalle Valo 			b43legacy_write16(dev, B43legacy_MMIO_PHY_RADIO,
1691423e3ce3SKalle Valo 					  (b43legacy_read16(dev,
1692423e3ce3SKalle Valo 					  B43legacy_MMIO_PHY_RADIO) & 0x7FFF));
1693423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0811, backup[4]);
1694423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0812, backup[5]);
1695423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0814, backup[6]);
1696423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0815, backup[7]);
1697423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_G_CRS,
1698423e3ce3SKalle Valo 					    backup[8]);
1699423e3ce3SKalle Valo 			b43legacy_phy_write(dev, 0x0802, backup[9]);
1700423e3ce3SKalle Valo 			if (phy->rev > 1) {
1701423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x080F, backup[19]);
1702423e3ce3SKalle Valo 				b43legacy_phy_write(dev, 0x0810, backup[20]);
1703423e3ce3SKalle Valo 			}
1704423e3ce3SKalle Valo 		}
1705423e3ce3SKalle Valo 	}
1706423e3ce3SKalle Valo 	if (i >= 15)
1707423e3ce3SKalle Valo 		ret = backup[13];
1708423e3ce3SKalle Valo 
1709423e3ce3SKalle Valo 	return ret;
1710423e3ce3SKalle Valo }
1711423e3ce3SKalle Valo 
b43legacy_radio_selectchannel(struct b43legacy_wldev * dev,u8 channel,int synthetic_pu_workaround)1712423e3ce3SKalle Valo int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev,
1713423e3ce3SKalle Valo 				  u8 channel,
1714423e3ce3SKalle Valo 				  int synthetic_pu_workaround)
1715423e3ce3SKalle Valo {
1716423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1717423e3ce3SKalle Valo 
1718423e3ce3SKalle Valo 	if (channel == 0xFF) {
1719423e3ce3SKalle Valo 		switch (phy->type) {
1720423e3ce3SKalle Valo 		case B43legacy_PHYTYPE_B:
1721423e3ce3SKalle Valo 		case B43legacy_PHYTYPE_G:
1722423e3ce3SKalle Valo 			channel = B43legacy_RADIO_DEFAULT_CHANNEL_BG;
1723423e3ce3SKalle Valo 			break;
1724423e3ce3SKalle Valo 		default:
1725423e3ce3SKalle Valo 			B43legacy_WARN_ON(1);
1726423e3ce3SKalle Valo 		}
1727423e3ce3SKalle Valo 	}
1728423e3ce3SKalle Valo 
1729423e3ce3SKalle Valo /* TODO: Check if channel is valid - return -EINVAL if not */
1730423e3ce3SKalle Valo 	if (synthetic_pu_workaround)
1731423e3ce3SKalle Valo 		b43legacy_synth_pu_workaround(dev, channel);
1732423e3ce3SKalle Valo 
1733423e3ce3SKalle Valo 	b43legacy_write16(dev, B43legacy_MMIO_CHANNEL,
1734423e3ce3SKalle Valo 			  channel2freq_bg(channel));
1735423e3ce3SKalle Valo 
1736423e3ce3SKalle Valo 	if (channel == 14) {
1737423e3ce3SKalle Valo 		if (dev->dev->bus->sprom.country_code == 5)   /* JAPAN) */
1738423e3ce3SKalle Valo 			b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
1739423e3ce3SKalle Valo 					      B43legacy_UCODEFLAGS_OFFSET,
1740423e3ce3SKalle Valo 					      b43legacy_shm_read32(dev,
1741423e3ce3SKalle Valo 					      B43legacy_SHM_SHARED,
1742423e3ce3SKalle Valo 					      B43legacy_UCODEFLAGS_OFFSET)
1743423e3ce3SKalle Valo 					      & ~(1 << 7));
1744423e3ce3SKalle Valo 		else
1745423e3ce3SKalle Valo 			b43legacy_shm_write32(dev, B43legacy_SHM_SHARED,
1746423e3ce3SKalle Valo 					      B43legacy_UCODEFLAGS_OFFSET,
1747423e3ce3SKalle Valo 					      b43legacy_shm_read32(dev,
1748423e3ce3SKalle Valo 					      B43legacy_SHM_SHARED,
1749423e3ce3SKalle Valo 					      B43legacy_UCODEFLAGS_OFFSET)
1750423e3ce3SKalle Valo 					      | (1 << 7));
1751423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
1752423e3ce3SKalle Valo 				  b43legacy_read16(dev,
1753423e3ce3SKalle Valo 				  B43legacy_MMIO_CHANNEL_EXT) | (1 << 11));
1754423e3ce3SKalle Valo 	} else
1755423e3ce3SKalle Valo 		b43legacy_write16(dev, B43legacy_MMIO_CHANNEL_EXT,
1756423e3ce3SKalle Valo 				  b43legacy_read16(dev,
1757423e3ce3SKalle Valo 				  B43legacy_MMIO_CHANNEL_EXT) & 0xF7BF);
1758423e3ce3SKalle Valo 
1759423e3ce3SKalle Valo 	phy->channel = channel;
1760423e3ce3SKalle Valo 	/*XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states
1761423e3ce3SKalle Valo 	 *     that 2000 usecs might suffice. */
1762423e3ce3SKalle Valo 	msleep(8);
1763423e3ce3SKalle Valo 
1764423e3ce3SKalle Valo 	return 0;
1765423e3ce3SKalle Valo }
1766423e3ce3SKalle Valo 
b43legacy_radio_set_txantenna(struct b43legacy_wldev * dev,u32 val)1767423e3ce3SKalle Valo void b43legacy_radio_set_txantenna(struct b43legacy_wldev *dev, u32 val)
1768423e3ce3SKalle Valo {
1769423e3ce3SKalle Valo 	u16 tmp;
1770423e3ce3SKalle Valo 
1771423e3ce3SKalle Valo 	val <<= 8;
1772423e3ce3SKalle Valo 	tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0022) & 0xFCFF;
1773423e3ce3SKalle Valo 	b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0022, tmp | val);
1774423e3ce3SKalle Valo 	tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x03A8) & 0xFCFF;
1775423e3ce3SKalle Valo 	b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x03A8, tmp | val);
1776423e3ce3SKalle Valo 	tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 0x0054) & 0xFCFF;
1777423e3ce3SKalle Valo 	b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0054, tmp | val);
1778423e3ce3SKalle Valo }
1779423e3ce3SKalle Valo 
1780423e3ce3SKalle Valo /* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */
b43legacy_get_txgain_base_band(u16 txpower)1781423e3ce3SKalle Valo static u16 b43legacy_get_txgain_base_band(u16 txpower)
1782423e3ce3SKalle Valo {
1783423e3ce3SKalle Valo 	u16 ret;
1784423e3ce3SKalle Valo 
1785423e3ce3SKalle Valo 	B43legacy_WARN_ON(txpower > 63);
1786423e3ce3SKalle Valo 
1787423e3ce3SKalle Valo 	if (txpower >= 54)
1788423e3ce3SKalle Valo 		ret = 2;
1789423e3ce3SKalle Valo 	else if (txpower >= 49)
1790423e3ce3SKalle Valo 		ret = 4;
1791423e3ce3SKalle Valo 	else if (txpower >= 44)
1792423e3ce3SKalle Valo 		ret = 5;
1793423e3ce3SKalle Valo 	else
1794423e3ce3SKalle Valo 		ret = 6;
1795423e3ce3SKalle Valo 
1796423e3ce3SKalle Valo 	return ret;
1797423e3ce3SKalle Valo }
1798423e3ce3SKalle Valo 
1799423e3ce3SKalle Valo /* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */
b43legacy_get_txgain_freq_power_amp(u16 txpower)1800423e3ce3SKalle Valo static u16 b43legacy_get_txgain_freq_power_amp(u16 txpower)
1801423e3ce3SKalle Valo {
1802423e3ce3SKalle Valo 	u16 ret;
1803423e3ce3SKalle Valo 
1804423e3ce3SKalle Valo 	B43legacy_WARN_ON(txpower > 63);
1805423e3ce3SKalle Valo 
1806423e3ce3SKalle Valo 	if (txpower >= 32)
1807423e3ce3SKalle Valo 		ret = 0;
1808423e3ce3SKalle Valo 	else if (txpower >= 25)
1809423e3ce3SKalle Valo 		ret = 1;
1810423e3ce3SKalle Valo 	else if (txpower >= 20)
1811423e3ce3SKalle Valo 		ret = 2;
1812423e3ce3SKalle Valo 	else if (txpower >= 12)
1813423e3ce3SKalle Valo 		ret = 3;
1814423e3ce3SKalle Valo 	else
1815423e3ce3SKalle Valo 		ret = 4;
1816423e3ce3SKalle Valo 
1817423e3ce3SKalle Valo 	return ret;
1818423e3ce3SKalle Valo }
1819423e3ce3SKalle Valo 
1820423e3ce3SKalle Valo /* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */
b43legacy_get_txgain_dac(u16 txpower)1821423e3ce3SKalle Valo static u16 b43legacy_get_txgain_dac(u16 txpower)
1822423e3ce3SKalle Valo {
1823423e3ce3SKalle Valo 	u16 ret;
1824423e3ce3SKalle Valo 
1825423e3ce3SKalle Valo 	B43legacy_WARN_ON(txpower > 63);
1826423e3ce3SKalle Valo 
1827423e3ce3SKalle Valo 	if (txpower >= 54)
1828423e3ce3SKalle Valo 		ret = txpower - 53;
1829423e3ce3SKalle Valo 	else if (txpower >= 49)
1830423e3ce3SKalle Valo 		ret = txpower - 42;
1831423e3ce3SKalle Valo 	else if (txpower >= 44)
1832423e3ce3SKalle Valo 		ret = txpower - 37;
1833423e3ce3SKalle Valo 	else if (txpower >= 32)
1834423e3ce3SKalle Valo 		ret = txpower - 32;
1835423e3ce3SKalle Valo 	else if (txpower >= 25)
1836423e3ce3SKalle Valo 		ret = txpower - 20;
1837423e3ce3SKalle Valo 	else if (txpower >= 20)
1838423e3ce3SKalle Valo 		ret = txpower - 13;
1839423e3ce3SKalle Valo 	else if (txpower >= 12)
1840423e3ce3SKalle Valo 		ret = txpower - 8;
1841423e3ce3SKalle Valo 	else
1842423e3ce3SKalle Valo 		ret = txpower;
1843423e3ce3SKalle Valo 
1844423e3ce3SKalle Valo 	return ret;
1845423e3ce3SKalle Valo }
1846423e3ce3SKalle Valo 
b43legacy_radio_set_txpower_a(struct b43legacy_wldev * dev,u16 txpower)1847423e3ce3SKalle Valo void b43legacy_radio_set_txpower_a(struct b43legacy_wldev *dev, u16 txpower)
1848423e3ce3SKalle Valo {
1849423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1850423e3ce3SKalle Valo 	u16 pamp;
1851423e3ce3SKalle Valo 	u16 base;
1852423e3ce3SKalle Valo 	u16 dac;
1853423e3ce3SKalle Valo 	u16 ilt;
1854423e3ce3SKalle Valo 
1855423e3ce3SKalle Valo 	txpower = clamp_val(txpower, 0, 63);
1856423e3ce3SKalle Valo 
1857423e3ce3SKalle Valo 	pamp = b43legacy_get_txgain_freq_power_amp(txpower);
1858423e3ce3SKalle Valo 	pamp <<= 5;
1859423e3ce3SKalle Valo 	pamp &= 0x00E0;
1860423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0019, pamp);
1861423e3ce3SKalle Valo 
1862423e3ce3SKalle Valo 	base = b43legacy_get_txgain_base_band(txpower);
1863423e3ce3SKalle Valo 	base &= 0x000F;
1864423e3ce3SKalle Valo 	b43legacy_phy_write(dev, 0x0017, base | 0x0020);
1865423e3ce3SKalle Valo 
1866423e3ce3SKalle Valo 	ilt = b43legacy_ilt_read(dev, 0x3001);
1867423e3ce3SKalle Valo 	ilt &= 0x0007;
1868423e3ce3SKalle Valo 
1869423e3ce3SKalle Valo 	dac = b43legacy_get_txgain_dac(txpower);
1870423e3ce3SKalle Valo 	dac <<= 3;
1871423e3ce3SKalle Valo 	dac |= ilt;
1872423e3ce3SKalle Valo 
1873423e3ce3SKalle Valo 	b43legacy_ilt_write(dev, 0x3001, dac);
1874423e3ce3SKalle Valo 
1875423e3ce3SKalle Valo 	phy->txpwr_offset = txpower;
1876423e3ce3SKalle Valo 
1877423e3ce3SKalle Valo 	/* TODO: FuncPlaceholder (Adjust BB loft cancel) */
1878423e3ce3SKalle Valo }
1879423e3ce3SKalle Valo 
b43legacy_radio_set_txpower_bg(struct b43legacy_wldev * dev,u16 baseband_attenuation,u16 radio_attenuation,u16 txpower)1880423e3ce3SKalle Valo void b43legacy_radio_set_txpower_bg(struct b43legacy_wldev *dev,
1881423e3ce3SKalle Valo 				    u16 baseband_attenuation,
1882423e3ce3SKalle Valo 				    u16 radio_attenuation,
1883423e3ce3SKalle Valo 				    u16 txpower)
1884423e3ce3SKalle Valo {
1885423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1886423e3ce3SKalle Valo 
1887423e3ce3SKalle Valo 	if (baseband_attenuation == 0xFFFF)
1888423e3ce3SKalle Valo 		baseband_attenuation = phy->bbatt;
1889423e3ce3SKalle Valo 	if (radio_attenuation == 0xFFFF)
1890423e3ce3SKalle Valo 		radio_attenuation = phy->rfatt;
1891423e3ce3SKalle Valo 	if (txpower == 0xFFFF)
1892423e3ce3SKalle Valo 		txpower = phy->txctl1;
1893423e3ce3SKalle Valo 	phy->bbatt = baseband_attenuation;
1894423e3ce3SKalle Valo 	phy->rfatt = radio_attenuation;
1895423e3ce3SKalle Valo 	phy->txctl1 = txpower;
1896423e3ce3SKalle Valo 
1897423e3ce3SKalle Valo 	B43legacy_WARN_ON(baseband_attenuation > 11);
1898423e3ce3SKalle Valo 	if (phy->radio_rev < 6)
1899423e3ce3SKalle Valo 		B43legacy_WARN_ON(radio_attenuation > 9);
1900423e3ce3SKalle Valo 	else
1901423e3ce3SKalle Valo 		B43legacy_WARN_ON(radio_attenuation > 31);
1902423e3ce3SKalle Valo 	B43legacy_WARN_ON(txpower > 7);
1903423e3ce3SKalle Valo 
1904423e3ce3SKalle Valo 	b43legacy_phy_set_baseband_attenuation(dev, baseband_attenuation);
1905423e3ce3SKalle Valo 	b43legacy_radio_write16(dev, 0x0043, radio_attenuation);
1906423e3ce3SKalle Valo 	b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0064,
1907423e3ce3SKalle Valo 			      radio_attenuation);
1908423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050)
1909423e3ce3SKalle Valo 		b43legacy_radio_write16(dev, 0x0052,
1910423e3ce3SKalle Valo 					(b43legacy_radio_read16(dev, 0x0052)
1911423e3ce3SKalle Valo 					& ~0x0070) | ((txpower << 4) & 0x0070));
1912423e3ce3SKalle Valo 	/* FIXME: The spec is very weird and unclear here. */
1913423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_G)
1914423e3ce3SKalle Valo 		b43legacy_phy_lo_adjust(dev, 0);
1915423e3ce3SKalle Valo }
1916423e3ce3SKalle Valo 
b43legacy_default_baseband_attenuation(struct b43legacy_wldev * dev)1917423e3ce3SKalle Valo u16 b43legacy_default_baseband_attenuation(struct b43legacy_wldev *dev)
1918423e3ce3SKalle Valo {
1919423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1920423e3ce3SKalle Valo 
1921423e3ce3SKalle Valo 	if (phy->radio_ver == 0x2050 && phy->radio_rev < 6)
1922423e3ce3SKalle Valo 		return 0;
1923423e3ce3SKalle Valo 	return 2;
1924423e3ce3SKalle Valo }
1925423e3ce3SKalle Valo 
b43legacy_default_radio_attenuation(struct b43legacy_wldev * dev)1926423e3ce3SKalle Valo u16 b43legacy_default_radio_attenuation(struct b43legacy_wldev *dev)
1927423e3ce3SKalle Valo {
1928423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
1929423e3ce3SKalle Valo 	u16 att = 0xFFFF;
1930423e3ce3SKalle Valo 
1931423e3ce3SKalle Valo 	switch (phy->radio_ver) {
1932423e3ce3SKalle Valo 	case 0x2053:
1933423e3ce3SKalle Valo 		switch (phy->radio_rev) {
1934423e3ce3SKalle Valo 		case 1:
1935423e3ce3SKalle Valo 			att = 6;
1936423e3ce3SKalle Valo 			break;
1937423e3ce3SKalle Valo 		}
1938423e3ce3SKalle Valo 		break;
1939423e3ce3SKalle Valo 	case 0x2050:
1940423e3ce3SKalle Valo 		switch (phy->radio_rev) {
1941423e3ce3SKalle Valo 		case 0:
1942423e3ce3SKalle Valo 			att = 5;
1943423e3ce3SKalle Valo 			break;
1944423e3ce3SKalle Valo 		case 1:
1945423e3ce3SKalle Valo 			if (phy->type == B43legacy_PHYTYPE_G) {
1946423e3ce3SKalle Valo 				if (is_bcm_board_vendor(dev) &&
1947423e3ce3SKalle Valo 				    dev->dev->bus->boardinfo.type == 0x421 &&
1948423e3ce3SKalle Valo 				    dev->dev->bus->sprom.board_rev >= 30)
1949423e3ce3SKalle Valo 					att = 3;
1950423e3ce3SKalle Valo 				else if (is_bcm_board_vendor(dev) &&
1951423e3ce3SKalle Valo 					 dev->dev->bus->boardinfo.type == 0x416)
1952423e3ce3SKalle Valo 					att = 3;
1953423e3ce3SKalle Valo 				else
1954423e3ce3SKalle Valo 					att = 1;
1955423e3ce3SKalle Valo 			} else {
1956423e3ce3SKalle Valo 				if (is_bcm_board_vendor(dev) &&
1957423e3ce3SKalle Valo 				    dev->dev->bus->boardinfo.type == 0x421 &&
1958423e3ce3SKalle Valo 				    dev->dev->bus->sprom.board_rev >= 30)
1959423e3ce3SKalle Valo 					att = 7;
1960423e3ce3SKalle Valo 				else
1961423e3ce3SKalle Valo 					att = 6;
1962423e3ce3SKalle Valo 			}
1963423e3ce3SKalle Valo 			break;
1964423e3ce3SKalle Valo 		case 2:
1965423e3ce3SKalle Valo 			if (phy->type == B43legacy_PHYTYPE_G) {
1966423e3ce3SKalle Valo 				if (is_bcm_board_vendor(dev) &&
1967423e3ce3SKalle Valo 				    dev->dev->bus->boardinfo.type == 0x421 &&
1968423e3ce3SKalle Valo 				    dev->dev->bus->sprom.board_rev >= 30)
1969423e3ce3SKalle Valo 					att = 3;
1970423e3ce3SKalle Valo 				else if (is_bcm_board_vendor(dev) &&
1971423e3ce3SKalle Valo 					 dev->dev->bus->boardinfo.type ==
1972423e3ce3SKalle Valo 					 0x416)
1973423e3ce3SKalle Valo 					att = 5;
1974423e3ce3SKalle Valo 				else if (dev->dev->bus->chip_id == 0x4320)
1975423e3ce3SKalle Valo 					att = 4;
1976423e3ce3SKalle Valo 				else
1977423e3ce3SKalle Valo 					att = 3;
1978423e3ce3SKalle Valo 			} else
1979423e3ce3SKalle Valo 				att = 6;
1980423e3ce3SKalle Valo 			break;
1981423e3ce3SKalle Valo 		case 3:
1982423e3ce3SKalle Valo 			att = 5;
1983423e3ce3SKalle Valo 			break;
1984423e3ce3SKalle Valo 		case 4:
1985423e3ce3SKalle Valo 		case 5:
1986423e3ce3SKalle Valo 			att = 1;
1987423e3ce3SKalle Valo 			break;
1988423e3ce3SKalle Valo 		case 6:
1989423e3ce3SKalle Valo 		case 7:
1990423e3ce3SKalle Valo 			att = 5;
1991423e3ce3SKalle Valo 			break;
1992423e3ce3SKalle Valo 		case 8:
1993423e3ce3SKalle Valo 			att = 0x1A;
1994423e3ce3SKalle Valo 			break;
1995423e3ce3SKalle Valo 		case 9:
1996423e3ce3SKalle Valo 		default:
1997423e3ce3SKalle Valo 			att = 5;
1998423e3ce3SKalle Valo 		}
1999423e3ce3SKalle Valo 	}
2000423e3ce3SKalle Valo 	if (is_bcm_board_vendor(dev) &&
2001423e3ce3SKalle Valo 	    dev->dev->bus->boardinfo.type == 0x421) {
2002423e3ce3SKalle Valo 		if (dev->dev->bus->sprom.board_rev < 0x43)
2003423e3ce3SKalle Valo 			att = 2;
2004423e3ce3SKalle Valo 		else if (dev->dev->bus->sprom.board_rev < 0x51)
2005423e3ce3SKalle Valo 			att = 3;
2006423e3ce3SKalle Valo 	}
2007423e3ce3SKalle Valo 	if (att == 0xFFFF)
2008423e3ce3SKalle Valo 		att = 5;
2009423e3ce3SKalle Valo 
2010423e3ce3SKalle Valo 	return att;
2011423e3ce3SKalle Valo }
2012423e3ce3SKalle Valo 
b43legacy_default_txctl1(struct b43legacy_wldev * dev)2013423e3ce3SKalle Valo u16 b43legacy_default_txctl1(struct b43legacy_wldev *dev)
2014423e3ce3SKalle Valo {
2015423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
2016423e3ce3SKalle Valo 
2017423e3ce3SKalle Valo 	if (phy->radio_ver != 0x2050)
2018423e3ce3SKalle Valo 		return 0;
2019423e3ce3SKalle Valo 	if (phy->radio_rev == 1)
2020423e3ce3SKalle Valo 		return 3;
2021423e3ce3SKalle Valo 	if (phy->radio_rev < 6)
2022423e3ce3SKalle Valo 		return 2;
2023423e3ce3SKalle Valo 	if (phy->radio_rev == 8)
2024423e3ce3SKalle Valo 		return 1;
2025423e3ce3SKalle Valo 	return 0;
2026423e3ce3SKalle Valo }
2027423e3ce3SKalle Valo 
b43legacy_radio_turn_on(struct b43legacy_wldev * dev)2028423e3ce3SKalle Valo void b43legacy_radio_turn_on(struct b43legacy_wldev *dev)
2029423e3ce3SKalle Valo {
2030423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
2031423e3ce3SKalle Valo 	int err;
2032423e3ce3SKalle Valo 	u8 channel;
2033423e3ce3SKalle Valo 
2034423e3ce3SKalle Valo 	might_sleep();
2035423e3ce3SKalle Valo 
2036423e3ce3SKalle Valo 	if (phy->radio_on)
2037423e3ce3SKalle Valo 		return;
2038423e3ce3SKalle Valo 
2039423e3ce3SKalle Valo 	switch (phy->type) {
2040423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B:
2041423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
2042423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0x8000);
2043423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xCC00);
2044423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015,
2045423e3ce3SKalle Valo 				    (phy->gmode ? 0x00C0 : 0x0000));
2046423e3ce3SKalle Valo 		if (phy->radio_off_context.valid) {
2047423e3ce3SKalle Valo 			/* Restore the RFover values. */
2048423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_RFOVER,
2049423e3ce3SKalle Valo 					    phy->radio_off_context.rfover);
2050423e3ce3SKalle Valo 			b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL,
2051423e3ce3SKalle Valo 					    phy->radio_off_context.rfoverval);
2052423e3ce3SKalle Valo 			phy->radio_off_context.valid = false;
2053423e3ce3SKalle Valo 		}
2054423e3ce3SKalle Valo 		channel = phy->channel;
2055423e3ce3SKalle Valo 		err = b43legacy_radio_selectchannel(dev,
2056423e3ce3SKalle Valo 					B43legacy_RADIO_DEFAULT_CHANNEL_BG, 1);
2057423e3ce3SKalle Valo 		err |= b43legacy_radio_selectchannel(dev, channel, 0);
2058423e3ce3SKalle Valo 		B43legacy_WARN_ON(err);
2059423e3ce3SKalle Valo 		break;
2060423e3ce3SKalle Valo 	default:
2061423e3ce3SKalle Valo 		B43legacy_BUG_ON(1);
2062423e3ce3SKalle Valo 	}
2063423e3ce3SKalle Valo 	phy->radio_on = true;
2064423e3ce3SKalle Valo }
2065423e3ce3SKalle Valo 
b43legacy_radio_turn_off(struct b43legacy_wldev * dev,bool force)2066423e3ce3SKalle Valo void b43legacy_radio_turn_off(struct b43legacy_wldev *dev, bool force)
2067423e3ce3SKalle Valo {
2068423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
2069423e3ce3SKalle Valo 
2070423e3ce3SKalle Valo 	if (!phy->radio_on && !force)
2071423e3ce3SKalle Valo 		return;
2072423e3ce3SKalle Valo 
2073423e3ce3SKalle Valo 	if (phy->type == B43legacy_PHYTYPE_G && dev->dev->id.revision >= 5) {
2074423e3ce3SKalle Valo 		u16 rfover, rfoverval;
2075423e3ce3SKalle Valo 
2076423e3ce3SKalle Valo 		rfover = b43legacy_phy_read(dev, B43legacy_PHY_RFOVER);
2077423e3ce3SKalle Valo 		rfoverval = b43legacy_phy_read(dev, B43legacy_PHY_RFOVERVAL);
2078423e3ce3SKalle Valo 		if (!force) {
2079423e3ce3SKalle Valo 			phy->radio_off_context.rfover = rfover;
2080423e3ce3SKalle Valo 			phy->radio_off_context.rfoverval = rfoverval;
2081423e3ce3SKalle Valo 			phy->radio_off_context.valid = true;
2082423e3ce3SKalle Valo 		}
2083423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_RFOVER, rfover | 0x008C);
2084423e3ce3SKalle Valo 		b43legacy_phy_write(dev, B43legacy_PHY_RFOVERVAL,
2085423e3ce3SKalle Valo 				    rfoverval & 0xFF73);
2086423e3ce3SKalle Valo 	} else
2087423e3ce3SKalle Valo 		b43legacy_phy_write(dev, 0x0015, 0xAA00);
2088423e3ce3SKalle Valo 	phy->radio_on = false;
2089423e3ce3SKalle Valo 	b43legacydbg(dev->wl, "Radio initialized\n");
2090423e3ce3SKalle Valo }
2091423e3ce3SKalle Valo 
b43legacy_radio_clear_tssi(struct b43legacy_wldev * dev)2092423e3ce3SKalle Valo void b43legacy_radio_clear_tssi(struct b43legacy_wldev *dev)
2093423e3ce3SKalle Valo {
2094423e3ce3SKalle Valo 	struct b43legacy_phy *phy = &dev->phy;
2095423e3ce3SKalle Valo 
2096423e3ce3SKalle Valo 	switch (phy->type) {
2097423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_B:
2098423e3ce3SKalle Valo 	case B43legacy_PHYTYPE_G:
2099423e3ce3SKalle Valo 		b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0058,
2100423e3ce3SKalle Valo 				      0x7F7F);
2101423e3ce3SKalle Valo 		b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x005a,
2102423e3ce3SKalle Valo 				      0x7F7F);
2103423e3ce3SKalle Valo 		b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0070,
2104423e3ce3SKalle Valo 				      0x7F7F);
2105423e3ce3SKalle Valo 		b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, 0x0072,
2106423e3ce3SKalle Valo 				      0x7F7F);
2107423e3ce3SKalle Valo 		break;
2108423e3ce3SKalle Valo 	}
2109423e3ce3SKalle Valo }
2110