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
15int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
16				  u32 sdio_blk_size, u32 extra_mem_blks,
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(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, ACX_HOST_IF_CFG_BITMAP,
38				   bitmap_conf, sizeof(*bitmap_conf));
39	if (ret < 0) {
40		wl1271_warning("wl1271 bitmap config opt failed: %d", ret);
41		goto out;
42	}
43
44out:
45	kfree(bitmap_conf);
46
47	return ret;
48}
49
50int 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(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, ACX_CSUM_CONFIG, acx, sizeof(*acx));
66	if (ret < 0) {
67		wl1271_warning("failed to set Tx checksum state: %d", ret);
68		goto out;
69	}
70
71out:
72	kfree(acx);
73	return ret;
74}
75
76int 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(sizeof(*acx), GFP_KERNEL);
84	if (!acx) {
85		ret = -ENOMEM;
86		goto out;
87	}
88
89	ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx));
90	if (ret < 0) {
91		wl1271_warning("failed to clear firmware statistics: %d", ret);
92		goto out;
93	}
94
95out:
96	kfree(acx);
97	return ret;
98}
99
100int 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(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, ACX_PEER_HT_OPERATION_MODE_CFG, acx,
118				   sizeof(*acx));
119
120	if (ret < 0) {
121		wl1271_warning("acx peer ht operation mode failed: %d", ret);
122		goto out;
123	}
124
125out:
126	kfree(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 */
136int 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(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, ACX_PEER_CAP, acx, sizeof(*acx));
175	if (ret < 0) {
176		wl1271_warning("acx ht capabilities setting failed: %d", ret);
177		goto out;
178	}
179
180out:
181	kfree(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 */
189int 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(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, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx));
203	if (ret < 0) {
204		wl1271_warning("acx interrupt notify setting failed: %d", ret);
205		goto out;
206	}
207
208out:
209	kfree(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 */
217int 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(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, ACX_RX_BA_FILTER, acx, sizeof(*acx));
230	if (ret < 0) {
231		wl1271_warning("acx rx ba activity filter setting failed: %d",
232			       ret);
233		goto out;
234	}
235
236out:
237	kfree(acx);
238	return ret;
239}
240
241int 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(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, ACX_AP_SLEEP_CFG, acx, sizeof(*acx));
262	if (ret < 0) {
263		wl1271_warning("acx config ap-sleep failed: %d", ret);
264		goto out;
265	}
266
267out:
268	kfree(acx);
269	return ret;
270}
271
272int 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(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, ACX_DYNAMIC_TRACES_CFG,
289				   acx, sizeof(*acx));
290	if (ret < 0) {
291		wl1271_warning("acx config dynamic fw traces failed: %d", ret);
292		goto out;
293	}
294out:
295	kfree(acx);
296	return ret;
297}
298
299int 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(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, ACX_TIME_SYNC_CFG,
318				   acx, sizeof(*acx));
319	if (ret < 0) {
320		wl1271_warning("acx time sync cfg failed: %d", ret);
321		goto out;
322	}
323out:
324	kfree(acx);
325	return ret;
326}
327