blob: 765036461204bc094ea25f63ead9331019bd18a1 [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łecki1d738e62011-07-07 15:25:27 +020045/**************************************************
Rafał Miłeckidc713fb2011-08-15 18:50:56 +020046 * Radio 2064.
47 **************************************************/
48
Rafał Miłeckibce4dc42011-08-31 23:36:20 +020049/* wlc_lcnphy_radio_2064_channel_tune_4313 */
Rafał Miłecki39f7d332011-08-28 14:59:58 +020050static void b43_radio_2064_channel_setup(struct b43_wldev *dev)
51{
52 u16 save[2];
53
54 b43_radio_set(dev, 0x09d, 0x4);
55 b43_radio_write(dev, 0x09e, 0xf);
56
Rafał Miłeckicf577fc2011-08-31 23:36:21 +020057 /* Channel specific values in theory, in practice always the same */
Rafał Miłecki39f7d332011-08-28 14:59:58 +020058 b43_radio_write(dev, 0x02a, 0xb);
59 b43_radio_maskset(dev, 0x030, ~0x3, 0xa);
60 b43_radio_maskset(dev, 0x091, ~0x3, 0);
61 b43_radio_maskset(dev, 0x038, ~0xf, 0x7);
62 b43_radio_maskset(dev, 0x030, ~0xc, 0x8);
63 b43_radio_maskset(dev, 0x05e, ~0xf, 0x8);
64 b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80);
65 b43_radio_write(dev, 0x06c, 0x80);
66
67 save[0] = b43_radio_read(dev, 0x044);
68 save[1] = b43_radio_read(dev, 0x12b);
69
70 b43_radio_set(dev, 0x044, 0x7);
71 b43_radio_set(dev, 0x12b, 0xe);
72
73 /* TODO */
74
75 b43_radio_write(dev, 0x040, 0xfb);
76
77 b43_radio_write(dev, 0x041, 0x9a);
78 b43_radio_write(dev, 0x042, 0xa3);
79 b43_radio_write(dev, 0x043, 0x0c);
80
81 /* TODO */
82
83 b43_radio_set(dev, 0x044, 0x0c);
84 udelay(1);
85
86 b43_radio_write(dev, 0x044, save[0]);
87 b43_radio_write(dev, 0x12b, save[1]);
88
Rafał Miłeckicf577fc2011-08-31 23:36:21 +020089 if (dev->phy.rev == 1) {
90 /* brcmsmac uses outdated 0x3 for 0x038 */
91 b43_radio_write(dev, 0x038, 0x0);
92 b43_radio_write(dev, 0x091, 0x7);
93 }
Rafał Miłecki39f7d332011-08-28 14:59:58 +020094}
95
Rafał Miłeckibce4dc42011-08-31 23:36:20 +020096/* wlc_radio_2064_init */
Rafał Miłeckidc713fb2011-08-15 18:50:56 +020097static void b43_radio_2064_init(struct b43_wldev *dev)
98{
Rafał Miłeckicf577fc2011-08-31 23:36:21 +020099 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
100 b43_radio_write(dev, 0x09c, 0x0020);
101 b43_radio_write(dev, 0x105, 0x0008);
102 } else {
103 /* TODO */
104 }
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200105 b43_radio_write(dev, 0x032, 0x0062);
106 b43_radio_write(dev, 0x033, 0x0019);
107 b43_radio_write(dev, 0x090, 0x0010);
108 b43_radio_write(dev, 0x010, 0x0000);
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200109 if (dev->phy.rev == 1) {
110 b43_radio_write(dev, 0x060, 0x007f);
111 b43_radio_write(dev, 0x061, 0x0072);
112 b43_radio_write(dev, 0x062, 0x007f);
113 }
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200114 b43_radio_write(dev, 0x01d, 0x0002);
115 b43_radio_write(dev, 0x01e, 0x0006);
116
117 b43_phy_write(dev, 0x4ea, 0x4688);
118 b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2);
119 b43_phy_mask(dev, 0x4eb, ~0x01c0);
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200120 b43_phy_maskset(dev, 0x46a, 0xff00, 0x19);
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200121
122 b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0);
123
124 b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
125 b43_radio_set(dev, 0x004, 0x40);
126 b43_radio_set(dev, 0x120, 0x10);
127 b43_radio_set(dev, 0x078, 0x80);
128 b43_radio_set(dev, 0x129, 0x2);
129 b43_radio_set(dev, 0x057, 0x1);
130 b43_radio_set(dev, 0x05b, 0x2);
131
132 /* TODO: wait for some bit to be set */
133 b43_radio_read(dev, 0x05c);
134
135 b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
136 b43_radio_mask(dev, 0x057, (u16) ~0xff01);
137
138 b43_phy_write(dev, 0x933, 0x2d6b);
139 b43_phy_write(dev, 0x934, 0x2d6b);
140 b43_phy_write(dev, 0x935, 0x2d6b);
141 b43_phy_write(dev, 0x936, 0x2d6b);
142 b43_phy_write(dev, 0x937, 0x016b);
143
144 b43_radio_mask(dev, 0x057, (u16) ~0xff02);
145 b43_radio_write(dev, 0x0c2, 0x006f);
146}
147
148/**************************************************
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200149 * Various PHY ops
150 **************************************************/
151
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200152/* wlc_lcnphy_toggle_afe_pwdn */
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200153static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev)
154{
155 u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2);
156 u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1);
157
158 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1);
159 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1);
160
161 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1);
162 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1);
163
164 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2);
165 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1);
166}
167
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200168/* wlc_lcnphy_clear_tx_power_offsets */
169static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev)
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200170{
171 u8 i;
172
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200173 if (1) { /* FIXME */
174 b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340);
175 for (i = 0; i < 30; i++) {
176 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
177 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
178 }
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200179 }
180
181 b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80);
182 for (i = 0; i < 64; i++) {
183 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
184 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
185 }
186}
187
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200188/* wlc_lcnphy_rev0_baseband_init */
189static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev)
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200190{
191 b43_radio_write(dev, 0x11c, 0);
192
193 b43_phy_write(dev, 0x43b, 0);
194 b43_phy_write(dev, 0x43c, 0);
195 b43_phy_write(dev, 0x44c, 0);
196 b43_phy_write(dev, 0x4e6, 0);
197 b43_phy_write(dev, 0x4f9, 0);
198 b43_phy_write(dev, 0x4b0, 0);
199 b43_phy_write(dev, 0x938, 0);
200 b43_phy_write(dev, 0x4b0, 0);
201 b43_phy_write(dev, 0x44e, 0);
202
203 b43_phy_set(dev, 0x567, 0x03);
204
205 b43_phy_set(dev, 0x44a, 0x44);
206 b43_phy_write(dev, 0x44a, 0x80);
207
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200208 if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM))
209 ; /* TODO */
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200210 b43_phy_maskset(dev, 0x634, ~0xff, 0xc);
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200211 if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) {
212 b43_phy_maskset(dev, 0x634, ~0xff, 0xa);
213 b43_phy_write(dev, 0x910, 0x1);
214 }
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200215
216 b43_phy_write(dev, 0x910, 0x1);
217
218 b43_phy_maskset(dev, 0x448, ~0x300, 0x100);
219 b43_phy_maskset(dev, 0x608, ~0xff, 0x17);
220 b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea);
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200221}
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200222
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200223/* wlc_lcnphy_bu_tweaks */
224static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev)
225{
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200226 b43_phy_set(dev, 0x805, 0x1);
227
228 b43_phy_maskset(dev, 0x42f, ~0x7, 0x3);
229 b43_phy_maskset(dev, 0x030, ~0x7, 0x3);
230
231 b43_phy_write(dev, 0x414, 0x1e10);
232 b43_phy_write(dev, 0x415, 0x0640);
233
234 b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700);
235
236 b43_phy_set(dev, 0x44a, 0x44);
237 b43_phy_write(dev, 0x44a, 0x80);
238
239 b43_phy_maskset(dev, 0x434, ~0xff, 0xfd);
240 b43_phy_maskset(dev, 0x420, ~0xff, 0x10);
241
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200242 if (dev->dev->bus_sprom->board_rev >= 0x1204)
243 b43_radio_set(dev, 0x09b, 0xf0);
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200244
245 b43_phy_write(dev, 0x7d6, 0x0902);
246
247 /* TODO: more ops */
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200248
249 if (dev->phy.rev == 1) {
250 /* TODO: more ops */
251
252 b43_phy_lcn_clear_tx_power_offsets(dev);
253 }
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200254}
255
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200256/* wlc_lcnphy_vbat_temp_sense_setup */
257static void b43_phy_lcn_sense_setup(struct b43_wldev *dev)
Rafał Miłecki765b07e2011-08-28 19:59:28 +0200258{
259 u8 i;
260
261 u16 save_radio_regs[6][2] = {
262 { 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 },
263 { 0x025, 0 }, { 0x112, 0 },
264 };
265 u16 save_phy_regs[14][2] = {
266 { 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 },
267 { 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 },
268 { 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 },
269 { 0x40d, 0 }, { 0x4a2, 0 },
270 };
271 u16 save_radio_4a4;
272
273 for (i = 0; i < 6; i++)
274 save_radio_regs[i][1] = b43_radio_read(dev,
275 save_radio_regs[i][0]);
276 for (i = 0; i < 14; i++)
277 save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]);
278 save_radio_4a4 = b43_radio_read(dev, 0x4a4);
279
280 /* TODO: config sth */
281
282 for (i = 0; i < 6; i++)
283 b43_radio_write(dev, save_radio_regs[i][0],
284 save_radio_regs[i][1]);
285 for (i = 0; i < 14; i++)
286 b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]);
287 b43_radio_write(dev, 0x4a4, save_radio_4a4);
288}
289
Rafał Miłecki0c5644b92011-09-16 12:34:00 +0200290static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev,
291 u8 filter_type)
292{
293 int i, j;
294 u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920,
295 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930,
296 0x931, 0x932 };
297 /* Table is from brcmsmac, values for type 25 were outdated, probably
298 * others need updating too */
299 struct lcn_tx_iir_filter tx_iir_filters_cck[] = {
300 { 0, { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778,
301 1582, 64, 128, 64 } },
302 { 1, { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608,
303 1863, 93, 167, 93 } },
304 { 2, { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192,
305 778, 1582, 64, 128, 64 } },
306 { 3, { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205,
307 754, 1760, 170, 340, 170 } },
308 { 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205,
309 767, 1760, 256, 185, 256 } },
310 { 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205,
311 767, 1760, 256, 273, 256 } },
312 { 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205,
313 767, 1760, 256, 352, 256 } },
314 { 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205,
315 767, 1760, 128, 233, 128 } },
316 { 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766,
317 1760, 256, 1881, 256 } },
318 { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765,
319 1760, 262, 1878, 262 } },
320 /* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720,
321 * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */
322 { 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614,
323 1864, 128, 384, 288 } },
324 { 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576,
325 613, 1864, 128, 384, 288 } },
326 { 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205,
327 754, 1760, 170, 340, 170 } },
328 };
329
330 for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) {
331 if (tx_iir_filters_cck[i].type == filter_type) {
332 for (j = 0; j < 16; j++)
333 b43_phy_write(dev, phy_regs[j],
334 tx_iir_filters_cck[i].values[j]);
335 return true;
336 }
337 }
338
339 return false;
340}
341
342static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev,
343 u8 filter_type)
344{
345 int i, j;
346 u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902,
347 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c,
348 0x90d, 0x90e };
349 struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = {
350 { 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0,
351 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } },
352 { 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750,
353 0xFE2B, 212, 0xFFCE, 212 } },
354 { 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748,
355 0xFEF2, 128, 0xFFE2, 128 } },
356 };
357
358 for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) {
359 if (tx_iir_filters_ofdm[i].type == filter_type) {
360 for (j = 0; j < 16; j++)
361 b43_phy_write(dev, phy_regs[j],
362 tx_iir_filters_ofdm[i].values[j]);
363 return true;
364 }
365 }
366
367 return false;
368}
369
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200370/**************************************************
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200371 * Channel switching ops.
372 **************************************************/
373
Rafał Miłeckib5347062011-09-16 12:33:59 +0200374/* wlc_lcnphy_set_chanspec_tweaks */
375static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel)
376{
377 struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
378
379 b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100);
380
381 if (channel == 1 || channel == 2 || channel == 3 || channel == 4 ||
382 channel == 9 || channel == 10 || channel == 11 || channel == 12) {
383 bcma_chipco_pll_write(cc, 0x2, 0x03000c04);
384 bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0);
385 bcma_chipco_pll_write(cc, 0x4, 0x200005c0);
386
387 bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
388
389 b43_phy_write(dev, 0x942, 0);
390
391 /* b43_phy_lcn_txrx_spur_avoidance_mode(dev, false); */
392 b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00);
393 b43_phy_write(dev, 0x425, 0x5907);
394 } else {
395 bcma_chipco_pll_write(cc, 0x2, 0x03140c04);
396 bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333);
397 bcma_chipco_pll_write(cc, 0x4, 0x202c2820);
398
399 bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
400
401 b43_phy_write(dev, 0x942, 0);
402
403 /* b43_phy_lcn_txrx_spur_avoidance_mode(dev, true); */
404 b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00);
405 b43_phy_write(dev, 0x425, 0x590a);
406 }
407
408 b43_phy_set(dev, 0x44a, 0x44);
409 b43_phy_write(dev, 0x44a, 0x80);
410}
411
412/* wlc_phy_chanspec_set_lcnphy */
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200413static int b43_phy_lcn_set_channel(struct b43_wldev *dev,
414 struct ieee80211_channel *channel,
415 enum nl80211_channel_type channel_type)
416{
Rafał Miłecki0c5644b92011-09-16 12:34:00 +0200417 static const u16 sfo_cfg[14][2] = {
418 {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078},
419 {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067},
420 {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055},
421 };
422
Rafał Miłeckib5347062011-09-16 12:33:59 +0200423 b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value);
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200424
425 b43_phy_set(dev, 0x44a, 0x44);
426 b43_phy_write(dev, 0x44a, 0x80);
427
428 b43_radio_2064_channel_setup(dev);
429 mdelay(1);
430
431 b43_phy_lcn_afe_set_unset(dev);
432
Rafał Miłecki0c5644b92011-09-16 12:34:00 +0200433 b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]);
434 b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]);
435
436 if (channel->hw_value == 14) {
437 b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8);
438 b43_phy_lcn_load_tx_iir_cck_filter(dev, 3);
439 } else {
440 b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8);
441 /* brcmsmac uses filter_type 2, we follow wl with 25 */
442 b43_phy_lcn_load_tx_iir_cck_filter(dev, 25);
443 }
444 /* brcmsmac uses filter_type 2, we follow wl with 0 */
445 b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0);
446
447 b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3);
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200448
449 return 0;
450}
451
452/**************************************************
Rafał Miłeckif9286682011-08-14 23:27:28 +0200453 * Basic PHY ops.
454 **************************************************/
455
456static int b43_phy_lcn_op_allocate(struct b43_wldev *dev)
457{
458 struct b43_phy_lcn *phy_lcn;
459
460 phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL);
461 if (!phy_lcn)
462 return -ENOMEM;
463 dev->phy.lcn = phy_lcn;
464
465 return 0;
466}
467
468static void b43_phy_lcn_op_free(struct b43_wldev *dev)
469{
470 struct b43_phy *phy = &dev->phy;
471 struct b43_phy_lcn *phy_lcn = phy->lcn;
472
473 kfree(phy_lcn);
474 phy->lcn = NULL;
475}
476
477static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev)
478{
479 struct b43_phy *phy = &dev->phy;
480 struct b43_phy_lcn *phy_lcn = phy->lcn;
481
482 memset(phy_lcn, 0, sizeof(*phy_lcn));
483}
484
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200485/* wlc_phy_init_lcnphy */
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200486static int b43_phy_lcn_op_init(struct b43_wldev *dev)
487{
Rafał Miłeckib5347062011-09-16 12:33:59 +0200488 struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
489
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200490 b43_phy_set(dev, 0x44a, 0x80);
491 b43_phy_mask(dev, 0x44a, 0x7f);
492 b43_phy_set(dev, 0x6d1, 0x80);
493 b43_phy_write(dev, 0x6d0, 0x7);
494
495 b43_phy_lcn_afe_set_unset(dev);
496
497 b43_phy_write(dev, 0x60a, 0xa0);
498 b43_phy_write(dev, 0x46a, 0x19);
499 b43_phy_maskset(dev, 0x663, 0xFF00, 0x64);
500
501 b43_phy_lcn_tables_init(dev);
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200502
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200503 b43_phy_lcn_rev0_baseband_init(dev);
504 b43_phy_lcn_bu_tweaks(dev);
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200505
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200506 if (dev->phy.radio_ver == 0x2064)
507 b43_radio_2064_init(dev);
508 else
509 B43_WARN_ON(1);
510
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200511 b43_phy_lcn_sense_setup(dev);
Rafał Miłecki765b07e2011-08-28 19:59:28 +0200512
Rafał Miłeckib5347062011-09-16 12:33:59 +0200513 b43_switch_channel(dev, dev->phy.channel);
514
515 bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9);
516 bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd);
517
518 /* TODO */
519
520 b43_phy_set(dev, 0x448, 0x4000);
521 udelay(100);
522 b43_phy_mask(dev, 0x448, ~0x4000);
523
524 /* TODO */
525
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200526 return 0;
527}
528
Rafał Miłeckiba356b52011-08-14 23:27:29 +0200529static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev,
530 bool blocked)
531{
532 if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
533 b43err(dev->wl, "MAC not suspended\n");
534
535 if (blocked) {
536 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00);
537 b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00);
538
539 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00);
540 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2);
541 b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808);
542
543 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8);
544 b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8);
545 } else {
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200546 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00);
547 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808);
548 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8);
Rafał Miłeckiba356b52011-08-14 23:27:29 +0200549 }
550}
551
Rafał Miłecki7ed88522011-08-14 23:27:30 +0200552static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on)
553{
554 if (on) {
555 b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7);
556 } else {
557 b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7);
558 b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7);
559 }
560}
561
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200562static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev,
563 unsigned int new_channel)
564{
565 struct ieee80211_channel *channel = dev->wl->hw->conf.channel;
566 enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type;
567
568 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
569 if ((new_channel < 1) || (new_channel > 14))
570 return -EINVAL;
571 } else {
572 return -EINVAL;
573 }
574
575 return b43_phy_lcn_set_channel(dev, channel, channel_type);
576}
577
Rafał Miłeckif9286682011-08-14 23:27:28 +0200578static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev)
579{
580 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
581 return 1;
582 return 36;
583}
584
585static enum b43_txpwr_result
586b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
587{
588 return B43_TXPWR_RES_DONE;
589}
590
591static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev)
592{
593}
594
595/**************************************************
Rafał Miłeckif533d0f2011-08-28 14:28:43 +0200596 * R/W ops.
597 **************************************************/
598
599static u16 b43_phy_lcn_op_read(struct b43_wldev *dev, u16 reg)
600{
601 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
602 return b43_read16(dev, B43_MMIO_PHY_DATA);
603}
604
605static void b43_phy_lcn_op_write(struct b43_wldev *dev, u16 reg, u16 value)
606{
607 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
608 b43_write16(dev, B43_MMIO_PHY_DATA, value);
609}
610
611static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
612 u16 set)
613{
614 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
615 b43_write16(dev, B43_MMIO_PHY_DATA,
616 (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
617}
618
619static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg)
620{
621 /* LCN-PHY needs 0x200 for read access */
622 reg |= 0x200;
623
624 b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
625 return b43_read16(dev, B43_MMIO_RADIO24_DATA);
626}
627
628static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg,
629 u16 value)
630{
631 b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
632 b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
633}
634
635/**************************************************
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200636 * PHY ops struct.
637 **************************************************/
638
639const struct b43_phy_operations b43_phyops_lcn = {
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200640 .allocate = b43_phy_lcn_op_allocate,
641 .free = b43_phy_lcn_op_free,
642 .prepare_structs = b43_phy_lcn_op_prepare_structs,
643 .init = b43_phy_lcn_op_init,
644 .phy_read = b43_phy_lcn_op_read,
645 .phy_write = b43_phy_lcn_op_write,
646 .phy_maskset = b43_phy_lcn_op_maskset,
647 .radio_read = b43_phy_lcn_op_radio_read,
648 .radio_write = b43_phy_lcn_op_radio_write,
649 .software_rfkill = b43_phy_lcn_op_software_rfkill,
650 .switch_analog = b43_phy_lcn_op_switch_analog,
651 .switch_channel = b43_phy_lcn_op_switch_channel,
652 .get_default_chan = b43_phy_lcn_op_get_default_chan,
653 .recalc_txpower = b43_phy_lcn_op_recalc_txpower,
654 .adjust_txpower = b43_phy_lcn_op_adjust_txpower,
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200655};