scu.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (c) 2017 Intel Corporation
  4. *
  5. * Intel Mobile Internet Devices (MID) based on Intel Atom SoCs have few
  6. * microcontrollers inside to do some auxiliary tasks. One of such
  7. * microcontroller is System Controller Unit (SCU) which, in particular,
  8. * is servicing watchdog and controlling system reset function.
  9. *
  10. * This driver enables IPC channel to SCU.
  11. */
  12. #include <common.h>
  13. #include <dm.h>
  14. #include <regmap.h>
  15. #include <syscon.h>
  16. #include <asm/cpu.h>
  17. #include <asm/scu.h>
  18. #include <linux/errno.h>
  19. #include <linux/io.h>
  20. #include <linux/kernel.h>
  21. /* SCU register map */
  22. struct ipc_regs {
  23. u32 cmd;
  24. u32 status;
  25. u32 sptr;
  26. u32 dptr;
  27. u32 reserved[28];
  28. u32 wbuf[4];
  29. u32 rbuf[4];
  30. };
  31. struct scu {
  32. struct ipc_regs *regs;
  33. };
  34. /**
  35. * scu_ipc_send_command() - send command to SCU
  36. * @regs: register map of SCU
  37. * @cmd: command
  38. *
  39. * Command Register (Write Only):
  40. * A write to this register results in an interrupt to the SCU core processor
  41. * Format:
  42. * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
  43. */
  44. static void scu_ipc_send_command(struct ipc_regs *regs, u32 cmd)
  45. {
  46. writel(cmd, &regs->cmd);
  47. }
  48. /**
  49. * scu_ipc_check_status() - check status of last command
  50. * @regs: register map of SCU
  51. *
  52. * Status Register (Read Only):
  53. * Driver will read this register to get the ready/busy status of the IPC
  54. * block and error status of the IPC command that was just processed by SCU
  55. * Format:
  56. * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
  57. */
  58. static int scu_ipc_check_status(struct ipc_regs *regs)
  59. {
  60. int loop_count = 100000;
  61. int status;
  62. do {
  63. status = readl(&regs->status);
  64. if (!(status & BIT(0)))
  65. break;
  66. udelay(1);
  67. } while (--loop_count);
  68. if (!loop_count)
  69. return -ETIMEDOUT;
  70. if (status & BIT(1)) {
  71. printf("%s() status=0x%08x\n", __func__, status);
  72. return -EIO;
  73. }
  74. return 0;
  75. }
  76. static int scu_ipc_cmd(struct ipc_regs *regs, u32 cmd, u32 sub,
  77. u32 *in, int inlen, u32 *out, int outlen)
  78. {
  79. int i, err;
  80. for (i = 0; i < inlen; i++)
  81. writel(*in++, &regs->wbuf[i]);
  82. scu_ipc_send_command(regs, (inlen << 16) | (sub << 12) | cmd);
  83. err = scu_ipc_check_status(regs);
  84. if (!err) {
  85. for (i = 0; i < outlen; i++)
  86. *out++ = readl(&regs->rbuf[i]);
  87. }
  88. return err;
  89. }
  90. /**
  91. * scu_ipc_simple_command() - send a simple command
  92. * @cmd: command
  93. * @sub: sub type
  94. *
  95. * Issue a simple command to the SCU. Do not use this interface if
  96. * you must then access data as any data values may be overwritten
  97. * by another SCU access by the time this function returns.
  98. *
  99. * This function may sleep. Locking for SCU accesses is handled for
  100. * the caller.
  101. */
  102. int scu_ipc_simple_command(u32 cmd, u32 sub)
  103. {
  104. struct scu *scu;
  105. struct udevice *dev;
  106. int ret;
  107. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  108. if (ret)
  109. return ret;
  110. scu = dev_get_priv(dev);
  111. scu_ipc_send_command(scu->regs, sub << 12 | cmd);
  112. return scu_ipc_check_status(scu->regs);
  113. }
  114. int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen)
  115. {
  116. struct scu *scu;
  117. struct udevice *dev;
  118. int ret;
  119. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  120. if (ret)
  121. return ret;
  122. scu = dev_get_priv(dev);
  123. return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen);
  124. }
  125. static int scu_ipc_probe(struct udevice *dev)
  126. {
  127. struct scu *scu = dev_get_priv(dev);
  128. scu->regs = syscon_get_first_range(X86_SYSCON_SCU);
  129. return 0;
  130. }
  131. static const struct udevice_id scu_ipc_match[] = {
  132. { .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU },
  133. { /* sentinel */ }
  134. };
  135. U_BOOT_DRIVER(scu_ipc) = {
  136. .name = "scu_ipc",
  137. .id = UCLASS_SYSCON,
  138. .of_match = scu_ipc_match,
  139. .probe = scu_ipc_probe,
  140. .priv_auto_alloc_size = sizeof(struct scu),
  141. };