1b3217252SRajan Vaja // SPDX-License-Identifier: GPL-2.0 2b3217252SRajan Vaja /* 3b3217252SRajan Vaja * Xilinx Zynq MPSoC Firmware layer for debugfs APIs 4b3217252SRajan Vaja * 5b3217252SRajan Vaja * Copyright (C) 2014-2018 Xilinx, Inc. 6b3217252SRajan Vaja * 7b3217252SRajan Vaja * Michal Simek <michal.simek@xilinx.com> 8b3217252SRajan Vaja * Davorin Mista <davorin.mista@aggios.com> 9b3217252SRajan Vaja * Jolly Shah <jollys@xilinx.com> 10b3217252SRajan Vaja * Rajan Vaja <rajanv@xilinx.com> 11b3217252SRajan Vaja */ 12b3217252SRajan Vaja 13b3217252SRajan Vaja #include <linux/compiler.h> 14b3217252SRajan Vaja #include <linux/module.h> 15b3217252SRajan Vaja #include <linux/slab.h> 16b3217252SRajan Vaja #include <linux/debugfs.h> 17b3217252SRajan Vaja #include <linux/uaccess.h> 18b3217252SRajan Vaja 19b3217252SRajan Vaja #include <linux/firmware/xlnx-zynqmp.h> 20b3217252SRajan Vaja #include "zynqmp-debug.h" 21b3217252SRajan Vaja 22b3217252SRajan Vaja #define PM_API_NAME_LEN 50 23b3217252SRajan Vaja 24b3217252SRajan Vaja struct pm_api_info { 25b3217252SRajan Vaja u32 api_id; 26b3217252SRajan Vaja char api_name[PM_API_NAME_LEN]; 27b3217252SRajan Vaja char api_name_len; 28b3217252SRajan Vaja }; 29b3217252SRajan Vaja 30b3217252SRajan Vaja static char debugfs_buf[PAGE_SIZE]; 31b3217252SRajan Vaja 32b3217252SRajan Vaja #define PM_API(id) {id, #id, strlen(#id)} 33b3217252SRajan Vaja static struct pm_api_info pm_api_list[] = { 34b3217252SRajan Vaja PM_API(PM_GET_API_VERSION), 35e60f02ddSRajan Vaja PM_API(PM_QUERY_DATA), 36b3217252SRajan Vaja }; 37b3217252SRajan Vaja 38b3217252SRajan Vaja struct dentry *firmware_debugfs_root; 39b3217252SRajan Vaja 40b3217252SRajan Vaja /** 41b3217252SRajan Vaja * zynqmp_pm_argument_value() - Extract argument value from a PM-API request 42b3217252SRajan Vaja * @arg: Entered PM-API argument in string format 43b3217252SRajan Vaja * 44b3217252SRajan Vaja * Return: Argument value in unsigned integer format on success 45b3217252SRajan Vaja * 0 otherwise 46b3217252SRajan Vaja */ 47b3217252SRajan Vaja static u64 zynqmp_pm_argument_value(char *arg) 48b3217252SRajan Vaja { 49b3217252SRajan Vaja u64 value; 50b3217252SRajan Vaja 51b3217252SRajan Vaja if (!arg) 52b3217252SRajan Vaja return 0; 53b3217252SRajan Vaja 54b3217252SRajan Vaja if (!kstrtou64(arg, 0, &value)) 55b3217252SRajan Vaja return value; 56b3217252SRajan Vaja 57b3217252SRajan Vaja return 0; 58b3217252SRajan Vaja } 59b3217252SRajan Vaja 60b3217252SRajan Vaja /** 61b3217252SRajan Vaja * get_pm_api_id() - Extract API-ID from a PM-API request 62b3217252SRajan Vaja * @pm_api_req: Entered PM-API argument in string format 63b3217252SRajan Vaja * @pm_id: API-ID 64b3217252SRajan Vaja * 65b3217252SRajan Vaja * Return: 0 on success else error code 66b3217252SRajan Vaja */ 67b3217252SRajan Vaja static int get_pm_api_id(char *pm_api_req, u32 *pm_id) 68b3217252SRajan Vaja { 69b3217252SRajan Vaja int i; 70b3217252SRajan Vaja 71b3217252SRajan Vaja for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) { 72b3217252SRajan Vaja if (!strncasecmp(pm_api_req, pm_api_list[i].api_name, 73b3217252SRajan Vaja pm_api_list[i].api_name_len)) { 74b3217252SRajan Vaja *pm_id = pm_api_list[i].api_id; 75b3217252SRajan Vaja break; 76b3217252SRajan Vaja } 77b3217252SRajan Vaja } 78b3217252SRajan Vaja 79b3217252SRajan Vaja /* If no name was entered look for PM-API ID instead */ 80b3217252SRajan Vaja if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id)) 81b3217252SRajan Vaja return -EINVAL; 82b3217252SRajan Vaja 83b3217252SRajan Vaja return 0; 84b3217252SRajan Vaja } 85b3217252SRajan Vaja 86b3217252SRajan Vaja static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret) 87b3217252SRajan Vaja { 88b3217252SRajan Vaja const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); 89b3217252SRajan Vaja u32 pm_api_version; 90b3217252SRajan Vaja int ret; 91e60f02ddSRajan Vaja struct zynqmp_pm_query_data qdata = {0}; 92b3217252SRajan Vaja 93b3217252SRajan Vaja if (!eemi_ops) 94b3217252SRajan Vaja return -ENXIO; 95b3217252SRajan Vaja 96b3217252SRajan Vaja switch (pm_id) { 97b3217252SRajan Vaja case PM_GET_API_VERSION: 98b3217252SRajan Vaja ret = eemi_ops->get_api_version(&pm_api_version); 99b3217252SRajan Vaja sprintf(debugfs_buf, "PM-API Version = %d.%d\n", 100b3217252SRajan Vaja pm_api_version >> 16, pm_api_version & 0xffff); 101b3217252SRajan Vaja break; 102e60f02ddSRajan Vaja case PM_QUERY_DATA: 103e60f02ddSRajan Vaja qdata.qid = pm_api_arg[0]; 104e60f02ddSRajan Vaja qdata.arg1 = pm_api_arg[1]; 105e60f02ddSRajan Vaja qdata.arg2 = pm_api_arg[2]; 106e60f02ddSRajan Vaja qdata.arg3 = pm_api_arg[3]; 107e60f02ddSRajan Vaja 108e60f02ddSRajan Vaja ret = eemi_ops->query_data(qdata, pm_api_ret); 109e60f02ddSRajan Vaja if (ret) 110e60f02ddSRajan Vaja break; 111e60f02ddSRajan Vaja 112e60f02ddSRajan Vaja switch (qdata.qid) { 113e60f02ddSRajan Vaja case PM_QID_CLOCK_GET_NAME: 114e60f02ddSRajan Vaja sprintf(debugfs_buf, "Clock name = %s\n", 115e60f02ddSRajan Vaja (char *)pm_api_ret); 116e60f02ddSRajan Vaja break; 117e60f02ddSRajan Vaja case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS: 118e60f02ddSRajan Vaja sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n", 119e60f02ddSRajan Vaja pm_api_ret[1], pm_api_ret[2]); 120e60f02ddSRajan Vaja break; 121e60f02ddSRajan Vaja default: 122e60f02ddSRajan Vaja sprintf(debugfs_buf, 123e60f02ddSRajan Vaja "data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n", 124e60f02ddSRajan Vaja pm_api_ret[0], pm_api_ret[1], 125e60f02ddSRajan Vaja pm_api_ret[2], pm_api_ret[3]); 126e60f02ddSRajan Vaja } 127e60f02ddSRajan Vaja break; 128b3217252SRajan Vaja default: 129b3217252SRajan Vaja sprintf(debugfs_buf, "Unsupported PM-API request\n"); 130b3217252SRajan Vaja ret = -EINVAL; 131b3217252SRajan Vaja } 132b3217252SRajan Vaja 133b3217252SRajan Vaja return ret; 134b3217252SRajan Vaja } 135b3217252SRajan Vaja 136b3217252SRajan Vaja /** 137b3217252SRajan Vaja * zynqmp_pm_debugfs_api_write() - debugfs write function 138b3217252SRajan Vaja * @file: User file 139b3217252SRajan Vaja * @ptr: User entered PM-API string 140b3217252SRajan Vaja * @len: Length of the userspace buffer 141b3217252SRajan Vaja * @off: Offset within the file 142b3217252SRajan Vaja * 143b3217252SRajan Vaja * Used for triggering pm api functions by writing 144b3217252SRajan Vaja * echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or 145b3217252SRajan Vaja * echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power 146b3217252SRajan Vaja * 147b3217252SRajan Vaja * Return: Number of bytes copied if PM-API request succeeds, 148b3217252SRajan Vaja * the corresponding error code otherwise 149b3217252SRajan Vaja */ 150b3217252SRajan Vaja static ssize_t zynqmp_pm_debugfs_api_write(struct file *file, 151b3217252SRajan Vaja const char __user *ptr, size_t len, 152b3217252SRajan Vaja loff_t *off) 153b3217252SRajan Vaja { 154b3217252SRajan Vaja char *kern_buff, *tmp_buff; 155b3217252SRajan Vaja char *pm_api_req; 156b3217252SRajan Vaja u32 pm_id = 0; 157b3217252SRajan Vaja u64 pm_api_arg[4] = {0, 0, 0, 0}; 158b3217252SRajan Vaja /* Return values from PM APIs calls */ 159b3217252SRajan Vaja u32 pm_api_ret[4] = {0, 0, 0, 0}; 160b3217252SRajan Vaja 161b3217252SRajan Vaja int ret; 162b3217252SRajan Vaja int i = 0; 163b3217252SRajan Vaja 164b3217252SRajan Vaja strcpy(debugfs_buf, ""); 165b3217252SRajan Vaja 166b3217252SRajan Vaja if (*off != 0 || len == 0) 167b3217252SRajan Vaja return -EINVAL; 168b3217252SRajan Vaja 169b3217252SRajan Vaja kern_buff = kzalloc(len, GFP_KERNEL); 170b3217252SRajan Vaja if (!kern_buff) 171b3217252SRajan Vaja return -ENOMEM; 172b3217252SRajan Vaja 173b3217252SRajan Vaja tmp_buff = kern_buff; 174b3217252SRajan Vaja 175b3217252SRajan Vaja ret = strncpy_from_user(kern_buff, ptr, len); 176b3217252SRajan Vaja if (ret < 0) { 177b3217252SRajan Vaja ret = -EFAULT; 178b3217252SRajan Vaja goto err; 179b3217252SRajan Vaja } 180b3217252SRajan Vaja 181b3217252SRajan Vaja /* Read the API name from a user request */ 182b3217252SRajan Vaja pm_api_req = strsep(&kern_buff, " "); 183b3217252SRajan Vaja 184b3217252SRajan Vaja ret = get_pm_api_id(pm_api_req, &pm_id); 185b3217252SRajan Vaja if (ret < 0) 186b3217252SRajan Vaja goto err; 187b3217252SRajan Vaja 188b3217252SRajan Vaja /* Read node_id and arguments from the PM-API request */ 189b3217252SRajan Vaja pm_api_req = strsep(&kern_buff, " "); 190b3217252SRajan Vaja while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) { 191b3217252SRajan Vaja pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req); 192b3217252SRajan Vaja pm_api_req = strsep(&kern_buff, " "); 193b3217252SRajan Vaja } 194b3217252SRajan Vaja 195b3217252SRajan Vaja ret = process_api_request(pm_id, pm_api_arg, pm_api_ret); 196b3217252SRajan Vaja 197b3217252SRajan Vaja err: 198b3217252SRajan Vaja kfree(tmp_buff); 199b3217252SRajan Vaja if (ret) 200b3217252SRajan Vaja return ret; 201b3217252SRajan Vaja 202b3217252SRajan Vaja return len; 203b3217252SRajan Vaja } 204b3217252SRajan Vaja 205b3217252SRajan Vaja /** 206b3217252SRajan Vaja * zynqmp_pm_debugfs_api_read() - debugfs read function 207b3217252SRajan Vaja * @file: User file 208b3217252SRajan Vaja * @ptr: Requested pm_api_version string 209b3217252SRajan Vaja * @len: Length of the userspace buffer 210b3217252SRajan Vaja * @off: Offset within the file 211b3217252SRajan Vaja * 212b3217252SRajan Vaja * Return: Length of the version string on success 213b3217252SRajan Vaja * else error code 214b3217252SRajan Vaja */ 215b3217252SRajan Vaja static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr, 216b3217252SRajan Vaja size_t len, loff_t *off) 217b3217252SRajan Vaja { 218b3217252SRajan Vaja return simple_read_from_buffer(ptr, len, off, debugfs_buf, 219b3217252SRajan Vaja strlen(debugfs_buf)); 220b3217252SRajan Vaja } 221b3217252SRajan Vaja 222b3217252SRajan Vaja /* Setup debugfs fops */ 223b3217252SRajan Vaja static const struct file_operations fops_zynqmp_pm_dbgfs = { 224b3217252SRajan Vaja .owner = THIS_MODULE, 225b3217252SRajan Vaja .write = zynqmp_pm_debugfs_api_write, 226b3217252SRajan Vaja .read = zynqmp_pm_debugfs_api_read, 227b3217252SRajan Vaja }; 228b3217252SRajan Vaja 229b3217252SRajan Vaja /** 230b3217252SRajan Vaja * zynqmp_pm_api_debugfs_init - Initialize debugfs interface 231b3217252SRajan Vaja * 232b3217252SRajan Vaja * Return: None 233b3217252SRajan Vaja */ 234b3217252SRajan Vaja void zynqmp_pm_api_debugfs_init(void) 235b3217252SRajan Vaja { 236b3217252SRajan Vaja /* Initialize debugfs interface */ 237b3217252SRajan Vaja firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL); 238b3217252SRajan Vaja debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL, 239b3217252SRajan Vaja &fops_zynqmp_pm_dbgfs); 240b3217252SRajan Vaja } 241b3217252SRajan Vaja 242b3217252SRajan Vaja /** 243b3217252SRajan Vaja * zynqmp_pm_api_debugfs_exit - Remove debugfs interface 244b3217252SRajan Vaja * 245b3217252SRajan Vaja * Return: None 246b3217252SRajan Vaja */ 247b3217252SRajan Vaja void zynqmp_pm_api_debugfs_exit(void) 248b3217252SRajan Vaja { 249b3217252SRajan Vaja debugfs_remove_recursive(firmware_debugfs_root); 250b3217252SRajan Vaja } 251