Commit | Line | Data |
---|---|---|
b2ba99ff LC |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2008-2010 Nokia Corporation | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * version 2 as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
18 | * 02110-1301 USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #include <linux/module.h> | |
23 | #include <linux/platform_device.h> | |
24 | ||
ffeb501c LC |
25 | #include <linux/err.h> |
26 | ||
dd5512eb LC |
27 | #include <linux/wl12xx.h> |
28 | ||
b2ba99ff | 29 | #include "../wlcore/wlcore.h" |
ffeb501c | 30 | #include "../wlcore/debug.h" |
4ded91ce | 31 | #include "../wlcore/io.h" |
dd5512eb | 32 | #include "../wlcore/acx.h" |
b3b4b4b8 | 33 | #include "../wlcore/tx.h" |
cd70f6a4 | 34 | #include "../wlcore/rx.h" |
b14684a0 | 35 | #include "../wlcore/io.h" |
dd5512eb | 36 | #include "../wlcore/boot.h" |
ffeb501c | 37 | |
166b2136 | 38 | #include "wl12xx.h" |
00782136 | 39 | #include "reg.h" |
9d68d1ee LC |
40 | #include "cmd.h" |
41 | #include "acx.h" | |
10b1e8a2 | 42 | #include "debugfs.h" |
25a43d78 | 43 | |
a5d751bb LC |
44 | static char *fref_param; |
45 | static char *tcxo_param; | |
46 | ||
e87288f0 LC |
47 | static struct wlcore_conf wl12xx_conf = { |
48 | .sg = { | |
49 | .params = { | |
50 | [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10, | |
51 | [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180, | |
52 | [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10, | |
53 | [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180, | |
54 | [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10, | |
55 | [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80, | |
56 | [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10, | |
57 | [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80, | |
58 | [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8, | |
59 | [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8, | |
60 | [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20, | |
61 | [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20, | |
62 | [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20, | |
63 | [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35, | |
64 | [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16, | |
65 | [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35, | |
66 | [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32, | |
67 | [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50, | |
68 | [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28, | |
69 | [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50, | |
70 | [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10, | |
71 | [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20, | |
72 | [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75, | |
73 | [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15, | |
74 | [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27, | |
75 | [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17, | |
76 | /* active scan params */ | |
77 | [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, | |
78 | [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, | |
79 | [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, | |
80 | /* passive scan params */ | |
81 | [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800, | |
82 | [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200, | |
83 | [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, | |
84 | /* passive scan in dual antenna params */ | |
85 | [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, | |
86 | [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0, | |
87 | [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0, | |
88 | /* general params */ | |
89 | [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, | |
90 | [CONF_SG_ANTENNA_CONFIGURATION] = 0, | |
91 | [CONF_SG_BEACON_MISS_PERCENT] = 60, | |
92 | [CONF_SG_DHCP_TIME] = 5000, | |
93 | [CONF_SG_RXT] = 1200, | |
94 | [CONF_SG_TXT] = 1000, | |
95 | [CONF_SG_ADAPTIVE_RXT_TXT] = 1, | |
96 | [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, | |
97 | [CONF_SG_HV3_MAX_SERVED] = 6, | |
98 | [CONF_SG_PS_POLL_TIMEOUT] = 10, | |
99 | [CONF_SG_UPSD_TIMEOUT] = 10, | |
100 | [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, | |
101 | [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5, | |
102 | [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30, | |
103 | /* AP params */ | |
104 | [CONF_AP_BEACON_MISS_TX] = 3, | |
105 | [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10, | |
106 | [CONF_AP_BEACON_WINDOW_INTERVAL] = 2, | |
107 | [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, | |
108 | [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, | |
109 | [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, | |
110 | /* CTS Diluting params */ | |
111 | [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, | |
112 | [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, | |
113 | }, | |
114 | .state = CONF_SG_PROTECTIVE, | |
115 | }, | |
116 | .rx = { | |
117 | .rx_msdu_life_time = 512000, | |
118 | .packet_detection_threshold = 0, | |
119 | .ps_poll_timeout = 15, | |
120 | .upsd_timeout = 15, | |
121 | .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, | |
122 | .rx_cca_threshold = 0, | |
123 | .irq_blk_threshold = 0xFFFF, | |
124 | .irq_pkt_threshold = 0, | |
125 | .irq_timeout = 600, | |
126 | .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, | |
127 | }, | |
128 | .tx = { | |
129 | .tx_energy_detection = 0, | |
130 | .sta_rc_conf = { | |
131 | .enabled_rates = 0, | |
132 | .short_retry_limit = 10, | |
133 | .long_retry_limit = 10, | |
134 | .aflags = 0, | |
135 | }, | |
136 | .ac_conf_count = 4, | |
137 | .ac_conf = { | |
138 | [CONF_TX_AC_BE] = { | |
139 | .ac = CONF_TX_AC_BE, | |
140 | .cw_min = 15, | |
141 | .cw_max = 63, | |
142 | .aifsn = 3, | |
143 | .tx_op_limit = 0, | |
144 | }, | |
145 | [CONF_TX_AC_BK] = { | |
146 | .ac = CONF_TX_AC_BK, | |
147 | .cw_min = 15, | |
148 | .cw_max = 63, | |
149 | .aifsn = 7, | |
150 | .tx_op_limit = 0, | |
151 | }, | |
152 | [CONF_TX_AC_VI] = { | |
153 | .ac = CONF_TX_AC_VI, | |
154 | .cw_min = 15, | |
155 | .cw_max = 63, | |
156 | .aifsn = CONF_TX_AIFS_PIFS, | |
157 | .tx_op_limit = 3008, | |
158 | }, | |
159 | [CONF_TX_AC_VO] = { | |
160 | .ac = CONF_TX_AC_VO, | |
161 | .cw_min = 15, | |
162 | .cw_max = 63, | |
163 | .aifsn = CONF_TX_AIFS_PIFS, | |
164 | .tx_op_limit = 1504, | |
165 | }, | |
166 | }, | |
167 | .max_tx_retries = 100, | |
168 | .ap_aging_period = 300, | |
169 | .tid_conf_count = 4, | |
170 | .tid_conf = { | |
171 | [CONF_TX_AC_BE] = { | |
172 | .queue_id = CONF_TX_AC_BE, | |
173 | .channel_type = CONF_CHANNEL_TYPE_EDCF, | |
174 | .tsid = CONF_TX_AC_BE, | |
175 | .ps_scheme = CONF_PS_SCHEME_LEGACY, | |
176 | .ack_policy = CONF_ACK_POLICY_LEGACY, | |
177 | .apsd_conf = {0, 0}, | |
178 | }, | |
179 | [CONF_TX_AC_BK] = { | |
180 | .queue_id = CONF_TX_AC_BK, | |
181 | .channel_type = CONF_CHANNEL_TYPE_EDCF, | |
182 | .tsid = CONF_TX_AC_BK, | |
183 | .ps_scheme = CONF_PS_SCHEME_LEGACY, | |
184 | .ack_policy = CONF_ACK_POLICY_LEGACY, | |
185 | .apsd_conf = {0, 0}, | |
186 | }, | |
187 | [CONF_TX_AC_VI] = { | |
188 | .queue_id = CONF_TX_AC_VI, | |
189 | .channel_type = CONF_CHANNEL_TYPE_EDCF, | |
190 | .tsid = CONF_TX_AC_VI, | |
191 | .ps_scheme = CONF_PS_SCHEME_LEGACY, | |
192 | .ack_policy = CONF_ACK_POLICY_LEGACY, | |
193 | .apsd_conf = {0, 0}, | |
194 | }, | |
195 | [CONF_TX_AC_VO] = { | |
196 | .queue_id = CONF_TX_AC_VO, | |
197 | .channel_type = CONF_CHANNEL_TYPE_EDCF, | |
198 | .tsid = CONF_TX_AC_VO, | |
199 | .ps_scheme = CONF_PS_SCHEME_LEGACY, | |
200 | .ack_policy = CONF_ACK_POLICY_LEGACY, | |
201 | .apsd_conf = {0, 0}, | |
202 | }, | |
203 | }, | |
204 | .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, | |
205 | .tx_compl_timeout = 700, | |
206 | .tx_compl_threshold = 4, | |
207 | .basic_rate = CONF_HW_BIT_RATE_1MBPS, | |
208 | .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, | |
209 | .tmpl_short_retry_limit = 10, | |
210 | .tmpl_long_retry_limit = 10, | |
211 | .tx_watchdog_timeout = 5000, | |
212 | }, | |
213 | .conn = { | |
214 | .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, | |
215 | .listen_interval = 1, | |
216 | .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, | |
217 | .suspend_listen_interval = 3, | |
218 | .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, | |
219 | .bcn_filt_ie_count = 2, | |
220 | .bcn_filt_ie = { | |
221 | [0] = { | |
222 | .ie = WLAN_EID_CHANNEL_SWITCH, | |
223 | .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, | |
224 | }, | |
225 | [1] = { | |
226 | .ie = WLAN_EID_HT_OPERATION, | |
227 | .rule = CONF_BCN_RULE_PASS_ON_CHANGE, | |
228 | }, | |
229 | }, | |
7b052214 IC |
230 | .synch_fail_thold = 12, |
231 | .bss_lose_timeout = 400, | |
e87288f0 LC |
232 | .beacon_rx_timeout = 10000, |
233 | .broadcast_timeout = 20000, | |
234 | .rx_broadcast_in_ps = 1, | |
235 | .ps_poll_threshold = 10, | |
236 | .bet_enable = CONF_BET_MODE_ENABLE, | |
237 | .bet_max_consecutive = 50, | |
238 | .psm_entry_retries = 8, | |
239 | .psm_exit_retries = 16, | |
240 | .psm_entry_nullfunc_retries = 3, | |
6e066921 | 241 | .dynamic_ps_timeout = 200, |
e87288f0 LC |
242 | .forced_ps = false, |
243 | .keep_alive_interval = 55000, | |
244 | .max_listen_interval = 20, | |
245 | }, | |
246 | .itrim = { | |
247 | .enable = false, | |
248 | .timeout = 50000, | |
249 | }, | |
250 | .pm_config = { | |
251 | .host_clk_settling_time = 5000, | |
252 | .host_fast_wakeup_support = false | |
253 | }, | |
254 | .roam_trigger = { | |
255 | .trigger_pacing = 1, | |
256 | .avg_weight_rssi_beacon = 20, | |
257 | .avg_weight_rssi_data = 10, | |
258 | .avg_weight_snr_beacon = 20, | |
259 | .avg_weight_snr_data = 10, | |
260 | }, | |
261 | .scan = { | |
262 | .min_dwell_time_active = 7500, | |
263 | .max_dwell_time_active = 30000, | |
264 | .min_dwell_time_passive = 100000, | |
265 | .max_dwell_time_passive = 100000, | |
266 | .num_probe_reqs = 2, | |
267 | .split_scan_timeout = 50000, | |
268 | }, | |
269 | .sched_scan = { | |
270 | /* | |
271 | * Values are in TU/1000 but since sched scan FW command | |
272 | * params are in TUs rounding up may occur. | |
273 | */ | |
274 | .base_dwell_time = 7500, | |
275 | .max_dwell_time_delta = 22500, | |
276 | /* based on 250bits per probe @1Mbps */ | |
277 | .dwell_time_delta_per_probe = 2000, | |
278 | /* based on 250bits per probe @6Mbps (plus a bit more) */ | |
279 | .dwell_time_delta_per_probe_5 = 350, | |
280 | .dwell_time_passive = 100000, | |
281 | .dwell_time_dfs = 150000, | |
282 | .num_probe_reqs = 2, | |
283 | .rssi_threshold = -90, | |
284 | .snr_threshold = 0, | |
285 | }, | |
e87288f0 LC |
286 | .ht = { |
287 | .rx_ba_win_size = 8, | |
288 | .tx_ba_win_size = 64, | |
289 | .inactivity_timeout = 10000, | |
290 | .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP, | |
291 | }, | |
5453dc10 LC |
292 | /* |
293 | * Memory config for wl127x chips is given in the | |
294 | * wl12xx_default_priv_conf struct. The below configuration is | |
295 | * for wl128x chips. | |
296 | */ | |
297 | .mem = { | |
e87288f0 LC |
298 | .num_stations = 1, |
299 | .ssid_profiles = 1, | |
300 | .rx_block_num = 40, | |
301 | .tx_min_block_num = 40, | |
302 | .dynamic_memory = 1, | |
303 | .min_req_tx_blocks = 45, | |
304 | .min_req_rx_blocks = 22, | |
305 | .tx_min = 27, | |
306 | }, | |
307 | .fm_coex = { | |
308 | .enable = true, | |
309 | .swallow_period = 5, | |
310 | .n_divider_fref_set_1 = 0xff, /* default */ | |
311 | .n_divider_fref_set_2 = 12, | |
312 | .m_divider_fref_set_1 = 148, | |
313 | .m_divider_fref_set_2 = 0xffff, /* default */ | |
314 | .coex_pll_stabilization_time = 0xffffffff, /* default */ | |
315 | .ldo_stabilization_time = 0xffff, /* default */ | |
316 | .fm_disturbed_band_margin = 0xff, /* default */ | |
317 | .swallow_clk_diff = 0xff, /* default */ | |
318 | }, | |
319 | .rx_streaming = { | |
320 | .duration = 150, | |
321 | .queues = 0x1, | |
322 | .interval = 20, | |
323 | .always = 0, | |
324 | }, | |
325 | .fwlog = { | |
326 | .mode = WL12XX_FWLOG_ON_DEMAND, | |
327 | .mem_blocks = 2, | |
328 | .severity = 0, | |
329 | .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, | |
330 | .output = WL12XX_FWLOG_OUTPUT_HOST, | |
331 | .threshold = 0, | |
332 | }, | |
333 | .rate = { | |
334 | .rate_retry_score = 32000, | |
335 | .per_add = 8192, | |
336 | .per_th1 = 2048, | |
337 | .per_th2 = 4096, | |
338 | .max_per = 8100, | |
339 | .inverse_curiosity_factor = 5, | |
340 | .tx_fail_low_th = 4, | |
341 | .tx_fail_high_th = 10, | |
342 | .per_alpha_shift = 4, | |
343 | .per_add_shift = 13, | |
344 | .per_beta1_shift = 10, | |
345 | .per_beta2_shift = 8, | |
346 | .rate_check_up = 2, | |
347 | .rate_check_down = 12, | |
348 | .rate_retry_policy = { | |
349 | 0x00, 0x00, 0x00, 0x00, 0x00, | |
350 | 0x00, 0x00, 0x00, 0x00, 0x00, | |
351 | 0x00, 0x00, 0x00, | |
352 | }, | |
353 | }, | |
354 | .hangover = { | |
355 | .recover_time = 0, | |
356 | .hangover_period = 20, | |
357 | .dynamic_mode = 1, | |
358 | .early_termination_mode = 1, | |
359 | .max_period = 20, | |
360 | .min_period = 1, | |
361 | .increase_delta = 1, | |
362 | .decrease_delta = 2, | |
363 | .quiet_time = 4, | |
364 | .increase_time = 1, | |
365 | .window_size = 16, | |
366 | }, | |
367 | }; | |
368 | ||
5453dc10 LC |
369 | static struct wl12xx_priv_conf wl12xx_default_priv_conf = { |
370 | .rf = { | |
371 | .tx_per_channel_power_compensation_2 = { | |
372 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
373 | }, | |
374 | .tx_per_channel_power_compensation_5 = { | |
375 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
376 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
377 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
378 | }, | |
379 | }, | |
380 | .mem_wl127x = { | |
381 | .num_stations = 1, | |
382 | .ssid_profiles = 1, | |
383 | .rx_block_num = 70, | |
384 | .tx_min_block_num = 40, | |
385 | .dynamic_memory = 1, | |
386 | .min_req_tx_blocks = 100, | |
387 | .min_req_rx_blocks = 22, | |
388 | .tx_min = 27, | |
389 | }, | |
390 | ||
391 | }; | |
e87288f0 | 392 | |
3edab305 AN |
393 | #define WL12XX_TX_HW_BLOCK_SPARE_DEFAULT 1 |
394 | #define WL12XX_TX_HW_BLOCK_GEM_SPARE 2 | |
b3b4b4b8 | 395 | #define WL12XX_TX_HW_BLOCK_SIZE 252 |
3edab305 | 396 | |
43a8bc5a AN |
397 | static const u8 wl12xx_rate_to_idx_2ghz[] = { |
398 | /* MCS rates are used only with 11n */ | |
399 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */ | |
400 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */ | |
401 | 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */ | |
402 | 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */ | |
403 | 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */ | |
404 | 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */ | |
405 | 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */ | |
406 | 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */ | |
407 | 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */ | |
408 | ||
409 | 11, /* WL12XX_CONF_HW_RXTX_RATE_54 */ | |
410 | 10, /* WL12XX_CONF_HW_RXTX_RATE_48 */ | |
411 | 9, /* WL12XX_CONF_HW_RXTX_RATE_36 */ | |
412 | 8, /* WL12XX_CONF_HW_RXTX_RATE_24 */ | |
413 | ||
414 | /* TI-specific rate */ | |
415 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */ | |
416 | ||
417 | 7, /* WL12XX_CONF_HW_RXTX_RATE_18 */ | |
418 | 6, /* WL12XX_CONF_HW_RXTX_RATE_12 */ | |
419 | 3, /* WL12XX_CONF_HW_RXTX_RATE_11 */ | |
420 | 5, /* WL12XX_CONF_HW_RXTX_RATE_9 */ | |
421 | 4, /* WL12XX_CONF_HW_RXTX_RATE_6 */ | |
422 | 2, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */ | |
423 | 1, /* WL12XX_CONF_HW_RXTX_RATE_2 */ | |
424 | 0 /* WL12XX_CONF_HW_RXTX_RATE_1 */ | |
425 | }; | |
426 | ||
427 | static const u8 wl12xx_rate_to_idx_5ghz[] = { | |
428 | /* MCS rates are used only with 11n */ | |
429 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI */ | |
430 | 7, /* WL12XX_CONF_HW_RXTX_RATE_MCS7 */ | |
431 | 6, /* WL12XX_CONF_HW_RXTX_RATE_MCS6 */ | |
432 | 5, /* WL12XX_CONF_HW_RXTX_RATE_MCS5 */ | |
433 | 4, /* WL12XX_CONF_HW_RXTX_RATE_MCS4 */ | |
434 | 3, /* WL12XX_CONF_HW_RXTX_RATE_MCS3 */ | |
435 | 2, /* WL12XX_CONF_HW_RXTX_RATE_MCS2 */ | |
436 | 1, /* WL12XX_CONF_HW_RXTX_RATE_MCS1 */ | |
437 | 0, /* WL12XX_CONF_HW_RXTX_RATE_MCS0 */ | |
438 | ||
439 | 7, /* WL12XX_CONF_HW_RXTX_RATE_54 */ | |
440 | 6, /* WL12XX_CONF_HW_RXTX_RATE_48 */ | |
441 | 5, /* WL12XX_CONF_HW_RXTX_RATE_36 */ | |
442 | 4, /* WL12XX_CONF_HW_RXTX_RATE_24 */ | |
443 | ||
444 | /* TI-specific rate */ | |
445 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_22 */ | |
446 | ||
447 | 3, /* WL12XX_CONF_HW_RXTX_RATE_18 */ | |
448 | 2, /* WL12XX_CONF_HW_RXTX_RATE_12 */ | |
449 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_11 */ | |
450 | 1, /* WL12XX_CONF_HW_RXTX_RATE_9 */ | |
451 | 0, /* WL12XX_CONF_HW_RXTX_RATE_6 */ | |
452 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_5_5 */ | |
453 | CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL12XX_CONF_HW_RXTX_RATE_2 */ | |
454 | CONF_HW_RXTX_RATE_UNSUPPORTED /* WL12XX_CONF_HW_RXTX_RATE_1 */ | |
455 | }; | |
456 | ||
457 | static const u8 *wl12xx_band_rate_to_idx[] = { | |
458 | [IEEE80211_BAND_2GHZ] = wl12xx_rate_to_idx_2ghz, | |
459 | [IEEE80211_BAND_5GHZ] = wl12xx_rate_to_idx_5ghz | |
460 | }; | |
461 | ||
462 | enum wl12xx_hw_rates { | |
463 | WL12XX_CONF_HW_RXTX_RATE_MCS7_SGI = 0, | |
464 | WL12XX_CONF_HW_RXTX_RATE_MCS7, | |
465 | WL12XX_CONF_HW_RXTX_RATE_MCS6, | |
466 | WL12XX_CONF_HW_RXTX_RATE_MCS5, | |
467 | WL12XX_CONF_HW_RXTX_RATE_MCS4, | |
468 | WL12XX_CONF_HW_RXTX_RATE_MCS3, | |
469 | WL12XX_CONF_HW_RXTX_RATE_MCS2, | |
470 | WL12XX_CONF_HW_RXTX_RATE_MCS1, | |
471 | WL12XX_CONF_HW_RXTX_RATE_MCS0, | |
472 | WL12XX_CONF_HW_RXTX_RATE_54, | |
473 | WL12XX_CONF_HW_RXTX_RATE_48, | |
474 | WL12XX_CONF_HW_RXTX_RATE_36, | |
475 | WL12XX_CONF_HW_RXTX_RATE_24, | |
476 | WL12XX_CONF_HW_RXTX_RATE_22, | |
477 | WL12XX_CONF_HW_RXTX_RATE_18, | |
478 | WL12XX_CONF_HW_RXTX_RATE_12, | |
479 | WL12XX_CONF_HW_RXTX_RATE_11, | |
480 | WL12XX_CONF_HW_RXTX_RATE_9, | |
481 | WL12XX_CONF_HW_RXTX_RATE_6, | |
482 | WL12XX_CONF_HW_RXTX_RATE_5_5, | |
483 | WL12XX_CONF_HW_RXTX_RATE_2, | |
484 | WL12XX_CONF_HW_RXTX_RATE_1, | |
485 | WL12XX_CONF_HW_RXTX_RATE_MAX, | |
486 | }; | |
3edab305 | 487 | |
25a43d78 LC |
488 | static struct wlcore_partition_set wl12xx_ptable[PART_TABLE_LEN] = { |
489 | [PART_DOWN] = { | |
490 | .mem = { | |
491 | .start = 0x00000000, | |
492 | .size = 0x000177c0 | |
493 | }, | |
494 | .reg = { | |
495 | .start = REGISTERS_BASE, | |
496 | .size = 0x00008800 | |
497 | }, | |
498 | .mem2 = { | |
499 | .start = 0x00000000, | |
500 | .size = 0x00000000 | |
501 | }, | |
502 | .mem3 = { | |
503 | .start = 0x00000000, | |
504 | .size = 0x00000000 | |
505 | }, | |
506 | }, | |
507 | ||
00782136 LC |
508 | [PART_BOOT] = { /* in wl12xx we can use a mix of work and down |
509 | * partition here */ | |
510 | .mem = { | |
511 | .start = 0x00040000, | |
512 | .size = 0x00014fc0 | |
513 | }, | |
514 | .reg = { | |
515 | .start = REGISTERS_BASE, | |
516 | .size = 0x00008800 | |
517 | }, | |
518 | .mem2 = { | |
519 | .start = 0x00000000, | |
520 | .size = 0x00000000 | |
521 | }, | |
522 | .mem3 = { | |
523 | .start = 0x00000000, | |
524 | .size = 0x00000000 | |
525 | }, | |
526 | }, | |
527 | ||
25a43d78 LC |
528 | [PART_WORK] = { |
529 | .mem = { | |
530 | .start = 0x00040000, | |
531 | .size = 0x00014fc0 | |
532 | }, | |
533 | .reg = { | |
534 | .start = REGISTERS_BASE, | |
535 | .size = 0x0000a000 | |
536 | }, | |
537 | .mem2 = { | |
538 | .start = 0x003004f8, | |
539 | .size = 0x00000004 | |
540 | }, | |
541 | .mem3 = { | |
542 | .start = 0x00040404, | |
543 | .size = 0x00000000 | |
544 | }, | |
545 | }, | |
546 | ||
547 | [PART_DRPW] = { | |
548 | .mem = { | |
549 | .start = 0x00040000, | |
550 | .size = 0x00014fc0 | |
551 | }, | |
552 | .reg = { | |
553 | .start = DRPW_BASE, | |
554 | .size = 0x00006000 | |
555 | }, | |
556 | .mem2 = { | |
557 | .start = 0x00000000, | |
558 | .size = 0x00000000 | |
559 | }, | |
560 | .mem3 = { | |
561 | .start = 0x00000000, | |
562 | .size = 0x00000000 | |
563 | } | |
564 | } | |
565 | }; | |
566 | ||
00782136 LC |
567 | static const int wl12xx_rtable[REG_TABLE_LEN] = { |
568 | [REG_ECPU_CONTROL] = WL12XX_REG_ECPU_CONTROL, | |
569 | [REG_INTERRUPT_NO_CLEAR] = WL12XX_REG_INTERRUPT_NO_CLEAR, | |
570 | [REG_INTERRUPT_ACK] = WL12XX_REG_INTERRUPT_ACK, | |
571 | [REG_COMMAND_MAILBOX_PTR] = WL12XX_REG_COMMAND_MAILBOX_PTR, | |
572 | [REG_EVENT_MAILBOX_PTR] = WL12XX_REG_EVENT_MAILBOX_PTR, | |
573 | [REG_INTERRUPT_TRIG] = WL12XX_REG_INTERRUPT_TRIG, | |
574 | [REG_INTERRUPT_MASK] = WL12XX_REG_INTERRUPT_MASK, | |
575 | [REG_PC_ON_RECOVERY] = WL12XX_SCR_PAD4, | |
576 | [REG_CHIP_ID_B] = WL12XX_CHIP_ID_B, | |
577 | [REG_CMD_MBOX_ADDRESS] = WL12XX_CMD_MBOX_ADDRESS, | |
578 | ||
579 | /* data access memory addresses, used with partition translation */ | |
580 | [REG_SLV_MEM_DATA] = WL1271_SLV_MEM_DATA, | |
581 | [REG_SLV_REG_DATA] = WL1271_SLV_REG_DATA, | |
582 | ||
583 | /* raw data access memory addresses */ | |
584 | [REG_RAW_FW_STATUS_ADDR] = FW_STATUS_ADDR, | |
585 | }; | |
586 | ||
6f7dd16c LC |
587 | /* TODO: maybe move to a new header file? */ |
588 | #define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin" | |
589 | #define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin" | |
590 | #define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" | |
591 | ||
592 | #define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin" | |
593 | #define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin" | |
594 | #define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" | |
595 | ||
b14684a0 LC |
596 | static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) |
597 | { | |
598 | if (wl->chip.id != CHIP_ID_1283_PG20) { | |
599 | struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; | |
4b4887e9 | 600 | struct wl127x_rx_mem_pool_addr rx_mem_addr; |
b14684a0 LC |
601 | |
602 | /* | |
603 | * Choose the block we want to read | |
604 | * For aggregated packets, only the first memory block | |
605 | * should be retrieved. The FW takes care of the rest. | |
606 | */ | |
607 | u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK; | |
608 | ||
609 | rx_mem_addr.addr = (mem_block << 8) + | |
610 | le32_to_cpu(wl_mem_map->packet_memory_pool_start); | |
611 | ||
612 | rx_mem_addr.addr_extra = rx_mem_addr.addr + 4; | |
613 | ||
614 | wl1271_write(wl, WL1271_SLV_REG_DATA, | |
615 | &rx_mem_addr, sizeof(rx_mem_addr), false); | |
616 | } | |
617 | } | |
618 | ||
6f7dd16c LC |
619 | static int wl12xx_identify_chip(struct wl1271 *wl) |
620 | { | |
621 | int ret = 0; | |
622 | ||
623 | switch (wl->chip.id) { | |
624 | case CHIP_ID_1271_PG10: | |
625 | wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", | |
626 | wl->chip.id); | |
627 | ||
f83985bb | 628 | wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; |
6f7dd16c LC |
629 | wl->sr_fw_name = WL127X_FW_NAME_SINGLE; |
630 | wl->mr_fw_name = WL127X_FW_NAME_MULTI; | |
5453dc10 LC |
631 | memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, |
632 | sizeof(wl->conf.mem)); | |
b14684a0 LC |
633 | |
634 | /* read data preparation is only needed by wl127x */ | |
635 | wl->ops->prepare_read = wl127x_prepare_read; | |
636 | ||
6f7dd16c LC |
637 | break; |
638 | ||
639 | case CHIP_ID_1271_PG20: | |
640 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", | |
641 | wl->chip.id); | |
642 | ||
f83985bb | 643 | wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; |
6f7dd16c LC |
644 | wl->plt_fw_name = WL127X_PLT_FW_NAME; |
645 | wl->sr_fw_name = WL127X_FW_NAME_SINGLE; | |
646 | wl->mr_fw_name = WL127X_FW_NAME_MULTI; | |
5453dc10 LC |
647 | memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, |
648 | sizeof(wl->conf.mem)); | |
b14684a0 LC |
649 | |
650 | /* read data preparation is only needed by wl127x */ | |
651 | wl->ops->prepare_read = wl127x_prepare_read; | |
652 | ||
6f7dd16c LC |
653 | break; |
654 | ||
655 | case CHIP_ID_1283_PG20: | |
656 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)", | |
657 | wl->chip.id); | |
658 | wl->plt_fw_name = WL128X_PLT_FW_NAME; | |
659 | wl->sr_fw_name = WL128X_FW_NAME_SINGLE; | |
660 | wl->mr_fw_name = WL128X_FW_NAME_MULTI; | |
b5d6d9b2 LC |
661 | |
662 | /* wl128x requires TX blocksize alignment */ | |
663 | wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; | |
664 | ||
6f7dd16c LC |
665 | break; |
666 | case CHIP_ID_1283_PG10: | |
667 | default: | |
668 | wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); | |
669 | ret = -ENODEV; | |
670 | goto out; | |
671 | } | |
672 | ||
673 | out: | |
674 | return ret; | |
675 | } | |
676 | ||
dd5512eb LC |
677 | static void wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) |
678 | { | |
679 | /* write address >> 1 + 0x30000 to OCP_POR_CTR */ | |
680 | addr = (addr >> 1) + 0x30000; | |
681 | wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr); | |
682 | ||
683 | /* write value to OCP_POR_WDATA */ | |
684 | wl1271_write32(wl, WL12XX_OCP_DATA_WRITE, val); | |
685 | ||
686 | /* write 1 to OCP_CMD */ | |
687 | wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE); | |
688 | } | |
689 | ||
690 | static u16 wl12xx_top_reg_read(struct wl1271 *wl, int addr) | |
691 | { | |
692 | u32 val; | |
693 | int timeout = OCP_CMD_LOOP; | |
694 | ||
695 | /* write address >> 1 + 0x30000 to OCP_POR_CTR */ | |
696 | addr = (addr >> 1) + 0x30000; | |
697 | wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr); | |
698 | ||
699 | /* write 2 to OCP_CMD */ | |
700 | wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ); | |
701 | ||
702 | /* poll for data ready */ | |
703 | do { | |
704 | val = wl1271_read32(wl, WL12XX_OCP_DATA_READ); | |
705 | } while (!(val & OCP_READY_MASK) && --timeout); | |
706 | ||
707 | if (!timeout) { | |
708 | wl1271_warning("Top register access timed out."); | |
709 | return 0xffff; | |
710 | } | |
711 | ||
712 | /* check data status and return if OK */ | |
713 | if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) | |
714 | return val & 0xffff; | |
715 | else { | |
716 | wl1271_warning("Top register access returned error."); | |
717 | return 0xffff; | |
718 | } | |
719 | } | |
720 | ||
721 | static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) | |
722 | { | |
723 | u16 spare_reg; | |
724 | ||
725 | /* Mask bits [2] & [8:4] in the sys_clk_cfg register */ | |
726 | spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG); | |
727 | if (spare_reg == 0xFFFF) | |
728 | return -EFAULT; | |
729 | spare_reg |= (BIT(3) | BIT(5) | BIT(6)); | |
730 | wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); | |
731 | ||
732 | /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */ | |
733 | wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG, | |
734 | WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); | |
735 | ||
736 | /* Delay execution for 15msec, to let the HW settle */ | |
737 | mdelay(15); | |
738 | ||
739 | return 0; | |
740 | } | |
741 | ||
742 | static bool wl128x_is_tcxo_valid(struct wl1271 *wl) | |
743 | { | |
744 | u16 tcxo_detection; | |
745 | ||
746 | tcxo_detection = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG); | |
747 | if (tcxo_detection & TCXO_DET_FAILED) | |
748 | return false; | |
749 | ||
750 | return true; | |
751 | } | |
752 | ||
753 | static bool wl128x_is_fref_valid(struct wl1271 *wl) | |
754 | { | |
755 | u16 fref_detection; | |
756 | ||
757 | fref_detection = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG); | |
758 | if (fref_detection & FREF_CLK_DETECT_FAIL) | |
759 | return false; | |
760 | ||
761 | return true; | |
762 | } | |
763 | ||
764 | static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl) | |
765 | { | |
766 | wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); | |
767 | wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); | |
768 | wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL); | |
769 | ||
770 | return 0; | |
771 | } | |
772 | ||
773 | static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) | |
774 | { | |
775 | u16 spare_reg; | |
776 | u16 pll_config; | |
777 | u8 input_freq; | |
a5d751bb | 778 | struct wl12xx_priv *priv = wl->priv; |
dd5512eb LC |
779 | |
780 | /* Mask bits [3:1] in the sys_clk_cfg register */ | |
781 | spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG); | |
782 | if (spare_reg == 0xFFFF) | |
783 | return -EFAULT; | |
784 | spare_reg |= BIT(2); | |
785 | wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); | |
786 | ||
787 | /* Handle special cases of the TCXO clock */ | |
a5d751bb LC |
788 | if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || |
789 | priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6) | |
dd5512eb LC |
790 | return wl128x_manually_configure_mcs_pll(wl); |
791 | ||
792 | /* Set the input frequency according to the selected clock source */ | |
793 | input_freq = (clk & 1) + 1; | |
794 | ||
795 | pll_config = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG); | |
796 | if (pll_config == 0xFFFF) | |
797 | return -EFAULT; | |
798 | pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); | |
799 | pll_config |= MCS_PLL_ENABLE_HP; | |
800 | wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); | |
801 | ||
802 | return 0; | |
803 | } | |
804 | ||
805 | /* | |
806 | * WL128x has two clocks input - TCXO and FREF. | |
807 | * TCXO is the main clock of the device, while FREF is used to sync | |
808 | * between the GPS and the cellular modem. | |
809 | * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used | |
810 | * as the WLAN/BT main clock. | |
811 | */ | |
812 | static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) | |
813 | { | |
a5d751bb | 814 | struct wl12xx_priv *priv = wl->priv; |
dd5512eb LC |
815 | u16 sys_clk_cfg; |
816 | ||
817 | /* For XTAL-only modes, FREF will be used after switching from TCXO */ | |
a5d751bb LC |
818 | if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL || |
819 | priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) { | |
dd5512eb LC |
820 | if (!wl128x_switch_tcxo_to_fref(wl)) |
821 | return -EINVAL; | |
822 | goto fref_clk; | |
823 | } | |
824 | ||
825 | /* Query the HW, to determine which clock source we should use */ | |
826 | sys_clk_cfg = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG); | |
827 | if (sys_clk_cfg == 0xFFFF) | |
828 | return -EINVAL; | |
829 | if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF) | |
830 | goto fref_clk; | |
831 | ||
832 | /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */ | |
a5d751bb LC |
833 | if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || |
834 | priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { | |
dd5512eb LC |
835 | if (!wl128x_switch_tcxo_to_fref(wl)) |
836 | return -EINVAL; | |
837 | goto fref_clk; | |
838 | } | |
839 | ||
840 | /* TCXO clock is selected */ | |
841 | if (!wl128x_is_tcxo_valid(wl)) | |
842 | return -EINVAL; | |
a5d751bb | 843 | *selected_clock = priv->tcxo_clock; |
dd5512eb LC |
844 | goto config_mcs_pll; |
845 | ||
846 | fref_clk: | |
847 | /* FREF clock is selected */ | |
848 | if (!wl128x_is_fref_valid(wl)) | |
849 | return -EINVAL; | |
a5d751bb | 850 | *selected_clock = priv->ref_clock; |
dd5512eb LC |
851 | |
852 | config_mcs_pll: | |
853 | return wl128x_configure_mcs_pll(wl, *selected_clock); | |
854 | } | |
855 | ||
856 | static int wl127x_boot_clk(struct wl1271 *wl) | |
857 | { | |
a5d751bb | 858 | struct wl12xx_priv *priv = wl->priv; |
dd5512eb LC |
859 | u32 pause; |
860 | u32 clk; | |
861 | ||
862 | if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3) | |
863 | wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION; | |
864 | ||
a5d751bb LC |
865 | if (priv->ref_clock == CONF_REF_CLK_19_2_E || |
866 | priv->ref_clock == CONF_REF_CLK_38_4_E || | |
867 | priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL) | |
dd5512eb LC |
868 | /* ref clk: 19.2/38.4/38.4-XTAL */ |
869 | clk = 0x3; | |
a5d751bb LC |
870 | else if (priv->ref_clock == CONF_REF_CLK_26_E || |
871 | priv->ref_clock == CONF_REF_CLK_52_E) | |
dd5512eb LC |
872 | /* ref clk: 26/52 */ |
873 | clk = 0x5; | |
874 | else | |
875 | return -EINVAL; | |
876 | ||
a5d751bb | 877 | if (priv->ref_clock != CONF_REF_CLK_19_2_E) { |
dd5512eb LC |
878 | u16 val; |
879 | /* Set clock type (open drain) */ | |
880 | val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE); | |
881 | val &= FREF_CLK_TYPE_BITS; | |
882 | wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val); | |
883 | ||
884 | /* Set clock pull mode (no pull) */ | |
885 | val = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL); | |
886 | val |= NO_PULL; | |
887 | wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val); | |
888 | } else { | |
889 | u16 val; | |
890 | /* Set clock polarity */ | |
891 | val = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY); | |
892 | val &= FREF_CLK_POLARITY_BITS; | |
893 | val |= CLK_REQ_OUTN_SEL; | |
894 | wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); | |
895 | } | |
896 | ||
897 | wl1271_write32(wl, WL12XX_PLL_PARAMETERS, clk); | |
898 | ||
899 | pause = wl1271_read32(wl, WL12XX_PLL_PARAMETERS); | |
900 | ||
901 | wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); | |
902 | ||
903 | pause &= ~(WU_COUNTER_PAUSE_VAL); | |
904 | pause |= WU_COUNTER_PAUSE_VAL; | |
905 | wl1271_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause); | |
906 | ||
907 | return 0; | |
908 | } | |
909 | ||
910 | static int wl1271_boot_soft_reset(struct wl1271 *wl) | |
911 | { | |
912 | unsigned long timeout; | |
913 | u32 boot_data; | |
914 | ||
915 | /* perform soft reset */ | |
916 | wl1271_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); | |
917 | ||
918 | /* SOFT_RESET is self clearing */ | |
919 | timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); | |
920 | while (1) { | |
921 | boot_data = wl1271_read32(wl, WL12XX_SLV_SOFT_RESET); | |
922 | wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); | |
923 | if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) | |
924 | break; | |
925 | ||
926 | if (time_after(jiffies, timeout)) { | |
927 | /* 1.2 check pWhalBus->uSelfClearTime if the | |
928 | * timeout was reached */ | |
929 | wl1271_error("soft reset timeout"); | |
930 | return -1; | |
931 | } | |
932 | ||
933 | udelay(SOFT_RESET_STALL_TIME); | |
934 | } | |
935 | ||
936 | /* disable Rx/Tx */ | |
937 | wl1271_write32(wl, WL12XX_ENABLE, 0x0); | |
938 | ||
939 | /* disable auto calibration on start*/ | |
940 | wl1271_write32(wl, WL12XX_SPARE_A2, 0xffff); | |
941 | ||
942 | return 0; | |
943 | } | |
944 | ||
945 | static int wl12xx_pre_boot(struct wl1271 *wl) | |
946 | { | |
a5d751bb | 947 | struct wl12xx_priv *priv = wl->priv; |
dd5512eb LC |
948 | int ret = 0; |
949 | u32 clk; | |
950 | int selected_clock = -1; | |
951 | ||
952 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
953 | ret = wl128x_boot_clk(wl, &selected_clock); | |
954 | if (ret < 0) | |
955 | goto out; | |
956 | } else { | |
957 | ret = wl127x_boot_clk(wl); | |
958 | if (ret < 0) | |
959 | goto out; | |
960 | } | |
961 | ||
962 | /* Continue the ELP wake up sequence */ | |
963 | wl1271_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); | |
964 | udelay(500); | |
965 | ||
966 | wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); | |
967 | ||
968 | /* Read-modify-write DRPW_SCRATCH_START register (see next state) | |
969 | to be used by DRPw FW. The RTRIM value will be added by the FW | |
970 | before taking DRPw out of reset */ | |
971 | ||
972 | clk = wl1271_read32(wl, WL12XX_DRPW_SCRATCH_START); | |
973 | ||
974 | wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); | |
975 | ||
976 | if (wl->chip.id == CHIP_ID_1283_PG20) | |
977 | clk |= ((selected_clock & 0x3) << 1) << 4; | |
978 | else | |
a5d751bb | 979 | clk |= (priv->ref_clock << 1) << 4; |
dd5512eb LC |
980 | |
981 | wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk); | |
982 | ||
983 | wlcore_set_partition(wl, &wl->ptable[PART_WORK]); | |
984 | ||
985 | /* Disable interrupts */ | |
986 | wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); | |
987 | ||
988 | ret = wl1271_boot_soft_reset(wl); | |
989 | if (ret < 0) | |
990 | goto out; | |
991 | ||
992 | out: | |
993 | return ret; | |
994 | } | |
995 | ||
996 | static void wl12xx_pre_upload(struct wl1271 *wl) | |
997 | { | |
998 | u32 tmp; | |
999 | ||
1000 | /* write firmware's last address (ie. it's length) to | |
1001 | * ACX_EEPROMLESS_IND_REG */ | |
1002 | wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); | |
1003 | ||
1004 | wl1271_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND); | |
1005 | ||
1006 | tmp = wlcore_read_reg(wl, REG_CHIP_ID_B); | |
1007 | ||
1008 | wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); | |
1009 | ||
1010 | /* 6. read the EEPROM parameters */ | |
1011 | tmp = wl1271_read32(wl, WL12XX_SCR_PAD2); | |
1012 | ||
1013 | /* WL1271: The reference driver skips steps 7 to 10 (jumps directly | |
1014 | * to upload_fw) */ | |
1015 | ||
1016 | if (wl->chip.id == CHIP_ID_1283_PG20) | |
1017 | wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); | |
1018 | } | |
1019 | ||
1020 | static void wl12xx_enable_interrupts(struct wl1271 *wl) | |
1021 | { | |
1022 | u32 polarity; | |
1023 | ||
1024 | polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY); | |
1025 | ||
1026 | /* We use HIGH polarity, so unset the LOW bit */ | |
1027 | polarity &= ~POLARITY_LOW; | |
1028 | wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity); | |
1029 | ||
1030 | wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); | |
1031 | ||
1032 | wlcore_enable_interrupts(wl); | |
1033 | wlcore_write_reg(wl, REG_INTERRUPT_MASK, | |
1034 | WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); | |
1035 | ||
1036 | wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL); | |
1037 | } | |
1038 | ||
1039 | static int wl12xx_boot(struct wl1271 *wl) | |
1040 | { | |
1041 | int ret; | |
1042 | ||
1043 | ret = wl12xx_pre_boot(wl); | |
1044 | if (ret < 0) | |
1045 | goto out; | |
1046 | ||
1047 | ret = wlcore_boot_upload_nvs(wl); | |
1048 | if (ret < 0) | |
1049 | goto out; | |
1050 | ||
1051 | wl12xx_pre_upload(wl); | |
1052 | ||
1053 | ret = wlcore_boot_upload_firmware(wl); | |
1054 | if (ret < 0) | |
1055 | goto out; | |
1056 | ||
1057 | ret = wlcore_boot_run_firmware(wl); | |
1058 | if (ret < 0) | |
1059 | goto out; | |
1060 | ||
1061 | wl12xx_enable_interrupts(wl); | |
1062 | ||
1063 | out: | |
1064 | return ret; | |
1065 | } | |
1066 | ||
5d10b195 AN |
1067 | static void wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, |
1068 | void *buf, size_t len) | |
f16ff758 | 1069 | { |
5d10b195 | 1070 | wl1271_write(wl, cmd_box_addr, buf, len, false); |
f16ff758 LC |
1071 | wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD); |
1072 | } | |
1073 | ||
1074 | static void wl12xx_ack_event(struct wl1271 *wl) | |
1075 | { | |
1076 | wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_EVENT_ACK); | |
1077 | } | |
1078 | ||
b3b4b4b8 AN |
1079 | static u32 wl12xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) |
1080 | { | |
1081 | u32 blk_size = WL12XX_TX_HW_BLOCK_SIZE; | |
1082 | u32 align_len = wlcore_calc_packet_alignment(wl, len); | |
1083 | ||
1084 | return (align_len + blk_size - 1) / blk_size + spare_blks; | |
1085 | } | |
1086 | ||
4a3b97ee AN |
1087 | static void |
1088 | wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, | |
1089 | u32 blks, u32 spare_blks) | |
1090 | { | |
1091 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
1092 | desc->wl128x_mem.total_mem_blocks = blks; | |
1093 | } else { | |
1094 | desc->wl127x_mem.extra_blocks = spare_blks; | |
1095 | desc->wl127x_mem.total_mem_blocks = blks; | |
1096 | } | |
1097 | } | |
1098 | ||
6f266e91 AN |
1099 | static void |
1100 | wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, | |
1101 | struct sk_buff *skb) | |
1102 | { | |
1103 | u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len); | |
1104 | ||
1105 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
1106 | desc->wl128x_mem.extra_bytes = aligned_len - skb->len; | |
1107 | desc->length = cpu_to_le16(aligned_len >> 2); | |
1108 | ||
1109 | wl1271_debug(DEBUG_TX, | |
1110 | "tx_fill_hdr: hlid: %d len: %d life: %d mem: %d extra: %d", | |
1111 | desc->hlid, | |
1112 | le16_to_cpu(desc->length), | |
1113 | le16_to_cpu(desc->life_time), | |
1114 | desc->wl128x_mem.total_mem_blocks, | |
1115 | desc->wl128x_mem.extra_bytes); | |
1116 | } else { | |
1117 | /* calculate number of padding bytes */ | |
1118 | int pad = aligned_len - skb->len; | |
1119 | desc->tx_attr |= | |
1120 | cpu_to_le16(pad << TX_HW_ATTR_OFST_LAST_WORD_PAD); | |
1121 | ||
1122 | /* Store the aligned length in terms of words */ | |
1123 | desc->length = cpu_to_le16(aligned_len >> 2); | |
1124 | ||
1125 | wl1271_debug(DEBUG_TX, | |
1126 | "tx_fill_hdr: pad: %d hlid: %d len: %d life: %d mem: %d", | |
1127 | pad, desc->hlid, | |
1128 | le16_to_cpu(desc->length), | |
1129 | le16_to_cpu(desc->life_time), | |
1130 | desc->wl127x_mem.total_mem_blocks); | |
1131 | } | |
1132 | } | |
1133 | ||
cd70f6a4 AN |
1134 | static enum wl_rx_buf_align |
1135 | wl12xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) | |
1136 | { | |
1137 | if (rx_desc & RX_BUF_UNALIGNED_PAYLOAD) | |
1138 | return WLCORE_RX_BUF_UNALIGNED; | |
1139 | ||
1140 | return WLCORE_RX_BUF_ALIGNED; | |
1141 | } | |
1142 | ||
4158149c AN |
1143 | static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, |
1144 | u32 data_len) | |
1145 | { | |
1146 | struct wl1271_rx_descriptor *desc = rx_data; | |
1147 | ||
1148 | /* invalid packet */ | |
1149 | if (data_len < sizeof(*desc) || | |
1150 | data_len < sizeof(*desc) + desc->pad_len) | |
1151 | return 0; | |
1152 | ||
1153 | return data_len - sizeof(*desc) - desc->pad_len; | |
1154 | } | |
1155 | ||
53d67a50 AN |
1156 | static void wl12xx_tx_delayed_compl(struct wl1271 *wl) |
1157 | { | |
0afd04e5 AN |
1158 | if (wl->fw_status_1->tx_results_counter == |
1159 | (wl->tx_results_count & 0xff)) | |
53d67a50 AN |
1160 | return; |
1161 | ||
1162 | wl1271_tx_complete(wl); | |
1163 | } | |
1164 | ||
9d68d1ee LC |
1165 | static int wl12xx_hw_init(struct wl1271 *wl) |
1166 | { | |
1167 | int ret; | |
1168 | ||
1169 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
1170 | u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; | |
1171 | ||
1172 | ret = wl128x_cmd_general_parms(wl); | |
1173 | if (ret < 0) | |
1174 | goto out; | |
1175 | ret = wl128x_cmd_radio_parms(wl); | |
1176 | if (ret < 0) | |
1177 | goto out; | |
1178 | ||
1179 | if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) | |
1180 | /* Enable SDIO padding */ | |
1181 | host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; | |
1182 | ||
1183 | /* Must be before wl1271_acx_init_mem_config() */ | |
1184 | ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap); | |
1185 | if (ret < 0) | |
1186 | goto out; | |
1187 | } else { | |
1188 | ret = wl1271_cmd_general_parms(wl); | |
1189 | if (ret < 0) | |
1190 | goto out; | |
1191 | ret = wl1271_cmd_radio_parms(wl); | |
1192 | if (ret < 0) | |
1193 | goto out; | |
1194 | ret = wl1271_cmd_ext_radio_parms(wl); | |
1195 | if (ret < 0) | |
1196 | goto out; | |
1197 | } | |
1198 | out: | |
1199 | return ret; | |
1200 | } | |
1201 | ||
fa7930af AN |
1202 | static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl, |
1203 | struct wl12xx_vif *wlvif) | |
1204 | { | |
1205 | return wlvif->rate_set; | |
1206 | } | |
1207 | ||
80cd6610 LC |
1208 | static int wl12xx_identify_fw(struct wl1271 *wl) |
1209 | { | |
1210 | unsigned int *fw_ver = wl->chip.fw_ver; | |
1211 | ||
1212 | /* Only new station firmwares support routing fw logs to the host */ | |
1213 | if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && | |
1214 | (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN)) | |
1215 | wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; | |
1216 | ||
1217 | /* This feature is not yet supported for AP mode */ | |
1218 | if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) | |
1219 | wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; | |
1220 | ||
1221 | return 0; | |
1222 | } | |
1223 | ||
e87288f0 LC |
1224 | static void wl12xx_conf_init(struct wl1271 *wl) |
1225 | { | |
5453dc10 LC |
1226 | struct wl12xx_priv *priv = wl->priv; |
1227 | ||
e87288f0 LC |
1228 | /* apply driver default configuration */ |
1229 | memcpy(&wl->conf, &wl12xx_conf, sizeof(wl12xx_conf)); | |
5453dc10 LC |
1230 | |
1231 | /* apply default private configuration */ | |
1232 | memcpy(&priv->conf, &wl12xx_default_priv_conf, sizeof(priv->conf)); | |
e87288f0 LC |
1233 | } |
1234 | ||
30d9b4a5 LC |
1235 | static bool wl12xx_mac_in_fuse(struct wl1271 *wl) |
1236 | { | |
1237 | bool supported = false; | |
1238 | u8 major, minor; | |
1239 | ||
1240 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
1241 | major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver); | |
1242 | minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver); | |
1243 | ||
1244 | /* in wl128x we have the MAC address if the PG is >= (2, 1) */ | |
1245 | if (major > 2 || (major == 2 && minor >= 1)) | |
1246 | supported = true; | |
1247 | } else { | |
1248 | major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver); | |
1249 | minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver); | |
1250 | ||
1251 | /* in wl127x we have the MAC address if the PG is >= (3, 1) */ | |
1252 | if (major == 3 && minor >= 1) | |
1253 | supported = true; | |
1254 | } | |
1255 | ||
1256 | wl1271_debug(DEBUG_PROBE, | |
1257 | "PG Ver major = %d minor = %d, MAC %s present", | |
1258 | major, minor, supported ? "is" : "is not"); | |
1259 | ||
1260 | return supported; | |
1261 | } | |
1262 | ||
1263 | static void wl12xx_get_fuse_mac(struct wl1271 *wl) | |
1264 | { | |
1265 | u32 mac1, mac2; | |
1266 | ||
1267 | wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); | |
1268 | ||
1269 | mac1 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1); | |
1270 | mac2 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2); | |
1271 | ||
1272 | /* these are the two parts of the BD_ADDR */ | |
1273 | wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + | |
1274 | ((mac1 & 0xff000000) >> 24); | |
1275 | wl->fuse_nic_addr = mac1 & 0xffffff; | |
1276 | ||
1277 | wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); | |
1278 | } | |
1279 | ||
4ded91ce LC |
1280 | static s8 wl12xx_get_pg_ver(struct wl1271 *wl) |
1281 | { | |
1282 | u32 die_info; | |
1283 | ||
1284 | if (wl->chip.id == CHIP_ID_1283_PG20) | |
dd5512eb | 1285 | die_info = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1); |
4ded91ce | 1286 | else |
dd5512eb | 1287 | die_info = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1); |
4ded91ce LC |
1288 | |
1289 | return (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET; | |
1290 | } | |
1291 | ||
30d9b4a5 LC |
1292 | static void wl12xx_get_mac(struct wl1271 *wl) |
1293 | { | |
1294 | if (wl12xx_mac_in_fuse(wl)) | |
1295 | wl12xx_get_fuse_mac(wl); | |
1296 | } | |
1297 | ||
2fc28de5 AN |
1298 | static void wl12xx_set_tx_desc_csum(struct wl1271 *wl, |
1299 | struct wl1271_tx_hw_descr *desc, | |
1300 | struct sk_buff *skb) | |
1301 | { | |
1302 | desc->wl12xx_reserved = 0; | |
1303 | } | |
1304 | ||
c331b344 LC |
1305 | static int wl12xx_plt_init(struct wl1271 *wl) |
1306 | { | |
1307 | int ret; | |
1308 | ||
1309 | ret = wl->ops->boot(wl); | |
1310 | if (ret < 0) | |
1311 | goto out; | |
1312 | ||
1313 | ret = wl->ops->hw_init(wl); | |
1314 | if (ret < 0) | |
1315 | goto out_irq_disable; | |
1316 | ||
1317 | ret = wl1271_acx_init_mem_config(wl); | |
1318 | if (ret < 0) | |
1319 | goto out_irq_disable; | |
1320 | ||
1321 | ret = wl12xx_acx_mem_cfg(wl); | |
1322 | if (ret < 0) | |
1323 | goto out_free_memmap; | |
1324 | ||
1325 | /* Enable data path */ | |
1326 | ret = wl1271_cmd_data_path(wl, 1); | |
1327 | if (ret < 0) | |
1328 | goto out_free_memmap; | |
1329 | ||
1330 | /* Configure for CAM power saving (ie. always active) */ | |
1331 | ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); | |
1332 | if (ret < 0) | |
1333 | goto out_free_memmap; | |
1334 | ||
1335 | /* configure PM */ | |
1336 | ret = wl1271_acx_pm_config(wl); | |
1337 | if (ret < 0) | |
1338 | goto out_free_memmap; | |
1339 | ||
1340 | goto out; | |
1341 | ||
1342 | out_free_memmap: | |
1343 | kfree(wl->target_mem_map); | |
1344 | wl->target_mem_map = NULL; | |
1345 | ||
1346 | out_irq_disable: | |
1347 | mutex_unlock(&wl->mutex); | |
1348 | /* Unlocking the mutex in the middle of handling is | |
1349 | inherently unsafe. In this case we deem it safe to do, | |
1350 | because we need to let any possibly pending IRQ out of | |
1351 | the system (and while we are WL1271_STATE_OFF the IRQ | |
1352 | work function will not do anything.) Also, any other | |
1353 | possible concurrent operations will fail due to the | |
1354 | current state, hence the wl1271 struct should be safe. */ | |
1355 | wlcore_disable_interrupts(wl); | |
1356 | mutex_lock(&wl->mutex); | |
1357 | out: | |
1358 | return ret; | |
1359 | } | |
1360 | ||
6f7dd16c | 1361 | static struct wlcore_ops wl12xx_ops = { |
4a3b97ee | 1362 | .identify_chip = wl12xx_identify_chip, |
80cd6610 | 1363 | .identify_fw = wl12xx_identify_fw, |
4a3b97ee | 1364 | .boot = wl12xx_boot, |
c331b344 | 1365 | .plt_init = wl12xx_plt_init, |
4a3b97ee AN |
1366 | .trigger_cmd = wl12xx_trigger_cmd, |
1367 | .ack_event = wl12xx_ack_event, | |
1368 | .calc_tx_blocks = wl12xx_calc_tx_blocks, | |
1369 | .set_tx_desc_blocks = wl12xx_set_tx_desc_blocks, | |
6f266e91 | 1370 | .set_tx_desc_data_len = wl12xx_set_tx_desc_data_len, |
cd70f6a4 | 1371 | .get_rx_buf_align = wl12xx_get_rx_buf_align, |
4158149c | 1372 | .get_rx_packet_len = wl12xx_get_rx_packet_len, |
53d67a50 AN |
1373 | .tx_immediate_compl = NULL, |
1374 | .tx_delayed_compl = wl12xx_tx_delayed_compl, | |
9d68d1ee | 1375 | .hw_init = wl12xx_hw_init, |
8a9affc0 | 1376 | .init_vif = NULL, |
fa7930af | 1377 | .sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask, |
4a3b97ee AN |
1378 | .get_pg_ver = wl12xx_get_pg_ver, |
1379 | .get_mac = wl12xx_get_mac, | |
2fc28de5 | 1380 | .set_tx_desc_csum = wl12xx_set_tx_desc_csum, |
169da04f | 1381 | .set_rx_csum = NULL, |
ebc7e57d | 1382 | .ap_get_mimo_wide_rate_mask = NULL, |
ad62d81a | 1383 | .debugfs_init = wl12xx_debugfs_add_files, |
6f7dd16c LC |
1384 | }; |
1385 | ||
4a589a6f AN |
1386 | static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { |
1387 | .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | | |
1388 | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), | |
1389 | .ht_supported = true, | |
1390 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, | |
1391 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, | |
1392 | .mcs = { | |
1393 | .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, | |
1394 | .rx_highest = cpu_to_le16(72), | |
1395 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, | |
1396 | }, | |
1397 | }; | |
1398 | ||
ffeb501c LC |
1399 | static int __devinit wl12xx_probe(struct platform_device *pdev) |
1400 | { | |
a5d751bb | 1401 | struct wl12xx_platform_data *pdata = pdev->dev.platform_data; |
ffeb501c LC |
1402 | struct wl1271 *wl; |
1403 | struct ieee80211_hw *hw; | |
96e0c683 | 1404 | struct wl12xx_priv *priv; |
ffeb501c | 1405 | |
96e0c683 | 1406 | hw = wlcore_alloc_hw(sizeof(*priv)); |
ffeb501c LC |
1407 | if (IS_ERR(hw)) { |
1408 | wl1271_error("can't allocate hw"); | |
1409 | return PTR_ERR(hw); | |
1410 | } | |
1411 | ||
1412 | wl = hw->priv; | |
a5d751bb | 1413 | priv = wl->priv; |
c31be25a | 1414 | wl->ops = &wl12xx_ops; |
25a43d78 | 1415 | wl->ptable = wl12xx_ptable; |
00782136 | 1416 | wl->rtable = wl12xx_rtable; |
72b0624f | 1417 | wl->num_tx_desc = 16; |
0afd04e5 | 1418 | wl->num_rx_desc = 8; |
3edab305 AN |
1419 | wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT; |
1420 | wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE; | |
43a8bc5a AN |
1421 | wl->band_rate_to_idx = wl12xx_band_rate_to_idx; |
1422 | wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; | |
1423 | wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; | |
6bac40a6 | 1424 | wl->fw_status_priv_len = 0; |
10b1e8a2 | 1425 | wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics); |
4a589a6f | 1426 | memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap)); |
e87288f0 | 1427 | wl12xx_conf_init(wl); |
ffeb501c | 1428 | |
a5d751bb LC |
1429 | if (!fref_param) { |
1430 | priv->ref_clock = pdata->board_ref_clock; | |
1431 | } else { | |
1432 | if (!strcmp(fref_param, "19.2")) | |
1433 | priv->ref_clock = WL12XX_REFCLOCK_19; | |
1434 | else if (!strcmp(fref_param, "26")) | |
1435 | priv->ref_clock = WL12XX_REFCLOCK_26; | |
1436 | else if (!strcmp(fref_param, "26x")) | |
1437 | priv->ref_clock = WL12XX_REFCLOCK_26_XTAL; | |
1438 | else if (!strcmp(fref_param, "38.4")) | |
1439 | priv->ref_clock = WL12XX_REFCLOCK_38; | |
1440 | else if (!strcmp(fref_param, "38.4x")) | |
1441 | priv->ref_clock = WL12XX_REFCLOCK_38_XTAL; | |
1442 | else if (!strcmp(fref_param, "52")) | |
1443 | priv->ref_clock = WL12XX_REFCLOCK_52; | |
1444 | else | |
1445 | wl1271_error("Invalid fref parameter %s", fref_param); | |
1446 | } | |
1447 | ||
1448 | if (!tcxo_param) { | |
1449 | priv->tcxo_clock = pdata->board_tcxo_clock; | |
1450 | } else { | |
1451 | if (!strcmp(tcxo_param, "19.2")) | |
1452 | priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2; | |
1453 | else if (!strcmp(tcxo_param, "26")) | |
1454 | priv->tcxo_clock = WL12XX_TCXOCLOCK_26; | |
1455 | else if (!strcmp(tcxo_param, "38.4")) | |
1456 | priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4; | |
1457 | else if (!strcmp(tcxo_param, "52")) | |
1458 | priv->tcxo_clock = WL12XX_TCXOCLOCK_52; | |
1459 | else if (!strcmp(tcxo_param, "16.368")) | |
1460 | priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368; | |
1461 | else if (!strcmp(tcxo_param, "32.736")) | |
1462 | priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736; | |
1463 | else if (!strcmp(tcxo_param, "16.8")) | |
1464 | priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8; | |
1465 | else if (!strcmp(tcxo_param, "33.6")) | |
1466 | priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6; | |
1467 | else | |
1468 | wl1271_error("Invalid tcxo parameter %s", tcxo_param); | |
1469 | } | |
1470 | ||
ffeb501c LC |
1471 | return wlcore_probe(wl, pdev); |
1472 | } | |
b2ba99ff LC |
1473 | |
1474 | static const struct platform_device_id wl12xx_id_table[] __devinitconst = { | |
1475 | { "wl12xx", 0 }, | |
1476 | { } /* Terminating Entry */ | |
1477 | }; | |
1478 | MODULE_DEVICE_TABLE(platform, wl12xx_id_table); | |
1479 | ||
1480 | static struct platform_driver wl12xx_driver = { | |
ffeb501c | 1481 | .probe = wl12xx_probe, |
b2ba99ff LC |
1482 | .remove = __devexit_p(wlcore_remove), |
1483 | .id_table = wl12xx_id_table, | |
1484 | .driver = { | |
1485 | .name = "wl12xx_driver", | |
1486 | .owner = THIS_MODULE, | |
1487 | } | |
1488 | }; | |
1489 | ||
1490 | static int __init wl12xx_init(void) | |
1491 | { | |
1492 | return platform_driver_register(&wl12xx_driver); | |
1493 | } | |
1494 | module_init(wl12xx_init); | |
1495 | ||
1496 | static void __exit wl12xx_exit(void) | |
1497 | { | |
1498 | platform_driver_unregister(&wl12xx_driver); | |
1499 | } | |
1500 | module_exit(wl12xx_exit); | |
1501 | ||
a5d751bb LC |
1502 | module_param_named(fref, fref_param, charp, 0); |
1503 | MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52"); | |
1504 | ||
1505 | module_param_named(tcxo, tcxo_param, charp, 0); | |
1506 | MODULE_PARM_DESC(tcxo, | |
1507 | "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6"); | |
1508 | ||
b2ba99ff LC |
1509 | MODULE_LICENSE("GPL v2"); |
1510 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); | |
6f7dd16c LC |
1511 | MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); |
1512 | MODULE_FIRMWARE(WL127X_FW_NAME_MULTI); | |
1513 | MODULE_FIRMWARE(WL127X_PLT_FW_NAME); | |
1514 | MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE); | |
1515 | MODULE_FIRMWARE(WL128X_FW_NAME_MULTI); | |
1516 | MODULE_FIRMWARE(WL128X_PLT_FW_NAME); |