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