linux/drivers/net/wireless/ath/wil6210/fw_inc.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17/* Algorithmic part of the firmware download.
  18 * To be included in the container file providing framework
  19 */
  20
  21#define wil_err_fw(wil, fmt, arg...) wil_err(wil, "ERR[ FW ]" fmt, ##arg)
  22#define wil_dbg_fw(wil, fmt, arg...) wil_dbg(wil, "DBG[ FW ]" fmt, ##arg)
  23#define wil_hex_dump_fw(prefix_str, prefix_type, rowsize,               \
  24                        groupsize, buf, len, ascii)                     \
  25                        print_hex_dump_debug("DBG[ FW ]" prefix_str,    \
  26                                             prefix_type, rowsize,      \
  27                                             groupsize, buf, len, ascii)
  28
  29#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
  30                ioaddr = wmi_buffer(wil, val); \
  31                if (!ioaddr) { \
  32                        wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
  33                                   le32_to_cpu(val)); \
  34                        return -EINVAL; \
  35                } \
  36        } while (0)
  37
  38/**
  39 * wil_fw_verify - verify firmware file validity
  40 *
  41 * perform various checks for the firmware file header.
  42 * records are not validated.
  43 *
  44 * Return file size or negative error
  45 */
  46static int wil_fw_verify(struct wil6210_priv *wil, const u8 *data, size_t size)
  47{
  48        const struct wil_fw_record_head *hdr = (const void *)data;
  49        struct wil_fw_record_file_header fh;
  50        const struct wil_fw_record_file_header *fh_;
  51        u32 crc;
  52        u32 dlen;
  53
  54        if (size % 4) {
  55                wil_err_fw(wil, "image size not aligned: %zu\n", size);
  56                return -EINVAL;
  57        }
  58        /* have enough data for the file header? */
  59        if (size < sizeof(*hdr) + sizeof(fh)) {
  60                wil_err_fw(wil, "file too short: %zu bytes\n", size);
  61                return -EINVAL;
  62        }
  63
  64        /* start with the file header? */
  65        if (le16_to_cpu(hdr->type) != wil_fw_type_file_header) {
  66                wil_err_fw(wil, "no file header\n");
  67                return -EINVAL;
  68        }
  69
  70        /* data_len */
  71        fh_ = (struct wil_fw_record_file_header *)&hdr[1];
  72        dlen = le32_to_cpu(fh_->data_len);
  73        if (dlen % 4) {
  74                wil_err_fw(wil, "data length not aligned: %lu\n", (ulong)dlen);
  75                return -EINVAL;
  76        }
  77        if (size < dlen) {
  78                wil_err_fw(wil, "file truncated at %zu/%lu\n",
  79                           size, (ulong)dlen);
  80                return -EINVAL;
  81        }
  82        if (dlen < sizeof(*hdr) + sizeof(fh)) {
  83                wil_err_fw(wil, "data length too short: %lu\n", (ulong)dlen);
  84                return -EINVAL;
  85        }
  86
  87        /* signature */
  88        if (le32_to_cpu(fh_->signature) != WIL_FW_SIGNATURE) {
  89                wil_err_fw(wil, "bad header signature: 0x%08x\n",
  90                           le32_to_cpu(fh_->signature));
  91                return -EINVAL;
  92        }
  93
  94        /* version */
  95        if (le32_to_cpu(fh_->version) > WIL_FW_FMT_VERSION) {
  96                wil_err_fw(wil, "unsupported header version: %d\n",
  97                           le32_to_cpu(fh_->version));
  98                return -EINVAL;
  99        }
 100
 101        /* checksum. ~crc32(~0, data, size) when fh.crc set to 0*/
 102        fh = *fh_;
 103        fh.crc = 0;
 104
 105        crc = crc32_le(~0, (unsigned char const *)hdr, sizeof(*hdr));
 106        crc = crc32_le(crc, (unsigned char const *)&fh, sizeof(fh));
 107        crc = crc32_le(crc, (unsigned char const *)&fh_[1],
 108                       dlen - sizeof(*hdr) - sizeof(fh));
 109        crc = ~crc;
 110
 111        if (crc != le32_to_cpu(fh_->crc)) {
 112                wil_err_fw(wil, "checksum mismatch:"
 113                           " calculated for %lu bytes 0x%08x != 0x%08x\n",
 114                           (ulong)dlen, crc, le32_to_cpu(fh_->crc));
 115                return -EINVAL;
 116        }
 117
 118        return (int)dlen;
 119}
 120
 121static int fw_ignore_section(struct wil6210_priv *wil, const void *data,
 122                             size_t size)
 123{
 124        return 0;
 125}
 126
 127static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
 128                             size_t size)
 129{
 130        wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true);
 131
 132        return 0;
 133}
 134
 135static int
 136fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
 137                       size_t size)
 138{
 139        const struct wil_fw_record_capabilities *rec = data;
 140        size_t capa_size;
 141
 142        if (size < sizeof(*rec) ||
 143            le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC)
 144                return 0;
 145
 146        capa_size = size - offsetof(struct wil_fw_record_capabilities,
 147                                    capabilities);
 148        bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
 149        memcpy(wil->fw_capabilities, rec->capabilities,
 150               min(sizeof(wil->fw_capabilities), capa_size));
 151        wil_hex_dump_fw("CAPA", DUMP_PREFIX_OFFSET, 16, 1,
 152                        rec->capabilities, capa_size, false);
 153        return 0;
 154}
 155
 156static int fw_handle_data(struct wil6210_priv *wil, const void *data,
 157                          size_t size)
 158{
 159        const struct wil_fw_record_data *d = data;
 160        void __iomem *dst;
 161        size_t s = size - sizeof(*d);
 162
 163        if (size < sizeof(*d) + sizeof(u32)) {
 164                wil_err_fw(wil, "data record too short: %zu\n", size);
 165                return -EINVAL;
 166        }
 167
 168        FW_ADDR_CHECK(dst, d->addr, "address");
 169        wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
 170                   s);
 171        wil_memcpy_toio_32(dst, d->data, s);
 172        wmb(); /* finish before processing next record */
 173
 174        return 0;
 175}
 176
 177static int fw_handle_fill(struct wil6210_priv *wil, const void *data,
 178                          size_t size)
 179{
 180        const struct wil_fw_record_fill *d = data;
 181        void __iomem *dst;
 182        u32 v;
 183        size_t s = (size_t)le32_to_cpu(d->size);
 184
 185        if (size != sizeof(*d)) {
 186                wil_err_fw(wil, "bad size for fill record: %zu\n", size);
 187                return -EINVAL;
 188        }
 189
 190        if (s < sizeof(u32)) {
 191                wil_err_fw(wil, "fill size too short: %zu\n", s);
 192                return -EINVAL;
 193        }
 194
 195        if (s % sizeof(u32)) {
 196                wil_err_fw(wil, "fill size not aligned: %zu\n", s);
 197                return -EINVAL;
 198        }
 199
 200        FW_ADDR_CHECK(dst, d->addr, "address");
 201
 202        v = le32_to_cpu(d->value);
 203        wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
 204                   le32_to_cpu(d->addr), v, s);
 205        wil_memset_toio_32(dst, v, s);
 206        wmb(); /* finish before processing next record */
 207
 208        return 0;
 209}
 210
 211static int fw_handle_file_header(struct wil6210_priv *wil, const void *data,
 212                                 size_t size)
 213{
 214        const struct wil_fw_record_file_header *d = data;
 215
 216        if (size != sizeof(*d)) {
 217                wil_err_fw(wil, "file header length incorrect: %zu\n", size);
 218                return -EINVAL;
 219        }
 220
 221        wil_dbg_fw(wil, "new file, ver. %d, %i bytes\n",
 222                   d->version, d->data_len);
 223        wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment,
 224                        sizeof(d->comment), true);
 225
 226        if (!memcmp(d->comment, WIL_FW_VERSION_PREFIX,
 227                    WIL_FW_VERSION_PREFIX_LEN))
 228                memcpy(wil->fw_version,
 229                       d->comment + WIL_FW_VERSION_PREFIX_LEN,
 230                       min(sizeof(d->comment) - WIL_FW_VERSION_PREFIX_LEN,
 231                           sizeof(wil->fw_version) - 1));
 232
 233        return 0;
 234}
 235
 236static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data,
 237                                  size_t size)
 238{
 239        const struct wil_fw_record_direct_write *d = data;
 240        const struct wil_fw_data_dwrite *block = d->data;
 241        int n, i;
 242
 243        if (size % sizeof(*block)) {
 244                wil_err_fw(wil, "record size not aligned on %zu: %zu\n",
 245                           sizeof(*block), size);
 246                return -EINVAL;
 247        }
 248        n = size / sizeof(*block);
 249
 250        for (i = 0; i < n; i++) {
 251                void __iomem *dst;
 252                u32 m = le32_to_cpu(block[i].mask);
 253                u32 v = le32_to_cpu(block[i].value);
 254                u32 x, y;
 255
 256                FW_ADDR_CHECK(dst, block[i].addr, "address");
 257
 258                x = readl(dst);
 259                y = (x & m) | (v & ~m);
 260                wil_dbg_fw(wil, "write [0x%08x] <== 0x%08x "
 261                           "(old 0x%08x val 0x%08x mask 0x%08x)\n",
 262                           le32_to_cpu(block[i].addr), y, x, v, m);
 263                writel(y, dst);
 264                wmb(); /* finish before processing next record */
 265        }
 266
 267        return 0;
 268}
 269
 270static int gw_write(struct wil6210_priv *wil, void __iomem *gwa_addr,
 271                    void __iomem *gwa_cmd, void __iomem *gwa_ctl, u32 gw_cmd,
 272                    u32 a)
 273{
 274        unsigned delay = 0;
 275
 276        writel(a, gwa_addr);
 277        writel(gw_cmd, gwa_cmd);
 278        wmb(); /* finish before activate gw */
 279
 280        writel(WIL_FW_GW_CTL_RUN, gwa_ctl); /* activate gw */
 281        do {
 282                udelay(1); /* typical time is few usec */
 283                if (delay++ > 100) {
 284                        wil_err_fw(wil, "gw timeout\n");
 285                        return -EINVAL;
 286                }
 287        } while (readl(gwa_ctl) & WIL_FW_GW_CTL_BUSY); /* gw done? */
 288
 289        return 0;
 290}
 291
 292static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data,
 293                                  size_t size)
 294{
 295        const struct wil_fw_record_gateway_data *d = data;
 296        const struct wil_fw_data_gw *block = d->data;
 297        void __iomem *gwa_addr;
 298        void __iomem *gwa_val;
 299        void __iomem *gwa_cmd;
 300        void __iomem *gwa_ctl;
 301        u32 gw_cmd;
 302        int n, i;
 303
 304        if (size < sizeof(*d) + sizeof(*block)) {
 305                wil_err_fw(wil, "gateway record too short: %zu\n", size);
 306                return -EINVAL;
 307        }
 308
 309        if ((size - sizeof(*d)) % sizeof(*block)) {
 310                wil_err_fw(wil, "gateway record data size"
 311                           " not aligned on %zu: %zu\n",
 312                           sizeof(*block), size - sizeof(*d));
 313                return -EINVAL;
 314        }
 315        n = (size - sizeof(*d)) / sizeof(*block);
 316
 317        gw_cmd = le32_to_cpu(d->command);
 318
 319        wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
 320                   n, gw_cmd);
 321
 322        FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
 323        FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
 324        FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
 325        FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
 326
 327        wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
 328                   " cmd 0x%08x ctl 0x%08x\n",
 329                   le32_to_cpu(d->gateway_addr_addr),
 330                   le32_to_cpu(d->gateway_value_addr),
 331                   le32_to_cpu(d->gateway_cmd_addr),
 332                   le32_to_cpu(d->gateway_ctrl_address));
 333
 334        for (i = 0; i < n; i++) {
 335                int rc;
 336                u32 a = le32_to_cpu(block[i].addr);
 337                u32 v = le32_to_cpu(block[i].value);
 338
 339                wil_dbg_fw(wil, "  gw write[%3d] [0x%08x] <== 0x%08x\n",
 340                           i, a, v);
 341
 342                writel(v, gwa_val);
 343                rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
 344                if (rc)
 345                        return rc;
 346        }
 347
 348        return 0;
 349}
 350
 351static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data,
 352                                   size_t size)
 353{
 354        const struct wil_fw_record_gateway_data4 *d = data;
 355        const struct wil_fw_data_gw4 *block = d->data;
 356        void __iomem *gwa_addr;
 357        void __iomem *gwa_val[ARRAY_SIZE(block->value)];
 358        void __iomem *gwa_cmd;
 359        void __iomem *gwa_ctl;
 360        u32 gw_cmd;
 361        int n, i, k;
 362
 363        if (size < sizeof(*d) + sizeof(*block)) {
 364                wil_err_fw(wil, "gateway4 record too short: %zu\n", size);
 365                return -EINVAL;
 366        }
 367
 368        if ((size - sizeof(*d)) % sizeof(*block)) {
 369                wil_err_fw(wil, "gateway4 record data size"
 370                           " not aligned on %zu: %zu\n",
 371                           sizeof(*block), size - sizeof(*d));
 372                return -EINVAL;
 373        }
 374        n = (size - sizeof(*d)) / sizeof(*block);
 375
 376        gw_cmd = le32_to_cpu(d->command);
 377
 378        wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
 379                   n, gw_cmd);
 380
 381        FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
 382        for (k = 0; k < ARRAY_SIZE(block->value); k++)
 383                FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
 384                              "gateway_value_addr");
 385        FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
 386        FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
 387
 388        wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
 389                   le32_to_cpu(d->gateway_addr_addr),
 390                   le32_to_cpu(d->gateway_cmd_addr),
 391                   le32_to_cpu(d->gateway_ctrl_address));
 392        wil_hex_dump_fw("val addresses: ", DUMP_PREFIX_NONE, 16, 4,
 393                        d->gateway_value_addr, sizeof(d->gateway_value_addr),
 394                        false);
 395
 396        for (i = 0; i < n; i++) {
 397                int rc;
 398                u32 a = le32_to_cpu(block[i].addr);
 399                u32 v[ARRAY_SIZE(block->value)];
 400
 401                for (k = 0; k < ARRAY_SIZE(block->value); k++)
 402                        v[k] = le32_to_cpu(block[i].value[k]);
 403
 404                wil_dbg_fw(wil, "  gw4 write[%3d] [0x%08x] <==\n", i, a);
 405                wil_hex_dump_fw("    val ", DUMP_PREFIX_NONE, 16, 4, v,
 406                                sizeof(v), false);
 407
 408                for (k = 0; k < ARRAY_SIZE(block->value); k++)
 409                        writel(v[k], gwa_val[k]);
 410                rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a);
 411                if (rc)
 412                        return rc;
 413        }
 414
 415        return 0;
 416}
 417
 418static const struct {
 419        int type;
 420        int (*load_handler)(struct wil6210_priv *wil, const void *data,
 421                            size_t size);
 422        int (*parse_handler)(struct wil6210_priv *wil, const void *data,
 423                             size_t size);
 424} wil_fw_handlers[] = {
 425        {wil_fw_type_comment, fw_handle_comment, fw_handle_capabilities},
 426        {wil_fw_type_data, fw_handle_data, fw_ignore_section},
 427        {wil_fw_type_fill, fw_handle_fill, fw_ignore_section},
 428        /* wil_fw_type_action */
 429        /* wil_fw_type_verify */
 430        {wil_fw_type_file_header, fw_handle_file_header,
 431                fw_handle_file_header},
 432        {wil_fw_type_direct_write, fw_handle_direct_write, fw_ignore_section},
 433        {wil_fw_type_gateway_data, fw_handle_gateway_data, fw_ignore_section},
 434        {wil_fw_type_gateway_data4, fw_handle_gateway_data4,
 435                fw_ignore_section},
 436};
 437
 438static int wil_fw_handle_record(struct wil6210_priv *wil, int type,
 439                                const void *data, size_t size, bool load)
 440{
 441        int i;
 442
 443        for (i = 0; i < ARRAY_SIZE(wil_fw_handlers); i++)
 444                if (wil_fw_handlers[i].type == type)
 445                        return load ?
 446                                wil_fw_handlers[i].load_handler(
 447                                        wil, data, size) :
 448                                wil_fw_handlers[i].parse_handler(
 449                                        wil, data, size);
 450
 451        wil_err_fw(wil, "unknown record type: %d\n", type);
 452        return -EINVAL;
 453}
 454
 455/**
 456 * wil_fw_process - process section from FW file
 457 * if load is true: Load the FW and uCode code and data to the
 458 * corresponding device memory regions,
 459 * otherwise only parse and look for capabilities
 460 *
 461 * Return error code
 462 */
 463static int wil_fw_process(struct wil6210_priv *wil, const void *data,
 464                          size_t size, bool load)
 465{
 466        int rc = 0;
 467        const struct wil_fw_record_head *hdr;
 468        size_t s, hdr_sz;
 469
 470        for (hdr = data;; hdr = (const void *)hdr + s, size -= s) {
 471                if (size < sizeof(*hdr))
 472                        break;
 473                hdr_sz = le32_to_cpu(hdr->size);
 474                s = sizeof(*hdr) + hdr_sz;
 475                if (s > size)
 476                        break;
 477                if (hdr_sz % 4) {
 478                        wil_err_fw(wil, "unaligned record size: %zu\n",
 479                                   hdr_sz);
 480                        return -EINVAL;
 481                }
 482                rc = wil_fw_handle_record(wil, le16_to_cpu(hdr->type),
 483                                          &hdr[1], hdr_sz, load);
 484                if (rc)
 485                        return rc;
 486        }
 487        if (size) {
 488                wil_err_fw(wil, "unprocessed bytes: %zu\n", size);
 489                if (size >= sizeof(*hdr)) {
 490                        wil_err_fw(wil, "Stop at offset %ld"
 491                                   " record type %d [%zd bytes]\n",
 492                                   (long)((const void *)hdr - data),
 493                                   le16_to_cpu(hdr->type), hdr_sz);
 494                }
 495                return -EINVAL;
 496        }
 497
 498        return rc;
 499}
 500
 501/**
 502 * wil_request_firmware - Request firmware
 503 *
 504 * Request firmware image from the file
 505 * If load is true, load firmware to device, otherwise
 506 * only parse and extract capabilities
 507 *
 508 * Return error code
 509 */
 510int wil_request_firmware(struct wil6210_priv *wil, const char *name,
 511                         bool load)
 512{
 513        int rc, rc1;
 514        const struct firmware *fw;
 515        size_t sz;
 516        const void *d;
 517
 518        rc = request_firmware(&fw, name, wil_to_dev(wil));
 519        if (rc) {
 520                wil_err_fw(wil, "Failed to load firmware %s\n", name);
 521                return rc;
 522        }
 523        wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);
 524
 525        for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) {
 526                rc1 = wil_fw_verify(wil, d, sz);
 527                if (rc1 < 0) {
 528                        rc = rc1;
 529                        goto out;
 530                }
 531                rc = wil_fw_process(wil, d, rc1, load);
 532                if (rc < 0)
 533                        goto out;
 534        }
 535
 536out:
 537        release_firmware(fw);
 538        return rc;
 539}
 540
 541/**
 542 * wil_fw_verify_file_exists - checks if firmware file exist
 543 *
 544 * @wil: driver context
 545 * @name: firmware file name
 546 *
 547 * return value - boolean, true for success, false for failure
 548 */
 549bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name)
 550{
 551        const struct firmware *fw;
 552        int rc;
 553
 554        rc = request_firmware(&fw, name, wil_to_dev(wil));
 555        if (!rc)
 556                release_firmware(fw);
 557        return rc != -ENOENT;
 558}
 559