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 *
7*a5cb804bSMichal Simek * Michal Simek <michal.simek@amd.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
38f5ba30baSJason Yan static 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 */
zynqmp_pm_argument_value(char * arg)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 */
get_pm_api_id(char * pm_api_req,u32 * pm_id)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
process_api_request(u32 pm_id,u64 * pm_api_arg,u32 * pm_api_ret)86b3217252SRajan Vaja static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
87b3217252SRajan Vaja {
88b3217252SRajan Vaja u32 pm_api_version;
89b3217252SRajan Vaja int ret;
90e60f02ddSRajan Vaja struct zynqmp_pm_query_data qdata = {0};
91b3217252SRajan Vaja
92b3217252SRajan Vaja switch (pm_id) {
93b3217252SRajan Vaja case PM_GET_API_VERSION:
94b9b3a8beSRajan Vaja ret = zynqmp_pm_get_api_version(&pm_api_version);
95b3217252SRajan Vaja sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
96b3217252SRajan Vaja pm_api_version >> 16, pm_api_version & 0xffff);
97b3217252SRajan Vaja break;
98e60f02ddSRajan Vaja case PM_QUERY_DATA:
99e60f02ddSRajan Vaja qdata.qid = pm_api_arg[0];
100e60f02ddSRajan Vaja qdata.arg1 = pm_api_arg[1];
101e60f02ddSRajan Vaja qdata.arg2 = pm_api_arg[2];
102e60f02ddSRajan Vaja qdata.arg3 = pm_api_arg[3];
103e60f02ddSRajan Vaja
1046366c1baSRajan Vaja ret = zynqmp_pm_query_data(qdata, pm_api_ret);
105e60f02ddSRajan Vaja if (ret)
106e60f02ddSRajan Vaja break;
107e60f02ddSRajan Vaja
108e60f02ddSRajan Vaja switch (qdata.qid) {
109e60f02ddSRajan Vaja case PM_QID_CLOCK_GET_NAME:
110e60f02ddSRajan Vaja sprintf(debugfs_buf, "Clock name = %s\n",
111e60f02ddSRajan Vaja (char *)pm_api_ret);
112e60f02ddSRajan Vaja break;
113e60f02ddSRajan Vaja case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS:
114e60f02ddSRajan Vaja sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",
115e60f02ddSRajan Vaja pm_api_ret[1], pm_api_ret[2]);
116e60f02ddSRajan Vaja break;
117e60f02ddSRajan Vaja default:
118e60f02ddSRajan Vaja sprintf(debugfs_buf,
119e60f02ddSRajan Vaja "data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",
120e60f02ddSRajan Vaja pm_api_ret[0], pm_api_ret[1],
121e60f02ddSRajan Vaja pm_api_ret[2], pm_api_ret[3]);
122e60f02ddSRajan Vaja }
123e60f02ddSRajan Vaja break;
124b3217252SRajan Vaja default:
125b3217252SRajan Vaja sprintf(debugfs_buf, "Unsupported PM-API request\n");
126b3217252SRajan Vaja ret = -EINVAL;
127b3217252SRajan Vaja }
128b3217252SRajan Vaja
129b3217252SRajan Vaja return ret;
130b3217252SRajan Vaja }
131b3217252SRajan Vaja
132b3217252SRajan Vaja /**
133b3217252SRajan Vaja * zynqmp_pm_debugfs_api_write() - debugfs write function
134b3217252SRajan Vaja * @file: User file
135b3217252SRajan Vaja * @ptr: User entered PM-API string
136b3217252SRajan Vaja * @len: Length of the userspace buffer
137b3217252SRajan Vaja * @off: Offset within the file
138b3217252SRajan Vaja *
139b3217252SRajan Vaja * Used for triggering pm api functions by writing
140b3217252SRajan Vaja * echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or
141b3217252SRajan Vaja * echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power
142b3217252SRajan Vaja *
143b3217252SRajan Vaja * Return: Number of bytes copied if PM-API request succeeds,
144b3217252SRajan Vaja * the corresponding error code otherwise
145b3217252SRajan Vaja */
zynqmp_pm_debugfs_api_write(struct file * file,const char __user * ptr,size_t len,loff_t * off)146b3217252SRajan Vaja static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
147b3217252SRajan Vaja const char __user *ptr, size_t len,
148b3217252SRajan Vaja loff_t *off)
149b3217252SRajan Vaja {
150b3217252SRajan Vaja char *kern_buff, *tmp_buff;
151b3217252SRajan Vaja char *pm_api_req;
152b3217252SRajan Vaja u32 pm_id = 0;
153b3217252SRajan Vaja u64 pm_api_arg[4] = {0, 0, 0, 0};
154b3217252SRajan Vaja /* Return values from PM APIs calls */
155b3217252SRajan Vaja u32 pm_api_ret[4] = {0, 0, 0, 0};
156b3217252SRajan Vaja
157b3217252SRajan Vaja int ret;
158b3217252SRajan Vaja int i = 0;
159b3217252SRajan Vaja
160b3217252SRajan Vaja strcpy(debugfs_buf, "");
161b3217252SRajan Vaja
162b9472f7dSJann Horn if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1)
163b3217252SRajan Vaja return -EINVAL;
164b3217252SRajan Vaja
165b9472f7dSJann Horn kern_buff = memdup_user_nul(ptr, len);
166b9472f7dSJann Horn if (IS_ERR(kern_buff))
167b9472f7dSJann Horn return PTR_ERR(kern_buff);
168b3217252SRajan Vaja tmp_buff = kern_buff;
169b3217252SRajan Vaja
170b3217252SRajan Vaja /* Read the API name from a user request */
171b3217252SRajan Vaja pm_api_req = strsep(&kern_buff, " ");
172b3217252SRajan Vaja
173b3217252SRajan Vaja ret = get_pm_api_id(pm_api_req, &pm_id);
174b3217252SRajan Vaja if (ret < 0)
175b3217252SRajan Vaja goto err;
176b3217252SRajan Vaja
177b3217252SRajan Vaja /* Read node_id and arguments from the PM-API request */
178b3217252SRajan Vaja pm_api_req = strsep(&kern_buff, " ");
179b3217252SRajan Vaja while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
180b3217252SRajan Vaja pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
181b3217252SRajan Vaja pm_api_req = strsep(&kern_buff, " ");
182b3217252SRajan Vaja }
183b3217252SRajan Vaja
184b3217252SRajan Vaja ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);
185b3217252SRajan Vaja
186b3217252SRajan Vaja err:
187b3217252SRajan Vaja kfree(tmp_buff);
188b3217252SRajan Vaja if (ret)
189b3217252SRajan Vaja return ret;
190b3217252SRajan Vaja
191b3217252SRajan Vaja return len;
192b3217252SRajan Vaja }
193b3217252SRajan Vaja
194b3217252SRajan Vaja /**
195b3217252SRajan Vaja * zynqmp_pm_debugfs_api_read() - debugfs read function
196b3217252SRajan Vaja * @file: User file
197b3217252SRajan Vaja * @ptr: Requested pm_api_version string
198b3217252SRajan Vaja * @len: Length of the userspace buffer
199b3217252SRajan Vaja * @off: Offset within the file
200b3217252SRajan Vaja *
201b3217252SRajan Vaja * Return: Length of the version string on success
202b3217252SRajan Vaja * else error code
203b3217252SRajan Vaja */
zynqmp_pm_debugfs_api_read(struct file * file,char __user * ptr,size_t len,loff_t * off)204b3217252SRajan Vaja static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,
205b3217252SRajan Vaja size_t len, loff_t *off)
206b3217252SRajan Vaja {
207b3217252SRajan Vaja return simple_read_from_buffer(ptr, len, off, debugfs_buf,
208b3217252SRajan Vaja strlen(debugfs_buf));
209b3217252SRajan Vaja }
210b3217252SRajan Vaja
211b3217252SRajan Vaja /* Setup debugfs fops */
212b3217252SRajan Vaja static const struct file_operations fops_zynqmp_pm_dbgfs = {
213b3217252SRajan Vaja .owner = THIS_MODULE,
214b3217252SRajan Vaja .write = zynqmp_pm_debugfs_api_write,
215b3217252SRajan Vaja .read = zynqmp_pm_debugfs_api_read,
216b3217252SRajan Vaja };
217b3217252SRajan Vaja
218b3217252SRajan Vaja /**
219b3217252SRajan Vaja * zynqmp_pm_api_debugfs_init - Initialize debugfs interface
220b3217252SRajan Vaja *
221b3217252SRajan Vaja * Return: None
222b3217252SRajan Vaja */
zynqmp_pm_api_debugfs_init(void)223b3217252SRajan Vaja void zynqmp_pm_api_debugfs_init(void)
224b3217252SRajan Vaja {
225b3217252SRajan Vaja /* Initialize debugfs interface */
226b3217252SRajan Vaja firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL);
227b3217252SRajan Vaja debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL,
228b3217252SRajan Vaja &fops_zynqmp_pm_dbgfs);
229b3217252SRajan Vaja }
230b3217252SRajan Vaja
231b3217252SRajan Vaja /**
232b3217252SRajan Vaja * zynqmp_pm_api_debugfs_exit - Remove debugfs interface
233b3217252SRajan Vaja *
234b3217252SRajan Vaja * Return: None
235b3217252SRajan Vaja */
zynqmp_pm_api_debugfs_exit(void)236b3217252SRajan Vaja void zynqmp_pm_api_debugfs_exit(void)
237b3217252SRajan Vaja {
238b3217252SRajan Vaja debugfs_remove_recursive(firmware_debugfs_root);
239b3217252SRajan Vaja }
240