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