]>
Commit | Line | Data |
---|---|---|
f5fc0f86 LC |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2009 Nokia Corporation | |
5 | * | |
6 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
20 | * 02110-1301 USA | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <linux/kernel.h> | |
25 | #include <linux/module.h> | |
5a0e3ad6 | 26 | #include <linux/slab.h> |
f5fc0f86 | 27 | |
00d20100 | 28 | #include "init.h" |
f5fc0f86 | 29 | #include "wl12xx_80211.h" |
00d20100 SL |
30 | #include "acx.h" |
31 | #include "cmd.h" | |
32 | #include "reg.h" | |
e0fe371b | 33 | #include "tx.h" |
48a61477 | 34 | #include "io.h" |
f5fc0f86 | 35 | |
92c77c73 | 36 | int wl1271_init_templates_config(struct wl1271 *wl) |
f5fc0f86 | 37 | { |
bfb24c9e | 38 | int ret, i; |
f5fc0f86 LC |
39 | |
40 | /* send empty templates for fw memory reservation */ | |
41 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, | |
154037d1 | 42 | WL1271_CMD_TEMPL_DFLT_SIZE, |
606c1487 | 43 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
44 | if (ret < 0) |
45 | return ret; | |
46 | ||
11eb5429 | 47 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, |
154037d1 | 48 | NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, |
11eb5429 JO |
49 | WL1271_RATE_AUTOMATIC); |
50 | if (ret < 0) | |
51 | return ret; | |
abb0b3bf | 52 | |
f5fc0f86 | 53 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL, |
bfb24c9e | 54 | sizeof(struct wl12xx_null_data_template), |
606c1487 | 55 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
56 | if (ret < 0) |
57 | return ret; | |
58 | ||
59 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL, | |
bfb24c9e | 60 | sizeof(struct wl12xx_ps_poll_template), |
606c1487 | 61 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
62 | if (ret < 0) |
63 | return ret; | |
64 | ||
65 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL, | |
66 | sizeof | |
bfb24c9e | 67 | (struct wl12xx_qos_null_data_template), |
606c1487 | 68 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
69 | if (ret < 0) |
70 | return ret; | |
71 | ||
72 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL, | |
154037d1 | 73 | WL1271_CMD_TEMPL_DFLT_SIZE, |
606c1487 | 74 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
75 | if (ret < 0) |
76 | return ret; | |
77 | ||
78 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL, | |
154037d1 | 79 | WL1271_CMD_TEMPL_DFLT_SIZE, |
606c1487 | 80 | 0, WL1271_RATE_AUTOMATIC); |
f5fc0f86 LC |
81 | if (ret < 0) |
82 | return ret; | |
83 | ||
c5312772 EP |
84 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, NULL, |
85 | sizeof | |
86 | (struct wl12xx_arp_rsp_template), | |
87 | 0, WL1271_RATE_AUTOMATIC); | |
88 | if (ret < 0) | |
89 | return ret; | |
90 | ||
92c77c73 EP |
91 | /* |
92 | * Put very large empty placeholders for all templates. These | |
93 | * reserve memory for later. | |
94 | */ | |
95 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL, | |
96 | WL1271_CMD_TEMPL_MAX_SIZE, | |
97 | 0, WL1271_RATE_AUTOMATIC); | |
98 | if (ret < 0) | |
99 | return ret; | |
100 | ||
101 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL, | |
102 | WL1271_CMD_TEMPL_MAX_SIZE, | |
103 | 0, WL1271_RATE_AUTOMATIC); | |
104 | if (ret < 0) | |
105 | return ret; | |
106 | ||
107 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL, | |
108 | sizeof | |
109 | (struct wl12xx_disconn_template), | |
110 | 0, WL1271_RATE_AUTOMATIC); | |
111 | if (ret < 0) | |
112 | return ret; | |
113 | ||
bfb24c9e JO |
114 | for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { |
115 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL, | |
154037d1 | 116 | WL1271_CMD_TEMPL_DFLT_SIZE, i, |
606c1487 | 117 | WL1271_RATE_AUTOMATIC); |
bfb24c9e JO |
118 | if (ret < 0) |
119 | return ret; | |
120 | } | |
121 | ||
f5fc0f86 LC |
122 | return 0; |
123 | } | |
124 | ||
87fbcb0f EP |
125 | static int wl1271_ap_init_deauth_template(struct wl1271 *wl, |
126 | struct wl12xx_vif *wlvif) | |
e0fe371b AN |
127 | { |
128 | struct wl12xx_disconn_template *tmpl; | |
129 | int ret; | |
af7fbb28 | 130 | u32 rate; |
e0fe371b AN |
131 | |
132 | tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL); | |
133 | if (!tmpl) { | |
134 | ret = -ENOMEM; | |
135 | goto out; | |
136 | } | |
137 | ||
138 | tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | | |
139 | IEEE80211_STYPE_DEAUTH); | |
140 | ||
87fbcb0f | 141 | rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
e0fe371b | 142 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, |
af7fbb28 | 143 | tmpl, sizeof(*tmpl), 0, rate); |
e0fe371b AN |
144 | |
145 | out: | |
146 | kfree(tmpl); | |
147 | return ret; | |
148 | } | |
149 | ||
784f694d EP |
150 | static int wl1271_ap_init_null_template(struct wl1271 *wl, |
151 | struct ieee80211_vif *vif) | |
e0fe371b | 152 | { |
87fbcb0f | 153 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
154 | struct ieee80211_hdr_3addr *nullfunc; |
155 | int ret; | |
af7fbb28 | 156 | u32 rate; |
e0fe371b AN |
157 | |
158 | nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL); | |
159 | if (!nullfunc) { | |
160 | ret = -ENOMEM; | |
161 | goto out; | |
162 | } | |
163 | ||
164 | nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | |
165 | IEEE80211_STYPE_NULLFUNC | | |
166 | IEEE80211_FCTL_FROMDS); | |
167 | ||
168 | /* nullfunc->addr1 is filled by FW */ | |
169 | ||
784f694d EP |
170 | memcpy(nullfunc->addr2, vif->addr, ETH_ALEN); |
171 | memcpy(nullfunc->addr3, vif->addr, ETH_ALEN); | |
e0fe371b | 172 | |
87fbcb0f | 173 | rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
e0fe371b | 174 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc, |
af7fbb28 | 175 | sizeof(*nullfunc), 0, rate); |
e0fe371b AN |
176 | |
177 | out: | |
178 | kfree(nullfunc); | |
179 | return ret; | |
180 | } | |
181 | ||
784f694d EP |
182 | static int wl1271_ap_init_qos_null_template(struct wl1271 *wl, |
183 | struct ieee80211_vif *vif) | |
e0fe371b | 184 | { |
87fbcb0f | 185 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
186 | struct ieee80211_qos_hdr *qosnull; |
187 | int ret; | |
af7fbb28 | 188 | u32 rate; |
e0fe371b AN |
189 | |
190 | qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL); | |
191 | if (!qosnull) { | |
192 | ret = -ENOMEM; | |
193 | goto out; | |
194 | } | |
195 | ||
196 | qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | | |
197 | IEEE80211_STYPE_QOS_NULLFUNC | | |
198 | IEEE80211_FCTL_FROMDS); | |
199 | ||
200 | /* qosnull->addr1 is filled by FW */ | |
201 | ||
784f694d EP |
202 | memcpy(qosnull->addr2, vif->addr, ETH_ALEN); |
203 | memcpy(qosnull->addr3, vif->addr, ETH_ALEN); | |
e0fe371b | 204 | |
87fbcb0f | 205 | rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
e0fe371b | 206 | ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull, |
af7fbb28 | 207 | sizeof(*qosnull), 0, rate); |
e0fe371b AN |
208 | |
209 | out: | |
210 | kfree(qosnull); | |
211 | return ret; | |
212 | } | |
213 | ||
92c77c73 | 214 | static int wl12xx_init_rx_config(struct wl1271 *wl) |
e0fe371b AN |
215 | { |
216 | int ret; | |
217 | ||
92c77c73 | 218 | ret = wl1271_acx_rx_msdu_life_time(wl); |
e0fe371b AN |
219 | if (ret < 0) |
220 | return ret; | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
92c77c73 | 225 | int wl1271_init_phy_config(struct wl1271 *wl) |
f5fc0f86 LC |
226 | { |
227 | int ret; | |
228 | ||
92c77c73 | 229 | ret = wl1271_acx_pd_threshold(wl); |
f5fc0f86 LC |
230 | if (ret < 0) |
231 | return ret; | |
232 | ||
f5fc0f86 LC |
233 | return 0; |
234 | } | |
235 | ||
92c77c73 | 236 | static int wl12xx_init_phy_vif_config(struct wl1271 *wl) |
f5fc0f86 LC |
237 | { |
238 | int ret; | |
239 | ||
f5fc0f86 LC |
240 | ret = wl1271_acx_slot(wl, DEFAULT_SLOT_TIME); |
241 | if (ret < 0) | |
242 | return ret; | |
243 | ||
f5fc0f86 LC |
244 | ret = wl1271_acx_service_period_timeout(wl); |
245 | if (ret < 0) | |
246 | return ret; | |
247 | ||
5f704d18 | 248 | ret = wl1271_acx_rts_threshold(wl, wl->hw->wiphy->rts_threshold); |
f5fc0f86 LC |
249 | if (ret < 0) |
250 | return ret; | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | static int wl1271_init_beacon_filter(struct wl1271 *wl) | |
256 | { | |
257 | int ret; | |
258 | ||
1922167b JO |
259 | /* disable beacon filtering at this stage */ |
260 | ret = wl1271_acx_beacon_filter_opt(wl, false); | |
f5fc0f86 LC |
261 | if (ret < 0) |
262 | return ret; | |
263 | ||
264 | ret = wl1271_acx_beacon_filter_table(wl); | |
265 | if (ret < 0) | |
266 | return ret; | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
12419cce | 271 | int wl1271_init_pta(struct wl1271 *wl) |
f5fc0f86 LC |
272 | { |
273 | int ret; | |
274 | ||
3be4112c | 275 | ret = wl12xx_acx_sg_cfg(wl); |
f5fc0f86 LC |
276 | if (ret < 0) |
277 | return ret; | |
278 | ||
7fc3a864 | 279 | ret = wl1271_acx_sg_enable(wl, wl->sg_enabled); |
f5fc0f86 LC |
280 | if (ret < 0) |
281 | return ret; | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
12419cce | 286 | int wl1271_init_energy_detection(struct wl1271 *wl) |
f5fc0f86 LC |
287 | { |
288 | int ret; | |
289 | ||
290 | ret = wl1271_acx_cca_threshold(wl); | |
291 | if (ret < 0) | |
292 | return ret; | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | static int wl1271_init_beacon_broadcast(struct wl1271 *wl) | |
298 | { | |
299 | int ret; | |
300 | ||
301 | ret = wl1271_acx_bcn_dtim_options(wl); | |
302 | if (ret < 0) | |
303 | return ret; | |
304 | ||
305 | return 0; | |
306 | } | |
307 | ||
95dac04f IY |
308 | static int wl12xx_init_fwlog(struct wl1271 *wl) |
309 | { | |
310 | int ret; | |
311 | ||
312 | if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) | |
313 | return 0; | |
314 | ||
315 | ret = wl12xx_cmd_config_fwlog(wl); | |
316 | if (ret < 0) | |
317 | return ret; | |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
92c77c73 | 322 | /* generic sta initialization (non vif-specific) */ |
30d0c8fd | 323 | static int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
e0fe371b AN |
324 | { |
325 | int ret; | |
326 | ||
49d750ca SL |
327 | if (wl->chip.id != CHIP_ID_1283_PG20) { |
328 | ret = wl1271_cmd_ext_radio_parms(wl); | |
329 | if (ret < 0) | |
330 | return ret; | |
331 | } | |
e0fe371b | 332 | |
c8bde243 EP |
333 | /* PS config */ |
334 | ret = wl1271_acx_config_ps(wl); | |
335 | if (ret < 0) | |
336 | return ret; | |
337 | ||
ff86843d SL |
338 | /* FM WLAN coexistence */ |
339 | ret = wl1271_acx_fm_coex(wl); | |
340 | if (ret < 0) | |
341 | return ret; | |
342 | ||
e0fe371b AN |
343 | /* Configure for ELP power saving */ |
344 | ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); | |
345 | if (ret < 0) | |
346 | return ret; | |
347 | ||
30d0c8fd | 348 | ret = wl1271_acx_sta_rate_policies(wl, wlvif); |
e0fe371b AN |
349 | if (ret < 0) |
350 | return ret; | |
351 | ||
352 | return 0; | |
353 | } | |
354 | ||
355 | static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl) | |
356 | { | |
357 | int ret, i; | |
358 | ||
e0fe371b AN |
359 | /* disable all keep-alive templates */ |
360 | for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { | |
361 | ret = wl1271_acx_keep_alive_config(wl, i, | |
362 | ACX_KEEP_ALIVE_TPL_INVALID); | |
363 | if (ret < 0) | |
364 | return ret; | |
365 | } | |
366 | ||
367 | /* disable the keep-alive feature */ | |
368 | ret = wl1271_acx_keep_alive_mode(wl, false); | |
369 | if (ret < 0) | |
370 | return ret; | |
371 | ||
372 | return 0; | |
373 | } | |
374 | ||
92c77c73 | 375 | /* generic ap initialization (non vif-specific) */ |
87fbcb0f | 376 | static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
e0fe371b | 377 | { |
70f47424 | 378 | int ret; |
e0fe371b | 379 | |
e0fe371b AN |
380 | /* Configure for power always on */ |
381 | ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); | |
382 | if (ret < 0) | |
383 | return ret; | |
384 | ||
87fbcb0f | 385 | ret = wl1271_init_ap_rates(wl, wlvif); |
e0fe371b AN |
386 | if (ret < 0) |
387 | return ret; | |
388 | ||
e0fe371b AN |
389 | return 0; |
390 | } | |
391 | ||
784f694d | 392 | int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif) |
e0fe371b | 393 | { |
87fbcb0f | 394 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
395 | int ret; |
396 | ||
87fbcb0f | 397 | ret = wl1271_ap_init_deauth_template(wl, wlvif); |
e0fe371b AN |
398 | if (ret < 0) |
399 | return ret; | |
400 | ||
784f694d | 401 | ret = wl1271_ap_init_null_template(wl, vif); |
e0fe371b AN |
402 | if (ret < 0) |
403 | return ret; | |
404 | ||
784f694d | 405 | ret = wl1271_ap_init_qos_null_template(wl, vif); |
e0fe371b AN |
406 | if (ret < 0) |
407 | return ret; | |
408 | ||
521a4a23 AN |
409 | /* |
410 | * when operating as AP we want to receive external beacons for | |
411 | * configuring ERP protection. | |
412 | */ | |
f42bd2cb | 413 | ret = wl1271_acx_beacon_filter_opt(wl, false); |
521a4a23 AN |
414 | if (ret < 0) |
415 | return ret; | |
416 | ||
e0fe371b AN |
417 | return 0; |
418 | } | |
419 | ||
784f694d EP |
420 | static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl, |
421 | struct ieee80211_vif *vif) | |
c45a85b5 | 422 | { |
784f694d | 423 | return wl1271_ap_init_templates(wl, vif); |
c45a85b5 AN |
424 | } |
425 | ||
87fbcb0f | 426 | int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
70f47424 AN |
427 | { |
428 | int i, ret; | |
429 | struct conf_tx_rate_class rc; | |
430 | u32 supported_rates; | |
431 | ||
87fbcb0f EP |
432 | wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", |
433 | wlvif->basic_rate_set); | |
70f47424 | 434 | |
87fbcb0f | 435 | if (wlvif->basic_rate_set == 0) |
70f47424 AN |
436 | return -EINVAL; |
437 | ||
87fbcb0f | 438 | rc.enabled_rates = wlvif->basic_rate_set; |
70f47424 AN |
439 | rc.long_retry_limit = 10; |
440 | rc.short_retry_limit = 10; | |
441 | rc.aflags = 0; | |
442 | ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_MGMT_RATE); | |
443 | if (ret < 0) | |
444 | return ret; | |
445 | ||
446 | /* use the min basic rate for AP broadcast/multicast */ | |
87fbcb0f | 447 | rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
70f47424 AN |
448 | rc.short_retry_limit = 10; |
449 | rc.long_retry_limit = 10; | |
450 | rc.aflags = 0; | |
451 | ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_BCST_RATE); | |
452 | if (ret < 0) | |
453 | return ret; | |
454 | ||
455 | /* | |
456 | * If the basic rates contain OFDM rates, use OFDM only | |
457 | * rates for unicast TX as well. Else use all supported rates. | |
458 | */ | |
87fbcb0f | 459 | if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) |
70f47424 AN |
460 | supported_rates = CONF_TX_OFDM_RATES; |
461 | else | |
462 | supported_rates = CONF_TX_AP_ENABLED_RATES; | |
463 | ||
1a8adb67 AN |
464 | /* unconditionally enable HT rates */ |
465 | supported_rates |= CONF_TX_MCS_RATES; | |
466 | ||
70f47424 AN |
467 | /* configure unicast TX rate classes */ |
468 | for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { | |
469 | rc.enabled_rates = supported_rates; | |
470 | rc.short_retry_limit = 10; | |
471 | rc.long_retry_limit = 10; | |
472 | rc.aflags = 0; | |
473 | ret = wl1271_acx_ap_rate_policy(wl, &rc, i); | |
474 | if (ret < 0) | |
475 | return ret; | |
476 | } | |
477 | ||
478 | return 0; | |
479 | } | |
480 | ||
4b7fac77 LS |
481 | static int wl1271_set_ba_policies(struct wl1271 *wl) |
482 | { | |
4b7fac77 | 483 | /* Reset the BA RX indicators */ |
4b7fac77 | 484 | wl->ba_rx_bitmap = 0; |
70559a06 | 485 | wl->ba_allowed = true; |
0f9c8250 | 486 | wl->ba_rx_session_count = 0; |
4b7fac77 | 487 | |
0f9c8250 AN |
488 | /* BA is supported in STA/AP modes */ |
489 | if (wl->bss_type != BSS_TYPE_AP_BSS && | |
490 | wl->bss_type != BSS_TYPE_STA_BSS) { | |
491 | wl->ba_support = false; | |
492 | return 0; | |
493 | } | |
4b7fac77 | 494 | |
0f9c8250 | 495 | wl->ba_support = true; |
4b7fac77 | 496 | |
0f9c8250 AN |
497 | /* 802.11n initiator BA session setting */ |
498 | return wl12xx_acx_set_ba_initiator_policy(wl); | |
4b7fac77 LS |
499 | } |
500 | ||
48a61477 SL |
501 | int wl1271_chip_specific_init(struct wl1271 *wl) |
502 | { | |
503 | int ret = 0; | |
504 | ||
505 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
506 | u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; | |
507 | ||
0da13da7 | 508 | if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT) |
48a61477 SL |
509 | /* Enable SDIO padding */ |
510 | host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; | |
511 | ||
512 | /* Must be before wl1271_acx_init_mem_config() */ | |
513 | ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap); | |
514 | if (ret < 0) | |
515 | goto out; | |
516 | } | |
517 | out: | |
518 | return ret; | |
519 | } | |
520 | ||
92c77c73 EP |
521 | /* vif-specifc initialization */ |
522 | static int wl12xx_init_sta_role(struct wl1271 *wl, struct ieee80211_vif *vif) | |
523 | { | |
524 | int ret; | |
525 | ||
526 | ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0); | |
527 | if (ret < 0) | |
528 | return ret; | |
529 | ||
530 | /* Initialize connection monitoring thresholds */ | |
531 | ret = wl1271_acx_conn_monit_params(wl, false); | |
532 | if (ret < 0) | |
533 | return ret; | |
534 | ||
535 | /* Beacon filtering */ | |
536 | ret = wl1271_init_beacon_filter(wl); | |
537 | if (ret < 0) | |
538 | return ret; | |
539 | ||
540 | /* Beacons and broadcast settings */ | |
541 | ret = wl1271_init_beacon_broadcast(wl); | |
542 | if (ret < 0) | |
543 | return ret; | |
48a61477 | 544 | |
92c77c73 EP |
545 | /* Configure rssi/snr averaging weights */ |
546 | ret = wl1271_acx_rssi_snr_avg_weights(wl); | |
547 | if (ret < 0) | |
548 | return ret; | |
549 | ||
550 | return 0; | |
551 | } | |
552 | ||
553 | /* vif-specific intialization */ | |
554 | static int wl12xx_init_ap_role(struct wl1271 *wl, struct ieee80211_vif *vif) | |
555 | { | |
556 | int ret; | |
557 | ||
558 | ret = wl1271_acx_ap_max_tx_retry(wl); | |
559 | if (ret < 0) | |
560 | return ret; | |
561 | ||
562 | /* initialize Tx power */ | |
563 | ret = wl1271_acx_tx_power(wl, wl->power_level); | |
564 | if (ret < 0) | |
565 | return ret; | |
566 | ||
567 | return 0; | |
568 | } | |
569 | ||
570 | int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) | |
f5fc0f86 | 571 | { |
87fbcb0f | 572 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
243eeb51 | 573 | struct conf_tx_ac_category *conf_ac; |
f2054df5 | 574 | struct conf_tx_tid *conf_tid; |
e0fe371b | 575 | bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); |
f5fc0f86 | 576 | |
92c77c73 EP |
577 | int ret, i; |
578 | ||
579 | /* Mode specific init */ | |
580 | if (is_ap) { | |
87fbcb0f | 581 | ret = wl1271_ap_hw_init(wl, wlvif); |
92c77c73 EP |
582 | if (ret < 0) |
583 | return ret; | |
584 | ||
585 | ret = wl12xx_init_ap_role(wl, vif); | |
586 | if (ret < 0) | |
587 | return ret; | |
588 | } else { | |
30d0c8fd | 589 | ret = wl1271_sta_hw_init(wl, wlvif); |
92c77c73 EP |
590 | if (ret < 0) |
591 | return ret; | |
592 | ||
593 | ret = wl12xx_init_sta_role(wl, vif); | |
594 | if (ret < 0) | |
595 | return ret; | |
596 | } | |
597 | ||
598 | wl12xx_init_phy_vif_config(wl); | |
599 | ||
600 | /* Default TID/AC configuration */ | |
601 | BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count); | |
602 | for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { | |
603 | conf_ac = &wl->conf.tx.ac_conf[i]; | |
604 | ret = wl1271_acx_ac_cfg(wl, conf_ac->ac, conf_ac->cw_min, | |
605 | conf_ac->cw_max, conf_ac->aifsn, | |
606 | conf_ac->tx_op_limit); | |
607 | if (ret < 0) | |
608 | return ret; | |
609 | ||
610 | conf_tid = &wl->conf.tx.tid_conf[i]; | |
611 | ret = wl1271_acx_tid_cfg(wl, | |
612 | conf_tid->queue_id, | |
613 | conf_tid->channel_type, | |
614 | conf_tid->tsid, | |
615 | conf_tid->ps_scheme, | |
616 | conf_tid->ack_policy, | |
617 | conf_tid->apsd_conf[0], | |
618 | conf_tid->apsd_conf[1]); | |
619 | if (ret < 0) | |
620 | return ret; | |
621 | } | |
622 | ||
623 | /* Configure HW encryption */ | |
624 | ret = wl1271_acx_feature_cfg(wl); | |
625 | if (ret < 0) | |
626 | return ret; | |
627 | ||
628 | /* Mode specific init - post mem init */ | |
629 | if (is_ap) | |
630 | ret = wl1271_ap_hw_init_post_mem(wl, vif); | |
631 | else | |
632 | ret = wl1271_sta_hw_init_post_mem(wl); | |
633 | ||
634 | if (ret < 0) | |
635 | return ret; | |
636 | ||
637 | /* Configure initiator BA sessions policies */ | |
638 | ret = wl1271_set_ba_policies(wl); | |
639 | if (ret < 0) | |
640 | return ret; | |
641 | ||
642 | return 0; | |
643 | } | |
644 | ||
645 | int wl1271_hw_init(struct wl1271 *wl) | |
646 | { | |
647 | int ret; | |
648 | ||
49d750ca SL |
649 | if (wl->chip.id == CHIP_ID_1283_PG20) |
650 | ret = wl128x_cmd_general_parms(wl); | |
651 | else | |
652 | ret = wl1271_cmd_general_parms(wl); | |
4a90406b | 653 | if (ret < 0) |
f5fc0f86 LC |
654 | return ret; |
655 | ||
49d750ca SL |
656 | if (wl->chip.id == CHIP_ID_1283_PG20) |
657 | ret = wl128x_cmd_radio_parms(wl); | |
658 | else | |
659 | ret = wl1271_cmd_radio_parms(wl); | |
4a90406b | 660 | if (ret < 0) |
f5fc0f86 LC |
661 | return ret; |
662 | ||
48a61477 SL |
663 | /* Chip-specific init */ |
664 | ret = wl1271_chip_specific_init(wl); | |
665 | if (ret < 0) | |
666 | return ret; | |
667 | ||
92c77c73 EP |
668 | /* Init templates */ |
669 | ret = wl1271_init_templates_config(wl); | |
670 | if (ret < 0) | |
671 | return ret; | |
644a4860 | 672 | |
92c77c73 EP |
673 | ret = wl12xx_acx_mem_cfg(wl); |
674 | if (ret < 0) | |
675 | return ret; | |
676 | ||
677 | /* Configure the FW logger */ | |
678 | ret = wl12xx_init_fwlog(wl); | |
f5fc0f86 LC |
679 | if (ret < 0) |
680 | return ret; | |
681 | ||
801f870b AN |
682 | /* Bluetooth WLAN coexistence */ |
683 | ret = wl1271_init_pta(wl); | |
684 | if (ret < 0) | |
685 | return ret; | |
686 | ||
f5fc0f86 LC |
687 | /* Default memory configuration */ |
688 | ret = wl1271_acx_init_mem_config(wl); | |
689 | if (ret < 0) | |
690 | return ret; | |
691 | ||
692 | /* RX config */ | |
08c1d1c7 | 693 | ret = wl12xx_init_rx_config(wl); |
f5fc0f86 LC |
694 | if (ret < 0) |
695 | goto out_free_memmap; | |
696 | ||
697 | /* PHY layer config */ | |
698 | ret = wl1271_init_phy_config(wl); | |
699 | if (ret < 0) | |
700 | goto out_free_memmap; | |
701 | ||
6e92b416 LC |
702 | ret = wl1271_acx_dco_itrim_params(wl); |
703 | if (ret < 0) | |
704 | goto out_free_memmap; | |
705 | ||
f5fc0f86 LC |
706 | /* Configure TX patch complete interrupt behavior */ |
707 | ret = wl1271_acx_tx_config_options(wl); | |
708 | if (ret < 0) | |
709 | goto out_free_memmap; | |
710 | ||
711 | /* RX complete interrupt pacing */ | |
712 | ret = wl1271_acx_init_rx_interrupt(wl); | |
713 | if (ret < 0) | |
714 | goto out_free_memmap; | |
715 | ||
f5fc0f86 LC |
716 | /* Energy detection */ |
717 | ret = wl1271_init_energy_detection(wl); | |
718 | if (ret < 0) | |
719 | goto out_free_memmap; | |
720 | ||
f5fc0f86 | 721 | /* Default fragmentation threshold */ |
5f704d18 | 722 | ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold); |
f5fc0f86 LC |
723 | if (ret < 0) |
724 | goto out_free_memmap; | |
725 | ||
f5fc0f86 | 726 | /* Enable data path */ |
94210897 | 727 | ret = wl1271_cmd_data_path(wl, 1); |
f5fc0f86 LC |
728 | if (ret < 0) |
729 | goto out_free_memmap; | |
730 | ||
38ad2d87 JO |
731 | /* configure PM */ |
732 | ret = wl1271_acx_pm_config(wl); | |
733 | if (ret < 0) | |
734 | goto out_free_memmap; | |
735 | ||
fa6ad9f0 EP |
736 | ret = wl12xx_acx_set_rate_mgmt_params(wl); |
737 | if (ret < 0) | |
738 | goto out_free_memmap; | |
739 | ||
9487775c EP |
740 | /* configure hangover */ |
741 | ret = wl12xx_acx_config_hangover(wl); | |
742 | if (ret < 0) | |
743 | goto out_free_memmap; | |
744 | ||
f5fc0f86 LC |
745 | return 0; |
746 | ||
747 | out_free_memmap: | |
748 | kfree(wl->target_mem_map); | |
34415236 | 749 | wl->target_mem_map = NULL; |
f5fc0f86 LC |
750 | |
751 | return ret; | |
752 | } |