mhu_v3_x.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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 <stdbool.h>
  8. #include <stdint.h>
  9. #include "mhu_v3_x.h"
  10. #include "mhu_v3_x_private.h"
  11. /*
  12. * Get the device base from the device struct. Return an error if the dev is
  13. * invalid.
  14. */
  15. static enum mhu_v3_x_error_t get_dev_base(const struct mhu_v3_x_dev_t *dev,
  16. union _mhu_v3_x_frame_t **base)
  17. {
  18. if (dev == NULL) {
  19. return MHU_V_3_X_ERR_INVALID_PARAM;
  20. }
  21. /* Ensure driver has been initialized */
  22. if (dev->is_initialized == false) {
  23. return MHU_V_3_X_ERR_NOT_INIT;
  24. }
  25. *base = (union _mhu_v3_x_frame_t *)dev->base;
  26. return MHU_V_3_X_ERR_NONE;
  27. }
  28. enum mhu_v3_x_error_t mhu_v3_x_driver_init(struct mhu_v3_x_dev_t *dev)
  29. {
  30. uint32_t aidr = 0;
  31. uint8_t mhu_major_rev;
  32. union _mhu_v3_x_frame_t *p_mhu;
  33. if (dev == NULL) {
  34. return MHU_V_3_X_ERR_INVALID_PARAM;
  35. }
  36. /* Return if already initialized */
  37. if (dev->is_initialized == true) {
  38. return MHU_V_3_X_ERR_NONE;
  39. }
  40. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  41. /* Read revision from MHU hardware */
  42. if (dev->frame == MHU_V3_X_PBX_FRAME) {
  43. aidr = p_mhu->pbx_frame.pbx_ctrl_page.pbx_aidr;
  44. } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
  45. aidr = p_mhu->mbx_frame.mbx_ctrl_page.mbx_aidr;
  46. } else {
  47. /* Only PBX and MBX frames are supported. */
  48. return MHU_V_3_X_ERR_UNSUPPORTED;
  49. }
  50. /* Read the MHU Architecture Major Revision */
  51. mhu_major_rev =
  52. ((aidr & MHU_ARCH_MAJOR_REV_MASK) >> MHU_ARCH_MAJOR_REV_OFF);
  53. /* Return error if the MHU major revision is not 3 */
  54. if (mhu_major_rev != MHU_MAJOR_REV_V3) {
  55. /* Unsupported MHU version */
  56. return MHU_V_3_X_ERR_UNSUPPORTED_VERSION;
  57. }
  58. /* Read the MHU Architecture Minor Revision */
  59. dev->subversion =
  60. ((aidr & MHU_ARCH_MINOR_REV_MASK) >> MHU_ARCH_MINOR_REV_MASK);
  61. /* Return error if the MHU minor revision is not 0 */
  62. if (dev->subversion != MHU_MINOR_REV_3_0) {
  63. /* Unsupported subversion */
  64. return MHU_V_3_X_ERR_UNSUPPORTED_VERSION;
  65. }
  66. /* Initialize the Postbox/Mailbox to remain in operational state */
  67. if (dev->frame == MHU_V3_X_PBX_FRAME) {
  68. p_mhu->pbx_frame.pbx_ctrl_page.pbx_ctrl |= MHU_V3_OP_REQ;
  69. } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
  70. p_mhu->mbx_frame.mbx_ctrl_page.mbx_ctrl |= MHU_V3_OP_REQ;
  71. } else {
  72. /* Only PBX and MBX frames are supported. */
  73. return MHU_V_3_X_ERR_UNSUPPORTED;
  74. }
  75. dev->is_initialized = true;
  76. return MHU_V_3_X_ERR_NONE;
  77. }
  78. enum mhu_v3_x_error_t mhu_v3_x_get_num_channel_implemented(
  79. const struct mhu_v3_x_dev_t *dev,
  80. enum mhu_v3_x_channel_type_t ch_type, uint8_t *num_ch)
  81. {
  82. enum mhu_v3_x_error_t status;
  83. union _mhu_v3_x_frame_t *p_mhu;
  84. if (num_ch == NULL) {
  85. return MHU_V_3_X_ERR_INVALID_PARAM;
  86. }
  87. /* Get dev->base if it is valid or return an error if dev is not */
  88. status = get_dev_base(dev, &p_mhu);
  89. if (status != MHU_V_3_X_ERR_NONE) {
  90. return status;
  91. }
  92. /* Only doorbell channel is supported */
  93. if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
  94. return MHU_V_3_X_ERR_UNSUPPORTED;
  95. }
  96. /* Read the number of channels implemented in the MHU */
  97. if (dev->frame == MHU_V3_X_PBX_FRAME) {
  98. *num_ch = (p_mhu->pbx_frame.pbx_ctrl_page.pbx_dbch_cfg0 + 1);
  99. } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
  100. *num_ch = (p_mhu->mbx_frame.mbx_ctrl_page.mbx_dbch_cfg0 + 1);
  101. } else {
  102. /* Only PBX and MBX frames are supported. */
  103. return MHU_V_3_X_ERR_UNSUPPORTED;
  104. }
  105. return MHU_V_3_X_ERR_NONE;
  106. }
  107. enum mhu_v3_x_error_t mhu_v3_x_doorbell_clear(const struct mhu_v3_x_dev_t *dev,
  108. const uint32_t channel, uint32_t flags)
  109. {
  110. union _mhu_v3_x_frame_t *p_mhu;
  111. struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
  112. enum mhu_v3_x_error_t status;
  113. /* Get dev->base if it is valid or return an error if dev is not */
  114. status = get_dev_base(dev, &p_mhu);
  115. if (status != MHU_V_3_X_ERR_NONE) {
  116. return status;
  117. }
  118. /* Only MBX can clear the Doorbell channel */
  119. if (dev->frame != MHU_V3_X_MBX_FRAME) {
  120. return MHU_V_3_X_ERR_INVALID_PARAM;
  121. }
  122. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  123. mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
  124. &(p_mhu->mbx_frame.mdbcw_page);
  125. /* Clear the bits in the doorbell channel */
  126. mdbcw_reg[channel].mdbcw_clr |= flags;
  127. return MHU_V_3_X_ERR_NONE;
  128. }
  129. enum mhu_v3_x_error_t mhu_v3_x_doorbell_write(const struct mhu_v3_x_dev_t *dev,
  130. const uint32_t channel, uint32_t flags)
  131. {
  132. union _mhu_v3_x_frame_t *p_mhu;
  133. struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
  134. enum mhu_v3_x_error_t status;
  135. /* Get dev->base if it is valid or return an error if dev is not */
  136. status = get_dev_base(dev, &p_mhu);
  137. if (status != MHU_V_3_X_ERR_NONE) {
  138. return status;
  139. }
  140. /* Only PBX can set the Doorbell channel value */
  141. if (dev->frame != MHU_V3_X_PBX_FRAME) {
  142. return MHU_V_3_X_ERR_INVALID_PARAM;
  143. }
  144. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  145. pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
  146. &(p_mhu->pbx_frame.pdbcw_page);
  147. /* Write the value to the doorbell channel */
  148. pdbcw_reg[channel].pdbcw_set |= flags;
  149. return MHU_V_3_X_ERR_NONE;
  150. }
  151. enum mhu_v3_x_error_t mhu_v3_x_doorbell_read(const struct mhu_v3_x_dev_t *dev,
  152. const uint32_t channel, uint32_t *flags)
  153. {
  154. union _mhu_v3_x_frame_t *p_mhu;
  155. enum mhu_v3_x_error_t status;
  156. struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
  157. struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
  158. if (flags == NULL) {
  159. return MHU_V_3_X_ERR_INVALID_PARAM;
  160. }
  161. /* Get dev->base if it is valid or return an error if dev is not */
  162. status = get_dev_base(dev, &p_mhu);
  163. if (status != MHU_V_3_X_ERR_NONE) {
  164. return status;
  165. }
  166. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  167. if (dev->frame == MHU_V3_X_PBX_FRAME) {
  168. pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
  169. &(p_mhu->pbx_frame.pdbcw_page);
  170. /* Read the value from Postbox Doorbell status register */
  171. *flags = pdbcw_reg[channel].pdbcw_st;
  172. } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
  173. mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
  174. &(p_mhu->mbx_frame.mdbcw_page);
  175. /* Read the value from Mailbox Doorbell status register */
  176. *flags = mdbcw_reg[channel].mdbcw_st;
  177. } else {
  178. /* Only PBX and MBX frames are supported. */
  179. return MHU_V_3_X_ERR_UNSUPPORTED;
  180. }
  181. return MHU_V_3_X_ERR_NONE;
  182. }
  183. enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_set(
  184. const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
  185. uint32_t flags)
  186. {
  187. union _mhu_v3_x_frame_t *p_mhu;
  188. struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
  189. enum mhu_v3_x_error_t status;
  190. /* Get dev->base if it is valid or return an error if dev is not */
  191. status = get_dev_base(dev, &p_mhu);
  192. if (status != MHU_V_3_X_ERR_NONE) {
  193. return status;
  194. }
  195. /* Doorbell channel mask is not applicable for PBX */
  196. if (dev->frame != MHU_V3_X_MBX_FRAME) {
  197. return MHU_V_3_X_ERR_INVALID_PARAM;
  198. }
  199. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  200. mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
  201. &(p_mhu->mbx_frame.mdbcw_page);
  202. /* Set the Doorbell channel mask */
  203. mdbcw_reg[channel].mdbcw_msk_set |= flags;
  204. return MHU_V_3_X_ERR_NONE;
  205. }
  206. enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_clear(
  207. const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
  208. uint32_t flags)
  209. {
  210. union _mhu_v3_x_frame_t *p_mhu;
  211. struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
  212. enum mhu_v3_x_error_t status;
  213. /* Get dev->base if it is valid or return an error if dev is not */
  214. status = get_dev_base(dev, &p_mhu);
  215. if (status != MHU_V_3_X_ERR_NONE) {
  216. return status;
  217. }
  218. /* Doorbell channel mask is not applicable for PBX */
  219. if (dev->frame != MHU_V3_X_MBX_FRAME) {
  220. return MHU_V_3_X_ERR_INVALID_PARAM;
  221. }
  222. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  223. mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
  224. &(p_mhu->mbx_frame.mdbcw_page);
  225. /* Clear the Doorbell channel mask */
  226. mdbcw_reg[channel].mdbcw_msk_clr = flags;
  227. return MHU_V_3_X_ERR_NONE;
  228. }
  229. enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_get(
  230. const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
  231. uint32_t *flags)
  232. {
  233. union _mhu_v3_x_frame_t *p_mhu;
  234. struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
  235. enum mhu_v3_x_error_t status;
  236. if (flags == NULL) {
  237. return MHU_V_3_X_ERR_INVALID_PARAM;
  238. }
  239. /* Get dev->base if it is valid or return an error if dev is not */
  240. status = get_dev_base(dev, &p_mhu);
  241. if (status != MHU_V_3_X_ERR_NONE) {
  242. return status;
  243. }
  244. /* Doorbell channel mask is not applicable for PBX */
  245. if (dev->frame != MHU_V3_X_MBX_FRAME) {
  246. return MHU_V_3_X_ERR_INVALID_PARAM;
  247. }
  248. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  249. mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
  250. &(p_mhu->mbx_frame.mdbcw_page);
  251. /* Save the Doorbell channel mask status */
  252. *flags = mdbcw_reg[channel].mdbcw_msk_st;
  253. return MHU_V_3_X_ERR_NONE;
  254. }
  255. enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_enable(
  256. const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
  257. enum mhu_v3_x_channel_type_t ch_type)
  258. {
  259. enum mhu_v3_x_error_t status;
  260. union _mhu_v3_x_frame_t *p_mhu;
  261. struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
  262. struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
  263. /* Get dev->base if it is valid or return an error if dev is not */
  264. status = get_dev_base(dev, &p_mhu);
  265. if (status != MHU_V_3_X_ERR_NONE) {
  266. return status;
  267. }
  268. /* Only doorbell channel is supported */
  269. if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
  270. return MHU_V_3_X_ERR_UNSUPPORTED;
  271. }
  272. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  273. if (dev->frame == MHU_V3_X_PBX_FRAME) {
  274. pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
  275. &(p_mhu->pbx_frame.pdbcw_page);
  276. /*
  277. * Enable this doorbell channel to generate interrupts for
  278. * transfer acknowledge events.
  279. */
  280. pdbcw_reg[channel].pdbcw_int_en = MHU_V3_X_PDBCW_INT_X_TFR_ACK;
  281. /*
  282. * Enable this doorbell channel to contribute to the PBX
  283. * combined interrupt.
  284. */
  285. pdbcw_reg[channel].pdbcw_ctrl = MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN;
  286. } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
  287. mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
  288. &(p_mhu->mbx_frame.mdbcw_page);
  289. /*
  290. * Enable this doorbell channel to contribute to the MBX
  291. * combined interrupt.
  292. */
  293. mdbcw_reg[channel].mdbcw_ctrl = MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN;
  294. } else {
  295. /* Only PBX and MBX frames are supported. */
  296. return MHU_V_3_X_ERR_UNSUPPORTED;
  297. }
  298. return MHU_V_3_X_ERR_NONE;
  299. }
  300. enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_disable(
  301. const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
  302. enum mhu_v3_x_channel_type_t ch_type)
  303. {
  304. enum mhu_v3_x_error_t status;
  305. union _mhu_v3_x_frame_t *p_mhu;
  306. struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
  307. struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
  308. /* Get dev->base if it is valid or return an error if dev is not */
  309. status = get_dev_base(dev, &p_mhu);
  310. if (status != MHU_V_3_X_ERR_NONE) {
  311. return status;
  312. }
  313. /* Only doorbell channel is supported */
  314. if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
  315. return MHU_V_3_X_ERR_UNSUPPORTED;
  316. }
  317. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  318. if (dev->frame == MHU_V3_X_PBX_FRAME) {
  319. pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
  320. &(p_mhu->pbx_frame.pdbcw_page);
  321. /* Clear channel transfer acknowledge event interrupt */
  322. pdbcw_reg[channel].pdbcw_int_clr = MHU_V3_X_PDBCW_INT_X_TFR_ACK;
  323. /* Disable channel transfer acknowledge event interrupt */
  324. pdbcw_reg[channel].pdbcw_int_en &=
  325. ~(MHU_V3_X_PDBCW_INT_X_TFR_ACK);
  326. /*
  327. * Disable this doorbell channel from contributing to the PBX
  328. * combined interrupt.
  329. */
  330. pdbcw_reg[channel].pdbcw_ctrl &=
  331. ~(MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN);
  332. } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
  333. mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
  334. &(p_mhu->mbx_frame.mdbcw_page);
  335. /*
  336. * Disable this doorbell channel from contributing to the MBX
  337. * combined interrupt.
  338. */
  339. mdbcw_reg[channel].mdbcw_ctrl &=
  340. ~(MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN);
  341. } else {
  342. /* Only PBX and MBX frames are supported. */
  343. return MHU_V_3_X_ERR_UNSUPPORTED;
  344. }
  345. return MHU_V_3_X_ERR_NONE;
  346. }
  347. enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_clear(
  348. const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
  349. enum mhu_v3_x_channel_type_t ch_type)
  350. {
  351. enum mhu_v3_x_error_t status;
  352. union _mhu_v3_x_frame_t *p_mhu;
  353. struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
  354. /* Get dev->base if it is valid or return an error if dev is not */
  355. status = get_dev_base(dev, &p_mhu);
  356. if (status != MHU_V_3_X_ERR_NONE) {
  357. return status;
  358. }
  359. /* Only doorbell channel is supported */
  360. if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
  361. return MHU_V_3_X_ERR_UNSUPPORTED;
  362. }
  363. /*
  364. * Only postbox doorbell channel transfer acknowledge interrupt can be
  365. * cleared manually.
  366. *
  367. * To clear MBX interrupt the unmasked status must be cleared using
  368. * mhu_v3_x_doorbell_clear.
  369. */
  370. if (dev->frame != MHU_V3_X_PBX_FRAME) {
  371. return MHU_V_3_X_ERR_INVALID_PARAM;
  372. }
  373. p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
  374. pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)&(
  375. p_mhu->pbx_frame.pdbcw_page);
  376. /* Clear channel transfer acknowledge event interrupt */
  377. pdbcw_reg[channel].pdbcw_int_clr |= 0x1;
  378. return MHU_V_3_X_ERR_NONE;
  379. }