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 38int 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 73void b43_phy_free(struct b43_wldev *dev) 74{ 75 dev->phy.ops->free(dev); 76 dev->phy.ops = NULL; 77} 78 79int 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, false); 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 103err_phy_exit: 104 if (ops->exit) 105 ops->exit(dev); 106err_block_rf: 107 ops->software_rfkill(dev, true); 108 109 return err; 110} 111 112void b43_phy_exit(struct b43_wldev *dev) 113{ 114 const struct b43_phy_operations *ops = dev->phy.ops; 115 116 ops->software_rfkill(dev, true); 117 if (ops->exit) 118 ops->exit(dev); 119} 120 121bool 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 130void b43_radio_lock(struct b43_wldev *dev) 131{ 132 u32 macctl; 133 134#if B43_DEBUG 135 B43_WARN_ON(dev->phy.radio_locked); 136 dev->phy.radio_locked = 1; 137#endif 138 139 macctl = b43_read32(dev, B43_MMIO_MACCTL); 140 macctl |= B43_MACCTL_RADIOLOCK; 141 b43_write32(dev, B43_MMIO_MACCTL, macctl); 142 /* Commit the write and wait for the firmware 143 * to finish any radio register access. */ 144 b43_read32(dev, B43_MMIO_MACCTL); 145 udelay(10); 146} 147 148void b43_radio_unlock(struct b43_wldev *dev) 149{ 150 u32 macctl; 151 152#if B43_DEBUG 153 B43_WARN_ON(!dev->phy.radio_locked); 154 dev->phy.radio_locked = 0; 155#endif 156 157 /* Commit any write */ 158 b43_read16(dev, B43_MMIO_PHY_VER); 159 /* unlock */ 160 macctl = b43_read32(dev, B43_MMIO_MACCTL); 161 macctl &= ~B43_MACCTL_RADIOLOCK; 162 b43_write32(dev, B43_MMIO_MACCTL, macctl); 163} 164 165void b43_phy_lock(struct b43_wldev *dev) 166{ 167#if B43_DEBUG 168 B43_WARN_ON(dev->phy.phy_locked); 169 dev->phy.phy_locked = 1; 170#endif 171 B43_WARN_ON(dev->dev->id.revision < 3); 172 173 if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) 174 b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); 175} 176 177void b43_phy_unlock(struct b43_wldev *dev) 178{ 179#if B43_DEBUG 180 B43_WARN_ON(!dev->phy.phy_locked); 181 dev->phy.phy_locked = 0; 182#endif 183 B43_WARN_ON(dev->dev->id.revision < 3); 184 185 if (!b43_is_mode(dev->wl, NL80211_IFTYPE_AP)) 186 b43_power_saving_ctl_bits(dev, 0); 187} 188 189static inline void assert_mac_suspended(struct b43_wldev *dev) 190{ 191 if (!B43_DEBUG) 192 return; 193 if ((b43_status(dev) >= B43_STAT_INITIALIZED) && 194 (dev->mac_suspended <= 0)) { 195 b43dbg(dev->wl, "PHY/RADIO register access with " 196 "enabled MAC.\n"); 197 dump_stack(); 198 } 199} 200 201u16 b43_radio_read(struct b43_wldev *dev, u16 reg) 202{ 203 assert_mac_suspended(dev); 204 return dev->phy.ops->radio_read(dev, reg); 205} 206 207void b43_radio_write(struct b43_wldev *dev, u16 reg, u16 value) 208{ 209 assert_mac_suspended(dev); 210 dev->phy.ops->radio_write(dev, reg, value); 211} 212 213void b43_radio_mask(struct b43_wldev *dev, u16 offset, u16 mask) 214{ 215 b43_radio_write16(dev, offset, 216 b43_radio_read16(dev, offset) & mask); 217} 218 219void b43_radio_set(struct b43_wldev *dev, u16 offset, u16 set) 220{ 221 b43_radio_write16(dev, offset, 222 b43_radio_read16(dev, offset) | set); 223} 224 225void b43_radio_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) 226{ 227 b43_radio_write16(dev, offset, 228 (b43_radio_read16(dev, offset) & mask) | set); 229} 230 231u16 b43_phy_read(struct b43_wldev *dev, u16 reg) 232{ 233 assert_mac_suspended(dev); 234 return dev->phy.ops->phy_read(dev, reg); 235} 236 237void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) 238{ 239 assert_mac_suspended(dev); 240 dev->phy.ops->phy_write(dev, reg, value); 241} 242 243void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) 244{ 245 assert_mac_suspended(dev); 246 dev->phy.ops->phy_write(dev, destreg, 247 dev->phy.ops->phy_read(dev, srcreg)); 248} 249 250void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) 251{ 252 if (dev->phy.ops->phy_maskset) { 253 assert_mac_suspended(dev); 254 dev->phy.ops->phy_maskset(dev, offset, mask, 0); 255 } else { 256 b43_phy_write(dev, offset, 257 b43_phy_read(dev, offset) & mask); 258 } 259} 260 261void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) 262{ 263 if (dev->phy.ops->phy_maskset) { 264 assert_mac_suspended(dev); 265 dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set); 266 } else { 267 b43_phy_write(dev, offset, 268 b43_phy_read(dev, offset) | set); 269 } 270} 271 272void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) 273{ 274 if (dev->phy.ops->phy_maskset) { 275 assert_mac_suspended(dev); 276 dev->phy.ops->phy_maskset(dev, offset, mask, set); 277 } else { 278 b43_phy_write(dev, offset, 279 (b43_phy_read(dev, offset) & mask) | set); 280 } 281} 282 283int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) 284{ 285 struct b43_phy *phy = &(dev->phy); 286 u16 channelcookie, savedcookie; 287 int err; 288 289 if (new_channel == B43_DEFAULT_CHANNEL) 290 new_channel = phy->ops->get_default_chan(dev); 291 292 /* First we set the channel radio code to prevent the 293 * firmware from sending ghost packets. 294 */ 295 channelcookie = new_channel; 296 if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) 297 channelcookie |= 0x100; 298 savedcookie = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN); 299 b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_CHAN, channelcookie); 300 301 /* Now try to switch the PHY hardware channel. */ 302 err = phy->ops->switch_channel(dev, new_channel); 303 if (err) 304 goto err_restore_cookie; 305 306 dev->phy.channel = new_channel; 307 /* Wait for the radio to tune to the channel and stabilize. */ 308 msleep(8); 309 310 return 0; 311 312err_restore_cookie: 313 b43_shm_write16(dev, B43_SHM_SHARED, 314 B43_SHM_SH_CHAN, savedcookie); 315 316 return err; 317} 318 319void b43_software_rfkill(struct b43_wldev *dev, bool blocked) 320{ 321 struct b43_phy *phy = &dev->phy; 322 323 b43_mac_suspend(dev); 324 phy->ops->software_rfkill(dev, blocked); 325 phy->radio_on = !blocked; 326 b43_mac_enable(dev); 327} 328 329/** 330 * b43_phy_txpower_adjust_work - TX power workqueue. 331 * 332 * Workqueue for updating the TX power parameters in hardware. 333 */ 334void b43_phy_txpower_adjust_work(struct work_struct *work) 335{ 336 struct b43_wl *wl = container_of(work, struct b43_wl, 337 txpower_adjust_work); 338 struct b43_wldev *dev; 339 340 mutex_lock(&wl->mutex); 341 dev = wl->current_dev; 342 343 if (likely(dev && (b43_status(dev) >= B43_STAT_STARTED))) 344 dev->phy.ops->adjust_txpower(dev); 345 346 mutex_unlock(&wl->mutex); 347} 348 349void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) 350{ 351 struct b43_phy *phy = &dev->phy; 352 unsigned long now = jiffies; 353 enum b43_txpwr_result result; 354 355 if (!(flags & B43_TXPWR_IGNORE_TIME)) { 356 /* Check if it's time for a TXpower check. */ 357 if (time_before(now, phy->next_txpwr_check_time)) 358 return; /* Not yet */ 359 } 360 /* The next check will be needed in two seconds, or later. */ 361 phy->next_txpwr_check_time = round_jiffies(now + (HZ * 2)); 362 363 if ((dev->dev->bus->boardinfo.vendor == SSB_BOARDVENDOR_BCM) && 364 (dev->dev->bus->boardinfo.type == SSB_BOARD_BU4306)) 365 return; /* No software txpower adjustment needed */ 366 367 result = phy->ops->recalc_txpower(dev, !!(flags & B43_TXPWR_IGNORE_TSSI)); 368 if (result == B43_TXPWR_RES_DONE) 369 return; /* We are done. */ 370 B43_WARN_ON(result != B43_TXPWR_RES_NEED_ADJUST); 371 B43_WARN_ON(phy->ops->adjust_txpower == NULL); 372 373 /* We must adjust the transmission power in hardware. 374 * Schedule b43_phy_txpower_adjust_work(). */ 375 ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); 376} 377 378int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) 379{ 380 const bool is_ofdm = (shm_offset != B43_SHM_SH_TSSI_CCK); 381 unsigned int a, b, c, d; 382 unsigned int average; 383 u32 tmp; 384 385 tmp = b43_shm_read32(dev, B43_SHM_SHARED, shm_offset); 386 a = tmp & 0xFF; 387 b = (tmp >> 8) & 0xFF; 388 c = (tmp >> 16) & 0xFF; 389 d = (tmp >> 24) & 0xFF; 390 if (a == 0 || a == B43_TSSI_MAX || 391 b == 0 || b == B43_TSSI_MAX || 392 c == 0 || c == B43_TSSI_MAX || 393 d == 0 || d == B43_TSSI_MAX) 394 return -ENOENT; 395 /* The values are OK. Clear them. */ 396 tmp = B43_TSSI_MAX | (B43_TSSI_MAX << 8) | 397 (B43_TSSI_MAX << 16) | (B43_TSSI_MAX << 24); 398 b43_shm_write32(dev, B43_SHM_SHARED, shm_offset, tmp); 399 400 if (is_ofdm) { 401 a = (a + 32) & 0x3F; 402 b = (b + 32) & 0x3F; 403 c = (c + 32) & 0x3F; 404 d = (d + 32) & 0x3F; 405 } 406 407 /* Get the average of the values with 0.5 added to each value. */ 408 average = (a + b + c + d + 2) / 4; 409 if (is_ofdm) { 410 /* Adjust for CCK-boost */ 411 if (b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_HOSTFLO) 412 & B43_HF_CCKBOOST) 413 average = (average >= 13) ? (average - 13) : 0; 414 } 415 416 return average; 417} 418 419void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on) 420{ 421 b43_write16(dev, B43_MMIO_PHY0, on ? 0 : 0xF4); 422} 423 424/* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */ 425struct b43_c32 b43_cordic(int theta) 426{ 427 u32 arctg[] = { 2949120, 1740967, 919879, 466945, 234379, 117304, 428 58666, 29335, 14668, 7334, 3667, 1833, 917, 458, 429 229, 115, 57, 29, }; 430 u8 i; 431 s32 tmp; 432 s8 signx = 1; 433 u32 angle = 0; 434 struct b43_c32 ret = { .i = 39797, .q = 0, }; 435 436 while (theta > (180 << 16)) 437 theta -= (360 << 16); 438 while (theta < -(180 << 16)) 439 theta += (360 << 16); 440 441 if (theta > (90 << 16)) { 442 theta -= (180 << 16); 443 signx = -1; 444 } else if (theta < -(90 << 16)) { 445 theta += (180 << 16); 446 signx = -1; 447 } 448 449 for (i = 0; i <= 17; i++) { 450 if (theta > angle) { 451 tmp = ret.i - (ret.q >> i); 452 ret.q += ret.i >> i; 453 ret.i = tmp; 454 angle += arctg[i]; 455 } else { 456 tmp = ret.i + (ret.q >> i); 457 ret.q -= ret.i >> i; 458 ret.i = tmp; 459 angle -= arctg[i]; 460 } 461 } 462 463 ret.i *= signx; 464 ret.q *= signx; 465 466 return ret; 467} 468