]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c
ArmPkg/ArmScmiDxe: Add clock enable function
[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 #include <Protocol/ArmScmiClock2Protocol.h>
23
24 #include "ArmScmiClockProtocolPrivate.h"
25 #include "ScmiPrivate.h"
26
27 /** Convert to 64 bit value from two 32 bit words.
28
29 @param[in] Low Lower 32 bits.
30 @param[in] High Higher 32 bits.
31
32 @retval UINT64 64 bit value.
33 **/
34 STATIC
35 UINT64
36 ConvertTo64Bit (
37 IN UINT32 Low,
38 IN UINT32 High
39 )
40 {
41 return (Low | ((UINT64)High << 32));
42 }
43
44 /** Return version of the clock management protocol supported by SCP firmware.
45
46 @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
47
48 @param[out] Version Version of the supported SCMI Clock management protocol.
49
50 @retval EFI_SUCCESS The version is returned.
51 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
52 @retval !(EFI_SUCCESS) Other errors.
53 **/
54 STATIC
55 EFI_STATUS
56 ClockGetVersion (
57 IN SCMI_CLOCK_PROTOCOL *This,
58 OUT UINT32 *Version
59 )
60 {
61 return ScmiGetProtocolVersion (SCMI_PROTOCOL_ID_CLOCK, Version);
62 }
63
64 /** Return total number of clock devices supported by the clock management
65 protocol.
66
67 @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
68
69 @param[out] TotalClocks Total number of clocks supported.
70
71 @retval EFI_SUCCESS Total number of clocks supported is returned.
72 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
73 @retval !(EFI_SUCCESS) Other errors.
74 **/
75 STATIC
76 EFI_STATUS
77 ClockGetTotalClocks (
78 IN SCMI_CLOCK_PROTOCOL *This,
79 OUT UINT32 *TotalClocks
80 )
81 {
82 EFI_STATUS Status;
83 UINT32 *ReturnValues;
84
85 Status = ScmiGetProtocolAttributes (SCMI_PROTOCOL_ID_CLOCK, &ReturnValues);
86 if (EFI_ERROR (Status)) {
87 return Status;
88 }
89
90 *TotalClocks = SCMI_CLOCK_PROTOCOL_TOTAL_CLKS (ReturnValues[0]);
91
92 return EFI_SUCCESS;
93 }
94
95 /** Return attributes of a clock device.
96
97 @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance.
98 @param[in] ClockId Identifier for the clock device.
99
100 @param[out] Enabled If TRUE, the clock device is enabled.
101 @param[out] ClockAsciiName A NULL terminated ASCII string with the clock
102 name, of up to 16 bytes.
103
104 @retval EFI_SUCCESS Clock device attributes are returned.
105 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
106 @retval !(EFI_SUCCESS) Other errors.
107 **/
108 STATIC
109 EFI_STATUS
110 ClockGetClockAttributes (
111 IN SCMI_CLOCK_PROTOCOL *This,
112 IN UINT32 ClockId,
113 OUT BOOLEAN *Enabled,
114 OUT CHAR8 *ClockAsciiName
115 )
116 {
117 EFI_STATUS Status;
118
119 UINT32 *MessageParams;
120 CLOCK_ATTRIBUTES *ClockAttributes;
121 SCMI_COMMAND Cmd;
122 UINT32 PayloadLength;
123
124 Status = ScmiCommandGetPayload (&MessageParams);
125 if (EFI_ERROR (Status)) {
126 return Status;
127 }
128
129 *MessageParams = ClockId;
130
131 Cmd.ProtocolId = SCMI_PROTOCOL_ID_CLOCK;
132 Cmd.MessageId = SCMI_MESSAGE_ID_CLOCK_ATTRIBUTES;
133
134 PayloadLength = sizeof (ClockId);
135
136 Status = ScmiCommandExecute (
137 &Cmd,
138 &PayloadLength,
139 (UINT32**)&ClockAttributes
140 );
141 if (EFI_ERROR (Status)) {
142 return Status;
143 }
144 // TRUE if bit 0 of ClockAttributes->Attributes is set.
145 *Enabled = CLOCK_ENABLED (ClockAttributes->Attributes);
146
147 AsciiStrCpyS (
148 ClockAsciiName,
149 SCMI_MAX_STR_LEN,
150 (CONST CHAR8*)ClockAttributes->ClockName
151 );
152
153 return EFI_SUCCESS;
154 }
155
156 /** Return list of rates supported by a given clock device.
157
158 @param[in] This A pointer to SCMI_CLOCK_PROTOCOL Instance.
159 @param[in] ClockId Identifier for the clock device.
160
161 @param[out] Format SCMI_CLOCK_RATE_FORMAT_DISCRETE: Clock device
162 supports range of clock rates which are non-linear.
163
164 SCMI_CLOCK_RATE_FORMAT_LINEAR: Clock device supports
165 range of linear clock rates from Min to Max in steps.
166
167 @param[out] TotalRates Total number of rates.
168
169 @param[in,out] RateArraySize Size of the RateArray.
170
171 @param[out] RateArray List of clock rates.
172
173 @retval EFI_SUCCESS List of clock rates is returned.
174 @retval EFI_DEVICE_ERROR SCP returns an SCMI error.
175 @retval EFI_BUFFER_TOO_SMALL RateArraySize is too small for the result.
176 It has been updated to the size needed.
177 @retval !(EFI_SUCCESS) Other errors.
178 **/
179 STATIC
180 EFI_STATUS
181 ClockDescribeRates (
182 IN SCMI_CLOCK_PROTOCOL *This,
183 IN UINT32 ClockId,
184 OUT SCMI_CLOCK_RATE_FORMAT *Format,
185 OUT UINT32 *TotalRates,
186 IN OUT UINT32 *RateArraySize,
187 OUT SCMI_CLOCK_RATE *RateArray
188 )
189 {
190 EFI_STATUS Status;
191
192 UINT32 PayloadLength;
193 SCMI_COMMAND Cmd;
194 UINT32 *MessageParams;
195 CLOCK_DESCRIBE_RATES *DescribeRates;
196 CLOCK_RATE_DWORD *Rate;
197
198 UINT32 RequiredArraySize = 0;
199 UINT32 RateIndex = 0;
200 UINT32 RateNo;
201 UINT32 RateOffset;
202
203 *TotalRates = 0;
204
205 Status = ScmiCommandGetPayload (&MessageParams);
206 if (EFI_ERROR (Status)) {
207 return Status;
208 }
209
210 Cmd.ProtocolId = SCMI_PROTOCOL_ID_CLOCK;
211 Cmd.MessageId = SCMI_MESSAGE_ID_CLOCK_DESCRIBE_RATES;
212
213 *MessageParams++ = ClockId;
214
215 do {
216
217 *MessageParams = RateIndex;
218
219 // Set Payload length, note PayloadLength is a IN/OUT parameter.
220 PayloadLength = sizeof (ClockId) + sizeof (RateIndex);
221
222 // Execute and wait for response on a SCMI channel.
223 Status = ScmiCommandExecute (
224 &Cmd,
225 &PayloadLength,
226 (UINT32**)&DescribeRates
227 );
228 if (EFI_ERROR (Status)) {
229 return Status;
230 }
231
232 if (*TotalRates == 0) {
233 // In the first iteration we will get number of returned rates and number
234 // of remaining rates. With this information calculate required size
235 // for rate array. If provided RateArraySize is less, return an
236 // error.
237
238 *Format = RATE_FORMAT (DescribeRates->NumRatesFlags);
239
240 *TotalRates = NUM_RATES (DescribeRates->NumRatesFlags)
241 + NUM_REMAIN_RATES (DescribeRates->NumRatesFlags);
242
243 if (*Format == SCMI_CLOCK_RATE_FORMAT_DISCRETE) {
244 RequiredArraySize = (*TotalRates) * sizeof (UINT64);
245 } else {
246 // We need to return triplet of 64 bit value for each rate
247 RequiredArraySize = (*TotalRates) * 3 * sizeof (UINT64);
248 }
249
250 if (RequiredArraySize > (*RateArraySize)) {
251 *RateArraySize = RequiredArraySize;
252 return EFI_BUFFER_TOO_SMALL;
253 }
254 }
255
256 RateOffset = 0;
257
258 if (*Format == SCMI_CLOCK_RATE_FORMAT_DISCRETE) {
259 for (RateNo = 0; RateNo < NUM_RATES (DescribeRates->NumRatesFlags); RateNo++) {
260 Rate = &DescribeRates->Rates[RateOffset++];
261 // Non-linear discrete rates.
262 RateArray[RateIndex++].Rate = ConvertTo64Bit (Rate->Low, Rate->High);
263 }
264 } else {
265 for (RateNo = 0; RateNo < NUM_RATES (DescribeRates->NumRatesFlags); RateNo++) {
266 // Linear clock rates from minimum to maximum in steps
267 // Minimum clock rate.
268 Rate = &DescribeRates->Rates[RateOffset++];
269 RateArray[RateIndex].Min = ConvertTo64Bit (Rate->Low, Rate->High);
270
271 Rate = &DescribeRates->Rates[RateOffset++];
272 // Maximum clock rate.
273 RateArray[RateIndex].Max = ConvertTo64Bit (Rate->Low, Rate->High);
274
275 Rate = &DescribeRates->Rates[RateOffset++];
276 // Step.
277 RateArray[RateIndex++].Step = 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 }