1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99#include <linux/kernel.h>
100#include <linux/netdevice.h>
101
102#include "iwm.h"
103#include "bus.h"
104#include "hal.h"
105#include "umac.h"
106#include "debug.h"
107
108static int iwm_nonwifi_cmd_init(struct iwm_priv *iwm,
109 struct iwm_nonwifi_cmd *cmd,
110 struct iwm_udma_nonwifi_cmd *udma_cmd)
111{
112 INIT_LIST_HEAD(&cmd->pending);
113
114 spin_lock(&iwm->cmd_lock);
115
116 cmd->resp_received = 0;
117
118 cmd->seq_num = iwm->nonwifi_seq_num;
119 udma_cmd->seq_num = cpu_to_le16(cmd->seq_num);
120
121 iwm->nonwifi_seq_num++;
122 iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX;
123
124 if (udma_cmd->resp)
125 list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd);
126
127 spin_unlock(&iwm->cmd_lock);
128
129 cmd->buf.start = cmd->buf.payload;
130 cmd->buf.len = 0;
131
132 memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
133
134 return cmd->seq_num;
135}
136
137u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm)
138{
139 u16 seq_num = iwm->wifi_seq_num;
140
141 iwm->wifi_seq_num++;
142 iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX;
143
144 return seq_num;
145}
146
147static void iwm_wifi_cmd_init(struct iwm_priv *iwm,
148 struct iwm_wifi_cmd *cmd,
149 struct iwm_udma_wifi_cmd *udma_cmd,
150 struct iwm_umac_cmd *umac_cmd,
151 struct iwm_lmac_cmd *lmac_cmd,
152 u16 payload_size)
153{
154 INIT_LIST_HEAD(&cmd->pending);
155
156 spin_lock(&iwm->cmd_lock);
157
158 cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm);
159 umac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
160
161 if (umac_cmd->resp)
162 list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd);
163
164 spin_unlock(&iwm->cmd_lock);
165
166 cmd->buf.start = cmd->buf.payload;
167 cmd->buf.len = 0;
168
169 if (lmac_cmd) {
170 cmd->buf.start -= sizeof(struct iwm_lmac_hdr);
171
172 lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
173 lmac_cmd->count = cpu_to_le16(payload_size);
174
175 memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd));
176
177 umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr));
178 } else
179 umac_cmd->count = 0;
180
181 umac_cmd->count = cpu_to_le16(payload_size +
182 le16_to_cpu(umac_cmd->count));
183 udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) +
184 le16_to_cpu(umac_cmd->count));
185
186 memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
187 memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd));
188}
189
190void iwm_cmd_flush(struct iwm_priv *iwm)
191{
192 struct iwm_wifi_cmd *wcmd, *wnext;
193 struct iwm_nonwifi_cmd *nwcmd, *nwnext;
194
195 list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) {
196 list_del(&wcmd->pending);
197 kfree(wcmd);
198 }
199
200 list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd,
201 pending) {
202 list_del(&nwcmd->pending);
203 kfree(nwcmd);
204 }
205}
206
207struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num)
208{
209 struct iwm_wifi_cmd *cmd, *next;
210
211 list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending)
212 if (cmd->seq_num == seq_num) {
213 list_del(&cmd->pending);
214 return cmd;
215 }
216
217 return NULL;
218}
219
220struct iwm_nonwifi_cmd *
221iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode)
222{
223 struct iwm_nonwifi_cmd *cmd, *next;
224
225 list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
226 if ((cmd->seq_num == seq_num) &&
227 (cmd->udma_cmd.opcode == cmd_opcode) &&
228 (cmd->resp_received)) {
229 list_del(&cmd->pending);
230 return cmd;
231 }
232
233 return NULL;
234}
235
236static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm,
237 struct iwm_udma_out_nonwifi_hdr *hdr,
238 struct iwm_udma_nonwifi_cmd *cmd)
239{
240 memset(hdr, 0, sizeof(*hdr));
241
242 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode);
243 SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp);
244 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1);
245 SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW,
246 cmd->handle_by_hw);
247 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
248 SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM,
249 le16_to_cpu(cmd->seq_num));
250
251 hdr->addr = cmd->addr;
252 hdr->op1_sz = cmd->op1_sz;
253 hdr->op2 = cmd->op2;
254}
255
256static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm,
257 struct iwm_nonwifi_cmd *cmd)
258{
259 struct iwm_udma_out_nonwifi_hdr *udma_hdr;
260 struct iwm_nonwifi_cmd_buff *buf;
261 struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd;
262
263 buf = &cmd->buf;
264
265 buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr);
266 buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr);
267
268 udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start);
269
270 iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd);
271
272 IWM_DBG_CMD(iwm, DBG,
273 "Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, "
274 "hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, "
275 "op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp,
276 udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr,
277 udma_cmd->op1_sz, udma_cmd->op2);
278
279 return iwm_bus_send_chunk(iwm, buf->start, buf->len);
280}
281
282void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop)
283{
284 struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf;
285
286 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop);
287}
288
289void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
290 struct iwm_udma_out_wifi_hdr *hdr,
291 struct iwm_udma_wifi_cmd *cmd)
292{
293 memset(hdr, 0, sizeof(*hdr));
294
295 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI);
296 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop);
297 SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
298
299 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT,
300 le16_to_cpu(cmd->count));
301 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group);
302 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid);
303 SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset);
304}
305
306void iwm_build_umac_hdr(struct iwm_priv *iwm,
307 struct iwm_umac_fw_cmd_hdr *hdr,
308 struct iwm_umac_cmd *cmd)
309{
310 memset(hdr, 0, sizeof(*hdr));
311
312 SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT,
313 le16_to_cpu(cmd->count));
314 SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color);
315 SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp);
316
317 hdr->cmd.cmd = cmd->id;
318 hdr->cmd.seq_num = cmd->seq_num;
319}
320
321static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm,
322 struct iwm_wifi_cmd *cmd)
323{
324 struct iwm_umac_wifi_out_hdr *umac_hdr;
325 struct iwm_wifi_cmd_buff *buf;
326 struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd;
327 struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd;
328 int ret;
329
330 buf = &cmd->buf;
331
332 buf->start -= sizeof(struct iwm_umac_wifi_out_hdr);
333 buf->len += sizeof(struct iwm_umac_wifi_out_hdr);
334
335 umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start);
336
337 iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd);
338 iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd);
339
340 IWM_DBG_CMD(iwm, DBG,
341 "Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, "
342 "eop = 0x%x, count = 0x%x, credit_group = 0x%x, "
343 "ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n",
344 UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id,
345 udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group,
346 udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num);
347
348 if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH)
349 IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n",
350 cmd->lmac_cmd.id);
351
352 ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len);
353
354
355
356
357
358
359 if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) {
360 IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n",
361 umac_cmd->id);
362 return ret;
363 }
364
365 return iwm_bus_send_chunk(iwm, buf->start, buf->len);
366}
367
368
369int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
370 struct iwm_udma_nonwifi_cmd *udma_cmd,
371 const void *payload)
372{
373 struct iwm_nonwifi_cmd *cmd;
374 int ret, seq_num;
375
376 cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
377 if (!cmd) {
378 IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
379 return -ENOMEM;
380 }
381
382 seq_num = iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd);
383
384 if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE ||
385 cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) {
386 cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz);
387 memcpy(&cmd->buf.payload, payload, cmd->buf.len);
388 }
389
390 ret = iwm_send_udma_nonwifi_cmd(iwm, cmd);
391
392 if (!udma_cmd->resp)
393 kfree(cmd);
394
395 if (ret < 0)
396 return ret;
397
398 return seq_num;
399}
400
401static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
402 struct iwm_lmac_cmd *cmd)
403{
404 memset(hdr, 0, sizeof(*hdr));
405
406 hdr->id = cmd->id;
407 hdr->flags = 0;
408 hdr->seq_num = cmd->seq_num;
409}
410
411
412
413
414
415
416
417int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
418 struct iwm_udma_wifi_cmd *udma_cmd,
419 struct iwm_umac_cmd *umac_cmd,
420 struct iwm_lmac_cmd *lmac_cmd,
421 const void *payload, u16 payload_size)
422{
423 struct iwm_wifi_cmd *cmd;
424 struct iwm_lmac_hdr *hdr;
425 int lmac_hdr_len = 0;
426 int ret;
427
428 cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL);
429 if (!cmd) {
430 IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n");
431 return -ENOMEM;
432 }
433
434 iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size);
435
436 if (lmac_cmd) {
437 hdr = (struct iwm_lmac_hdr *)(cmd->buf.start);
438
439 iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd);
440 lmac_hdr_len = sizeof(struct iwm_lmac_hdr);
441 }
442
443 memcpy(cmd->buf.payload, payload, payload_size);
444 cmd->buf.len = le16_to_cpu(umac_cmd->count);
445
446 ret = iwm_send_udma_wifi_cmd(iwm, cmd);
447
448
449 if (!umac_cmd->resp)
450 kfree(cmd);
451 return ret;
452}
453
454
455
456
457
458
459int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
460 struct iwm_udma_wifi_cmd *udma_cmd,
461 struct iwm_umac_cmd *umac_cmd,
462 const void *payload, u16 payload_size)
463{
464 return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL,
465 payload, payload_size);
466}
467