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