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