light_aon.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2021 Alibaba Group Holding Limited.
  4. */
  5. #include <linux/err.h>
  6. #include <linux/firmware/thead/ipc.h>
  7. #include <linux/interrupt.h>
  8. #include <linux/irq.h>
  9. #include <linux/kernel.h>
  10. #include <linux/mailbox_client.h>
  11. #include <linux/module.h>
  12. #include <linux/mutex.h>
  13. #include <linux/of_platform.h>
  14. #include <linux/platform_device.h>
  15. #define MAX_RX_TIMEOUT (msecs_to_jiffies(300))
  16. #define MAX_TX_TIMEOUT (msecs_to_jiffies(500))
  17. struct light_aon_chan {
  18. struct light_aon_ipc *aon_ipc;
  19. struct mbox_client cl;
  20. struct mbox_chan *ch;
  21. struct completion tx_done;
  22. };
  23. struct light_aon_ipc {
  24. struct light_aon_chan chans;
  25. struct device *dev;
  26. struct mutex lock;
  27. struct completion done;
  28. u32 *msg;
  29. };
  30. /*
  31. * This type is used to indicate error response for most functions.
  32. */
  33. enum light_aon_error_codes {
  34. LIGHT_AON_ERR_NONE = 0, /* Success */
  35. LIGHT_AON_ERR_VERSION = 1, /* Incompatible API version */
  36. LIGHT_AON_ERR_CONFIG = 2, /* Configuration error */
  37. LIGHT_AON_ERR_PARM = 3, /* Bad parameter */
  38. LIGHT_AON_ERR_NOACCESS = 4, /* Permission error (no access) */
  39. LIGHT_AON_ERR_LOCKED = 5, /* Permission error (locked) */
  40. LIGHT_AON_ERR_UNAVAILABLE = 6, /* Unavailable (out of resources) */
  41. LIGHT_AON_ERR_NOTFOUND = 7, /* Not found */
  42. LIGHT_AON_ERR_NOPOWER = 8, /* No power */
  43. LIGHT_AON_ERR_IPC = 9, /* Generic IPC error */
  44. LIGHT_AON_ERR_BUSY = 10, /* Resource is currently busy/active */
  45. LIGHT_AON_ERR_FAIL = 11, /* General I/O failure */
  46. LIGHT_AON_ERR_LAST
  47. };
  48. static int light_aon_linux_errmap[LIGHT_AON_ERR_LAST] = {
  49. 0, /* LIGHT_AON_ERR_NONE */
  50. -EINVAL, /* LIGHT_AON_ERR_VERSION */
  51. -EINVAL, /* LIGHT_AON_ERR_CONFIG */
  52. -EINVAL, /* LIGHT_AON_ERR_PARM */
  53. -EACCES, /* LIGHT_AON_ERR_NOACCESS */
  54. -EACCES, /* LIGHT_AON_ERR_LOCKED */
  55. -ERANGE, /* LIGHT_AON_ERR_UNAVAILABLE */
  56. -EEXIST, /* LIGHT_AON_ERR_NOTFOUND */
  57. -EPERM, /* LIGHT_AON_ERR_NOPOWER */
  58. -EPIPE, /* LIGHT_AON_ERR_IPC */
  59. -EBUSY, /* LIGHT_AON_ERR_BUSY */
  60. -EIO, /* LIGHT_AON_ERR_FAIL */
  61. };
  62. static struct light_aon_ipc *light_aon_ipc_handle;
  63. static inline int light_aon_to_linux_errno(int errno)
  64. {
  65. if (errno >= LIGHT_AON_ERR_NONE && errno < LIGHT_AON_ERR_LAST)
  66. return light_aon_linux_errmap[errno];
  67. return -EIO;
  68. }
  69. /*
  70. * Get the default handle used by SCU
  71. */
  72. int light_aon_get_handle(struct light_aon_ipc **ipc)
  73. {
  74. if (!light_aon_ipc_handle)
  75. return -EPROBE_DEFER;
  76. *ipc = light_aon_ipc_handle;
  77. return 0;
  78. }
  79. EXPORT_SYMBOL(light_aon_get_handle);
  80. static void light_aon_tx_done(struct mbox_client *cl, void *mssg, int r)
  81. {
  82. struct light_aon_chan *aon_chan = container_of(cl, struct light_aon_chan, cl);
  83. complete(&aon_chan->tx_done);
  84. }
  85. static void light_aon_rx_callback(struct mbox_client *c, void *msg)
  86. {
  87. struct light_aon_chan *aon_chan = container_of(c, struct light_aon_chan, cl);
  88. struct light_aon_ipc *aon_ipc = aon_chan->aon_ipc;
  89. memcpy(aon_ipc->msg, msg, LIGHT_AON_RPC_MSG_NUM * sizeof(u32));
  90. dev_dbg(aon_ipc->dev, "msg head: 0x%x\n", *((u32 *)msg));
  91. complete(&aon_ipc->done);
  92. }
  93. static int light_aon_ipc_write(struct light_aon_ipc *aon_ipc, void *msg)
  94. {
  95. struct light_aon_rpc_msg_hdr *hdr = msg;
  96. struct light_aon_chan *aon_chan;
  97. u32 *data = msg;
  98. int ret;
  99. /* check size, currently it requires 7 MSG in one transfer */
  100. if (hdr->size != LIGHT_AON_RPC_MSG_NUM)
  101. return -EINVAL;
  102. dev_dbg(aon_ipc->dev, "RPC SVC %u FUNC %u SIZE %u\n", hdr->svc,
  103. hdr->func, hdr->size);
  104. aon_chan = &aon_ipc->chans;
  105. if (!wait_for_completion_timeout(&aon_chan->tx_done,
  106. MAX_TX_TIMEOUT)) {
  107. dev_err(aon_ipc->dev, "tx_done timeout\n");
  108. return -ETIMEDOUT;
  109. }
  110. reinit_completion(&aon_chan->tx_done);
  111. ret = mbox_send_message(aon_chan->ch, data);
  112. if (ret < 0)
  113. return ret;
  114. return 0;
  115. }
  116. /*
  117. * RPC command/response
  118. */
  119. int light_aon_call_rpc(struct light_aon_ipc *aon_ipc, void *msg, bool have_resp)
  120. {
  121. struct light_aon_rpc_msg_hdr *hdr;
  122. int ret;
  123. if (WARN_ON(!aon_ipc || !msg))
  124. return -EINVAL;
  125. mutex_lock(&aon_ipc->lock);
  126. reinit_completion(&aon_ipc->done);
  127. if (have_resp)
  128. aon_ipc->msg = msg;
  129. ret = light_aon_ipc_write(aon_ipc, msg);
  130. if (ret < 0) {
  131. dev_err(aon_ipc->dev, "RPC send msg failed: %d\n", ret);
  132. goto out;
  133. }
  134. if (have_resp) {
  135. if (!wait_for_completion_timeout(&aon_ipc->done,
  136. MAX_RX_TIMEOUT)) {
  137. dev_err(aon_ipc->dev, "RPC send msg timeout\n");
  138. mutex_unlock(&aon_ipc->lock);
  139. return -ETIMEDOUT;
  140. }
  141. /* response status is stored in hdr->func field */
  142. hdr = msg;
  143. ret = hdr->func;
  144. }
  145. out:
  146. mutex_unlock(&aon_ipc->lock);
  147. dev_dbg(aon_ipc->dev, "RPC SVC done\n");
  148. return light_aon_to_linux_errno(ret);
  149. }
  150. EXPORT_SYMBOL(light_aon_call_rpc);
  151. static int light_aon_probe(struct platform_device *pdev)
  152. {
  153. struct device *dev = &pdev->dev;
  154. struct light_aon_ipc *aon_ipc;
  155. struct light_aon_chan *aon_chan;
  156. struct mbox_client *cl;
  157. int ret;
  158. aon_ipc = devm_kzalloc(dev, sizeof(*aon_ipc), GFP_KERNEL);
  159. if (!aon_ipc)
  160. return -ENOMEM;
  161. aon_chan = &aon_ipc->chans;
  162. cl = &aon_chan->cl;
  163. cl->dev = dev;
  164. cl->tx_block = false;
  165. cl->knows_txdone = true;
  166. cl->rx_callback = light_aon_rx_callback;
  167. /* Initial tx_done completion as "done" */
  168. cl->tx_done = light_aon_tx_done;
  169. init_completion(&aon_chan->tx_done);
  170. complete(&aon_chan->tx_done);
  171. aon_chan->aon_ipc = aon_ipc;
  172. aon_chan->ch = mbox_request_channel_byname(cl, "aon");
  173. if (IS_ERR(aon_chan->ch)) {
  174. ret = PTR_ERR(aon_chan->ch);
  175. if (ret != -EPROBE_DEFER)
  176. dev_err(dev, "Failed to request aon mbox chan ret %d\n", ret);
  177. return ret;
  178. }
  179. dev_dbg(dev, "request thead mbox chan: aon\n");
  180. aon_ipc->dev = dev;
  181. mutex_init(&aon_ipc->lock);
  182. init_completion(&aon_ipc->done);
  183. light_aon_ipc_handle = aon_ipc;
  184. return devm_of_platform_populate(dev);
  185. }
  186. static const struct of_device_id light_aon_match[] = {
  187. { .compatible = "thead,light-aon", },
  188. { /* Sentinel */ }
  189. };
  190. static struct platform_driver light_aon_driver = {
  191. .driver = {
  192. .name = "light-aon",
  193. .of_match_table = light_aon_match,
  194. },
  195. .probe = light_aon_probe,
  196. };
  197. builtin_platform_driver(light_aon_driver);
  198. MODULE_AUTHOR("fugang.duan <duanfugang.dfg@linux.alibaba.com>");
  199. MODULE_DESCRIPTION("Thead Light firmware protocol driver");
  200. MODULE_LICENSE("GPL v2");