mhu_wrapper_v3_x.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. #include <assert.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include <string.h>
  10. #include <drivers/arm/mhu.h>
  11. #include "mhu_v3_x.h"
  12. #define MHU_NOTIFY_VALUE U(1234)
  13. #ifndef ALIGN_UP
  14. #define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
  15. #endif
  16. /*
  17. * MHUv3 Wrapper utility macros
  18. */
  19. #define IS_ALIGNED(val, align) (val == ALIGN_UP(val, align))
  20. /*
  21. * MHU devices for host:
  22. * HSE: Host to Secure Enclave (sender device)
  23. * SEH: Secure Enclave to Host (receiver device)
  24. */
  25. struct mhu_v3_x_dev_t mhu_hse_dev = {0, MHU_V3_X_PBX_FRAME};
  26. struct mhu_v3_x_dev_t mhu_seh_dev = {0, MHU_V3_X_MBX_FRAME};
  27. /* MHUv3 driver error to MHUv3 wrapper error mapping */
  28. static enum mhu_error_t error_mapping_to_mhu_error_t(enum mhu_v3_x_error_t err)
  29. {
  30. switch (err) {
  31. case MHU_V_3_X_ERR_NONE:
  32. return MHU_ERR_NONE;
  33. case MHU_V_3_X_ERR_NOT_INIT:
  34. return MHU_ERR_NOT_INIT;
  35. case MHU_V_3_X_ERR_UNSUPPORTED_VERSION:
  36. return MHU_ERR_UNSUPPORTED_VERSION;
  37. case MHU_V_3_X_ERR_UNSUPPORTED:
  38. return MHU_ERR_UNSUPPORTED;
  39. case MHU_V_3_X_ERR_INVALID_PARAM:
  40. return MHU_ERR_INVALID_ARG;
  41. default:
  42. return MHU_ERR_GENERAL;
  43. }
  44. }
  45. static enum mhu_error_t signal_and_wait_for_clear(
  46. void *mhu_sender_dev, uint32_t value)
  47. {
  48. enum mhu_v3_x_error_t err;
  49. struct mhu_v3_x_dev_t *dev;
  50. uint8_t num_channels;
  51. uint32_t read_val;
  52. dev = (struct mhu_v3_x_dev_t *)mhu_sender_dev;
  53. if ((dev == NULL) || (dev->base == 0)) {
  54. return MHU_ERR_INVALID_ARG;
  55. }
  56. err = mhu_v3_x_get_num_channel_implemented(dev,
  57. MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels);
  58. if (err != MHU_V_3_X_ERR_NONE) {
  59. return error_mapping_to_mhu_error_t(err);
  60. }
  61. /* Wait for any pending acknowledgment from transmitter side */
  62. do {
  63. err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
  64. if (err != MHU_V_3_X_ERR_NONE) {
  65. return error_mapping_to_mhu_error_t(err);
  66. }
  67. } while ((read_val & value) == value);
  68. /* Use the last channel to notify that a transfer is ready */
  69. err = mhu_v3_x_doorbell_write(dev, num_channels - 1, value);
  70. if (err != MHU_V_3_X_ERR_NONE) {
  71. return error_mapping_to_mhu_error_t(err);
  72. }
  73. /* Wait until receiver side acknowledges the transfer */
  74. do {
  75. err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
  76. if (err != MHU_V_3_X_ERR_NONE) {
  77. return error_mapping_to_mhu_error_t(err);
  78. }
  79. } while ((read_val & value) == value);
  80. return error_mapping_to_mhu_error_t(MHU_V_3_X_ERR_NONE);
  81. }
  82. static enum mhu_error_t wait_for_signal(
  83. void *mhu_receiver_dev, uint32_t value)
  84. {
  85. enum mhu_v3_x_error_t err;
  86. struct mhu_v3_x_dev_t *dev;
  87. uint32_t read_val;
  88. uint8_t num_channels;
  89. dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev;
  90. if ((dev == NULL) || (dev->base == 0)) {
  91. return MHU_ERR_INVALID_ARG;
  92. }
  93. err = mhu_v3_x_get_num_channel_implemented(dev,
  94. MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels);
  95. if (err != MHU_V_3_X_ERR_NONE) {
  96. return error_mapping_to_mhu_error_t(err);
  97. }
  98. do {
  99. err = mhu_v3_x_doorbell_read(dev, num_channels - 1, &read_val);
  100. if (err != MHU_V_3_X_ERR_NONE) {
  101. return error_mapping_to_mhu_error_t(err);
  102. }
  103. } while (read_val != value);
  104. return error_mapping_to_mhu_error_t(err);
  105. }
  106. static enum mhu_error_t clear_and_wait_for_signal(
  107. void *mhu_receiver_dev, uint32_t value)
  108. {
  109. enum mhu_v3_x_error_t err;
  110. struct mhu_v3_x_dev_t *dev;
  111. uint8_t num_channels;
  112. dev = (struct mhu_v3_x_dev_t *)mhu_receiver_dev;
  113. if ((dev == NULL) || (dev->base == 0)) {
  114. return MHU_ERR_INVALID_ARG;
  115. }
  116. err = mhu_v3_x_get_num_channel_implemented(dev,
  117. MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels);
  118. if (err != MHU_V_3_X_ERR_NONE) {
  119. return error_mapping_to_mhu_error_t(err);
  120. }
  121. /* Clear all channels */
  122. for (int i = 0; i < num_channels; i++) {
  123. err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX);
  124. if (err != MHU_V_3_X_ERR_NONE) {
  125. return error_mapping_to_mhu_error_t(err);
  126. }
  127. }
  128. return wait_for_signal(mhu_receiver_dev, value);
  129. }
  130. static enum mhu_error_t validate_buffer_params(uintptr_t buf_addr)
  131. {
  132. if ((buf_addr == 0) || (!IS_ALIGNED(buf_addr, sizeof(uint32_t)))) {
  133. return MHU_ERR_INVALID_ARG;
  134. }
  135. return MHU_ERR_NONE;
  136. }
  137. enum mhu_error_t mhu_init_sender(uintptr_t mhu_sender_base)
  138. {
  139. enum mhu_v3_x_error_t err;
  140. struct mhu_v3_x_dev_t *dev;
  141. uint8_t num_ch;
  142. uint32_t ch;
  143. assert(mhu_sender_base != (uintptr_t)NULL);
  144. mhu_hse_dev.base = mhu_sender_base;
  145. dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev;
  146. /* Initialize MHUv3 */
  147. err = mhu_v3_x_driver_init(dev);
  148. if (err != MHU_V_3_X_ERR_NONE) {
  149. return error_mapping_to_mhu_error_t(err);
  150. }
  151. /* Read the number of doorbell channels implemented in the MHU */
  152. err = mhu_v3_x_get_num_channel_implemented(
  153. dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch);
  154. if (err != MHU_V_3_X_ERR_NONE) {
  155. return error_mapping_to_mhu_error_t(err);
  156. } else if (num_ch < 2) {
  157. /* This wrapper requires at least two channels implemented */
  158. return MHU_ERR_UNSUPPORTED;
  159. }
  160. /*
  161. * The sender polls the postbox doorbell channel window status register
  162. * to get notified about successful transfer. So, disable the doorbell
  163. * channel's contribution to postbox combined interrupt.
  164. *
  165. * Also, clear and disable the postbox doorbell channel transfer
  166. * acknowledge interrupt.
  167. */
  168. for (ch = 0; ch < num_ch; ch++) {
  169. err = mhu_v3_x_channel_interrupt_disable(
  170. dev, ch, MHU_V3_X_CHANNEL_TYPE_DBCH);
  171. if (err != MHU_V_3_X_ERR_NONE) {
  172. return error_mapping_to_mhu_error_t(err);
  173. }
  174. }
  175. return MHU_ERR_NONE;
  176. }
  177. enum mhu_error_t mhu_init_receiver(uintptr_t mhu_receiver_base)
  178. {
  179. enum mhu_v3_x_error_t err;
  180. struct mhu_v3_x_dev_t *dev;
  181. uint32_t ch;
  182. uint8_t num_ch;
  183. assert(mhu_receiver_base != (uintptr_t)NULL);
  184. mhu_seh_dev.base = mhu_receiver_base;
  185. dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev;
  186. /* Initialize MHUv3 */
  187. err = mhu_v3_x_driver_init(dev);
  188. if (err != MHU_V_3_X_ERR_NONE) {
  189. return error_mapping_to_mhu_error_t(err);
  190. }
  191. /* Read the number of doorbell channels implemented in the MHU */
  192. err = mhu_v3_x_get_num_channel_implemented(
  193. dev, MHU_V3_X_CHANNEL_TYPE_DBCH, &num_ch);
  194. if (err != MHU_V_3_X_ERR_NONE) {
  195. return error_mapping_to_mhu_error_t(err);
  196. } else if (num_ch < 2) {
  197. /* This wrapper requires at least two channels implemented */
  198. return MHU_ERR_UNSUPPORTED;
  199. }
  200. /* Mask all channels except the notifying channel */
  201. for (ch = 0; ch < (num_ch - 1); ch++) {
  202. /* Mask interrupts on channels used for data */
  203. err = mhu_v3_x_doorbell_mask_set(dev, ch, UINT32_MAX);
  204. if (err != MHU_V_3_X_ERR_NONE) {
  205. return error_mapping_to_mhu_error_t(err);
  206. }
  207. }
  208. /* Unmask doorbell notification channel interrupt */
  209. err = mhu_v3_x_doorbell_mask_clear(dev, (num_ch - 1), UINT32_MAX);
  210. if (err != MHU_V_3_X_ERR_NONE) {
  211. return error_mapping_to_mhu_error_t(err);
  212. }
  213. /*
  214. * Enable the doorbell channel's contribution to mailbox combined
  215. * interrupt.
  216. */
  217. err = mhu_v3_x_channel_interrupt_enable(dev, (num_ch - 1),
  218. MHU_V3_X_CHANNEL_TYPE_DBCH);
  219. if (err != MHU_V_3_X_ERR_NONE) {
  220. return error_mapping_to_mhu_error_t(err);
  221. }
  222. return MHU_ERR_NONE;
  223. }
  224. /*
  225. * Public function. See mhu.h
  226. *
  227. * The basic steps of transferring a message:
  228. * 1. Send the size of the payload on Channel 0. It is the very first Bytes of
  229. * the transfer. Continue with Channel 1.
  230. * 2. Send the payload, writing the channels one after the other (4 Bytes
  231. * each). The last available channel is reserved for controlling the
  232. * transfer. When the last channel is reached or no more data is left, STOP.
  233. * 3. Notify the receiver using the last channel and wait for acknowledge. If
  234. * there is still data to transfer, jump to step 2. Otherwise, proceed.
  235. *
  236. */
  237. enum mhu_error_t mhu_send_data(const uint8_t *send_buffer, size_t size)
  238. {
  239. enum mhu_error_t mhu_err;
  240. enum mhu_v3_x_error_t mhu_v3_err;
  241. uint8_t num_channels;
  242. uint8_t chan;
  243. uint32_t *buffer;
  244. struct mhu_v3_x_dev_t *dev;
  245. if (size == 0) {
  246. return MHU_ERR_NONE;
  247. }
  248. dev = (struct mhu_v3_x_dev_t *)&mhu_hse_dev;
  249. chan = 0;
  250. if ((dev == NULL) || (dev->base == 0)) {
  251. return MHU_ERR_INVALID_ARG;
  252. }
  253. mhu_err = validate_buffer_params((uintptr_t)send_buffer);
  254. if (mhu_err != MHU_ERR_NONE) {
  255. return mhu_err;
  256. }
  257. mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev,
  258. MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels);
  259. if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
  260. return error_mapping_to_mhu_error_t(mhu_v3_err);
  261. }
  262. /* First send the size of the actual message. */
  263. mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, (uint32_t)size);
  264. if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
  265. return error_mapping_to_mhu_error_t(mhu_v3_err);
  266. }
  267. chan++;
  268. buffer = (uint32_t *)send_buffer;
  269. for (size_t i = 0; i < size; i += 4) {
  270. mhu_v3_err = mhu_v3_x_doorbell_write(dev, chan, *buffer++);
  271. if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
  272. return error_mapping_to_mhu_error_t(mhu_v3_err);
  273. }
  274. if (++chan == (num_channels - 1)) {
  275. /* Use the last channel to notify transfer complete */
  276. mhu_err = signal_and_wait_for_clear(
  277. dev, MHU_NOTIFY_VALUE);
  278. if (mhu_err != MHU_ERR_NONE) {
  279. return mhu_err;
  280. }
  281. chan = 0;
  282. }
  283. }
  284. if (chan != 0) {
  285. /* Use the last channel to notify transfer complete */
  286. mhu_err = signal_and_wait_for_clear(dev, MHU_NOTIFY_VALUE);
  287. if (mhu_err != MHU_ERR_NONE) {
  288. return mhu_err;
  289. }
  290. }
  291. return MHU_ERR_NONE;
  292. }
  293. /*
  294. * Public function. See mhu.h
  295. *
  296. * The basic steps of receiving a message:
  297. * 1. Read the size of the payload from Channel 0. It is the very first
  298. * 4 Bytes of the transfer. Continue with Channel 1.
  299. * 2. Receive the payload, read the channels one after the other
  300. * (4 Bytes each). The last available channel is reserved for controlling
  301. * the transfer.
  302. * When the last channel is reached clear all the channels
  303. * (also sending an acknowledge on the last channel).
  304. * 3. If there is still data to receive wait for a notification on the last
  305. * channel and jump to step 2 as soon as it arrived. Otherwise, proceed.
  306. *
  307. */
  308. enum mhu_error_t mhu_receive_data(uint8_t *receive_buffer, size_t *size)
  309. {
  310. enum mhu_error_t mhu_err;
  311. enum mhu_v3_x_error_t mhu_v3_err;
  312. uint32_t msg_len;
  313. uint8_t num_channels;
  314. uint8_t chan;
  315. uint32_t *buffer;
  316. struct mhu_v3_x_dev_t *dev;
  317. dev = (struct mhu_v3_x_dev_t *)&mhu_seh_dev;
  318. chan = 0;
  319. mhu_err = validate_buffer_params((uintptr_t)receive_buffer);
  320. if (mhu_err != MHU_ERR_NONE) {
  321. return mhu_err;
  322. }
  323. mhu_v3_err = mhu_v3_x_get_num_channel_implemented(dev,
  324. MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels);
  325. if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
  326. return error_mapping_to_mhu_error_t(mhu_v3_err);
  327. }
  328. /* Busy wait for incoming reply */
  329. mhu_err = wait_for_signal(dev, MHU_NOTIFY_VALUE);
  330. if (mhu_err != MHU_ERR_NONE) {
  331. return mhu_err;
  332. }
  333. /* The first word is the length of the actual message. */
  334. mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, &msg_len);
  335. if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
  336. return error_mapping_to_mhu_error_t(mhu_v3_err);
  337. }
  338. chan++;
  339. if (*size < msg_len) {
  340. /* Message buffer too small */
  341. *size = msg_len;
  342. return MHU_ERR_BUFFER_TOO_SMALL;
  343. }
  344. buffer = (uint32_t *)receive_buffer;
  345. for (size_t i = 0; i < msg_len; i += 4) {
  346. mhu_v3_err = mhu_v3_x_doorbell_read(dev, chan, buffer++);
  347. if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
  348. return error_mapping_to_mhu_error_t(mhu_v3_err);
  349. }
  350. /* Only wait for next transfer if still missing data. */
  351. if (++chan == (num_channels - 1) && (msg_len - i) > 4) {
  352. /* Busy wait for next transfer */
  353. mhu_err = clear_and_wait_for_signal(
  354. dev, MHU_NOTIFY_VALUE);
  355. if (mhu_err != MHU_ERR_NONE) {
  356. return mhu_err;
  357. }
  358. chan = 0;
  359. }
  360. }
  361. /* Clear all channels */
  362. for (uint8_t i = U(0); i < num_channels; i++) {
  363. mhu_v3_err = mhu_v3_x_doorbell_clear(dev, i, UINT32_MAX);
  364. if (mhu_v3_err != MHU_V_3_X_ERR_NONE) {
  365. return error_mapping_to_mhu_error_t(mhu_v3_err);
  366. }
  367. }
  368. *size = msg_len;
  369. return MHU_ERR_NONE;
  370. }
  371. size_t mhu_get_max_message_size(void)
  372. {
  373. enum mhu_v3_x_error_t err __maybe_unused;
  374. uint8_t num_channels;
  375. err = mhu_v3_x_get_num_channel_implemented(&mhu_seh_dev,
  376. MHU_V3_X_CHANNEL_TYPE_DBCH, &num_channels);
  377. assert(err == MHU_V_3_X_ERR_NONE);
  378. assert(num_channels != U(0));
  379. /*
  380. * Returns only usable size of memory. As one channel is specifically
  381. * used to inform about the size of payload, discard it from available
  382. * memory size.
  383. */
  384. return (num_channels - 1) * sizeof(uint32_t);
  385. }