blob: 06d8efc2f29fa8d29ea0b002c6d9c20fc29bc5bc [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
40/**************************************************
Rafał Miłeckidc713fb2011-08-15 18:50:56 +020041 * Radio 2064.
42 **************************************************/
43
Rafał Miłeckibce4dc42011-08-31 23:36:20 +020044/* wlc_lcnphy_radio_2064_channel_tune_4313 */
Rafał Miłecki39f7d332011-08-28 14:59:58 +020045static void b43_radio_2064_channel_setup(struct b43_wldev *dev)
46{
47 u16 save[2];
48
49 b43_radio_set(dev, 0x09d, 0x4);
50 b43_radio_write(dev, 0x09e, 0xf);
51
Rafał Miłeckicf577fc2011-08-31 23:36:21 +020052 /* Channel specific values in theory, in practice always the same */
Rafał Miłecki39f7d332011-08-28 14:59:58 +020053 b43_radio_write(dev, 0x02a, 0xb);
54 b43_radio_maskset(dev, 0x030, ~0x3, 0xa);
55 b43_radio_maskset(dev, 0x091, ~0x3, 0);
56 b43_radio_maskset(dev, 0x038, ~0xf, 0x7);
57 b43_radio_maskset(dev, 0x030, ~0xc, 0x8);
58 b43_radio_maskset(dev, 0x05e, ~0xf, 0x8);
59 b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80);
60 b43_radio_write(dev, 0x06c, 0x80);
61
62 save[0] = b43_radio_read(dev, 0x044);
63 save[1] = b43_radio_read(dev, 0x12b);
64
65 b43_radio_set(dev, 0x044, 0x7);
66 b43_radio_set(dev, 0x12b, 0xe);
67
68 /* TODO */
69
70 b43_radio_write(dev, 0x040, 0xfb);
71
72 b43_radio_write(dev, 0x041, 0x9a);
73 b43_radio_write(dev, 0x042, 0xa3);
74 b43_radio_write(dev, 0x043, 0x0c);
75
76 /* TODO */
77
78 b43_radio_set(dev, 0x044, 0x0c);
79 udelay(1);
80
81 b43_radio_write(dev, 0x044, save[0]);
82 b43_radio_write(dev, 0x12b, save[1]);
83
Rafał Miłeckicf577fc2011-08-31 23:36:21 +020084 if (dev->phy.rev == 1) {
85 /* brcmsmac uses outdated 0x3 for 0x038 */
86 b43_radio_write(dev, 0x038, 0x0);
87 b43_radio_write(dev, 0x091, 0x7);
88 }
Rafał Miłecki39f7d332011-08-28 14:59:58 +020089}
90
Rafał Miłeckibce4dc42011-08-31 23:36:20 +020091/* wlc_radio_2064_init */
Rafał Miłeckidc713fb2011-08-15 18:50:56 +020092static void b43_radio_2064_init(struct b43_wldev *dev)
93{
Rafał Miłeckicf577fc2011-08-31 23:36:21 +020094 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
95 b43_radio_write(dev, 0x09c, 0x0020);
96 b43_radio_write(dev, 0x105, 0x0008);
97 } else {
98 /* TODO */
99 }
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200100 b43_radio_write(dev, 0x032, 0x0062);
101 b43_radio_write(dev, 0x033, 0x0019);
102 b43_radio_write(dev, 0x090, 0x0010);
103 b43_radio_write(dev, 0x010, 0x0000);
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200104 if (dev->phy.rev == 1) {
105 b43_radio_write(dev, 0x060, 0x007f);
106 b43_radio_write(dev, 0x061, 0x0072);
107 b43_radio_write(dev, 0x062, 0x007f);
108 }
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200109 b43_radio_write(dev, 0x01d, 0x0002);
110 b43_radio_write(dev, 0x01e, 0x0006);
111
112 b43_phy_write(dev, 0x4ea, 0x4688);
113 b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2);
114 b43_phy_mask(dev, 0x4eb, ~0x01c0);
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200115 b43_phy_maskset(dev, 0x46a, 0xff00, 0x19);
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200116
117 b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0);
118
119 b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
120 b43_radio_set(dev, 0x004, 0x40);
121 b43_radio_set(dev, 0x120, 0x10);
122 b43_radio_set(dev, 0x078, 0x80);
123 b43_radio_set(dev, 0x129, 0x2);
124 b43_radio_set(dev, 0x057, 0x1);
125 b43_radio_set(dev, 0x05b, 0x2);
126
127 /* TODO: wait for some bit to be set */
128 b43_radio_read(dev, 0x05c);
129
130 b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
131 b43_radio_mask(dev, 0x057, (u16) ~0xff01);
132
133 b43_phy_write(dev, 0x933, 0x2d6b);
134 b43_phy_write(dev, 0x934, 0x2d6b);
135 b43_phy_write(dev, 0x935, 0x2d6b);
136 b43_phy_write(dev, 0x936, 0x2d6b);
137 b43_phy_write(dev, 0x937, 0x016b);
138
139 b43_radio_mask(dev, 0x057, (u16) ~0xff02);
140 b43_radio_write(dev, 0x0c2, 0x006f);
141}
142
143/**************************************************
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200144 * Various PHY ops
145 **************************************************/
146
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200147/* wlc_lcnphy_toggle_afe_pwdn */
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200148static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev)
149{
150 u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2);
151 u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1);
152
153 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1);
154 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1);
155
156 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1);
157 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1);
158
159 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2);
160 b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1);
161}
162
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200163/* wlc_lcnphy_clear_tx_power_offsets */
164static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev)
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200165{
166 u8 i;
167
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200168 if (1) { /* FIXME */
169 b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340);
170 for (i = 0; i < 30; i++) {
171 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
172 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
173 }
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200174 }
175
176 b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80);
177 for (i = 0; i < 64; i++) {
178 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
179 b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
180 }
181}
182
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200183/* wlc_lcnphy_rev0_baseband_init */
184static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev)
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200185{
186 b43_radio_write(dev, 0x11c, 0);
187
188 b43_phy_write(dev, 0x43b, 0);
189 b43_phy_write(dev, 0x43c, 0);
190 b43_phy_write(dev, 0x44c, 0);
191 b43_phy_write(dev, 0x4e6, 0);
192 b43_phy_write(dev, 0x4f9, 0);
193 b43_phy_write(dev, 0x4b0, 0);
194 b43_phy_write(dev, 0x938, 0);
195 b43_phy_write(dev, 0x4b0, 0);
196 b43_phy_write(dev, 0x44e, 0);
197
198 b43_phy_set(dev, 0x567, 0x03);
199
200 b43_phy_set(dev, 0x44a, 0x44);
201 b43_phy_write(dev, 0x44a, 0x80);
202
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200203 if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM))
204 ; /* TODO */
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200205 b43_phy_maskset(dev, 0x634, ~0xff, 0xc);
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200206 if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) {
207 b43_phy_maskset(dev, 0x634, ~0xff, 0xa);
208 b43_phy_write(dev, 0x910, 0x1);
209 }
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200210
211 b43_phy_write(dev, 0x910, 0x1);
212
213 b43_phy_maskset(dev, 0x448, ~0x300, 0x100);
214 b43_phy_maskset(dev, 0x608, ~0xff, 0x17);
215 b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea);
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200216}
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200217
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200218/* wlc_lcnphy_bu_tweaks */
219static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev)
220{
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200221 b43_phy_set(dev, 0x805, 0x1);
222
223 b43_phy_maskset(dev, 0x42f, ~0x7, 0x3);
224 b43_phy_maskset(dev, 0x030, ~0x7, 0x3);
225
226 b43_phy_write(dev, 0x414, 0x1e10);
227 b43_phy_write(dev, 0x415, 0x0640);
228
229 b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700);
230
231 b43_phy_set(dev, 0x44a, 0x44);
232 b43_phy_write(dev, 0x44a, 0x80);
233
234 b43_phy_maskset(dev, 0x434, ~0xff, 0xfd);
235 b43_phy_maskset(dev, 0x420, ~0xff, 0x10);
236
Rafał Miłeckicf577fc2011-08-31 23:36:21 +0200237 if (dev->dev->bus_sprom->board_rev >= 0x1204)
238 b43_radio_set(dev, 0x09b, 0xf0);
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200239
240 b43_phy_write(dev, 0x7d6, 0x0902);
241
242 /* TODO: more ops */
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200243
244 if (dev->phy.rev == 1) {
245 /* TODO: more ops */
246
247 b43_phy_lcn_clear_tx_power_offsets(dev);
248 }
Rafał Miłeckibd3bf692011-08-28 14:28:44 +0200249}
250
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200251/* wlc_lcnphy_vbat_temp_sense_setup */
252static void b43_phy_lcn_sense_setup(struct b43_wldev *dev)
Rafał Miłecki765b07e2011-08-28 19:59:28 +0200253{
254 u8 i;
255
256 u16 save_radio_regs[6][2] = {
257 { 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 },
258 { 0x025, 0 }, { 0x112, 0 },
259 };
260 u16 save_phy_regs[14][2] = {
261 { 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 },
262 { 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 },
263 { 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 },
264 { 0x40d, 0 }, { 0x4a2, 0 },
265 };
266 u16 save_radio_4a4;
267
268 for (i = 0; i < 6; i++)
269 save_radio_regs[i][1] = b43_radio_read(dev,
270 save_radio_regs[i][0]);
271 for (i = 0; i < 14; i++)
272 save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]);
273 save_radio_4a4 = b43_radio_read(dev, 0x4a4);
274
275 /* TODO: config sth */
276
277 for (i = 0; i < 6; i++)
278 b43_radio_write(dev, save_radio_regs[i][0],
279 save_radio_regs[i][1]);
280 for (i = 0; i < 14; i++)
281 b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]);
282 b43_radio_write(dev, 0x4a4, save_radio_4a4);
283}
284
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200285/**************************************************
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200286 * Channel switching ops.
287 **************************************************/
288
Rafał Miłeckib5347062011-09-16 12:33:59 +0200289/* wlc_lcnphy_set_chanspec_tweaks */
290static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel)
291{
292 struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
293
294 b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100);
295
296 if (channel == 1 || channel == 2 || channel == 3 || channel == 4 ||
297 channel == 9 || channel == 10 || channel == 11 || channel == 12) {
298 bcma_chipco_pll_write(cc, 0x2, 0x03000c04);
299 bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0);
300 bcma_chipco_pll_write(cc, 0x4, 0x200005c0);
301
302 bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
303
304 b43_phy_write(dev, 0x942, 0);
305
306 /* b43_phy_lcn_txrx_spur_avoidance_mode(dev, false); */
307 b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00);
308 b43_phy_write(dev, 0x425, 0x5907);
309 } else {
310 bcma_chipco_pll_write(cc, 0x2, 0x03140c04);
311 bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333);
312 bcma_chipco_pll_write(cc, 0x4, 0x202c2820);
313
314 bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
315
316 b43_phy_write(dev, 0x942, 0);
317
318 /* b43_phy_lcn_txrx_spur_avoidance_mode(dev, true); */
319 b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00);
320 b43_phy_write(dev, 0x425, 0x590a);
321 }
322
323 b43_phy_set(dev, 0x44a, 0x44);
324 b43_phy_write(dev, 0x44a, 0x80);
325}
326
327/* wlc_phy_chanspec_set_lcnphy */
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200328static int b43_phy_lcn_set_channel(struct b43_wldev *dev,
329 struct ieee80211_channel *channel,
330 enum nl80211_channel_type channel_type)
331{
Rafał Miłeckib5347062011-09-16 12:33:59 +0200332 b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value);
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200333
334 b43_phy_set(dev, 0x44a, 0x44);
335 b43_phy_write(dev, 0x44a, 0x80);
336
337 b43_radio_2064_channel_setup(dev);
338 mdelay(1);
339
340 b43_phy_lcn_afe_set_unset(dev);
341
342 /* TODO */
343
344 return 0;
345}
346
347/**************************************************
Rafał Miłeckif9286682011-08-14 23:27:28 +0200348 * Basic PHY ops.
349 **************************************************/
350
351static int b43_phy_lcn_op_allocate(struct b43_wldev *dev)
352{
353 struct b43_phy_lcn *phy_lcn;
354
355 phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL);
356 if (!phy_lcn)
357 return -ENOMEM;
358 dev->phy.lcn = phy_lcn;
359
360 return 0;
361}
362
363static void b43_phy_lcn_op_free(struct b43_wldev *dev)
364{
365 struct b43_phy *phy = &dev->phy;
366 struct b43_phy_lcn *phy_lcn = phy->lcn;
367
368 kfree(phy_lcn);
369 phy->lcn = NULL;
370}
371
372static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev)
373{
374 struct b43_phy *phy = &dev->phy;
375 struct b43_phy_lcn *phy_lcn = phy->lcn;
376
377 memset(phy_lcn, 0, sizeof(*phy_lcn));
378}
379
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200380/* wlc_phy_init_lcnphy */
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200381static int b43_phy_lcn_op_init(struct b43_wldev *dev)
382{
Rafał Miłeckib5347062011-09-16 12:33:59 +0200383 struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
384
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200385 b43_phy_set(dev, 0x44a, 0x80);
386 b43_phy_mask(dev, 0x44a, 0x7f);
387 b43_phy_set(dev, 0x6d1, 0x80);
388 b43_phy_write(dev, 0x6d0, 0x7);
389
390 b43_phy_lcn_afe_set_unset(dev);
391
392 b43_phy_write(dev, 0x60a, 0xa0);
393 b43_phy_write(dev, 0x46a, 0x19);
394 b43_phy_maskset(dev, 0x663, 0xFF00, 0x64);
395
396 b43_phy_lcn_tables_init(dev);
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200397
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200398 b43_phy_lcn_rev0_baseband_init(dev);
399 b43_phy_lcn_bu_tweaks(dev);
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200400
Rafał Miłeckidc713fb2011-08-15 18:50:56 +0200401 if (dev->phy.radio_ver == 0x2064)
402 b43_radio_2064_init(dev);
403 else
404 B43_WARN_ON(1);
405
Rafał Miłeckibce4dc42011-08-31 23:36:20 +0200406 b43_phy_lcn_sense_setup(dev);
Rafał Miłecki765b07e2011-08-28 19:59:28 +0200407
Rafał Miłeckib5347062011-09-16 12:33:59 +0200408 b43_switch_channel(dev, dev->phy.channel);
409
410 bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9);
411 bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd);
412
413 /* TODO */
414
415 b43_phy_set(dev, 0x448, 0x4000);
416 udelay(100);
417 b43_phy_mask(dev, 0x448, ~0x4000);
418
419 /* TODO */
420
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200421 return 0;
422}
423
Rafał Miłeckiba356b52011-08-14 23:27:29 +0200424static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev,
425 bool blocked)
426{
427 if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
428 b43err(dev->wl, "MAC not suspended\n");
429
430 if (blocked) {
431 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00);
432 b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00);
433
434 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00);
435 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2);
436 b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808);
437
438 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8);
439 b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8);
440 } else {
Rafał Miłecki78bc2462011-08-15 18:50:55 +0200441 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00);
442 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808);
443 b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8);
Rafał Miłeckiba356b52011-08-14 23:27:29 +0200444 }
445}
446
Rafał Miłecki7ed88522011-08-14 23:27:30 +0200447static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on)
448{
449 if (on) {
450 b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7);
451 } else {
452 b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7);
453 b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7);
454 }
455}
456
Rafał Miłecki39f7d332011-08-28 14:59:58 +0200457static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev,
458 unsigned int new_channel)
459{
460 struct ieee80211_channel *channel = dev->wl->hw->conf.channel;
461 enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type;
462
463 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
464 if ((new_channel < 1) || (new_channel > 14))
465 return -EINVAL;
466 } else {
467 return -EINVAL;
468 }
469
470 return b43_phy_lcn_set_channel(dev, channel, channel_type);
471}
472
Rafał Miłeckif9286682011-08-14 23:27:28 +0200473static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev)
474{
475 if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
476 return 1;
477 return 36;
478}
479
480static enum b43_txpwr_result
481b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
482{
483 return B43_TXPWR_RES_DONE;
484}
485
486static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev)
487{
488}
489
490/**************************************************
Rafał Miłeckif533d0f2011-08-28 14:28:43 +0200491 * R/W ops.
492 **************************************************/
493
494static u16 b43_phy_lcn_op_read(struct b43_wldev *dev, u16 reg)
495{
496 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
497 return b43_read16(dev, B43_MMIO_PHY_DATA);
498}
499
500static void b43_phy_lcn_op_write(struct b43_wldev *dev, u16 reg, u16 value)
501{
502 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
503 b43_write16(dev, B43_MMIO_PHY_DATA, value);
504}
505
506static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
507 u16 set)
508{
509 b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
510 b43_write16(dev, B43_MMIO_PHY_DATA,
511 (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
512}
513
514static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg)
515{
516 /* LCN-PHY needs 0x200 for read access */
517 reg |= 0x200;
518
519 b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
520 return b43_read16(dev, B43_MMIO_RADIO24_DATA);
521}
522
523static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg,
524 u16 value)
525{
526 b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
527 b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
528}
529
530/**************************************************
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200531 * PHY ops struct.
532 **************************************************/
533
534const struct b43_phy_operations b43_phyops_lcn = {
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200535 .allocate = b43_phy_lcn_op_allocate,
536 .free = b43_phy_lcn_op_free,
537 .prepare_structs = b43_phy_lcn_op_prepare_structs,
538 .init = b43_phy_lcn_op_init,
539 .phy_read = b43_phy_lcn_op_read,
540 .phy_write = b43_phy_lcn_op_write,
541 .phy_maskset = b43_phy_lcn_op_maskset,
542 .radio_read = b43_phy_lcn_op_radio_read,
543 .radio_write = b43_phy_lcn_op_radio_write,
544 .software_rfkill = b43_phy_lcn_op_software_rfkill,
545 .switch_analog = b43_phy_lcn_op_switch_analog,
546 .switch_channel = b43_phy_lcn_op_switch_channel,
547 .get_default_chan = b43_phy_lcn_op_get_default_chan,
548 .recalc_txpower = b43_phy_lcn_op_recalc_txpower,
549 .adjust_txpower = b43_phy_lcn_op_adjust_txpower,
Rafał Miłecki1d738e62011-07-07 15:25:27 +0200550};