1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Xilinx Zynq MPSoC Firmware layer |
4 | * |
5 | * Copyright (C) 2014-2022 Xilinx, Inc. |
6 | * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. |
7 | * |
8 | * Michal Simek <michal.simek@amd.com> |
9 | * Davorin Mista <davorin.mista@aggios.com> |
10 | * Jolly Shah <jollys@xilinx.com> |
11 | * Rajan Vaja <rajanv@xilinx.com> |
12 | */ |
13 | |
14 | #include <linux/arm-smccc.h> |
15 | #include <linux/compiler.h> |
16 | #include <linux/device.h> |
17 | #include <linux/init.h> |
18 | #include <linux/mfd/core.h> |
19 | #include <linux/module.h> |
20 | #include <linux/of.h> |
21 | #include <linux/of_platform.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/uaccess.h> |
25 | #include <linux/hashtable.h> |
26 | |
27 | #include <linux/firmware/xlnx-zynqmp.h> |
28 | #include <linux/firmware/xlnx-event-manager.h> |
29 | #include "zynqmp-debug.h" |
30 | |
31 | /* Max HashMap Order for PM API feature check (1<<7 = 128) */ |
32 | #define PM_API_FEATURE_CHECK_MAX_ORDER 7 |
33 | |
34 | /* CRL registers and bitfields */ |
35 | #define CRL_APB_BASE 0xFF5E0000U |
36 | /* BOOT_PIN_CTRL- Used to control the mode pins after boot */ |
37 | #define CRL_APB_BOOT_PIN_CTRL (CRL_APB_BASE + (0x250U)) |
38 | /* BOOT_PIN_CTRL_MASK- out_val[11:8], out_en[3:0] */ |
39 | #define CRL_APB_BOOTPIN_CTRL_MASK 0xF0FU |
40 | |
41 | /* IOCTL/QUERY feature payload size */ |
42 | #define FEATURE_PAYLOAD_SIZE 2 |
43 | |
44 | /* Firmware feature check version mask */ |
45 | #define FIRMWARE_VERSION_MASK GENMASK(15, 0) |
46 | |
47 | static bool feature_check_enabled; |
48 | static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); |
49 | static u32 ioctl_features[FEATURE_PAYLOAD_SIZE]; |
50 | static u32 query_features[FEATURE_PAYLOAD_SIZE]; |
51 | |
52 | static struct platform_device *em_dev; |
53 | |
54 | /** |
55 | * struct zynqmp_devinfo - Structure for Zynqmp device instance |
56 | * @dev: Device Pointer |
57 | * @feature_conf_id: Feature conf id |
58 | */ |
59 | struct zynqmp_devinfo { |
60 | struct device *dev; |
61 | u32 feature_conf_id; |
62 | }; |
63 | |
64 | /** |
65 | * struct pm_api_feature_data - PM API Feature data |
66 | * @pm_api_id: PM API Id, used as key to index into hashmap |
67 | * @feature_status: status of PM API feature: valid, invalid |
68 | * @hentry: hlist_node that hooks this entry into hashtable |
69 | */ |
70 | struct pm_api_feature_data { |
71 | u32 pm_api_id; |
72 | int feature_status; |
73 | struct hlist_node hentry; |
74 | }; |
75 | |
76 | static const struct mfd_cell firmware_devs[] = { |
77 | { |
78 | .name = "zynqmp_power_controller" , |
79 | }, |
80 | }; |
81 | |
82 | /** |
83 | * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes |
84 | * @ret_status: PMUFW return code |
85 | * |
86 | * Return: corresponding Linux error code |
87 | */ |
88 | static int zynqmp_pm_ret_code(u32 ret_status) |
89 | { |
90 | switch (ret_status) { |
91 | case XST_PM_SUCCESS: |
92 | case XST_PM_DOUBLE_REQ: |
93 | return 0; |
94 | case XST_PM_NO_FEATURE: |
95 | return -ENOTSUPP; |
96 | case XST_PM_INVALID_VERSION: |
97 | return -EOPNOTSUPP; |
98 | case XST_PM_NO_ACCESS: |
99 | return -EACCES; |
100 | case XST_PM_ABORT_SUSPEND: |
101 | return -ECANCELED; |
102 | case XST_PM_MULT_USER: |
103 | return -EUSERS; |
104 | case XST_PM_INTERNAL: |
105 | case XST_PM_CONFLICT: |
106 | case XST_PM_INVALID_NODE: |
107 | case XST_PM_INVALID_CRC: |
108 | default: |
109 | return -EINVAL; |
110 | } |
111 | } |
112 | |
113 | static noinline int do_fw_call_fail(u32 *ret_payload, u32 num_args, ...) |
114 | { |
115 | return -ENODEV; |
116 | } |
117 | |
118 | /* |
119 | * PM function call wrapper |
120 | * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration |
121 | */ |
122 | static int (*do_fw_call)(u32 *ret_payload, u32, ...) = do_fw_call_fail; |
123 | |
124 | /** |
125 | * do_fw_call_smc() - Call system-level platform management layer (SMC) |
126 | * @num_args: Number of variable arguments should be <= 8 |
127 | * @ret_payload: Returned value array |
128 | * |
129 | * Invoke platform management function via SMC call (no hypervisor present). |
130 | * |
131 | * Return: Returns status, either success or error+reason |
132 | */ |
133 | static noinline int do_fw_call_smc(u32 *ret_payload, u32 num_args, ...) |
134 | { |
135 | struct arm_smccc_res res; |
136 | u64 args[8] = {0}; |
137 | va_list arg_list; |
138 | u8 i; |
139 | |
140 | if (num_args > 8) |
141 | return -EINVAL; |
142 | |
143 | va_start(arg_list, num_args); |
144 | |
145 | for (i = 0; i < num_args; i++) |
146 | args[i] = va_arg(arg_list, u64); |
147 | |
148 | va_end(arg_list); |
149 | |
150 | arm_smccc_smc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); |
151 | |
152 | if (ret_payload) { |
153 | ret_payload[0] = lower_32_bits(res.a0); |
154 | ret_payload[1] = upper_32_bits(res.a0); |
155 | ret_payload[2] = lower_32_bits(res.a1); |
156 | ret_payload[3] = upper_32_bits(res.a1); |
157 | } |
158 | |
159 | return zynqmp_pm_ret_code(ret_status: (enum pm_ret_status)res.a0); |
160 | } |
161 | |
162 | /** |
163 | * do_fw_call_hvc() - Call system-level platform management layer (HVC) |
164 | * @num_args: Number of variable arguments should be <= 8 |
165 | * @ret_payload: Returned value array |
166 | * |
167 | * Invoke platform management function via HVC |
168 | * HVC-based for communication through hypervisor |
169 | * (no direct communication with ATF). |
170 | * |
171 | * Return: Returns status, either success or error+reason |
172 | */ |
173 | static noinline int do_fw_call_hvc(u32 *ret_payload, u32 num_args, ...) |
174 | { |
175 | struct arm_smccc_res res; |
176 | u64 args[8] = {0}; |
177 | va_list arg_list; |
178 | u8 i; |
179 | |
180 | if (num_args > 8) |
181 | return -EINVAL; |
182 | |
183 | va_start(arg_list, num_args); |
184 | |
185 | for (i = 0; i < num_args; i++) |
186 | args[i] = va_arg(arg_list, u64); |
187 | |
188 | va_end(arg_list); |
189 | |
190 | arm_smccc_hvc(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], &res); |
191 | |
192 | if (ret_payload) { |
193 | ret_payload[0] = lower_32_bits(res.a0); |
194 | ret_payload[1] = upper_32_bits(res.a0); |
195 | ret_payload[2] = lower_32_bits(res.a1); |
196 | ret_payload[3] = upper_32_bits(res.a1); |
197 | } |
198 | |
199 | return zynqmp_pm_ret_code(ret_status: (enum pm_ret_status)res.a0); |
200 | } |
201 | |
202 | static int __do_feature_check_call(const u32 api_id, u32 *ret_payload) |
203 | { |
204 | int ret; |
205 | u64 smc_arg[2]; |
206 | u32 module_id; |
207 | u32 feature_check_api_id; |
208 | |
209 | module_id = FIELD_GET(MODULE_ID_MASK, api_id); |
210 | |
211 | /* |
212 | * Feature check of APIs belonging to PM, XSEM, and TF-A are handled by calling |
213 | * PM_FEATURE_CHECK API. For other modules, call PM_API_FEATURES API. |
214 | */ |
215 | if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID || module_id == TF_A_MODULE_ID) |
216 | feature_check_api_id = PM_FEATURE_CHECK; |
217 | else |
218 | feature_check_api_id = PM_API_FEATURES; |
219 | |
220 | /* |
221 | * Feature check of TF-A APIs is done in the TF-A layer and it expects for |
222 | * MODULE_ID_MASK bits of SMC's arg[0] to be the same as PM_MODULE_ID. |
223 | */ |
224 | if (module_id == TF_A_MODULE_ID) |
225 | module_id = PM_MODULE_ID; |
226 | |
227 | smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, module_id) | feature_check_api_id; |
228 | smc_arg[1] = (api_id & API_ID_MASK); |
229 | |
230 | ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]); |
231 | if (ret) |
232 | ret = -EOPNOTSUPP; |
233 | else |
234 | ret = ret_payload[1]; |
235 | |
236 | return ret; |
237 | } |
238 | |
239 | static int do_feature_check_call(const u32 api_id) |
240 | { |
241 | int ret; |
242 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
243 | struct pm_api_feature_data *feature_data; |
244 | |
245 | /* Check for existing entry in hash table for given api */ |
246 | hash_for_each_possible(pm_api_features_map, feature_data, hentry, |
247 | api_id) { |
248 | if (feature_data->pm_api_id == api_id) |
249 | return feature_data->feature_status; |
250 | } |
251 | |
252 | /* Add new entry if not present */ |
253 | feature_data = kmalloc(size: sizeof(*feature_data), GFP_ATOMIC); |
254 | if (!feature_data) |
255 | return -ENOMEM; |
256 | |
257 | feature_data->pm_api_id = api_id; |
258 | ret = __do_feature_check_call(api_id, ret_payload); |
259 | |
260 | feature_data->feature_status = ret; |
261 | hash_add(pm_api_features_map, &feature_data->hentry, api_id); |
262 | |
263 | if (api_id == PM_IOCTL) |
264 | /* Store supported IOCTL IDs mask */ |
265 | memcpy(ioctl_features, &ret_payload[2], FEATURE_PAYLOAD_SIZE * 4); |
266 | else if (api_id == PM_QUERY_DATA) |
267 | /* Store supported QUERY IDs mask */ |
268 | memcpy(query_features, &ret_payload[2], FEATURE_PAYLOAD_SIZE * 4); |
269 | |
270 | return ret; |
271 | } |
272 | |
273 | /** |
274 | * zynqmp_pm_feature() - Check whether given feature is supported or not and |
275 | * store supported IOCTL/QUERY ID mask |
276 | * @api_id: API ID to check |
277 | * |
278 | * Return: Returns status, either success or error+reason |
279 | */ |
280 | int zynqmp_pm_feature(const u32 api_id) |
281 | { |
282 | int ret; |
283 | |
284 | if (!feature_check_enabled) |
285 | return 0; |
286 | |
287 | ret = do_feature_check_call(api_id); |
288 | |
289 | return ret; |
290 | } |
291 | EXPORT_SYMBOL_GPL(zynqmp_pm_feature); |
292 | |
293 | /** |
294 | * zynqmp_pm_is_function_supported() - Check whether given IOCTL/QUERY function |
295 | * is supported or not |
296 | * @api_id: PM_IOCTL or PM_QUERY_DATA |
297 | * @id: IOCTL or QUERY function IDs |
298 | * |
299 | * Return: Returns status, either success or error+reason |
300 | */ |
301 | int zynqmp_pm_is_function_supported(const u32 api_id, const u32 id) |
302 | { |
303 | int ret; |
304 | u32 *bit_mask; |
305 | |
306 | /* Input arguments validation */ |
307 | if (id >= 64 || (api_id != PM_IOCTL && api_id != PM_QUERY_DATA)) |
308 | return -EINVAL; |
309 | |
310 | /* Check feature check API version */ |
311 | ret = do_feature_check_call(api_id: PM_FEATURE_CHECK); |
312 | if (ret < 0) |
313 | return ret; |
314 | |
315 | /* Check if feature check version 2 is supported or not */ |
316 | if ((ret & FIRMWARE_VERSION_MASK) == PM_API_VERSION_2) { |
317 | /* |
318 | * Call feature check for IOCTL/QUERY API to get IOCTL ID or |
319 | * QUERY ID feature status. |
320 | */ |
321 | ret = do_feature_check_call(api_id); |
322 | if (ret < 0) |
323 | return ret; |
324 | |
325 | bit_mask = (api_id == PM_IOCTL) ? ioctl_features : query_features; |
326 | |
327 | if ((bit_mask[(id / 32)] & BIT((id % 32))) == 0U) |
328 | return -EOPNOTSUPP; |
329 | } else { |
330 | return -ENODATA; |
331 | } |
332 | |
333 | return 0; |
334 | } |
335 | EXPORT_SYMBOL_GPL(zynqmp_pm_is_function_supported); |
336 | |
337 | /** |
338 | * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer |
339 | * caller function depending on the configuration |
340 | * @pm_api_id: Requested PM-API call |
341 | * @ret_payload: Returned value array |
342 | * @num_args: Number of arguments to requested PM-API call |
343 | * |
344 | * Invoke platform management function for SMC or HVC call, depending on |
345 | * configuration. |
346 | * Following SMC Calling Convention (SMCCC) for SMC64: |
347 | * Pm Function Identifier, |
348 | * PM_SIP_SVC + PM_API_ID = |
349 | * ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) |
350 | * ((SMC_64) << FUNCID_CC_SHIFT) |
351 | * ((SIP_START) << FUNCID_OEN_SHIFT) |
352 | * ((PM_API_ID) & FUNCID_NUM_MASK)) |
353 | * |
354 | * PM_SIP_SVC - Registered ZynqMP SIP Service Call. |
355 | * PM_API_ID - Platform Management API ID. |
356 | * |
357 | * Return: Returns status, either success or error+reason |
358 | */ |
359 | int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 *ret_payload, u32 num_args, ...) |
360 | { |
361 | /* |
362 | * Added SIP service call Function Identifier |
363 | * Make sure to stay in x0 register |
364 | */ |
365 | u64 smc_arg[8]; |
366 | int ret, i; |
367 | va_list arg_list; |
368 | u32 args[14] = {0}; |
369 | |
370 | if (num_args > 14) |
371 | return -EINVAL; |
372 | |
373 | va_start(arg_list, num_args); |
374 | |
375 | /* Check if feature is supported or not */ |
376 | ret = zynqmp_pm_feature(pm_api_id); |
377 | if (ret < 0) |
378 | return ret; |
379 | |
380 | for (i = 0; i < num_args; i++) |
381 | args[i] = va_arg(arg_list, u32); |
382 | |
383 | va_end(arg_list); |
384 | |
385 | smc_arg[0] = PM_SIP_SVC | pm_api_id; |
386 | for (i = 0; i < 7; i++) |
387 | smc_arg[i + 1] = ((u64)args[(i * 2) + 1] << 32) | args[i * 2]; |
388 | |
389 | return do_fw_call(ret_payload, 8, smc_arg[0], smc_arg[1], smc_arg[2], smc_arg[3], |
390 | smc_arg[4], smc_arg[5], smc_arg[6], smc_arg[7]); |
391 | } |
392 | |
393 | static u32 pm_api_version; |
394 | static u32 pm_tz_version; |
395 | static u32 pm_family_code; |
396 | static u32 pm_sub_family_code; |
397 | |
398 | int zynqmp_pm_register_sgi(u32 sgi_num, u32 reset) |
399 | { |
400 | int ret; |
401 | |
402 | ret = zynqmp_pm_invoke_fn(TF_A_PM_REGISTER_SGI, NULL, num_args: 2, sgi_num, reset); |
403 | if (ret != -EOPNOTSUPP && !ret) |
404 | return ret; |
405 | |
406 | /* try old implementation as fallback strategy if above fails */ |
407 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 3, IOCTL_REGISTER_SGI, sgi_num, reset); |
408 | } |
409 | |
410 | /** |
411 | * zynqmp_pm_get_api_version() - Get version number of PMU PM firmware |
412 | * @version: Returned version value |
413 | * |
414 | * Return: Returns status, either success or error+reason |
415 | */ |
416 | int zynqmp_pm_get_api_version(u32 *version) |
417 | { |
418 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
419 | int ret; |
420 | |
421 | if (!version) |
422 | return -EINVAL; |
423 | |
424 | /* Check is PM API version already verified */ |
425 | if (pm_api_version > 0) { |
426 | *version = pm_api_version; |
427 | return 0; |
428 | } |
429 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_GET_API_VERSION, ret_payload, num_args: 0); |
430 | *version = ret_payload[1]; |
431 | |
432 | return ret; |
433 | } |
434 | EXPORT_SYMBOL_GPL(zynqmp_pm_get_api_version); |
435 | |
436 | /** |
437 | * zynqmp_pm_get_chipid - Get silicon ID registers |
438 | * @idcode: IDCODE register |
439 | * @version: version register |
440 | * |
441 | * Return: Returns the status of the operation and the idcode and version |
442 | * registers in @idcode and @version. |
443 | */ |
444 | int zynqmp_pm_get_chipid(u32 *idcode, u32 *version) |
445 | { |
446 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
447 | int ret; |
448 | |
449 | if (!idcode || !version) |
450 | return -EINVAL; |
451 | |
452 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_GET_CHIPID, ret_payload, num_args: 0); |
453 | *idcode = ret_payload[1]; |
454 | *version = ret_payload[2]; |
455 | |
456 | return ret; |
457 | } |
458 | EXPORT_SYMBOL_GPL(zynqmp_pm_get_chipid); |
459 | |
460 | /** |
461 | * zynqmp_pm_get_family_info() - Get family info of platform |
462 | * @family: Returned family code value |
463 | * @subfamily: Returned sub-family code value |
464 | * |
465 | * Return: Returns status, either success or error+reason |
466 | */ |
467 | int zynqmp_pm_get_family_info(u32 *family, u32 *subfamily) |
468 | { |
469 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
470 | u32 idcode; |
471 | int ret; |
472 | |
473 | /* Check is family or sub-family code already received */ |
474 | if (pm_family_code && pm_sub_family_code) { |
475 | *family = pm_family_code; |
476 | *subfamily = pm_sub_family_code; |
477 | return 0; |
478 | } |
479 | |
480 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_GET_CHIPID, ret_payload, num_args: 0); |
481 | if (ret < 0) |
482 | return ret; |
483 | |
484 | idcode = ret_payload[1]; |
485 | pm_family_code = FIELD_GET(FAMILY_CODE_MASK, idcode); |
486 | pm_sub_family_code = FIELD_GET(SUB_FAMILY_CODE_MASK, idcode); |
487 | *family = pm_family_code; |
488 | *subfamily = pm_sub_family_code; |
489 | |
490 | return 0; |
491 | } |
492 | EXPORT_SYMBOL_GPL(zynqmp_pm_get_family_info); |
493 | |
494 | /** |
495 | * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version |
496 | * @version: Returned version value |
497 | * |
498 | * Return: Returns status, either success or error+reason |
499 | */ |
500 | static int zynqmp_pm_get_trustzone_version(u32 *version) |
501 | { |
502 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
503 | int ret; |
504 | |
505 | if (!version) |
506 | return -EINVAL; |
507 | |
508 | /* Check is PM trustzone version already verified */ |
509 | if (pm_tz_version > 0) { |
510 | *version = pm_tz_version; |
511 | return 0; |
512 | } |
513 | ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, ret_payload, num_args: 0); |
514 | *version = ret_payload[1]; |
515 | |
516 | return ret; |
517 | } |
518 | |
519 | /** |
520 | * get_set_conduit_method() - Choose SMC or HVC based communication |
521 | * @np: Pointer to the device_node structure |
522 | * |
523 | * Use SMC or HVC-based functions to communicate with EL2/EL3. |
524 | * |
525 | * Return: Returns 0 on success or error code |
526 | */ |
527 | static int get_set_conduit_method(struct device_node *np) |
528 | { |
529 | const char *method; |
530 | |
531 | if (of_property_read_string(np, propname: "method" , out_string: &method)) { |
532 | pr_warn("%s missing \"method\" property\n" , __func__); |
533 | return -ENXIO; |
534 | } |
535 | |
536 | if (!strcmp("hvc" , method)) { |
537 | do_fw_call = do_fw_call_hvc; |
538 | } else if (!strcmp("smc" , method)) { |
539 | do_fw_call = do_fw_call_smc; |
540 | } else { |
541 | pr_warn("%s Invalid \"method\" property: %s\n" , |
542 | __func__, method); |
543 | return -EINVAL; |
544 | } |
545 | |
546 | return 0; |
547 | } |
548 | |
549 | /** |
550 | * zynqmp_pm_query_data() - Get query data from firmware |
551 | * @qdata: Variable to the zynqmp_pm_query_data structure |
552 | * @out: Returned output value |
553 | * |
554 | * Return: Returns status, either success or error+reason |
555 | */ |
556 | int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out) |
557 | { |
558 | int ret; |
559 | |
560 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_QUERY_DATA, ret_payload: out, num_args: 4, qdata.qid, qdata.arg1, qdata.arg2, |
561 | qdata.arg3); |
562 | |
563 | /* |
564 | * For clock name query, all bytes in SMC response are clock name |
565 | * characters and return code is always success. For invalid clocks, |
566 | * clock name bytes would be zeros. |
567 | */ |
568 | return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret; |
569 | } |
570 | EXPORT_SYMBOL_GPL(zynqmp_pm_query_data); |
571 | |
572 | /** |
573 | * zynqmp_pm_clock_enable() - Enable the clock for given id |
574 | * @clock_id: ID of the clock to be enabled |
575 | * |
576 | * This function is used by master to enable the clock |
577 | * including peripherals and PLL clocks. |
578 | * |
579 | * Return: Returns status, either success or error+reason |
580 | */ |
581 | int zynqmp_pm_clock_enable(u32 clock_id) |
582 | { |
583 | return zynqmp_pm_invoke_fn(pm_api_id: PM_CLOCK_ENABLE, NULL, num_args: 1, clock_id); |
584 | } |
585 | EXPORT_SYMBOL_GPL(zynqmp_pm_clock_enable); |
586 | |
587 | /** |
588 | * zynqmp_pm_clock_disable() - Disable the clock for given id |
589 | * @clock_id: ID of the clock to be disable |
590 | * |
591 | * This function is used by master to disable the clock |
592 | * including peripherals and PLL clocks. |
593 | * |
594 | * Return: Returns status, either success or error+reason |
595 | */ |
596 | int zynqmp_pm_clock_disable(u32 clock_id) |
597 | { |
598 | return zynqmp_pm_invoke_fn(pm_api_id: PM_CLOCK_DISABLE, NULL, num_args: 1, clock_id); |
599 | } |
600 | EXPORT_SYMBOL_GPL(zynqmp_pm_clock_disable); |
601 | |
602 | /** |
603 | * zynqmp_pm_clock_getstate() - Get the clock state for given id |
604 | * @clock_id: ID of the clock to be queried |
605 | * @state: 1/0 (Enabled/Disabled) |
606 | * |
607 | * This function is used by master to get the state of clock |
608 | * including peripherals and PLL clocks. |
609 | * |
610 | * Return: Returns status, either success or error+reason |
611 | */ |
612 | int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state) |
613 | { |
614 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
615 | int ret; |
616 | |
617 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_CLOCK_GETSTATE, ret_payload, num_args: 1, clock_id); |
618 | *state = ret_payload[1]; |
619 | |
620 | return ret; |
621 | } |
622 | EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getstate); |
623 | |
624 | /** |
625 | * zynqmp_pm_clock_setdivider() - Set the clock divider for given id |
626 | * @clock_id: ID of the clock |
627 | * @divider: divider value |
628 | * |
629 | * This function is used by master to set divider for any clock |
630 | * to achieve desired rate. |
631 | * |
632 | * Return: Returns status, either success or error+reason |
633 | */ |
634 | int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider) |
635 | { |
636 | return zynqmp_pm_invoke_fn(pm_api_id: PM_CLOCK_SETDIVIDER, NULL, num_args: 2, clock_id, divider); |
637 | } |
638 | EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setdivider); |
639 | |
640 | /** |
641 | * zynqmp_pm_clock_getdivider() - Get the clock divider for given id |
642 | * @clock_id: ID of the clock |
643 | * @divider: divider value |
644 | * |
645 | * This function is used by master to get divider values |
646 | * for any clock. |
647 | * |
648 | * Return: Returns status, either success or error+reason |
649 | */ |
650 | int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider) |
651 | { |
652 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
653 | int ret; |
654 | |
655 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_CLOCK_GETDIVIDER, ret_payload, num_args: 1, clock_id); |
656 | *divider = ret_payload[1]; |
657 | |
658 | return ret; |
659 | } |
660 | EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getdivider); |
661 | |
662 | /** |
663 | * zynqmp_pm_clock_setparent() - Set the clock parent for given id |
664 | * @clock_id: ID of the clock |
665 | * @parent_id: parent id |
666 | * |
667 | * This function is used by master to set parent for any clock. |
668 | * |
669 | * Return: Returns status, either success or error+reason |
670 | */ |
671 | int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id) |
672 | { |
673 | return zynqmp_pm_invoke_fn(pm_api_id: PM_CLOCK_SETPARENT, NULL, num_args: 2, clock_id, parent_id); |
674 | } |
675 | EXPORT_SYMBOL_GPL(zynqmp_pm_clock_setparent); |
676 | |
677 | /** |
678 | * zynqmp_pm_clock_getparent() - Get the clock parent for given id |
679 | * @clock_id: ID of the clock |
680 | * @parent_id: parent id |
681 | * |
682 | * This function is used by master to get parent index |
683 | * for any clock. |
684 | * |
685 | * Return: Returns status, either success or error+reason |
686 | */ |
687 | int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id) |
688 | { |
689 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
690 | int ret; |
691 | |
692 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_CLOCK_GETPARENT, ret_payload, num_args: 1, clock_id); |
693 | *parent_id = ret_payload[1]; |
694 | |
695 | return ret; |
696 | } |
697 | EXPORT_SYMBOL_GPL(zynqmp_pm_clock_getparent); |
698 | |
699 | /** |
700 | * zynqmp_pm_set_pll_frac_mode() - PM API for set PLL mode |
701 | * |
702 | * @clk_id: PLL clock ID |
703 | * @mode: PLL mode (PLL_MODE_FRAC/PLL_MODE_INT) |
704 | * |
705 | * This function sets PLL mode |
706 | * |
707 | * Return: Returns status, either success or error+reason |
708 | */ |
709 | int zynqmp_pm_set_pll_frac_mode(u32 clk_id, u32 mode) |
710 | { |
711 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, 0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode); |
712 | } |
713 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_mode); |
714 | |
715 | /** |
716 | * zynqmp_pm_get_pll_frac_mode() - PM API for get PLL mode |
717 | * |
718 | * @clk_id: PLL clock ID |
719 | * @mode: PLL mode |
720 | * |
721 | * This function return current PLL mode |
722 | * |
723 | * Return: Returns status, either success or error+reason |
724 | */ |
725 | int zynqmp_pm_get_pll_frac_mode(u32 clk_id, u32 *mode) |
726 | { |
727 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, ret_payload: mode, num_args: 3, 0, IOCTL_GET_PLL_FRAC_MODE, clk_id); |
728 | } |
729 | EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_mode); |
730 | |
731 | /** |
732 | * zynqmp_pm_set_pll_frac_data() - PM API for setting pll fraction data |
733 | * |
734 | * @clk_id: PLL clock ID |
735 | * @data: fraction data |
736 | * |
737 | * This function sets fraction data. |
738 | * It is valid for fraction mode only. |
739 | * |
740 | * Return: Returns status, either success or error+reason |
741 | */ |
742 | int zynqmp_pm_set_pll_frac_data(u32 clk_id, u32 data) |
743 | { |
744 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, 0, IOCTL_SET_PLL_FRAC_DATA, clk_id, data); |
745 | } |
746 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_pll_frac_data); |
747 | |
748 | /** |
749 | * zynqmp_pm_get_pll_frac_data() - PM API for getting pll fraction data |
750 | * |
751 | * @clk_id: PLL clock ID |
752 | * @data: fraction data |
753 | * |
754 | * This function returns fraction data value. |
755 | * |
756 | * Return: Returns status, either success or error+reason |
757 | */ |
758 | int zynqmp_pm_get_pll_frac_data(u32 clk_id, u32 *data) |
759 | { |
760 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, ret_payload: data, num_args: 3, 0, IOCTL_GET_PLL_FRAC_DATA, clk_id); |
761 | } |
762 | EXPORT_SYMBOL_GPL(zynqmp_pm_get_pll_frac_data); |
763 | |
764 | /** |
765 | * zynqmp_pm_set_sd_tapdelay() - Set tap delay for the SD device |
766 | * |
767 | * @node_id: Node ID of the device |
768 | * @type: Type of tap delay to set (input/output) |
769 | * @value: Value to set fot the tap delay |
770 | * |
771 | * This function sets input/output tap delay for the SD device. |
772 | * |
773 | * Return: Returns status, either success or error+reason |
774 | */ |
775 | int zynqmp_pm_set_sd_tapdelay(u32 node_id, u32 type, u32 value) |
776 | { |
777 | u32 reg = (type == PM_TAPDELAY_INPUT) ? SD_ITAPDLY : SD_OTAPDLYSEL; |
778 | u32 mask = (node_id == NODE_SD_0) ? GENMASK(15, 0) : GENMASK(31, 16); |
779 | |
780 | if (value) { |
781 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, node_id, IOCTL_SET_SD_TAPDELAY, type, |
782 | value); |
783 | } |
784 | |
785 | /* |
786 | * Work around completely misdesigned firmware API on Xilinx ZynqMP. |
787 | * The IOCTL_SET_SD_TAPDELAY firmware call allows the caller to only |
788 | * ever set IOU_SLCR SD_ITAPDLY Register SD0_ITAPDLYENA/SD1_ITAPDLYENA |
789 | * bits, but there is no matching call to clear those bits. If those |
790 | * bits are not cleared, SDMMC tuning may fail. |
791 | * |
792 | * Luckily, there are PM_MMIO_READ/PM_MMIO_WRITE calls which seem to |
793 | * allow complete unrestricted access to all address space, including |
794 | * IOU_SLCR SD_ITAPDLY Register and all the other registers, access |
795 | * to which was supposed to be protected by the current firmware API. |
796 | * |
797 | * Use PM_MMIO_READ/PM_MMIO_WRITE to re-implement the missing counter |
798 | * part of IOCTL_SET_SD_TAPDELAY which clears SDx_ITAPDLYENA bits. |
799 | */ |
800 | return zynqmp_pm_invoke_fn(pm_api_id: PM_MMIO_WRITE, NULL, num_args: 2, reg, mask); |
801 | } |
802 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_tapdelay); |
803 | |
804 | /** |
805 | * zynqmp_pm_sd_dll_reset() - Reset DLL logic |
806 | * |
807 | * @node_id: Node ID of the device |
808 | * @type: Reset type |
809 | * |
810 | * This function resets DLL logic for the SD device. |
811 | * |
812 | * Return: Returns status, either success or error+reason |
813 | */ |
814 | int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type) |
815 | { |
816 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 3, node_id, IOCTL_SD_DLL_RESET, type); |
817 | } |
818 | EXPORT_SYMBOL_GPL(zynqmp_pm_sd_dll_reset); |
819 | |
820 | /** |
821 | * zynqmp_pm_ospi_mux_select() - OSPI Mux selection |
822 | * |
823 | * @dev_id: Device Id of the OSPI device. |
824 | * @select: OSPI Mux select value. |
825 | * |
826 | * This function select the OSPI Mux. |
827 | * |
828 | * Return: Returns status, either success or error+reason |
829 | */ |
830 | int zynqmp_pm_ospi_mux_select(u32 dev_id, u32 select) |
831 | { |
832 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 3, dev_id, IOCTL_OSPI_MUX_SELECT, select); |
833 | } |
834 | EXPORT_SYMBOL_GPL(zynqmp_pm_ospi_mux_select); |
835 | |
836 | /** |
837 | * zynqmp_pm_write_ggs() - PM API for writing global general storage (ggs) |
838 | * @index: GGS register index |
839 | * @value: Register value to be written |
840 | * |
841 | * This function writes value to GGS register. |
842 | * |
843 | * Return: Returns status, either success or error+reason |
844 | */ |
845 | int zynqmp_pm_write_ggs(u32 index, u32 value) |
846 | { |
847 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, 0, IOCTL_WRITE_GGS, index, value); |
848 | } |
849 | EXPORT_SYMBOL_GPL(zynqmp_pm_write_ggs); |
850 | |
851 | /** |
852 | * zynqmp_pm_read_ggs() - PM API for reading global general storage (ggs) |
853 | * @index: GGS register index |
854 | * @value: Register value to be written |
855 | * |
856 | * This function returns GGS register value. |
857 | * |
858 | * Return: Returns status, either success or error+reason |
859 | */ |
860 | int zynqmp_pm_read_ggs(u32 index, u32 *value) |
861 | { |
862 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, ret_payload: value, num_args: 3, 0, IOCTL_READ_GGS, index); |
863 | } |
864 | EXPORT_SYMBOL_GPL(zynqmp_pm_read_ggs); |
865 | |
866 | /** |
867 | * zynqmp_pm_write_pggs() - PM API for writing persistent global general |
868 | * storage (pggs) |
869 | * @index: PGGS register index |
870 | * @value: Register value to be written |
871 | * |
872 | * This function writes value to PGGS register. |
873 | * |
874 | * Return: Returns status, either success or error+reason |
875 | */ |
876 | int zynqmp_pm_write_pggs(u32 index, u32 value) |
877 | { |
878 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, 0, IOCTL_WRITE_PGGS, index, value); |
879 | } |
880 | EXPORT_SYMBOL_GPL(zynqmp_pm_write_pggs); |
881 | |
882 | /** |
883 | * zynqmp_pm_read_pggs() - PM API for reading persistent global general |
884 | * storage (pggs) |
885 | * @index: PGGS register index |
886 | * @value: Register value to be written |
887 | * |
888 | * This function returns PGGS register value. |
889 | * |
890 | * Return: Returns status, either success or error+reason |
891 | */ |
892 | int zynqmp_pm_read_pggs(u32 index, u32 *value) |
893 | { |
894 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, ret_payload: value, num_args: 3, 0, IOCTL_READ_PGGS, index); |
895 | } |
896 | EXPORT_SYMBOL_GPL(zynqmp_pm_read_pggs); |
897 | |
898 | int zynqmp_pm_set_tapdelay_bypass(u32 index, u32 value) |
899 | { |
900 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, 0, IOCTL_SET_TAPDELAY_BYPASS, index, value); |
901 | } |
902 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_tapdelay_bypass); |
903 | |
904 | /** |
905 | * zynqmp_pm_set_boot_health_status() - PM API for setting healthy boot status |
906 | * @value: Status value to be written |
907 | * |
908 | * This function sets healthy bit value to indicate boot health status |
909 | * to firmware. |
910 | * |
911 | * Return: Returns status, either success or error+reason |
912 | */ |
913 | int zynqmp_pm_set_boot_health_status(u32 value) |
914 | { |
915 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 3, 0, IOCTL_SET_BOOT_HEALTH_STATUS, value); |
916 | } |
917 | |
918 | /** |
919 | * zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release) |
920 | * @reset: Reset to be configured |
921 | * @assert_flag: Flag stating should reset be asserted (1) or |
922 | * released (0) |
923 | * |
924 | * Return: Returns status, either success or error+reason |
925 | */ |
926 | int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset, |
927 | const enum zynqmp_pm_reset_action assert_flag) |
928 | { |
929 | return zynqmp_pm_invoke_fn(pm_api_id: PM_RESET_ASSERT, NULL, num_args: 2, reset, assert_flag); |
930 | } |
931 | EXPORT_SYMBOL_GPL(zynqmp_pm_reset_assert); |
932 | |
933 | /** |
934 | * zynqmp_pm_reset_get_status - Get status of the reset |
935 | * @reset: Reset whose status should be returned |
936 | * @status: Returned status |
937 | * |
938 | * Return: Returns status, either success or error+reason |
939 | */ |
940 | int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, u32 *status) |
941 | { |
942 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
943 | int ret; |
944 | |
945 | if (!status) |
946 | return -EINVAL; |
947 | |
948 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_RESET_GET_STATUS, ret_payload, num_args: 1, reset); |
949 | *status = ret_payload[1]; |
950 | |
951 | return ret; |
952 | } |
953 | EXPORT_SYMBOL_GPL(zynqmp_pm_reset_get_status); |
954 | |
955 | /** |
956 | * zynqmp_pm_fpga_load - Perform the fpga load |
957 | * @address: Address to write to |
958 | * @size: pl bitstream size |
959 | * @flags: Bitstream type |
960 | * -XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration |
961 | * -XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration |
962 | * |
963 | * This function provides access to pmufw. To transfer |
964 | * the required bitstream into PL. |
965 | * |
966 | * Return: Returns status, either success or error+reason |
967 | */ |
968 | int zynqmp_pm_fpga_load(const u64 address, const u32 size, const u32 flags) |
969 | { |
970 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
971 | int ret; |
972 | |
973 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_FPGA_LOAD, ret_payload, num_args: 4, lower_32_bits(address), |
974 | upper_32_bits(address), size, flags); |
975 | if (ret_payload[0]) |
976 | return -ret_payload[0]; |
977 | |
978 | return ret; |
979 | } |
980 | EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_load); |
981 | |
982 | /** |
983 | * zynqmp_pm_fpga_get_status - Read value from PCAP status register |
984 | * @value: Value to read |
985 | * |
986 | * This function provides access to the pmufw to get the PCAP |
987 | * status |
988 | * |
989 | * Return: Returns status, either success or error+reason |
990 | */ |
991 | int zynqmp_pm_fpga_get_status(u32 *value) |
992 | { |
993 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
994 | int ret; |
995 | |
996 | if (!value) |
997 | return -EINVAL; |
998 | |
999 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_FPGA_GET_STATUS, ret_payload, num_args: 0); |
1000 | *value = ret_payload[1]; |
1001 | |
1002 | return ret; |
1003 | } |
1004 | EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_status); |
1005 | |
1006 | /** |
1007 | * zynqmp_pm_fpga_get_config_status - Get the FPGA configuration status. |
1008 | * @value: Buffer to store FPGA configuration status. |
1009 | * |
1010 | * This function provides access to the pmufw to get the FPGA configuration |
1011 | * status |
1012 | * |
1013 | * Return: 0 on success, a negative value on error |
1014 | */ |
1015 | int zynqmp_pm_fpga_get_config_status(u32 *value) |
1016 | { |
1017 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1018 | u32 buf, lower_addr, upper_addr; |
1019 | int ret; |
1020 | |
1021 | if (!value) |
1022 | return -EINVAL; |
1023 | |
1024 | lower_addr = lower_32_bits((u64)&buf); |
1025 | upper_addr = upper_32_bits((u64)&buf); |
1026 | |
1027 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_FPGA_READ, ret_payload, num_args: 4, |
1028 | XILINX_ZYNQMP_PM_FPGA_CONFIG_STAT_OFFSET, lower_addr, upper_addr, |
1029 | XILINX_ZYNQMP_PM_FPGA_READ_CONFIG_REG); |
1030 | |
1031 | *value = ret_payload[1]; |
1032 | |
1033 | return ret; |
1034 | } |
1035 | EXPORT_SYMBOL_GPL(zynqmp_pm_fpga_get_config_status); |
1036 | |
1037 | /** |
1038 | * zynqmp_pm_pinctrl_request - Request Pin from firmware |
1039 | * @pin: Pin number to request |
1040 | * |
1041 | * This function requests pin from firmware. |
1042 | * |
1043 | * Return: Returns status, either success or error+reason. |
1044 | */ |
1045 | int zynqmp_pm_pinctrl_request(const u32 pin) |
1046 | { |
1047 | return zynqmp_pm_invoke_fn(pm_api_id: PM_PINCTRL_REQUEST, NULL, num_args: 1, pin); |
1048 | } |
1049 | EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_request); |
1050 | |
1051 | /** |
1052 | * zynqmp_pm_pinctrl_release - Inform firmware that Pin control is released |
1053 | * @pin: Pin number to release |
1054 | * |
1055 | * This function release pin from firmware. |
1056 | * |
1057 | * Return: Returns status, either success or error+reason. |
1058 | */ |
1059 | int zynqmp_pm_pinctrl_release(const u32 pin) |
1060 | { |
1061 | return zynqmp_pm_invoke_fn(pm_api_id: PM_PINCTRL_RELEASE, NULL, num_args: 1, pin); |
1062 | } |
1063 | EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_release); |
1064 | |
1065 | /** |
1066 | * zynqmp_pm_pinctrl_set_function - Set requested function for the pin |
1067 | * @pin: Pin number |
1068 | * @id: Function ID to set |
1069 | * |
1070 | * This function sets requested function for the given pin. |
1071 | * |
1072 | * Return: Returns status, either success or error+reason. |
1073 | */ |
1074 | int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 id) |
1075 | { |
1076 | return zynqmp_pm_invoke_fn(pm_api_id: PM_PINCTRL_SET_FUNCTION, NULL, num_args: 2, pin, id); |
1077 | } |
1078 | EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_function); |
1079 | |
1080 | /** |
1081 | * zynqmp_pm_pinctrl_get_config - Get configuration parameter for the pin |
1082 | * @pin: Pin number |
1083 | * @param: Parameter to get |
1084 | * @value: Buffer to store parameter value |
1085 | * |
1086 | * This function gets requested configuration parameter for the given pin. |
1087 | * |
1088 | * Return: Returns status, either success or error+reason. |
1089 | */ |
1090 | int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param, |
1091 | u32 *value) |
1092 | { |
1093 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1094 | int ret; |
1095 | |
1096 | if (!value) |
1097 | return -EINVAL; |
1098 | |
1099 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_PINCTRL_CONFIG_PARAM_GET, ret_payload, num_args: 2, pin, param); |
1100 | *value = ret_payload[1]; |
1101 | |
1102 | return ret; |
1103 | } |
1104 | EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_get_config); |
1105 | |
1106 | /** |
1107 | * zynqmp_pm_pinctrl_set_config - Set configuration parameter for the pin |
1108 | * @pin: Pin number |
1109 | * @param: Parameter to set |
1110 | * @value: Parameter value to set |
1111 | * |
1112 | * This function sets requested configuration parameter for the given pin. |
1113 | * |
1114 | * Return: Returns status, either success or error+reason. |
1115 | */ |
1116 | int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, |
1117 | u32 value) |
1118 | { |
1119 | int ret; |
1120 | |
1121 | if (pm_family_code == ZYNQMP_FAMILY_CODE && |
1122 | param == PM_PINCTRL_CONFIG_TRI_STATE) { |
1123 | ret = zynqmp_pm_feature(PM_PINCTRL_CONFIG_PARAM_SET); |
1124 | if (ret < PM_PINCTRL_PARAM_SET_VERSION) |
1125 | return -EOPNOTSUPP; |
1126 | } |
1127 | |
1128 | return zynqmp_pm_invoke_fn(pm_api_id: PM_PINCTRL_CONFIG_PARAM_SET, NULL, num_args: 3, pin, param, value); |
1129 | } |
1130 | EXPORT_SYMBOL_GPL(zynqmp_pm_pinctrl_set_config); |
1131 | |
1132 | /** |
1133 | * zynqmp_pm_bootmode_read() - PM Config API for read bootpin status |
1134 | * @ps_mode: Returned output value of ps_mode |
1135 | * |
1136 | * This API function is to be used for notify the power management controller |
1137 | * to read bootpin status. |
1138 | * |
1139 | * Return: status, either success or error+reason |
1140 | */ |
1141 | unsigned int zynqmp_pm_bootmode_read(u32 *ps_mode) |
1142 | { |
1143 | unsigned int ret; |
1144 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1145 | |
1146 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_MMIO_READ, ret_payload, num_args: 1, CRL_APB_BOOT_PIN_CTRL); |
1147 | |
1148 | *ps_mode = ret_payload[1]; |
1149 | |
1150 | return ret; |
1151 | } |
1152 | EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_read); |
1153 | |
1154 | /** |
1155 | * zynqmp_pm_bootmode_write() - PM Config API for Configure bootpin |
1156 | * @ps_mode: Value to be written to the bootpin ctrl register |
1157 | * |
1158 | * This API function is to be used for notify the power management controller |
1159 | * to configure bootpin. |
1160 | * |
1161 | * Return: Returns status, either success or error+reason |
1162 | */ |
1163 | int zynqmp_pm_bootmode_write(u32 ps_mode) |
1164 | { |
1165 | return zynqmp_pm_invoke_fn(pm_api_id: PM_MMIO_WRITE, NULL, num_args: 3, CRL_APB_BOOT_PIN_CTRL, |
1166 | CRL_APB_BOOTPIN_CTRL_MASK, ps_mode); |
1167 | } |
1168 | EXPORT_SYMBOL_GPL(zynqmp_pm_bootmode_write); |
1169 | |
1170 | /** |
1171 | * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller |
1172 | * master has initialized its own power management |
1173 | * |
1174 | * Return: Returns status, either success or error+reason |
1175 | * |
1176 | * This API function is to be used for notify the power management controller |
1177 | * about the completed power management initialization. |
1178 | */ |
1179 | int zynqmp_pm_init_finalize(void) |
1180 | { |
1181 | return zynqmp_pm_invoke_fn(pm_api_id: PM_PM_INIT_FINALIZE, NULL, num_args: 0); |
1182 | } |
1183 | EXPORT_SYMBOL_GPL(zynqmp_pm_init_finalize); |
1184 | |
1185 | /** |
1186 | * zynqmp_pm_set_suspend_mode() - Set system suspend mode |
1187 | * @mode: Mode to set for system suspend |
1188 | * |
1189 | * This API function is used to set mode of system suspend. |
1190 | * |
1191 | * Return: Returns status, either success or error+reason |
1192 | */ |
1193 | int zynqmp_pm_set_suspend_mode(u32 mode) |
1194 | { |
1195 | return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, NULL, num_args: 1, mode); |
1196 | } |
1197 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_suspend_mode); |
1198 | |
1199 | /** |
1200 | * zynqmp_pm_request_node() - Request a node with specific capabilities |
1201 | * @node: Node ID of the slave |
1202 | * @capabilities: Requested capabilities of the slave |
1203 | * @qos: Quality of service (not supported) |
1204 | * @ack: Flag to specify whether acknowledge is requested |
1205 | * |
1206 | * This function is used by master to request particular node from firmware. |
1207 | * Every master must request node before using it. |
1208 | * |
1209 | * Return: Returns status, either success or error+reason |
1210 | */ |
1211 | int zynqmp_pm_request_node(const u32 node, const u32 capabilities, |
1212 | const u32 qos, const enum zynqmp_pm_request_ack ack) |
1213 | { |
1214 | return zynqmp_pm_invoke_fn(pm_api_id: PM_REQUEST_NODE, NULL, num_args: 4, node, capabilities, qos, ack); |
1215 | } |
1216 | EXPORT_SYMBOL_GPL(zynqmp_pm_request_node); |
1217 | |
1218 | /** |
1219 | * zynqmp_pm_release_node() - Release a node |
1220 | * @node: Node ID of the slave |
1221 | * |
1222 | * This function is used by master to inform firmware that master |
1223 | * has released node. Once released, master must not use that node |
1224 | * without re-request. |
1225 | * |
1226 | * Return: Returns status, either success or error+reason |
1227 | */ |
1228 | int zynqmp_pm_release_node(const u32 node) |
1229 | { |
1230 | return zynqmp_pm_invoke_fn(pm_api_id: PM_RELEASE_NODE, NULL, num_args: 1, node); |
1231 | } |
1232 | EXPORT_SYMBOL_GPL(zynqmp_pm_release_node); |
1233 | |
1234 | /** |
1235 | * zynqmp_pm_get_rpu_mode() - Get RPU mode |
1236 | * @node_id: Node ID of the device |
1237 | * @rpu_mode: return by reference value |
1238 | * either split or lockstep |
1239 | * |
1240 | * Return: return 0 on success or error+reason. |
1241 | * if success, then rpu_mode will be set |
1242 | * to current rpu mode. |
1243 | */ |
1244 | int zynqmp_pm_get_rpu_mode(u32 node_id, enum rpu_oper_mode *rpu_mode) |
1245 | { |
1246 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1247 | int ret; |
1248 | |
1249 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, ret_payload, num_args: 2, node_id, IOCTL_GET_RPU_OPER_MODE); |
1250 | |
1251 | /* only set rpu_mode if no error */ |
1252 | if (ret == XST_PM_SUCCESS) |
1253 | *rpu_mode = ret_payload[0]; |
1254 | |
1255 | return ret; |
1256 | } |
1257 | EXPORT_SYMBOL_GPL(zynqmp_pm_get_rpu_mode); |
1258 | |
1259 | /** |
1260 | * zynqmp_pm_set_rpu_mode() - Set RPU mode |
1261 | * @node_id: Node ID of the device |
1262 | * @rpu_mode: Argument 1 to requested IOCTL call. either split or lockstep |
1263 | * |
1264 | * This function is used to set RPU mode to split or |
1265 | * lockstep |
1266 | * |
1267 | * Return: Returns status, either success or error+reason |
1268 | */ |
1269 | int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode) |
1270 | { |
1271 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 3, node_id, IOCTL_SET_RPU_OPER_MODE, |
1272 | (u32)rpu_mode); |
1273 | } |
1274 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_rpu_mode); |
1275 | |
1276 | /** |
1277 | * zynqmp_pm_set_tcm_config - configure TCM |
1278 | * @node_id: Firmware specific TCM subsystem ID |
1279 | * @tcm_mode: Argument 1 to requested IOCTL call |
1280 | * either PM_RPU_TCM_COMB or PM_RPU_TCM_SPLIT |
1281 | * |
1282 | * This function is used to set RPU mode to split or combined |
1283 | * |
1284 | * Return: status: 0 for success, else failure |
1285 | */ |
1286 | int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode) |
1287 | { |
1288 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 3, node_id, IOCTL_TCM_COMB_CONFIG, |
1289 | (u32)tcm_mode); |
1290 | } |
1291 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_tcm_config); |
1292 | |
1293 | /** |
1294 | * zynqmp_pm_force_pwrdwn - PM call to request for another PU or subsystem to |
1295 | * be powered down forcefully |
1296 | * @node: Node ID of the targeted PU or subsystem |
1297 | * @ack: Flag to specify whether acknowledge is requested |
1298 | * |
1299 | * Return: status, either success or error+reason |
1300 | */ |
1301 | int zynqmp_pm_force_pwrdwn(const u32 node, |
1302 | const enum zynqmp_pm_request_ack ack) |
1303 | { |
1304 | return zynqmp_pm_invoke_fn(pm_api_id: PM_FORCE_POWERDOWN, NULL, num_args: 2, node, ack); |
1305 | } |
1306 | EXPORT_SYMBOL_GPL(zynqmp_pm_force_pwrdwn); |
1307 | |
1308 | /** |
1309 | * zynqmp_pm_request_wake - PM call to wake up selected master or subsystem |
1310 | * @node: Node ID of the master or subsystem |
1311 | * @set_addr: Specifies whether the address argument is relevant |
1312 | * @address: Address from which to resume when woken up |
1313 | * @ack: Flag to specify whether acknowledge requested |
1314 | * |
1315 | * Return: status, either success or error+reason |
1316 | */ |
1317 | int zynqmp_pm_request_wake(const u32 node, |
1318 | const bool set_addr, |
1319 | const u64 address, |
1320 | const enum zynqmp_pm_request_ack ack) |
1321 | { |
1322 | /* set_addr flag is encoded into 1st bit of address */ |
1323 | return zynqmp_pm_invoke_fn(pm_api_id: PM_REQUEST_WAKEUP, NULL, num_args: 4, node, address | set_addr, |
1324 | address >> 32, ack); |
1325 | } |
1326 | EXPORT_SYMBOL_GPL(zynqmp_pm_request_wake); |
1327 | |
1328 | /** |
1329 | * zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves |
1330 | * @node: Node ID of the slave |
1331 | * @capabilities: Requested capabilities of the slave |
1332 | * @qos: Quality of service (not supported) |
1333 | * @ack: Flag to specify whether acknowledge is requested |
1334 | * |
1335 | * This API function is to be used for slaves a PU already has requested |
1336 | * to change its capabilities. |
1337 | * |
1338 | * Return: Returns status, either success or error+reason |
1339 | */ |
1340 | int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities, |
1341 | const u32 qos, |
1342 | const enum zynqmp_pm_request_ack ack) |
1343 | { |
1344 | return zynqmp_pm_invoke_fn(pm_api_id: PM_SET_REQUIREMENT, NULL, num_args: 4, node, capabilities, qos, ack); |
1345 | } |
1346 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_requirement); |
1347 | |
1348 | /** |
1349 | * zynqmp_pm_load_pdi - Load and process PDI |
1350 | * @src: Source device where PDI is located |
1351 | * @address: PDI src address |
1352 | * |
1353 | * This function provides support to load PDI from linux |
1354 | * |
1355 | * Return: Returns status, either success or error+reason |
1356 | */ |
1357 | int zynqmp_pm_load_pdi(const u32 src, const u64 address) |
1358 | { |
1359 | return zynqmp_pm_invoke_fn(PM_LOAD_PDI, NULL, num_args: 3, src, lower_32_bits(address), |
1360 | upper_32_bits(address)); |
1361 | } |
1362 | EXPORT_SYMBOL_GPL(zynqmp_pm_load_pdi); |
1363 | |
1364 | /** |
1365 | * zynqmp_pm_aes_engine - Access AES hardware to encrypt/decrypt the data using |
1366 | * AES-GCM core. |
1367 | * @address: Address of the AesParams structure. |
1368 | * @out: Returned output value |
1369 | * |
1370 | * Return: Returns status, either success or error code. |
1371 | */ |
1372 | int zynqmp_pm_aes_engine(const u64 address, u32 *out) |
1373 | { |
1374 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1375 | int ret; |
1376 | |
1377 | if (!out) |
1378 | return -EINVAL; |
1379 | |
1380 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_SECURE_AES, ret_payload, num_args: 2, upper_32_bits(address), |
1381 | lower_32_bits(address)); |
1382 | *out = ret_payload[1]; |
1383 | |
1384 | return ret; |
1385 | } |
1386 | EXPORT_SYMBOL_GPL(zynqmp_pm_aes_engine); |
1387 | |
1388 | /** |
1389 | * zynqmp_pm_efuse_access - Provides access to efuse memory. |
1390 | * @address: Address of the efuse params structure |
1391 | * @out: Returned output value |
1392 | * |
1393 | * Return: Returns status, either success or error code. |
1394 | */ |
1395 | int zynqmp_pm_efuse_access(const u64 address, u32 *out) |
1396 | { |
1397 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1398 | int ret; |
1399 | |
1400 | if (!out) |
1401 | return -EINVAL; |
1402 | |
1403 | ret = zynqmp_pm_invoke_fn(pm_api_id: PM_EFUSE_ACCESS, ret_payload, num_args: 2, |
1404 | upper_32_bits(address), |
1405 | lower_32_bits(address)); |
1406 | *out = ret_payload[1]; |
1407 | |
1408 | return ret; |
1409 | } |
1410 | EXPORT_SYMBOL_GPL(zynqmp_pm_efuse_access); |
1411 | |
1412 | /** |
1413 | * zynqmp_pm_sha_hash - Access the SHA engine to calculate the hash |
1414 | * @address: Address of the data/ Address of output buffer where |
1415 | * hash should be stored. |
1416 | * @size: Size of the data. |
1417 | * @flags: |
1418 | * BIT(0) - for initializing csudma driver and SHA3(Here address |
1419 | * and size inputs can be NULL). |
1420 | * BIT(1) - to call Sha3_Update API which can be called multiple |
1421 | * times when data is not contiguous. |
1422 | * BIT(2) - to get final hash of the whole updated data. |
1423 | * Hash will be overwritten at provided address with |
1424 | * 48 bytes. |
1425 | * |
1426 | * Return: Returns status, either success or error code. |
1427 | */ |
1428 | int zynqmp_pm_sha_hash(const u64 address, const u32 size, const u32 flags) |
1429 | { |
1430 | u32 lower_addr = lower_32_bits(address); |
1431 | u32 upper_addr = upper_32_bits(address); |
1432 | |
1433 | return zynqmp_pm_invoke_fn(pm_api_id: PM_SECURE_SHA, NULL, num_args: 4, upper_addr, lower_addr, size, flags); |
1434 | } |
1435 | EXPORT_SYMBOL_GPL(zynqmp_pm_sha_hash); |
1436 | |
1437 | /** |
1438 | * zynqmp_pm_register_notifier() - PM API for register a subsystem |
1439 | * to be notified about specific |
1440 | * event/error. |
1441 | * @node: Node ID to which the event is related. |
1442 | * @event: Event Mask of Error events for which wants to get notified. |
1443 | * @wake: Wake subsystem upon capturing the event if value 1 |
1444 | * @enable: Enable the registration for value 1, disable for value 0 |
1445 | * |
1446 | * This function is used to register/un-register for particular node-event |
1447 | * combination in firmware. |
1448 | * |
1449 | * Return: Returns status, either success or error+reason |
1450 | */ |
1451 | |
1452 | int zynqmp_pm_register_notifier(const u32 node, const u32 event, |
1453 | const u32 wake, const u32 enable) |
1454 | { |
1455 | return zynqmp_pm_invoke_fn(pm_api_id: PM_REGISTER_NOTIFIER, NULL, num_args: 4, node, event, wake, enable); |
1456 | } |
1457 | EXPORT_SYMBOL_GPL(zynqmp_pm_register_notifier); |
1458 | |
1459 | /** |
1460 | * zynqmp_pm_system_shutdown - PM call to request a system shutdown or restart |
1461 | * @type: Shutdown or restart? 0 for shutdown, 1 for restart |
1462 | * @subtype: Specifies which system should be restarted or shut down |
1463 | * |
1464 | * Return: Returns status, either success or error+reason |
1465 | */ |
1466 | int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype) |
1467 | { |
1468 | return zynqmp_pm_invoke_fn(pm_api_id: PM_SYSTEM_SHUTDOWN, NULL, num_args: 2, type, subtype); |
1469 | } |
1470 | |
1471 | /** |
1472 | * zynqmp_pm_set_feature_config - PM call to request IOCTL for feature config |
1473 | * @id: The config ID of the feature to be configured |
1474 | * @value: The config value of the feature to be configured |
1475 | * |
1476 | * Return: Returns 0 on success or error value on failure. |
1477 | */ |
1478 | int zynqmp_pm_set_feature_config(enum pm_feature_config_id id, u32 value) |
1479 | { |
1480 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, 0, IOCTL_SET_FEATURE_CONFIG, id, value); |
1481 | } |
1482 | |
1483 | /** |
1484 | * zynqmp_pm_get_feature_config - PM call to get value of configured feature |
1485 | * @id: The config id of the feature to be queried |
1486 | * @payload: Returned value array |
1487 | * |
1488 | * Return: Returns 0 on success or error value on failure. |
1489 | */ |
1490 | int zynqmp_pm_get_feature_config(enum pm_feature_config_id id, |
1491 | u32 *payload) |
1492 | { |
1493 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, ret_payload: payload, num_args: 3, 0, IOCTL_GET_FEATURE_CONFIG, id); |
1494 | } |
1495 | |
1496 | /** |
1497 | * zynqmp_pm_set_sd_config - PM call to set value of SD config registers |
1498 | * @node: SD node ID |
1499 | * @config: The config type of SD registers |
1500 | * @value: Value to be set |
1501 | * |
1502 | * Return: Returns 0 on success or error value on failure. |
1503 | */ |
1504 | int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value) |
1505 | { |
1506 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, node, IOCTL_SET_SD_CONFIG, config, value); |
1507 | } |
1508 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_sd_config); |
1509 | |
1510 | /** |
1511 | * zynqmp_pm_set_gem_config - PM call to set value of GEM config registers |
1512 | * @node: GEM node ID |
1513 | * @config: The config type of GEM registers |
1514 | * @value: Value to be set |
1515 | * |
1516 | * Return: Returns 0 on success or error value on failure. |
1517 | */ |
1518 | int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config, |
1519 | u32 value) |
1520 | { |
1521 | return zynqmp_pm_invoke_fn(pm_api_id: PM_IOCTL, NULL, num_args: 4, node, IOCTL_SET_GEM_CONFIG, config, value); |
1522 | } |
1523 | EXPORT_SYMBOL_GPL(zynqmp_pm_set_gem_config); |
1524 | |
1525 | /** |
1526 | * struct zynqmp_pm_shutdown_scope - Struct for shutdown scope |
1527 | * @subtype: Shutdown subtype |
1528 | * @name: Matching string for scope argument |
1529 | * |
1530 | * This struct encapsulates mapping between shutdown scope ID and string. |
1531 | */ |
1532 | struct zynqmp_pm_shutdown_scope { |
1533 | const enum zynqmp_pm_shutdown_subtype subtype; |
1534 | const char *name; |
1535 | }; |
1536 | |
1537 | static struct zynqmp_pm_shutdown_scope shutdown_scopes[] = { |
1538 | [ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM] = { |
1539 | .subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM, |
1540 | .name = "subsystem" , |
1541 | }, |
1542 | [ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY] = { |
1543 | .subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_PS_ONLY, |
1544 | .name = "ps_only" , |
1545 | }, |
1546 | [ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM] = { |
1547 | .subtype = ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM, |
1548 | .name = "system" , |
1549 | }, |
1550 | }; |
1551 | |
1552 | static struct zynqmp_pm_shutdown_scope *selected_scope = |
1553 | &shutdown_scopes[ZYNQMP_PM_SHUTDOWN_SUBTYPE_SYSTEM]; |
1554 | |
1555 | /** |
1556 | * zynqmp_pm_is_shutdown_scope_valid - Check if shutdown scope string is valid |
1557 | * @scope_string: Shutdown scope string |
1558 | * |
1559 | * Return: Return pointer to matching shutdown scope struct from |
1560 | * array of available options in system if string is valid, |
1561 | * otherwise returns NULL. |
1562 | */ |
1563 | static struct zynqmp_pm_shutdown_scope* |
1564 | zynqmp_pm_is_shutdown_scope_valid(const char *scope_string) |
1565 | { |
1566 | int count; |
1567 | |
1568 | for (count = 0; count < ARRAY_SIZE(shutdown_scopes); count++) |
1569 | if (sysfs_streq(s1: scope_string, s2: shutdown_scopes[count].name)) |
1570 | return &shutdown_scopes[count]; |
1571 | |
1572 | return NULL; |
1573 | } |
1574 | |
1575 | static ssize_t shutdown_scope_show(struct device *device, |
1576 | struct device_attribute *attr, |
1577 | char *buf) |
1578 | { |
1579 | int i; |
1580 | |
1581 | for (i = 0; i < ARRAY_SIZE(shutdown_scopes); i++) { |
1582 | if (&shutdown_scopes[i] == selected_scope) { |
1583 | strcat(p: buf, q: "[" ); |
1584 | strcat(p: buf, q: shutdown_scopes[i].name); |
1585 | strcat(p: buf, q: "]" ); |
1586 | } else { |
1587 | strcat(p: buf, q: shutdown_scopes[i].name); |
1588 | } |
1589 | strcat(p: buf, q: " " ); |
1590 | } |
1591 | strcat(p: buf, q: "\n" ); |
1592 | |
1593 | return strlen(buf); |
1594 | } |
1595 | |
1596 | static ssize_t shutdown_scope_store(struct device *device, |
1597 | struct device_attribute *attr, |
1598 | const char *buf, size_t count) |
1599 | { |
1600 | int ret; |
1601 | struct zynqmp_pm_shutdown_scope *scope; |
1602 | |
1603 | scope = zynqmp_pm_is_shutdown_scope_valid(scope_string: buf); |
1604 | if (!scope) |
1605 | return -EINVAL; |
1606 | |
1607 | ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY, |
1608 | scope->subtype); |
1609 | if (ret) { |
1610 | pr_err("unable to set shutdown scope %s\n" , buf); |
1611 | return ret; |
1612 | } |
1613 | |
1614 | selected_scope = scope; |
1615 | |
1616 | return count; |
1617 | } |
1618 | |
1619 | static DEVICE_ATTR_RW(shutdown_scope); |
1620 | |
1621 | static ssize_t health_status_store(struct device *device, |
1622 | struct device_attribute *attr, |
1623 | const char *buf, size_t count) |
1624 | { |
1625 | int ret; |
1626 | unsigned int value; |
1627 | |
1628 | ret = kstrtouint(s: buf, base: 10, res: &value); |
1629 | if (ret) |
1630 | return ret; |
1631 | |
1632 | ret = zynqmp_pm_set_boot_health_status(value); |
1633 | if (ret) { |
1634 | dev_err(device, "unable to set healthy bit value to %u\n" , |
1635 | value); |
1636 | return ret; |
1637 | } |
1638 | |
1639 | return count; |
1640 | } |
1641 | |
1642 | static DEVICE_ATTR_WO(health_status); |
1643 | |
1644 | static ssize_t ggs_show(struct device *device, |
1645 | struct device_attribute *attr, |
1646 | char *buf, |
1647 | u32 reg) |
1648 | { |
1649 | int ret; |
1650 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1651 | |
1652 | ret = zynqmp_pm_read_ggs(reg, ret_payload); |
1653 | if (ret) |
1654 | return ret; |
1655 | |
1656 | return sprintf(buf, fmt: "0x%x\n" , ret_payload[1]); |
1657 | } |
1658 | |
1659 | static ssize_t ggs_store(struct device *device, |
1660 | struct device_attribute *attr, |
1661 | const char *buf, size_t count, |
1662 | u32 reg) |
1663 | { |
1664 | long value; |
1665 | int ret; |
1666 | |
1667 | if (reg >= GSS_NUM_REGS) |
1668 | return -EINVAL; |
1669 | |
1670 | ret = kstrtol(s: buf, base: 16, res: &value); |
1671 | if (ret) { |
1672 | count = -EFAULT; |
1673 | goto err; |
1674 | } |
1675 | |
1676 | ret = zynqmp_pm_write_ggs(reg, value); |
1677 | if (ret) |
1678 | count = -EFAULT; |
1679 | err: |
1680 | return count; |
1681 | } |
1682 | |
1683 | /* GGS register show functions */ |
1684 | #define GGS0_SHOW(N) \ |
1685 | ssize_t ggs##N##_show(struct device *device, \ |
1686 | struct device_attribute *attr, \ |
1687 | char *buf) \ |
1688 | { \ |
1689 | return ggs_show(device, attr, buf, N); \ |
1690 | } |
1691 | |
1692 | static GGS0_SHOW(0); |
1693 | static GGS0_SHOW(1); |
1694 | static GGS0_SHOW(2); |
1695 | static GGS0_SHOW(3); |
1696 | |
1697 | /* GGS register store function */ |
1698 | #define GGS0_STORE(N) \ |
1699 | ssize_t ggs##N##_store(struct device *device, \ |
1700 | struct device_attribute *attr, \ |
1701 | const char *buf, \ |
1702 | size_t count) \ |
1703 | { \ |
1704 | return ggs_store(device, attr, buf, count, N); \ |
1705 | } |
1706 | |
1707 | static GGS0_STORE(0); |
1708 | static GGS0_STORE(1); |
1709 | static GGS0_STORE(2); |
1710 | static GGS0_STORE(3); |
1711 | |
1712 | static ssize_t pggs_show(struct device *device, |
1713 | struct device_attribute *attr, |
1714 | char *buf, |
1715 | u32 reg) |
1716 | { |
1717 | int ret; |
1718 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1719 | |
1720 | ret = zynqmp_pm_read_pggs(reg, ret_payload); |
1721 | if (ret) |
1722 | return ret; |
1723 | |
1724 | return sprintf(buf, fmt: "0x%x\n" , ret_payload[1]); |
1725 | } |
1726 | |
1727 | static ssize_t pggs_store(struct device *device, |
1728 | struct device_attribute *attr, |
1729 | const char *buf, size_t count, |
1730 | u32 reg) |
1731 | { |
1732 | long value; |
1733 | int ret; |
1734 | |
1735 | if (reg >= GSS_NUM_REGS) |
1736 | return -EINVAL; |
1737 | |
1738 | ret = kstrtol(s: buf, base: 16, res: &value); |
1739 | if (ret) { |
1740 | count = -EFAULT; |
1741 | goto err; |
1742 | } |
1743 | |
1744 | ret = zynqmp_pm_write_pggs(reg, value); |
1745 | if (ret) |
1746 | count = -EFAULT; |
1747 | |
1748 | err: |
1749 | return count; |
1750 | } |
1751 | |
1752 | #define PGGS0_SHOW(N) \ |
1753 | ssize_t pggs##N##_show(struct device *device, \ |
1754 | struct device_attribute *attr, \ |
1755 | char *buf) \ |
1756 | { \ |
1757 | return pggs_show(device, attr, buf, N); \ |
1758 | } |
1759 | |
1760 | #define PGGS0_STORE(N) \ |
1761 | ssize_t pggs##N##_store(struct device *device, \ |
1762 | struct device_attribute *attr, \ |
1763 | const char *buf, \ |
1764 | size_t count) \ |
1765 | { \ |
1766 | return pggs_store(device, attr, buf, count, N); \ |
1767 | } |
1768 | |
1769 | /* PGGS register show functions */ |
1770 | static PGGS0_SHOW(0); |
1771 | static PGGS0_SHOW(1); |
1772 | static PGGS0_SHOW(2); |
1773 | static PGGS0_SHOW(3); |
1774 | |
1775 | /* PGGS register store functions */ |
1776 | static PGGS0_STORE(0); |
1777 | static PGGS0_STORE(1); |
1778 | static PGGS0_STORE(2); |
1779 | static PGGS0_STORE(3); |
1780 | |
1781 | /* GGS register attributes */ |
1782 | static DEVICE_ATTR_RW(ggs0); |
1783 | static DEVICE_ATTR_RW(ggs1); |
1784 | static DEVICE_ATTR_RW(ggs2); |
1785 | static DEVICE_ATTR_RW(ggs3); |
1786 | |
1787 | /* PGGS register attributes */ |
1788 | static DEVICE_ATTR_RW(pggs0); |
1789 | static DEVICE_ATTR_RW(pggs1); |
1790 | static DEVICE_ATTR_RW(pggs2); |
1791 | static DEVICE_ATTR_RW(pggs3); |
1792 | |
1793 | static ssize_t feature_config_id_show(struct device *device, |
1794 | struct device_attribute *attr, |
1795 | char *buf) |
1796 | { |
1797 | struct zynqmp_devinfo *devinfo = dev_get_drvdata(dev: device); |
1798 | |
1799 | return sysfs_emit(buf, fmt: "%d\n" , devinfo->feature_conf_id); |
1800 | } |
1801 | |
1802 | static ssize_t feature_config_id_store(struct device *device, |
1803 | struct device_attribute *attr, |
1804 | const char *buf, size_t count) |
1805 | { |
1806 | u32 config_id; |
1807 | int ret; |
1808 | struct zynqmp_devinfo *devinfo = dev_get_drvdata(dev: device); |
1809 | |
1810 | if (!buf) |
1811 | return -EINVAL; |
1812 | |
1813 | ret = kstrtou32(s: buf, base: 10, res: &config_id); |
1814 | if (ret) |
1815 | return ret; |
1816 | |
1817 | devinfo->feature_conf_id = config_id; |
1818 | |
1819 | return count; |
1820 | } |
1821 | |
1822 | static DEVICE_ATTR_RW(feature_config_id); |
1823 | |
1824 | static ssize_t feature_config_value_show(struct device *device, |
1825 | struct device_attribute *attr, |
1826 | char *buf) |
1827 | { |
1828 | int ret; |
1829 | u32 ret_payload[PAYLOAD_ARG_CNT]; |
1830 | struct zynqmp_devinfo *devinfo = dev_get_drvdata(dev: device); |
1831 | |
1832 | ret = zynqmp_pm_get_feature_config(devinfo->feature_conf_id, |
1833 | ret_payload); |
1834 | if (ret) |
1835 | return ret; |
1836 | |
1837 | return sysfs_emit(buf, fmt: "%d\n" , ret_payload[1]); |
1838 | } |
1839 | |
1840 | static ssize_t feature_config_value_store(struct device *device, |
1841 | struct device_attribute *attr, |
1842 | const char *buf, size_t count) |
1843 | { |
1844 | u32 value; |
1845 | int ret; |
1846 | struct zynqmp_devinfo *devinfo = dev_get_drvdata(dev: device); |
1847 | |
1848 | if (!buf) |
1849 | return -EINVAL; |
1850 | |
1851 | ret = kstrtou32(s: buf, base: 10, res: &value); |
1852 | if (ret) |
1853 | return ret; |
1854 | |
1855 | ret = zynqmp_pm_set_feature_config(devinfo->feature_conf_id, |
1856 | value); |
1857 | if (ret) |
1858 | return ret; |
1859 | |
1860 | return count; |
1861 | } |
1862 | |
1863 | static DEVICE_ATTR_RW(feature_config_value); |
1864 | |
1865 | static struct attribute *zynqmp_firmware_attrs[] = { |
1866 | &dev_attr_ggs0.attr, |
1867 | &dev_attr_ggs1.attr, |
1868 | &dev_attr_ggs2.attr, |
1869 | &dev_attr_ggs3.attr, |
1870 | &dev_attr_pggs0.attr, |
1871 | &dev_attr_pggs1.attr, |
1872 | &dev_attr_pggs2.attr, |
1873 | &dev_attr_pggs3.attr, |
1874 | &dev_attr_shutdown_scope.attr, |
1875 | &dev_attr_health_status.attr, |
1876 | &dev_attr_feature_config_id.attr, |
1877 | &dev_attr_feature_config_value.attr, |
1878 | NULL, |
1879 | }; |
1880 | |
1881 | ATTRIBUTE_GROUPS(zynqmp_firmware); |
1882 | |
1883 | static int zynqmp_firmware_probe(struct platform_device *pdev) |
1884 | { |
1885 | struct device *dev = &pdev->dev; |
1886 | struct zynqmp_devinfo *devinfo; |
1887 | int ret; |
1888 | |
1889 | ret = get_set_conduit_method(np: dev->of_node); |
1890 | if (ret) |
1891 | return ret; |
1892 | |
1893 | ret = do_feature_check_call(api_id: PM_FEATURE_CHECK); |
1894 | if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1)) |
1895 | feature_check_enabled = true; |
1896 | |
1897 | devinfo = devm_kzalloc(dev, size: sizeof(*devinfo), GFP_KERNEL); |
1898 | if (!devinfo) |
1899 | return -ENOMEM; |
1900 | |
1901 | devinfo->dev = dev; |
1902 | |
1903 | platform_set_drvdata(pdev, data: devinfo); |
1904 | |
1905 | /* Check PM API version number */ |
1906 | ret = zynqmp_pm_get_api_version(&pm_api_version); |
1907 | if (ret) |
1908 | return ret; |
1909 | |
1910 | if (pm_api_version < ZYNQMP_PM_VERSION) { |
1911 | panic(fmt: "%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n" , |
1912 | __func__, |
1913 | ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR, |
1914 | pm_api_version >> 16, pm_api_version & 0xFFFF); |
1915 | } |
1916 | |
1917 | pr_info("%s Platform Management API v%d.%d\n" , __func__, |
1918 | pm_api_version >> 16, pm_api_version & 0xFFFF); |
1919 | |
1920 | /* Get the Family code and sub family code of platform */ |
1921 | ret = zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code); |
1922 | if (ret < 0) |
1923 | return ret; |
1924 | |
1925 | /* Check trustzone version number */ |
1926 | ret = zynqmp_pm_get_trustzone_version(version: &pm_tz_version); |
1927 | if (ret) |
1928 | panic(fmt: "Legacy trustzone found without version support\n" ); |
1929 | |
1930 | if (pm_tz_version < ZYNQMP_TZ_VERSION) |
1931 | panic(fmt: "%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n" , |
1932 | __func__, |
1933 | ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR, |
1934 | pm_tz_version >> 16, pm_tz_version & 0xFFFF); |
1935 | |
1936 | pr_info("%s Trustzone version v%d.%d\n" , __func__, |
1937 | pm_tz_version >> 16, pm_tz_version & 0xFFFF); |
1938 | |
1939 | ret = mfd_add_devices(parent: &pdev->dev, PLATFORM_DEVID_NONE, cells: firmware_devs, |
1940 | ARRAY_SIZE(firmware_devs), NULL, irq_base: 0, NULL); |
1941 | if (ret) { |
1942 | dev_err(&pdev->dev, "failed to add MFD devices %d\n" , ret); |
1943 | return ret; |
1944 | } |
1945 | |
1946 | zynqmp_pm_api_debugfs_init(); |
1947 | |
1948 | if (pm_family_code == VERSAL_FAMILY_CODE) { |
1949 | em_dev = platform_device_register_data(parent: &pdev->dev, name: "xlnx_event_manager" , |
1950 | id: -1, NULL, size: 0); |
1951 | if (IS_ERR(ptr: em_dev)) |
1952 | dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: em_dev), fmt: "EM register fail with error\n" ); |
1953 | } |
1954 | |
1955 | return of_platform_populate(root: dev->of_node, NULL, NULL, parent: dev); |
1956 | } |
1957 | |
1958 | static void zynqmp_firmware_remove(struct platform_device *pdev) |
1959 | { |
1960 | struct pm_api_feature_data *feature_data; |
1961 | struct hlist_node *tmp; |
1962 | int i; |
1963 | |
1964 | mfd_remove_devices(parent: &pdev->dev); |
1965 | zynqmp_pm_api_debugfs_exit(); |
1966 | |
1967 | hash_for_each_safe(pm_api_features_map, i, tmp, feature_data, hentry) { |
1968 | hash_del(node: &feature_data->hentry); |
1969 | kfree(objp: feature_data); |
1970 | } |
1971 | |
1972 | platform_device_unregister(em_dev); |
1973 | } |
1974 | |
1975 | static const struct of_device_id zynqmp_firmware_of_match[] = { |
1976 | {.compatible = "xlnx,zynqmp-firmware" }, |
1977 | {.compatible = "xlnx,versal-firmware" }, |
1978 | {}, |
1979 | }; |
1980 | MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match); |
1981 | |
1982 | static struct platform_driver zynqmp_firmware_driver = { |
1983 | .driver = { |
1984 | .name = "zynqmp_firmware" , |
1985 | .of_match_table = zynqmp_firmware_of_match, |
1986 | .dev_groups = zynqmp_firmware_groups, |
1987 | }, |
1988 | .probe = zynqmp_firmware_probe, |
1989 | .remove_new = zynqmp_firmware_remove, |
1990 | }; |
1991 | module_platform_driver(zynqmp_firmware_driver); |
1992 | |