549 lines · c
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 any6 * purpose with or without fee is hereby granted, provided that the above7 * copyright notice and this permission notice appear in all copies.8 *9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF15 * 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 23int 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 44int 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 114int 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 170int 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 237int 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 284int 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 320int 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 363int 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 403int 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 454int 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 493int 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 524void ath6kl_bmi_reset(struct ath6kl *ar)525{526 ar->bmi.done_sent = false;527}528 529int 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 544void ath6kl_bmi_cleanup(struct ath6kl *ar)545{546 kfree(ar->bmi.cmd_buf);547 ar->bmi.cmd_buf = NULL;548}549