1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * This file is part of wl18xx |
4 | * |
5 | * Copyright (C) 2011 Texas Instruments Inc. |
6 | */ |
7 | |
8 | #include "../wlcore/cmd.h" |
9 | #include "../wlcore/debug.h" |
10 | #include "../wlcore/acx.h" |
11 | |
12 | #include "acx.h" |
13 | #include "wl18xx.h" |
14 | |
15 | int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, |
16 | u32 sdio_blk_size, u32 , |
17 | u32 len_field_size) |
18 | { |
19 | struct wl18xx_acx_host_config_bitmap *bitmap_conf; |
20 | int ret; |
21 | |
22 | wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d" , |
23 | host_cfg_bitmap, sdio_blk_size, extra_mem_blks, |
24 | len_field_size); |
25 | |
26 | bitmap_conf = kzalloc(size: sizeof(*bitmap_conf), GFP_KERNEL); |
27 | if (!bitmap_conf) { |
28 | ret = -ENOMEM; |
29 | goto out; |
30 | } |
31 | |
32 | bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); |
33 | bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size); |
34 | bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks); |
35 | bitmap_conf->length_field_size = cpu_to_le32(len_field_size); |
36 | |
37 | ret = wl1271_cmd_configure(wl, id: ACX_HOST_IF_CFG_BITMAP, |
38 | buf: bitmap_conf, len: sizeof(*bitmap_conf)); |
39 | if (ret < 0) { |
40 | wl1271_warning("wl1271 bitmap config opt failed: %d" , ret); |
41 | goto out; |
42 | } |
43 | |
44 | out: |
45 | kfree(objp: bitmap_conf); |
46 | |
47 | return ret; |
48 | } |
49 | |
50 | int wl18xx_acx_set_checksum_state(struct wl1271 *wl) |
51 | { |
52 | struct wl18xx_acx_checksum_state *acx; |
53 | int ret; |
54 | |
55 | wl1271_debug(DEBUG_ACX, "acx checksum state" ); |
56 | |
57 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
58 | if (!acx) { |
59 | ret = -ENOMEM; |
60 | goto out; |
61 | } |
62 | |
63 | acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED; |
64 | |
65 | ret = wl1271_cmd_configure(wl, id: ACX_CSUM_CONFIG, buf: acx, len: sizeof(*acx)); |
66 | if (ret < 0) { |
67 | wl1271_warning("failed to set Tx checksum state: %d" , ret); |
68 | goto out; |
69 | } |
70 | |
71 | out: |
72 | kfree(objp: acx); |
73 | return ret; |
74 | } |
75 | |
76 | int wl18xx_acx_clear_statistics(struct wl1271 *wl) |
77 | { |
78 | struct wl18xx_acx_clear_statistics *acx; |
79 | int ret = 0; |
80 | |
81 | wl1271_debug(DEBUG_ACX, "acx clear statistics" ); |
82 | |
83 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
84 | if (!acx) { |
85 | ret = -ENOMEM; |
86 | goto out; |
87 | } |
88 | |
89 | ret = wl1271_cmd_configure(wl, id: ACX_CLEAR_STATISTICS, buf: acx, len: sizeof(*acx)); |
90 | if (ret < 0) { |
91 | wl1271_warning("failed to clear firmware statistics: %d" , ret); |
92 | goto out; |
93 | } |
94 | |
95 | out: |
96 | kfree(objp: acx); |
97 | return ret; |
98 | } |
99 | |
100 | int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide) |
101 | { |
102 | struct wlcore_peer_ht_operation_mode *acx; |
103 | int ret; |
104 | |
105 | wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d" , |
106 | hlid, wide); |
107 | |
108 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
109 | if (!acx) { |
110 | ret = -ENOMEM; |
111 | goto out; |
112 | } |
113 | |
114 | acx->hlid = hlid; |
115 | acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ; |
116 | |
117 | ret = wl1271_cmd_configure(wl, id: ACX_PEER_HT_OPERATION_MODE_CFG, buf: acx, |
118 | len: sizeof(*acx)); |
119 | |
120 | if (ret < 0) { |
121 | wl1271_warning("acx peer ht operation mode failed: %d" , ret); |
122 | goto out; |
123 | } |
124 | |
125 | out: |
126 | kfree(objp: acx); |
127 | return ret; |
128 | |
129 | } |
130 | |
131 | /* |
132 | * this command is basically the same as wl1271_acx_ht_capabilities, |
133 | * with the addition of supported rates. they should be unified in |
134 | * the next fw api change |
135 | */ |
136 | int wl18xx_acx_set_peer_cap(struct wl1271 *wl, |
137 | struct ieee80211_sta_ht_cap *ht_cap, |
138 | bool allow_ht_operation, |
139 | u32 rate_set, u8 hlid) |
140 | { |
141 | struct wlcore_acx_peer_cap *acx; |
142 | int ret = 0; |
143 | u32 ht_capabilites = 0; |
144 | |
145 | wl1271_debug(DEBUG_ACX, |
146 | "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x" , |
147 | ht_cap->ht_supported, ht_cap->cap, rate_set); |
148 | |
149 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
150 | if (!acx) { |
151 | ret = -ENOMEM; |
152 | goto out; |
153 | } |
154 | |
155 | if (allow_ht_operation && ht_cap->ht_supported) { |
156 | /* no need to translate capabilities - use the spec values */ |
157 | ht_capabilites = ht_cap->cap; |
158 | |
159 | /* |
160 | * this bit is not employed by the spec but only by FW to |
161 | * indicate peer HT support |
162 | */ |
163 | ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION; |
164 | |
165 | /* get data from A-MPDU parameters field */ |
166 | acx->ampdu_max_length = ht_cap->ampdu_factor; |
167 | acx->ampdu_min_spacing = ht_cap->ampdu_density; |
168 | } |
169 | |
170 | acx->hlid = hlid; |
171 | acx->ht_capabilites = cpu_to_le32(ht_capabilites); |
172 | acx->supported_rates = cpu_to_le32(rate_set); |
173 | |
174 | ret = wl1271_cmd_configure(wl, id: ACX_PEER_CAP, buf: acx, len: sizeof(*acx)); |
175 | if (ret < 0) { |
176 | wl1271_warning("acx ht capabilities setting failed: %d" , ret); |
177 | goto out; |
178 | } |
179 | |
180 | out: |
181 | kfree(objp: acx); |
182 | return ret; |
183 | } |
184 | |
185 | /* |
186 | * When the host is suspended, we don't want to get any fast-link/PSM |
187 | * notifications |
188 | */ |
189 | int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, |
190 | bool action) |
191 | { |
192 | struct wl18xx_acx_interrupt_notify *acx; |
193 | int ret = 0; |
194 | |
195 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
196 | if (!acx) { |
197 | ret = -ENOMEM; |
198 | goto out; |
199 | } |
200 | |
201 | acx->enable = action; |
202 | ret = wl1271_cmd_configure(wl, id: ACX_INTERRUPT_NOTIFY, buf: acx, len: sizeof(*acx)); |
203 | if (ret < 0) { |
204 | wl1271_warning("acx interrupt notify setting failed: %d" , ret); |
205 | goto out; |
206 | } |
207 | |
208 | out: |
209 | kfree(objp: acx); |
210 | return ret; |
211 | } |
212 | |
213 | /* |
214 | * When the host is suspended, we can configure the FW to disable RX BA |
215 | * notifications. |
216 | */ |
217 | int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action) |
218 | { |
219 | struct wl18xx_acx_rx_ba_filter *acx; |
220 | int ret = 0; |
221 | |
222 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
223 | if (!acx) { |
224 | ret = -ENOMEM; |
225 | goto out; |
226 | } |
227 | |
228 | acx->enable = (u32)action; |
229 | ret = wl1271_cmd_configure(wl, id: ACX_RX_BA_FILTER, buf: acx, len: sizeof(*acx)); |
230 | if (ret < 0) { |
231 | wl1271_warning("acx rx ba activity filter setting failed: %d" , |
232 | ret); |
233 | goto out; |
234 | } |
235 | |
236 | out: |
237 | kfree(objp: acx); |
238 | return ret; |
239 | } |
240 | |
241 | int wl18xx_acx_ap_sleep(struct wl1271 *wl) |
242 | { |
243 | struct wl18xx_priv *priv = wl->priv; |
244 | struct acx_ap_sleep_cfg *acx; |
245 | struct conf_ap_sleep_settings *conf = &priv->conf.ap_sleep; |
246 | int ret; |
247 | |
248 | wl1271_debug(DEBUG_ACX, "acx config ap sleep" ); |
249 | |
250 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
251 | if (!acx) { |
252 | ret = -ENOMEM; |
253 | goto out; |
254 | } |
255 | |
256 | acx->idle_duty_cycle = conf->idle_duty_cycle; |
257 | acx->connected_duty_cycle = conf->connected_duty_cycle; |
258 | acx->max_stations_thresh = conf->max_stations_thresh; |
259 | acx->idle_conn_thresh = conf->idle_conn_thresh; |
260 | |
261 | ret = wl1271_cmd_configure(wl, id: ACX_AP_SLEEP_CFG, buf: acx, len: sizeof(*acx)); |
262 | if (ret < 0) { |
263 | wl1271_warning("acx config ap-sleep failed: %d" , ret); |
264 | goto out; |
265 | } |
266 | |
267 | out: |
268 | kfree(objp: acx); |
269 | return ret; |
270 | } |
271 | |
272 | int wl18xx_acx_dynamic_fw_traces(struct wl1271 *wl) |
273 | { |
274 | struct acx_dynamic_fw_traces_cfg *acx; |
275 | int ret; |
276 | |
277 | wl1271_debug(DEBUG_ACX, "acx dynamic fw traces config %d" , |
278 | wl->dynamic_fw_traces); |
279 | |
280 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
281 | if (!acx) { |
282 | ret = -ENOMEM; |
283 | goto out; |
284 | } |
285 | |
286 | acx->dynamic_fw_traces = cpu_to_le32(wl->dynamic_fw_traces); |
287 | |
288 | ret = wl1271_cmd_configure(wl, id: ACX_DYNAMIC_TRACES_CFG, |
289 | buf: acx, len: sizeof(*acx)); |
290 | if (ret < 0) { |
291 | wl1271_warning("acx config dynamic fw traces failed: %d" , ret); |
292 | goto out; |
293 | } |
294 | out: |
295 | kfree(objp: acx); |
296 | return ret; |
297 | } |
298 | |
299 | int wl18xx_acx_time_sync_cfg(struct wl1271 *wl) |
300 | { |
301 | struct acx_time_sync_cfg *acx; |
302 | int ret; |
303 | |
304 | wl1271_debug(DEBUG_ACX, "acx time sync cfg: mode %d, addr: %pM" , |
305 | wl->conf.sg.params[WL18XX_CONF_SG_TIME_SYNC], |
306 | wl->zone_master_mac_addr); |
307 | |
308 | acx = kzalloc(size: sizeof(*acx), GFP_KERNEL); |
309 | if (!acx) { |
310 | ret = -ENOMEM; |
311 | goto out; |
312 | } |
313 | |
314 | acx->sync_mode = wl->conf.sg.params[WL18XX_CONF_SG_TIME_SYNC]; |
315 | memcpy(acx->zone_mac_addr, wl->zone_master_mac_addr, ETH_ALEN); |
316 | |
317 | ret = wl1271_cmd_configure(wl, id: ACX_TIME_SYNC_CFG, |
318 | buf: acx, len: sizeof(*acx)); |
319 | if (ret < 0) { |
320 | wl1271_warning("acx time sync cfg failed: %d" , ret); |
321 | goto out; |
322 | } |
323 | out: |
324 | kfree(objp: acx); |
325 | return ret; |
326 | } |
327 | |