ScmiClockProtocol.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. /** @file
  2. Copyright (c) 2017-2021, Arm Limited. All rights reserved.
  3. SPDX-License-Identifier: BSD-2-Clause-Patent
  4. System Control and Management Interface V1.0
  5. http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/
  6. DEN0056A_System_Control_and_Management_Interface.pdf
  7. **/
  8. #include <Library/BaseLib.h>
  9. #include <Library/DebugLib.h>
  10. #include <Library/UefiBootServicesTableLib.h>
  11. #include <Protocol/ArmScmiClockProtocol.h>
  12. #include <Protocol/ArmScmiClock2Protocol.h>
  13. #include "ArmScmiClockProtocolPrivate.h"
  14. #include "ScmiPrivate.h"
  15. /** Convert to 64 bit value from two 32 bit words.
  16. @param[in] Low Lower 32 bits.
  17. @param[in] High Higher 32 bits.
  18. @retval UINT64 64 bit value.
  19. **/
  20. STATIC
  21. UINT64
  22. ConvertTo64Bit (
  23. IN UINT32 Low,
  24. IN UINT32 High
  25. )
  26. {
  27. return (Low | ((UINT64)High << 32));
  28. }
  29. /** Return version of the clock management protocol supported by SCP firmware.
  30. @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
  31. @param[out] Version Version of the supported SCMI Clock management protocol.
  32. @retval EFI_SUCCESS The version is returned.
  33. @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
  34. @retval !(EFI_SUCCESS) Other errors.
  35. **/
  36. STATIC
  37. EFI_STATUS
  38. ClockGetVersion (
  39. IN SCMI_CLOCK_PROTOCOL *This,
  40. OUT UINT32 *Version
  41. )
  42. {
  43. return ScmiGetProtocolVersion (ScmiProtocolIdClock, Version);
  44. }
  45. /** Return total number of clock devices supported by the clock management
  46. protocol.
  47. @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
  48. @param[out] TotalClocks Total number of clocks supported.
  49. @retval EFI_SUCCESS Total number of clocks supported is returned.
  50. @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
  51. @retval !(EFI_SUCCESS) Other errors.
  52. **/
  53. STATIC
  54. EFI_STATUS
  55. ClockGetTotalClocks (
  56. IN SCMI_CLOCK_PROTOCOL *This,
  57. OUT UINT32 *TotalClocks
  58. )
  59. {
  60. EFI_STATUS Status;
  61. UINT32 *ReturnValues;
  62. Status = ScmiGetProtocolAttributes (ScmiProtocolIdClock, &ReturnValues);
  63. if (EFI_ERROR (Status)) {
  64. return Status;
  65. }
  66. *TotalClocks = SCMI_CLOCK_PROTOCOL_TOTAL_CLKS (ReturnValues[0]);
  67. return EFI_SUCCESS;
  68. }
  69. /** Return attributes of a clock device.
  70. @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
  71. @param[in] ClockId Identifier for the clock device.
  72. @param[out] Enabled If TRUE, the clock device is enabled.
  73. @param[out] ClockAsciiName A NULL terminated ASCII string with the clock
  74. name, of up to 16 bytes.
  75. @retval EFI_SUCCESS Clock device attributes are returned.
  76. @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
  77. @retval !(EFI_SUCCESS) Other errors.
  78. **/
  79. STATIC
  80. EFI_STATUS
  81. ClockGetClockAttributes (
  82. IN SCMI_CLOCK_PROTOCOL *This,
  83. IN UINT32 ClockId,
  84. OUT BOOLEAN *Enabled,
  85. OUT CHAR8 *ClockAsciiName
  86. )
  87. {
  88. EFI_STATUS Status;
  89. UINT32 *MessageParams;
  90. CLOCK_ATTRIBUTES *ClockAttributes;
  91. SCMI_COMMAND Cmd;
  92. UINT32 PayloadLength;
  93. Status = ScmiCommandGetPayload (&MessageParams);
  94. if (EFI_ERROR (Status)) {
  95. return Status;
  96. }
  97. *MessageParams = ClockId;
  98. Cmd.ProtocolId = ScmiProtocolIdClock;
  99. Cmd.MessageId = ScmiMessageIdClockAttributes;
  100. PayloadLength = sizeof (ClockId);
  101. Status = ScmiCommandExecute (
  102. &Cmd,
  103. &PayloadLength,
  104. (UINT32 **)&ClockAttributes
  105. );
  106. if (EFI_ERROR (Status)) {
  107. return Status;
  108. }
  109. // TRUE if bit 0 of ClockAttributes->Attributes is set.
  110. *Enabled = CLOCK_ENABLED (ClockAttributes->Attributes);
  111. AsciiStrCpyS (
  112. ClockAsciiName,
  113. SCMI_MAX_STR_LEN,
  114. (CONST CHAR8 *)ClockAttributes->ClockName
  115. );
  116. return EFI_SUCCESS;
  117. }
  118. /** Return list of rates supported by a given clock device.
  119. @param[in] This A pointer to SCMI_CLOCK_PROTOCOL Instance.
  120. @param[in] ClockId Identifier for the clock device.
  121. @param[out] Format ScmiClockRateFormatDiscrete: Clock device
  122. supports range of clock rates which are non-linear.
  123. ScmiClockRateFormatLinear: Clock device supports
  124. range of linear clock rates from Min to Max in steps.
  125. @param[out] TotalRates Total number of rates.
  126. @param[in,out] RateArraySize Size of the RateArray.
  127. @param[out] RateArray List of clock rates.
  128. @retval EFI_SUCCESS List of clock rates is returned.
  129. @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
  130. @retval EFI_BUFFER_TOO_SMALL RateArraySize is too small for the result.
  131. It has been updated to the size needed.
  132. @retval !(EFI_SUCCESS) Other errors.
  133. **/
  134. STATIC
  135. EFI_STATUS
  136. ClockDescribeRates (
  137. IN SCMI_CLOCK_PROTOCOL *This,
  138. IN UINT32 ClockId,
  139. OUT SCMI_CLOCK_RATE_FORMAT *Format,
  140. OUT UINT32 *TotalRates,
  141. IN OUT UINT32 *RateArraySize,
  142. OUT SCMI_CLOCK_RATE *RateArray
  143. )
  144. {
  145. EFI_STATUS Status;
  146. UINT32 PayloadLength;
  147. SCMI_COMMAND Cmd;
  148. UINT32 *MessageParams;
  149. CLOCK_DESCRIBE_RATES *DescribeRates;
  150. CLOCK_RATE_DWORD *Rate;
  151. UINT32 RequiredArraySize;
  152. UINT32 RateIndex;
  153. UINT32 RateNo;
  154. UINT32 RateOffset;
  155. *TotalRates = 0;
  156. RequiredArraySize = 0;
  157. RateIndex = 0;
  158. Status = ScmiCommandGetPayload (&MessageParams);
  159. if (EFI_ERROR (Status)) {
  160. return Status;
  161. }
  162. Cmd.ProtocolId = ScmiProtocolIdClock;
  163. Cmd.MessageId = ScmiMessageIdClockDescribeRates;
  164. *MessageParams++ = ClockId;
  165. do {
  166. *MessageParams = RateIndex;
  167. // Set Payload length, note PayloadLength is a IN/OUT parameter.
  168. PayloadLength = sizeof (ClockId) + sizeof (RateIndex);
  169. // Execute and wait for response on a SCMI channel.
  170. Status = ScmiCommandExecute (
  171. &Cmd,
  172. &PayloadLength,
  173. (UINT32 **)&DescribeRates
  174. );
  175. if (EFI_ERROR (Status)) {
  176. return Status;
  177. }
  178. if (*TotalRates == 0) {
  179. // In the first iteration we will get number of returned rates and number
  180. // of remaining rates. With this information calculate required size
  181. // for rate array. If provided RateArraySize is less, return an
  182. // error.
  183. *Format = RATE_FORMAT (DescribeRates->NumRatesFlags);
  184. *TotalRates = NUM_RATES (DescribeRates->NumRatesFlags)
  185. + NUM_REMAIN_RATES (DescribeRates->NumRatesFlags);
  186. RequiredArraySize = (*TotalRates) * sizeof (UINT64);
  187. if (RequiredArraySize > (*RateArraySize)) {
  188. *RateArraySize = RequiredArraySize;
  189. return EFI_BUFFER_TOO_SMALL;
  190. }
  191. }
  192. RateOffset = 0;
  193. if (*Format == ScmiClockRateFormatDiscrete) {
  194. for (RateNo = 0; RateNo < NUM_RATES (DescribeRates->NumRatesFlags); RateNo++) {
  195. Rate = &DescribeRates->Rates[RateOffset++];
  196. // Non-linear discrete rates.
  197. RateArray[RateIndex++].DiscreteRate.Rate =
  198. ConvertTo64Bit (Rate->Low, Rate->High);
  199. }
  200. } else {
  201. // Linear clock rates from minimum to maximum in steps
  202. // Minimum clock rate.
  203. Rate = &DescribeRates->Rates[RateOffset++];
  204. RateArray[RateIndex].ContinuousRate.Min =
  205. ConvertTo64Bit (Rate->Low, Rate->High);
  206. Rate = &DescribeRates->Rates[RateOffset++];
  207. // Maximum clock rate.
  208. RateArray[RateIndex].ContinuousRate.Max =
  209. ConvertTo64Bit (Rate->Low, Rate->High);
  210. Rate = &DescribeRates->Rates[RateOffset++];
  211. // Step.
  212. RateArray[RateIndex++].ContinuousRate.Step =
  213. ConvertTo64Bit (Rate->Low, Rate->High);
  214. }
  215. } while (NUM_REMAIN_RATES (DescribeRates->NumRatesFlags) != 0);
  216. // Update RateArraySize with RequiredArraySize.
  217. *RateArraySize = RequiredArraySize;
  218. return EFI_SUCCESS;
  219. }
  220. /** Get clock rate.
  221. @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
  222. @param[in] ClockId Identifier for the clock device.
  223. @param[out] Rate Clock rate.
  224. @retval EFI_SUCCESS Clock rate is returned.
  225. @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
  226. @retval !(EFI_SUCCESS) Other errors.
  227. **/
  228. STATIC
  229. EFI_STATUS
  230. ClockRateGet (
  231. IN SCMI_CLOCK_PROTOCOL *This,
  232. IN UINT32 ClockId,
  233. OUT UINT64 *Rate
  234. )
  235. {
  236. EFI_STATUS Status;
  237. UINT32 *MessageParams;
  238. CLOCK_RATE_DWORD *ClockRate;
  239. SCMI_COMMAND Cmd;
  240. UINT32 PayloadLength;
  241. Status = ScmiCommandGetPayload (&MessageParams);
  242. if (EFI_ERROR (Status)) {
  243. return Status;
  244. }
  245. // Fill arguments for clock protocol command.
  246. *MessageParams = ClockId;
  247. Cmd.ProtocolId = ScmiProtocolIdClock;
  248. Cmd.MessageId = ScmiMessageIdClockRateGet;
  249. PayloadLength = sizeof (ClockId);
  250. // Execute and wait for response on a SCMI channel.
  251. Status = ScmiCommandExecute (
  252. &Cmd,
  253. &PayloadLength,
  254. (UINT32 **)&ClockRate
  255. );
  256. if (EFI_ERROR (Status)) {
  257. return Status;
  258. }
  259. *Rate = ConvertTo64Bit (ClockRate->Low, ClockRate->High);
  260. return EFI_SUCCESS;
  261. }
  262. /** Set clock rate.
  263. @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
  264. @param[in] ClockId Identifier for the clock device.
  265. @param[in] Rate Clock rate.
  266. @retval EFI_SUCCESS Clock rate set success.
  267. @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
  268. @retval !(EFI_SUCCESS) Other errors.
  269. **/
  270. STATIC
  271. EFI_STATUS
  272. ClockRateSet (
  273. IN SCMI_CLOCK_PROTOCOL *This,
  274. IN UINT32 ClockId,
  275. IN UINT64 Rate
  276. )
  277. {
  278. EFI_STATUS Status;
  279. CLOCK_RATE_SET_ATTRIBUTES *ClockRateSetAttributes;
  280. SCMI_COMMAND Cmd;
  281. UINT32 PayloadLength;
  282. Status = ScmiCommandGetPayload ((UINT32 **)&ClockRateSetAttributes);
  283. if (EFI_ERROR (Status)) {
  284. return Status;
  285. }
  286. // Fill arguments for clock protocol command.
  287. ClockRateSetAttributes->ClockId = ClockId;
  288. ClockRateSetAttributes->Flags = CLOCK_SET_DEFAULT_FLAGS;
  289. ClockRateSetAttributes->Rate.Low = (UINT32)Rate;
  290. ClockRateSetAttributes->Rate.High = (UINT32)(Rate >> 32);
  291. Cmd.ProtocolId = ScmiProtocolIdClock;
  292. Cmd.MessageId = ScmiMessageIdClockRateSet;
  293. PayloadLength = sizeof (CLOCK_RATE_SET_ATTRIBUTES);
  294. // Execute and wait for response on a SCMI channel.
  295. Status = ScmiCommandExecute (
  296. &Cmd,
  297. &PayloadLength,
  298. NULL
  299. );
  300. return Status;
  301. }
  302. /** Enable/Disable specified clock.
  303. @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
  304. @param[in] ClockId Identifier for the clock device.
  305. @param[in] Enable TRUE to enable, FALSE to disable.
  306. @retval EFI_SUCCESS Clock enable/disable successful.
  307. @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
  308. @retval !(EFI_SUCCESS) Other errors.
  309. **/
  310. STATIC
  311. EFI_STATUS
  312. ClockEnable (
  313. IN SCMI_CLOCK2_PROTOCOL *This,
  314. IN UINT32 ClockId,
  315. IN BOOLEAN Enable
  316. )
  317. {
  318. EFI_STATUS Status;
  319. CLOCK_CONFIG_SET_ATTRIBUTES *ClockConfigSetAttributes;
  320. SCMI_COMMAND Cmd;
  321. UINT32 PayloadLength;
  322. Status = ScmiCommandGetPayload ((UINT32 **)&ClockConfigSetAttributes);
  323. if (EFI_ERROR (Status)) {
  324. return Status;
  325. }
  326. // Fill arguments for clock protocol command.
  327. ClockConfigSetAttributes->ClockId = ClockId;
  328. ClockConfigSetAttributes->Attributes = Enable ? BIT0 : 0;
  329. Cmd.ProtocolId = ScmiProtocolIdClock;
  330. Cmd.MessageId = ScmiMessageIdClockConfigSet;
  331. PayloadLength = sizeof (CLOCK_CONFIG_SET_ATTRIBUTES);
  332. // Execute and wait for response on a SCMI channel.
  333. Status = ScmiCommandExecute (
  334. &Cmd,
  335. &PayloadLength,
  336. NULL
  337. );
  338. return Status;
  339. }
  340. // Instance of the SCMI clock management protocol.
  341. STATIC CONST SCMI_CLOCK_PROTOCOL ScmiClockProtocol = {
  342. ClockGetVersion,
  343. ClockGetTotalClocks,
  344. ClockGetClockAttributes,
  345. ClockDescribeRates,
  346. ClockRateGet,
  347. ClockRateSet
  348. };
  349. // Instance of the SCMI clock management protocol.
  350. STATIC CONST SCMI_CLOCK2_PROTOCOL ScmiClock2Protocol = {
  351. (SCMI_CLOCK2_GET_VERSION)ClockGetVersion,
  352. (SCMI_CLOCK2_GET_TOTAL_CLOCKS)ClockGetTotalClocks,
  353. (SCMI_CLOCK2_GET_CLOCK_ATTRIBUTES)ClockGetClockAttributes,
  354. (SCMI_CLOCK2_DESCRIBE_RATES)ClockDescribeRates,
  355. (SCMI_CLOCK2_RATE_GET)ClockRateGet,
  356. (SCMI_CLOCK2_RATE_SET)ClockRateSet,
  357. SCMI_CLOCK2_PROTOCOL_VERSION,
  358. ClockEnable
  359. };
  360. /** Initialize clock management protocol and install protocol on a given handle.
  361. @param[in] Handle Handle to install clock management protocol.
  362. @retval EFI_SUCCESS Clock protocol interface installed successfully.
  363. **/
  364. EFI_STATUS
  365. ScmiClockProtocolInit (
  366. IN EFI_HANDLE *Handle
  367. )
  368. {
  369. return gBS->InstallMultipleProtocolInterfaces (
  370. Handle,
  371. &gArmScmiClockProtocolGuid,
  372. &ScmiClockProtocol,
  373. &gArmScmiClock2ProtocolGuid,
  374. &ScmiClock2Protocol,
  375. NULL
  376. );
  377. }