root/drivers/net/wireless/ath/ath6kl/bmi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ath6kl_bmi_done
  2. ath6kl_bmi_get_target_info
  3. ath6kl_bmi_read
  4. ath6kl_bmi_write
  5. ath6kl_bmi_execute
  6. ath6kl_bmi_set_app_start
  7. ath6kl_bmi_reg_read
  8. ath6kl_bmi_reg_write
  9. ath6kl_bmi_lz_data
  10. ath6kl_bmi_lz_stream_start
  11. ath6kl_bmi_fast_download
  12. ath6kl_bmi_reset
  13. ath6kl_bmi_init
  14. ath6kl_bmi_cleanup

   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 
  23 int 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 
  44 int 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 
 114 int 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 
 170 int 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 
 237 int 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 
 284 int 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 
 320 int 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 
 363 int 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 
 403 int 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 
 454 int 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 
 493 int 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 
 524 void ath6kl_bmi_reset(struct ath6kl *ar)
 525 {
 526         ar->bmi.done_sent = false;
 527 }
 528 
 529 int 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 
 544 void ath6kl_bmi_cleanup(struct ath6kl *ar)
 545 {
 546         kfree(ar->bmi.cmd_buf);
 547         ar->bmi.cmd_buf = NULL;
 548 }

/* [<][>][^][v][top][bottom][index][help] */