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