diff options
Diffstat (limited to 'drivers/net/wireless/b43/phy_common.c')
-rw-r--r-- | drivers/net/wireless/b43/phy_common.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c new file mode 100644 index 000000000000..af37abccccb3 --- /dev/null +++ b/drivers/net/wireless/b43/phy_common.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | |||
3 | Broadcom B43 wireless driver | ||
4 | Common PHY routines | ||
5 | |||
6 | Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, | ||
7 | Copyright (c) 2005-2007 Stefano Brivio <stefano.brivio@polimi.it> | ||
8 | Copyright (c) 2005-2008 Michael Buesch <mb@bu3sch.de> | ||
9 | Copyright (c) 2005, 2006 Danny van Dyk <kugelfang@gentoo.org> | ||
10 | Copyright (c) 2005, 2006 Andreas Jaggi <andreas.jaggi@waterwave.ch> | ||
11 | |||
12 | This program is free software; you can redistribute it and/or modify | ||
13 | it under the terms of the GNU General Public License as published by | ||
14 | the Free Software Foundation; either version 2 of the License, or | ||
15 | (at your option) any later version. | ||
16 | |||
17 | This program is distributed in the hope that it will be useful, | ||
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | GNU General Public License for more details. | ||
21 | |||
22 | You should have received a copy of the GNU General Public License | ||
23 | along with this program; see the file COPYING. If not, write to | ||
24 | the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, | ||
25 | Boston, MA 02110-1301, USA. | ||
26 | |||
27 | */ | ||
28 | |||
29 | #include "phy_common.h" | ||
30 | #include "phy_g.h" | ||
31 | #include "phy_a.h" | ||
32 | #include "phy_n.h" | ||
33 | #include "phy_lp.h" | ||
34 | #include "b43.h" | ||
35 | #include "main.h" | ||
36 | |||
37 | |||
38 | int b43_phy_allocate(struct b43_wldev *dev) | ||
39 | { | ||
40 | struct b43_phy *phy = &(dev->phy); | ||
41 | int err; | ||
42 | |||
43 | phy->ops = NULL; | ||
44 | |||
45 | switch (phy->type) { | ||
46 | case B43_PHYTYPE_A: | ||
47 | phy->ops = &b43_phyops_a; | ||
48 | break; | ||
49 | case B43_PHYTYPE_G: | ||
50 | phy->ops = &b43_phyops_g; | ||
51 | break; | ||
52 | case B43_PHYTYPE_N: | ||
53 | #ifdef CONFIG_B43_NPHY | ||
54 | phy->ops = &b43_phyops_n; | ||
55 | #endif | ||
56 | break; | ||
57 | case B43_PHYTYPE_LP: | ||
58 | #ifdef CONFIG_B43_PHY_LP | ||
59 | phy->ops = &b43_phyops_lp; | ||
60 | #endif | ||
61 | break; | ||
62 | } | ||
63 | if (B43_WARN_ON(!phy->ops)) | ||
64 | return -ENODEV; | ||
65 | |||
66 | err = phy->ops->allocate(dev); | ||
67 | if (err) | ||
68 | phy->ops = NULL; | ||
69 | |||
70 | return err; | ||
71 | } | ||
72 | |||
73 | void b43_phy_free(struct b43_wldev *dev) | ||
74 | { | ||
75 | dev->phy.ops->free(dev); | ||
76 | dev->phy.ops = NULL; | ||
77 | } | ||
78 | |||
79 | int b43_phy_init(struct b43_wldev *dev) | ||
80 | { | ||
81 | struct b43_phy *phy = &dev->phy; | ||
82 | const struct b43_phy_operations *ops = phy->ops; | ||
83 | int err; | ||
84 | |||
85 | phy->channel = ops->get_default_chan(dev); | ||
86 | |||
87 | ops->software_rfkill(dev, RFKILL_STATE_UNBLOCKED); | ||
88 | err = ops->init(dev); | ||
89 | if (err) { | ||
90 | b43err(dev->wl, "PHY init failed\n"); | ||
91 | goto err_block_rf; | ||
92 | } | ||
93 | /* Make sure to switch hardware and firmware (SHM) to | ||
94 | * the default channel. */ | ||
95 | err = b43_switch_channel(dev, ops->get_default_chan(dev)); | ||
96 | if (err) { | ||
97 | b43err(dev->wl, "PHY init: Channel switch to default failed\n"); | ||
98 | goto err_phy_exit; | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | |||
103 | err_phy_exit: | ||
104 | if (ops->exit) | ||
105 | ops->exit(dev); | ||
106 | err_block_rf: | ||
107 | ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED); | ||
108 | |||
109 | return err; | ||
110 | } | ||
111 | |||
112 | void b43_phy_exit(struct b43_wldev *dev) | ||
113 | { | ||
114 | const struct b43_phy_operations *ops = dev->phy.ops; | ||
115 | |||
116 | ops->software_rfkill(dev, RFKILL_STATE_SOFT_BLOCKED); | ||
117 | if (ops->exit) | ||
118 | ops->exit(dev); | ||
119 | } | ||
120 | |||
121 | bool b43_has_hardware_pctl(struct b43_wldev *dev) | ||
122 | { | ||
123 | if (!dev->phy.hardware_power_control) | ||
124 | return 0; | ||
125 | if (!dev->phy.ops->supports_hwpctl) | ||
126 | return 0; | ||
127 | return dev->phy.ops->supports_hwpctl(dev); | ||
128 | } | ||
129 | |||
130 | void b43_radio_lock(struct b43_wldev *dev) | ||
131 | { | ||
132 | u32 macctl; | ||
133 | |||
134 | macctl = b43_read32(dev, B43_MMIO_MACCTL); | ||
135 | B43_WARN_ON(macctl & B43_MACCTL_RADIOLOCK); | ||
136 | macctl |= B43_MACCTL_RADIOLOCK; | ||
137 | b43_write32(dev, B43_MMIO_MACCTL, macctl); | ||
138 | /* Commit the write and wait for the device | ||
139 | * to exit any radio register access. */ | ||
140 | b43_read32(dev, B43_MMIO_MACCTL); | ||
141 | udelay(10); | ||
142 | } | ||
143 | |||
144 | void b43_radio_unlock(struct b43_wldev *dev) | ||
145 | { | ||
146 | u32 macctl; | ||
147 | |||
148 | /* Commit any write */ | ||
149 | b43_read16(dev, B43_MMIO_PHY_VER); | ||
150 | /* unlock */ | ||
151 | macctl = b43_read32(dev, B43_MMIO_MACCTL); | ||
152 | B43_WARN_ON(!(macctl & B43_MACCTL_RADIOLOCK)); | ||
153 | macctl &= ~B43_MACCTL_RADIOLOCK; | ||
154 | b43_write32(dev, B43_MMIO_MACCTL, macctl); | ||
155 | } | ||
156 | |||
157 | void b43_phy_lock(struct b43_wldev *dev) | ||
158 | { | ||
159 | #if B43_DEBUG | ||
160 | B43_WARN_ON(dev->phy.phy_locked); | ||
161 | dev->phy.phy_locked = 1; | ||
162 | #endif | ||
163 | B43_WARN_ON(dev->dev->id.revision < 3); | ||
164 | |||
165 | if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) | ||
166 | b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); | ||
167 | } | ||
168 | |||
169 | void b43_phy_unlock(struct b43_wldev *dev) | ||
170 | { | ||
171 | #if B43_DEBUG | ||
172 | B43_WARN_ON(!dev->phy.phy_locked); | ||
173 | dev->phy.phy_locked = 0; | ||
174 | #endif | ||
175 | B43_WARN_ON(dev->dev->id.revision < 3); | ||
176 | |||
177 | if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) | ||
178 | b43_power_saving_ctl_bits(dev, 0); | ||
179 | } | ||
180 | |||
181 | u16 b43_radio_read(struct b43_wldev *dev, u16 reg) | ||
182 | { | ||
183 | return dev->phy.ops->radio_read(dev, reg); | ||
184 | } | ||
185 | |||
186 | void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) | ||
187 | { | ||
188 | dev->phy.ops->radio_write(dev, reg, value); | ||
189 | } | ||
190 | |||
191 | void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask) | ||
192 | { | ||
193 | b43_radio_write16(dev, offset, | ||
194 | b43_radio_read16(dev, offset) & mask); | ||
195 | } | ||
196 | |||
197 | void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set) | ||
198 | { | ||
199 | b43_radio_write16(dev, offset, | ||
200 | b43_radio_read16(dev, offset) | set); | ||
201 | } | ||
202 | |||
203 | void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) | ||
204 | { | ||
205 | b43_radio_write16(dev, offset, | ||
206 | (b43_radio_read16(dev, offset) & mask) | set); | ||
207 | } | ||
208 | |||
209 | u16 b43_phy_read(struct b43_wldev *dev, u16 reg) | ||
210 | { | ||
211 | return dev->phy.ops->phy_read(dev, reg); | ||
212 | } | ||
213 | |||
214 | void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) | ||
215 | { | ||
216 | dev->phy.ops->phy_write(dev, reg, value); | ||
217 | } | ||
218 | |||
219 | void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) | ||
220 | { | ||
221 | b43_phy_write(dev, offset, | ||
222 | b43_phy_read(dev, offset) & mask); | ||
223 | } | ||
224 | |||
225 | void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) | ||
226 | { | ||
227 | b43_phy_write(dev, offset, | ||
228 | b43_phy_read(dev, offset) | set); | ||
229 | } | ||
230 | |||
231 | void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) | ||
232 | { | ||
233 | b43_phy_write(dev, offset, | ||
234 | (b43_phy_read(dev, offset) & mask) | set); | ||
235 | } | ||
236 | |||
237 | int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) | ||
238 | { | ||
239 | struct b43_phy *phy = &(dev->phy); | ||
240 | u16 channelcookie, savedcookie; | ||
241 | int err; | ||
242 | |||
243 | if (new_channel == B43_DEFAULT_CHANNEL) | ||
244 | new_channel = phy->ops->get_default_chan(dev); | ||
245 | |||
246 | /* First we set the channel radio code to prevent the | ||
247 | * firmware from sending ghost packets. | ||
248 | */ | ||
249 | channelcookie = new_channel; | ||
250 | if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) | ||
251 | channelcookie |= 0x100; | ||
252 | //FIXME set 40Mhz flag if required | ||
253 | savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN); | ||
254 | b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie); | ||
255 | |||
256 | /* Now try to switch the PHY hardware channel. */ | ||
257 | err = phy->ops->switch_channel(dev, new_channel); | ||
258 | if (err) | ||
259 | goto err_restore_cookie; | ||
260 | |||
261 | dev->phy.channel = new_channel; | ||
262 | /* Wait for the radio to tune to the channel and stabilize. */ | ||
263 | msleep(8); | ||
264 | |||
265 | return 0; | ||
266 | |||
267 | err_restore_cookie: | ||
268 | b43_shm_write16(dev, B43_SHM_SHARED, | ||
269 | B43_SHM_SH_CHAN, savedcookie); | ||
270 | |||
271 | return err; | ||
272 | } | ||
273 | |||
274 | void b43_software_rfkill(struct b43_wldev *dev, enum rfkill_state state) | ||
275 | { | ||
276 | struct b43_phy *phy = &dev->phy; | ||
277 | |||
278 | if (state == RFKILL_STATE_HARD_BLOCKED) { | ||
279 | /* We cannot hardware-block the device */ | ||
280 | state = RFKILL_STATE_SOFT_BLOCKED; | ||
281 | } | ||
282 | |||
283 | phy->ops->software_rfkill(dev, state); | ||
284 | phy->radio_on = (state == RFKILL_STATE_UNBLOCKED); | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * b43_phy_txpower_adjust_work - TX power workqueue. | ||
289 | * | ||
290 | * Workqueue for updating the TX power parameters in hardware. | ||
291 | */ | ||
292 | void b43_phy_txpower_adjust_work(struct work_struct *work) | ||
293 | { | ||
294 | struct b43_wl *wl = container_of(work, struct b43_wl, | ||
295 | txpower_adjust_work); | ||
296 | struct b43_wldev *dev; | ||
297 | |||
298 | mutex_lock(&wl->mutex); | ||
299 | dev = wl->current_dev; | ||
300 | |||
301 | if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED))) | ||
302 | dev->phy.ops->adjust_txpower(dev); | ||
303 | |||
304 | mutex_unlock(&wl->mutex); | ||
305 | } | ||
306 | |||
307 | /* Called with wl->irq_lock locked */ | ||
308 | void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) | ||
309 | { | ||
310 | struct b43_phy *phy = &dev->phy; | ||
311 | unsigned long now = jiffies; | ||
312 | enum b43_txpwr_result result; | ||
313 | |||
314 | if (!(flags & B43_TXPWR_IGNORE_TIME)) { | ||
315 | /* Check if it's time for a TXpower check. */ | ||
316 | if (time_before(now, phy->next_txpwr_check_time)) | ||
317 | return; /* Not yet */ | ||
318 | } | ||
319 | /* The next check will be needed in two seconds, or later. */ | ||
320 | phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2)); | ||
321 | |||
322 | if ((dev->dev->bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) && | ||
323 | (dev->dev->bus->boardinfo.type == SSB_BOARD_BU4306)) | ||
324 | return; /* No software txpower adjustment needed */ | ||
325 | |||
326 | result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI)); | ||
327 | if (result == B43_TXPWR_RES_DONE) | ||
328 | return; /* We are done. */ | ||
329 | B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST); | ||
330 | B43_WARN_ON(phy->ops->adjust_txpower == NULL); | ||
331 | |||
332 | /* We must adjust the transmission power in hardware. | ||
333 | * Schedule b43_phy_txpower_adjust_work(). */ | ||
334 | queue_work(dev->wl->hw->workqueue, &dev->wl->txpower_adjust_work); | ||
335 | } | ||
336 | |||
337 | int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) | ||
338 | { | ||
339 | const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK); | ||
340 | unsigned int a, b, c, d; | ||
341 | unsigned int average; | ||
342 | u32 tmp; | ||
343 | |||
344 | tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset); | ||
345 | a = tmp & 0xFF; | ||
346 | b = (tmp >> 8) & 0xFF; | ||
347 | c = (tmp >> 16) & 0xFF; | ||
348 | d = (tmp >> 24) & 0xFF; | ||
349 | if (a == 0 || a == B43_TSSI_MAX || | ||
350 | b == 0 || b == B43_TSSI_MAX || | ||
351 | c == 0 || c == B43_TSSI_MAX || | ||
352 | d == 0 || d == B43_TSSI_MAX) | ||
353 | return -ENOENT; | ||
354 | /* The values are OK. Clear them. */ | ||
355 | tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) | | ||
356 | (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24); | ||
357 | b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp); | ||
358 | |||
359 | if (is_ofdm) { | ||
360 | a = (a + 32) & 0x3F; | ||
361 | b = (b + 32) & 0x3F; | ||
362 | c = (c + 32) & 0x3F; | ||
363 | d = (d + 32) & 0x3F; | ||
364 | } | ||
365 | |||
366 | /* Get the average of the values with 0.5 added to each value. */ | ||
367 | average = (a + b + c + d + 2) / 4; | ||
368 | if (is_ofdm) { | ||
369 | /* Adjust for CCK-boost */ | ||
370 | if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO) | ||
371 | & B43_HF_CCKBOOST) | ||
372 | average = (average >= 13) ? (average - 13) : 0; | ||
373 | } | ||
374 | |||
375 | return average; | ||
376 | } | ||
377 | |||
378 | void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) | ||
379 | { | ||
380 | b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); | ||
381 | } | ||