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