Path: blob/master/drivers/firmware/xilinx/zynqmp-debug.c
50303 views
// SPDX-License-Identifier: GPL-2.01/*2* Xilinx Zynq MPSoC Firmware layer for debugfs APIs3*4* Copyright (C) 2014-2018 Xilinx, Inc.5* Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc.6*7* Michal Simek <[email protected]>8* Davorin Mista <[email protected]>9* Jolly Shah <[email protected]>10* Rajan Vaja <[email protected]>11*/1213#include <linux/compiler.h>14#include <linux/module.h>15#include <linux/slab.h>16#include <linux/debugfs.h>17#include <linux/uaccess.h>1819#include <linux/firmware/xlnx-zynqmp.h>20#include "zynqmp-debug.h"2122#define PM_API_NAME_LEN 502324struct pm_api_info {25u32 api_id;26char api_name[PM_API_NAME_LEN];27char api_name_len;28};2930static char debugfs_buf[PAGE_SIZE];3132#define PM_API(id) {id, #id, strlen(#id)}33static struct pm_api_info pm_api_list[] = {34PM_API(PM_FORCE_POWERDOWN),35PM_API(PM_REQUEST_WAKEUP),36PM_API(PM_SYSTEM_SHUTDOWN),37PM_API(PM_REQUEST_NODE),38PM_API(PM_RELEASE_NODE),39PM_API(PM_SET_REQUIREMENT),40PM_API(PM_GET_API_VERSION),41PM_API(PM_GET_NODE_STATUS),42PM_API(PM_REGISTER_NOTIFIER),43PM_API(PM_RESET_ASSERT),44PM_API(PM_RESET_GET_STATUS),45PM_API(PM_GET_CHIPID),46PM_API(PM_PINCTRL_SET_FUNCTION),47PM_API(PM_PINCTRL_CONFIG_PARAM_GET),48PM_API(PM_PINCTRL_CONFIG_PARAM_SET),49PM_API(PM_IOCTL),50PM_API(PM_CLOCK_ENABLE),51PM_API(PM_CLOCK_DISABLE),52PM_API(PM_CLOCK_GETSTATE),53PM_API(PM_CLOCK_SETDIVIDER),54PM_API(PM_CLOCK_GETDIVIDER),55PM_API(PM_CLOCK_SETPARENT),56PM_API(PM_CLOCK_GETPARENT),57PM_API(PM_QUERY_DATA),58};5960static struct dentry *firmware_debugfs_root;6162/**63* zynqmp_pm_ioctl - PM IOCTL for device control and configs64* @node: Node ID of the device65* @ioctl: ID of the requested IOCTL66* @arg1: Argument 1 of requested IOCTL call67* @arg2: Argument 2 of requested IOCTL call68* @arg3: Argument 3 of requested IOCTL call69* @out: Returned output value70*71* Return: Returns status, either success or error+reason72*/73static int zynqmp_pm_ioctl(const u32 node, const u32 ioctl, const u32 arg1,74const u32 arg2, const u32 arg3, u32 *out)75{76return zynqmp_pm_invoke_fn(PM_IOCTL, out, 5, node, ioctl, arg1, arg2, arg3);77}7879/**80* zynqmp_pm_argument_value() - Extract argument value from a PM-API request81* @arg: Entered PM-API argument in string format82*83* Return: Argument value in unsigned integer format on success84* 0 otherwise85*/86static u64 zynqmp_pm_argument_value(char *arg)87{88u64 value;8990if (!arg)91return 0;9293if (!kstrtou64(arg, 0, &value))94return value;9596return 0;97}9899/**100* get_pm_api_id() - Extract API-ID from a PM-API request101* @pm_api_req: Entered PM-API argument in string format102* @pm_id: API-ID103*104* Return: 0 on success else error code105*/106static int get_pm_api_id(char *pm_api_req, u32 *pm_id)107{108int i;109110for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) {111if (!strncasecmp(pm_api_req, pm_api_list[i].api_name,112pm_api_list[i].api_name_len)) {113*pm_id = pm_api_list[i].api_id;114break;115}116}117118/* If no name was entered look for PM-API ID instead */119if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id))120return -EINVAL;121122return 0;123}124125static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)126{127u32 pm_api_version;128int ret;129struct zynqmp_pm_query_data qdata = {0};130131switch (pm_id) {132case PM_GET_API_VERSION:133ret = zynqmp_pm_get_api_version(&pm_api_version);134sprintf(debugfs_buf, "PM-API Version = %d.%d\n",135pm_api_version >> 16, pm_api_version & 0xffff);136break;137case PM_FORCE_POWERDOWN:138ret = zynqmp_pm_force_pwrdwn(pm_api_arg[0],139pm_api_arg[1] ? pm_api_arg[1] :140ZYNQMP_PM_REQUEST_ACK_NO);141break;142case PM_REQUEST_WAKEUP:143ret = zynqmp_pm_request_wake(pm_api_arg[0],144pm_api_arg[1], pm_api_arg[2],145pm_api_arg[3] ? pm_api_arg[3] :146ZYNQMP_PM_REQUEST_ACK_NO);147break;148case PM_SYSTEM_SHUTDOWN:149ret = zynqmp_pm_system_shutdown(pm_api_arg[0], pm_api_arg[1]);150break;151case PM_REQUEST_NODE:152ret = zynqmp_pm_request_node(pm_api_arg[0],153pm_api_arg[1] ? pm_api_arg[1] :154ZYNQMP_PM_CAPABILITY_ACCESS,155pm_api_arg[2] ? pm_api_arg[2] : 0,156pm_api_arg[3] ? pm_api_arg[3] :157ZYNQMP_PM_REQUEST_ACK_BLOCKING);158break;159case PM_RELEASE_NODE:160ret = zynqmp_pm_release_node(pm_api_arg[0]);161break;162case PM_SET_REQUIREMENT:163ret = zynqmp_pm_set_requirement(pm_api_arg[0],164pm_api_arg[1] ? pm_api_arg[1] :165ZYNQMP_PM_CAPABILITY_CONTEXT,166pm_api_arg[2] ?167pm_api_arg[2] : 0,168pm_api_arg[3] ? pm_api_arg[3] :169ZYNQMP_PM_REQUEST_ACK_BLOCKING);170break;171case PM_GET_NODE_STATUS:172ret = zynqmp_pm_get_node_status(pm_api_arg[0],173&pm_api_ret[0],174&pm_api_ret[1],175&pm_api_ret[2]);176if (!ret)177sprintf(debugfs_buf,178"GET_NODE_STATUS:\n\tNodeId: %llu\n\tStatus: %u\n\tRequirements: %u\n\tUsage: %u\n",179pm_api_arg[0], pm_api_ret[0],180pm_api_ret[1], pm_api_ret[2]);181break;182case PM_REGISTER_NOTIFIER:183ret = zynqmp_pm_register_notifier(pm_api_arg[0],184pm_api_arg[1] ?185pm_api_arg[1] : 0,186pm_api_arg[2] ?187pm_api_arg[2] : 0,188pm_api_arg[3] ?189pm_api_arg[3] : 0);190break;191case PM_RESET_ASSERT:192ret = zynqmp_pm_reset_assert(pm_api_arg[0], pm_api_arg[1]);193break;194case PM_RESET_GET_STATUS:195ret = zynqmp_pm_reset_get_status(pm_api_arg[0], &pm_api_ret[0]);196if (!ret)197sprintf(debugfs_buf, "Reset status: %u\n",198pm_api_ret[0]);199break;200case PM_GET_CHIPID:201ret = zynqmp_pm_get_chipid(&pm_api_ret[0], &pm_api_ret[1]);202if (!ret)203sprintf(debugfs_buf, "Idcode: %#x, Version:%#x\n",204pm_api_ret[0], pm_api_ret[1]);205break;206case PM_PINCTRL_SET_FUNCTION:207ret = zynqmp_pm_pinctrl_set_function(pm_api_arg[0],208pm_api_arg[1]);209break;210case PM_PINCTRL_CONFIG_PARAM_GET:211ret = zynqmp_pm_pinctrl_get_config(pm_api_arg[0], pm_api_arg[1],212&pm_api_ret[0]);213if (!ret)214sprintf(debugfs_buf,215"Pin: %llu, Param: %llu, Value: %u\n",216pm_api_arg[0], pm_api_arg[1],217pm_api_ret[0]);218break;219case PM_PINCTRL_CONFIG_PARAM_SET:220ret = zynqmp_pm_pinctrl_set_config(pm_api_arg[0],221pm_api_arg[1],222pm_api_arg[2]);223break;224case PM_IOCTL:225ret = zynqmp_pm_ioctl(pm_api_arg[0], pm_api_arg[1],226pm_api_arg[2], pm_api_arg[3],227pm_api_arg[4], &pm_api_ret[0]);228if (!ret && (pm_api_arg[1] == IOCTL_GET_RPU_OPER_MODE ||229pm_api_arg[1] == IOCTL_GET_PLL_FRAC_MODE ||230pm_api_arg[1] == IOCTL_GET_PLL_FRAC_DATA ||231pm_api_arg[1] == IOCTL_READ_GGS ||232pm_api_arg[1] == IOCTL_READ_PGGS ||233pm_api_arg[1] == IOCTL_READ_REG))234sprintf(debugfs_buf, "IOCTL return value: %u\n",235pm_api_ret[1]);236if (!ret && pm_api_arg[1] == IOCTL_GET_QOS)237sprintf(debugfs_buf, "Default QoS: %u\nCurrent QoS: %u\n",238pm_api_ret[1], pm_api_ret[2]);239break;240case PM_CLOCK_ENABLE:241ret = zynqmp_pm_clock_enable(pm_api_arg[0]);242break;243case PM_CLOCK_DISABLE:244ret = zynqmp_pm_clock_disable(pm_api_arg[0]);245break;246case PM_CLOCK_GETSTATE:247ret = zynqmp_pm_clock_getstate(pm_api_arg[0], &pm_api_ret[0]);248if (!ret)249sprintf(debugfs_buf, "Clock state: %u\n",250pm_api_ret[0]);251break;252case PM_CLOCK_SETDIVIDER:253ret = zynqmp_pm_clock_setdivider(pm_api_arg[0], pm_api_arg[1]);254break;255case PM_CLOCK_GETDIVIDER:256ret = zynqmp_pm_clock_getdivider(pm_api_arg[0], &pm_api_ret[0]);257if (!ret)258sprintf(debugfs_buf, "Divider Value: %d\n",259pm_api_ret[0]);260break;261case PM_CLOCK_SETPARENT:262ret = zynqmp_pm_clock_setparent(pm_api_arg[0], pm_api_arg[1]);263break;264case PM_CLOCK_GETPARENT:265ret = zynqmp_pm_clock_getparent(pm_api_arg[0], &pm_api_ret[0]);266if (!ret)267sprintf(debugfs_buf,268"Clock parent Index: %u\n", pm_api_ret[0]);269break;270case PM_QUERY_DATA:271qdata.qid = pm_api_arg[0];272qdata.arg1 = pm_api_arg[1];273qdata.arg2 = pm_api_arg[2];274qdata.arg3 = pm_api_arg[3];275276ret = zynqmp_pm_query_data(qdata, pm_api_ret);277if (ret)278break;279280switch (qdata.qid) {281case PM_QID_CLOCK_GET_NAME:282sprintf(debugfs_buf, "Clock name = %s\n",283(char *)pm_api_ret);284break;285case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS:286sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",287pm_api_ret[1], pm_api_ret[2]);288break;289default:290sprintf(debugfs_buf,291"data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",292pm_api_ret[0], pm_api_ret[1],293pm_api_ret[2], pm_api_ret[3]);294}295break;296default:297sprintf(debugfs_buf, "Unsupported PM-API request\n");298ret = -EINVAL;299}300301return ret;302}303304/**305* zynqmp_pm_debugfs_api_write() - debugfs write function306* @file: User file307* @ptr: User entered PM-API string308* @len: Length of the userspace buffer309* @off: Offset within the file310*311* Used for triggering pm api functions by writing312* echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or313* echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power314*315* Return: Number of bytes copied if PM-API request succeeds,316* the corresponding error code otherwise317*/318static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,319const char __user *ptr, size_t len,320loff_t *off)321{322char *kern_buff, *tmp_buff;323char *pm_api_req;324u32 pm_id = 0;325u64 pm_api_arg[5] = {0, 0, 0, 0, 0};326/* Return values from PM APIs calls */327u32 pm_api_ret[4] = {0, 0, 0, 0};328329int ret;330int i = 0;331332strcpy(debugfs_buf, "");333334if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1)335return -EINVAL;336337kern_buff = memdup_user_nul(ptr, len);338if (IS_ERR(kern_buff))339return PTR_ERR(kern_buff);340tmp_buff = kern_buff;341342/* Read the API name from a user request */343pm_api_req = strsep(&kern_buff, " ");344345ret = get_pm_api_id(pm_api_req, &pm_id);346if (ret < 0)347goto err;348349/* Read node_id and arguments from the PM-API request */350pm_api_req = strsep(&kern_buff, " ");351while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {352pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);353pm_api_req = strsep(&kern_buff, " ");354}355356ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);357358err:359kfree(tmp_buff);360if (ret)361return ret;362363return len;364}365366/**367* zynqmp_pm_debugfs_api_read() - debugfs read function368* @file: User file369* @ptr: Requested pm_api_version string370* @len: Length of the userspace buffer371* @off: Offset within the file372*373* Return: Length of the version string on success374* else error code375*/376static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,377size_t len, loff_t *off)378{379return simple_read_from_buffer(ptr, len, off, debugfs_buf,380strlen(debugfs_buf));381}382383/* Setup debugfs fops */384static const struct file_operations fops_zynqmp_pm_dbgfs = {385.owner = THIS_MODULE,386.write = zynqmp_pm_debugfs_api_write,387.read = zynqmp_pm_debugfs_api_read,388};389390/**391* zynqmp_pm_api_debugfs_init - Initialize debugfs interface392*393* Return: None394*/395void zynqmp_pm_api_debugfs_init(void)396{397/* Initialize debugfs interface */398firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL);399debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL,400&fops_zynqmp_pm_dbgfs);401}402403/**404* zynqmp_pm_api_debugfs_exit - Remove debugfs interface405*406* Return: None407*/408void zynqmp_pm_api_debugfs_exit(void)409{410debugfs_remove_recursive(firmware_debugfs_root);411}412413414