linux/drivers/net/wireless/iwmc3200wifi/hal.c
<<
>>
Prefs
   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