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 ath6kl_err("mismatched byte count %d vs. expected %zd\n", 91 le32_to_cpu(targ_info->byte_count), 92 sizeof(*targ_info)); 93 return -EINVAL; 94 } 95 96 /* Read the remainder of the targ_info */ 97 ret = ath6kl_hif_bmi_read(ar, 98 ((u8 *)targ_info) + 99 sizeof(targ_info->byte_count), 100 sizeof(*targ_info) - 101 sizeof(targ_info->byte_count)); 102 103 if (ret) { 104 ath6kl_err("Unable to read target info (%d bytes): %d\n", 105 targ_info->byte_count, ret); 106 return ret; 107 } 108 } 109 110 ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", 111 targ_info->version, targ_info->type); 112 113 return 0; 114 } 115 116 int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 117 { 118 u32 cid = BMI_READ_MEMORY; 119 int ret; 120 u32 offset; 121 u32 len_remain, rx_len; 122 u16 size; 123 124 if (ar->bmi.done_sent) { 125 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 126 return -EACCES; 127 } 128 129 size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len); 130 if (size > ar->bmi.max_cmd_size) { 131 WARN_ON(1); 132 return -EINVAL; 133 } 134 memset(ar->bmi.cmd_buf, 0, size); 135 136 ath6kl_dbg(ATH6KL_DBG_BMI, 137 "bmi read memory: device: addr: 0x%x, len: %d\n", 138 addr, len); 139 140 len_remain = len; 141 142 while (len_remain) { 143 rx_len = (len_remain < ar->bmi.max_data_size) ? 144 len_remain : ar->bmi.max_data_size; 145 offset = 0; 146 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 147 offset += sizeof(cid); 148 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 149 offset += sizeof(addr); 150 memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len)); 151 offset += sizeof(len); 152 153 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 154 if (ret) { 155 ath6kl_err("Unable to write to the device: %d\n", 156 ret); 157 return ret; 158 } 159 ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len); 160 if (ret) { 161 ath6kl_err("Unable to read from the device: %d\n", 162 ret); 163 return ret; 164 } 165 memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); 166 len_remain -= rx_len; addr += rx_len; 167 } 168 169 return 0; 170 } 171 172 int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 173 { 174 u32 cid = BMI_WRITE_MEMORY; 175 int ret; 176 u32 offset; 177 u32 len_remain, tx_len; 178 const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); 179 u8 aligned_buf[400]; 180 u8 *src; 181 182 if (ar->bmi.done_sent) { 183 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 184 return -EACCES; 185 } 186 187 if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) { 188 WARN_ON(1); 189 return -EINVAL; 190 } 191 192 if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf))) 193 return -E2BIG; 194 195 memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header); 196 197 ath6kl_dbg(ATH6KL_DBG_BMI, 198 "bmi write memory: addr: 0x%x, len: %d\n", addr, len); 199 200 len_remain = len; 201 while (len_remain) { 202 src = &buf[len - len_remain]; 203 204 if (len_remain < (ar->bmi.max_data_size - header)) { 205 if (len_remain & 3) { 206 /* align it with 4 bytes */ 207 len_remain = len_remain + 208 (4 - (len_remain & 3)); 209 memcpy(aligned_buf, src, len_remain); 210 src = aligned_buf; 211 } 212 tx_len = len_remain; 213 } else { 214 tx_len = (ar->bmi.max_data_size - header); 215 } 216 217 offset = 0; 218 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 219 offset += sizeof(cid); 220 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 221 offset += sizeof(addr); 222 memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 223 offset += sizeof(tx_len); 224 memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); 225 offset += tx_len; 226 227 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 228 if (ret) { 229 ath6kl_err("Unable to write to the device: %d\n", 230 ret); 231 return ret; 232 } 233 len_remain -= tx_len; addr += tx_len; 234 } 235 236 return 0; 237 } 238 239 int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) 240 { 241 u32 cid = BMI_EXECUTE; 242 int ret; 243 u32 offset; 244 u16 size; 245 246 if (ar->bmi.done_sent) { 247 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 248 return -EACCES; 249 } 250 251 size = sizeof(cid) + sizeof(addr) + sizeof(*param); 252 if (size > ar->bmi.max_cmd_size) { 253 WARN_ON(1); 254 return -EINVAL; 255 } 256 memset(ar->bmi.cmd_buf, 0, size); 257 258 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", 259 addr, *param); 260 261 offset = 0; 262 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 263 offset += sizeof(cid); 264 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 265 offset += sizeof(addr); 266 memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); 267 offset += sizeof(*param); 268 269 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 270 if (ret) { 271 ath6kl_err("Unable to write to the device: %d\n", ret); 272 return ret; 273 } 274 275 ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 276 if (ret) { 277 ath6kl_err("Unable to read from the device: %d\n", ret); 278 return ret; 279 } 280 281 memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 282 283 return 0; 284 } 285 286 int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) 287 { 288 u32 cid = BMI_SET_APP_START; 289 int ret; 290 u32 offset; 291 u16 size; 292 293 if (ar->bmi.done_sent) { 294 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 295 return -EACCES; 296 } 297 298 size = sizeof(cid) + sizeof(addr); 299 if (size > ar->bmi.max_cmd_size) { 300 WARN_ON(1); 301 return -EINVAL; 302 } 303 memset(ar->bmi.cmd_buf, 0, size); 304 305 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); 306 307 offset = 0; 308 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 309 offset += sizeof(cid); 310 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 311 offset += sizeof(addr); 312 313 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 314 if (ret) { 315 ath6kl_err("Unable to write to the device: %d\n", ret); 316 return ret; 317 } 318 319 return 0; 320 } 321 322 int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) 323 { 324 u32 cid = BMI_READ_SOC_REGISTER; 325 int ret; 326 u32 offset; 327 u16 size; 328 329 if (ar->bmi.done_sent) { 330 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 331 return -EACCES; 332 } 333 334 size = sizeof(cid) + sizeof(addr); 335 if (size > ar->bmi.max_cmd_size) { 336 WARN_ON(1); 337 return -EINVAL; 338 } 339 memset(ar->bmi.cmd_buf, 0, size); 340 341 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); 342 343 offset = 0; 344 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 345 offset += sizeof(cid); 346 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 347 offset += sizeof(addr); 348 349 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 350 if (ret) { 351 ath6kl_err("Unable to write to the device: %d\n", ret); 352 return ret; 353 } 354 355 ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 356 if (ret) { 357 ath6kl_err("Unable to read from the device: %d\n", ret); 358 return ret; 359 } 360 memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 361 362 return 0; 363 } 364 365 int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) 366 { 367 u32 cid = BMI_WRITE_SOC_REGISTER; 368 int ret; 369 u32 offset; 370 u16 size; 371 372 if (ar->bmi.done_sent) { 373 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 374 return -EACCES; 375 } 376 377 size = sizeof(cid) + sizeof(addr) + sizeof(param); 378 if (size > ar->bmi.max_cmd_size) { 379 WARN_ON(1); 380 return -EINVAL; 381 } 382 memset(ar->bmi.cmd_buf, 0, size); 383 384 ath6kl_dbg(ATH6KL_DBG_BMI, 385 "bmi write SOC reg: addr: 0x%x, param: %d\n", 386 addr, param); 387 388 offset = 0; 389 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 390 offset += sizeof(cid); 391 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 392 offset += sizeof(addr); 393 memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); 394 offset += sizeof(param); 395 396 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 397 if (ret) { 398 ath6kl_err("Unable to write to the device: %d\n", ret); 399 return ret; 400 } 401 402 return 0; 403 } 404 405 int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) 406 { 407 u32 cid = BMI_LZ_DATA; 408 int ret; 409 u32 offset; 410 u32 len_remain, tx_len; 411 const u32 header = sizeof(cid) + sizeof(len); 412 u16 size; 413 414 if (ar->bmi.done_sent) { 415 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 416 return -EACCES; 417 } 418 419 size = ar->bmi.max_data_size + header; 420 if (size > ar->bmi.max_cmd_size) { 421 WARN_ON(1); 422 return -EINVAL; 423 } 424 memset(ar->bmi.cmd_buf, 0, size); 425 426 ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", 427 len); 428 429 len_remain = len; 430 while (len_remain) { 431 tx_len = (len_remain < (ar->bmi.max_data_size - header)) ? 432 len_remain : (ar->bmi.max_data_size - header); 433 434 offset = 0; 435 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 436 offset += sizeof(cid); 437 memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 438 offset += sizeof(tx_len); 439 memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], 440 tx_len); 441 offset += tx_len; 442 443 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 444 if (ret) { 445 ath6kl_err("Unable to write to the device: %d\n", 446 ret); 447 return ret; 448 } 449 450 len_remain -= tx_len; 451 } 452 453 return 0; 454 } 455 456 int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) 457 { 458 u32 cid = BMI_LZ_STREAM_START; 459 int ret; 460 u32 offset; 461 u16 size; 462 463 if (ar->bmi.done_sent) { 464 ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 465 return -EACCES; 466 } 467 468 size = sizeof(cid) + sizeof(addr); 469 if (size > ar->bmi.max_cmd_size) { 470 WARN_ON(1); 471 return -EINVAL; 472 } 473 memset(ar->bmi.cmd_buf, 0, size); 474 475 ath6kl_dbg(ATH6KL_DBG_BMI, 476 "bmi LZ stream start: addr: 0x%x)\n", 477 addr); 478 479 offset = 0; 480 memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 481 offset += sizeof(cid); 482 memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 483 offset += sizeof(addr); 484 485 ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 486 if (ret) { 487 ath6kl_err("Unable to start LZ stream to the device: %d\n", 488 ret); 489 return ret; 490 } 491 492 return 0; 493 } 494 495 int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 496 { 497 int ret; 498 u32 last_word = 0; 499 u32 last_word_offset = len & ~0x3; 500 u32 unaligned_bytes = len & 0x3; 501 502 ret = ath6kl_bmi_lz_stream_start(ar, addr); 503 if (ret) 504 return ret; 505 506 if (unaligned_bytes) { 507 /* copy the last word into a zero padded buffer */ 508 memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); 509 } 510 511 ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); 512 if (ret) 513 return ret; 514 515 if (unaligned_bytes) 516 ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); 517 518 if (!ret) { 519 /* Close compressed stream and open a new (fake) one. 520 * This serves mainly to flush Target caches. */ 521 ret = ath6kl_bmi_lz_stream_start(ar, 0x00); 522 } 523 return ret; 524 } 525 526 void ath6kl_bmi_reset(struct ath6kl *ar) 527 { 528 ar->bmi.done_sent = false; 529 } 530 531 int ath6kl_bmi_init(struct ath6kl *ar) 532 { 533 if (WARN_ON(ar->bmi.max_data_size == 0)) 534 return -EINVAL; 535 536 /* cmd + addr + len + data_size */ 537 ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3); 538 539 ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_KERNEL); 540 if (!ar->bmi.cmd_buf) 541 return -ENOMEM; 542 543 return 0; 544 } 545 546 void ath6kl_bmi_cleanup(struct ath6kl *ar) 547 { 548 kfree(ar->bmi.cmd_buf); 549 ar->bmi.cmd_buf = NULL; 550 } 551