blob: 6569da3a7a395987f8be1e5a6a285580343066fa [file] [log] [blame]
John W. Linvillef2223132006-01-23 16:59:58 -05001/*
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
Michael Buesch8829d552006-04-13 02:30:26 +020038/* 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
John W. Linvillef2223132006-01-23 16:59:58 -050071/* 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{
Michael Buesch8829d552006-04-13 02:30:26 +020077 int limit;
78 int clocksrc;
John W. Linvillef2223132006-01-23 16:59:58 -050079 int divisor;
John W. Linvillef2223132006-01-23 16:59:58 -050080 u32 tmp;
John W. Linvillef2223132006-01-23 16:59:58 -050081
Michael Buesch8829d552006-04-13 02:30:26 +020082 assert(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL);
83 assert(bcm->current_core == &bcm->core_chipcommon);
John W. Linvillef2223132006-01-23 16:59:58 -050084
Michael Buesch8829d552006-04-13 02:30:26 +020085 clocksrc = bcm43xx_pctl_get_slowclksrc(bcm);
John W. Linvillef2223132006-01-23 16:59:58 -050086 if (bcm->current_core->rev < 6) {
Michael Buesch8829d552006-04-13 02:30:26 +020087 switch (clocksrc) {
88 case BCM43xx_PCTL_CLKSRC_PCI:
89 divisor = 64;
90 break;
91 case BCM43xx_PCTL_CLKSRC_XTALOS:
John W. Linvillef2223132006-01-23 16:59:58 -050092 divisor = 32;
Michael Buesch8829d552006-04-13 02:30:26 +020093 break;
94 default:
95 assert(0);
96 divisor = 1;
John W. Linvillef2223132006-01-23 16:59:58 -050097 }
98 } else if (bcm->current_core->rev < 10) {
Michael Buesch8829d552006-04-13 02:30:26 +020099 switch (clocksrc) {
100 case BCM43xx_PCTL_CLKSRC_LOPWROS:
John W. Linvillef2223132006-01-23 16:59:58 -0500101 divisor = 1;
Michael Buesch8829d552006-04-13 02:30:26 +0200102 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 }
John W. Linvillef2223132006-01-23 16:59:58 -0500113 } else {
114 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL);
Michael Buesch8829d552006-04-13 02:30:26 +0200115 divisor = ((tmp & 0xFFFF0000) >> 16) + 1;
116 divisor *= 4;
John W. Linvillef2223132006-01-23 16:59:58 -0500117 }
Michael Buesch8829d552006-04-13 02:30:26 +0200118
119 switch (clocksrc) {
120 case BCM43xx_PCTL_CLKSRC_LOPWROS:
John W. Linvillef2223132006-01-23 16:59:58 -0500121 if (get_max)
122 limit = 43000;
123 else
124 limit = 25000;
125 break;
Michael Buesch8829d552006-04-13 02:30:26 +0200126 case BCM43xx_PCTL_CLKSRC_XTALOS:
John W. Linvillef2223132006-01-23 16:59:58 -0500127 if (get_max)
128 limit = 20200000;
129 else
130 limit = 19800000;
131 break;
Michael Buesch8829d552006-04-13 02:30:26 +0200132 case BCM43xx_PCTL_CLKSRC_PCI:
John W. Linvillef2223132006-01-23 16:59:58 -0500133 if (get_max)
134 limit = 34000000;
135 else
136 limit = 25000000;
137 break;
138 default:
139 assert(0);
Michael Buesch8829d552006-04-13 02:30:26 +0200140 limit = 0;
John W. Linvillef2223132006-01-23 16:59:58 -0500141 }
142 limit /= divisor;
143
John W. Linvillef2223132006-01-23 16:59:58 -0500144 return limit;
145}
146
Michael Buesch8829d552006-04-13 02:30:26 +0200147
John W. Linvillef2223132006-01-23 16:59:58 -0500148/* 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 if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
157 return 0;
158 old_core = bcm->current_core;
159 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
160 if (err == -ENODEV)
161 return 0;
162 if (err)
163 goto out;
164
165 maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1);
166 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY,
167 (maxfreq * 150 + 999999) / 1000000);
168 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY,
169 (maxfreq * 15 + 999999) / 1000000);
170
171 err = bcm43xx_switch_core(bcm, old_core);
172 assert(err == 0);
173
174out:
175 return err;
176}
177
178u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm)
179{
180 u16 delay = 0;
181 int err;
182 u32 pll_on_delay;
183 struct bcm43xx_coreinfo *old_core;
184 int minfreq;
185
186 if (bcm->bustype != BCM43xx_BUSTYPE_PCI)
187 goto out;
188 if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
189 goto out;
190 old_core = bcm->current_core;
191 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
192 if (err == -ENODEV)
193 goto out;
194
195 minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0);
196 pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY);
197 delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;
198
199 err = bcm43xx_switch_core(bcm, old_core);
200 assert(err == 0);
201
202out:
203 return delay;
204}
205
206/* set the powercontrol clock
207 * as described in http://bcm-specs.sipsolutions.net/PowerControl
208 */
209int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode)
210{
211 int err;
212 struct bcm43xx_coreinfo *old_core;
213 u32 tmp;
214
215 old_core = bcm->current_core;
216 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
217 if (err == -ENODEV)
218 return 0;
219 if (err)
220 goto out;
221
222 if (bcm->core_chipcommon.rev < 6) {
223 if (mode == BCM43xx_PCTL_CLK_FAST) {
224 err = bcm43xx_pctl_set_crystal(bcm, 1);
225 if (err)
226 goto out;
227 }
228 } else {
229 if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) &&
230 (bcm->core_chipcommon.rev < 10)) {
231 switch (mode) {
232 case BCM43xx_PCTL_CLK_FAST:
233 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
234 tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL;
235 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
236 break;
237 case BCM43xx_PCTL_CLK_SLOW:
238 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
239 tmp |= BCM43xx_PCTL_FORCE_SLOW;
240 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
241 break;
242 case BCM43xx_PCTL_CLK_DYNAMIC:
243 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
244 tmp &= ~BCM43xx_PCTL_FORCE_SLOW;
245 tmp |= BCM43xx_PCTL_FORCE_PLL;
246 tmp &= ~BCM43xx_PCTL_DYN_XTAL;
247 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
248 }
249 }
250 }
251
252 err = bcm43xx_switch_core(bcm, old_core);
253 assert(err == 0);
254
255out:
256 return err;
257}
258
259int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on)
260{
261 int err;
262 u32 in, out, outenable;
263
264 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in);
265 if (err)
266 goto err_pci;
267 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out);
268 if (err)
269 goto err_pci;
270 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable);
271 if (err)
272 goto err_pci;
273
274 outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
275
276 if (on) {
277 if (in & 0x40)
278 return 0;
279
280 out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
281
282 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
283 if (err)
284 goto err_pci;
285 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
286 if (err)
287 goto err_pci;
288 udelay(1000);
289
290 out &= ~BCM43xx_PCTL_PLL_POWERDOWN;
291 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
292 if (err)
293 goto err_pci;
294 udelay(5000);
295 } else {
296 if (bcm->current_core->rev < 5)
297 return 0;
298 if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW)
299 return 0;
300
301/* XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time?
302 * err = bcm43xx_switch_core(bcm, bcm->active_80211_core);
303 * if (err)
304 * return err;
305 * if (((bcm->current_core->rev >= 3) &&
306 * (bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) ||
307 * ((bcm->current_core->rev < 3) &&
308 * !(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4))))
309 * return 0;
310 * err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
311 * if (err)
312 * return err;
313 */
314
315 err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);
316 if (err)
317 goto out;
318 out &= ~BCM43xx_PCTL_XTAL_POWERUP;
319 out |= BCM43xx_PCTL_PLL_POWERDOWN;
320 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
321 if (err)
322 goto err_pci;
323 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
324 if (err)
325 goto err_pci;
326 }
327
328out:
329 return err;
330
331err_pci:
332 printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n");
333 err = -EBUSY;
334 goto out;
335}
336
337/* Set the PowerSavingControlBits.
338 * Bitvalues:
339 * 0 => unset the bit
340 * 1 => set the bit
341 * -1 => calculate the bit
342 */
343void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,
344 int bit25, int bit26)
345{
346 int i;
347 u32 status;
348
349//FIXME: Force 25 to off and 26 to on for now:
350bit25 = 0;
351bit26 = 1;
352
353 if (bit25 == -1) {
354 //TODO: If powersave is not off and FIXME is not set and we are not in adhoc
355 // and thus is not an AP and we are associated, set bit 25
356 }
357 if (bit26 == -1) {
358 //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME,
359 // or we are associated, or FIXME, or the latest PS-Poll packet sent was
360 // successful, set bit26
361 }
362 status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
363 if (bit25)
364 status |= BCM43xx_SBF_PS1;
365 else
366 status &= ~BCM43xx_SBF_PS1;
367 if (bit26)
368 status |= BCM43xx_SBF_PS2;
369 else
370 status &= ~BCM43xx_SBF_PS2;
371 bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
372 if (bit26 && bcm->current_core->rev >= 5) {
373 for (i = 0; i < 100; i++) {
374 if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4)
375 break;
376 udelay(10);
377 }
378 }
379}