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