intf.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*
  2. * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. #include <assert.h>
  7. #include <bpmp_ipc.h>
  8. #include <common/debug.h>
  9. #include <drivers/delay_timer.h>
  10. #include <errno.h>
  11. #include <lib/mmio.h>
  12. #include <lib/utils_def.h>
  13. #include <stdbool.h>
  14. #include <string.h>
  15. #include <tegra_def.h>
  16. #include "intf.h"
  17. #include "ivc.h"
  18. /**
  19. * Holds IVC channel data
  20. */
  21. struct ccplex_bpmp_channel_data {
  22. /* Buffer for incoming data */
  23. struct frame_data *ib;
  24. /* Buffer for outgoing data */
  25. struct frame_data *ob;
  26. };
  27. static struct ccplex_bpmp_channel_data s_channel;
  28. static struct ivc ivc_ccplex_bpmp_channel;
  29. /*
  30. * Helper functions to access the HSP doorbell registers
  31. */
  32. static inline uint32_t hsp_db_read(uint32_t reg)
  33. {
  34. return mmio_read_32((uint32_t)(TEGRA_HSP_DBELL_BASE + reg));
  35. }
  36. static inline void hsp_db_write(uint32_t reg, uint32_t val)
  37. {
  38. mmio_write_32((uint32_t)(TEGRA_HSP_DBELL_BASE + reg), val);
  39. }
  40. /*******************************************************************************
  41. * IVC wrappers for CCPLEX <-> BPMP communication.
  42. ******************************************************************************/
  43. static void tegra_bpmp_ring_bpmp_doorbell(void);
  44. /*
  45. * Get the next frame where data can be written.
  46. */
  47. static struct frame_data *tegra_bpmp_get_next_out_frame(void)
  48. {
  49. struct frame_data *frame;
  50. const struct ivc *ch = &ivc_ccplex_bpmp_channel;
  51. frame = (struct frame_data *)tegra_ivc_write_get_next_frame(ch);
  52. if (frame == NULL) {
  53. ERROR("%s: Error in getting next frame, exiting\n", __func__);
  54. } else {
  55. s_channel.ob = frame;
  56. }
  57. return frame;
  58. }
  59. static void tegra_bpmp_signal_slave(void)
  60. {
  61. (void)tegra_ivc_write_advance(&ivc_ccplex_bpmp_channel);
  62. tegra_bpmp_ring_bpmp_doorbell();
  63. }
  64. static int32_t tegra_bpmp_free_master(void)
  65. {
  66. return tegra_ivc_read_advance(&ivc_ccplex_bpmp_channel);
  67. }
  68. static bool tegra_bpmp_slave_acked(void)
  69. {
  70. struct frame_data *frame;
  71. bool ret = true;
  72. frame = (struct frame_data *)tegra_ivc_read_get_next_frame(&ivc_ccplex_bpmp_channel);
  73. if (frame == NULL) {
  74. ret = false;
  75. } else {
  76. s_channel.ib = frame;
  77. }
  78. return ret;
  79. }
  80. static struct frame_data *tegra_bpmp_get_cur_in_frame(void)
  81. {
  82. return s_channel.ib;
  83. }
  84. /*
  85. * Enables BPMP to ring CCPlex doorbell
  86. */
  87. static void tegra_bpmp_enable_ccplex_doorbell(void)
  88. {
  89. uint32_t reg;
  90. reg = hsp_db_read(HSP_DBELL_1_ENABLE);
  91. reg |= HSP_MASTER_BPMP_BIT;
  92. hsp_db_write(HSP_DBELL_1_ENABLE, reg);
  93. }
  94. /*
  95. * CCPlex rings the BPMP doorbell
  96. */
  97. static void tegra_bpmp_ring_bpmp_doorbell(void)
  98. {
  99. /*
  100. * Any writes to this register has the same effect,
  101. * uses master ID of the write transaction and set
  102. * corresponding flag.
  103. */
  104. hsp_db_write(HSP_DBELL_3_TRIGGER, HSP_MASTER_CCPLEX_BIT);
  105. }
  106. /*
  107. * Returns true if CCPLex can ring BPMP doorbell, otherwise false.
  108. * This also signals that BPMP is up and ready.
  109. */
  110. static bool tegra_bpmp_can_ccplex_ring_doorbell(void)
  111. {
  112. uint32_t reg;
  113. /* check if ccplex can communicate with bpmp */
  114. reg = hsp_db_read(HSP_DBELL_3_ENABLE);
  115. return ((reg & HSP_MASTER_CCPLEX_BIT) != 0U);
  116. }
  117. static int32_t tegra_bpmp_wait_for_slave_ack(void)
  118. {
  119. uint32_t timeout = TIMEOUT_RESPONSE_FROM_BPMP_US;
  120. while (!tegra_bpmp_slave_acked() && (timeout != 0U)) {
  121. udelay(1);
  122. timeout--;
  123. };
  124. return ((timeout == 0U) ? -ETIMEDOUT : 0);
  125. }
  126. /*
  127. * Notification from the ivc layer
  128. */
  129. static void tegra_bpmp_ivc_notify(const struct ivc *ivc)
  130. {
  131. (void)(ivc);
  132. tegra_bpmp_ring_bpmp_doorbell();
  133. }
  134. /*
  135. * Atomic send/receive API, which means it waits until slave acks
  136. */
  137. static int32_t tegra_bpmp_ipc_send_req_atomic(uint32_t mrq, void *p_out,
  138. uint32_t size_out, void *p_in, uint32_t size_in)
  139. {
  140. struct frame_data *frame = tegra_bpmp_get_next_out_frame();
  141. const struct frame_data *f_in = NULL;
  142. int32_t ret = 0;
  143. void *p_fdata;
  144. if ((p_out == NULL) || (size_out > IVC_DATA_SZ_BYTES) ||
  145. (frame == NULL)) {
  146. ERROR("%s: invalid parameters, exiting\n", __func__);
  147. return -EINVAL;
  148. }
  149. /* prepare the command frame */
  150. frame->mrq = mrq;
  151. frame->flags = FLAG_DO_ACK;
  152. p_fdata = frame->data;
  153. (void)memcpy(p_fdata, p_out, (size_t)size_out);
  154. /* signal the slave */
  155. tegra_bpmp_signal_slave();
  156. /* wait for slave to ack */
  157. ret = tegra_bpmp_wait_for_slave_ack();
  158. if (ret < 0) {
  159. ERROR("%s: wait for slave failed (%d)\n", __func__, ret);
  160. return ret;
  161. }
  162. /* retrieve the response frame */
  163. if ((size_in <= IVC_DATA_SZ_BYTES) && (p_in != NULL)) {
  164. f_in = tegra_bpmp_get_cur_in_frame();
  165. if (f_in != NULL) {
  166. ERROR("Failed to get next input frame!\n");
  167. } else {
  168. (void)memcpy(p_in, p_fdata, (size_t)size_in);
  169. }
  170. }
  171. ret = tegra_bpmp_free_master();
  172. if (ret < 0) {
  173. ERROR("%s: free master failed (%d)\n", __func__, ret);
  174. }
  175. return ret;
  176. }
  177. /*
  178. * Initializes the BPMP<--->CCPlex communication path.
  179. */
  180. int32_t tegra_bpmp_ipc_init(void)
  181. {
  182. size_t msg_size;
  183. uint32_t frame_size, timeout;
  184. int32_t error = 0;
  185. /* allow bpmp to ring CCPLEX's doorbell */
  186. tegra_bpmp_enable_ccplex_doorbell();
  187. /* wait for BPMP to actually ring the doorbell */
  188. timeout = TIMEOUT_RESPONSE_FROM_BPMP_US;
  189. while ((timeout != 0U) && !tegra_bpmp_can_ccplex_ring_doorbell()) {
  190. udelay(1); /* bpmp turn-around time */
  191. timeout--;
  192. }
  193. if (timeout == 0U) {
  194. ERROR("%s: BPMP firmware is not ready\n", __func__);
  195. return -ENOTSUP;
  196. }
  197. INFO("%s: BPMP handshake completed\n", __func__);
  198. msg_size = tegra_ivc_align(IVC_CMD_SZ_BYTES);
  199. frame_size = (uint32_t)tegra_ivc_total_queue_size(msg_size);
  200. if (frame_size > TEGRA_BPMP_IPC_CH_MAP_SIZE) {
  201. ERROR("%s: carveout size is not sufficient\n", __func__);
  202. return -EINVAL;
  203. }
  204. error = tegra_ivc_init(&ivc_ccplex_bpmp_channel,
  205. (uint32_t)TEGRA_BPMP_IPC_RX_PHYS_BASE,
  206. (uint32_t)TEGRA_BPMP_IPC_TX_PHYS_BASE,
  207. 1U, frame_size, tegra_bpmp_ivc_notify);
  208. if (error != 0) {
  209. ERROR("%s: IVC init failed (%d)\n", __func__, error);
  210. } else {
  211. /* reset channel */
  212. tegra_ivc_channel_reset(&ivc_ccplex_bpmp_channel);
  213. /* wait for notification from BPMP */
  214. while (tegra_ivc_channel_notified(&ivc_ccplex_bpmp_channel) != 0) {
  215. /*
  216. * Interrupt BPMP with doorbell each time after
  217. * tegra_ivc_channel_notified() returns non zero
  218. * value.
  219. */
  220. tegra_bpmp_ring_bpmp_doorbell();
  221. }
  222. INFO("%s: All communication channels initialized\n", __func__);
  223. }
  224. return error;
  225. }
  226. /* Handler to reset a hardware module */
  227. int32_t tegra_bpmp_ipc_reset_module(uint32_t rst_id)
  228. {
  229. int32_t ret;
  230. struct mrq_reset_request req = {
  231. .cmd = (uint32_t)CMD_RESET_MODULE,
  232. .reset_id = rst_id
  233. };
  234. /* only GPCDMA/XUSB_PADCTL resets are supported */
  235. assert((rst_id == TEGRA_RESET_ID_XUSB_PADCTL) ||
  236. (rst_id == TEGRA_RESET_ID_GPCDMA));
  237. ret = tegra_bpmp_ipc_send_req_atomic(MRQ_RESET, &req,
  238. (uint32_t)sizeof(req), NULL, 0);
  239. if (ret != 0) {
  240. ERROR("%s: failed for module %d with error %d\n", __func__,
  241. rst_id, ret);
  242. }
  243. return ret;
  244. }
  245. int tegra_bpmp_ipc_enable_clock(uint32_t clk_id)
  246. {
  247. int ret;
  248. struct mrq_clk_request req;
  249. /* only SE clocks are supported */
  250. if (clk_id != TEGRA_CLK_SE) {
  251. return -ENOTSUP;
  252. }
  253. /* prepare the MRQ_CLK command */
  254. req.cmd_and_id = make_mrq_clk_cmd(CMD_CLK_ENABLE, clk_id);
  255. ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
  256. NULL, 0);
  257. if (ret != 0) {
  258. ERROR("%s: failed for module %d with error %d\n", __func__,
  259. clk_id, ret);
  260. }
  261. return ret;
  262. }
  263. int tegra_bpmp_ipc_disable_clock(uint32_t clk_id)
  264. {
  265. int ret;
  266. struct mrq_clk_request req;
  267. /* only SE clocks are supported */
  268. if (clk_id != TEGRA_CLK_SE) {
  269. return -ENOTSUP;
  270. }
  271. /* prepare the MRQ_CLK command */
  272. req.cmd_and_id = make_mrq_clk_cmd(CMD_CLK_DISABLE, clk_id);
  273. ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
  274. NULL, 0);
  275. if (ret != 0) {
  276. ERROR("%s: failed for module %d with error %d\n", __func__,
  277. clk_id, ret);
  278. }
  279. return ret;
  280. }