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 laid 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 laid 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