• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/net/wireless/iwmc3200wifi/
1/*
2 * Intel Wireless Multicomm 3200 WiFi driver
3 *
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *   * Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in
14 *     the documentation and/or other materials provided with the
15 *     distribution.
16 *   * Neither the name of Intel Corporation nor the names of its
17 *     contributors may be used to endorse or promote products derived
18 *     from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
36 *
37 */
38
39/*
40 * Hardware Abstraction Layer for iwm.
41 *
42 * This file mostly defines an abstraction API for
43 * sending various commands to the target.
44 *
45 * We have 2 types of commands: wifi and non-wifi ones.
46 *
47 * - wifi commands:
48 *   They are used for sending LMAC and UMAC commands,
49 *   and thus are the most commonly used ones.
50 *   There are 2 different wifi command types, the regular
51 *   one and the LMAC one. The former is used to send
52 *   UMAC commands (see UMAC_CMD_OPCODE_* from umac.h)
53 *   while the latter is used for sending commands to the
54 *   LMAC. If you look at LMAC commands you'll se that they
55 *   are actually regular iwlwifi target commands encapsulated
56 *   into a special UMAC command called UMAC passthrough.
57 *   This is due to the fact the host talks exclusively
58 *   to the UMAC and so there needs to be a special UMAC
59 *   command for talking to the LMAC.
60 *   This is how a wifi command is layed out:
61 *    ------------------------
62 *   | iwm_udma_out_wifi_hdr  |
63 *    ------------------------
64 *   | SW meta_data (32 bits) |
65 *    ------------------------
66 *   | iwm_dev_cmd_hdr        |
67 *    ------------------------
68 *   | payload                |
69 *   | ....                   |
70 *
71 * - non-wifi, or general commands:
72 *   Those commands are handled by the device's bootrom,
73 *   and are typically sent when the UMAC and the LMAC
74 *   are not yet available.
75 *    *   This is how a non-wifi command is layed out:
76 *    ---------------------------
77 *   | iwm_udma_out_nonwifi_hdr  |
78 *    ---------------------------
79 *   | payload                   |
80 *   | ....                      |
81
82 *
83 * All the commands start with a UDMA header, which is
84 * basically a 32 bits field. The 4 LSB there define
85 * an opcode that allows the target to differentiate
86 * between wifi (opcode is 0xf) and non-wifi commands
87 * (opcode is [0..0xe]).
88 *
89 * When a command (wifi or non-wifi) is supposed to receive
90 * an answer, we queue the command buffer. When we do receive
91 * a command response from the UMAC, we go through the list
92 * of pending command, and pass both the command and the answer
93 * to the rx handler. Each command is sent with a unique
94 * sequence id, and the answer is sent with the same one. This
95 * is how we're supposed to match an answer with its command.
96 * See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi()
97 * for the implementation details.
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	/* We keep sending UMAC reset regardless of the command credits.
358	 * The UMAC is supposed to be reset anyway and the Tx credits are
359	 * reinitialized afterwards. If we are lucky, the reset could
360	 * still be done even though we have run out of credits for the
361	 * command pool at this moment.*/
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/* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */
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; /* Is this ever used? */
412	hdr->seq_num = cmd->seq_num;
413}
414
415/*
416 * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
417 * Sending command to the LMAC is equivalent to sending a
418 * regular UMAC command with the LMAC passthrough or the LMAC
419 * wrapper UMAC command IDs.
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	/* We free the cmd if we're not expecting any response */
453	if (!umac_cmd->resp)
454		kfree(cmd);
455	return ret;
456}
457
458/*
459 * iwm_hal_send_umac_cmd(): This is a special case for
460 * iwm_hal_send_host_cmd() to send direct UMAC cmd (without
461 * LMAC involved).
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