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

firmware: xilinx: Add debugfs interface

Firmware-debug provides debugfs interface to all APIs.
Debugfs can be used to call firmware APIs with required
parameters.

Usage:
* Calling firmware API through debugfs:
# echo "<api-name> <arg1> .. <argn>" > /sys/.../zynqmp-firmware/pm

* Read output of last called firmware API:
# cat /sys/.../zynqmp-firmware/pm

Refer ug1200 for more information on these APIs:
* https://www.xilinx.com/support/documentation/user_guides/ug1200-eemi-api.pdf

Add basic debugfs file to get API version.

Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Jolly Shah <jollys@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>

authored by

Rajan Vaja and committed by
Michal Simek
b3217252 f9627312

+259
+7
drivers/firmware/xilinx/Kconfig
··· 13 13 Say yes to enable ZynqMP firmware interface driver. 14 14 If in doubt, say N. 15 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 + 16 23 endmenu
+1
drivers/firmware/xilinx/Makefile
··· 2 2 # Makefile for Xilinx firmwares 3 3 4 4 obj-$(CONFIG_ZYNQMP_FIRMWARE) += zynqmp.o 5 + obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += zynqmp-debug.o
+222
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 + }; 36 + 37 + struct dentry *firmware_debugfs_root; 38 + 39 + /** 40 + * zynqmp_pm_argument_value() - Extract argument value from a PM-API request 41 + * @arg: Entered PM-API argument in string format 42 + * 43 + * Return: Argument value in unsigned integer format on success 44 + * 0 otherwise 45 + */ 46 + static u64 zynqmp_pm_argument_value(char *arg) 47 + { 48 + u64 value; 49 + 50 + if (!arg) 51 + return 0; 52 + 53 + if (!kstrtou64(arg, 0, &value)) 54 + return value; 55 + 56 + return 0; 57 + } 58 + 59 + /** 60 + * get_pm_api_id() - Extract API-ID from a PM-API request 61 + * @pm_api_req: Entered PM-API argument in string format 62 + * @pm_id: API-ID 63 + * 64 + * Return: 0 on success else error code 65 + */ 66 + static int get_pm_api_id(char *pm_api_req, u32 *pm_id) 67 + { 68 + int i; 69 + 70 + for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) { 71 + if (!strncasecmp(pm_api_req, pm_api_list[i].api_name, 72 + pm_api_list[i].api_name_len)) { 73 + *pm_id = pm_api_list[i].api_id; 74 + break; 75 + } 76 + } 77 + 78 + /* If no name was entered look for PM-API ID instead */ 79 + if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id)) 80 + return -EINVAL; 81 + 82 + return 0; 83 + } 84 + 85 + static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret) 86 + { 87 + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); 88 + u32 pm_api_version; 89 + int ret; 90 + 91 + if (!eemi_ops) 92 + return -ENXIO; 93 + 94 + switch (pm_id) { 95 + case PM_GET_API_VERSION: 96 + ret = eemi_ops->get_api_version(&pm_api_version); 97 + sprintf(debugfs_buf, "PM-API Version = %d.%d\n", 98 + pm_api_version >> 16, pm_api_version & 0xffff); 99 + break; 100 + default: 101 + sprintf(debugfs_buf, "Unsupported PM-API request\n"); 102 + ret = -EINVAL; 103 + } 104 + 105 + return ret; 106 + } 107 + 108 + /** 109 + * zynqmp_pm_debugfs_api_write() - debugfs write function 110 + * @file: User file 111 + * @ptr: User entered PM-API string 112 + * @len: Length of the userspace buffer 113 + * @off: Offset within the file 114 + * 115 + * Used for triggering pm api functions by writing 116 + * echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or 117 + * echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power 118 + * 119 + * Return: Number of bytes copied if PM-API request succeeds, 120 + * the corresponding error code otherwise 121 + */ 122 + static ssize_t zynqmp_pm_debugfs_api_write(struct file *file, 123 + const char __user *ptr, size_t len, 124 + loff_t *off) 125 + { 126 + char *kern_buff, *tmp_buff; 127 + char *pm_api_req; 128 + u32 pm_id = 0; 129 + u64 pm_api_arg[4] = {0, 0, 0, 0}; 130 + /* Return values from PM APIs calls */ 131 + u32 pm_api_ret[4] = {0, 0, 0, 0}; 132 + 133 + int ret; 134 + int i = 0; 135 + 136 + strcpy(debugfs_buf, ""); 137 + 138 + if (*off != 0 || len == 0) 139 + return -EINVAL; 140 + 141 + kern_buff = kzalloc(len, GFP_KERNEL); 142 + if (!kern_buff) 143 + return -ENOMEM; 144 + 145 + tmp_buff = kern_buff; 146 + 147 + ret = strncpy_from_user(kern_buff, ptr, len); 148 + if (ret < 0) { 149 + ret = -EFAULT; 150 + goto err; 151 + } 152 + 153 + /* Read the API name from a user request */ 154 + pm_api_req = strsep(&kern_buff, " "); 155 + 156 + ret = get_pm_api_id(pm_api_req, &pm_id); 157 + if (ret < 0) 158 + goto err; 159 + 160 + /* Read node_id and arguments from the PM-API request */ 161 + pm_api_req = strsep(&kern_buff, " "); 162 + while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) { 163 + pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req); 164 + pm_api_req = strsep(&kern_buff, " "); 165 + } 166 + 167 + ret = process_api_request(pm_id, pm_api_arg, pm_api_ret); 168 + 169 + err: 170 + kfree(tmp_buff); 171 + if (ret) 172 + return ret; 173 + 174 + return len; 175 + } 176 + 177 + /** 178 + * zynqmp_pm_debugfs_api_read() - debugfs read function 179 + * @file: User file 180 + * @ptr: Requested pm_api_version string 181 + * @len: Length of the userspace buffer 182 + * @off: Offset within the file 183 + * 184 + * Return: Length of the version string on success 185 + * else error code 186 + */ 187 + static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr, 188 + size_t len, loff_t *off) 189 + { 190 + return simple_read_from_buffer(ptr, len, off, debugfs_buf, 191 + strlen(debugfs_buf)); 192 + } 193 + 194 + /* Setup debugfs fops */ 195 + static const struct file_operations fops_zynqmp_pm_dbgfs = { 196 + .owner = THIS_MODULE, 197 + .write = zynqmp_pm_debugfs_api_write, 198 + .read = zynqmp_pm_debugfs_api_read, 199 + }; 200 + 201 + /** 202 + * zynqmp_pm_api_debugfs_init - Initialize debugfs interface 203 + * 204 + * Return: None 205 + */ 206 + void zynqmp_pm_api_debugfs_init(void) 207 + { 208 + /* Initialize debugfs interface */ 209 + firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL); 210 + debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL, 211 + &fops_zynqmp_pm_dbgfs); 212 + } 213 + 214 + /** 215 + * zynqmp_pm_api_debugfs_exit - Remove debugfs interface 216 + * 217 + * Return: None 218 + */ 219 + void zynqmp_pm_api_debugfs_exit(void) 220 + { 221 + debugfs_remove_recursive(firmware_debugfs_root); 222 + }
+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__ */
+5
drivers/firmware/xilinx/zynqmp.c
··· 21 21 #include <linux/uaccess.h> 22 22 23 23 #include <linux/firmware/xlnx-zynqmp.h> 24 + #include "zynqmp-debug.h" 24 25 25 26 /** 26 27 * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes ··· 494 493 pr_info("%s Trustzone version v%d.%d\n", __func__, 495 494 pm_tz_version >> 16, pm_tz_version & 0xFFFF); 496 495 496 + zynqmp_pm_api_debugfs_init(); 497 + 497 498 return of_platform_populate(dev->of_node, NULL, NULL, dev); 498 499 } 499 500 500 501 static int zynqmp_firmware_remove(struct platform_device *pdev) 501 502 { 503 + zynqmp_pm_api_debugfs_exit(); 504 + 502 505 return 0; 503 506 } 504 507