linux/drivers/net/wireless/ath/ath6kl/bmi.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2004-2011 Atheros Communications Inc.
   3 * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
   4 *
   5 * Permission to use, copy, modify, and/or distribute this software for any
   6 * purpose with or without fee is hereby granted, provided that the above
   7 * copyright notice and this permission notice appear in all copies.
   8 *
   9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16 */
  17
  18#include "core.h"
  19#include "hif-ops.h"
  20#include "target.h"
  21#include "debug.h"
  22
  23int ath6kl_bmi_done(struct ath6kl *ar)
  24{
  25        int ret;
  26        u32 cid = BMI_DONE;
  27
  28        if (ar->bmi.done_sent) {
  29                ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
  30                return 0;
  31        }
  32
  33        ar->bmi.done_sent = true;
  34
  35        ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
  36        if (ret) {
  37                ath6kl_err("Unable to send bmi done: %d\n", ret);
  38                return ret;
  39        }
  40
  41        return 0;
  42}
  43
  44int ath6kl_bmi_get_target_info(struct ath6kl *ar,
  45                               struct ath6kl_bmi_target_info *targ_info)
  46{
  47        int ret;
  48        u32 cid = BMI_GET_TARGET_INFO;
  49
  50        if (ar->bmi.done_sent) {
  51                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
  52                return -EACCES;
  53        }
  54
  55        ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
  56        if (ret) {
  57                ath6kl_err("Unable to send get target info: %d\n", ret);
  58                return ret;
  59        }
  60
  61        if (ar->hif_type == ATH6KL_HIF_TYPE_USB) {
  62                ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info,
  63                                          sizeof(*targ_info));
  64        } else {
  65                ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version,
  66                                sizeof(targ_info->version));
  67        }
  68
  69        if (ret) {
  70                ath6kl_err("Unable to recv target info: %d\n", ret);
  71                return ret;
  72        }
  73
  74        if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
  75                /* Determine how many bytes are in the Target's targ_info */
  76                ret = ath6kl_hif_bmi_read(ar,
  77                                   (u8 *)&targ_info->byte_count,
  78                                   sizeof(targ_info->byte_count));
  79                if (ret) {
  80                        ath6kl_err("unable to read target info byte count: %d\n",
  81                                   ret);
  82                        return ret;
  83                }
  84
  85                /*
  86                 * The target's targ_info doesn't match the host's targ_info.
  87                 * We need to do some backwards compatibility to make this work.
  88                 */
  89                if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
  90                        WARN_ON(1);
  91                        return -EINVAL;
  92                }
  93
  94                /* Read the remainder of the targ_info */
  95                ret = ath6kl_hif_bmi_read(ar,
  96                                   ((u8 *)targ_info) +
  97                                   sizeof(targ_info->byte_count),
  98                                   sizeof(*targ_info) -
  99                                   sizeof(targ_info->byte_count));
 100
 101                if (ret) {
 102                        ath6kl_err("Unable to read target info (%d bytes): %d\n",
 103                                   targ_info->byte_count, ret);
 104                        return ret;
 105                }
 106        }
 107
 108        ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
 109                   targ_info->version, targ_info->type);
 110
 111        return 0;
 112}
 113
 114int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
 115{
 116        u32 cid = BMI_READ_MEMORY;
 117        int ret;
 118        u32 offset;
 119        u32 len_remain, rx_len;
 120        u16 size;
 121
 122        if (ar->bmi.done_sent) {
 123                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 124                return -EACCES;
 125        }
 126
 127        size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
 128        if (size > ar->bmi.max_cmd_size) {
 129                WARN_ON(1);
 130                return -EINVAL;
 131        }
 132        memset(ar->bmi.cmd_buf, 0, size);
 133
 134        ath6kl_dbg(ATH6KL_DBG_BMI,
 135                   "bmi read memory: device: addr: 0x%x, len: %d\n",
 136                   addr, len);
 137
 138        len_remain = len;
 139
 140        while (len_remain) {
 141                rx_len = (len_remain < ar->bmi.max_data_size) ?
 142                                        len_remain : ar->bmi.max_data_size;
 143                offset = 0;
 144                memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 145                offset += sizeof(cid);
 146                memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 147                offset += sizeof(addr);
 148                memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
 149                offset += sizeof(len);
 150
 151                ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 152                if (ret) {
 153                        ath6kl_err("Unable to write to the device: %d\n",
 154                                   ret);
 155                        return ret;
 156                }
 157                ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
 158                if (ret) {
 159                        ath6kl_err("Unable to read from the device: %d\n",
 160                                   ret);
 161                        return ret;
 162                }
 163                memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
 164                len_remain -= rx_len; addr += rx_len;
 165        }
 166
 167        return 0;
 168}
 169
 170int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
 171{
 172        u32 cid = BMI_WRITE_MEMORY;
 173        int ret;
 174        u32 offset;
 175        u32 len_remain, tx_len;
 176        const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
 177        u8 aligned_buf[400];
 178        u8 *src;
 179
 180        if (ar->bmi.done_sent) {
 181                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 182                return -EACCES;
 183        }
 184
 185        if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
 186                WARN_ON(1);
 187                return -EINVAL;
 188        }
 189
 190        if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
 191                return -E2BIG;
 192
 193        memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
 194
 195        ath6kl_dbg(ATH6KL_DBG_BMI,
 196                   "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
 197
 198        len_remain = len;
 199        while (len_remain) {
 200                src = &buf[len - len_remain];
 201
 202                if (len_remain < (ar->bmi.max_data_size - header)) {
 203                        if (len_remain & 3) {
 204                                /* align it with 4 bytes */
 205                                len_remain = len_remain +
 206                                             (4 - (len_remain & 3));
 207                                memcpy(aligned_buf, src, len_remain);
 208                                src = aligned_buf;
 209                        }
 210                        tx_len = len_remain;
 211                } else {
 212                        tx_len = (ar->bmi.max_data_size - header);
 213                }
 214
 215                offset = 0;
 216                memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 217                offset += sizeof(cid);
 218                memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 219                offset += sizeof(addr);
 220                memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
 221                offset += sizeof(tx_len);
 222                memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
 223                offset += tx_len;
 224
 225                ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 226                if (ret) {
 227                        ath6kl_err("Unable to write to the device: %d\n",
 228                                   ret);
 229                        return ret;
 230                }
 231                len_remain -= tx_len; addr += tx_len;
 232        }
 233
 234        return 0;
 235}
 236
 237int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
 238{
 239        u32 cid = BMI_EXECUTE;
 240        int ret;
 241        u32 offset;
 242        u16 size;
 243
 244        if (ar->bmi.done_sent) {
 245                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 246                return -EACCES;
 247        }
 248
 249        size = sizeof(cid) + sizeof(addr) + sizeof(param);
 250        if (size > ar->bmi.max_cmd_size) {
 251                WARN_ON(1);
 252                return -EINVAL;
 253        }
 254        memset(ar->bmi.cmd_buf, 0, size);
 255
 256        ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
 257                   addr, *param);
 258
 259        offset = 0;
 260        memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 261        offset += sizeof(cid);
 262        memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 263        offset += sizeof(addr);
 264        memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
 265        offset += sizeof(*param);
 266
 267        ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 268        if (ret) {
 269                ath6kl_err("Unable to write to the device: %d\n", ret);
 270                return ret;
 271        }
 272
 273        ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
 274        if (ret) {
 275                ath6kl_err("Unable to read from the device: %d\n", ret);
 276                return ret;
 277        }
 278
 279        memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
 280
 281        return 0;
 282}
 283
 284int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
 285{
 286        u32 cid = BMI_SET_APP_START;
 287        int ret;
 288        u32 offset;
 289        u16 size;
 290
 291        if (ar->bmi.done_sent) {
 292                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 293                return -EACCES;
 294        }
 295
 296        size = sizeof(cid) + sizeof(addr);
 297        if (size > ar->bmi.max_cmd_size) {
 298                WARN_ON(1);
 299                return -EINVAL;
 300        }
 301        memset(ar->bmi.cmd_buf, 0, size);
 302
 303        ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
 304
 305        offset = 0;
 306        memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 307        offset += sizeof(cid);
 308        memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 309        offset += sizeof(addr);
 310
 311        ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 312        if (ret) {
 313                ath6kl_err("Unable to write to the device: %d\n", ret);
 314                return ret;
 315        }
 316
 317        return 0;
 318}
 319
 320int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
 321{
 322        u32 cid = BMI_READ_SOC_REGISTER;
 323        int ret;
 324        u32 offset;
 325        u16 size;
 326
 327        if (ar->bmi.done_sent) {
 328                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 329                return -EACCES;
 330        }
 331
 332        size = sizeof(cid) + sizeof(addr);
 333        if (size > ar->bmi.max_cmd_size) {
 334                WARN_ON(1);
 335                return -EINVAL;
 336        }
 337        memset(ar->bmi.cmd_buf, 0, size);
 338
 339        ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
 340
 341        offset = 0;
 342        memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 343        offset += sizeof(cid);
 344        memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 345        offset += sizeof(addr);
 346
 347        ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 348        if (ret) {
 349                ath6kl_err("Unable to write to the device: %d\n", ret);
 350                return ret;
 351        }
 352
 353        ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
 354        if (ret) {
 355                ath6kl_err("Unable to read from the device: %d\n", ret);
 356                return ret;
 357        }
 358        memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
 359
 360        return 0;
 361}
 362
 363int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
 364{
 365        u32 cid = BMI_WRITE_SOC_REGISTER;
 366        int ret;
 367        u32 offset;
 368        u16 size;
 369
 370        if (ar->bmi.done_sent) {
 371                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 372                return -EACCES;
 373        }
 374
 375        size = sizeof(cid) + sizeof(addr) + sizeof(param);
 376        if (size > ar->bmi.max_cmd_size) {
 377                WARN_ON(1);
 378                return -EINVAL;
 379        }
 380        memset(ar->bmi.cmd_buf, 0, size);
 381
 382        ath6kl_dbg(ATH6KL_DBG_BMI,
 383                   "bmi write SOC reg: addr: 0x%x, param: %d\n",
 384                    addr, param);
 385
 386        offset = 0;
 387        memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 388        offset += sizeof(cid);
 389        memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 390        offset += sizeof(addr);
 391        memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
 392        offset += sizeof(param);
 393
 394        ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 395        if (ret) {
 396                ath6kl_err("Unable to write to the device: %d\n", ret);
 397                return ret;
 398        }
 399
 400        return 0;
 401}
 402
 403int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
 404{
 405        u32 cid = BMI_LZ_DATA;
 406        int ret;
 407        u32 offset;
 408        u32 len_remain, tx_len;
 409        const u32 header = sizeof(cid) + sizeof(len);
 410        u16 size;
 411
 412        if (ar->bmi.done_sent) {
 413                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 414                return -EACCES;
 415        }
 416
 417        size = ar->bmi.max_data_size + header;
 418        if (size > ar->bmi.max_cmd_size) {
 419                WARN_ON(1);
 420                return -EINVAL;
 421        }
 422        memset(ar->bmi.cmd_buf, 0, size);
 423
 424        ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
 425                   len);
 426
 427        len_remain = len;
 428        while (len_remain) {
 429                tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
 430                          len_remain : (ar->bmi.max_data_size - header);
 431
 432                offset = 0;
 433                memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 434                offset += sizeof(cid);
 435                memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
 436                offset += sizeof(tx_len);
 437                memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
 438                       tx_len);
 439                offset += tx_len;
 440
 441                ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 442                if (ret) {
 443                        ath6kl_err("Unable to write to the device: %d\n",
 444                                   ret);
 445                        return ret;
 446                }
 447
 448                len_remain -= tx_len;
 449        }
 450
 451        return 0;
 452}
 453
 454int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
 455{
 456        u32 cid = BMI_LZ_STREAM_START;
 457        int ret;
 458        u32 offset;
 459        u16 size;
 460
 461        if (ar->bmi.done_sent) {
 462                ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
 463                return -EACCES;
 464        }
 465
 466        size = sizeof(cid) + sizeof(addr);
 467        if (size > ar->bmi.max_cmd_size) {
 468                WARN_ON(1);
 469                return -EINVAL;
 470        }
 471        memset(ar->bmi.cmd_buf, 0, size);
 472
 473        ath6kl_dbg(ATH6KL_DBG_BMI,
 474                   "bmi LZ stream start: addr: 0x%x)\n",
 475                    addr);
 476
 477        offset = 0;
 478        memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
 479        offset += sizeof(cid);
 480        memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
 481        offset += sizeof(addr);
 482
 483        ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
 484        if (ret) {
 485                ath6kl_err("Unable to start LZ stream to the device: %d\n",
 486                           ret);
 487                return ret;
 488        }
 489
 490        return 0;
 491}
 492
 493int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
 494{
 495        int ret;
 496        u32 last_word = 0;
 497        u32 last_word_offset = len & ~0x3;
 498        u32 unaligned_bytes = len & 0x3;
 499
 500        ret = ath6kl_bmi_lz_stream_start(ar, addr);
 501        if (ret)
 502                return ret;
 503
 504        if (unaligned_bytes) {
 505                /* copy the last word into a zero padded buffer */
 506                memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
 507        }
 508
 509        ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
 510        if (ret)
 511                return ret;
 512
 513        if (unaligned_bytes)
 514                ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
 515
 516        if (!ret) {
 517                /* Close compressed stream and open a new (fake) one.
 518                 * This serves mainly to flush Target caches. */
 519                ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
 520        }
 521        return ret;
 522}
 523
 524void ath6kl_bmi_reset(struct ath6kl *ar)
 525{
 526        ar->bmi.done_sent = false;
 527}
 528
 529int ath6kl_bmi_init(struct ath6kl *ar)
 530{
 531        if (WARN_ON(ar->bmi.max_data_size == 0))
 532                return -EINVAL;
 533
 534        /* cmd + addr + len + data_size */
 535        ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
 536
 537        ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_KERNEL);
 538        if (!ar->bmi.cmd_buf)
 539                return -ENOMEM;
 540
 541        return 0;
 542}
 543
 544void ath6kl_bmi_cleanup(struct ath6kl *ar)
 545{
 546        kfree(ar->bmi.cmd_buf);
 547        ar->bmi.cmd_buf = NULL;
 548}
 549