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