mailbox-xgene-slimpro.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * APM X-Gene SLIMpro MailBox Driver
  3. *
  4. * Copyright (c) 2015, Applied Micro Circuits Corporation
  5. * Author: Feng Kan fkan@apm.com
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation; either version 2 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. #include <linux/acpi.h>
  22. #include <linux/delay.h>
  23. #include <linux/interrupt.h>
  24. #include <linux/io.h>
  25. #include <linux/mailbox_controller.h>
  26. #include <linux/module.h>
  27. #include <linux/of.h>
  28. #include <linux/platform_device.h>
  29. #include <linux/spinlock.h>
  30. #define MBOX_CON_NAME "slimpro-mbox"
  31. #define MBOX_REG_SET_OFFSET 0x1000
  32. #define MBOX_CNT 8
  33. #define MBOX_STATUS_AVAIL_MASK BIT(16)
  34. #define MBOX_STATUS_ACK_MASK BIT(0)
  35. /* Configuration and Status Registers */
  36. #define REG_DB_IN 0x00
  37. #define REG_DB_DIN0 0x04
  38. #define REG_DB_DIN1 0x08
  39. #define REG_DB_OUT 0x10
  40. #define REG_DB_DOUT0 0x14
  41. #define REG_DB_DOUT1 0x18
  42. #define REG_DB_STAT 0x20
  43. #define REG_DB_STATMASK 0x24
  44. /**
  45. * X-Gene SlimPRO mailbox channel information
  46. *
  47. * @dev: Device to which it is attached
  48. * @chan: Pointer to mailbox communication channel
  49. * @reg: Base address to access channel registers
  50. * @irq: Interrupt number of the channel
  51. * @rx_msg: Received message storage
  52. */
  53. struct slimpro_mbox_chan {
  54. struct device *dev;
  55. struct mbox_chan *chan;
  56. void __iomem *reg;
  57. int irq;
  58. u32 rx_msg[3];
  59. };
  60. /**
  61. * X-Gene SlimPRO Mailbox controller data
  62. *
  63. * X-Gene SlimPRO Mailbox controller has 8 commnunication channels.
  64. * Each channel has a separate IRQ number assgined to it.
  65. *
  66. * @mb_ctrl: Representation of the commnunication channel controller
  67. * @mc: Array of SlimPRO mailbox channels of the controller
  68. * @chans: Array of mailbox communication channels
  69. *
  70. */
  71. struct slimpro_mbox {
  72. struct mbox_controller mb_ctrl;
  73. struct slimpro_mbox_chan mc[MBOX_CNT];
  74. struct mbox_chan chans[MBOX_CNT];
  75. };
  76. static void mb_chan_send_msg(struct slimpro_mbox_chan *mb_chan, u32 *msg)
  77. {
  78. writel(msg[1], mb_chan->reg + REG_DB_DOUT0);
  79. writel(msg[2], mb_chan->reg + REG_DB_DOUT1);
  80. writel(msg[0], mb_chan->reg + REG_DB_OUT);
  81. }
  82. static void mb_chan_recv_msg(struct slimpro_mbox_chan *mb_chan)
  83. {
  84. mb_chan->rx_msg[1] = readl(mb_chan->reg + REG_DB_DIN0);
  85. mb_chan->rx_msg[2] = readl(mb_chan->reg + REG_DB_DIN1);
  86. mb_chan->rx_msg[0] = readl(mb_chan->reg + REG_DB_IN);
  87. }
  88. static int mb_chan_status_ack(struct slimpro_mbox_chan *mb_chan)
  89. {
  90. u32 val = readl(mb_chan->reg + REG_DB_STAT);
  91. if (val & MBOX_STATUS_ACK_MASK) {
  92. writel(MBOX_STATUS_ACK_MASK, mb_chan->reg + REG_DB_STAT);
  93. return 1;
  94. }
  95. return 0;
  96. }
  97. static int mb_chan_status_avail(struct slimpro_mbox_chan *mb_chan)
  98. {
  99. u32 val = readl(mb_chan->reg + REG_DB_STAT);
  100. if (val & MBOX_STATUS_AVAIL_MASK) {
  101. mb_chan_recv_msg(mb_chan);
  102. writel(MBOX_STATUS_AVAIL_MASK, mb_chan->reg + REG_DB_STAT);
  103. return 1;
  104. }
  105. return 0;
  106. }
  107. static irqreturn_t slimpro_mbox_irq(int irq, void *id)
  108. {
  109. struct slimpro_mbox_chan *mb_chan = id;
  110. if (mb_chan_status_ack(mb_chan))
  111. mbox_chan_txdone(mb_chan->chan, 0);
  112. if (mb_chan_status_avail(mb_chan))
  113. mbox_chan_received_data(mb_chan->chan, mb_chan->rx_msg);
  114. return IRQ_HANDLED;
  115. }
  116. static int slimpro_mbox_send_data(struct mbox_chan *chan, void *msg)
  117. {
  118. struct slimpro_mbox_chan *mb_chan = chan->con_priv;
  119. mb_chan_send_msg(mb_chan, msg);
  120. return 0;
  121. }
  122. static int slimpro_mbox_startup(struct mbox_chan *chan)
  123. {
  124. struct slimpro_mbox_chan *mb_chan = chan->con_priv;
  125. int rc;
  126. u32 val;
  127. rc = devm_request_irq(mb_chan->dev, mb_chan->irq, slimpro_mbox_irq, 0,
  128. MBOX_CON_NAME, mb_chan);
  129. if (unlikely(rc)) {
  130. dev_err(mb_chan->dev, "failed to register mailbox interrupt %d\n",
  131. mb_chan->irq);
  132. return rc;
  133. }
  134. /* Enable HW interrupt */
  135. writel(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK,
  136. mb_chan->reg + REG_DB_STAT);
  137. /* Unmask doorbell status interrupt */
  138. val = readl(mb_chan->reg + REG_DB_STATMASK);
  139. val &= ~(MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);
  140. writel(val, mb_chan->reg + REG_DB_STATMASK);
  141. return 0;
  142. }
  143. static void slimpro_mbox_shutdown(struct mbox_chan *chan)
  144. {
  145. struct slimpro_mbox_chan *mb_chan = chan->con_priv;
  146. u32 val;
  147. /* Mask doorbell status interrupt */
  148. val = readl(mb_chan->reg + REG_DB_STATMASK);
  149. val |= (MBOX_STATUS_ACK_MASK | MBOX_STATUS_AVAIL_MASK);
  150. writel(val, mb_chan->reg + REG_DB_STATMASK);
  151. devm_free_irq(mb_chan->dev, mb_chan->irq, mb_chan);
  152. }
  153. static const struct mbox_chan_ops slimpro_mbox_ops = {
  154. .send_data = slimpro_mbox_send_data,
  155. .startup = slimpro_mbox_startup,
  156. .shutdown = slimpro_mbox_shutdown,
  157. };
  158. static int slimpro_mbox_probe(struct platform_device *pdev)
  159. {
  160. struct slimpro_mbox *ctx;
  161. struct resource *regs;
  162. void __iomem *mb_base;
  163. int rc;
  164. int i;
  165. ctx = devm_kzalloc(&pdev->dev, sizeof(struct slimpro_mbox), GFP_KERNEL);
  166. if (!ctx)
  167. return -ENOMEM;
  168. platform_set_drvdata(pdev, ctx);
  169. regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  170. mb_base = devm_ioremap_resource(&pdev->dev, regs);
  171. if (IS_ERR(mb_base))
  172. return PTR_ERR(mb_base);
  173. /* Setup mailbox links */
  174. for (i = 0; i < MBOX_CNT; i++) {
  175. ctx->mc[i].irq = platform_get_irq(pdev, i);
  176. if (ctx->mc[i].irq < 0) {
  177. if (i == 0) {
  178. dev_err(&pdev->dev, "no available IRQ\n");
  179. return -EINVAL;
  180. }
  181. dev_info(&pdev->dev, "no IRQ for channel %d\n", i);
  182. break;
  183. }
  184. ctx->mc[i].dev = &pdev->dev;
  185. ctx->mc[i].reg = mb_base + i * MBOX_REG_SET_OFFSET;
  186. ctx->mc[i].chan = &ctx->chans[i];
  187. ctx->chans[i].con_priv = &ctx->mc[i];
  188. }
  189. /* Setup mailbox controller */
  190. ctx->mb_ctrl.dev = &pdev->dev;
  191. ctx->mb_ctrl.chans = ctx->chans;
  192. ctx->mb_ctrl.txdone_irq = true;
  193. ctx->mb_ctrl.ops = &slimpro_mbox_ops;
  194. ctx->mb_ctrl.num_chans = i;
  195. rc = mbox_controller_register(&ctx->mb_ctrl);
  196. if (rc) {
  197. dev_err(&pdev->dev,
  198. "APM X-Gene SLIMpro MailBox register failed:%d\n", rc);
  199. return rc;
  200. }
  201. dev_info(&pdev->dev, "APM X-Gene SLIMpro MailBox registered\n");
  202. return 0;
  203. }
  204. static int slimpro_mbox_remove(struct platform_device *pdev)
  205. {
  206. struct slimpro_mbox *smb = platform_get_drvdata(pdev);
  207. mbox_controller_unregister(&smb->mb_ctrl);
  208. return 0;
  209. }
  210. static const struct of_device_id slimpro_of_match[] = {
  211. {.compatible = "apm,xgene-slimpro-mbox" },
  212. { },
  213. };
  214. MODULE_DEVICE_TABLE(of, slimpro_of_match);
  215. #ifdef CONFIG_ACPI
  216. static const struct acpi_device_id slimpro_acpi_ids[] = {
  217. {"APMC0D01", 0},
  218. {}
  219. };
  220. MODULE_DEVICE_TABLE(acpi, slimpro_acpi_ids);
  221. #endif
  222. static struct platform_driver slimpro_mbox_driver = {
  223. .probe = slimpro_mbox_probe,
  224. .remove = slimpro_mbox_remove,
  225. .driver = {
  226. .name = "xgene-slimpro-mbox",
  227. .of_match_table = of_match_ptr(slimpro_of_match),
  228. .acpi_match_table = ACPI_PTR(slimpro_acpi_ids)
  229. },
  230. };
  231. static int __init slimpro_mbox_init(void)
  232. {
  233. return platform_driver_register(&slimpro_mbox_driver);
  234. }
  235. static void __exit slimpro_mbox_exit(void)
  236. {
  237. platform_driver_unregister(&slimpro_mbox_driver);
  238. }
  239. subsys_initcall(slimpro_mbox_init);
  240. module_exit(slimpro_mbox_exit);
  241. MODULE_DESCRIPTION("APM X-Gene SLIMpro Mailbox Driver");
  242. MODULE_LICENSE("GPL");