cs40l26-debugfs.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // SPDX-License-Identifier: GPL-2.0
  2. //
  3. // cs40l26-debugfs.c -- CS40L26 Boosted Haptic Driver with Integrated DSP and
  4. // Waveform Memory with Advanced Closed Loop Algorithms and LRA protection
  5. //
  6. // Copyright 2022 Cirrus Logic, Inc.
  7. //
  8. // Author: Fred Treven <[email protected]>
  9. //
  10. // This program is free software; you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License version 2 as
  12. // published by the Free Software Foundation.
  13. #ifdef CONFIG_CS40L26_SAMSUNG_FEATURE
  14. #include <linux/vibrator/cs40l26.h>
  15. #else
  16. #include <linux/mfd/cs40l26.h>
  17. #endif
  18. #ifdef CONFIG_DEBUG_FS
  19. static ssize_t cs40l26_fw_ctrl_name_read(struct file *file, char __user *user_buf, size_t count,
  20. loff_t *ppos)
  21. {
  22. struct cs40l26_private *cs40l26 = file->private_data;
  23. ssize_t error = 0;
  24. mutex_lock(&cs40l26->lock);
  25. if (cs40l26->dbg_fw_ctrl_name)
  26. error = simple_read_from_buffer(user_buf, count, ppos, cs40l26->dbg_fw_ctrl_name,
  27. strlen(cs40l26->dbg_fw_ctrl_name));
  28. mutex_unlock(&cs40l26->lock);
  29. return error;
  30. }
  31. static ssize_t cs40l26_fw_ctrl_name_write(struct file *file, const char __user *user_buf,
  32. size_t count, loff_t *ppos)
  33. {
  34. struct cs40l26_private *cs40l26 = file->private_data;
  35. ssize_t error = 0;
  36. mutex_lock(&cs40l26->lock);
  37. kfree(cs40l26->dbg_fw_ctrl_name);
  38. cs40l26->dbg_fw_ctrl_name = NULL;
  39. cs40l26->dbg_fw_ctrl_name = kzalloc(count, GFP_KERNEL);
  40. if (!cs40l26->dbg_fw_ctrl_name) {
  41. error = -ENOMEM;
  42. goto err_mutex;
  43. }
  44. error = simple_write_to_buffer(cs40l26->dbg_fw_ctrl_name, count, ppos, user_buf, count);
  45. err_mutex:
  46. mutex_unlock(&cs40l26->lock);
  47. return error ? error : count;
  48. }
  49. static ssize_t cs40l26_fw_algo_id_read(struct file *file, char __user *user_buf, size_t count,
  50. loff_t *ppos)
  51. {
  52. struct cs40l26_private *cs40l26 = file->private_data;
  53. ssize_t error;
  54. char *str;
  55. str = kzalloc(CS40L26_ALGO_ID_MAX_STR_LEN, GFP_KERNEL);
  56. if (!str)
  57. return -ENOMEM;
  58. mutex_lock(&cs40l26->lock);
  59. snprintf(str, count, "0x%06X\n", cs40l26->dbg_fw_algo_id);
  60. mutex_unlock(&cs40l26->lock);
  61. error = simple_read_from_buffer(user_buf, count, ppos, str, strlen(str));
  62. kfree(str);
  63. return error;
  64. }
  65. static ssize_t cs40l26_fw_algo_id_write(struct file *file, const char __user *user_buf,
  66. size_t count, loff_t *ppos)
  67. {
  68. struct cs40l26_private *cs40l26 = file->private_data;
  69. ssize_t error;
  70. char *str;
  71. u32 val;
  72. str = kzalloc(count, GFP_KERNEL);
  73. if (!str)
  74. return -ENOMEM;
  75. simple_write_to_buffer(str, count, ppos, user_buf, count);
  76. error = kstrtou32(str, 16, &val);
  77. if (error)
  78. goto exit_free;
  79. mutex_lock(&cs40l26->lock);
  80. cs40l26->dbg_fw_algo_id = val;
  81. mutex_unlock(&cs40l26->lock);
  82. exit_free:
  83. kfree(str);
  84. return error ? error : count;
  85. }
  86. static ssize_t cs40l26_fw_ctrl_val_read(struct file *file, char __user *user_buf, size_t count,
  87. loff_t *ppos)
  88. {
  89. struct cs40l26_private *cs40l26 = file->private_data;
  90. u32 reg, val, mem_type;
  91. char *result, *input;
  92. ssize_t error;
  93. if (!cs40l26->dbg_fw_ctrl_name || !cs40l26->dbg_fw_algo_id)
  94. return -ENODEV;
  95. if (strlen(cs40l26->dbg_fw_ctrl_name) == 0)
  96. return -ENODATA;
  97. error = pm_runtime_get_sync(cs40l26->dev);
  98. if (error < 0) {
  99. cs40l26_resume_error_handle(cs40l26->dev, (int) error);
  100. return error;
  101. }
  102. mutex_lock(&cs40l26->lock);
  103. mem_type = cs40l26->dbg_fw_ym ? CL_DSP_YM_UNPACKED_TYPE : CL_DSP_XM_UNPACKED_TYPE;
  104. input = kzalloc(strlen(cs40l26->dbg_fw_ctrl_name), GFP_KERNEL);
  105. if (!input) {
  106. error = -ENOMEM;
  107. goto err_mutex;
  108. }
  109. snprintf(input, strlen(cs40l26->dbg_fw_ctrl_name), "%s", cs40l26->dbg_fw_ctrl_name);
  110. error = cl_dsp_get_reg(cs40l26->dsp, input, mem_type, cs40l26->dbg_fw_algo_id, &reg);
  111. kfree(input);
  112. if (error)
  113. goto err_mutex;
  114. error = regmap_read(cs40l26->regmap, reg, &val);
  115. if (error) {
  116. dev_err(cs40l26->dev, "Failed to read fw control\n");
  117. goto err_mutex;
  118. }
  119. result = kzalloc(CS40L26_ALGO_ID_MAX_STR_LEN, GFP_KERNEL);
  120. if (!result) {
  121. error = -ENOMEM;
  122. goto err_mutex;
  123. }
  124. snprintf(result, CS40L26_ALGO_ID_MAX_STR_LEN, "0x%08X\n", val);
  125. error = simple_read_from_buffer(user_buf, count, ppos, result, strlen(result));
  126. kfree(result);
  127. err_mutex:
  128. mutex_unlock(&cs40l26->lock);
  129. pm_runtime_mark_last_busy(cs40l26->dev);
  130. pm_runtime_put_autosuspend(cs40l26->dev);
  131. return error;
  132. }
  133. static const struct {
  134. const char *name;
  135. const struct file_operations fops;
  136. } cs40l26_debugfs_fops[] = {
  137. {
  138. .name = "fw_ctrl_name",
  139. .fops = {
  140. .open = simple_open,
  141. .read = cs40l26_fw_ctrl_name_read,
  142. .write = cs40l26_fw_ctrl_name_write,
  143. },
  144. },
  145. {
  146. .name = "fw_algo_id",
  147. .fops = {
  148. .open = simple_open,
  149. .read = cs40l26_fw_algo_id_read,
  150. .write = cs40l26_fw_algo_id_write,
  151. },
  152. },
  153. {
  154. .name = "fw_ctrl_val",
  155. .fops = {
  156. .open = simple_open,
  157. .read = cs40l26_fw_ctrl_val_read,
  158. },
  159. },
  160. };
  161. void cs40l26_debugfs_init(struct cs40l26_private *cs40l26)
  162. {
  163. struct dentry *root = NULL;
  164. int i;
  165. cs40l26_debugfs_cleanup(cs40l26);
  166. root = debugfs_create_dir("cs40l26", NULL);
  167. if (!root)
  168. return;
  169. debugfs_create_bool("fw_ym_space", CL_DSP_DEBUGFS_RW_FILE_MODE, root, &cs40l26->dbg_fw_ym);
  170. for (i = 0; i < CS40L26_NUM_DEBUGFS; i++)
  171. debugfs_create_file(cs40l26_debugfs_fops[i].name, CL_DSP_DEBUGFS_RW_FILE_MODE,
  172. root, cs40l26, &cs40l26_debugfs_fops[i].fops);
  173. cs40l26->dbg_fw_ym = false;
  174. cs40l26->dbg_fw_algo_id = CS40L26_VIBEGEN_ALGO_ID;
  175. cs40l26->debugfs_root = root;
  176. if (cs40l26->fw_id == CS40L26_FW_ID &&
  177. cl_dsp_algo_is_present(cs40l26->dsp, CS40L26_EVENT_LOGGER_ALGO_ID)) {
  178. cs40l26->cl_dsp_db = cl_dsp_debugfs_create(cs40l26->dsp, cs40l26->debugfs_root,
  179. (u32) CS40L26_EVENT_LOGGER_ALGO_ID);
  180. if (IS_ERR(cs40l26->cl_dsp_db) || !cs40l26->cl_dsp_db)
  181. dev_err(cs40l26->dev, "Failed to create CL DSP Debugfs\n");
  182. }
  183. }
  184. EXPORT_SYMBOL_GPL(cs40l26_debugfs_init);
  185. void cs40l26_debugfs_cleanup(struct cs40l26_private *cs40l26)
  186. {
  187. cl_dsp_debugfs_destroy(cs40l26->cl_dsp_db);
  188. cs40l26->cl_dsp_db = NULL;
  189. kfree(cs40l26->dbg_fw_ctrl_name);
  190. cs40l26->dbg_fw_ctrl_name = NULL;
  191. debugfs_remove_recursive(cs40l26->debugfs_root);
  192. }
  193. EXPORT_SYMBOL_GPL(cs40l26_debugfs_cleanup);
  194. #endif /* CONFIG_DEBUG_FS */