rockchip-mailbox.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /*
  2. * Copyright (c) 2015, Fuzhou Rockchip Electronics Co., Ltd
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms and conditions of the GNU General Public License,
  6. * version 2, as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope it will be useful, but WITHOUT
  9. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. * more details.
  12. */
  13. #include <linux/clk.h>
  14. #include <linux/interrupt.h>
  15. #include <linux/io.h>
  16. #include <linux/kernel.h>
  17. #include <linux/mailbox_controller.h>
  18. #include <linux/module.h>
  19. #include <linux/of_device.h>
  20. #include <linux/platform_device.h>
  21. #define MAILBOX_A2B_INTEN 0x00
  22. #define MAILBOX_A2B_STATUS 0x04
  23. #define MAILBOX_A2B_CMD(x) (0x08 + (x) * 8)
  24. #define MAILBOX_A2B_DAT(x) (0x0c + (x) * 8)
  25. #define MAILBOX_B2A_INTEN 0x28
  26. #define MAILBOX_B2A_STATUS 0x2C
  27. #define MAILBOX_B2A_CMD(x) (0x30 + (x) * 8)
  28. #define MAILBOX_B2A_DAT(x) (0x34 + (x) * 8)
  29. struct rockchip_mbox_msg {
  30. u32 cmd;
  31. int rx_size;
  32. };
  33. struct rockchip_mbox_data {
  34. int num_chans;
  35. };
  36. struct rockchip_mbox_chan {
  37. int idx;
  38. int irq;
  39. struct rockchip_mbox_msg *msg;
  40. struct rockchip_mbox *mb;
  41. };
  42. struct rockchip_mbox {
  43. struct mbox_controller mbox;
  44. struct clk *pclk;
  45. void __iomem *mbox_base;
  46. /* The maximum size of buf for each channel */
  47. u32 buf_size;
  48. struct rockchip_mbox_chan *chans;
  49. };
  50. static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data)
  51. {
  52. struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev);
  53. struct rockchip_mbox_msg *msg = data;
  54. struct rockchip_mbox_chan *chans = mb->chans;
  55. if (!msg)
  56. return -EINVAL;
  57. if (msg->rx_size > mb->buf_size) {
  58. dev_err(mb->mbox.dev, "Transmit size over buf size(%d)\n",
  59. mb->buf_size);
  60. return -EINVAL;
  61. }
  62. dev_dbg(mb->mbox.dev, "Chan[%d]: A2B message, cmd 0x%08x\n",
  63. chans->idx, msg->cmd);
  64. mb->chans[chans->idx].msg = msg;
  65. writel_relaxed(msg->cmd, mb->mbox_base + MAILBOX_A2B_CMD(chans->idx));
  66. writel_relaxed(msg->rx_size, mb->mbox_base +
  67. MAILBOX_A2B_DAT(chans->idx));
  68. return 0;
  69. }
  70. static int rockchip_mbox_startup(struct mbox_chan *chan)
  71. {
  72. struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev);
  73. /* Enable all B2A interrupts */
  74. writel_relaxed((1 << mb->mbox.num_chans) - 1,
  75. mb->mbox_base + MAILBOX_B2A_INTEN);
  76. return 0;
  77. }
  78. static void rockchip_mbox_shutdown(struct mbox_chan *chan)
  79. {
  80. struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev);
  81. struct rockchip_mbox_chan *chans = mb->chans;
  82. /* Disable all B2A interrupts */
  83. writel_relaxed(0, mb->mbox_base + MAILBOX_B2A_INTEN);
  84. mb->chans[chans->idx].msg = NULL;
  85. }
  86. static const struct mbox_chan_ops rockchip_mbox_chan_ops = {
  87. .send_data = rockchip_mbox_send_data,
  88. .startup = rockchip_mbox_startup,
  89. .shutdown = rockchip_mbox_shutdown,
  90. };
  91. static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id)
  92. {
  93. int idx;
  94. struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id;
  95. u32 status = readl_relaxed(mb->mbox_base + MAILBOX_B2A_STATUS);
  96. for (idx = 0; idx < mb->mbox.num_chans; idx++) {
  97. if ((status & (1 << idx)) && (irq == mb->chans[idx].irq)) {
  98. /* Clear mbox interrupt */
  99. writel_relaxed(1 << idx,
  100. mb->mbox_base + MAILBOX_B2A_STATUS);
  101. return IRQ_WAKE_THREAD;
  102. }
  103. }
  104. return IRQ_NONE;
  105. }
  106. static irqreturn_t rockchip_mbox_isr(int irq, void *dev_id)
  107. {
  108. int idx;
  109. struct rockchip_mbox_msg *msg = NULL;
  110. struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id;
  111. for (idx = 0; idx < mb->mbox.num_chans; idx++) {
  112. if (irq != mb->chans[idx].irq)
  113. continue;
  114. msg = mb->chans[idx].msg;
  115. if (!msg) {
  116. dev_err(mb->mbox.dev,
  117. "Chan[%d]: B2A message is NULL\n", idx);
  118. break; /* spurious */
  119. }
  120. mbox_chan_received_data(&mb->mbox.chans[idx], msg);
  121. mb->chans[idx].msg = NULL;
  122. dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x\n",
  123. idx, msg->cmd);
  124. break;
  125. }
  126. return IRQ_HANDLED;
  127. }
  128. static const struct rockchip_mbox_data rk3368_drv_data = {
  129. .num_chans = 4,
  130. };
  131. static const struct of_device_id rockchip_mbox_of_match[] = {
  132. { .compatible = "rockchip,rk3368-mailbox", .data = &rk3368_drv_data},
  133. { },
  134. };
  135. MODULE_DEVICE_TABLE(of, rockchp_mbox_of_match);
  136. static int rockchip_mbox_probe(struct platform_device *pdev)
  137. {
  138. struct rockchip_mbox *mb;
  139. const struct of_device_id *match;
  140. const struct rockchip_mbox_data *drv_data;
  141. struct resource *res;
  142. int ret, irq, i;
  143. if (!pdev->dev.of_node)
  144. return -ENODEV;
  145. match = of_match_node(rockchip_mbox_of_match, pdev->dev.of_node);
  146. drv_data = (const struct rockchip_mbox_data *)match->data;
  147. mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
  148. if (!mb)
  149. return -ENOMEM;
  150. mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
  151. sizeof(*mb->chans), GFP_KERNEL);
  152. if (!mb->chans)
  153. return -ENOMEM;
  154. mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
  155. sizeof(*mb->mbox.chans), GFP_KERNEL);
  156. if (!mb->mbox.chans)
  157. return -ENOMEM;
  158. platform_set_drvdata(pdev, mb);
  159. mb->mbox.dev = &pdev->dev;
  160. mb->mbox.num_chans = drv_data->num_chans;
  161. mb->mbox.ops = &rockchip_mbox_chan_ops;
  162. mb->mbox.txdone_irq = true;
  163. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  164. if (!res)
  165. return -ENODEV;
  166. mb->mbox_base = devm_ioremap_resource(&pdev->dev, res);
  167. if (IS_ERR(mb->mbox_base))
  168. return PTR_ERR(mb->mbox_base);
  169. /* Each channel has two buffers for A2B and B2A */
  170. mb->buf_size = (size_t)resource_size(res) / (drv_data->num_chans * 2);
  171. mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox");
  172. if (IS_ERR(mb->pclk)) {
  173. ret = PTR_ERR(mb->pclk);
  174. dev_err(&pdev->dev, "failed to get pclk_mailbox clock: %d\n",
  175. ret);
  176. return ret;
  177. }
  178. ret = clk_prepare_enable(mb->pclk);
  179. if (ret) {
  180. dev_err(&pdev->dev, "failed to enable pclk: %d\n", ret);
  181. return ret;
  182. }
  183. for (i = 0; i < mb->mbox.num_chans; i++) {
  184. irq = platform_get_irq(pdev, i);
  185. if (irq < 0)
  186. return irq;
  187. ret = devm_request_threaded_irq(&pdev->dev, irq,
  188. rockchip_mbox_irq,
  189. rockchip_mbox_isr, IRQF_ONESHOT,
  190. dev_name(&pdev->dev), mb);
  191. if (ret < 0)
  192. return ret;
  193. mb->chans[i].idx = i;
  194. mb->chans[i].irq = irq;
  195. mb->chans[i].mb = mb;
  196. mb->chans[i].msg = NULL;
  197. }
  198. ret = mbox_controller_register(&mb->mbox);
  199. if (ret < 0)
  200. dev_err(&pdev->dev, "Failed to register mailbox: %d\n", ret);
  201. return ret;
  202. }
  203. static int rockchip_mbox_remove(struct platform_device *pdev)
  204. {
  205. struct rockchip_mbox *mb = platform_get_drvdata(pdev);
  206. if (!mb)
  207. return -EINVAL;
  208. mbox_controller_unregister(&mb->mbox);
  209. return 0;
  210. }
  211. static struct platform_driver rockchip_mbox_driver = {
  212. .probe = rockchip_mbox_probe,
  213. .remove = rockchip_mbox_remove,
  214. .driver = {
  215. .name = "rockchip-mailbox",
  216. .of_match_table = of_match_ptr(rockchip_mbox_of_match),
  217. },
  218. };
  219. module_platform_driver(rockchip_mbox_driver);
  220. MODULE_LICENSE("GPL v2");
  221. MODULE_DESCRIPTION("Rockchip mailbox: communicate between CPU cores and MCU");
  222. MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
  223. MODULE_AUTHOR("Caesar Wang <wxt@rock-chips.com>");