3 Copyright (c) 2017-2021, Arm Limited. All rights reserved.
5 SPDX-License-Identifier: BSD-2-Clause-Patent
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
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>
18 #include "ArmScmiClockProtocolPrivate.h"
19 #include "ScmiPrivate.h"
21 /** Convert to 64 bit value from two 32 bit words.
23 @param[in] Low Lower 32 bits.
24 @param[in] High Higher 32 bits.
26 @retval UINT64 64 bit value.
35 return (Low
| ((UINT64
)High
<< 32));
38 /** Return version of the clock management protocol supported by SCP firmware.
40 @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
42 @param[out] Version Version of the supported SCMI Clock management protocol.
44 @retval EFI_SUCCESS The version is returned.
45 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
46 @retval !(EFI_SUCCESS) Other errors.
51 IN SCMI_CLOCK_PROTOCOL
*This
,
55 return ScmiGetProtocolVersion (SCMI_PROTOCOL_ID_CLOCK
, Version
);
58 /** Return total number of clock devices supported by the clock management
61 @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
63 @param[out] TotalClocks Total number of clocks supported.
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.
72 IN SCMI_CLOCK_PROTOCOL
*This
,
73 OUT UINT32
*TotalClocks
79 Status
= ScmiGetProtocolAttributes (SCMI_PROTOCOL_ID_CLOCK
, &ReturnValues
);
80 if (EFI_ERROR (Status
)) {
84 *TotalClocks
= SCMI_CLOCK_PROTOCOL_TOTAL_CLKS (ReturnValues
[0]);
89 /** Return attributes of a clock device.
91 @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
92 @param[in] ClockId Identifier for the clock device.
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.
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.
104 ClockGetClockAttributes (
105 IN SCMI_CLOCK_PROTOCOL
*This
,
107 OUT BOOLEAN
*Enabled
,
108 OUT CHAR8
*ClockAsciiName
113 UINT32
*MessageParams
;
114 CLOCK_ATTRIBUTES
*ClockAttributes
;
116 UINT32 PayloadLength
;
118 Status
= ScmiCommandGetPayload (&MessageParams
);
119 if (EFI_ERROR (Status
)) {
123 *MessageParams
= ClockId
;
125 Cmd
.ProtocolId
= SCMI_PROTOCOL_ID_CLOCK
;
126 Cmd
.MessageId
= SCMI_MESSAGE_ID_CLOCK_ATTRIBUTES
;
128 PayloadLength
= sizeof (ClockId
);
130 Status
= ScmiCommandExecute (
133 (UINT32
**)&ClockAttributes
135 if (EFI_ERROR (Status
)) {
138 // TRUE if bit 0 of ClockAttributes->Attributes is set.
139 *Enabled
= CLOCK_ENABLED (ClockAttributes
->Attributes
);
144 (CONST CHAR8
*)ClockAttributes
->ClockName
150 /** Return list of rates supported by a given clock device.
152 @param[in] This A pointer to SCMI_CLOCK_PROTOCOL Instance.
153 @param[in] ClockId Identifier for the clock device.
155 @param[out] Format SCMI_CLOCK_RATE_FORMAT_DISCRETE: Clock device
156 supports range of clock rates which are non-linear.
158 SCMI_CLOCK_RATE_FORMAT_LINEAR: Clock device supports
159 range of linear clock rates from Min to Max in steps.
161 @param[out] TotalRates Total number of rates.
163 @param[in,out] RateArraySize Size of the RateArray.
165 @param[out] RateArray List of clock rates.
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.
176 IN SCMI_CLOCK_PROTOCOL
*This
,
178 OUT SCMI_CLOCK_RATE_FORMAT
*Format
,
179 OUT UINT32
*TotalRates
,
180 IN OUT UINT32
*RateArraySize
,
181 OUT SCMI_CLOCK_RATE
*RateArray
186 UINT32 PayloadLength
;
188 UINT32
*MessageParams
;
189 CLOCK_DESCRIBE_RATES
*DescribeRates
;
190 CLOCK_RATE_DWORD
*Rate
;
192 UINT32 RequiredArraySize
;
198 RequiredArraySize
= 0;
201 Status
= ScmiCommandGetPayload (&MessageParams
);
202 if (EFI_ERROR (Status
)) {
206 Cmd
.ProtocolId
= SCMI_PROTOCOL_ID_CLOCK
;
207 Cmd
.MessageId
= SCMI_MESSAGE_ID_CLOCK_DESCRIBE_RATES
;
209 *MessageParams
++ = ClockId
;
213 *MessageParams
= RateIndex
;
215 // Set Payload length, note PayloadLength is a IN/OUT parameter.
216 PayloadLength
= sizeof (ClockId
) + sizeof (RateIndex
);
218 // Execute and wait for response on a SCMI channel.
219 Status
= ScmiCommandExecute (
222 (UINT32
**)&DescribeRates
224 if (EFI_ERROR (Status
)) {
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
234 *Format
= RATE_FORMAT (DescribeRates
->NumRatesFlags
);
236 *TotalRates
= NUM_RATES (DescribeRates
->NumRatesFlags
)
237 + NUM_REMAIN_RATES (DescribeRates
->NumRatesFlags
);
239 if (*Format
== SCMI_CLOCK_RATE_FORMAT_DISCRETE
) {
240 RequiredArraySize
= (*TotalRates
) * sizeof (UINT64
);
242 // We need to return triplet of 64 bit value for each rate
243 RequiredArraySize
= (*TotalRates
) * 3 * sizeof (UINT64
);
246 if (RequiredArraySize
> (*RateArraySize
)) {
247 *RateArraySize
= RequiredArraySize
;
248 return EFI_BUFFER_TOO_SMALL
;
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
);
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
);
269 Rate
= &DescribeRates
->Rates
[RateOffset
++];
270 // Maximum clock rate.
271 RateArray
[RateIndex
].ContinuousRate
.Max
=
272 ConvertTo64Bit (Rate
->Low
, Rate
->High
);
274 Rate
= &DescribeRates
->Rates
[RateOffset
++];
276 RateArray
[RateIndex
++].ContinuousRate
.Step
=
277 ConvertTo64Bit (Rate
->Low
, Rate
->High
);
280 } while (NUM_REMAIN_RATES (DescribeRates
->NumRatesFlags
) != 0);
282 // Update RateArraySize with RequiredArraySize.
283 *RateArraySize
= RequiredArraySize
;
290 @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
291 @param[in] ClockId Identifier for the clock device.
293 @param[out] Rate Clock rate.
295 @retval EFI_SUCCESS Clock rate is returned.
296 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
297 @retval !(EFI_SUCCESS) Other errors.
302 IN SCMI_CLOCK_PROTOCOL
*This
,
309 UINT32
*MessageParams
;
310 CLOCK_RATE_DWORD
*ClockRate
;
313 UINT32 PayloadLength
;
315 Status
= ScmiCommandGetPayload (&MessageParams
);
316 if (EFI_ERROR (Status
)) {
320 // Fill arguments for clock protocol command.
321 *MessageParams
= ClockId
;
323 Cmd
.ProtocolId
= SCMI_PROTOCOL_ID_CLOCK
;
324 Cmd
.MessageId
= SCMI_MESSAGE_ID_CLOCK_RATE_GET
;
326 PayloadLength
= sizeof (ClockId
);
328 // Execute and wait for response on a SCMI channel.
329 Status
= ScmiCommandExecute (
334 if (EFI_ERROR (Status
)) {
338 *Rate
= ConvertTo64Bit (ClockRate
->Low
, ClockRate
->High
);
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.
349 @retval EFI_SUCCESS Clock rate set success.
350 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
351 @retval !(EFI_SUCCESS) Other errors.
356 IN SCMI_CLOCK_PROTOCOL
*This
,
362 CLOCK_RATE_SET_ATTRIBUTES
*ClockRateSetAttributes
;
364 UINT32 PayloadLength
;
366 Status
= ScmiCommandGetPayload ((UINT32
**)&ClockRateSetAttributes
);
367 if (EFI_ERROR (Status
)) {
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);
377 Cmd
.ProtocolId
= SCMI_PROTOCOL_ID_CLOCK
;
378 Cmd
.MessageId
= SCMI_MESSAGE_ID_CLOCK_RATE_SET
;
380 PayloadLength
= sizeof (CLOCK_RATE_SET_ATTRIBUTES
);
382 // Execute and wait for response on a SCMI channel.
383 Status
= ScmiCommandExecute (
392 /** Enable/Disable specified clock.
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.
398 @retval EFI_SUCCESS Clock enable/disable successful.
399 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
400 @retval !(EFI_SUCCESS) Other errors.
405 IN SCMI_CLOCK2_PROTOCOL
*This
,
411 CLOCK_CONFIG_SET_ATTRIBUTES
*ClockConfigSetAttributes
;
413 UINT32 PayloadLength
;
415 Status
= ScmiCommandGetPayload ((UINT32
**)&ClockConfigSetAttributes
);
416 if (EFI_ERROR (Status
)) {
420 // Fill arguments for clock protocol command.
421 ClockConfigSetAttributes
->ClockId
= ClockId
;
422 ClockConfigSetAttributes
->Attributes
= Enable
? BIT0
: 0;
424 Cmd
.ProtocolId
= SCMI_PROTOCOL_ID_CLOCK
;
425 Cmd
.MessageId
= SCMI_MESSAGE_ID_CLOCK_CONFIG_SET
;
427 PayloadLength
= sizeof (CLOCK_CONFIG_SET_ATTRIBUTES
);
429 // Execute and wait for response on a SCMI channel.
430 Status
= ScmiCommandExecute (
439 // Instance of the SCMI clock management protocol.
440 STATIC CONST SCMI_CLOCK_PROTOCOL ScmiClockProtocol
= {
443 ClockGetClockAttributes
,
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
,
461 /** Initialize clock management protocol and install protocol on a given handle.
463 @param[in] Handle Handle to install clock management protocol.
465 @retval EFI_SUCCESS Clock protocol interface installed successfully.
468 ScmiClockProtocolInit (
469 IN EFI_HANDLE
* Handle
472 return gBS
->InstallMultipleProtocolInterfaces (
474 &gArmScmiClockProtocolGuid
,
476 &gArmScmiClock2ProtocolGuid
,