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]), ¶m, 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 } 549