]>
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 | ||
0603d891 EP |
236 | static int wl12xx_init_phy_vif_config(struct wl1271 *wl, |
237 | struct wl12xx_vif *wlvif) | |
f5fc0f86 LC |
238 | { |
239 | int ret; | |
240 | ||
0603d891 | 241 | ret = wl1271_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME); |
f5fc0f86 LC |
242 | if (ret < 0) |
243 | return ret; | |
244 | ||
0603d891 | 245 | ret = wl1271_acx_service_period_timeout(wl, wlvif); |
f5fc0f86 LC |
246 | if (ret < 0) |
247 | return ret; | |
248 | ||
0603d891 | 249 | ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold); |
f5fc0f86 LC |
250 | if (ret < 0) |
251 | return ret; | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
0603d891 EP |
256 | static int wl1271_init_beacon_filter(struct wl1271 *wl, |
257 | struct wl12xx_vif *wlvif) | |
f5fc0f86 LC |
258 | { |
259 | int ret; | |
260 | ||
1922167b | 261 | /* disable beacon filtering at this stage */ |
0603d891 | 262 | ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); |
f5fc0f86 LC |
263 | if (ret < 0) |
264 | return ret; | |
265 | ||
0603d891 | 266 | ret = wl1271_acx_beacon_filter_table(wl, wlvif); |
f5fc0f86 LC |
267 | if (ret < 0) |
268 | return ret; | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
12419cce | 273 | int wl1271_init_pta(struct wl1271 *wl) |
f5fc0f86 LC |
274 | { |
275 | int ret; | |
276 | ||
3be4112c | 277 | ret = wl12xx_acx_sg_cfg(wl); |
f5fc0f86 LC |
278 | if (ret < 0) |
279 | return ret; | |
280 | ||
7fc3a864 | 281 | ret = wl1271_acx_sg_enable(wl, wl->sg_enabled); |
f5fc0f86 LC |
282 | if (ret < 0) |
283 | return ret; | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
12419cce | 288 | int wl1271_init_energy_detection(struct wl1271 *wl) |
f5fc0f86 LC |
289 | { |
290 | int ret; | |
291 | ||
292 | ret = wl1271_acx_cca_threshold(wl); | |
293 | if (ret < 0) | |
294 | return ret; | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
0603d891 EP |
299 | static int wl1271_init_beacon_broadcast(struct wl1271 *wl, |
300 | struct wl12xx_vif *wlvif) | |
f5fc0f86 LC |
301 | { |
302 | int ret; | |
303 | ||
0603d891 | 304 | ret = wl1271_acx_bcn_dtim_options(wl, wlvif); |
f5fc0f86 LC |
305 | if (ret < 0) |
306 | return ret; | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
95dac04f IY |
311 | static int wl12xx_init_fwlog(struct wl1271 *wl) |
312 | { | |
313 | int ret; | |
314 | ||
315 | if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED) | |
316 | return 0; | |
317 | ||
318 | ret = wl12xx_cmd_config_fwlog(wl); | |
319 | if (ret < 0) | |
320 | return ret; | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
92c77c73 | 325 | /* generic sta initialization (non vif-specific) */ |
30d0c8fd | 326 | static int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
e0fe371b AN |
327 | { |
328 | int ret; | |
329 | ||
49d750ca SL |
330 | if (wl->chip.id != CHIP_ID_1283_PG20) { |
331 | ret = wl1271_cmd_ext_radio_parms(wl); | |
332 | if (ret < 0) | |
333 | return ret; | |
334 | } | |
e0fe371b | 335 | |
c8bde243 | 336 | /* PS config */ |
d2d66c56 | 337 | ret = wl12xx_acx_config_ps(wl, wlvif); |
c8bde243 EP |
338 | if (ret < 0) |
339 | return ret; | |
340 | ||
ff86843d SL |
341 | /* FM WLAN coexistence */ |
342 | ret = wl1271_acx_fm_coex(wl); | |
343 | if (ret < 0) | |
344 | return ret; | |
345 | ||
30d0c8fd | 346 | ret = wl1271_acx_sta_rate_policies(wl, wlvif); |
e0fe371b AN |
347 | if (ret < 0) |
348 | return ret; | |
349 | ||
350 | return 0; | |
351 | } | |
352 | ||
0603d891 EP |
353 | static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl, |
354 | struct ieee80211_vif *vif) | |
e0fe371b | 355 | { |
0603d891 | 356 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
357 | int ret, i; |
358 | ||
e0fe371b AN |
359 | /* disable all keep-alive templates */ |
360 | for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { | |
0603d891 | 361 | ret = wl1271_acx_keep_alive_config(wl, wlvif, i, |
e0fe371b AN |
362 | ACX_KEEP_ALIVE_TPL_INVALID); |
363 | if (ret < 0) | |
364 | return ret; | |
365 | } | |
366 | ||
367 | /* disable the keep-alive feature */ | |
0603d891 | 368 | ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); |
e0fe371b AN |
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 | |
87fbcb0f | 380 | ret = wl1271_init_ap_rates(wl, wlvif); |
e0fe371b AN |
381 | if (ret < 0) |
382 | return ret; | |
383 | ||
e0fe371b AN |
384 | return 0; |
385 | } | |
386 | ||
784f694d | 387 | int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif) |
e0fe371b | 388 | { |
87fbcb0f | 389 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
e0fe371b AN |
390 | int ret; |
391 | ||
87fbcb0f | 392 | ret = wl1271_ap_init_deauth_template(wl, wlvif); |
e0fe371b AN |
393 | if (ret < 0) |
394 | return ret; | |
395 | ||
784f694d | 396 | ret = wl1271_ap_init_null_template(wl, vif); |
e0fe371b AN |
397 | if (ret < 0) |
398 | return ret; | |
399 | ||
784f694d | 400 | ret = wl1271_ap_init_qos_null_template(wl, vif); |
e0fe371b AN |
401 | if (ret < 0) |
402 | return ret; | |
403 | ||
521a4a23 AN |
404 | /* |
405 | * when operating as AP we want to receive external beacons for | |
406 | * configuring ERP protection. | |
407 | */ | |
0603d891 | 408 | ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); |
521a4a23 AN |
409 | if (ret < 0) |
410 | return ret; | |
411 | ||
e0fe371b AN |
412 | return 0; |
413 | } | |
414 | ||
784f694d EP |
415 | static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl, |
416 | struct ieee80211_vif *vif) | |
c45a85b5 | 417 | { |
784f694d | 418 | return wl1271_ap_init_templates(wl, vif); |
c45a85b5 AN |
419 | } |
420 | ||
87fbcb0f | 421 | int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
70f47424 AN |
422 | { |
423 | int i, ret; | |
424 | struct conf_tx_rate_class rc; | |
425 | u32 supported_rates; | |
426 | ||
87fbcb0f EP |
427 | wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", |
428 | wlvif->basic_rate_set); | |
70f47424 | 429 | |
87fbcb0f | 430 | if (wlvif->basic_rate_set == 0) |
70f47424 AN |
431 | return -EINVAL; |
432 | ||
87fbcb0f | 433 | rc.enabled_rates = wlvif->basic_rate_set; |
70f47424 AN |
434 | rc.long_retry_limit = 10; |
435 | rc.short_retry_limit = 10; | |
436 | rc.aflags = 0; | |
e5a359f8 | 437 | ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx); |
70f47424 AN |
438 | if (ret < 0) |
439 | return ret; | |
440 | ||
441 | /* use the min basic rate for AP broadcast/multicast */ | |
87fbcb0f | 442 | rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); |
70f47424 AN |
443 | rc.short_retry_limit = 10; |
444 | rc.long_retry_limit = 10; | |
445 | rc.aflags = 0; | |
e5a359f8 | 446 | ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx); |
70f47424 AN |
447 | if (ret < 0) |
448 | return ret; | |
449 | ||
450 | /* | |
451 | * If the basic rates contain OFDM rates, use OFDM only | |
452 | * rates for unicast TX as well. Else use all supported rates. | |
453 | */ | |
87fbcb0f | 454 | if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) |
70f47424 AN |
455 | supported_rates = CONF_TX_OFDM_RATES; |
456 | else | |
457 | supported_rates = CONF_TX_AP_ENABLED_RATES; | |
458 | ||
1a8adb67 AN |
459 | /* unconditionally enable HT rates */ |
460 | supported_rates |= CONF_TX_MCS_RATES; | |
461 | ||
70f47424 AN |
462 | /* configure unicast TX rate classes */ |
463 | for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { | |
464 | rc.enabled_rates = supported_rates; | |
465 | rc.short_retry_limit = 10; | |
466 | rc.long_retry_limit = 10; | |
467 | rc.aflags = 0; | |
e5a359f8 EP |
468 | ret = wl1271_acx_ap_rate_policy(wl, &rc, |
469 | wlvif->ap.ucast_rate_idx[i]); | |
70f47424 AN |
470 | if (ret < 0) |
471 | return ret; | |
472 | } | |
473 | ||
474 | return 0; | |
475 | } | |
476 | ||
536129c8 | 477 | static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
4b7fac77 | 478 | { |
4b7fac77 | 479 | /* Reset the BA RX indicators */ |
d0802abd | 480 | wlvif->ba_allowed = true; |
0f9c8250 | 481 | wl->ba_rx_session_count = 0; |
4b7fac77 | 482 | |
0f9c8250 | 483 | /* BA is supported in STA/AP modes */ |
536129c8 EP |
484 | if (wlvif->bss_type != BSS_TYPE_AP_BSS && |
485 | wlvif->bss_type != BSS_TYPE_STA_BSS) { | |
d0802abd | 486 | wlvif->ba_support = false; |
0f9c8250 AN |
487 | return 0; |
488 | } | |
4b7fac77 | 489 | |
d0802abd | 490 | wlvif->ba_support = true; |
4b7fac77 | 491 | |
0f9c8250 | 492 | /* 802.11n initiator BA session setting */ |
0603d891 | 493 | return wl12xx_acx_set_ba_initiator_policy(wl, wlvif); |
4b7fac77 LS |
494 | } |
495 | ||
48a61477 SL |
496 | int wl1271_chip_specific_init(struct wl1271 *wl) |
497 | { | |
498 | int ret = 0; | |
499 | ||
500 | if (wl->chip.id == CHIP_ID_1283_PG20) { | |
501 | u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; | |
502 | ||
0da13da7 | 503 | if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT) |
48a61477 SL |
504 | /* Enable SDIO padding */ |
505 | host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; | |
506 | ||
507 | /* Must be before wl1271_acx_init_mem_config() */ | |
508 | ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap); | |
509 | if (ret < 0) | |
510 | goto out; | |
511 | } | |
512 | out: | |
513 | return ret; | |
514 | } | |
515 | ||
92c77c73 | 516 | /* vif-specifc initialization */ |
0603d891 | 517 | static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
92c77c73 EP |
518 | { |
519 | int ret; | |
520 | ||
0603d891 | 521 | ret = wl1271_acx_group_address_tbl(wl, wlvif, true, NULL, 0); |
92c77c73 EP |
522 | if (ret < 0) |
523 | return ret; | |
524 | ||
525 | /* Initialize connection monitoring thresholds */ | |
0603d891 | 526 | ret = wl1271_acx_conn_monit_params(wl, wlvif, false); |
92c77c73 EP |
527 | if (ret < 0) |
528 | return ret; | |
529 | ||
530 | /* Beacon filtering */ | |
0603d891 | 531 | ret = wl1271_init_beacon_filter(wl, wlvif); |
92c77c73 EP |
532 | if (ret < 0) |
533 | return ret; | |
534 | ||
535 | /* Beacons and broadcast settings */ | |
0603d891 | 536 | ret = wl1271_init_beacon_broadcast(wl, wlvif); |
92c77c73 EP |
537 | if (ret < 0) |
538 | return ret; | |
48a61477 | 539 | |
92c77c73 | 540 | /* Configure rssi/snr averaging weights */ |
0603d891 | 541 | ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif); |
92c77c73 EP |
542 | if (ret < 0) |
543 | return ret; | |
544 | ||
545 | return 0; | |
546 | } | |
547 | ||
548 | /* vif-specific intialization */ | |
0603d891 | 549 | static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) |
92c77c73 EP |
550 | { |
551 | int ret; | |
552 | ||
0603d891 | 553 | ret = wl1271_acx_ap_max_tx_retry(wl, wlvif); |
92c77c73 EP |
554 | if (ret < 0) |
555 | return ret; | |
556 | ||
557 | /* initialize Tx power */ | |
6bd65029 | 558 | ret = wl1271_acx_tx_power(wl, wlvif, wlvif->power_level); |
92c77c73 EP |
559 | if (ret < 0) |
560 | return ret; | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) | |
f5fc0f86 | 566 | { |
87fbcb0f | 567 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
243eeb51 | 568 | struct conf_tx_ac_category *conf_ac; |
f2054df5 | 569 | struct conf_tx_tid *conf_tid; |
536129c8 | 570 | bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); |
92c77c73 EP |
571 | int ret, i; |
572 | ||
a4e4130d EP |
573 | /* |
574 | * consider all existing roles before configuring psm. | |
575 | * TODO: reconfigure on interface removal. | |
576 | */ | |
577 | if (!wl->ap_count) { | |
578 | if (is_ap) { | |
579 | /* Configure for power always on */ | |
580 | ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); | |
581 | if (ret < 0) | |
582 | return ret; | |
583 | } else if (!wl->sta_count) { | |
584 | /* Configure for ELP power saving */ | |
585 | ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); | |
586 | if (ret < 0) | |
587 | return ret; | |
588 | } | |
589 | } | |
590 | ||
92c77c73 EP |
591 | /* Mode specific init */ |
592 | if (is_ap) { | |
87fbcb0f | 593 | ret = wl1271_ap_hw_init(wl, wlvif); |
92c77c73 EP |
594 | if (ret < 0) |
595 | return ret; | |
596 | ||
0603d891 | 597 | ret = wl12xx_init_ap_role(wl, wlvif); |
92c77c73 EP |
598 | if (ret < 0) |
599 | return ret; | |
600 | } else { | |
30d0c8fd | 601 | ret = wl1271_sta_hw_init(wl, wlvif); |
92c77c73 EP |
602 | if (ret < 0) |
603 | return ret; | |
604 | ||
0603d891 | 605 | ret = wl12xx_init_sta_role(wl, wlvif); |
92c77c73 EP |
606 | if (ret < 0) |
607 | return ret; | |
608 | } | |
609 | ||
0603d891 | 610 | wl12xx_init_phy_vif_config(wl, wlvif); |
92c77c73 EP |
611 | |
612 | /* Default TID/AC configuration */ | |
613 | BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count); | |
614 | for (i = 0; i < wl->conf.tx.tid_conf_count; i++) { | |
615 | conf_ac = &wl->conf.tx.ac_conf[i]; | |
0603d891 EP |
616 | ret = wl1271_acx_ac_cfg(wl, wlvif, conf_ac->ac, |
617 | conf_ac->cw_min, conf_ac->cw_max, | |
618 | conf_ac->aifsn, conf_ac->tx_op_limit); | |
92c77c73 EP |
619 | if (ret < 0) |
620 | return ret; | |
621 | ||
622 | conf_tid = &wl->conf.tx.tid_conf[i]; | |
0603d891 | 623 | ret = wl1271_acx_tid_cfg(wl, wlvif, |
92c77c73 EP |
624 | conf_tid->queue_id, |
625 | conf_tid->channel_type, | |
626 | conf_tid->tsid, | |
627 | conf_tid->ps_scheme, | |
628 | conf_tid->ack_policy, | |
629 | conf_tid->apsd_conf[0], | |
630 | conf_tid->apsd_conf[1]); | |
631 | if (ret < 0) | |
632 | return ret; | |
633 | } | |
634 | ||
635 | /* Configure HW encryption */ | |
0603d891 | 636 | ret = wl1271_acx_feature_cfg(wl, wlvif); |
92c77c73 EP |
637 | if (ret < 0) |
638 | return ret; | |
639 | ||
640 | /* Mode specific init - post mem init */ | |
641 | if (is_ap) | |
642 | ret = wl1271_ap_hw_init_post_mem(wl, vif); | |
643 | else | |
0603d891 | 644 | ret = wl1271_sta_hw_init_post_mem(wl, vif); |
92c77c73 EP |
645 | |
646 | if (ret < 0) | |
647 | return ret; | |
648 | ||
649 | /* Configure initiator BA sessions policies */ | |
536129c8 | 650 | ret = wl1271_set_ba_policies(wl, wlvif); |
92c77c73 EP |
651 | if (ret < 0) |
652 | return ret; | |
653 | ||
654 | return 0; | |
655 | } | |
656 | ||
657 | int wl1271_hw_init(struct wl1271 *wl) | |
658 | { | |
659 | int ret; | |
660 | ||
49d750ca SL |
661 | if (wl->chip.id == CHIP_ID_1283_PG20) |
662 | ret = wl128x_cmd_general_parms(wl); | |
663 | else | |
664 | ret = wl1271_cmd_general_parms(wl); | |
4a90406b | 665 | if (ret < 0) |
f5fc0f86 LC |
666 | return ret; |
667 | ||
49d750ca SL |
668 | if (wl->chip.id == CHIP_ID_1283_PG20) |
669 | ret = wl128x_cmd_radio_parms(wl); | |
670 | else | |
671 | ret = wl1271_cmd_radio_parms(wl); | |
4a90406b | 672 | if (ret < 0) |
f5fc0f86 LC |
673 | return ret; |
674 | ||
48a61477 SL |
675 | /* Chip-specific init */ |
676 | ret = wl1271_chip_specific_init(wl); | |
677 | if (ret < 0) | |
678 | return ret; | |
679 | ||
92c77c73 EP |
680 | /* Init templates */ |
681 | ret = wl1271_init_templates_config(wl); | |
682 | if (ret < 0) | |
683 | return ret; | |
644a4860 | 684 | |
92c77c73 EP |
685 | ret = wl12xx_acx_mem_cfg(wl); |
686 | if (ret < 0) | |
687 | return ret; | |
688 | ||
689 | /* Configure the FW logger */ | |
690 | ret = wl12xx_init_fwlog(wl); | |
f5fc0f86 LC |
691 | if (ret < 0) |
692 | return ret; | |
693 | ||
801f870b AN |
694 | /* Bluetooth WLAN coexistence */ |
695 | ret = wl1271_init_pta(wl); | |
696 | if (ret < 0) | |
697 | return ret; | |
698 | ||
f5fc0f86 LC |
699 | /* Default memory configuration */ |
700 | ret = wl1271_acx_init_mem_config(wl); | |
701 | if (ret < 0) | |
702 | return ret; | |
703 | ||
704 | /* RX config */ | |
08c1d1c7 | 705 | ret = wl12xx_init_rx_config(wl); |
f5fc0f86 LC |
706 | if (ret < 0) |
707 | goto out_free_memmap; | |
708 | ||
709 | /* PHY layer config */ | |
710 | ret = wl1271_init_phy_config(wl); | |
711 | if (ret < 0) | |
712 | goto out_free_memmap; | |
713 | ||
6e92b416 LC |
714 | ret = wl1271_acx_dco_itrim_params(wl); |
715 | if (ret < 0) | |
716 | goto out_free_memmap; | |
717 | ||
f5fc0f86 LC |
718 | /* Configure TX patch complete interrupt behavior */ |
719 | ret = wl1271_acx_tx_config_options(wl); | |
720 | if (ret < 0) | |
721 | goto out_free_memmap; | |
722 | ||
723 | /* RX complete interrupt pacing */ | |
724 | ret = wl1271_acx_init_rx_interrupt(wl); | |
725 | if (ret < 0) | |
726 | goto out_free_memmap; | |
727 | ||
f5fc0f86 LC |
728 | /* Energy detection */ |
729 | ret = wl1271_init_energy_detection(wl); | |
730 | if (ret < 0) | |
731 | goto out_free_memmap; | |
732 | ||
f5fc0f86 | 733 | /* Default fragmentation threshold */ |
5f704d18 | 734 | ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold); |
f5fc0f86 LC |
735 | if (ret < 0) |
736 | goto out_free_memmap; | |
737 | ||
f5fc0f86 | 738 | /* Enable data path */ |
94210897 | 739 | ret = wl1271_cmd_data_path(wl, 1); |
f5fc0f86 LC |
740 | if (ret < 0) |
741 | goto out_free_memmap; | |
742 | ||
38ad2d87 JO |
743 | /* configure PM */ |
744 | ret = wl1271_acx_pm_config(wl); | |
745 | if (ret < 0) | |
746 | goto out_free_memmap; | |
747 | ||
fa6ad9f0 EP |
748 | ret = wl12xx_acx_set_rate_mgmt_params(wl); |
749 | if (ret < 0) | |
750 | goto out_free_memmap; | |
751 | ||
9487775c EP |
752 | /* configure hangover */ |
753 | ret = wl12xx_acx_config_hangover(wl); | |
754 | if (ret < 0) | |
755 | goto out_free_memmap; | |
756 | ||
f5fc0f86 LC |
757 | return 0; |
758 | ||
759 | out_free_memmap: | |
760 | kfree(wl->target_mem_map); | |
34415236 | 761 | wl->target_mem_map = NULL; |
f5fc0f86 LC |
762 | |
763 | return ret; | |
764 | } |