File Index
Files and directories
Sign in
FILE

zynqmp-debug.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/xilinx/zynqmp-debug.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Xilinx Zynq MPSoC Firmware layer for debugfs APIs
  4 *
  5 *  Copyright (C) 2014-2018 Xilinx, Inc.
  6 *  Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc.
  7 *
  8 *  Michal Simek <michal.simek@amd.com>
  9 *  Davorin Mista <davorin.mista@aggios.com>
 10 *  Jolly Shah <jollys@xilinx.com>
 11 *  Rajan Vaja <rajanv@xilinx.com>
 12 */
 13
 14#include <linux/compiler.h>
 15#include <linux/module.h>
 16#include <linux/slab.h>
 17#include <linux/debugfs.h>
 18#include <linux/uaccess.h>
 19
 20#include <linux/firmware/xlnx-zynqmp.h>
 21#include "zynqmp-debug.h"
 22
 23#define PM_API_NAME_LEN			50
 24
 25struct pm_api_info {
 26	u32 api_id;
 27	char api_name[PM_API_NAME_LEN];
 28	char api_name_len;
 29};
 30
 31static char debugfs_buf[PAGE_SIZE];
 32
 33#define PM_API(id)		 {id, #id, strlen(#id)}
 34static struct pm_api_info pm_api_list[] = {
 35	PM_API(PM_FORCE_POWERDOWN),
 36	PM_API(PM_REQUEST_WAKEUP),
 37	PM_API(PM_SYSTEM_SHUTDOWN),
 38	PM_API(PM_REQUEST_NODE),
 39	PM_API(PM_RELEASE_NODE),
 40	PM_API(PM_SET_REQUIREMENT),
 41	PM_API(PM_GET_API_VERSION),
 42	PM_API(PM_GET_NODE_STATUS),
 43	PM_API(PM_REGISTER_NOTIFIER),
 44	PM_API(PM_RESET_ASSERT),
 45	PM_API(PM_RESET_GET_STATUS),
 46	PM_API(PM_GET_CHIPID),
 47	PM_API(PM_PINCTRL_SET_FUNCTION),
 48	PM_API(PM_PINCTRL_CONFIG_PARAM_GET),
 49	PM_API(PM_PINCTRL_CONFIG_PARAM_SET),
 50	PM_API(PM_IOCTL),
 51	PM_API(PM_CLOCK_ENABLE),
 52	PM_API(PM_CLOCK_DISABLE),
 53	PM_API(PM_CLOCK_GETSTATE),
 54	PM_API(PM_CLOCK_SETDIVIDER),
 55	PM_API(PM_CLOCK_GETDIVIDER),
 56	PM_API(PM_CLOCK_SETPARENT),
 57	PM_API(PM_CLOCK_GETPARENT),
 58	PM_API(PM_QUERY_DATA),
 59};
 60
 61static struct dentry *firmware_debugfs_root;
 62
 63/**
 64 * zynqmp_pm_ioctl - PM IOCTL for device control and configs
 65 * @node:	Node ID of the device
 66 * @ioctl:	ID of the requested IOCTL
 67 * @arg1:	Argument 1 of requested IOCTL call
 68 * @arg2:	Argument 2 of requested IOCTL call
 69 * @arg3:	Argument 3 of requested IOCTL call
 70 * @out:	Returned output value
 71 *
 72 * Return:	Returns status, either success or error+reason
 73 */
 74static int zynqmp_pm_ioctl(const u32 node, const u32 ioctl, const u32 arg1,
 75			   const u32 arg2, const u32 arg3, u32 *out)
 76{
 77	return zynqmp_pm_invoke_fn(PM_IOCTL, out, 5, node, ioctl, arg1, arg2, arg3);
 78}
 79
 80/**
 81 * zynqmp_pm_argument_value() - Extract argument value from a PM-API request
 82 * @arg:	Entered PM-API argument in string format
 83 *
 84 * Return: Argument value in unsigned integer format on success
 85 *	   0 otherwise
 86 */
 87static u64 zynqmp_pm_argument_value(char *arg)
 88{
 89	u64 value;
 90
 91	if (!arg)
 92		return 0;
 93
 94	if (!kstrtou64(arg, 0, &value))
 95		return value;
 96
 97	return 0;
 98}
 99
100/**
101 * get_pm_api_id() - Extract API-ID from a PM-API request
102 * @pm_api_req:		Entered PM-API argument in string format
103 * @pm_id:		API-ID
104 *
105 * Return: 0 on success else error code
106 */
107static int get_pm_api_id(char *pm_api_req, u32 *pm_id)
108{
109	int i;
110
111	for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) {
112		if (!strncasecmp(pm_api_req, pm_api_list[i].api_name,
113				 pm_api_list[i].api_name_len)) {
114			*pm_id = pm_api_list[i].api_id;
115			break;
116		}
117	}
118
119	/* If no name was entered look for PM-API ID instead */
120	if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id))
121		return -EINVAL;
122
123	return 0;
124}
125
126static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
127{
128	u32 pm_api_version;
129	int ret;
130	struct zynqmp_pm_query_data qdata = {0};
131
132	switch (pm_id) {
133	case PM_GET_API_VERSION:
134		ret = zynqmp_pm_get_api_version(&pm_api_version);
135		sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
136			pm_api_version >> 16, pm_api_version & 0xffff);
137		break;
138	case PM_FORCE_POWERDOWN:
139		ret = zynqmp_pm_force_pwrdwn(pm_api_arg[0],
140					     pm_api_arg[1] ? pm_api_arg[1] :
141					     ZYNQMP_PM_REQUEST_ACK_NO);
142		break;
143	case PM_REQUEST_WAKEUP:
144		ret = zynqmp_pm_request_wake(pm_api_arg[0],
145					     pm_api_arg[1], pm_api_arg[2],
146					     pm_api_arg[3] ? pm_api_arg[3] :
147					     ZYNQMP_PM_REQUEST_ACK_NO);
148		break;
149	case PM_SYSTEM_SHUTDOWN:
150		ret = zynqmp_pm_system_shutdown(pm_api_arg[0], pm_api_arg[1]);
151		break;
152	case PM_REQUEST_NODE:
153		ret = zynqmp_pm_request_node(pm_api_arg[0],
154					     pm_api_arg[1] ? pm_api_arg[1] :
155					     ZYNQMP_PM_CAPABILITY_ACCESS,
156					     pm_api_arg[2] ? pm_api_arg[2] : 0,
157					     pm_api_arg[3] ? pm_api_arg[3] :
158					     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
159		break;
160	case PM_RELEASE_NODE:
161		ret = zynqmp_pm_release_node(pm_api_arg[0]);
162		break;
163	case PM_SET_REQUIREMENT:
164		ret = zynqmp_pm_set_requirement(pm_api_arg[0],
165						pm_api_arg[1] ? pm_api_arg[1] :
166						ZYNQMP_PM_CAPABILITY_CONTEXT,
167						pm_api_arg[2] ?
168						pm_api_arg[2] : 0,
169						pm_api_arg[3] ? pm_api_arg[3] :
170						ZYNQMP_PM_REQUEST_ACK_BLOCKING);
171		break;
172	case PM_GET_NODE_STATUS:
173		ret = zynqmp_pm_get_node_status(pm_api_arg[0],
174						&pm_api_ret[0],
175						&pm_api_ret[1],
176						&pm_api_ret[2]);
177		if (!ret)
178			sprintf(debugfs_buf,
179				"GET_NODE_STATUS:\n\tNodeId: %llu\n\tStatus: %u\n\tRequirements: %u\n\tUsage: %u\n",
180				pm_api_arg[0], pm_api_ret[0],
181				pm_api_ret[1], pm_api_ret[2]);
182		break;
183	case PM_REGISTER_NOTIFIER:
184		ret = zynqmp_pm_register_notifier(pm_api_arg[0],
185						  pm_api_arg[1] ?
186						  pm_api_arg[1] : 0,
187						  pm_api_arg[2] ?
188						  pm_api_arg[2] : 0,
189						  pm_api_arg[3] ?
190						  pm_api_arg[3] : 0);
191		break;
192	case PM_RESET_ASSERT:
193		ret = zynqmp_pm_reset_assert(pm_api_arg[0], pm_api_arg[1]);
194		break;
195	case PM_RESET_GET_STATUS:
196		ret = zynqmp_pm_reset_get_status(pm_api_arg[0], &pm_api_ret[0]);
197		if (!ret)
198			sprintf(debugfs_buf, "Reset status: %u\n",
199				pm_api_ret[0]);
200		break;
201	case PM_GET_CHIPID:
202		ret = zynqmp_pm_get_chipid(&pm_api_ret[0], &pm_api_ret[1]);
203		if (!ret)
204			sprintf(debugfs_buf, "Idcode: %#x, Version:%#x\n",
205				pm_api_ret[0], pm_api_ret[1]);
206		break;
207	case PM_PINCTRL_SET_FUNCTION:
208		ret = zynqmp_pm_pinctrl_set_function(pm_api_arg[0],
209						     pm_api_arg[1]);
210		break;
211	case PM_PINCTRL_CONFIG_PARAM_GET:
212		ret = zynqmp_pm_pinctrl_get_config(pm_api_arg[0], pm_api_arg[1],
213						   &pm_api_ret[0]);
214		if (!ret)
215			sprintf(debugfs_buf,
216				"Pin: %llu, Param: %llu, Value: %u\n",
217				pm_api_arg[0], pm_api_arg[1],
218				pm_api_ret[0]);
219		break;
220	case PM_PINCTRL_CONFIG_PARAM_SET:
221		ret = zynqmp_pm_pinctrl_set_config(pm_api_arg[0],
222						   pm_api_arg[1],
223						   pm_api_arg[2]);
224		break;
225	case PM_IOCTL:
226		ret = zynqmp_pm_ioctl(pm_api_arg[0], pm_api_arg[1],
227				      pm_api_arg[2], pm_api_arg[3],
228				      pm_api_arg[4], &pm_api_ret[0]);
229		if (!ret && (pm_api_arg[1] == IOCTL_GET_RPU_OPER_MODE ||
230			     pm_api_arg[1] == IOCTL_GET_PLL_FRAC_MODE ||
231			     pm_api_arg[1] == IOCTL_GET_PLL_FRAC_DATA ||
232			     pm_api_arg[1] == IOCTL_READ_GGS ||
233			     pm_api_arg[1] == IOCTL_READ_PGGS ||
234			     pm_api_arg[1] == IOCTL_READ_REG))
235			sprintf(debugfs_buf, "IOCTL return value: %u\n",
236				pm_api_ret[1]);
237		if (!ret && pm_api_arg[1] == IOCTL_GET_QOS)
238			sprintf(debugfs_buf, "Default QoS: %u\nCurrent QoS: %u\n",
239				pm_api_ret[1], pm_api_ret[2]);
240		break;
241	case PM_CLOCK_ENABLE:
242		ret = zynqmp_pm_clock_enable(pm_api_arg[0]);
243		break;
244	case PM_CLOCK_DISABLE:
245		ret = zynqmp_pm_clock_disable(pm_api_arg[0]);
246		break;
247	case PM_CLOCK_GETSTATE:
248		ret = zynqmp_pm_clock_getstate(pm_api_arg[0], &pm_api_ret[0]);
249		if (!ret)
250			sprintf(debugfs_buf, "Clock state: %u\n",
251				pm_api_ret[0]);
252		break;
253	case PM_CLOCK_SETDIVIDER:
254		ret = zynqmp_pm_clock_setdivider(pm_api_arg[0], pm_api_arg[1]);
255		break;
256	case PM_CLOCK_GETDIVIDER:
257		ret = zynqmp_pm_clock_getdivider(pm_api_arg[0], &pm_api_ret[0]);
258		if (!ret)
259			sprintf(debugfs_buf, "Divider Value: %d\n",
260				pm_api_ret[0]);
261		break;
262	case PM_CLOCK_SETPARENT:
263		ret = zynqmp_pm_clock_setparent(pm_api_arg[0], pm_api_arg[1]);
264		break;
265	case PM_CLOCK_GETPARENT:
266		ret = zynqmp_pm_clock_getparent(pm_api_arg[0], &pm_api_ret[0]);
267		if (!ret)
268			sprintf(debugfs_buf,
269				"Clock parent Index: %u\n", pm_api_ret[0]);
270		break;
271	case PM_QUERY_DATA:
272		qdata.qid = pm_api_arg[0];
273		qdata.arg1 = pm_api_arg[1];
274		qdata.arg2 = pm_api_arg[2];
275		qdata.arg3 = pm_api_arg[3];
276
277		ret = zynqmp_pm_query_data(qdata, pm_api_ret);
278		if (ret)
279			break;
280
281		switch (qdata.qid) {
282		case PM_QID_CLOCK_GET_NAME:
283			sprintf(debugfs_buf, "Clock name = %s\n",
284				(char *)pm_api_ret);
285			break;
286		case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS:
287			sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",
288				pm_api_ret[1], pm_api_ret[2]);
289			break;
290		default:
291			sprintf(debugfs_buf,
292				"data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",
293				pm_api_ret[0], pm_api_ret[1],
294				pm_api_ret[2], pm_api_ret[3]);
295		}
296		break;
297	default:
298		sprintf(debugfs_buf, "Unsupported PM-API request\n");
299		ret = -EINVAL;
300	}
301
302	return ret;
303}
304
305/**
306 * zynqmp_pm_debugfs_api_write() - debugfs write function
307 * @file:	User file
308 * @ptr:	User entered PM-API string
309 * @len:	Length of the userspace buffer
310 * @off:	Offset within the file
311 *
312 * Used for triggering pm api functions by writing
313 * echo <pm_api_id>	> /sys/kernel/debug/zynqmp_pm/power or
314 * echo <pm_api_name>	> /sys/kernel/debug/zynqmp_pm/power
315 *
316 * Return: Number of bytes copied if PM-API request succeeds,
317 *	   the corresponding error code otherwise
318 */
319static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
320					   const char __user *ptr, size_t len,
321					   loff_t *off)
322{
323	char *kern_buff, *tmp_buff;
324	char *pm_api_req;
325	u32 pm_id = 0;
326	u64 pm_api_arg[5] = {0, 0, 0, 0, 0};
327	/* Return values from PM APIs calls */
328	u32 pm_api_ret[4] = {0, 0, 0, 0};
329
330	int ret;
331	int i = 0;
332
333	strcpy(debugfs_buf, "");
334
335	if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1)
336		return -EINVAL;
337
338	kern_buff = memdup_user_nul(ptr, len);
339	if (IS_ERR(kern_buff))
340		return PTR_ERR(kern_buff);
341	tmp_buff = kern_buff;
342
343	/* Read the API name from a user request */
344	pm_api_req = strsep(&kern_buff, " ");
345
346	ret = get_pm_api_id(pm_api_req, &pm_id);
347	if (ret < 0)
348		goto err;
349
350	/* Read node_id and arguments from the PM-API request */
351	pm_api_req = strsep(&kern_buff, " ");
352	while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
353		pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
354		pm_api_req = strsep(&kern_buff, " ");
355	}
356
357	ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);
358
359err:
360	kfree(tmp_buff);
361	if (ret)
362		return ret;
363
364	return len;
365}
366
367/**
368 * zynqmp_pm_debugfs_api_read() - debugfs read function
369 * @file:	User file
370 * @ptr:	Requested pm_api_version string
371 * @len:	Length of the userspace buffer
372 * @off:	Offset within the file
373 *
374 * Return: Length of the version string on success
375 *	   else error code
376 */
377static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,
378					  size_t len, loff_t *off)
379{
380	return simple_read_from_buffer(ptr, len, off, debugfs_buf,
381				       strlen(debugfs_buf));
382}
383
384/* Setup debugfs fops */
385static const struct file_operations fops_zynqmp_pm_dbgfs = {
386	.owner = THIS_MODULE,
387	.write = zynqmp_pm_debugfs_api_write,
388	.read = zynqmp_pm_debugfs_api_read,
389};
390
391/**
392 * zynqmp_pm_api_debugfs_init - Initialize debugfs interface
393 *
394 * Return:	None
395 */
396void zynqmp_pm_api_debugfs_init(void)
397{
398	/* Initialize debugfs interface */
399	firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL);
400	debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL,
401			    &fops_zynqmp_pm_dbgfs);
402}
403
404/**
405 * zynqmp_pm_api_debugfs_exit - Remove debugfs interface
406 *
407 * Return:	None
408 */
409void zynqmp_pm_api_debugfs_exit(void)
410{
411	debugfs_remove_recursive(firmware_debugfs_root);
412}
~/ Read-only file preview.