1 | /** @file
|
---|
2 | Arm SCMI Info Library.
|
---|
3 |
|
---|
4 | Copyright (c) 2022 - 2023, Arm Limited. All rights reserved.<BR>
|
---|
5 |
|
---|
6 | Arm Functional Fixed Hardware Specification:
|
---|
7 | - https://developer.arm.com/documentation/den0048/latest/
|
---|
8 |
|
---|
9 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
10 | **/
|
---|
11 |
|
---|
12 | #include <Library/AcpiLib.h>
|
---|
13 | #include <Library/DynamicTablesScmiInfoLib.h>
|
---|
14 | #include <Library/DebugLib.h>
|
---|
15 | #include <Library/MemoryAllocationLib.h>
|
---|
16 | #include <Library/UefiBootServicesTableLib.h>
|
---|
17 | #include <Protocol/ArmScmi.h>
|
---|
18 | #include <Protocol/ArmScmiPerformanceProtocol.h>
|
---|
19 |
|
---|
20 | /** Arm FFH registers
|
---|
21 |
|
---|
22 | Cf. Arm Functional Fixed Hardware Specification
|
---|
23 | s3.2 Performance management and Collaborative Processor Performance Control
|
---|
24 | */
|
---|
25 | #define ARM_FFH_DELIVERED_PERF_COUNTER_REGISTER 0x0
|
---|
26 | #define ARM_FFH_REFERENCE_PERF_COUNTER_REGISTER 0x1
|
---|
27 |
|
---|
28 | /// Arm SCMI performance protocol.
|
---|
29 | STATIC SCMI_PERFORMANCE_PROTOCOL *ScmiPerfProtocol;
|
---|
30 |
|
---|
31 | /** Arm SCMI Info Library constructor.
|
---|
32 |
|
---|
33 | @param ImageHandle Image of the loaded driver.
|
---|
34 | @param SystemTable Pointer to the System Table.
|
---|
35 |
|
---|
36 | @retval EFI_SUCCESS Success.
|
---|
37 | @retval EFI_DEVICE_ERROR Device error.
|
---|
38 | @retval EFI_INVALID_PARAMETER Invalid parameter.
|
---|
39 | @retval EFI_NOT_FOUND Not Found
|
---|
40 | @retval EFI_TIMEOUT Timeout.
|
---|
41 | @retval EFI_UNSUPPORTED Unsupported.
|
---|
42 | **/
|
---|
43 | EFI_STATUS
|
---|
44 | EFIAPI
|
---|
45 | DynamicTablesScmiInfoLibConstructor (
|
---|
46 | IN EFI_HANDLE ImageHandle,
|
---|
47 | IN EFI_SYSTEM_TABLE *SystemTable
|
---|
48 | )
|
---|
49 | {
|
---|
50 | EFI_STATUS Status;
|
---|
51 | UINT32 Version;
|
---|
52 |
|
---|
53 | Status = gBS->LocateProtocol (
|
---|
54 | &gArmScmiPerformanceProtocolGuid,
|
---|
55 | NULL,
|
---|
56 | (VOID **)&ScmiPerfProtocol
|
---|
57 | );
|
---|
58 | if (EFI_ERROR (Status)) {
|
---|
59 | return Status;
|
---|
60 | }
|
---|
61 |
|
---|
62 | Status = ScmiPerfProtocol->GetVersion (ScmiPerfProtocol, &Version);
|
---|
63 | if (EFI_ERROR (Status)) {
|
---|
64 | return Status;
|
---|
65 | }
|
---|
66 |
|
---|
67 | // FastChannels were added in SCMI v2.0 spec.
|
---|
68 | if (Version < PERFORMANCE_PROTOCOL_VERSION_V2) {
|
---|
69 | DEBUG ((
|
---|
70 | DEBUG_ERROR,
|
---|
71 | "DynamicTablesScmiInfoLib requires SCMI version > 2.0\n"
|
---|
72 | ));
|
---|
73 | return EFI_UNSUPPORTED;
|
---|
74 | }
|
---|
75 |
|
---|
76 | return Status;
|
---|
77 | }
|
---|
78 |
|
---|
79 | /** Get the OPPs/performance states of a power domain.
|
---|
80 |
|
---|
81 | This function is a wrapper around the SCMI PERFORMANCE_DESCRIBE_LEVELS
|
---|
82 | command. The list of discrete performance states is returned in a buffer
|
---|
83 | that must be freed by the caller.
|
---|
84 |
|
---|
85 | @param[in] DomainId Identifier for the performance domain.
|
---|
86 | @param[out] LevelArray If success, pointer to the list of list of
|
---|
87 | performance state. This memory must be freed by
|
---|
88 | the caller.
|
---|
89 | @param[out] LevelArrayCount If success, contains the number of states in
|
---|
90 | LevelArray.
|
---|
91 |
|
---|
92 | @retval EFI_SUCCESS Success.
|
---|
93 | @retval EFI_DEVICE_ERROR Device error.
|
---|
94 | @retval EFI_INVALID_PARAMETER Invalid parameter.
|
---|
95 | @retval EFI_TIMEOUT Time out.
|
---|
96 | @retval EFI_UNSUPPORTED Unsupported.
|
---|
97 | **/
|
---|
98 | STATIC
|
---|
99 | EFI_STATUS
|
---|
100 | EFIAPI
|
---|
101 | DynamicTablesScmiInfoDescribeLevels (
|
---|
102 | IN UINT32 DomainId,
|
---|
103 | OUT SCMI_PERFORMANCE_LEVEL **LevelArray,
|
---|
104 | OUT UINT32 *LevelArrayCount
|
---|
105 | )
|
---|
106 | {
|
---|
107 | EFI_STATUS Status;
|
---|
108 | SCMI_PERFORMANCE_LEVEL *Array;
|
---|
109 | UINT32 Count;
|
---|
110 | UINT32 Size;
|
---|
111 |
|
---|
112 | if ((ScmiPerfProtocol == NULL) ||
|
---|
113 | (LevelArray == NULL) ||
|
---|
114 | (LevelArrayCount == NULL))
|
---|
115 | {
|
---|
116 | return EFI_INVALID_PARAMETER;
|
---|
117 | }
|
---|
118 |
|
---|
119 | // First call to get the number of levels.
|
---|
120 | Size = 0;
|
---|
121 | Status = ScmiPerfProtocol->DescribeLevels (
|
---|
122 | ScmiPerfProtocol,
|
---|
123 | DomainId,
|
---|
124 | &Count,
|
---|
125 | &Size,
|
---|
126 | NULL
|
---|
127 | );
|
---|
128 | if (Status != EFI_BUFFER_TOO_SMALL) {
|
---|
129 | // EFI_SUCCESS is not a valid option.
|
---|
130 | if (Status == EFI_SUCCESS) {
|
---|
131 | return EFI_INVALID_PARAMETER;
|
---|
132 | } else {
|
---|
133 | return Status;
|
---|
134 | }
|
---|
135 | }
|
---|
136 |
|
---|
137 | Array = AllocateZeroPool (Size);
|
---|
138 | if (Array == NULL) {
|
---|
139 | return EFI_OUT_OF_RESOURCES;
|
---|
140 | }
|
---|
141 |
|
---|
142 | // Second call to get the descriptions of the levels.
|
---|
143 | Status = ScmiPerfProtocol->DescribeLevels (
|
---|
144 | ScmiPerfProtocol,
|
---|
145 | DomainId,
|
---|
146 | &Count,
|
---|
147 | &Size,
|
---|
148 | Array
|
---|
149 | );
|
---|
150 | if (EFI_ERROR (Status)) {
|
---|
151 | return Status;
|
---|
152 | }
|
---|
153 |
|
---|
154 | *LevelArray = Array;
|
---|
155 | *LevelArrayCount = Count;
|
---|
156 |
|
---|
157 | return Status;
|
---|
158 | }
|
---|
159 |
|
---|
160 | /** Populate a AML_CPC_INFO object based on SCMI information.
|
---|
161 |
|
---|
162 | @param[in] DomainId Identifier for the performance domain.
|
---|
163 | @param[out] CpcInfo If success, this structure was populated from
|
---|
164 | information queried to the SCP.
|
---|
165 |
|
---|
166 | @retval EFI_SUCCESS Success.
|
---|
167 | @retval EFI_DEVICE_ERROR Device error.
|
---|
168 | @retval EFI_INVALID_PARAMETER Invalid parameter.
|
---|
169 | @retval EFI_TIMEOUT Time out.
|
---|
170 | @retval EFI_UNSUPPORTED Unsupported.
|
---|
171 | **/
|
---|
172 | EFI_STATUS
|
---|
173 | EFIAPI
|
---|
174 | DynamicTablesScmiInfoGetFastChannel (
|
---|
175 | IN UINT32 DomainId,
|
---|
176 | OUT AML_CPC_INFO *CpcInfo
|
---|
177 | )
|
---|
178 | {
|
---|
179 | EFI_STATUS Status;
|
---|
180 | SCMI_PERFORMANCE_FASTCHANNEL FcLevelGet;
|
---|
181 | SCMI_PERFORMANCE_FASTCHANNEL FcLimitsSet;
|
---|
182 | SCMI_PERFORMANCE_DOMAIN_ATTRIBUTES DomainAttributes;
|
---|
183 |
|
---|
184 | SCMI_PERFORMANCE_LEVEL *LevelArray;
|
---|
185 | UINT32 LevelCount;
|
---|
186 |
|
---|
187 | UINT64 FcLevelGetAddr;
|
---|
188 | UINT64 FcLimitsMaxSetAddr;
|
---|
189 | UINT64 FcLimitsMinSetAddr;
|
---|
190 |
|
---|
191 | if ((ScmiPerfProtocol == NULL) ||
|
---|
192 | (CpcInfo == NULL))
|
---|
193 | {
|
---|
194 | return EFI_INVALID_PARAMETER;
|
---|
195 | }
|
---|
196 |
|
---|
197 | Status = ScmiPerfProtocol->DescribeFastchannel (
|
---|
198 | ScmiPerfProtocol,
|
---|
199 | DomainId,
|
---|
200 | ScmiMessageIdPerformanceLevelSet,
|
---|
201 | &FcLevelGet
|
---|
202 | );
|
---|
203 | if (EFI_ERROR (Status)) {
|
---|
204 | return Status;
|
---|
205 | }
|
---|
206 |
|
---|
207 | Status = ScmiPerfProtocol->DescribeFastchannel (
|
---|
208 | ScmiPerfProtocol,
|
---|
209 | DomainId,
|
---|
210 | ScmiMessageIdPerformanceLimitsSet,
|
---|
211 | &FcLimitsSet
|
---|
212 | );
|
---|
213 | if (EFI_ERROR (Status)) {
|
---|
214 | return Status;
|
---|
215 | }
|
---|
216 |
|
---|
217 | Status = ScmiPerfProtocol->GetDomainAttributes (
|
---|
218 | ScmiPerfProtocol,
|
---|
219 | DomainId,
|
---|
220 | &DomainAttributes
|
---|
221 | );
|
---|
222 | if (EFI_ERROR (Status)) {
|
---|
223 | return Status;
|
---|
224 | }
|
---|
225 |
|
---|
226 | Status = DynamicTablesScmiInfoDescribeLevels (DomainId, &LevelArray, &LevelCount);
|
---|
227 | if (EFI_ERROR (Status)) {
|
---|
228 | return Status;
|
---|
229 | }
|
---|
230 |
|
---|
231 | /* Do some safety checks.
|
---|
232 | Only support FastChannels (and not doorbells) as this is
|
---|
233 | the only mechanism supported by SCP.
|
---|
234 | FcLimits[Get|Set] require 2 UINT32 values (max, then min) and
|
---|
235 | FcLimits[Get|Set] require 1 UINT32 value (level).
|
---|
236 | */
|
---|
237 | if ((FcLevelGet.ChanSize != sizeof (UINT32)) ||
|
---|
238 | ((FcLevelGet.Attributes & SCMI_PERF_FC_ATTRIB_HAS_DOORBELL) ==
|
---|
239 | SCMI_PERF_FC_ATTRIB_HAS_DOORBELL) ||
|
---|
240 | (FcLimitsSet.ChanSize != 2 * sizeof (UINT32)) ||
|
---|
241 | ((FcLimitsSet.Attributes & SCMI_PERF_FC_ATTRIB_HAS_DOORBELL) ==
|
---|
242 | SCMI_PERF_FC_ATTRIB_HAS_DOORBELL))
|
---|
243 | {
|
---|
244 | Status = EFI_INVALID_PARAMETER;
|
---|
245 | goto exit_handler;
|
---|
246 | }
|
---|
247 |
|
---|
248 | FcLevelGetAddr = ((UINT64)FcLevelGet.ChanAddrHigh << 32) |
|
---|
249 | FcLevelGet.ChanAddrLow;
|
---|
250 | FcLimitsMaxSetAddr = ((UINT64)FcLimitsSet.ChanAddrHigh << 32) |
|
---|
251 | FcLimitsSet.ChanAddrLow;
|
---|
252 | FcLimitsMinSetAddr = FcLimitsMaxSetAddr + 0x4;
|
---|
253 |
|
---|
254 | CpcInfo->Revision = EFI_ACPI_6_5_AML_CPC_REVISION;
|
---|
255 | CpcInfo->HighestPerformanceInteger = LevelArray[LevelCount - 1].Level;
|
---|
256 | CpcInfo->NominalPerformanceInteger = DomainAttributes.SustainedPerfLevel;
|
---|
257 | CpcInfo->LowestNonlinearPerformanceInteger = LevelArray[0].Level;
|
---|
258 | CpcInfo->LowestPerformanceInteger = LevelArray[0].Level;
|
---|
259 |
|
---|
260 | CpcInfo->DesiredPerformanceRegister.AddressSpaceId = EFI_ACPI_6_5_SYSTEM_MEMORY;
|
---|
261 | CpcInfo->DesiredPerformanceRegister.RegisterBitWidth = 32;
|
---|
262 | CpcInfo->DesiredPerformanceRegister.RegisterBitOffset = 0;
|
---|
263 | CpcInfo->DesiredPerformanceRegister.AccessSize = EFI_ACPI_6_5_DWORD;
|
---|
264 | CpcInfo->DesiredPerformanceRegister.Address = FcLevelGetAddr;
|
---|
265 |
|
---|
266 | CpcInfo->MinimumPerformanceRegister.AddressSpaceId = EFI_ACPI_6_5_SYSTEM_MEMORY;
|
---|
267 | CpcInfo->MinimumPerformanceRegister.RegisterBitWidth = 32;
|
---|
268 | CpcInfo->MinimumPerformanceRegister.RegisterBitOffset = 0;
|
---|
269 | CpcInfo->MinimumPerformanceRegister.AccessSize = EFI_ACPI_6_5_DWORD;
|
---|
270 | CpcInfo->MinimumPerformanceRegister.Address = FcLimitsMinSetAddr;
|
---|
271 |
|
---|
272 | CpcInfo->MaximumPerformanceRegister.AddressSpaceId = EFI_ACPI_6_5_SYSTEM_MEMORY;
|
---|
273 | CpcInfo->MaximumPerformanceRegister.RegisterBitWidth = 32;
|
---|
274 | CpcInfo->MaximumPerformanceRegister.RegisterBitOffset = 0;
|
---|
275 | CpcInfo->MaximumPerformanceRegister.AccessSize = EFI_ACPI_6_5_DWORD;
|
---|
276 | CpcInfo->MaximumPerformanceRegister.Address = FcLimitsMaxSetAddr;
|
---|
277 |
|
---|
278 | CpcInfo->ReferencePerformanceCounterRegister.AddressSpaceId = EFI_ACPI_6_5_FUNCTIONAL_FIXED_HARDWARE;
|
---|
279 | CpcInfo->ReferencePerformanceCounterRegister.RegisterBitWidth = 0x40;
|
---|
280 | CpcInfo->ReferencePerformanceCounterRegister.RegisterBitOffset = 0;
|
---|
281 | CpcInfo->ReferencePerformanceCounterRegister.AccessSize = ARM_FFH_REFERENCE_PERF_COUNTER_REGISTER;
|
---|
282 | CpcInfo->ReferencePerformanceCounterRegister.Address = 0x4;
|
---|
283 |
|
---|
284 | CpcInfo->DeliveredPerformanceCounterRegister.AddressSpaceId = EFI_ACPI_6_5_FUNCTIONAL_FIXED_HARDWARE;
|
---|
285 | CpcInfo->DeliveredPerformanceCounterRegister.RegisterBitWidth = 0x40;
|
---|
286 | CpcInfo->DeliveredPerformanceCounterRegister.RegisterBitOffset = 0;
|
---|
287 | CpcInfo->DeliveredPerformanceCounterRegister.AccessSize = ARM_FFH_DELIVERED_PERF_COUNTER_REGISTER;
|
---|
288 | CpcInfo->DeliveredPerformanceCounterRegister.Address = 0x4;
|
---|
289 |
|
---|
290 | // SCMI should advertise performance values on a unified scale. So frequency
|
---|
291 | // values are not available. LowestFrequencyInteger and
|
---|
292 | // NominalFrequencyInteger are populated in the ConfigurationManager.
|
---|
293 |
|
---|
294 | exit_handler:
|
---|
295 | FreePool (LevelArray);
|
---|
296 | return Status;
|
---|
297 | }
|
---|