/drivers/net/wireless/ath/ath10k/bmi.c

https://github.com/ThomasHabets/linux · C · 524 lines · 388 code · 113 blank · 23 comment · 45 complexity · 1847958ef9ed1a475f3cc05a03969e0b MD5 · raw file

  1. // SPDX-License-Identifier: ISC
  2. /*
  3. * Copyright (c) 2005-2011 Atheros Communications Inc.
  4. * Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc.
  5. */
  6. #include "bmi.h"
  7. #include "hif.h"
  8. #include "debug.h"
  9. #include "htc.h"
  10. #include "hw.h"
  11. void ath10k_bmi_start(struct ath10k *ar)
  12. {
  13. int ret;
  14. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n");
  15. ar->bmi.done_sent = false;
  16. /* Enable hardware clock to speed up firmware download */
  17. if (ar->hw_params.hw_ops->enable_pll_clk) {
  18. ret = ar->hw_params.hw_ops->enable_pll_clk(ar);
  19. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi enable pll ret %d\n", ret);
  20. }
  21. }
  22. int ath10k_bmi_done(struct ath10k *ar)
  23. {
  24. struct bmi_cmd cmd;
  25. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);
  26. int ret;
  27. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n");
  28. if (ar->bmi.done_sent) {
  29. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n");
  30. return 0;
  31. }
  32. ar->bmi.done_sent = true;
  33. cmd.id = __cpu_to_le32(BMI_DONE);
  34. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
  35. if (ret) {
  36. ath10k_warn(ar, "unable to write to the device: %d\n", ret);
  37. return ret;
  38. }
  39. return 0;
  40. }
  41. int ath10k_bmi_get_target_info(struct ath10k *ar,
  42. struct bmi_target_info *target_info)
  43. {
  44. struct bmi_cmd cmd;
  45. union bmi_resp resp;
  46. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
  47. u32 resplen = sizeof(resp.get_target_info);
  48. int ret;
  49. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n");
  50. if (ar->bmi.done_sent) {
  51. ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
  52. return -EBUSY;
  53. }
  54. cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
  55. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
  56. if (ret) {
  57. ath10k_warn(ar, "unable to get target info from device\n");
  58. return ret;
  59. }
  60. if (resplen < sizeof(resp.get_target_info)) {
  61. ath10k_warn(ar, "invalid get_target_info response length (%d)\n",
  62. resplen);
  63. return -EIO;
  64. }
  65. target_info->version = __le32_to_cpu(resp.get_target_info.version);
  66. target_info->type = __le32_to_cpu(resp.get_target_info.type);
  67. return 0;
  68. }
  69. #define TARGET_VERSION_SENTINAL 0xffffffffu
  70. int ath10k_bmi_get_target_info_sdio(struct ath10k *ar,
  71. struct bmi_target_info *target_info)
  72. {
  73. struct bmi_cmd cmd;
  74. union bmi_resp resp;
  75. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);
  76. u32 resplen, ver_len;
  77. __le32 tmp;
  78. int ret;
  79. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n");
  80. if (ar->bmi.done_sent) {
  81. ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");
  82. return -EBUSY;
  83. }
  84. cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);
  85. /* Step 1: Read 4 bytes of the target info and check if it is
  86. * the special sentinal version word or the first word in the
  87. * version response.
  88. */
  89. resplen = sizeof(u32);
  90. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen);
  91. if (ret) {
  92. ath10k_warn(ar, "unable to read from device\n");
  93. return ret;
  94. }
  95. /* Some SDIO boards have a special sentinal byte before the real
  96. * version response.
  97. */
  98. if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) {
  99. /* Step 1b: Read the version length */
  100. resplen = sizeof(u32);
  101. ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp,
  102. &resplen);
  103. if (ret) {
  104. ath10k_warn(ar, "unable to read from device\n");
  105. return ret;
  106. }
  107. }
  108. ver_len = __le32_to_cpu(tmp);
  109. /* Step 2: Check the target info length */
  110. if (ver_len != sizeof(resp.get_target_info)) {
  111. ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n",
  112. ver_len, sizeof(resp.get_target_info));
  113. return -EINVAL;
  114. }
  115. /* Step 3: Read the rest of the version response */
  116. resplen = sizeof(resp.get_target_info) - sizeof(u32);
  117. ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0,
  118. &resp.get_target_info.version,
  119. &resplen);
  120. if (ret) {
  121. ath10k_warn(ar, "unable to read from device\n");
  122. return ret;
  123. }
  124. target_info->version = __le32_to_cpu(resp.get_target_info.version);
  125. target_info->type = __le32_to_cpu(resp.get_target_info.type);
  126. return 0;
  127. }
  128. int ath10k_bmi_read_memory(struct ath10k *ar,
  129. u32 address, void *buffer, u32 length)
  130. {
  131. struct bmi_cmd cmd;
  132. union bmi_resp resp;
  133. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);
  134. u32 rxlen;
  135. int ret;
  136. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",
  137. address, length);
  138. if (ar->bmi.done_sent) {
  139. ath10k_warn(ar, "command disallowed\n");
  140. return -EBUSY;
  141. }
  142. while (length) {
  143. rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);
  144. cmd.id = __cpu_to_le32(BMI_READ_MEMORY);
  145. cmd.read_mem.addr = __cpu_to_le32(address);
  146. cmd.read_mem.len = __cpu_to_le32(rxlen);
  147. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
  148. &resp, &rxlen);
  149. if (ret) {
  150. ath10k_warn(ar, "unable to read from the device (%d)\n",
  151. ret);
  152. return ret;
  153. }
  154. memcpy(buffer, resp.read_mem.payload, rxlen);
  155. address += rxlen;
  156. buffer += rxlen;
  157. length -= rxlen;
  158. }
  159. return 0;
  160. }
  161. int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val)
  162. {
  163. struct bmi_cmd cmd;
  164. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg);
  165. int ret;
  166. ath10k_dbg(ar, ATH10K_DBG_BMI,
  167. "bmi write soc register 0x%08x val 0x%08x\n",
  168. address, reg_val);
  169. if (ar->bmi.done_sent) {
  170. ath10k_warn(ar, "bmi write soc register command in progress\n");
  171. return -EBUSY;
  172. }
  173. cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER);
  174. cmd.write_soc_reg.addr = __cpu_to_le32(address);
  175. cmd.write_soc_reg.value = __cpu_to_le32(reg_val);
  176. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
  177. if (ret) {
  178. ath10k_warn(ar, "Unable to write soc register to device: %d\n",
  179. ret);
  180. return ret;
  181. }
  182. return 0;
  183. }
  184. int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val)
  185. {
  186. struct bmi_cmd cmd;
  187. union bmi_resp resp;
  188. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg);
  189. u32 resplen = sizeof(resp.read_soc_reg);
  190. int ret;
  191. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n",
  192. address);
  193. if (ar->bmi.done_sent) {
  194. ath10k_warn(ar, "bmi read soc register command in progress\n");
  195. return -EBUSY;
  196. }
  197. cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER);
  198. cmd.read_soc_reg.addr = __cpu_to_le32(address);
  199. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
  200. if (ret) {
  201. ath10k_warn(ar, "Unable to read soc register from device: %d\n",
  202. ret);
  203. return ret;
  204. }
  205. *reg_val = __le32_to_cpu(resp.read_soc_reg.value);
  206. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n",
  207. *reg_val);
  208. return 0;
  209. }
  210. int ath10k_bmi_write_memory(struct ath10k *ar,
  211. u32 address, const void *buffer, u32 length)
  212. {
  213. struct bmi_cmd cmd;
  214. u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);
  215. u32 txlen;
  216. int ret;
  217. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",
  218. address, length);
  219. if (ar->bmi.done_sent) {
  220. ath10k_warn(ar, "command disallowed\n");
  221. return -EBUSY;
  222. }
  223. while (length) {
  224. txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
  225. /* copy before roundup to avoid reading beyond buffer*/
  226. memcpy(cmd.write_mem.payload, buffer, txlen);
  227. txlen = roundup(txlen, 4);
  228. cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY);
  229. cmd.write_mem.addr = __cpu_to_le32(address);
  230. cmd.write_mem.len = __cpu_to_le32(txlen);
  231. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
  232. NULL, NULL);
  233. if (ret) {
  234. ath10k_warn(ar, "unable to write to the device (%d)\n",
  235. ret);
  236. return ret;
  237. }
  238. /* fixup roundup() so `length` zeroes out for last chunk */
  239. txlen = min(txlen, length);
  240. address += txlen;
  241. buffer += txlen;
  242. length -= txlen;
  243. }
  244. return 0;
  245. }
  246. int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)
  247. {
  248. struct bmi_cmd cmd;
  249. union bmi_resp resp;
  250. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);
  251. u32 resplen = sizeof(resp.execute);
  252. int ret;
  253. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",
  254. address, param);
  255. if (ar->bmi.done_sent) {
  256. ath10k_warn(ar, "command disallowed\n");
  257. return -EBUSY;
  258. }
  259. cmd.id = __cpu_to_le32(BMI_EXECUTE);
  260. cmd.execute.addr = __cpu_to_le32(address);
  261. cmd.execute.param = __cpu_to_le32(param);
  262. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);
  263. if (ret) {
  264. ath10k_warn(ar, "unable to read from the device\n");
  265. return ret;
  266. }
  267. if (resplen < sizeof(resp.execute)) {
  268. ath10k_warn(ar, "invalid execute response length (%d)\n",
  269. resplen);
  270. return -EIO;
  271. }
  272. *result = __le32_to_cpu(resp.execute.result);
  273. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);
  274. return 0;
  275. }
  276. static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length)
  277. {
  278. struct bmi_cmd *cmd;
  279. u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data);
  280. u32 txlen;
  281. int ret;
  282. size_t buf_len;
  283. ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n",
  284. buffer, length);
  285. if (ar->bmi.done_sent) {
  286. ath10k_warn(ar, "command disallowed\n");
  287. return -EBUSY;
  288. }
  289. buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE;
  290. cmd = kzalloc(buf_len, GFP_KERNEL);
  291. if (!cmd)
  292. return -ENOMEM;
  293. while (length) {
  294. txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen);
  295. WARN_ON_ONCE(txlen & 3);
  296. cmd->id = __cpu_to_le32(BMI_LZ_DATA);
  297. cmd->lz_data.len = __cpu_to_le32(txlen);
  298. memcpy(cmd->lz_data.payload, buffer, txlen);
  299. ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen,
  300. NULL, NULL);
  301. if (ret) {
  302. ath10k_warn(ar, "unable to write to the device\n");
  303. kfree(cmd);
  304. return ret;
  305. }
  306. buffer += txlen;
  307. length -= txlen;
  308. }
  309. kfree(cmd);
  310. return 0;
  311. }
  312. int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)
  313. {
  314. struct bmi_cmd cmd;
  315. u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);
  316. u32 txlen;
  317. int ret;
  318. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n",
  319. buffer, length);
  320. if (ar->bmi.done_sent) {
  321. ath10k_warn(ar, "command disallowed\n");
  322. return -EBUSY;
  323. }
  324. while (length) {
  325. txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);
  326. WARN_ON_ONCE(txlen & 3);
  327. cmd.id = __cpu_to_le32(BMI_LZ_DATA);
  328. cmd.lz_data.len = __cpu_to_le32(txlen);
  329. memcpy(cmd.lz_data.payload, buffer, txlen);
  330. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
  331. NULL, NULL);
  332. if (ret) {
  333. ath10k_warn(ar, "unable to write to the device\n");
  334. return ret;
  335. }
  336. buffer += txlen;
  337. length -= txlen;
  338. }
  339. return 0;
  340. }
  341. int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)
  342. {
  343. struct bmi_cmd cmd;
  344. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);
  345. int ret;
  346. ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",
  347. address);
  348. if (ar->bmi.done_sent) {
  349. ath10k_warn(ar, "command disallowed\n");
  350. return -EBUSY;
  351. }
  352. cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START);
  353. cmd.lz_start.addr = __cpu_to_le32(address);
  354. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
  355. if (ret) {
  356. ath10k_warn(ar, "unable to Start LZ Stream to the device\n");
  357. return ret;
  358. }
  359. return 0;
  360. }
  361. int ath10k_bmi_fast_download(struct ath10k *ar,
  362. u32 address, const void *buffer, u32 length)
  363. {
  364. u8 trailer[4] = {};
  365. u32 head_len = rounddown(length, 4);
  366. u32 trailer_len = length - head_len;
  367. int ret;
  368. ath10k_dbg(ar, ATH10K_DBG_BMI,
  369. "bmi fast download address 0x%x buffer 0x%pK length %d\n",
  370. address, buffer, length);
  371. ret = ath10k_bmi_lz_stream_start(ar, address);
  372. if (ret)
  373. return ret;
  374. /* copy the last word into a zero padded buffer */
  375. if (trailer_len > 0)
  376. memcpy(trailer, buffer + head_len, trailer_len);
  377. if (ar->hw_params.bmi_large_size_download)
  378. ret = ath10k_bmi_lz_data_large(ar, buffer, head_len);
  379. else
  380. ret = ath10k_bmi_lz_data(ar, buffer, head_len);
  381. if (ret)
  382. return ret;
  383. if (trailer_len > 0)
  384. ret = ath10k_bmi_lz_data(ar, trailer, 4);
  385. if (ret != 0)
  386. return ret;
  387. /*
  388. * Close compressed stream and open a new (fake) one.
  389. * This serves mainly to flush Target caches.
  390. */
  391. ret = ath10k_bmi_lz_stream_start(ar, 0x00);
  392. return ret;
  393. }
  394. int ath10k_bmi_set_start(struct ath10k *ar, u32 address)
  395. {
  396. struct bmi_cmd cmd;
  397. u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start);
  398. int ret;
  399. if (ar->bmi.done_sent) {
  400. ath10k_warn(ar, "bmi set start command disallowed\n");
  401. return -EBUSY;
  402. }
  403. cmd.id = __cpu_to_le32(BMI_SET_APP_START);
  404. cmd.set_app_start.addr = __cpu_to_le32(address);
  405. ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);
  406. if (ret) {
  407. ath10k_warn(ar, "unable to set start to the device:%d\n", ret);
  408. return ret;
  409. }
  410. return 0;
  411. }