1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/compiler.h>
14#include <linux/module.h>
15#include <linux/slab.h>
16#include <linux/debugfs.h>
17#include <linux/uaccess.h>
18
19#include <linux/firmware/xlnx-zynqmp.h>
20#include "zynqmp-debug.h"
21
22#define PM_API_NAME_LEN 50
23
24struct pm_api_info {
25 u32 api_id;
26 char api_name[PM_API_NAME_LEN];
27 char api_name_len;
28};
29
30static char debugfs_buf[PAGE_SIZE];
31
32#define PM_API(id) {id, #id, strlen(#id)}
33static struct pm_api_info pm_api_list[] = {
34 PM_API(PM_REQUEST_SUSPEND),
35 PM_API(PM_SELF_SUSPEND),
36 PM_API(PM_FORCE_POWERDOWN),
37 PM_API(PM_ABORT_SUSPEND),
38 PM_API(PM_REQUEST_WAKEUP),
39 PM_API(PM_SET_WAKEUP_SOURCE),
40 PM_API(PM_SYSTEM_SHUTDOWN),
41 PM_API(PM_REQUEST_NODE),
42 PM_API(PM_RELEASE_NODE),
43 PM_API(PM_SET_REQUIREMENT),
44 PM_API(PM_SET_MAX_LATENCY),
45 PM_API(PM_GET_API_VERSION),
46 PM_API(PM_SET_CONFIGURATION),
47 PM_API(PM_GET_NODE_STATUS),
48 PM_API(PM_GET_OPERATING_CHARACTERISTIC),
49 PM_API(PM_REGISTER_NOTIFIER),
50 PM_API(PM_RESET_ASSERT),
51 PM_API(PM_RESET_GET_STATUS),
52 PM_API(PM_GET_CHIPID),
53 PM_API(PM_PINCTRL_GET_FUNCTION),
54 PM_API(PM_PINCTRL_SET_FUNCTION),
55 PM_API(PM_PINCTRL_CONFIG_PARAM_GET),
56 PM_API(PM_PINCTRL_CONFIG_PARAM_SET),
57 PM_API(PM_IOCTL),
58 PM_API(PM_CLOCK_ENABLE),
59 PM_API(PM_CLOCK_DISABLE),
60 PM_API(PM_CLOCK_GETSTATE),
61 PM_API(PM_CLOCK_SETDIVIDER),
62 PM_API(PM_CLOCK_GETDIVIDER),
63 PM_API(PM_CLOCK_SETRATE),
64 PM_API(PM_CLOCK_GETRATE),
65 PM_API(PM_CLOCK_SETPARENT),
66 PM_API(PM_CLOCK_GETPARENT),
67 PM_API(PM_QUERY_DATA),
68};
69
70struct dentry *firmware_debugfs_root;
71
72
73
74
75
76
77
78
79
80static int zynqmp_pm_self_suspend(const u32 node, const u32 latency,
81 const u32 state)
82{
83 return zynqmp_pm_invoke_fn(PM_SELF_SUSPEND, node, latency,
84 state, 0, NULL);
85}
86
87
88
89
90
91
92
93
94static int zynqmp_pm_abort_suspend(const enum zynqmp_pm_abort_reason reason)
95{
96 return zynqmp_pm_invoke_fn(PM_ABORT_SUSPEND, reason, 0, 0, 0, NULL);
97}
98
99
100
101
102
103
104
105
106
107
108static int zynqmp_pm_register_notifier(const u32 node, const u32 event,
109 const u32 wake, const u32 enable)
110{
111 return zynqmp_pm_invoke_fn(PM_REGISTER_NOTIFIER, node, event,
112 wake, enable, NULL);
113}
114
115
116
117
118
119
120
121
122static u64 zynqmp_pm_argument_value(char *arg)
123{
124 u64 value;
125
126 if (!arg)
127 return 0;
128
129 if (!kstrtou64(arg, 0, &value))
130 return value;
131
132 return 0;
133}
134
135
136
137
138
139
140
141
142static int get_pm_api_id(char *pm_api_req, u32 *pm_id)
143{
144 int i;
145
146 for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) {
147 if (!strncasecmp(pm_api_req, pm_api_list[i].api_name,
148 pm_api_list[i].api_name_len)) {
149 *pm_id = pm_api_list[i].api_id;
150 break;
151 }
152 }
153
154
155 if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id))
156 return -EINVAL;
157
158 return 0;
159}
160
161static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
162{
163 const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
164 u32 pm_api_version;
165 u64 rate;
166 int ret;
167 struct zynqmp_pm_query_data qdata = {0};
168
169 switch (pm_id) {
170 case PM_GET_API_VERSION:
171 ret = eemi_ops->get_api_version(&pm_api_version);
172 sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
173 pm_api_version >> 16, pm_api_version & 0xffff);
174 break;
175 case PM_REQUEST_SUSPEND:
176 ret = eemi_ops->request_suspend(pm_api_arg[0],
177 pm_api_arg[1] ? pm_api_arg[1] :
178 ZYNQMP_PM_REQUEST_ACK_NO,
179 pm_api_arg[2] ? pm_api_arg[2] :
180 ZYNQMP_PM_MAX_LATENCY, 0);
181 break;
182 case PM_SELF_SUSPEND:
183 ret = zynqmp_pm_self_suspend(pm_api_arg[0],
184 pm_api_arg[1] ? pm_api_arg[1] :
185 ZYNQMP_PM_MAX_LATENCY, 0);
186 break;
187 case PM_FORCE_POWERDOWN:
188 ret = eemi_ops->force_powerdown(pm_api_arg[0],
189 pm_api_arg[1] ? pm_api_arg[1] :
190 ZYNQMP_PM_REQUEST_ACK_NO);
191 break;
192 case PM_ABORT_SUSPEND:
193 ret = zynqmp_pm_abort_suspend(pm_api_arg[0] ? pm_api_arg[0] :
194 ZYNQMP_PM_ABORT_REASON_UNKNOWN);
195 break;
196 case PM_REQUEST_WAKEUP:
197 ret = eemi_ops->request_wakeup(pm_api_arg[0],
198 pm_api_arg[1], pm_api_arg[2],
199 pm_api_arg[3] ? pm_api_arg[3] :
200 ZYNQMP_PM_REQUEST_ACK_NO);
201 break;
202 case PM_SET_WAKEUP_SOURCE:
203 ret = eemi_ops->set_wakeup_source(pm_api_arg[0], pm_api_arg[1],
204 pm_api_arg[2]);
205 break;
206 case PM_SYSTEM_SHUTDOWN:
207 ret = eemi_ops->system_shutdown(pm_api_arg[0], pm_api_arg[1]);
208 break;
209 case PM_REQUEST_NODE:
210 ret = eemi_ops->request_node(pm_api_arg[0],
211 pm_api_arg[1] ? pm_api_arg[1] :
212 ZYNQMP_PM_CAPABILITY_ACCESS,
213 pm_api_arg[2] ? pm_api_arg[2] : 0,
214 pm_api_arg[3] ? pm_api_arg[3] :
215 ZYNQMP_PM_REQUEST_ACK_BLOCKING);
216 break;
217 case PM_RELEASE_NODE:
218 ret = eemi_ops->release_node(pm_api_arg[0]);
219 break;
220 case PM_SET_REQUIREMENT:
221 ret = eemi_ops->set_requirement(pm_api_arg[0],
222 pm_api_arg[1] ? pm_api_arg[1] :
223 ZYNQMP_PM_CAPABILITY_CONTEXT,
224 pm_api_arg[2] ?
225 pm_api_arg[2] : 0,
226 pm_api_arg[3] ? pm_api_arg[3] :
227 ZYNQMP_PM_REQUEST_ACK_BLOCKING);
228 break;
229 case PM_SET_MAX_LATENCY:
230 ret = eemi_ops->set_max_latency(pm_api_arg[0],
231 pm_api_arg[1] ? pm_api_arg[1] :
232 ZYNQMP_PM_MAX_LATENCY);
233 break;
234 case PM_SET_CONFIGURATION:
235 ret = eemi_ops->set_configuration(pm_api_arg[0]);
236 break;
237 case PM_GET_NODE_STATUS:
238 ret = eemi_ops->get_node_status(pm_api_arg[0],
239 &pm_api_ret[0],
240 &pm_api_ret[1],
241 &pm_api_ret[2]);
242 if (!ret)
243 sprintf(debugfs_buf,
244 "GET_NODE_STATUS:\n\tNodeId: %llu\n\tStatus: %u\n\tRequirements: %u\n\tUsage: %u\n",
245 pm_api_arg[0], pm_api_ret[0],
246 pm_api_ret[1], pm_api_ret[2]);
247 break;
248 case PM_GET_OPERATING_CHARACTERISTIC:
249 ret = eemi_ops->get_operating_characteristic(pm_api_arg[0],
250 pm_api_arg[1] ? pm_api_arg[1] :
251 ZYNQMP_PM_OPERATING_CHARACTERISTIC_POWER,
252 &pm_api_ret[0]);
253 if (!ret)
254 sprintf(debugfs_buf,
255 "GET_OPERATING_CHARACTERISTIC:\n\tNodeId: %llu\n\tType: %llu\n\tResult: %u\n",
256 pm_api_arg[0], pm_api_arg[1],
257 pm_api_ret[0]);
258 break;
259 case PM_REGISTER_NOTIFIER:
260 ret = zynqmp_pm_register_notifier(pm_api_arg[0],
261 pm_api_arg[1] ?
262 pm_api_arg[1] : 0,
263 pm_api_arg[2] ?
264 pm_api_arg[2] : 0,
265 pm_api_arg[3] ?
266 pm_api_arg[3] : 0);
267 break;
268 case PM_RESET_ASSERT:
269 ret = eemi_ops->reset_assert(pm_api_arg[0], pm_api_arg[1]);
270 break;
271 case PM_RESET_GET_STATUS:
272 ret = eemi_ops->reset_get_status(pm_api_arg[0], &pm_api_ret[0]);
273 if (!ret)
274 sprintf(debugfs_buf, "Reset status: %u\n",
275 pm_api_ret[0]);
276 break;
277 case PM_GET_CHIPID:
278 ret = eemi_ops->get_chipid(&pm_api_ret[0], &pm_api_ret[1]);
279 if (!ret)
280 sprintf(debugfs_buf, "Idcode: %#x, Version:%#x\n",
281 pm_api_ret[0], pm_api_ret[1]);
282 break;
283 case PM_PINCTRL_GET_FUNCTION:
284 ret = eemi_ops->pinctrl_get_function(pm_api_arg[0],
285 &pm_api_ret[0]);
286 if (!ret)
287 sprintf(debugfs_buf,
288 "Current set function for the pin: %u\n",
289 pm_api_ret[0]);
290 break;
291 case PM_PINCTRL_SET_FUNCTION:
292 ret = eemi_ops->pinctrl_set_function(pm_api_arg[0],
293 pm_api_arg[1]);
294 break;
295 case PM_PINCTRL_CONFIG_PARAM_GET:
296 ret = eemi_ops->pinctrl_get_config(pm_api_arg[0], pm_api_arg[1],
297 &pm_api_ret[0]);
298 if (!ret)
299 sprintf(debugfs_buf,
300 "Pin: %llu, Param: %llu, Value: %u\n",
301 pm_api_arg[0], pm_api_arg[1],
302 pm_api_ret[0]);
303 break;
304 case PM_PINCTRL_CONFIG_PARAM_SET:
305 ret = eemi_ops->pinctrl_set_config(pm_api_arg[0],
306 pm_api_arg[1],
307 pm_api_arg[2]);
308 break;
309 case PM_IOCTL:
310 ret = eemi_ops->ioctl(pm_api_arg[0], pm_api_arg[1],
311 pm_api_arg[2], pm_api_arg[3],
312 &pm_api_ret[0]);
313 if (!ret && (pm_api_arg[1] == IOCTL_GET_RPU_OPER_MODE ||
314 pm_api_arg[1] == IOCTL_GET_PLL_FRAC_MODE ||
315 pm_api_arg[1] == IOCTL_GET_PLL_FRAC_DATA ||
316 pm_api_arg[1] == IOCTL_READ_GGS ||
317 pm_api_arg[1] == IOCTL_READ_PGGS ||
318 pm_api_arg[1] == IOCTL_PROBE_COUNTER_READ))
319 sprintf(debugfs_buf, "IOCTL return value: %u\n",
320 pm_api_ret[1]);
321 break;
322 case PM_CLOCK_ENABLE:
323 ret = eemi_ops->clock_enable(pm_api_arg[0]);
324 break;
325 case PM_CLOCK_DISABLE:
326 ret = eemi_ops->clock_disable(pm_api_arg[0]);
327 break;
328 case PM_CLOCK_GETSTATE:
329 ret = eemi_ops->clock_getstate(pm_api_arg[0], &pm_api_ret[0]);
330 if (!ret)
331 sprintf(debugfs_buf, "Clock state: %u\n",
332 pm_api_ret[0]);
333 break;
334 case PM_CLOCK_SETDIVIDER:
335 ret = eemi_ops->clock_setdivider(pm_api_arg[0], pm_api_arg[1]);
336 break;
337 case PM_CLOCK_GETDIVIDER:
338 ret = eemi_ops->clock_getdivider(pm_api_arg[0], &pm_api_ret[0]);
339 if (!ret)
340 sprintf(debugfs_buf, "Divider Value: %d\n",
341 pm_api_ret[0]);
342 break;
343 case PM_CLOCK_SETRATE:
344 ret = eemi_ops->clock_setrate(pm_api_arg[0], pm_api_arg[1]);
345 break;
346 case PM_CLOCK_GETRATE:
347 ret = eemi_ops->clock_getrate(pm_api_arg[0], &rate);
348 if (!ret)
349 sprintf(debugfs_buf, "Clock rate :%llu\n", rate);
350 break;
351 case PM_CLOCK_SETPARENT:
352 ret = eemi_ops->clock_setparent(pm_api_arg[0], pm_api_arg[1]);
353 break;
354 case PM_CLOCK_GETPARENT:
355 ret = eemi_ops->clock_getparent(pm_api_arg[0], &pm_api_ret[0]);
356 if (!ret)
357 sprintf(debugfs_buf,
358 "Clock parent Index: %u\n", pm_api_ret[0]);
359 break;
360 case PM_QUERY_DATA:
361 qdata.qid = pm_api_arg[0];
362 qdata.arg1 = pm_api_arg[1];
363 qdata.arg2 = pm_api_arg[2];
364 qdata.arg3 = pm_api_arg[3];
365
366 ret = eemi_ops->query_data(qdata, pm_api_ret);
367 if (ret)
368 break;
369
370 switch (qdata.qid) {
371 case PM_QID_CLOCK_GET_NAME:
372 sprintf(debugfs_buf, "Clock name = %s\n",
373 (char *)pm_api_ret);
374 break;
375 case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS:
376 sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",
377 pm_api_ret[1], pm_api_ret[2]);
378 break;
379 default:
380 sprintf(debugfs_buf,
381 "data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",
382 pm_api_ret[0], pm_api_ret[1],
383 pm_api_ret[2], pm_api_ret[3]);
384 }
385 break;
386 default:
387 sprintf(debugfs_buf, "Unsupported PM-API request\n");
388 ret = -EINVAL;
389 }
390
391 return ret;
392}
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
409 const char __user *ptr, size_t len,
410 loff_t *off)
411{
412 char *kern_buff, *tmp_buff;
413 char *pm_api_req;
414 u32 pm_id = 0;
415 u64 pm_api_arg[4] = {0, 0, 0, 0};
416
417 u32 pm_api_ret[4] = {0, 0, 0, 0};
418
419 int ret;
420 int i = 0;
421
422 strcpy(debugfs_buf, "");
423
424 if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1)
425 return -EINVAL;
426
427 kern_buff = memdup_user_nul(ptr, len);
428 if (IS_ERR(kern_buff))
429 return PTR_ERR(kern_buff);
430 tmp_buff = kern_buff;
431
432
433 pm_api_req = strsep(&kern_buff, " ");
434
435 ret = get_pm_api_id(pm_api_req, &pm_id);
436 if (ret < 0)
437 goto err;
438
439
440 pm_api_req = strsep(&kern_buff, " ");
441 while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
442 pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
443 pm_api_req = strsep(&kern_buff, " ");
444 }
445
446 ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);
447
448err:
449 kfree(tmp_buff);
450 if (ret)
451 return ret;
452
453 return len;
454}
455
456
457
458
459
460
461
462
463
464
465
466static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,
467 size_t len, loff_t *off)
468{
469 return simple_read_from_buffer(ptr, len, off, debugfs_buf,
470 strlen(debugfs_buf));
471}
472
473
474static const struct file_operations fops_zynqmp_pm_dbgfs = {
475 .owner = THIS_MODULE,
476 .write = zynqmp_pm_debugfs_api_write,
477 .read = zynqmp_pm_debugfs_api_read,
478};
479
480
481
482
483
484
485void zynqmp_pm_api_debugfs_init(void)
486{
487
488 firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL);
489 debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL,
490 &fops_zynqmp_pm_dbgfs);
491}
492
493
494
495
496
497
498void zynqmp_pm_api_debugfs_exit(void)
499{
500 debugfs_remove_recursive(firmware_debugfs_root);
501}
502