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