scu.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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_raw_command() - IPC command with data and pointers
  92. * @cmd: IPC command code
  93. * @sub: IPC command sub type
  94. * @in: input data of this IPC command
  95. * @inlen: input data length in dwords
  96. * @out: output data of this IPC command
  97. * @outlen: output data length in dwords
  98. * @dptr: data writing to SPTR register
  99. * @sptr: data writing to DPTR register
  100. *
  101. * Send an IPC command to SCU with input/output data and source/dest pointers.
  102. *
  103. * Return: an IPC error code or 0 on success.
  104. */
  105. int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out,
  106. int outlen, u32 dptr, u32 sptr)
  107. {
  108. int inbuflen = DIV_ROUND_UP(inlen, 4);
  109. struct udevice *dev;
  110. struct scu *scu;
  111. int ret;
  112. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  113. if (ret)
  114. return ret;
  115. scu = dev_get_priv(dev);
  116. /* Up to 16 bytes */
  117. if (inbuflen > 4)
  118. return -EINVAL;
  119. writel(dptr, &scu->regs->dptr);
  120. writel(sptr, &scu->regs->sptr);
  121. /*
  122. * SRAM controller doesn't support 8-bit writes, it only
  123. * supports 32-bit writes, so we have to copy input data into
  124. * the temporary buffer, and SCU FW will use the inlen to
  125. * determine the actual input data length in the temporary
  126. * buffer.
  127. */
  128. u32 inbuf[4] = {0};
  129. memcpy(inbuf, in, inlen);
  130. return scu_ipc_cmd(scu->regs, cmd, sub, inbuf, inlen, out, outlen);
  131. }
  132. /**
  133. * scu_ipc_simple_command() - send a simple command
  134. * @cmd: command
  135. * @sub: sub type
  136. *
  137. * Issue a simple command to the SCU. Do not use this interface if
  138. * you must then access data as any data values may be overwritten
  139. * by another SCU access by the time this function returns.
  140. *
  141. * This function may sleep. Locking for SCU accesses is handled for
  142. * the caller.
  143. */
  144. int scu_ipc_simple_command(u32 cmd, u32 sub)
  145. {
  146. struct scu *scu;
  147. struct udevice *dev;
  148. int ret;
  149. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  150. if (ret)
  151. return ret;
  152. scu = dev_get_priv(dev);
  153. scu_ipc_send_command(scu->regs, sub << 12 | cmd);
  154. return scu_ipc_check_status(scu->regs);
  155. }
  156. /**
  157. * scu_ipc_command - command with data
  158. * @cmd: command
  159. * @sub: sub type
  160. * @in: input data
  161. * @inlen: input length in dwords
  162. * @out: output data
  163. * @outlen: output length in dwords
  164. *
  165. * Issue a command to the SCU which involves data transfers.
  166. */
  167. int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen)
  168. {
  169. struct scu *scu;
  170. struct udevice *dev;
  171. int ret;
  172. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  173. if (ret)
  174. return ret;
  175. scu = dev_get_priv(dev);
  176. return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen);
  177. }
  178. static int scu_ipc_probe(struct udevice *dev)
  179. {
  180. struct scu *scu = dev_get_priv(dev);
  181. scu->regs = syscon_get_first_range(X86_SYSCON_SCU);
  182. return 0;
  183. }
  184. static const struct udevice_id scu_ipc_match[] = {
  185. { .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU },
  186. { /* sentinel */ }
  187. };
  188. U_BOOT_DRIVER(scu_ipc) = {
  189. .name = "scu_ipc",
  190. .id = UCLASS_SYSCON,
  191. .of_match = scu_ipc_match,
  192. .probe = scu_ipc_probe,
  193. .priv_auto_alloc_size = sizeof(struct scu),
  194. };