1/* 2 3 Broadcom BCM43xx wireless driver 4 5 Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>, 6 Stefano Brivio <st3@riseup.net> 7 Michael Buesch <mbuesch@freenet.de> 8 Danny van Dyk <kugelfang@gentoo.org> 9 Andreas Jaggi <andreas.jaggi@waterwave.ch> 10 11 Some parts of the code in this file are derived from the ipw2200 12 driver Copyright(c) 2003 - 2004 Intel Corporation. 13 14 This program is free software; you can redistribute it and/or modify 15 it under the terms of the GNU General Public License as published by 16 the Free Software Foundation; either version 2 of the License, or 17 (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, 20 but WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 GNU General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; see the file COPYING. If not, write to 26 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, 27 Boston, MA 02110-1301, USA. 28 29*/ 30 31#include <linux/delay.h> 32 33#include "bcm43xx.h" 34#include "bcm43xx_power.h" 35#include "bcm43xx_main.h" 36 37 38/* Get the Slow Clock Source */ 39static int bcm43xx_pctl_get_slowclksrc(struct bcm43xx_private *bcm) 40{ 41 u32 tmp; 42 int err; 43 44 assert(bcm->current_core == &bcm->core_chipcommon); 45 if (bcm->current_core->rev < 6) { 46 if (bcm->bustype == BCM43xx_BUSTYPE_PCMCIA || 47 bcm->bustype == BCM43xx_BUSTYPE_SB) 48 return BCM43xx_PCTL_CLKSRC_XTALOS; 49 if (bcm->bustype == BCM43xx_BUSTYPE_PCI) { 50 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp); 51 assert(!err); 52 if (tmp & 0x10) 53 return BCM43xx_PCTL_CLKSRC_PCI; 54 return BCM43xx_PCTL_CLKSRC_XTALOS; 55 } 56 } 57 if (bcm->current_core->rev < 10) { 58 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); 59 tmp &= 0x7; 60 if (tmp == 0) 61 return BCM43xx_PCTL_CLKSRC_LOPWROS; 62 if (tmp == 1) 63 return BCM43xx_PCTL_CLKSRC_XTALOS; 64 if (tmp == 2) 65 return BCM43xx_PCTL_CLKSRC_PCI; 66 } 67 68 return BCM43xx_PCTL_CLKSRC_XTALOS; 69} 70 71/* Get max/min slowclock frequency 72 * as described in http://bcm-specs.sipsolutions.net/PowerControl 73 */ 74static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm, 75 int get_max) 76{ 77 int limit; 78 int clocksrc; 79 int divisor; 80 u32 tmp; 81 82 assert(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL); 83 assert(bcm->current_core == &bcm->core_chipcommon); 84 85 clocksrc = bcm43xx_pctl_get_slowclksrc(bcm); 86 if (bcm->current_core->rev < 6) { 87 switch (clocksrc) { 88 case BCM43xx_PCTL_CLKSRC_PCI: 89 divisor = 64; 90 break; 91 case BCM43xx_PCTL_CLKSRC_XTALOS: 92 divisor = 32; 93 break; 94 default: 95 assert(0); 96 divisor = 1; 97 } 98 } else if (bcm->current_core->rev < 10) { 99 switch (clocksrc) { 100 case BCM43xx_PCTL_CLKSRC_LOPWROS: 101 divisor = 1; 102 break; 103 case BCM43xx_PCTL_CLKSRC_XTALOS: 104 case BCM43xx_PCTL_CLKSRC_PCI: 105 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); 106 divisor = ((tmp & 0xFFFF0000) >> 16) + 1; 107 divisor *= 4; 108 break; 109 default: 110 assert(0); 111 divisor = 1; 112 } 113 } else { 114 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL); 115 divisor = ((tmp & 0xFFFF0000) >> 16) + 1; 116 divisor *= 4; 117 } 118 119 switch (clocksrc) { 120 case BCM43xx_PCTL_CLKSRC_LOPWROS: 121 if (get_max) 122 limit = 43000; 123 else 124 limit = 25000; 125 break; 126 case BCM43xx_PCTL_CLKSRC_XTALOS: 127 if (get_max) 128 limit = 20200000; 129 else 130 limit = 19800000; 131 break; 132 case BCM43xx_PCTL_CLKSRC_PCI: 133 if (get_max) 134 limit = 34000000; 135 else 136 limit = 25000000; 137 break; 138 default: 139 assert(0); 140 limit = 0; 141 } 142 limit /= divisor; 143 144 return limit; 145} 146 147 148/* init power control 149 * as described in http://bcm-specs.sipsolutions.net/PowerControl 150 */ 151int bcm43xx_pctl_init(struct bcm43xx_private *bcm) 152{ 153 int err, maxfreq; 154 struct bcm43xx_coreinfo *old_core; 155 156 old_core = bcm->current_core; 157 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); 158 if (err == -ENODEV) 159 return 0; 160 if (err) 161 goto out; 162 163 if (bcm->chip_id == 0x4321) { 164 if (bcm->chip_rev == 0) 165 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x03A4); 166 if (bcm->chip_rev == 1) 167 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x00A4); 168 } 169 170 if (bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) { 171 if (bcm->current_core->rev >= 10) { 172 /* Set Idle Power clock rate to 1Mhz */ 173 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL, 174 (bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL) 175 & 0x0000FFFF) | 0x40000); 176 } else { 177 maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1); 178 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY, 179 (maxfreq * 150 + 999999) / 1000000); 180 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY, 181 (maxfreq * 15 + 999999) / 1000000); 182 } 183 } 184 185 err = bcm43xx_switch_core(bcm, old_core); 186 assert(err == 0); 187 188out: 189 return err; 190} 191 192u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm) 193{ 194 u16 delay = 0; 195 int err; 196 u32 pll_on_delay; 197 struct bcm43xx_coreinfo *old_core; 198 int minfreq; 199 200 if (bcm->bustype != BCM43xx_BUSTYPE_PCI) 201 goto out; 202 if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL)) 203 goto out; 204 old_core = bcm->current_core; 205 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); 206 if (err == -ENODEV) 207 goto out; 208 209 minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0); 210 pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY); 211 delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq; 212 213 err = bcm43xx_switch_core(bcm, old_core); 214 assert(err == 0); 215 216out: 217 return delay; 218} 219 220/* set the powercontrol clock 221 * as described in http://bcm-specs.sipsolutions.net/PowerControl 222 */ 223int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode) 224{ 225 int err; 226 struct bcm43xx_coreinfo *old_core; 227 u32 tmp; 228 229 old_core = bcm->current_core; 230 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); 231 if (err == -ENODEV) 232 return 0; 233 if (err) 234 goto out; 235 236 if (bcm->core_chipcommon.rev < 6) { 237 if (mode == BCM43xx_PCTL_CLK_FAST) { 238 err = bcm43xx_pctl_set_crystal(bcm, 1); 239 if (err) 240 goto out; 241 } 242 } else { 243 if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) && 244 (bcm->core_chipcommon.rev < 10)) { 245 switch (mode) { 246 case BCM43xx_PCTL_CLK_FAST: 247 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); 248 tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL; 249 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); 250 break; 251 case BCM43xx_PCTL_CLK_SLOW: 252 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); 253 tmp |= BCM43xx_PCTL_FORCE_SLOW; 254 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); 255 break; 256 case BCM43xx_PCTL_CLK_DYNAMIC: 257 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); 258 tmp &= ~BCM43xx_PCTL_FORCE_SLOW; 259 tmp |= BCM43xx_PCTL_FORCE_PLL; 260 tmp &= ~BCM43xx_PCTL_DYN_XTAL; 261 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); 262 } 263 } 264 } 265 266 err = bcm43xx_switch_core(bcm, old_core); 267 assert(err == 0); 268 269out: 270 return err; 271} 272 273int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on) 274{ 275 int err; 276 u32 in, out, outenable; 277 278 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in); 279 if (err) 280 goto err_pci; 281 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out); 282 if (err) 283 goto err_pci; 284 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable); 285 if (err) 286 goto err_pci; 287 288 outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN); 289 290 if (on) { 291 if (in & 0x40) 292 return 0; 293 294 out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN); 295 296 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); 297 if (err) 298 goto err_pci; 299 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable); 300 if (err) 301 goto err_pci; 302 udelay(1000); 303 304 out &= ~BCM43xx_PCTL_PLL_POWERDOWN; 305 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); 306 if (err) 307 goto err_pci; 308 udelay(5000); 309 } else { 310 if (bcm->current_core->rev < 5) 311 return 0; 312 if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW) 313 return 0; 314 315 316 err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW); 317 if (err) 318 goto out; 319 out &= ~BCM43xx_PCTL_XTAL_POWERUP; 320 out |= BCM43xx_PCTL_PLL_POWERDOWN; 321 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); 322 if (err) 323 goto err_pci; 324 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable); 325 if (err) 326 goto err_pci; 327 } 328 329out: 330 return err; 331 332err_pci: 333 printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n"); 334 err = -EBUSY; 335 goto out; 336} 337 338/* Set the PowerSavingControlBits. 339 * Bitvalues: 340 * 0 => unset the bit 341 * 1 => set the bit 342 * -1 => calculate the bit 343 */ 344void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm, 345 int bit25, int bit26) 346{ 347 int i; 348 u32 status; 349 350bit25 = 0; 351bit26 = 1; 352 353 if (bit25 == -1) { 354 // and thus is not an AP and we are associated, set bit 25 355 } 356 if (bit26 == -1) { 357 // successful, set bit26 358 } 359 status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); 360 if (bit25) 361 status |= BCM43xx_SBF_PS1; 362 else 363 status &= ~BCM43xx_SBF_PS1; 364 if (bit26) 365 status |= BCM43xx_SBF_PS2; 366 else 367 status &= ~BCM43xx_SBF_PS2; 368 bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); 369 if (bit26 && bcm->current_core->rev >= 5) { 370 for (i = 0; i < 100; i++) { 371 if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4) 372 break; 373 udelay(10); 374 } 375 } 376} 377