Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'zynqmp-soc-for-v4.20-v2' of https://github.com/Xilinx/linux-xlnx into next/drivers

arm64: zynqmp: SoC changes for v4.20

- Adding firmware API for SoC with debugfs interface
Firmware driver communicates to Platform Management Unit (PMU) by using
SMC instructions routed to Arm Trusted Firmware (ATF). Initial version
adds support for base firmware driver with query and clock APIs.

EEMI spec is available here:
https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf

* tag 'zynqmp-soc-for-v4.20-v2' of https://github.com/Xilinx/linux-xlnx:
firmware: xilinx: Add debugfs for query data API
firmware: xilinx: Add debugfs interface
firmware: xilinx: Add clock APIs
firmware: xilinx: Add query data API
firmware: xilinx: Add Zynqmp firmware driver
dt-bindings: firmware: Add bindings for ZynqMP firmware

Signed-off-by: Arnd Bergmann <arnd@arndb.de>

+970
+29
Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
··· 1 + ----------------------------------------------------------------- 2 + Device Tree Bindings for the Xilinx Zynq MPSoC Firmware Interface 3 + ----------------------------------------------------------------- 4 + 5 + The zynqmp-firmware node describes the interface to platform firmware. 6 + ZynqMP has an interface to communicate with secure firmware. Firmware 7 + driver provides an interface to firmware APIs. Interface APIs can be 8 + used by any driver to communicate to PMUFW(Platform Management Unit). 9 + These requests include clock management, pin control, device control, 10 + power management service, FPGA service and other platform management 11 + services. 12 + 13 + Required properties: 14 + - compatible: Must contain: "xlnx,zynqmp-firmware" 15 + - method: The method of calling the PM-API firmware layer. 16 + Permitted values are: 17 + - "smc" : SMC #0, following the SMCCC 18 + - "hvc" : HVC #0, following the SMCCC 19 + 20 + ------- 21 + Example 22 + ------- 23 + 24 + firmware { 25 + zynqmp_firmware: zynqmp-firmware { 26 + compatible = "xlnx,zynqmp-firmware"; 27 + method = "smc"; 28 + }; 29 + };
+1
arch/arm64/Kconfig.platforms
··· 301 301 302 302 config ARCH_ZYNQMP 303 303 bool "Xilinx ZynqMP Family" 304 + select ZYNQMP_FIRMWARE 304 305 help 305 306 This enables support for Xilinx ZynqMP Family 306 307
+1
drivers/firmware/Kconfig
··· 291 291 source "drivers/firmware/efi/Kconfig" 292 292 source "drivers/firmware/meson/Kconfig" 293 293 source "drivers/firmware/tegra/Kconfig" 294 + source "drivers/firmware/xilinx/Kconfig" 294 295 295 296 endmenu
+1
drivers/firmware/Makefile
··· 32 32 obj-$(CONFIG_EFI) += efi/ 33 33 obj-$(CONFIG_UEFI_CPER) += efi/ 34 34 obj-y += tegra/ 35 + obj-y += xilinx/
+23
drivers/firmware/xilinx/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # Kconfig for Xilinx firmwares 3 + 4 + menu "Zynq MPSoC Firmware Drivers" 5 + depends on ARCH_ZYNQMP 6 + 7 + config ZYNQMP_FIRMWARE 8 + bool "Enable Xilinx Zynq MPSoC firmware interface" 9 + help 10 + Firmware interface driver is used by different 11 + drivers to communicate with the firmware for 12 + various platform management services. 13 + Say yes to enable ZynqMP firmware interface driver. 14 + If in doubt, say N. 15 + 16 + config ZYNQMP_FIRMWARE_DEBUG 17 + bool "Enable Xilinx Zynq MPSoC firmware debug APIs" 18 + depends on ZYNQMP_FIRMWARE && DEBUG_FS 19 + help 20 + Say yes to enable ZynqMP firmware interface debug APIs. 21 + If in doubt, say N. 22 + 23 + endmenu
+5
drivers/firmware/xilinx/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # Makefile for Xilinx firmwares 3 + 4 + obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o 5 + obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o
+250
drivers/firmware/xilinx/zynqmp-debug.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Xilinx Zynq MPSoC Firmware layer for debugfs APIs 4 + * 5 + * Copyright (C) 2014-2018 Xilinx, Inc. 6 + * 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> 11 + */ 12 + 13 + #include <linux/compiler.h> 14 + #include <linux/module.h> 15 + #include <linux/slab.h> 16 + #include <linux/debugfs.h> 17 + #include <linux/uaccess.h> 18 + 19 + #include <linux/firmware/xlnx-zynqmp.h> 20 + #include "zynqmp-debug.h" 21 + 22 + #define PM_API_NAME_LEN 50 23 + 24 + struct pm_api_info { 25 + u32 api_id; 26 + char api_name[PM_API_NAME_LEN]; 27 + char api_name_len; 28 + }; 29 + 30 + static char debugfs_buf[PAGE_SIZE]; 31 + 32 + #define PM_API(id) {id, #id, strlen(#id)} 33 + static struct pm_api_info pm_api_list[] = { 34 + PM_API(PM_GET_API_VERSION), 35 + PM_API(PM_QUERY_DATA), 36 + }; 37 + 38 + struct dentry *firmware_debugfs_root; 39 + 40 + /** 41 + * zynqmp_pm_argument_value() - Extract argument value from a PM-API request 42 + * @arg: Entered PM-API argument in string format 43 + * 44 + * Return: Argument value in unsigned integer format on success 45 + * 0 otherwise 46 + */ 47 + static u64 zynqmp_pm_argument_value(char *arg) 48 + { 49 + u64 value; 50 + 51 + if (!arg) 52 + return 0; 53 + 54 + if (!kstrtou64(arg, 0, &value)) 55 + return value; 56 + 57 + return 0; 58 + } 59 + 60 + /** 61 + * get_pm_api_id() - Extract API-ID from a PM-API request 62 + * @pm_api_req: Entered PM-API argument in string format 63 + * @pm_id: API-ID 64 + * 65 + * Return: 0 on success else error code 66 + */ 67 + static int get_pm_api_id(char *pm_api_req, u32 *pm_id) 68 + { 69 + int i; 70 + 71 + for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) { 72 + if (!strncasecmp(pm_api_req, pm_api_list[i].api_name, 73 + pm_api_list[i].api_name_len)) { 74 + *pm_id = pm_api_list[i].api_id; 75 + break; 76 + } 77 + } 78 + 79 + /* If no name was entered look for PM-API ID instead */ 80 + if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id)) 81 + return -EINVAL; 82 + 83 + return 0; 84 + } 85 + 86 + static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret) 87 + { 88 + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); 89 + u32 pm_api_version; 90 + int ret; 91 + struct zynqmp_pm_query_data qdata = {0}; 92 + 93 + if (!eemi_ops) 94 + return -ENXIO; 95 + 96 + switch (pm_id) { 97 + case PM_GET_API_VERSION: 98 + ret = eemi_ops->get_api_version(&pm_api_version); 99 + sprintf(debugfs_buf, "PM-API Version = %d.%d\n", 100 + pm_api_version >> 16, pm_api_version & 0xffff); 101 + break; 102 + case PM_QUERY_DATA: 103 + qdata.qid = pm_api_arg[0]; 104 + qdata.arg1 = pm_api_arg[1]; 105 + qdata.arg2 = pm_api_arg[2]; 106 + qdata.arg3 = pm_api_arg[3]; 107 + 108 + ret = eemi_ops->query_data(qdata, pm_api_ret); 109 + if (ret) 110 + break; 111 + 112 + switch (qdata.qid) { 113 + case PM_QID_CLOCK_GET_NAME: 114 + sprintf(debugfs_buf, "Clock name = %s\n", 115 + (char *)pm_api_ret); 116 + break; 117 + case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS: 118 + sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n", 119 + pm_api_ret[1], pm_api_ret[2]); 120 + break; 121 + default: 122 + sprintf(debugfs_buf, 123 + "data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n", 124 + pm_api_ret[0], pm_api_ret[1], 125 + pm_api_ret[2], pm_api_ret[3]); 126 + } 127 + break; 128 + default: 129 + sprintf(debugfs_buf, "Unsupported PM-API request\n"); 130 + ret = -EINVAL; 131 + } 132 + 133 + return ret; 134 + } 135 + 136 + /** 137 + * zynqmp_pm_debugfs_api_write() - debugfs write function 138 + * @file: User file 139 + * @ptr: User entered PM-API string 140 + * @len: Length of the userspace buffer 141 + * @off: Offset within the file 142 + * 143 + * Used for triggering pm api functions by writing 144 + * echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or 145 + * echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power 146 + * 147 + * Return: Number of bytes copied if PM-API request succeeds, 148 + * the corresponding error code otherwise 149 + */ 150 + static ssize_t zynqmp_pm_debugfs_api_write(struct file *file, 151 + const char __user *ptr, size_t len, 152 + loff_t *off) 153 + { 154 + char *kern_buff, *tmp_buff; 155 + char *pm_api_req; 156 + u32 pm_id = 0; 157 + u64 pm_api_arg[4] = {0, 0, 0, 0}; 158 + /* Return values from PM APIs calls */ 159 + u32 pm_api_ret[4] = {0, 0, 0, 0}; 160 + 161 + int ret; 162 + int i = 0; 163 + 164 + strcpy(debugfs_buf, ""); 165 + 166 + if (*off != 0 || len == 0) 167 + return -EINVAL; 168 + 169 + kern_buff = kzalloc(len, GFP_KERNEL); 170 + if (!kern_buff) 171 + return -ENOMEM; 172 + 173 + tmp_buff = kern_buff; 174 + 175 + ret = strncpy_from_user(kern_buff, ptr, len); 176 + if (ret < 0) { 177 + ret = -EFAULT; 178 + goto err; 179 + } 180 + 181 + /* Read the API name from a user request */ 182 + pm_api_req = strsep(&kern_buff, " "); 183 + 184 + ret = get_pm_api_id(pm_api_req, &pm_id); 185 + if (ret < 0) 186 + goto err; 187 + 188 + /* Read node_id and arguments from the PM-API request */ 189 + pm_api_req = strsep(&kern_buff, " "); 190 + while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) { 191 + pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req); 192 + pm_api_req = strsep(&kern_buff, " "); 193 + } 194 + 195 + ret = process_api_request(pm_id, pm_api_arg, pm_api_ret); 196 + 197 + err: 198 + kfree(tmp_buff); 199 + if (ret) 200 + return ret; 201 + 202 + return len; 203 + } 204 + 205 + /** 206 + * zynqmp_pm_debugfs_api_read() - debugfs read function 207 + * @file: User file 208 + * @ptr: Requested pm_api_version string 209 + * @len: Length of the userspace buffer 210 + * @off: Offset within the file 211 + * 212 + * Return: Length of the version string on success 213 + * else error code 214 + */ 215 + static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr, 216 + size_t len, loff_t *off) 217 + { 218 + return simple_read_from_buffer(ptr, len, off, debugfs_buf, 219 + strlen(debugfs_buf)); 220 + } 221 + 222 + /* Setup debugfs fops */ 223 + static const struct file_operations fops_zynqmp_pm_dbgfs = { 224 + .owner = THIS_MODULE, 225 + .write = zynqmp_pm_debugfs_api_write, 226 + .read = zynqmp_pm_debugfs_api_read, 227 + }; 228 + 229 + /** 230 + * zynqmp_pm_api_debugfs_init - Initialize debugfs interface 231 + * 232 + * Return: None 233 + */ 234 + void zynqmp_pm_api_debugfs_init(void) 235 + { 236 + /* Initialize debugfs interface */ 237 + firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL); 238 + debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL, 239 + &fops_zynqmp_pm_dbgfs); 240 + } 241 + 242 + /** 243 + * zynqmp_pm_api_debugfs_exit - Remove debugfs interface 244 + * 245 + * Return: None 246 + */ 247 + void zynqmp_pm_api_debugfs_exit(void) 248 + { 249 + debugfs_remove_recursive(firmware_debugfs_root); 250 + }
+24
drivers/firmware/xilinx/zynqmp-debug.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Xilinx Zynq MPSoC Firmware layer 4 + * 5 + * Copyright (C) 2014-2018 Xilinx 6 + * 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> 11 + */ 12 + 13 + #ifndef __FIRMWARE_ZYNQMP_DEBUG_H__ 14 + #define __FIRMWARE_ZYNQMP_DEBUG_H__ 15 + 16 + #if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE_DEBUG) 17 + void zynqmp_pm_api_debugfs_init(void); 18 + void zynqmp_pm_api_debugfs_exit(void); 19 + #else 20 + static inline void zynqmp_pm_api_debugfs_init(void) { } 21 + static inline void zynqmp_pm_api_debugfs_exit(void) { } 22 + #endif 23 + 24 + #endif /* __FIRMWARE_ZYNQMP_DEBUG_H__ */
+523
drivers/firmware/xilinx/zynqmp.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Xilinx Zynq MPSoC Firmware layer 4 + * 5 + * Copyright (C) 2014-2018 Xilinx, Inc. 6 + * 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> 11 + */ 12 + 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> 18 + #include <linux/of.h> 19 + #include <linux/of_platform.h> 20 + #include <linux/slab.h> 21 + #include <linux/uaccess.h> 22 + 23 + #include <linux/firmware/xlnx-zynqmp.h> 24 + #include "zynqmp-debug.h" 25 + 26 + /** 27 + * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes 28 + * @ret_status: PMUFW return code 29 + * 30 + * Return: corresponding Linux error code 31 + */ 32 + static int zynqmp_pm_ret_code(u32 ret_status) 33 + { 34 + switch (ret_status) { 35 + case XST_PM_SUCCESS: 36 + case XST_PM_DOUBLE_REQ: 37 + return 0; 38 + case XST_PM_NO_ACCESS: 39 + return -EACCES; 40 + case XST_PM_ABORT_SUSPEND: 41 + return -ECANCELED; 42 + case XST_PM_INTERNAL: 43 + case XST_PM_CONFLICT: 44 + case XST_PM_INVALID_NODE: 45 + default: 46 + return -EINVAL; 47 + } 48 + } 49 + 50 + static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2, 51 + u32 *ret_payload) 52 + { 53 + return -ENODEV; 54 + } 55 + 56 + /* 57 + * PM function call wrapper 58 + * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration 59 + */ 60 + static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail; 61 + 62 + /** 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 68 + * 69 + * Invoke platform management function via SMC call (no hypervisor present). 70 + * 71 + * Return: Returns status, either success or error+reason 72 + */ 73 + static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2, 74 + u32 *ret_payload) 75 + { 76 + struct arm_smccc_res res; 77 + 78 + arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res); 79 + 80 + if (ret_payload) { 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); 85 + } 86 + 87 + return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); 88 + } 89 + 90 + /** 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 96 + * 97 + * Invoke platform management function via HVC 98 + * HVC-based for communication through hypervisor 99 + * (no direct communication with ATF). 100 + * 101 + * Return: Returns status, either success or error+reason 102 + */ 103 + static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2, 104 + u32 *ret_payload) 105 + { 106 + struct arm_smccc_res res; 107 + 108 + arm_smccc_hvc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res); 109 + 110 + if (ret_payload) { 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); 115 + } 116 + 117 + return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); 118 + } 119 + 120 + /** 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 129 + * 130 + * Invoke platform management function for SMC or HVC call, depending on 131 + * configuration. 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)) 139 + * 140 + * PM_SIP_SVC - Registered ZynqMP SIP Service Call. 141 + * PM_API_ID - Platform Management API ID. 142 + * 143 + * Return: Returns status, either success or error+reason 144 + */ 145 + int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1, 146 + u32 arg2, u32 arg3, u32 *ret_payload) 147 + { 148 + /* 149 + * Added SIP service call Function Identifier 150 + * Make sure to stay in x0 register 151 + */ 152 + u64 smc_arg[4]; 153 + 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; 157 + 158 + return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload); 159 + } 160 + 161 + static u32 pm_api_version; 162 + static u32 pm_tz_version; 163 + 164 + /** 165 + * zynqmp_pm_get_api_version() - Get version number of PMU PM firmware 166 + * @version: Returned version value 167 + * 168 + * Return: Returns status, either success or error+reason 169 + */ 170 + static int zynqmp_pm_get_api_version(u32 *version) 171 + { 172 + u32 ret_payload[PAYLOAD_ARG_CNT]; 173 + int ret; 174 + 175 + if (!version) 176 + return -EINVAL; 177 + 178 + /* Check is PM API version already verified */ 179 + if (pm_api_version > 0) { 180 + *version = pm_api_version; 181 + return 0; 182 + } 183 + ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, 0, 0, 0, 0, ret_payload); 184 + *version = ret_payload[1]; 185 + 186 + return ret; 187 + } 188 + 189 + /** 190 + * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version 191 + * @version: Returned version value 192 + * 193 + * Return: Returns status, either success or error+reason 194 + */ 195 + static int zynqmp_pm_get_trustzone_version(u32 *version) 196 + { 197 + u32 ret_payload[PAYLOAD_ARG_CNT]; 198 + int ret; 199 + 200 + if (!version) 201 + return -EINVAL; 202 + 203 + /* Check is PM trustzone version already verified */ 204 + if (pm_tz_version > 0) { 205 + *version = pm_tz_version; 206 + return 0; 207 + } 208 + ret = zynqmp_pm_invoke_fn(PM_GET_TRUSTZONE_VERSION, 0, 0, 209 + 0, 0, ret_payload); 210 + *version = ret_payload[1]; 211 + 212 + return ret; 213 + } 214 + 215 + /** 216 + * get_set_conduit_method() - Choose SMC or HVC based communication 217 + * @np: Pointer to the device_node structure 218 + * 219 + * Use SMC or HVC-based functions to communicate with EL2/EL3. 220 + * 221 + * Return: Returns 0 on success or error code 222 + */ 223 + static int get_set_conduit_method(struct device_node *np) 224 + { 225 + const char *method; 226 + 227 + if (of_property_read_string(np, "method", &method)) { 228 + pr_warn("%s missing \"method\" property\n", __func__); 229 + return -ENXIO; 230 + } 231 + 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; 236 + } else { 237 + pr_warn("%s Invalid \"method\" property: %s\n", 238 + __func__, method); 239 + return -EINVAL; 240 + } 241 + 242 + return 0; 243 + } 244 + 245 + /** 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 249 + * 250 + * Return: Returns status, either success or error+reason 251 + */ 252 + static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out) 253 + { 254 + int ret; 255 + 256 + ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1, 257 + qdata.arg2, qdata.arg3, out); 258 + 259 + /* 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. 263 + */ 264 + return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret; 265 + } 266 + 267 + /** 268 + * zynqmp_pm_clock_enable() - Enable the clock for given id 269 + * @clock_id: ID of the clock to be enabled 270 + * 271 + * This function is used by master to enable the clock 272 + * including peripherals and PLL clocks. 273 + * 274 + * Return: Returns status, either success or error+reason 275 + */ 276 + static int zynqmp_pm_clock_enable(u32 clock_id) 277 + { 278 + return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, clock_id, 0, 0, 0, NULL); 279 + } 280 + 281 + /** 282 + * zynqmp_pm_clock_disable() - Disable the clock for given id 283 + * @clock_id: ID of the clock to be disable 284 + * 285 + * This function is used by master to disable the clock 286 + * including peripherals and PLL clocks. 287 + * 288 + * Return: Returns status, either success or error+reason 289 + */ 290 + static int zynqmp_pm_clock_disable(u32 clock_id) 291 + { 292 + return zynqmp_pm_invoke_fn(PM_CLOCK_DISABLE, clock_id, 0, 0, 0, NULL); 293 + } 294 + 295 + /** 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) 299 + * 300 + * This function is used by master to get the state of clock 301 + * including peripherals and PLL clocks. 302 + * 303 + * Return: Returns status, either success or error+reason 304 + */ 305 + static int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state) 306 + { 307 + u32 ret_payload[PAYLOAD_ARG_CNT]; 308 + int ret; 309 + 310 + ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, clock_id, 0, 311 + 0, 0, ret_payload); 312 + *state = ret_payload[1]; 313 + 314 + return ret; 315 + } 316 + 317 + /** 318 + * zynqmp_pm_clock_setdivider() - Set the clock divider for given id 319 + * @clock_id: ID of the clock 320 + * @divider: divider value 321 + * 322 + * This function is used by master to set divider for any clock 323 + * to achieve desired rate. 324 + * 325 + * Return: Returns status, either success or error+reason 326 + */ 327 + static int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider) 328 + { 329 + return zynqmp_pm_invoke_fn(PM_CLOCK_SETDIVIDER, clock_id, divider, 330 + 0, 0, NULL); 331 + } 332 + 333 + /** 334 + * zynqmp_pm_clock_getdivider() - Get the clock divider for given id 335 + * @clock_id: ID of the clock 336 + * @divider: divider value 337 + * 338 + * This function is used by master to get divider values 339 + * for any clock. 340 + * 341 + * Return: Returns status, either success or error+reason 342 + */ 343 + static int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider) 344 + { 345 + u32 ret_payload[PAYLOAD_ARG_CNT]; 346 + int ret; 347 + 348 + ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETDIVIDER, clock_id, 0, 349 + 0, 0, ret_payload); 350 + *divider = ret_payload[1]; 351 + 352 + return ret; 353 + } 354 + 355 + /** 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 359 + * 360 + * This function is used by master to set rate for any clock. 361 + * 362 + * Return: Returns status, either success or error+reason 363 + */ 364 + static int zynqmp_pm_clock_setrate(u32 clock_id, u64 rate) 365 + { 366 + return zynqmp_pm_invoke_fn(PM_CLOCK_SETRATE, clock_id, 367 + lower_32_bits(rate), 368 + upper_32_bits(rate), 369 + 0, NULL); 370 + } 371 + 372 + /** 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 376 + * 377 + * This function is used by master to get rate 378 + * for any clock. 379 + * 380 + * Return: Returns status, either success or error+reason 381 + */ 382 + static int zynqmp_pm_clock_getrate(u32 clock_id, u64 *rate) 383 + { 384 + u32 ret_payload[PAYLOAD_ARG_CNT]; 385 + int ret; 386 + 387 + ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETRATE, clock_id, 0, 388 + 0, 0, ret_payload); 389 + *rate = ((u64)ret_payload[2] << 32) | ret_payload[1]; 390 + 391 + return ret; 392 + } 393 + 394 + /** 395 + * zynqmp_pm_clock_setparent() - Set the clock parent for given id 396 + * @clock_id: ID of the clock 397 + * @parent_id: parent id 398 + * 399 + * This function is used by master to set parent for any clock. 400 + * 401 + * Return: Returns status, either success or error+reason 402 + */ 403 + static int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id) 404 + { 405 + return zynqmp_pm_invoke_fn(PM_CLOCK_SETPARENT, clock_id, 406 + parent_id, 0, 0, NULL); 407 + } 408 + 409 + /** 410 + * zynqmp_pm_clock_getparent() - Get the clock parent for given id 411 + * @clock_id: ID of the clock 412 + * @parent_id: parent id 413 + * 414 + * This function is used by master to get parent index 415 + * for any clock. 416 + * 417 + * Return: Returns status, either success or error+reason 418 + */ 419 + static int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id) 420 + { 421 + u32 ret_payload[PAYLOAD_ARG_CNT]; 422 + int ret; 423 + 424 + ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETPARENT, clock_id, 0, 425 + 0, 0, ret_payload); 426 + *parent_id = ret_payload[1]; 427 + 428 + return ret; 429 + } 430 + 431 + static const struct zynqmp_eemi_ops eemi_ops = { 432 + .get_api_version = zynqmp_pm_get_api_version, 433 + .query_data = zynqmp_pm_query_data, 434 + .clock_enable = zynqmp_pm_clock_enable, 435 + .clock_disable = zynqmp_pm_clock_disable, 436 + .clock_getstate = zynqmp_pm_clock_getstate, 437 + .clock_setdivider = zynqmp_pm_clock_setdivider, 438 + .clock_getdivider = zynqmp_pm_clock_getdivider, 439 + .clock_setrate = zynqmp_pm_clock_setrate, 440 + .clock_getrate = zynqmp_pm_clock_getrate, 441 + .clock_setparent = zynqmp_pm_clock_setparent, 442 + .clock_getparent = zynqmp_pm_clock_getparent, 443 + }; 444 + 445 + /** 446 + * zynqmp_pm_get_eemi_ops - Get eemi ops functions 447 + * 448 + * Return: Pointer of eemi_ops structure 449 + */ 450 + const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) 451 + { 452 + return &eemi_ops; 453 + } 454 + EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops); 455 + 456 + static int zynqmp_firmware_probe(struct platform_device *pdev) 457 + { 458 + struct device *dev = &pdev->dev; 459 + struct device_node *np; 460 + int ret; 461 + 462 + np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); 463 + if (!np) 464 + return 0; 465 + of_node_put(np); 466 + 467 + ret = get_set_conduit_method(dev->of_node); 468 + if (ret) 469 + return ret; 470 + 471 + /* Check PM API version number */ 472 + zynqmp_pm_get_api_version(&pm_api_version); 473 + if (pm_api_version < ZYNQMP_PM_VERSION) { 474 + panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n", 475 + __func__, 476 + ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR, 477 + pm_api_version >> 16, pm_api_version & 0xFFFF); 478 + } 479 + 480 + pr_info("%s Platform Management API v%d.%d\n", __func__, 481 + pm_api_version >> 16, pm_api_version & 0xFFFF); 482 + 483 + /* Check trustzone version number */ 484 + ret = zynqmp_pm_get_trustzone_version(&pm_tz_version); 485 + if (ret) 486 + panic("Legacy trustzone found without version support\n"); 487 + 488 + if (pm_tz_version < ZYNQMP_TZ_VERSION) 489 + panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n", 490 + __func__, 491 + ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR, 492 + pm_tz_version >> 16, pm_tz_version & 0xFFFF); 493 + 494 + pr_info("%s Trustzone version v%d.%d\n", __func__, 495 + pm_tz_version >> 16, pm_tz_version & 0xFFFF); 496 + 497 + zynqmp_pm_api_debugfs_init(); 498 + 499 + return of_platform_populate(dev->of_node, NULL, NULL, dev); 500 + } 501 + 502 + static int zynqmp_firmware_remove(struct platform_device *pdev) 503 + { 504 + zynqmp_pm_api_debugfs_exit(); 505 + 506 + return 0; 507 + } 508 + 509 + static const struct of_device_id zynqmp_firmware_of_match[] = { 510 + {.compatible = "xlnx,zynqmp-firmware"}, 511 + {}, 512 + }; 513 + MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match); 514 + 515 + static struct platform_driver zynqmp_firmware_driver = { 516 + .driver = { 517 + .name = "zynqmp_firmware", 518 + .of_match_table = zynqmp_firmware_of_match, 519 + }, 520 + .probe = zynqmp_firmware_probe, 521 + .remove = zynqmp_firmware_remove, 522 + }; 523 + module_platform_driver(zynqmp_firmware_driver);
+113
include/linux/firmware/xlnx-zynqmp.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Xilinx Zynq MPSoC Firmware layer 4 + * 5 + * Copyright (C) 2014-2018 Xilinx 6 + * 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> 11 + */ 12 + 13 + #ifndef __FIRMWARE_ZYNQMP_H__ 14 + #define __FIRMWARE_ZYNQMP_H__ 15 + 16 + #define ZYNQMP_PM_VERSION_MAJOR 1 17 + #define ZYNQMP_PM_VERSION_MINOR 0 18 + 19 + #define ZYNQMP_PM_VERSION ((ZYNQMP_PM_VERSION_MAJOR << 16) | \ 20 + ZYNQMP_PM_VERSION_MINOR) 21 + 22 + #define ZYNQMP_TZ_VERSION_MAJOR 1 23 + #define ZYNQMP_TZ_VERSION_MINOR 0 24 + 25 + #define ZYNQMP_TZ_VERSION ((ZYNQMP_TZ_VERSION_MAJOR << 16) | \ 26 + ZYNQMP_TZ_VERSION_MINOR) 27 + 28 + /* SMC SIP service Call Function Identifier Prefix */ 29 + #define PM_SIP_SVC 0xC2000000 30 + #define PM_GET_TRUSTZONE_VERSION 0xa03 31 + 32 + /* Number of 32bits values in payload */ 33 + #define PAYLOAD_ARG_CNT 4U 34 + 35 + enum pm_api_id { 36 + PM_GET_API_VERSION = 1, 37 + PM_QUERY_DATA = 35, 38 + PM_CLOCK_ENABLE, 39 + PM_CLOCK_DISABLE, 40 + PM_CLOCK_GETSTATE, 41 + PM_CLOCK_SETDIVIDER, 42 + PM_CLOCK_GETDIVIDER, 43 + PM_CLOCK_SETRATE, 44 + PM_CLOCK_GETRATE, 45 + PM_CLOCK_SETPARENT, 46 + PM_CLOCK_GETPARENT, 47 + }; 48 + 49 + /* PMU-FW return status codes */ 50 + enum pm_ret_status { 51 + XST_PM_SUCCESS = 0, 52 + XST_PM_INTERNAL = 2000, 53 + XST_PM_CONFLICT, 54 + XST_PM_NO_ACCESS, 55 + XST_PM_INVALID_NODE, 56 + XST_PM_DOUBLE_REQ, 57 + XST_PM_ABORT_SUSPEND, 58 + }; 59 + 60 + enum pm_ioctl_id { 61 + IOCTL_SET_PLL_FRAC_MODE = 8, 62 + IOCTL_GET_PLL_FRAC_MODE, 63 + IOCTL_SET_PLL_FRAC_DATA, 64 + IOCTL_GET_PLL_FRAC_DATA, 65 + }; 66 + 67 + enum pm_query_id { 68 + PM_QID_INVALID, 69 + PM_QID_CLOCK_GET_NAME, 70 + PM_QID_CLOCK_GET_TOPOLOGY, 71 + PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS, 72 + PM_QID_CLOCK_GET_PARENTS, 73 + PM_QID_CLOCK_GET_ATTRIBUTES, 74 + }; 75 + 76 + /** 77 + * struct zynqmp_pm_query_data - PM query data 78 + * @qid: query ID 79 + * @arg1: Argument 1 of query data 80 + * @arg2: Argument 2 of query data 81 + * @arg3: Argument 3 of query data 82 + */ 83 + struct zynqmp_pm_query_data { 84 + u32 qid; 85 + u32 arg1; 86 + u32 arg2; 87 + u32 arg3; 88 + }; 89 + 90 + struct zynqmp_eemi_ops { 91 + int (*get_api_version)(u32 *version); 92 + int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out); 93 + int (*clock_enable)(u32 clock_id); 94 + int (*clock_disable)(u32 clock_id); 95 + int (*clock_getstate)(u32 clock_id, u32 *state); 96 + int (*clock_setdivider)(u32 clock_id, u32 divider); 97 + int (*clock_getdivider)(u32 clock_id, u32 *divider); 98 + int (*clock_setrate)(u32 clock_id, u64 rate); 99 + int (*clock_getrate)(u32 clock_id, u64 *rate); 100 + int (*clock_setparent)(u32 clock_id, u32 parent_id); 101 + int (*clock_getparent)(u32 clock_id, u32 *parent_id); 102 + }; 103 + 104 + #if IS_REACHABLE(CONFIG_ARCH_ZYNQMP) 105 + const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); 106 + #else 107 + static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) 108 + { 109 + return NULL; 110 + } 111 + #endif 112 + 113 + #endif /* __FIRMWARE_ZYNQMP_H__ */