scu.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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/bitops.h>
  19. #include <linux/delay.h>
  20. #include <linux/errno.h>
  21. #include <linux/io.h>
  22. #include <linux/kernel.h>
  23. /* SCU register map */
  24. struct ipc_regs {
  25. u32 cmd;
  26. u32 status;
  27. u32 sptr;
  28. u32 dptr;
  29. u32 reserved[28];
  30. u32 wbuf[4];
  31. u32 rbuf[4];
  32. };
  33. struct scu {
  34. struct ipc_regs *regs;
  35. };
  36. /**
  37. * scu_ipc_send_command() - send command to SCU
  38. * @regs: register map of SCU
  39. * @cmd: command
  40. *
  41. * Command Register (Write Only):
  42. * A write to this register results in an interrupt to the SCU core processor
  43. * Format:
  44. * |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
  45. */
  46. static void scu_ipc_send_command(struct ipc_regs *regs, u32 cmd)
  47. {
  48. writel(cmd, &regs->cmd);
  49. }
  50. /**
  51. * scu_ipc_check_status() - check status of last command
  52. * @regs: register map of SCU
  53. *
  54. * Status Register (Read Only):
  55. * Driver will read this register to get the ready/busy status of the IPC
  56. * block and error status of the IPC command that was just processed by SCU
  57. * Format:
  58. * |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
  59. */
  60. static int scu_ipc_check_status(struct ipc_regs *regs)
  61. {
  62. int loop_count = 100000;
  63. int status;
  64. do {
  65. status = readl(&regs->status);
  66. if (!(status & BIT(0)))
  67. break;
  68. udelay(1);
  69. } while (--loop_count);
  70. if (!loop_count)
  71. return -ETIMEDOUT;
  72. if (status & BIT(1)) {
  73. printf("%s() status=0x%08x\n", __func__, status);
  74. return -EIO;
  75. }
  76. return 0;
  77. }
  78. static int scu_ipc_cmd(struct ipc_regs *regs, u32 cmd, u32 sub,
  79. u32 *in, int inlen, u32 *out, int outlen)
  80. {
  81. int i, err;
  82. for (i = 0; i < inlen; i++)
  83. writel(*in++, &regs->wbuf[i]);
  84. scu_ipc_send_command(regs, (inlen << 16) | (sub << 12) | cmd);
  85. err = scu_ipc_check_status(regs);
  86. if (!err) {
  87. for (i = 0; i < outlen; i++)
  88. *out++ = readl(&regs->rbuf[i]);
  89. }
  90. return err;
  91. }
  92. /**
  93. * scu_ipc_raw_command() - IPC command with data and pointers
  94. * @cmd: IPC command code
  95. * @sub: IPC command sub type
  96. * @in: input data of this IPC command
  97. * @inlen: input data length in dwords
  98. * @out: output data of this IPC command
  99. * @outlen: output data length in dwords
  100. * @dptr: data writing to SPTR register
  101. * @sptr: data writing to DPTR register
  102. *
  103. * Send an IPC command to SCU with input/output data and source/dest pointers.
  104. *
  105. * Return: an IPC error code or 0 on success.
  106. */
  107. int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out,
  108. int outlen, u32 dptr, u32 sptr)
  109. {
  110. int inbuflen = DIV_ROUND_UP(inlen, 4);
  111. struct udevice *dev;
  112. struct scu *scu;
  113. int ret;
  114. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  115. if (ret)
  116. return ret;
  117. scu = dev_get_priv(dev);
  118. /* Up to 16 bytes */
  119. if (inbuflen > 4)
  120. return -EINVAL;
  121. writel(dptr, &scu->regs->dptr);
  122. writel(sptr, &scu->regs->sptr);
  123. /*
  124. * SRAM controller doesn't support 8-bit writes, it only
  125. * supports 32-bit writes, so we have to copy input data into
  126. * the temporary buffer, and SCU FW will use the inlen to
  127. * determine the actual input data length in the temporary
  128. * buffer.
  129. */
  130. u32 inbuf[4] = {0};
  131. memcpy(inbuf, in, inlen);
  132. return scu_ipc_cmd(scu->regs, cmd, sub, inbuf, inlen, out, outlen);
  133. }
  134. /**
  135. * scu_ipc_simple_command() - send a simple command
  136. * @cmd: command
  137. * @sub: sub type
  138. *
  139. * Issue a simple command to the SCU. Do not use this interface if
  140. * you must then access data as any data values may be overwritten
  141. * by another SCU access by the time this function returns.
  142. *
  143. * This function may sleep. Locking for SCU accesses is handled for
  144. * the caller.
  145. */
  146. int scu_ipc_simple_command(u32 cmd, u32 sub)
  147. {
  148. struct scu *scu;
  149. struct udevice *dev;
  150. int ret;
  151. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  152. if (ret)
  153. return ret;
  154. scu = dev_get_priv(dev);
  155. scu_ipc_send_command(scu->regs, sub << 12 | cmd);
  156. return scu_ipc_check_status(scu->regs);
  157. }
  158. /**
  159. * scu_ipc_command - command with data
  160. * @cmd: command
  161. * @sub: sub type
  162. * @in: input data
  163. * @inlen: input length in dwords
  164. * @out: output data
  165. * @outlen: output length in dwords
  166. *
  167. * Issue a command to the SCU which involves data transfers.
  168. */
  169. int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen)
  170. {
  171. struct scu *scu;
  172. struct udevice *dev;
  173. int ret;
  174. ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
  175. if (ret)
  176. return ret;
  177. scu = dev_get_priv(dev);
  178. return scu_ipc_cmd(scu->regs, cmd, sub, in, inlen, out, outlen);
  179. }
  180. static int scu_ipc_probe(struct udevice *dev)
  181. {
  182. struct scu *scu = dev_get_priv(dev);
  183. scu->regs = syscon_get_first_range(X86_SYSCON_SCU);
  184. return 0;
  185. }
  186. static const struct udevice_id scu_ipc_match[] = {
  187. { .compatible = "intel,scu-ipc", .data = X86_SYSCON_SCU },
  188. { /* sentinel */ }
  189. };
  190. U_BOOT_DRIVER(scu_ipc) = {
  191. .name = "scu_ipc",
  192. .id = UCLASS_SYSCON,
  193. .of_match = scu_ipc_match,
  194. .probe = scu_ipc_probe,
  195. .priv_auto_alloc_size = sizeof(struct scu),
  196. };