zynqmp-debug.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  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. *
  7. * Michal Simek <michal.simek@xilinx.com>
  8. * Davorin Mista <davorin.mista@aggios.com>
  9. * Jolly Shah <jollys@xilinx.com>
  10. * Rajan Vaja <rajanv@xilinx.com>
  11. */
  12. #include <linux/compiler.h>
  13. #include <linux/module.h>
  14. #include <linux/slab.h>
  15. #include <linux/debugfs.h>
  16. #include <linux/uaccess.h>
  17. #include <linux/firmware/xlnx-zynqmp.h>
  18. #include "zynqmp-debug.h"
  19. #define PM_API_NAME_LEN 50
  20. struct pm_api_info {
  21. u32 api_id;
  22. char api_name[PM_API_NAME_LEN];
  23. char api_name_len;
  24. };
  25. static char debugfs_buf[PAGE_SIZE];
  26. #define PM_API(id) {id, #id, strlen(#id)}
  27. static struct pm_api_info pm_api_list[] = {
  28. PM_API(PM_GET_API_VERSION),
  29. PM_API(PM_QUERY_DATA),
  30. };
  31. struct dentry *firmware_debugfs_root;
  32. /**
  33. * zynqmp_pm_argument_value() - Extract argument value from a PM-API request
  34. * @arg: Entered PM-API argument in string format
  35. *
  36. * Return: Argument value in unsigned integer format on success
  37. * 0 otherwise
  38. */
  39. static u64 zynqmp_pm_argument_value(char *arg)
  40. {
  41. u64 value;
  42. if (!arg)
  43. return 0;
  44. if (!kstrtou64(arg, 0, &value))
  45. return value;
  46. return 0;
  47. }
  48. /**
  49. * get_pm_api_id() - Extract API-ID from a PM-API request
  50. * @pm_api_req: Entered PM-API argument in string format
  51. * @pm_id: API-ID
  52. *
  53. * Return: 0 on success else error code
  54. */
  55. static int get_pm_api_id(char *pm_api_req, u32 *pm_id)
  56. {
  57. int i;
  58. for (i = 0; i < ARRAY_SIZE(pm_api_list) ; i++) {
  59. if (!strncasecmp(pm_api_req, pm_api_list[i].api_name,
  60. pm_api_list[i].api_name_len)) {
  61. *pm_id = pm_api_list[i].api_id;
  62. break;
  63. }
  64. }
  65. /* If no name was entered look for PM-API ID instead */
  66. if (i == ARRAY_SIZE(pm_api_list) && kstrtouint(pm_api_req, 10, pm_id))
  67. return -EINVAL;
  68. return 0;
  69. }
  70. static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret)
  71. {
  72. const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
  73. u32 pm_api_version;
  74. int ret;
  75. struct zynqmp_pm_query_data qdata = {0};
  76. if (!eemi_ops)
  77. return -ENXIO;
  78. switch (pm_id) {
  79. case PM_GET_API_VERSION:
  80. ret = eemi_ops->get_api_version(&pm_api_version);
  81. sprintf(debugfs_buf, "PM-API Version = %d.%d\n",
  82. pm_api_version >> 16, pm_api_version & 0xffff);
  83. break;
  84. case PM_QUERY_DATA:
  85. qdata.qid = pm_api_arg[0];
  86. qdata.arg1 = pm_api_arg[1];
  87. qdata.arg2 = pm_api_arg[2];
  88. qdata.arg3 = pm_api_arg[3];
  89. ret = eemi_ops->query_data(qdata, pm_api_ret);
  90. if (ret)
  91. break;
  92. switch (qdata.qid) {
  93. case PM_QID_CLOCK_GET_NAME:
  94. sprintf(debugfs_buf, "Clock name = %s\n",
  95. (char *)pm_api_ret);
  96. break;
  97. case PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS:
  98. sprintf(debugfs_buf, "Multiplier = %d, Divider = %d\n",
  99. pm_api_ret[1], pm_api_ret[2]);
  100. break;
  101. default:
  102. sprintf(debugfs_buf,
  103. "data[0] = 0x%08x\ndata[1] = 0x%08x\n data[2] = 0x%08x\ndata[3] = 0x%08x\n",
  104. pm_api_ret[0], pm_api_ret[1],
  105. pm_api_ret[2], pm_api_ret[3]);
  106. }
  107. break;
  108. default:
  109. sprintf(debugfs_buf, "Unsupported PM-API request\n");
  110. ret = -EINVAL;
  111. }
  112. return ret;
  113. }
  114. /**
  115. * zynqmp_pm_debugfs_api_write() - debugfs write function
  116. * @file: User file
  117. * @ptr: User entered PM-API string
  118. * @len: Length of the userspace buffer
  119. * @off: Offset within the file
  120. *
  121. * Used for triggering pm api functions by writing
  122. * echo <pm_api_id> > /sys/kernel/debug/zynqmp_pm/power or
  123. * echo <pm_api_name> > /sys/kernel/debug/zynqmp_pm/power
  124. *
  125. * Return: Number of bytes copied if PM-API request succeeds,
  126. * the corresponding error code otherwise
  127. */
  128. static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
  129. const char __user *ptr, size_t len,
  130. loff_t *off)
  131. {
  132. char *kern_buff, *tmp_buff;
  133. char *pm_api_req;
  134. u32 pm_id = 0;
  135. u64 pm_api_arg[4] = {0, 0, 0, 0};
  136. /* Return values from PM APIs calls */
  137. u32 pm_api_ret[4] = {0, 0, 0, 0};
  138. int ret;
  139. int i = 0;
  140. strcpy(debugfs_buf, "");
  141. if (*off != 0 || len == 0)
  142. return -EINVAL;
  143. kern_buff = kzalloc(len, GFP_KERNEL);
  144. if (!kern_buff)
  145. return -ENOMEM;
  146. tmp_buff = kern_buff;
  147. ret = strncpy_from_user(kern_buff, ptr, len);
  148. if (ret < 0) {
  149. ret = -EFAULT;
  150. goto err;
  151. }
  152. /* Read the API name from a user request */
  153. pm_api_req = strsep(&kern_buff, " ");
  154. ret = get_pm_api_id(pm_api_req, &pm_id);
  155. if (ret < 0)
  156. goto err;
  157. /* Read node_id and arguments from the PM-API request */
  158. pm_api_req = strsep(&kern_buff, " ");
  159. while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
  160. pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
  161. pm_api_req = strsep(&kern_buff, " ");
  162. }
  163. ret = process_api_request(pm_id, pm_api_arg, pm_api_ret);
  164. err:
  165. kfree(tmp_buff);
  166. if (ret)
  167. return ret;
  168. return len;
  169. }
  170. /**
  171. * zynqmp_pm_debugfs_api_read() - debugfs read function
  172. * @file: User file
  173. * @ptr: Requested pm_api_version string
  174. * @len: Length of the userspace buffer
  175. * @off: Offset within the file
  176. *
  177. * Return: Length of the version string on success
  178. * else error code
  179. */
  180. static ssize_t zynqmp_pm_debugfs_api_read(struct file *file, char __user *ptr,
  181. size_t len, loff_t *off)
  182. {
  183. return simple_read_from_buffer(ptr, len, off, debugfs_buf,
  184. strlen(debugfs_buf));
  185. }
  186. /* Setup debugfs fops */
  187. static const struct file_operations fops_zynqmp_pm_dbgfs = {
  188. .owner = THIS_MODULE,
  189. .write = zynqmp_pm_debugfs_api_write,
  190. .read = zynqmp_pm_debugfs_api_read,
  191. };
  192. /**
  193. * zynqmp_pm_api_debugfs_init - Initialize debugfs interface
  194. *
  195. * Return: None
  196. */
  197. void zynqmp_pm_api_debugfs_init(void)
  198. {
  199. /* Initialize debugfs interface */
  200. firmware_debugfs_root = debugfs_create_dir("zynqmp-firmware", NULL);
  201. debugfs_create_file("pm", 0660, firmware_debugfs_root, NULL,
  202. &fops_zynqmp_pm_dbgfs);
  203. }
  204. /**
  205. * zynqmp_pm_api_debugfs_exit - Remove debugfs interface
  206. *
  207. * Return: None
  208. */
  209. void zynqmp_pm_api_debugfs_exit(void)
  210. {
  211. debugfs_remove_recursive(firmware_debugfs_root);
  212. }