bmi.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * Copyright (c) 2005-2011 Atheros Communications Inc.
  3. * Copyright (c) 2011-2013 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. #include "bmi.h"
  18. #include "hif.h"
  19. #include "debug.h"
  20. #include "htc.h"
  21. int ath10k_bmi_done(struct ath10k *ar)
  22. {
  23. struct bmi_cmd cmd;
  24. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
  25. int ret;
  26. if (ar->bmi.done_sent) {
  27. ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__);
  28. return 0;
  29. }
  30. ar->bmi.done_sent = true;
  31. cmd.id = __cpu_to_le32(BMI_DONE);
  32. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
  33. if (ret) {
  34. ath10k_warn("unable to write to the device: %d\n", ret);
  35. return ret;
  36. }
  37. ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n");
  38. return 0;
  39. }
  40. int ath10k_bmi_get_target_info(struct ath10k *ar,
  41. struct bmi_target_info *target_info)
  42. {
  43. struct bmi_cmd cmd;
  44. union bmi_resp resp;
  45. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
  46. u32 resplen = sizeof(resp.get_target_info);
  47. int ret;
  48. if (ar->bmi.done_sent) {
  49. ath10k_warn("BMI Get Target Info Command disallowed\n");
  50. return -EBUSY;
  51. }
  52. cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
  53. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
  54. if (ret) {
  55. ath10k_warn("unable to get target info from device\n");
  56. return ret;
  57. }
  58. if (resplen < sizeof(resp.get_target_info)) {
  59. ath10k_warn("invalid get_target_info response length (%d)\n",
  60. resplen);
  61. return -EIO;
  62. }
  63. target_info->version = __le32_to_cpu(resp.get_target_info.version);
  64. target_info->type = __le32_to_cpu(resp.get_target_info.type);
  65. return 0;
  66. }
  67. int ath10k_bmi_read_memory(struct ath10k *ar,
  68. u32 address, void *buffer, u32 length)
  69. {
  70. struct bmi_cmd cmd;
  71. union bmi_resp resp;
  72. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
  73. u32 rxlen;
  74. int ret;
  75. if (ar->bmi.done_sent) {
  76. ath10k_warn("command disallowed\n");
  77. return -EBUSY;
  78. }
  79. ath10k_dbg(ATH10K_DBG_CORE,
  80. "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
  81. __func__, ar, address, length);
  82. while (length) {
  83. rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
  84. cmd.id = __cpu_to_le32(BMI_READ_MEMORY);
  85. cmd.read_mem.addr = __cpu_to_le32(address);
  86. cmd.read_mem.len = __cpu_to_le32(rxlen);
  87. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
  88. &resp, &rxlen);
  89. if (ret) {
  90. ath10k_warn("unable to read from the device\n");
  91. return ret;
  92. }
  93. memcpy(buffer, resp.read_mem.payload, rxlen);
  94. address += rxlen;
  95. buffer += rxlen;
  96. length -= rxlen;
  97. }
  98. return 0;
  99. }
  100. int ath10k_bmi_write_memory(struct ath10k *ar,
  101. u32 address, const void *buffer, u32 length)
  102. {
  103. struct bmi_cmd cmd;
  104. u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
  105. u32 txlen;
  106. int ret;
  107. if (ar->bmi.done_sent) {
  108. ath10k_warn("command disallowed\n");
  109. return -EBUSY;
  110. }
  111. ath10k_dbg(ATH10K_DBG_CORE,
  112. "%s: (device: 0x%p, address: 0x%x, length: %d)\n",
  113. __func__, ar, address, length);
  114. while (length) {
  115. txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
  116. /* copy before roundup to avoid reading beyond buffer*/
  117. memcpy(cmd.write_mem.payload, buffer, txlen);
  118. txlen = roundup(txlen, 4);
  119. cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY);
  120. cmd.write_mem.addr = __cpu_to_le32(address);
  121. cmd.write_mem.len = __cpu_to_le32(txlen);
  122. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
  123. NULL, NULL);
  124. if (ret) {
  125. ath10k_warn("unable to write to the device\n");
  126. return ret;
  127. }
  128. /* fixup roundup() so `length` zeroes out for last chunk */
  129. txlen = min(txlen, length);
  130. address += txlen;
  131. buffer += txlen;
  132. length -= txlen;
  133. }
  134. return 0;
  135. }
  136. int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param)
  137. {
  138. struct bmi_cmd cmd;
  139. union bmi_resp resp;
  140. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
  141. u32 resplen = sizeof(resp.execute);
  142. int ret;
  143. if (ar->bmi.done_sent) {
  144. ath10k_warn("command disallowed\n");
  145. return -EBUSY;
  146. }
  147. ath10k_dbg(ATH10K_DBG_CORE,
  148. "%s: (device: 0x%p, address: 0x%x, param: %d)\n",
  149. __func__, ar, address, *param);
  150. cmd.id = __cpu_to_le32(BMI_EXECUTE);
  151. cmd.execute.addr = __cpu_to_le32(address);
  152. cmd.execute.param = __cpu_to_le32(*param);
  153. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
  154. if (ret) {
  155. ath10k_warn("unable to read from the device\n");
  156. return ret;
  157. }
  158. if (resplen < sizeof(resp.execute)) {
  159. ath10k_warn("invalid execute response length (%d)\n",
  160. resplen);
  161. return ret;
  162. }
  163. *param = __le32_to_cpu(resp.execute.result);
  164. return 0;
  165. }
  166. int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
  167. {
  168. struct bmi_cmd cmd;
  169. u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
  170. u32 txlen;
  171. int ret;
  172. if (ar->bmi.done_sent) {
  173. ath10k_warn("command disallowed\n");
  174. return -EBUSY;
  175. }
  176. while (length) {
  177. txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
  178. WARN_ON_ONCE(txlen & 3);
  179. cmd.id = __cpu_to_le32(BMI_LZ_DATA);
  180. cmd.lz_data.len = __cpu_to_le32(txlen);
  181. memcpy(cmd.lz_data.payload, buffer, txlen);
  182. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
  183. NULL, NULL);
  184. if (ret) {
  185. ath10k_warn("unable to write to the device\n");
  186. return ret;
  187. }
  188. buffer += txlen;
  189. length -= txlen;
  190. }
  191. return 0;
  192. }
  193. int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
  194. {
  195. struct bmi_cmd cmd;
  196. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
  197. int ret;
  198. if (ar->bmi.done_sent) {
  199. ath10k_warn("command disallowed\n");
  200. return -EBUSY;
  201. }
  202. cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START);
  203. cmd.lz_start.addr = __cpu_to_le32(address);
  204. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
  205. if (ret) {
  206. ath10k_warn("unable to Start LZ Stream to the device\n");
  207. return ret;
  208. }
  209. return 0;
  210. }
  211. int ath10k_bmi_fast_download(struct ath10k *ar,
  212. u32 address, const void *buffer, u32 length)
  213. {
  214. u8 trailer[4] = {};
  215. u32 head_len = rounddown(length, 4);
  216. u32 trailer_len = length - head_len;
  217. int ret;
  218. ret = ath10k_bmi_lz_stream_start(ar, address);
  219. if (ret)
  220. return ret;
  221. /* copy the last word into a zero padded buffer */
  222. if (trailer_len > 0)
  223. memcpy(trailer, buffer + head_len, trailer_len);
  224. ret = ath10k_bmi_lz_data(ar, buffer, head_len);
  225. if (ret)
  226. return ret;
  227. if (trailer_len > 0)
  228. ret = ath10k_bmi_lz_data(ar, trailer, 4);
  229. if (ret != 0)
  230. return ret;
  231. /*
  232. * Close compressed stream and open a new (fake) one.
  233. * This serves mainly to flush Target caches.
  234. */
  235. ret = ath10k_bmi_lz_stream_start(ar, 0x00);
  236. return ret;
  237. }