1 // SPDX-License-Identifier: GPL-2.0
3 * Xilinx Zynq MPSoC Firmware layer
5 * Copyright (C) 2014-2018 Xilinx, Inc.
7 * Michal Simek <michal.simek@xilinx.com>
8 * Davorin Mista <davorin.mista@aggios.com>
9 * Jolly Shah <jollys@xilinx.com>
10 * Rajan Vaja <rajanv@xilinx.com>
13 #include <linux/arm-smccc.h>
14 #include <linux/compiler.h>
15 #include <linux/device.h>
16 #include <linux/init.h>
17 #include <linux/module.h>
19 #include <linux/of_platform.h>
20 #include <linux/slab.h>
21 #include <linux/uaccess.h>
23 #include <linux/firmware/xlnx-zynqmp.h>
24 #include "zynqmp-debug.h"
27 * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes
28 * @ret_status: PMUFW return code
30 * Return: corresponding Linux error code
32 static int zynqmp_pm_ret_code(u32 ret_status
)
36 case XST_PM_DOUBLE_REQ
:
38 case XST_PM_NO_ACCESS
:
40 case XST_PM_ABORT_SUSPEND
:
44 case XST_PM_INVALID_NODE
:
50 static noinline
int do_fw_call_fail(u64 arg0
, u64 arg1
, u64 arg2
,
57 * PM function call wrapper
58 * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration
60 static int (*do_fw_call
)(u64
, u64
, u64
, u32
*ret_payload
) = do_fw_call_fail
;
63 * do_fw_call_smc() - Call system-level platform management layer (SMC)
64 * @arg0: Argument 0 to SMC call
65 * @arg1: Argument 1 to SMC call
66 * @arg2: Argument 2 to SMC call
67 * @ret_payload: Returned value array
69 * Invoke platform management function via SMC call (no hypervisor present).
71 * Return: Returns status, either success or error+reason
73 static noinline
int do_fw_call_smc(u64 arg0
, u64 arg1
, u64 arg2
,
76 struct arm_smccc_res res
;
78 arm_smccc_smc(arg0
, arg1
, arg2
, 0, 0, 0, 0, 0, &res
);
81 ret_payload
[0] = lower_32_bits(res
.a0
);
82 ret_payload
[1] = upper_32_bits(res
.a0
);
83 ret_payload
[2] = lower_32_bits(res
.a1
);
84 ret_payload
[3] = upper_32_bits(res
.a1
);
87 return zynqmp_pm_ret_code((enum pm_ret_status
)res
.a0
);
91 * do_fw_call_hvc() - Call system-level platform management layer (HVC)
92 * @arg0: Argument 0 to HVC call
93 * @arg1: Argument 1 to HVC call
94 * @arg2: Argument 2 to HVC call
95 * @ret_payload: Returned value array
97 * Invoke platform management function via HVC
98 * HVC-based for communication through hypervisor
99 * (no direct communication with ATF).
101 * Return: Returns status, either success or error+reason
103 static noinline
int do_fw_call_hvc(u64 arg0
, u64 arg1
, u64 arg2
,
106 struct arm_smccc_res res
;
108 arm_smccc_hvc(arg0
, arg1
, arg2
, 0, 0, 0, 0, 0, &res
);
111 ret_payload
[0] = lower_32_bits(res
.a0
);
112 ret_payload
[1] = upper_32_bits(res
.a0
);
113 ret_payload
[2] = lower_32_bits(res
.a1
);
114 ret_payload
[3] = upper_32_bits(res
.a1
);
117 return zynqmp_pm_ret_code((enum pm_ret_status
)res
.a0
);
121 * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer
122 * caller function depending on the configuration
123 * @pm_api_id: Requested PM-API call
124 * @arg0: Argument 0 to requested PM-API call
125 * @arg1: Argument 1 to requested PM-API call
126 * @arg2: Argument 2 to requested PM-API call
127 * @arg3: Argument 3 to requested PM-API call
128 * @ret_payload: Returned value array
130 * Invoke platform management function for SMC or HVC call, depending on
132 * Following SMC Calling Convention (SMCCC) for SMC64:
133 * Pm Function Identifier,
134 * PM_SIP_SVC + PM_API_ID =
135 * ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)
136 * ((SMC_64) << FUNCID_CC_SHIFT)
137 * ((SIP_START) << FUNCID_OEN_SHIFT)
138 * ((PM_API_ID) & FUNCID_NUM_MASK))
140 * PM_SIP_SVC - Registered ZynqMP SIP Service Call.
141 * PM_API_ID - Platform Management API ID.
143 * Return: Returns status, either success or error+reason
145 int zynqmp_pm_invoke_fn(u32 pm_api_id
, u32 arg0
, u32 arg1
,
146 u32 arg2
, u32 arg3
, u32
*ret_payload
)
149 * Added SIP service call Function Identifier
150 * Make sure to stay in x0 register
154 smc_arg
[0] = PM_SIP_SVC
| pm_api_id
;
155 smc_arg
[1] = ((u64
)arg1
<< 32) | arg0
;
156 smc_arg
[2] = ((u64
)arg3
<< 32) | arg2
;
158 return do_fw_call(smc_arg
[0], smc_arg
[1], smc_arg
[2], ret_payload
);
161 static u32 pm_api_version
;
162 static u32 pm_tz_version
;
165 * zynqmp_pm_get_api_version() - Get version number of PMU PM firmware
166 * @version: Returned version value
168 * Return: Returns status, either success or error+reason
170 static int zynqmp_pm_get_api_version(u32
*version
)
172 u32 ret_payload
[PAYLOAD_ARG_CNT
];
178 /* Check is PM API version already verified */
179 if (pm_api_version
> 0) {
180 *version
= pm_api_version
;
183 ret
= zynqmp_pm_invoke_fn(PM_GET_API_VERSION
, 0, 0, 0, 0, ret_payload
);
184 *version
= ret_payload
[1];
190 * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version
191 * @version: Returned version value
193 * Return: Returns status, either success or error+reason
195 static int zynqmp_pm_get_trustzone_version(u32
*version
)
197 u32 ret_payload
[PAYLOAD_ARG_CNT
];
203 /* Check is PM trustzone version already verified */
204 if (pm_tz_version
> 0) {
205 *version
= pm_tz_version
;
208 ret
= zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION
, 0, 0,
210 *version
= ret_payload
[1];
216 * get_set_conduit_method() - Choose SMC or HVC based communication
217 * @np: Pointer to the device_node structure
219 * Use SMC or HVC-based functions to communicate with EL2/EL3.
221 * Return: Returns 0 on success or error code
223 static int get_set_conduit_method(struct device_node
*np
)
227 if (of_property_read_string(np
, "method", &method
)) {
228 pr_warn("%s missing \"method\" property\n", __func__
);
232 if (!strcmp("hvc", method
)) {
233 do_fw_call
= do_fw_call_hvc
;
234 } else if (!strcmp("smc", method
)) {
235 do_fw_call
= do_fw_call_smc
;
237 pr_warn("%s Invalid \"method\" property: %s\n",
246 * zynqmp_pm_query_data() - Get query data from firmware
247 * @qdata: Variable to the zynqmp_pm_query_data structure
248 * @out: Returned output value
250 * Return: Returns status, either success or error+reason
252 static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata
, u32
*out
)
256 ret
= zynqmp_pm_invoke_fn(PM_QUERY_DATA
, qdata
.qid
, qdata
.arg1
,
257 qdata
.arg2
, qdata
.arg3
, out
);
260 * For clock name query, all bytes in SMC response are clock name
261 * characters and return code is always success. For invalid clocks,
262 * clock name bytes would be zeros.
264 return qdata
.qid
== PM_QID_CLOCK_GET_NAME
? 0 : ret
;
268 * zynqmp_pm_clock_enable() - Enable the clock for given id
269 * @clock_id: ID of the clock to be enabled
271 * This function is used by master to enable the clock
272 * including peripherals and PLL clocks.
274 * Return: Returns status, either success or error+reason
276 static int zynqmp_pm_clock_enable(u32 clock_id
)
278 return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE
, clock_id
, 0, 0, 0, NULL
);
282 * zynqmp_pm_clock_disable() - Disable the clock for given id
283 * @clock_id: ID of the clock to be disable
285 * This function is used by master to disable the clock
286 * including peripherals and PLL clocks.
288 * Return: Returns status, either success or error+reason
290 static int zynqmp_pm_clock_disable(u32 clock_id
)
292 return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE
, clock_id
, 0, 0, 0, NULL
);
296 * zynqmp_pm_clock_getstate() - Get the clock state for given id
297 * @clock_id: ID of the clock to be queried
298 * @state: 1/0 (Enabled/Disabled)
300 * This function is used by master to get the state of clock
301 * including peripherals and PLL clocks.
303 * Return: Returns status, either success or error+reason
305 static int zynqmp_pm_clock_getstate(u32 clock_id
, u32
*state
)
307 u32 ret_payload
[PAYLOAD_ARG_CNT
];
310 ret
= zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE
, clock_id
, 0,
312 *state
= ret_payload
[1];
318 * zynqmp_pm_clock_setdivider() - Set the clock divider for given id
319 * @clock_id: ID of the clock
320 * @divider: divider value
322 * This function is used by master to set divider for any clock
323 * to achieve desired rate.
325 * Return: Returns status, either success or error+reason
327 static int zynqmp_pm_clock_setdivider(u32 clock_id
, u32 divider
)
329 return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER
, clock_id
, divider
,
334 * zynqmp_pm_clock_getdivider() - Get the clock divider for given id
335 * @clock_id: ID of the clock
336 * @divider: divider value
338 * This function is used by master to get divider values
341 * Return: Returns status, either success or error+reason
343 static int zynqmp_pm_clock_getdivider(u32 clock_id
, u32
*divider
)
345 u32 ret_payload
[PAYLOAD_ARG_CNT
];
348 ret
= zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER
, clock_id
, 0,
350 *divider
= ret_payload
[1];
356 * zynqmp_pm_clock_setrate() - Set the clock rate for given id
357 * @clock_id: ID of the clock
358 * @rate: rate value in hz
360 * This function is used by master to set rate for any clock.
362 * Return: Returns status, either success or error+reason
364 static int zynqmp_pm_clock_setrate(u32 clock_id
, u64 rate
)
366 return zynqmp_pm_invoke_fn(PM_CLOCK_SETRATE
, clock_id
,
373 * zynqmp_pm_clock_getrate() - Get the clock rate for given id
374 * @clock_id: ID of the clock
375 * @rate: rate value in hz
377 * This function is used by master to get rate
380 * Return: Returns status, either success or error+reason
382 static int zynqmp_pm_clock_getrate(u32 clock_id
, u64
*rate
)
384 u32 ret_payload
[PAYLOAD_ARG_CNT
];
387 ret
= zynqmp_pm_invoke_fn(PM_CLOCK_GETRATE
, clock_id
, 0,
389 *rate
= ((u64
)ret_payload
[2] << 32) | ret_payload
[1];
395 * zynqmp_pm_clock_setparent() - Set the clock parent for given id
396 * @clock_id: ID of the clock
397 * @parent_id: parent id
399 * This function is used by master to set parent for any clock.
401 * Return: Returns status, either success or error+reason
403 static int zynqmp_pm_clock_setparent(u32 clock_id
, u32 parent_id
)
405 return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT
, clock_id
,
406 parent_id
, 0, 0, NULL
);
410 * zynqmp_pm_clock_getparent() - Get the clock parent for given id
411 * @clock_id: ID of the clock
412 * @parent_id: parent id
414 * This function is used by master to get parent index
417 * Return: Returns status, either success or error+reason
419 static int zynqmp_pm_clock_getparent(u32 clock_id
, u32
*parent_id
)
421 u32 ret_payload
[PAYLOAD_ARG_CNT
];
424 ret
= zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT
, clock_id
, 0,
426 *parent_id
= ret_payload
[1];
432 * zynqmp_is_valid_ioctl() - Check whether IOCTL ID is valid or not
433 * @ioctl_id: IOCTL ID
435 * Return: 1 if IOCTL is valid else 0
437 static inline int zynqmp_is_valid_ioctl(u32 ioctl_id
)
440 case IOCTL_SET_PLL_FRAC_MODE
:
441 case IOCTL_GET_PLL_FRAC_MODE
:
442 case IOCTL_SET_PLL_FRAC_DATA
:
443 case IOCTL_GET_PLL_FRAC_DATA
:
451 * zynqmp_pm_ioctl() - PM IOCTL API for device control and configs
452 * @node_id: Node ID of the device
453 * @ioctl_id: ID of the requested IOCTL
454 * @arg1: Argument 1 to requested IOCTL call
455 * @arg2: Argument 2 to requested IOCTL call
456 * @out: Returned output value
458 * This function calls IOCTL to firmware for device control and configuration.
460 * Return: Returns status, either success or error+reason
462 static int zynqmp_pm_ioctl(u32 node_id
, u32 ioctl_id
, u32 arg1
, u32 arg2
,
465 if (!zynqmp_is_valid_ioctl(ioctl_id
))
468 return zynqmp_pm_invoke_fn(PM_IOCTL
, node_id
, ioctl_id
,
472 static const struct zynqmp_eemi_ops eemi_ops
= {
473 .get_api_version
= zynqmp_pm_get_api_version
,
474 .query_data
= zynqmp_pm_query_data
,
475 .clock_enable
= zynqmp_pm_clock_enable
,
476 .clock_disable
= zynqmp_pm_clock_disable
,
477 .clock_getstate
= zynqmp_pm_clock_getstate
,
478 .clock_setdivider
= zynqmp_pm_clock_setdivider
,
479 .clock_getdivider
= zynqmp_pm_clock_getdivider
,
480 .clock_setrate
= zynqmp_pm_clock_setrate
,
481 .clock_getrate
= zynqmp_pm_clock_getrate
,
482 .clock_setparent
= zynqmp_pm_clock_setparent
,
483 .clock_getparent
= zynqmp_pm_clock_getparent
,
484 .ioctl
= zynqmp_pm_ioctl
,
488 * zynqmp_pm_get_eemi_ops - Get eemi ops functions
490 * Return: Pointer of eemi_ops structure
492 const struct zynqmp_eemi_ops
*zynqmp_pm_get_eemi_ops(void)
496 EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops
);
498 static int zynqmp_firmware_probe(struct platform_device
*pdev
)
500 struct device
*dev
= &pdev
->dev
;
501 struct device_node
*np
;
504 np
= of_find_compatible_node(NULL
, NULL
, "xlnx,zynqmp");
509 ret
= get_set_conduit_method(dev
->of_node
);
513 /* Check PM API version number */
514 zynqmp_pm_get_api_version(&pm_api_version
);
515 if (pm_api_version
< ZYNQMP_PM_VERSION
) {
516 panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",
518 ZYNQMP_PM_VERSION_MAJOR
, ZYNQMP_PM_VERSION_MINOR
,
519 pm_api_version
>> 16, pm_api_version
& 0xFFFF);
522 pr_info("%s Platform Management API v%d.%d\n", __func__
,
523 pm_api_version
>> 16, pm_api_version
& 0xFFFF);
525 /* Check trustzone version number */
526 ret
= zynqmp_pm_get_trustzone_version(&pm_tz_version
);
528 panic("Legacy trustzone found without version support\n");
530 if (pm_tz_version
< ZYNQMP_TZ_VERSION
)
531 panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",
533 ZYNQMP_TZ_VERSION_MAJOR
, ZYNQMP_TZ_VERSION_MINOR
,
534 pm_tz_version
>> 16, pm_tz_version
& 0xFFFF);
536 pr_info("%s Trustzone version v%d.%d\n", __func__
,
537 pm_tz_version
>> 16, pm_tz_version
& 0xFFFF);
539 zynqmp_pm_api_debugfs_init();
541 return of_platform_populate(dev
->of_node
, NULL
, NULL
, dev
);
544 static int zynqmp_firmware_remove(struct platform_device
*pdev
)
546 zynqmp_pm_api_debugfs_exit();
551 static const struct of_device_id zynqmp_firmware_of_match
[] = {
552 {.compatible
= "xlnx,zynqmp-firmware"},
555 MODULE_DEVICE_TABLE(of
, zynqmp_firmware_of_match
);
557 static struct platform_driver zynqmp_firmware_driver
= {
559 .name
= "zynqmp_firmware",
560 .of_match_table
= zynqmp_firmware_of_match
,
562 .probe
= zynqmp_firmware_probe
,
563 .remove
= zynqmp_firmware_remove
,
565 module_platform_driver(zynqmp_firmware_driver
);