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