blob: 895164783c3b5f21092b04cd09c956ead206ef65 [file] [log] [blame]
Rafał Miłecki1d738e62011-07-07 15:25:27 +02001/*
2
3 Broadcom B43 wireless driver
4 IEEE 802.11n LCN-PHY support
5
Rafał Miłecki108f4f32011-09-03 21:01:02 +02006 Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
7
Rafał Miłecki1d738e62011-07-07 15:25:27 +02008 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; see the file COPYING. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22
Rafał Miłeckib5347062011-09-16 12:33:59 +020023 This file incorporates work covered by the following copyright and
24 permission notice:
25
26 Copyright (c) 2010 Broadcom Corporation
27
28 Permission to use, copy, modify, and/or distribute this software for any
29 purpose with or without fee is hereby granted, provided that the above
30 copyright notice and this permission notice appear in all copies.
Rafał Miłecki1d738e62011-07-07 15:25:27 +020031*/
32
33#include <linux/slab.h>
34
35#include "b43.h"
36#include "phy_lcn.h"
37#include "tables_phy_lcn.h"
38#include "main.h"
39
Rafał Miłecki0c5644b92011-09-16 12:34:00 +020040struct lcn_tx_iir_filter {
41 u8 type;
42 u16 values[16];
43};
44
Rafał Miłeckiac78a522011-09-16 12:34:01 +020045/* In theory it's PHY common function, move if needed */
46/* brcms_b_switch_macfreq */
47static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode)
48{
49 if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) {
50 switch (spurmode) {
51 case 2: /* 126 Mhz */
52 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082);
53 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
54 break;
55 case 1: /* 123 Mhz */
56 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341);
57 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
58 break;
59 default: /* 120 Mhz */
60 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889);
61 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
62 break;
63 }
64 } else if (dev->phy.type == B43_PHYTYPE_LCN) {
65 switch (spurmode) {
66 case 1: /* 82 Mhz */
67 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0);
68 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
69 break;
70 default: /* 80 Mhz */
71 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD);
72 b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
73 break;
74 }
75 }
76}
77
Rafał Miłecki1d738e62011-07-07 15:25:27 +020078/**************************************************
Rafał Miłeckidc713fb2011-08-15 18:50:56 +020079 * Radio 2064.
80 **************************************************/
81
Rafał Miłeckibce4dc42011-08-31 23:36:20 +020082/* wlc_lcnphy_radio_2064_channel_tune_4313 */
Rafał Miłecki39f7d332011-08-28 14:59:58 +020083static void b43_radio_2064_channel_setup(struct b43_wldev *dev)
84{
85 u16 save[2];
86
87 b43_radio_set(dev, 0x09d, 0x4);
88 b43_radio_write(dev, 0x09e, 0xf);
89
Rafał Miłeckicf577fc2011-08-31 23:36:21 +020090 /* Channel specific values in theory, in practice always the same */
Rafał Miłecki39f7d332011-08-28 14:59:58 +020091 b43_radio_write(dev, 0x02a, 0xb);
92 b43_radio_maskset(dev, 0x030, ~0x3, 0xa);
93 b43_radio_maskset(dev, 0x091, ~0x3, 0);
94 b43_radio_maskset(dev, 0x038, ~0xf, 0x7);
95 b43_radio_maskset(dev, 0x030, ~0xc, 0x8);
96 b43_radio_maskset(dev, 0x05e, ~0xf, 0x8);
97 b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80);
98 b43_radio_write(dev, 0x06c, 0x80);
99
100 save[0] = b43_radio_read(dev, 0x044);
101 save[1] = b43_radio_read(dev, 0x12b);
102
103 b43_radio_set(dev, 0x044, 0x7);
104 b43_radio_set(dev, 0x12b, 0xe);
105
106 /* TODO */
107
108 b43_radio_write(dev, 0x040, 0xfb);
109
110 b43_radio_write(dev, 0x041, 0x9a);
111 b43_radio_write(dev, 0x042, 0xa3);
112 b43_radio_write(dev, 0x043, 0x0c);
113
114 /* TODO */
115
116 b43_radio_set(dev, 0x044, 0x0c);
117 udelay(1);
118
119 b43_radio_write(dev, 0x044, save[0]);
120 b43_radio_write(dev, 0x12b, save[1]);
121
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200122 if (dev->phy.rev == 1) {
123 /* brcmsmac uses outdated 0x3 for 0x038 */
124 b43_radio_write(dev, 0x038, 0x0);
125 b43_radio_write(dev, 0x091, 0x7);
126 }
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200127}
128
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200129/* wlc_radio_2064_init */
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200130static void b43_radio_2064_init(struct b43_wldev *dev)
131{
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200132 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
133 b43_radio_write(dev, 0x09c, 0x0020);
134 b43_radio_write(dev, 0x105, 0x0008);
135 } else {
136 /* TODO */
137 }
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200138 b43_radio_write(dev, 0x032, 0x0062);
139 b43_radio_write(dev, 0x033, 0x0019);
140 b43_radio_write(dev, 0x090, 0x0010);
141 b43_radio_write(dev, 0x010, 0x0000);
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200142 if (dev->phy.rev == 1) {
143 b43_radio_write(dev, 0x060, 0x007f);
144 b43_radio_write(dev, 0x061, 0x0072);
145 b43_radio_write(dev, 0x062, 0x007f);
146 }
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200147 b43_radio_write(dev, 0x01d, 0x0002);
148 b43_radio_write(dev, 0x01e, 0x0006);
149
150 b43_phy_write(dev, 0x4ea, 0x4688);
151 b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2);
152 b43_phy_mask(dev, 0x4eb, ~0x01c0);
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200153 b43_phy_maskset(dev, 0x46a, 0xff00, 0x19);
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200154
155 b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0);
156
157 b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
158 b43_radio_set(dev, 0x004, 0x40);
159 b43_radio_set(dev, 0x120, 0x10);
160 b43_radio_set(dev, 0x078, 0x80);
161 b43_radio_set(dev, 0x129, 0x2);
162 b43_radio_set(dev, 0x057, 0x1);
163 b43_radio_set(dev, 0x05b, 0x2);
164
165 /* TODO: wait for some bit to be set */
166 b43_radio_read(dev, 0x05c);
167
168 b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
169 b43_radio_mask(dev, 0x057, (u16) ~0xff01);
170
171 b43_phy_write(dev, 0x933, 0x2d6b);
172 b43_phy_write(dev, 0x934, 0x2d6b);
173 b43_phy_write(dev, 0x935, 0x2d6b);
174 b43_phy_write(dev, 0x936, 0x2d6b);
175 b43_phy_write(dev, 0x937, 0x016b);
176
177 b43_radio_mask(dev, 0x057, (u16) ~0xff02);
178 b43_radio_write(dev, 0x0c2, 0x006f);
179}
180
181/**************************************************
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200182 * Various PHY ops
183 **************************************************/
184
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200185/* wlc_lcnphy_toggle_afe_pwdn */
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200186static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev)
187{
188 u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2);
189 u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1);
190
191 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1);
192 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1);
193
194 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1);
195 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1);
196
197 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2);
198 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1);
199}
200
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200201/* wlc_lcnphy_clear_tx_power_offsets */
202static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev)
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200203{
204 u8 i;
205
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200206 if (1) { /* FIXME */
207 b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340);
208 for (i = 0; i < 30; i++) {
209 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
210 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
211 }
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200212 }
213
214 b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80);
215 for (i = 0; i < 64; i++) {
216 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
217 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
218 }
219}
220
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200221/* wlc_lcnphy_rev0_baseband_init */
222static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev)
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200223{
224 b43_radio_write(dev, 0x11c, 0);
225
226 b43_phy_write(dev, 0x43b, 0);
227 b43_phy_write(dev, 0x43c, 0);
228 b43_phy_write(dev, 0x44c, 0);
229 b43_phy_write(dev, 0x4e6, 0);
230 b43_phy_write(dev, 0x4f9, 0);
231 b43_phy_write(dev, 0x4b0, 0);
232 b43_phy_write(dev, 0x938, 0);
233 b43_phy_write(dev, 0x4b0, 0);
234 b43_phy_write(dev, 0x44e, 0);
235
236 b43_phy_set(dev, 0x567, 0x03);
237
238 b43_phy_set(dev, 0x44a, 0x44);
239 b43_phy_write(dev, 0x44a, 0x80);
240
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200241 if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM))
242 ; /* TODO */
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200243 b43_phy_maskset(dev, 0x634, ~0xff, 0xc);
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200244 if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) {
245 b43_phy_maskset(dev, 0x634, ~0xff, 0xa);
246 b43_phy_write(dev, 0x910, 0x1);
247 }
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200248
249 b43_phy_write(dev, 0x910, 0x1);
250
251 b43_phy_maskset(dev, 0x448, ~0x300, 0x100);
252 b43_phy_maskset(dev, 0x608, ~0xff, 0x17);
253 b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea);
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200254}
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200255
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200256/* wlc_lcnphy_bu_tweaks */
257static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev)
258{
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200259 b43_phy_set(dev, 0x805, 0x1);
260
261 b43_phy_maskset(dev, 0x42f, ~0x7, 0x3);
262 b43_phy_maskset(dev, 0x030, ~0x7, 0x3);
263
264 b43_phy_write(dev, 0x414, 0x1e10);
265 b43_phy_write(dev, 0x415, 0x0640);
266
267 b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700);
268
269 b43_phy_set(dev, 0x44a, 0x44);
270 b43_phy_write(dev, 0x44a, 0x80);
271
272 b43_phy_maskset(dev, 0x434, ~0xff, 0xfd);
273 b43_phy_maskset(dev, 0x420, ~0xff, 0x10);
274
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200275 if (dev->dev->bus_sprom->board_rev >= 0x1204)
276 b43_radio_set(dev, 0x09b, 0xf0);
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200277
278 b43_phy_write(dev, 0x7d6, 0x0902);
279
280 /* TODO: more ops */
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200281
282 if (dev->phy.rev == 1) {
283 /* TODO: more ops */
284
285 b43_phy_lcn_clear_tx_power_offsets(dev);
286 }
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200287}
288
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200289/* wlc_lcnphy_vbat_temp_sense_setup */
290static void b43_phy_lcn_sense_setup(struct b43_wldev *dev)
Rafał Miłecki765b07e2011-08-28 19:59:28 +0200291{
292 u8 i;
293
294 u16 save_radio_regs[6][2] = {
295 { 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 },
296 { 0x025, 0 }, { 0x112, 0 },
297 };
298 u16 save_phy_regs[14][2] = {
299 { 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 },
300 { 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 },
301 { 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 },
302 { 0x40d, 0 }, { 0x4a2, 0 },
303 };
304 u16 save_radio_4a4;
305
306 for (i = 0; i < 6; i++)
307 save_radio_regs[i][1] = b43_radio_read(dev,
308 save_radio_regs[i][0]);
309 for (i = 0; i < 14; i++)
310 save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]);
311 save_radio_4a4 = b43_radio_read(dev, 0x4a4);
312
313 /* TODO: config sth */
314
315 for (i = 0; i < 6; i++)
316 b43_radio_write(dev, save_radio_regs[i][0],
317 save_radio_regs[i][1]);
318 for (i = 0; i < 14; i++)
319 b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]);
320 b43_radio_write(dev, 0x4a4, save_radio_4a4);
321}
322
Rafał Miłecki0c5644b92011-09-16 12:34:00 +0200323static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev,
324 u8 filter_type)
325{
326 int i, j;
327 u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920,
328 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930,
329 0x931, 0x932 };
330 /* Table is from brcmsmac, values for type 25 were outdated, probably
331 * others need updating too */
332 struct lcn_tx_iir_filter tx_iir_filters_cck[] = {
333 { 0, { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778,
334 1582, 64, 128, 64 } },
335 { 1, { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608,
336 1863, 93, 167, 93 } },
337 { 2, { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192,
338 778, 1582, 64, 128, 64 } },
339 { 3, { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205,
340 754, 1760, 170, 340, 170 } },
341 { 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205,
342 767, 1760, 256, 185, 256 } },
343 { 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205,
344 767, 1760, 256, 273, 256 } },
345 { 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205,
346 767, 1760, 256, 352, 256 } },
347 { 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205,
348 767, 1760, 128, 233, 128 } },
349 { 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766,
350 1760, 256, 1881, 256 } },
351 { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765,
352 1760, 262, 1878, 262 } },
353 /* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720,
354 * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */
355 { 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614,
356 1864, 128, 384, 288 } },
357 { 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576,
358 613, 1864, 128, 384, 288 } },
359 { 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205,
360 754, 1760, 170, 340, 170 } },
361 };
362
363 for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) {
364 if (tx_iir_filters_cck[i].type == filter_type) {
365 for (j = 0; j < 16; j++)
366 b43_phy_write(dev, phy_regs[j],
367 tx_iir_filters_cck[i].values[j]);
368 return true;
369 }
370 }
371
372 return false;
373}
374
375static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev,
376 u8 filter_type)
377{
378 int i, j;
379 u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902,
380 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c,
381 0x90d, 0x90e };
382 struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = {
383 { 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0,
384 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } },
385 { 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750,
386 0xFE2B, 212, 0xFFCE, 212 } },
387 { 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748,
388 0xFEF2, 128, 0xFFE2, 128 } },
389 };
390
391 for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) {
392 if (tx_iir_filters_ofdm[i].type == filter_type) {
393 for (j = 0; j < 16; j++)
394 b43_phy_write(dev, phy_regs[j],
395 tx_iir_filters_ofdm[i].values[j]);
396 return true;
397 }
398 }
399
400 return false;
401}
402
Rafał Miłeckiac78a522011-09-16 12:34:01 +0200403/* wlc_lcnphy_txrx_spur_avoidance_mode */
404static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev,
405 bool enable)
406{
407 if (enable) {
408 b43_phy_write(dev, 0x942, 0x7);
409 b43_phy_write(dev, 0x93b, ((1 << 13) + 23));
410 b43_phy_write(dev, 0x93c, ((1 << 13) + 1989));
411
412 b43_phy_write(dev, 0x44a, 0x084);
413 b43_phy_write(dev, 0x44a, 0x080);
414 b43_phy_write(dev, 0x6d3, 0x2222);
415 b43_phy_write(dev, 0x6d3, 0x2220);
416 } else {
417 b43_phy_write(dev, 0x942, 0x0);
418 b43_phy_write(dev, 0x93b, ((0 << 13) + 23));
419 b43_phy_write(dev, 0x93c, ((0 << 13) + 1989));
420 }
421 b43_phy_switch_macfreq(dev, enable);
422}
423
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200424/**************************************************
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200425 * Channel switching ops.
426 **************************************************/
427
Rafał Miłeckib5347062011-09-16 12:33:59 +0200428/* wlc_lcnphy_set_chanspec_tweaks */
429static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel)
430{
431 struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
432
433 b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100);
434
435 if (channel == 1 || channel == 2 || channel == 3 || channel == 4 ||
436 channel == 9 || channel == 10 || channel == 11 || channel == 12) {
437 bcma_chipco_pll_write(cc, 0x2, 0x03000c04);
438 bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0);
439 bcma_chipco_pll_write(cc, 0x4, 0x200005c0);
440
441 bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
442
443 b43_phy_write(dev, 0x942, 0);
444
Rafał Miłeckiac78a522011-09-16 12:34:01 +0200445 b43_phy_lcn_txrx_spur_avoidance_mode(dev, false);
Rafał Miłeckib5347062011-09-16 12:33:59 +0200446 b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00);
447 b43_phy_write(dev, 0x425, 0x5907);
448 } else {
449 bcma_chipco_pll_write(cc, 0x2, 0x03140c04);
450 bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333);
451 bcma_chipco_pll_write(cc, 0x4, 0x202c2820);
452
453 bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
454
455 b43_phy_write(dev, 0x942, 0);
456
Rafał Miłeckiac78a522011-09-16 12:34:01 +0200457 b43_phy_lcn_txrx_spur_avoidance_mode(dev, true);
Rafał Miłeckib5347062011-09-16 12:33:59 +0200458 b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00);
459 b43_phy_write(dev, 0x425, 0x590a);
460 }
461
462 b43_phy_set(dev, 0x44a, 0x44);
463 b43_phy_write(dev, 0x44a, 0x80);
464}
465
466/* wlc_phy_chanspec_set_lcnphy */
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200467static int b43_phy_lcn_set_channel(struct b43_wldev *dev,
468 struct ieee80211_channel *channel,
469 enum nl80211_channel_type channel_type)
470{
Rafał Miłecki0c5644b92011-09-16 12:34:00 +0200471 static const u16 sfo_cfg[14][2] = {
472 {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078},
473 {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067},
474 {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055},
475 };
476
Rafał Miłeckib5347062011-09-16 12:33:59 +0200477 b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value);
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200478
479 b43_phy_set(dev, 0x44a, 0x44);
480 b43_phy_write(dev, 0x44a, 0x80);
481
482 b43_radio_2064_channel_setup(dev);
483 mdelay(1);
484
485 b43_phy_lcn_afe_set_unset(dev);
486
Rafał Miłecki0c5644b92011-09-16 12:34:00 +0200487 b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]);
488 b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]);
489
490 if (channel->hw_value == 14) {
491 b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8);
492 b43_phy_lcn_load_tx_iir_cck_filter(dev, 3);
493 } else {
494 b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8);
495 /* brcmsmac uses filter_type 2, we follow wl with 25 */
496 b43_phy_lcn_load_tx_iir_cck_filter(dev, 25);
497 }
498 /* brcmsmac uses filter_type 2, we follow wl with 0 */
499 b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0);
500
501 b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3);
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200502
503 return 0;
504}
505
506/**************************************************
Rafał Miłeckif9286682011-08-14 23:27:28 +0200507 * Basic PHY ops.
508 **************************************************/
509
510static int b43_phy_lcn_op_allocate(struct b43_wldev *dev)
511{
512 struct b43_phy_lcn *phy_lcn;
513
514 phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL);
515 if (!phy_lcn)
516 return -ENOMEM;
517 dev->phy.lcn = phy_lcn;
518
519 return 0;
520}
521
522static void b43_phy_lcn_op_free(struct b43_wldev *dev)
523{
524 struct b43_phy *phy = &dev->phy;
525 struct b43_phy_lcn *phy_lcn = phy->lcn;
526
527 kfree(phy_lcn);
528 phy->lcn = NULL;
529}
530
531static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev)
532{
533 struct b43_phy *phy = &dev->phy;
534 struct b43_phy_lcn *phy_lcn = phy->lcn;
535
536 memset(phy_lcn, 0, sizeof(*phy_lcn));
537}
538
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200539/* wlc_phy_init_lcnphy */
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200540static int b43_phy_lcn_op_init(struct b43_wldev *dev)
541{
Rafał Miłeckib5347062011-09-16 12:33:59 +0200542 struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
543
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200544 b43_phy_set(dev, 0x44a, 0x80);
545 b43_phy_mask(dev, 0x44a, 0x7f);
546 b43_phy_set(dev, 0x6d1, 0x80);
547 b43_phy_write(dev, 0x6d0, 0x7);
548
549 b43_phy_lcn_afe_set_unset(dev);
550
551 b43_phy_write(dev, 0x60a, 0xa0);
552 b43_phy_write(dev, 0x46a, 0x19);
553 b43_phy_maskset(dev, 0x663, 0xFF00, 0x64);
554
555 b43_phy_lcn_tables_init(dev);
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200556
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200557 b43_phy_lcn_rev0_baseband_init(dev);
558 b43_phy_lcn_bu_tweaks(dev);
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200559
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200560 if (dev->phy.radio_ver == 0x2064)
561 b43_radio_2064_init(dev);
562 else
563 B43_WARN_ON(1);
564
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200565 b43_phy_lcn_sense_setup(dev);
Rafał Miłecki765b07e2011-08-28 19:59:28 +0200566
Rafał Miłeckib5347062011-09-16 12:33:59 +0200567 b43_switch_channel(dev, dev->phy.channel);
568
569 bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9);
570 bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd);
571
572 /* TODO */
573
574 b43_phy_set(dev, 0x448, 0x4000);
575 udelay(100);
576 b43_phy_mask(dev, 0x448, ~0x4000);
577
578 /* TODO */
579
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200580 return 0;
581}
582
Rafał Miłeckiba356b52011-08-14 23:27:29 +0200583static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev,
584 bool blocked)
585{
586 if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
587 b43err(dev->wl, "MAC not suspended\n");
588
589 if (blocked) {
590 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00);
591 b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00);
592
593 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00);
594 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2);
595 b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808);
596
597 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8);
598 b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8);
599 } else {
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200600 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00);
601 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808);
602 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8);
Rafał Miłeckiba356b52011-08-14 23:27:29 +0200603 }
604}
605
Rafał Miłecki7ed88522011-08-14 23:27:30 +0200606static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on)
607{
608 if (on) {
609 b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7);
610 } else {
611 b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7);
612 b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7);
613 }
614}
615
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200616static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev,
617 unsigned int new_channel)
618{
619 struct ieee80211_channel *channel = dev->wl->hw->conf.channel;
620 enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type;
621
622 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
623 if ((new_channel < 1) || (new_channel > 14))
624 return -EINVAL;
625 } else {
626 return -EINVAL;
627 }
628
629 return b43_phy_lcn_set_channel(dev, channel, channel_type);
630}
631
Rafał Miłeckif9286682011-08-14 23:27:28 +0200632static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev)
633{
634 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
635 return 1;
636 return 36;
637}
638
639static enum b43_txpwr_result
640b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
641{
642 return B43_TXPWR_RES_DONE;
643}
644
645static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev)
646{
647}
648
649/**************************************************
Rafał Miłeckif533d0f2011-08-28 14:28:43 +0200650 * R/W ops.
651 **************************************************/
652
653static u16 b43_phy_lcn_op_read(struct b43_wldev *dev, u16 reg)
654{
655 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
656 return b43_read16(dev, B43_MMIO_PHY_DATA);
657}
658
659static void b43_phy_lcn_op_write(struct b43_wldev *dev, u16 reg, u16 value)
660{
661 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
662 b43_write16(dev, B43_MMIO_PHY_DATA, value);
663}
664
665static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
666 u16 set)
667{
668 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
669 b43_write16(dev, B43_MMIO_PHY_DATA,
670 (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
671}
672
673static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg)
674{
675 /* LCN-PHY needs 0x200 for read access */
676 reg |= 0x200;
677
678 b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
679 return b43_read16(dev, B43_MMIO_RADIO24_DATA);
680}
681
682static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg,
683 u16 value)
684{
685 b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
686 b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
687}
688
689/**************************************************
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200690 * PHY ops struct.
691 **************************************************/
692
693const struct b43_phy_operations b43_phyops_lcn = {
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200694 .allocate = b43_phy_lcn_op_allocate,
695 .free = b43_phy_lcn_op_free,
696 .prepare_structs = b43_phy_lcn_op_prepare_structs,
697 .init = b43_phy_lcn_op_init,
698 .phy_read = b43_phy_lcn_op_read,
699 .phy_write = b43_phy_lcn_op_write,
700 .phy_maskset = b43_phy_lcn_op_maskset,
701 .radio_read = b43_phy_lcn_op_radio_read,
702 .radio_write = b43_phy_lcn_op_radio_write,
703 .software_rfkill = b43_phy_lcn_op_software_rfkill,
704 .switch_analog = b43_phy_lcn_op_switch_analog,
705 .switch_channel = b43_phy_lcn_op_switch_channel,
706 .get_default_chan = b43_phy_lcn_op_get_default_chan,
707 .recalc_txpower = b43_phy_lcn_op_recalc_txpower,
708 .adjust_txpower = b43_phy_lcn_op_adjust_txpower,
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200709};