blob: 3c92b62807c56afafd5a88a04b8ab8d3f29ccdbb [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
38/* Get max/min slowclock frequency
39 * as described in http://bcm-specs.sipsolutions.net/PowerControl
40 */
41static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm,
42 int get_max)
43{
44 int limit = 0;
45 int divisor;
46 int selection;
47 int err;
48 u32 tmp;
49 struct bcm43xx_coreinfo *old_core;
50
51 if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
52 goto out;
53 old_core = bcm->current_core;
54 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
55 if (err)
56 goto out;
57
58 if (bcm->current_core->rev < 6) {
59 if ((bcm->bustype == BCM43xx_BUSTYPE_PCMCIA) ||
60 (bcm->bustype == BCM43xx_BUSTYPE_SB)) {
61 selection = 1;
62 divisor = 32;
63 } else {
64 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp);
65 if (err) {
66 printk(KERN_ERR PFX "clockfreqlimit pcicfg read failure\n");
67 goto out_switchback;
68 }
69 if (tmp & 0x10) {
70 /* PCI */
71 selection = 2;
72 divisor = 64;
73 } else {
74 /* XTAL */
75 selection = 1;
76 divisor = 32;
77 }
78 }
79 } else if (bcm->current_core->rev < 10) {
80 selection = (tmp & 0x07);
81 if (selection) {
82 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
83 divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16));
84 } else
85 divisor = 1;
86 } else {
87 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL);
88 divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16));
89 selection = 1;
90 }
91
92 switch (selection) {
93 case 0:
94 /* LPO */
95 if (get_max)
96 limit = 43000;
97 else
98 limit = 25000;
99 break;
100 case 1:
101 /* XTAL */
102 if (get_max)
103 limit = 20200000;
104 else
105 limit = 19800000;
106 break;
107 case 2:
108 /* PCI */
109 if (get_max)
110 limit = 34000000;
111 else
112 limit = 25000000;
113 break;
114 default:
115 assert(0);
116 }
117 limit /= divisor;
118
119out_switchback:
120 err = bcm43xx_switch_core(bcm, old_core);
121 assert(err == 0);
122
123out:
124 return limit;
125}
126
127/* init power control
128 * as described in http://bcm-specs.sipsolutions.net/PowerControl
129 */
130int bcm43xx_pctl_init(struct bcm43xx_private *bcm)
131{
132 int err, maxfreq;
133 struct bcm43xx_coreinfo *old_core;
134
135 if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
136 return 0;
137 old_core = bcm->current_core;
138 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
139 if (err == -ENODEV)
140 return 0;
141 if (err)
142 goto out;
143
144 maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1);
145 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY,
146 (maxfreq * 150 + 999999) / 1000000);
147 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY,
148 (maxfreq * 15 + 999999) / 1000000);
149
150 err = bcm43xx_switch_core(bcm, old_core);
151 assert(err == 0);
152
153out:
154 return err;
155}
156
157u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm)
158{
159 u16 delay = 0;
160 int err;
161 u32 pll_on_delay;
162 struct bcm43xx_coreinfo *old_core;
163 int minfreq;
164
165 if (bcm->bustype != BCM43xx_BUSTYPE_PCI)
166 goto out;
167 if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
168 goto out;
169 old_core = bcm->current_core;
170 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
171 if (err == -ENODEV)
172 goto out;
173
174 minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0);
175 pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY);
176 delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;
177
178 err = bcm43xx_switch_core(bcm, old_core);
179 assert(err == 0);
180
181out:
182 return delay;
183}
184
185/* set the powercontrol clock
186 * as described in http://bcm-specs.sipsolutions.net/PowerControl
187 */
188int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode)
189{
190 int err;
191 struct bcm43xx_coreinfo *old_core;
192 u32 tmp;
193
194 old_core = bcm->current_core;
195 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
196 if (err == -ENODEV)
197 return 0;
198 if (err)
199 goto out;
200
201 if (bcm->core_chipcommon.rev < 6) {
202 if (mode == BCM43xx_PCTL_CLK_FAST) {
203 err = bcm43xx_pctl_set_crystal(bcm, 1);
204 if (err)
205 goto out;
206 }
207 } else {
208 if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) &&
209 (bcm->core_chipcommon.rev < 10)) {
210 switch (mode) {
211 case BCM43xx_PCTL_CLK_FAST:
212 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
213 tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL;
214 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
215 break;
216 case BCM43xx_PCTL_CLK_SLOW:
217 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
218 tmp |= BCM43xx_PCTL_FORCE_SLOW;
219 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
220 break;
221 case BCM43xx_PCTL_CLK_DYNAMIC:
222 tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
223 tmp &= ~BCM43xx_PCTL_FORCE_SLOW;
224 tmp |= BCM43xx_PCTL_FORCE_PLL;
225 tmp &= ~BCM43xx_PCTL_DYN_XTAL;
226 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
227 }
228 }
229 }
230
231 err = bcm43xx_switch_core(bcm, old_core);
232 assert(err == 0);
233
234out:
235 return err;
236}
237
238int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on)
239{
240 int err;
241 u32 in, out, outenable;
242
243 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in);
244 if (err)
245 goto err_pci;
246 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out);
247 if (err)
248 goto err_pci;
249 err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable);
250 if (err)
251 goto err_pci;
252
253 outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
254
255 if (on) {
256 if (in & 0x40)
257 return 0;
258
259 out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
260
261 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
262 if (err)
263 goto err_pci;
264 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
265 if (err)
266 goto err_pci;
267 udelay(1000);
268
269 out &= ~BCM43xx_PCTL_PLL_POWERDOWN;
270 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
271 if (err)
272 goto err_pci;
273 udelay(5000);
274 } else {
275 if (bcm->current_core->rev < 5)
276 return 0;
277 if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW)
278 return 0;
279
280/* XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time?
281 * err = bcm43xx_switch_core(bcm, bcm->active_80211_core);
282 * if (err)
283 * return err;
284 * if (((bcm->current_core->rev >= 3) &&
285 * (bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) ||
286 * ((bcm->current_core->rev < 3) &&
287 * !(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4))))
288 * return 0;
289 * err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
290 * if (err)
291 * return err;
292 */
293
294 err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);
295 if (err)
296 goto out;
297 out &= ~BCM43xx_PCTL_XTAL_POWERUP;
298 out |= BCM43xx_PCTL_PLL_POWERDOWN;
299 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
300 if (err)
301 goto err_pci;
302 err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
303 if (err)
304 goto err_pci;
305 }
306
307out:
308 return err;
309
310err_pci:
311 printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n");
312 err = -EBUSY;
313 goto out;
314}
315
316/* Set the PowerSavingControlBits.
317 * Bitvalues:
318 * 0 => unset the bit
319 * 1 => set the bit
320 * -1 => calculate the bit
321 */
322void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,
323 int bit25, int bit26)
324{
325 int i;
326 u32 status;
327
328//FIXME: Force 25 to off and 26 to on for now:
329bit25 = 0;
330bit26 = 1;
331
332 if (bit25 == -1) {
333 //TODO: If powersave is not off and FIXME is not set and we are not in adhoc
334 // and thus is not an AP and we are associated, set bit 25
335 }
336 if (bit26 == -1) {
337 //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME,
338 // or we are associated, or FIXME, or the latest PS-Poll packet sent was
339 // successful, set bit26
340 }
341 status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
342 if (bit25)
343 status |= BCM43xx_SBF_PS1;
344 else
345 status &= ~BCM43xx_SBF_PS1;
346 if (bit26)
347 status |= BCM43xx_SBF_PS2;
348 else
349 status &= ~BCM43xx_SBF_PS2;
350 bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
351 if (bit26 && bcm->current_core->rev >= 5) {
352 for (i = 0; i < 100; i++) {
353 if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4)
354 break;
355 udelay(10);
356 }
357 }
358}