gsi_dbg.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/completion.h>
  6. #include <linux/debugfs.h>
  7. #include <linux/dma-mapping.h>
  8. #include <linux/random.h>
  9. #include <linux/uaccess.h>
  10. #include <linux/msm_gsi.h>
  11. #include "gsi.h"
  12. #include "gsihal.h"
  13. #define GSI_MAX_MSG_LEN 4096
  14. #define TERR(fmt, args...) \
  15. pr_err("%s:%d " fmt, __func__, __LINE__, ## args)
  16. #define TDBG(fmt, args...) \
  17. pr_debug("%s:%d " fmt, __func__, __LINE__, ## args)
  18. #define PRT_STAT(fmt, args...) \
  19. pr_err(fmt, ## args)
  20. static struct dentry *dent;
  21. static char dbg_buff[GSI_MAX_MSG_LEN];
  22. static void *gsi_ipc_logbuf_low;
  23. static void gsi_wq_print_dp_stats(struct work_struct *work);
  24. static DECLARE_DELAYED_WORK(gsi_print_dp_stats_work, gsi_wq_print_dp_stats);
  25. static void gsi_wq_update_dp_stats(struct work_struct *work);
  26. static DECLARE_DELAYED_WORK(gsi_update_dp_stats_work, gsi_wq_update_dp_stats);
  27. static ssize_t gsi_dump_evt(struct file *file,
  28. const char __user *buf, size_t count, loff_t *ppos)
  29. {
  30. u32 arg1;
  31. u32 arg2;
  32. unsigned long missing;
  33. char *sptr, *token;
  34. uint32_t val;
  35. struct gsi_evt_ctx *ctx;
  36. uint16_t i;
  37. if (count >= sizeof(dbg_buff))
  38. return -EINVAL;
  39. missing = copy_from_user(dbg_buff, buf, count);
  40. if (missing)
  41. return -EFAULT;
  42. dbg_buff[count] = '\0';
  43. sptr = dbg_buff;
  44. token = strsep(&sptr, " ");
  45. if (!token)
  46. return -EINVAL;
  47. if (kstrtou32(token, 0, &arg1))
  48. return -EINVAL;
  49. token = strsep(&sptr, " ");
  50. if (!token)
  51. return -EINVAL;
  52. if (kstrtou32(token, 0, &arg2))
  53. return -EINVAL;
  54. TDBG("arg1=%u arg2=%u\n", arg1, arg2);
  55. if (arg1 >= gsi_ctx->max_ev) {
  56. TERR("invalid evt ring id %u\n", arg1);
  57. return -EINVAL;
  58. }
  59. gsi_ctx->per.vote_clk_cb();
  60. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_0,
  61. gsi_ctx->per.ee, arg1);
  62. TERR("EV%2d CTX0 0x%x\n", arg1, val);
  63. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_1,
  64. gsi_ctx->per.ee, arg1);
  65. TERR("EV%2d CTX1 0x%x\n", arg1, val);
  66. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_2,
  67. gsi_ctx->per.ee, arg1);
  68. TERR("EV%2d CTX2 0x%x\n", arg1, val);
  69. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_3,
  70. gsi_ctx->per.ee, arg1);
  71. TERR("EV%2d CTX3 0x%x\n", arg1, val);
  72. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_4,
  73. gsi_ctx->per.ee, arg1);
  74. TERR("EV%2d CTX4 0x%x\n", arg1, val);
  75. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_5,
  76. gsi_ctx->per.ee, arg1);
  77. TERR("EV%2d CTX5 0x%x\n", arg1, val);
  78. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_6,
  79. gsi_ctx->per.ee, arg1);
  80. TERR("EV%2d CTX6 0x%x\n", arg1, val);
  81. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_7,
  82. gsi_ctx->per.ee, arg1);
  83. TERR("EV%2d CTX7 0x%x\n", arg1, val);
  84. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_8,
  85. gsi_ctx->per.ee, arg1);
  86. TERR("EV%2d CTX8 0x%x\n", arg1, val);
  87. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_9,
  88. gsi_ctx->per.ee, arg1);
  89. TERR("EV%2d CTX9 0x%x\n", arg1, val);
  90. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_10,
  91. gsi_ctx->per.ee, arg1);
  92. TERR("EV%2d CTX10 0x%x\n", arg1, val);
  93. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_11,
  94. gsi_ctx->per.ee, arg1);
  95. TERR("EV%2d CTX11 0x%x\n", arg1, val);
  96. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_12,
  97. gsi_ctx->per.ee, arg1);
  98. TERR("EV%2d CTX12 0x%x\n", arg1, val);
  99. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_CNTXT_13,
  100. gsi_ctx->per.ee, arg1);
  101. TERR("EV%2d CTX13 0x%x\n", arg1, val);
  102. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_SCRATCH_0,
  103. gsi_ctx->per.ee, arg1);
  104. TERR("EV%2d SCR0 0x%x\n", arg1, val);
  105. val = gsihal_read_reg_nk(GSI_EE_n_EV_CH_k_SCRATCH_1,
  106. gsi_ctx->per.ee, arg1);
  107. TERR("EV%2d SCR1 0x%x\n", arg1, val);
  108. gsi_ctx->per.unvote_clk_cb();
  109. if (arg2) {
  110. ctx = &gsi_ctx->evtr[arg1];
  111. if (ctx->props.ring_base_vaddr) {
  112. for (i = 0; i < ctx->props.ring_len / 16; i++)
  113. TERR("EV%2d (0x%08llx) %08x %08x %08x %08x\n",
  114. arg1, ctx->props.ring_base_addr + i * 16,
  115. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  116. i * 16 + 0),
  117. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  118. i * 16 + 4),
  119. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  120. i * 16 + 8),
  121. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  122. i * 16 + 12));
  123. } else {
  124. TERR("No VA supplied for event ring id %u\n", arg1);
  125. }
  126. }
  127. return count;
  128. }
  129. static ssize_t gsi_dump_ch(struct file *file,
  130. const char __user *buf, size_t count, loff_t *ppos)
  131. {
  132. u32 arg1;
  133. u32 arg2;
  134. unsigned long missing;
  135. char *sptr, *token;
  136. struct gsi_chan_ctx *ctx;
  137. uint16_t i;
  138. if (count >= sizeof(dbg_buff))
  139. return -EINVAL;
  140. missing = copy_from_user(dbg_buff, buf, count);
  141. if (missing)
  142. return -EFAULT;
  143. dbg_buff[count] = '\0';
  144. sptr = dbg_buff;
  145. token = strsep(&sptr, " ");
  146. if (!token)
  147. return -EINVAL;
  148. if (kstrtou32(token, 0, &arg1))
  149. return -EINVAL;
  150. token = strsep(&sptr, " ");
  151. if (!token)
  152. return -EINVAL;
  153. if (kstrtou32(token, 0, &arg2))
  154. return -EINVAL;
  155. TDBG("arg1=%u arg2=%u\n", arg1, arg2);
  156. if (arg1 >= gsi_ctx->max_ch) {
  157. TERR("invalid chan id %u\n", arg1);
  158. return -EINVAL;
  159. }
  160. gsi_ctx->per.vote_clk_cb();
  161. gsi_dump_ch_info(arg1);
  162. gsi_ctx->per.unvote_clk_cb();
  163. if (arg2) {
  164. ctx = &gsi_ctx->chan[arg1];
  165. if (ctx->props.ring_base_vaddr) {
  166. for (i = 0; i < ctx->props.ring_len / 16; i++)
  167. TERR("CH%2d (0x%08llx) %08x %08x %08x %08x\n",
  168. arg1, ctx->props.ring_base_addr + i * 16,
  169. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  170. i * 16 + 0),
  171. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  172. i * 16 + 4),
  173. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  174. i * 16 + 8),
  175. *(u32 *)((u8 *)ctx->props.ring_base_vaddr +
  176. i * 16 + 12));
  177. } else {
  178. TERR("No VA supplied for chan id %u\n", arg1);
  179. }
  180. }
  181. return count;
  182. }
  183. static void gsi_dump_ch_stats(struct gsi_chan_ctx *ctx)
  184. {
  185. if (!ctx->allocated)
  186. return;
  187. PRT_STAT("CH%2d:\n", ctx->props.ch_id);
  188. PRT_STAT("queued=%lu compl=%lu\n",
  189. ctx->stats.queued,
  190. ctx->stats.completed);
  191. PRT_STAT("cb->poll=%lu poll->cb=%lu poll_pend_irq=%lu\n",
  192. ctx->stats.callback_to_poll,
  193. ctx->stats.poll_to_callback,
  194. ctx->stats.poll_pending_irq);
  195. PRT_STAT("invalid_tre_error=%lu\n",
  196. ctx->stats.invalid_tre_error);
  197. PRT_STAT("poll_ok=%lu poll_empty=%lu\n",
  198. ctx->stats.poll_ok, ctx->stats.poll_empty);
  199. if (ctx->evtr)
  200. PRT_STAT("compl_evt=%lu\n",
  201. ctx->evtr->stats.completed);
  202. PRT_STAT("userdata_in_use=%lu\n", ctx->stats.userdata_in_use);
  203. PRT_STAT("ch_below_lo=%lu\n", ctx->stats.dp.ch_below_lo);
  204. PRT_STAT("ch_below_hi=%lu\n", ctx->stats.dp.ch_below_hi);
  205. PRT_STAT("ch_above_hi=%lu\n", ctx->stats.dp.ch_above_hi);
  206. PRT_STAT("time_empty=%lums\n", ctx->stats.dp.empty_time);
  207. PRT_STAT("\n");
  208. }
  209. static ssize_t gsi_dump_stats(struct file *file,
  210. const char __user *buf, size_t count, loff_t *ppos)
  211. {
  212. int ch_id;
  213. int min, max, ret;
  214. ret = kstrtos32_from_user(buf, count, 0, &ch_id);
  215. if (ret)
  216. return ret;
  217. if (ch_id == -1) {
  218. min = 0;
  219. max = gsi_ctx->max_ch;
  220. } else if (ch_id < 0 || ch_id >= gsi_ctx->max_ch ||
  221. !gsi_ctx->chan[ch_id].allocated) {
  222. goto error;
  223. } else {
  224. min = ch_id;
  225. max = ch_id + 1;
  226. }
  227. for (ch_id = min; ch_id < max; ch_id++)
  228. gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]);
  229. return count;
  230. error:
  231. TERR("Usage: echo ch_id > stats. Use -1 for all\n");
  232. return -EINVAL;
  233. }
  234. static int gsi_dbg_create_stats_wq(void)
  235. {
  236. gsi_ctx->dp_stat_wq =
  237. create_singlethread_workqueue("gsi_stat");
  238. if (!gsi_ctx->dp_stat_wq) {
  239. TERR("failed create workqueue\n");
  240. return -ENOMEM;
  241. }
  242. return 0;
  243. }
  244. static void gsi_dbg_destroy_stats_wq(void)
  245. {
  246. cancel_delayed_work_sync(&gsi_update_dp_stats_work);
  247. cancel_delayed_work_sync(&gsi_print_dp_stats_work);
  248. flush_workqueue(gsi_ctx->dp_stat_wq);
  249. destroy_workqueue(gsi_ctx->dp_stat_wq);
  250. gsi_ctx->dp_stat_wq = NULL;
  251. }
  252. static ssize_t gsi_enable_dp_stats(struct file *file,
  253. const char __user *buf, size_t count, loff_t *ppos)
  254. {
  255. int ch_id;
  256. bool enable;
  257. int ret;
  258. if (count >= sizeof(dbg_buff))
  259. goto error;
  260. if (copy_from_user(dbg_buff, buf, count))
  261. goto error;
  262. dbg_buff[count] = '\0';
  263. if (dbg_buff[0] != '+' && dbg_buff[0] != '-')
  264. goto error;
  265. enable = (dbg_buff[0] == '+');
  266. if (kstrtos32(dbg_buff + 1, 0, &ch_id))
  267. goto error;
  268. if (ch_id < 0 || ch_id >= gsi_ctx->max_ch ||
  269. !gsi_ctx->chan[ch_id].allocated) {
  270. goto error;
  271. }
  272. if (gsi_ctx->chan[ch_id].enable_dp_stats == enable) {
  273. TERR("ch_%d: already enabled/disabled\n", ch_id);
  274. return -EINVAL;
  275. }
  276. gsi_ctx->chan[ch_id].enable_dp_stats = enable;
  277. if (enable)
  278. gsi_ctx->num_ch_dp_stats++;
  279. else
  280. gsi_ctx->num_ch_dp_stats--;
  281. if (enable) {
  282. if (gsi_ctx->num_ch_dp_stats == 1) {
  283. ret = gsi_dbg_create_stats_wq();
  284. if (ret)
  285. return ret;
  286. }
  287. cancel_delayed_work_sync(&gsi_update_dp_stats_work);
  288. queue_delayed_work(gsi_ctx->dp_stat_wq,
  289. &gsi_update_dp_stats_work, msecs_to_jiffies(10));
  290. } else if (!enable && gsi_ctx->num_ch_dp_stats == 0) {
  291. gsi_dbg_destroy_stats_wq();
  292. }
  293. return count;
  294. error:
  295. TERR("Usage: echo [+-]ch_id > enable_dp_stats\n");
  296. return -EINVAL;
  297. }
  298. static ssize_t gsi_set_max_elem_dp_stats(struct file *file,
  299. const char __user *buf, size_t count, loff_t *ppos)
  300. {
  301. u32 ch_id;
  302. u32 max_elem;
  303. unsigned long missing;
  304. char *sptr, *token;
  305. if (count >= sizeof(dbg_buff))
  306. goto error;
  307. missing = copy_from_user(dbg_buff, buf, count);
  308. if (missing)
  309. goto error;
  310. dbg_buff[count] = '\0';
  311. sptr = dbg_buff;
  312. token = strsep(&sptr, " ");
  313. if (!token) {
  314. TERR("\n");
  315. goto error;
  316. }
  317. if (kstrtou32(token, 0, &ch_id)) {
  318. TERR("\n");
  319. goto error;
  320. }
  321. token = strsep(&sptr, " ");
  322. if (!token) {
  323. /* get */
  324. if (kstrtou32(dbg_buff, 0, &ch_id))
  325. goto error;
  326. if (ch_id >= gsi_ctx->max_ch)
  327. goto error;
  328. PRT_STAT("ch %d: max_re_expected=%d\n", ch_id,
  329. gsi_ctx->chan[ch_id].props.max_re_expected);
  330. return count;
  331. }
  332. if (kstrtou32(token, 0, &max_elem)) {
  333. TERR("\n");
  334. goto error;
  335. }
  336. TDBG("ch_id=%u max_elem=%u\n", ch_id, max_elem);
  337. if (ch_id >= gsi_ctx->max_ch) {
  338. TERR("invalid chan id %u\n", ch_id);
  339. goto error;
  340. }
  341. gsi_ctx->chan[ch_id].props.max_re_expected = max_elem;
  342. return count;
  343. error:
  344. TERR("Usage: (set) echo <ch_id> <max_elem> > max_elem_dp_stats\n");
  345. TERR("Usage: (get) echo <ch_id> > max_elem_dp_stats\n");
  346. return -EINVAL;
  347. }
  348. static void gsi_wq_print_dp_stats(struct work_struct *work)
  349. {
  350. int ch_id;
  351. for (ch_id = 0; ch_id < gsi_ctx->max_ch; ch_id++) {
  352. if (gsi_ctx->chan[ch_id].print_dp_stats)
  353. gsi_dump_ch_stats(&gsi_ctx->chan[ch_id]);
  354. }
  355. queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_print_dp_stats_work,
  356. msecs_to_jiffies(1000));
  357. }
  358. static void gsi_dbg_update_ch_dp_stats(struct gsi_chan_ctx *ctx)
  359. {
  360. uint16_t start_hw;
  361. uint16_t end_hw;
  362. uint64_t rp_hw;
  363. uint64_t wp_hw;
  364. int ee = gsi_ctx->per.ee;
  365. uint16_t used_hw;
  366. gsi_ctx->per.vote_clk_cb();
  367. rp_hw = gsihal_read_reg_nk(GSI_EE_n_GSI_CH_k_CNTXT_4,
  368. ee, ctx->props.ch_id);
  369. rp_hw |= ((uint64_t)gsihal_read_reg_nk(GSI_EE_n_GSI_CH_k_CNTXT_5,
  370. ee, ctx->props.ch_id)) << 32;
  371. wp_hw = gsihal_read_reg_nk(GSI_EE_n_GSI_CH_k_CNTXT_6,
  372. ee, ctx->props.ch_id);
  373. wp_hw |= ((uint64_t)gsihal_read_reg_nk(GSI_EE_n_GSI_CH_k_CNTXT_7,
  374. ee, ctx->props.ch_id)) << 32;
  375. gsi_ctx->per.unvote_clk_cb();
  376. start_hw = gsi_find_idx_from_addr(&ctx->ring, rp_hw);
  377. end_hw = gsi_find_idx_from_addr(&ctx->ring, wp_hw);
  378. if (end_hw >= start_hw)
  379. used_hw = end_hw - start_hw;
  380. else
  381. used_hw = ctx->ring.max_num_elem + 1 - (start_hw - end_hw);
  382. TDBG("ch %d used %d\n", ctx->props.ch_id, used_hw);
  383. gsi_update_ch_dp_stats(ctx, used_hw);
  384. }
  385. static void gsi_wq_update_dp_stats(struct work_struct *work)
  386. {
  387. int ch_id;
  388. for (ch_id = 0; ch_id < gsi_ctx->max_ch; ch_id++) {
  389. if (gsi_ctx->chan[ch_id].allocated &&
  390. gsi_ctx->chan[ch_id].enable_dp_stats)
  391. gsi_dbg_update_ch_dp_stats(&gsi_ctx->chan[ch_id]);
  392. }
  393. queue_delayed_work(gsi_ctx->dp_stat_wq, &gsi_update_dp_stats_work,
  394. msecs_to_jiffies(10));
  395. }
  396. static ssize_t gsi_rst_stats(struct file *file,
  397. const char __user *buf, size_t count, loff_t *ppos)
  398. {
  399. int ch_id;
  400. int min, max, ret;
  401. ret = kstrtos32_from_user(buf, count, 0, &ch_id);
  402. if (ret)
  403. return ret;
  404. if (ch_id == -1) {
  405. min = 0;
  406. max = gsi_ctx->max_ch;
  407. } else if (ch_id < 0 || ch_id >= gsi_ctx->max_ch ||
  408. !gsi_ctx->chan[ch_id].allocated) {
  409. goto error;
  410. } else {
  411. min = ch_id;
  412. max = ch_id + 1;
  413. }
  414. for (ch_id = min; ch_id < max; ch_id++)
  415. memset(&gsi_ctx->chan[ch_id].stats, 0,
  416. sizeof(gsi_ctx->chan[ch_id].stats));
  417. return count;
  418. error:
  419. TERR("Usage: echo ch_id > rst_stats. Use -1 for all\n");
  420. return -EINVAL;
  421. }
  422. static ssize_t gsi_print_dp_stats(struct file *file,
  423. const char __user *buf, size_t count, loff_t *ppos)
  424. {
  425. int ch_id;
  426. bool enable;
  427. int ret;
  428. if (count >= sizeof(dbg_buff))
  429. goto error;
  430. if (copy_from_user(dbg_buff, buf, count))
  431. goto error;
  432. dbg_buff[count] = '\0';
  433. if (dbg_buff[0] != '+' && dbg_buff[0] != '-')
  434. goto error;
  435. enable = (dbg_buff[0] == '+');
  436. if (kstrtos32(dbg_buff + 1, 0, &ch_id))
  437. goto error;
  438. if (ch_id < 0 || ch_id >= gsi_ctx->max_ch ||
  439. !gsi_ctx->chan[ch_id].allocated) {
  440. goto error;
  441. }
  442. if (gsi_ctx->chan[ch_id].print_dp_stats == enable) {
  443. TERR("ch_%d: already enabled/disabled\n", ch_id);
  444. return -EINVAL;
  445. }
  446. gsi_ctx->chan[ch_id].print_dp_stats = enable;
  447. if (enable)
  448. gsi_ctx->num_ch_dp_stats++;
  449. else
  450. gsi_ctx->num_ch_dp_stats--;
  451. if (enable) {
  452. if (gsi_ctx->num_ch_dp_stats == 1) {
  453. ret = gsi_dbg_create_stats_wq();
  454. if (ret)
  455. return ret;
  456. }
  457. cancel_delayed_work_sync(&gsi_print_dp_stats_work);
  458. queue_delayed_work(gsi_ctx->dp_stat_wq,
  459. &gsi_print_dp_stats_work, msecs_to_jiffies(10));
  460. } else if (!enable && gsi_ctx->num_ch_dp_stats == 0) {
  461. gsi_dbg_destroy_stats_wq();
  462. }
  463. return count;
  464. error:
  465. TERR("Usage: echo [+-]ch_id > print_dp_stats\n");
  466. return -EINVAL;
  467. }
  468. static ssize_t gsi_enable_ipc_low(struct file *file,
  469. const char __user *ubuf, size_t count, loff_t *ppos)
  470. {
  471. s8 option = 0;
  472. int ret;
  473. ret = kstrtos8_from_user(ubuf, count, 0, &option);
  474. if (ret)
  475. return ret;
  476. mutex_lock(&gsi_ctx->mlock);
  477. if (option) {
  478. if (!gsi_ipc_logbuf_low) {
  479. gsi_ipc_logbuf_low =
  480. ipc_log_context_create(GSI_IPC_LOG_PAGES,
  481. "gsi_low", MINIDUMP_MASK);
  482. if (gsi_ipc_logbuf_low == NULL)
  483. TERR("failed to get ipc_logbuf_low\n");
  484. }
  485. gsi_ctx->ipc_logbuf_low = gsi_ipc_logbuf_low;
  486. } else {
  487. gsi_ctx->ipc_logbuf_low = NULL;
  488. }
  489. mutex_unlock(&gsi_ctx->mlock);
  490. return count;
  491. }
  492. static ssize_t gsi_read_gsi_hw_profiling_stats(struct file *file,
  493. char __user *buf, size_t count, loff_t *ppos)
  494. {
  495. struct gsi_hw_profiling_data stats;
  496. int ret, nbytes, cnt = 0;
  497. u64 totalCycles = 0, util = 0;
  498. if (gsi_ctx->per.ver < GSI_VER_2_9) {
  499. nbytes = scnprintf(dbg_buff, GSI_MAX_MSG_LEN,
  500. "This feature only support on GSI2.9+\n");
  501. cnt += nbytes;
  502. goto done;
  503. }
  504. gsi_ctx->per.vote_clk_cb();
  505. ret = gsi_get_hw_profiling_stats(&stats);
  506. gsi_ctx->per.unvote_clk_cb();
  507. if (!ret) {
  508. totalCycles = stats.mcs_busy_cnt + stats.mcs_idle_cnt +
  509. stats.bp_and_pending_cnt;
  510. if (totalCycles != 0)
  511. util = div_u64(
  512. 100 * (stats.mcs_busy_cnt + stats.bp_and_pending_cnt),
  513. totalCycles);
  514. else
  515. util = 0;
  516. nbytes = scnprintf(dbg_buff, GSI_MAX_MSG_LEN,
  517. "bp_count=0x%llx\n"
  518. "bp_and_pending_count=0x%llx\n"
  519. "mcs_busy=0x%llx\n"
  520. "mcs_idle=0x%llx\n"
  521. "total_cycle_count=0x%llx\n"
  522. "utilization_percentage=%llu%%\n",
  523. stats.bp_cnt,
  524. stats.bp_and_pending_cnt,
  525. stats.mcs_busy_cnt,
  526. stats.mcs_idle_cnt,
  527. totalCycles,
  528. util);
  529. cnt += nbytes;
  530. } else {
  531. nbytes = scnprintf(dbg_buff, GSI_MAX_MSG_LEN,
  532. "Fail to read GSI HW Profiling stats\n");
  533. cnt += nbytes;
  534. }
  535. done:
  536. return simple_read_from_buffer(buf, count, ppos, dbg_buff, cnt);
  537. }
  538. static ssize_t gsi_read_gsi_fw_version(struct file *file,
  539. char __user *buf, size_t count, loff_t *ppos)
  540. {
  541. struct gsi_fw_version ver;
  542. int ret, nbytes, cnt = 0;
  543. if (gsi_ctx->per.ver < GSI_VER_2_9) {
  544. nbytes = scnprintf(dbg_buff, GSI_MAX_MSG_LEN,
  545. "This feature only support on GSI2.9+\n");
  546. cnt += nbytes;
  547. goto done;
  548. }
  549. gsi_ctx->per.vote_clk_cb();
  550. ret = gsi_get_fw_version(&ver);
  551. gsi_ctx->per.unvote_clk_cb();
  552. if (!ret) {
  553. nbytes = scnprintf(dbg_buff, GSI_MAX_MSG_LEN,
  554. "hw=%d\nflavor=%d\nfw=%d\n",
  555. ver.hw,
  556. ver.flavor,
  557. ver.fw);
  558. cnt += nbytes;
  559. } else {
  560. nbytes = scnprintf(dbg_buff, GSI_MAX_MSG_LEN,
  561. "Fail to read GSI FW version\n");
  562. cnt += nbytes;
  563. }
  564. done:
  565. return simple_read_from_buffer(buf, count, ppos, dbg_buff, cnt);
  566. }
  567. static const struct file_operations gsi_ev_dump_ops = {
  568. .write = gsi_dump_evt,
  569. };
  570. static const struct file_operations gsi_ch_dump_ops = {
  571. .write = gsi_dump_ch,
  572. };
  573. static const struct file_operations gsi_stats_ops = {
  574. .write = gsi_dump_stats,
  575. };
  576. static const struct file_operations gsi_enable_dp_stats_ops = {
  577. .write = gsi_enable_dp_stats,
  578. };
  579. static const struct file_operations gsi_max_elem_dp_stats_ops = {
  580. .write = gsi_set_max_elem_dp_stats,
  581. };
  582. static const struct file_operations gsi_rst_stats_ops = {
  583. .write = gsi_rst_stats,
  584. };
  585. static const struct file_operations gsi_print_dp_stats_ops = {
  586. .write = gsi_print_dp_stats,
  587. };
  588. static const struct file_operations gsi_ipc_low_ops = {
  589. .write = gsi_enable_ipc_low,
  590. };
  591. static const struct file_operations gsi_hw_profiling_ops = {
  592. .read = gsi_read_gsi_hw_profiling_stats,
  593. };
  594. static const struct file_operations gsi_ver_ops = {
  595. .read = gsi_read_gsi_fw_version,
  596. };
  597. void gsi_debugfs_init(void)
  598. {
  599. static struct dentry *dfile;
  600. const mode_t write_only_mode = 0220;
  601. const mode_t read_only_mode = 0440;
  602. dent = debugfs_create_dir("gsi", 0);
  603. if (IS_ERR(dent)) {
  604. TERR("fail to create dir\n");
  605. return;
  606. }
  607. dfile = debugfs_create_file("ev_dump", write_only_mode,
  608. dent, 0, &gsi_ev_dump_ops);
  609. if (!dfile || IS_ERR(dfile)) {
  610. TERR("fail to create ev_dump file\n");
  611. goto fail;
  612. }
  613. dfile = debugfs_create_file("ch_dump", write_only_mode,
  614. dent, 0, &gsi_ch_dump_ops);
  615. if (!dfile || IS_ERR(dfile)) {
  616. TERR("fail to create ch_dump file\n");
  617. goto fail;
  618. }
  619. dfile = debugfs_create_file("stats", write_only_mode, dent,
  620. 0, &gsi_stats_ops);
  621. if (!dfile || IS_ERR(dfile)) {
  622. TERR("fail to create stats file\n");
  623. goto fail;
  624. }
  625. dfile = debugfs_create_file("enable_dp_stats", write_only_mode, dent,
  626. 0, &gsi_enable_dp_stats_ops);
  627. if (!dfile || IS_ERR(dfile)) {
  628. TERR("fail to create stats file\n");
  629. goto fail;
  630. }
  631. dfile = debugfs_create_file("max_elem_dp_stats", write_only_mode,
  632. dent, 0, &gsi_max_elem_dp_stats_ops);
  633. if (!dfile || IS_ERR(dfile)) {
  634. TERR("fail to create stats file\n");
  635. goto fail;
  636. }
  637. dfile = debugfs_create_file("rst_stats", write_only_mode,
  638. dent, 0, &gsi_rst_stats_ops);
  639. if (!dfile || IS_ERR(dfile)) {
  640. TERR("fail to create stats file\n");
  641. goto fail;
  642. }
  643. dfile = debugfs_create_file("print_dp_stats",
  644. write_only_mode, dent, 0, &gsi_print_dp_stats_ops);
  645. if (!dfile || IS_ERR(dfile)) {
  646. TERR("fail to create stats file\n");
  647. goto fail;
  648. }
  649. dfile = debugfs_create_file("ipc_low", write_only_mode,
  650. dent, 0, &gsi_ipc_low_ops);
  651. if (!dfile || IS_ERR(dfile)) {
  652. TERR("could not create ipc_low\n");
  653. goto fail;
  654. }
  655. dfile = debugfs_create_file("gsi_hw_profiling_stats", read_only_mode,
  656. dent, 0, &gsi_hw_profiling_ops);
  657. if (!dfile || IS_ERR(dfile)) {
  658. TERR("could not create gsi_hw_profiling_stats\n");
  659. goto fail;
  660. }
  661. dfile = debugfs_create_file("gsi_fw_version", read_only_mode, dent, 0,
  662. &gsi_ver_ops);
  663. if (!dfile || IS_ERR(dfile)) {
  664. TERR("could not create gsi_fw_version\n");
  665. goto fail;
  666. }
  667. return;
  668. fail:
  669. debugfs_remove_recursive(dent);
  670. }