debug.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/err.h>
  6. #include <linux/seq_file.h>
  7. #include <linux/debugfs.h>
  8. #include <linux/uaccess.h>
  9. #include <linux/slab.h>
  10. #include "main.h"
  11. #include "debug.h"
  12. #include "qmi.h"
  13. #include "power.h"
  14. void *icnss_ipc_log_context;
  15. void *icnss_ipc_log_long_context;
  16. void *icnss_ipc_log_smp2p_context;
  17. void *icnss_ipc_soc_wake_context;
  18. static ssize_t icnss_regwrite_write(struct file *fp,
  19. const char __user *user_buf,
  20. size_t count, loff_t *off)
  21. {
  22. struct icnss_priv *priv =
  23. ((struct seq_file *)fp->private_data)->private;
  24. char buf[64];
  25. char *sptr, *token;
  26. unsigned int len = 0;
  27. uint32_t reg_offset, mem_type, reg_val;
  28. const char *delim = " ";
  29. int ret = 0;
  30. if (!test_bit(ICNSS_FW_READY, &priv->state) ||
  31. !test_bit(ICNSS_POWER_ON, &priv->state))
  32. return -EINVAL;
  33. len = min(count, sizeof(buf) - 1);
  34. if (copy_from_user(buf, user_buf, len))
  35. return -EFAULT;
  36. buf[len] = '\0';
  37. sptr = buf;
  38. token = strsep(&sptr, delim);
  39. if (!token)
  40. return -EINVAL;
  41. if (!sptr)
  42. return -EINVAL;
  43. if (kstrtou32(token, 0, &mem_type))
  44. return -EINVAL;
  45. token = strsep(&sptr, delim);
  46. if (!token)
  47. return -EINVAL;
  48. if (!sptr)
  49. return -EINVAL;
  50. if (kstrtou32(token, 0, &reg_offset))
  51. return -EINVAL;
  52. token = strsep(&sptr, delim);
  53. if (!token)
  54. return -EINVAL;
  55. if (kstrtou32(token, 0, &reg_val))
  56. return -EINVAL;
  57. ret = wlfw_athdiag_write_send_sync_msg(priv, reg_offset, mem_type,
  58. sizeof(uint32_t),
  59. (uint8_t *)&reg_val);
  60. if (ret)
  61. return ret;
  62. return count;
  63. }
  64. static int icnss_regwrite_show(struct seq_file *s, void *data)
  65. {
  66. struct icnss_priv *priv = s->private;
  67. seq_puts(s, "Usage: echo <mem_type> <offset> <reg_val> > <debugfs>/icnss/reg_write\n");
  68. if (!test_bit(ICNSS_FW_READY, &priv->state))
  69. seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
  70. return 0;
  71. }
  72. static int icnss_regwrite_open(struct inode *inode, struct file *file)
  73. {
  74. return single_open(file, icnss_regwrite_show, inode->i_private);
  75. }
  76. static const struct file_operations icnss_regwrite_fops = {
  77. .read = seq_read,
  78. .write = icnss_regwrite_write,
  79. .open = icnss_regwrite_open,
  80. .owner = THIS_MODULE,
  81. .llseek = seq_lseek,
  82. };
  83. static int icnss_regread_show(struct seq_file *s, void *data)
  84. {
  85. struct icnss_priv *priv = s->private;
  86. mutex_lock(&priv->dev_lock);
  87. if (!priv->diag_reg_read_buf) {
  88. seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n");
  89. if (!test_bit(ICNSS_FW_READY, &priv->state))
  90. seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
  91. mutex_unlock(&priv->dev_lock);
  92. return 0;
  93. }
  94. seq_printf(s, "REGREAD: Addr 0x%x Type 0x%x Length 0x%x\n",
  95. priv->diag_reg_read_addr, priv->diag_reg_read_mem_type,
  96. priv->diag_reg_read_len);
  97. seq_hex_dump(s, "", DUMP_PREFIX_OFFSET, 32, 4, priv->diag_reg_read_buf,
  98. priv->diag_reg_read_len, false);
  99. priv->diag_reg_read_len = 0;
  100. kfree(priv->diag_reg_read_buf);
  101. priv->diag_reg_read_buf = NULL;
  102. mutex_unlock(&priv->dev_lock);
  103. return 0;
  104. }
  105. static int icnss_regread_open(struct inode *inode, struct file *file)
  106. {
  107. return single_open(file, icnss_regread_show, inode->i_private);
  108. }
  109. static ssize_t icnss_reg_parse(const char __user *user_buf, size_t count,
  110. struct icnss_reg_info *reg_info_ptr)
  111. {
  112. char buf[64] = {0};
  113. char *sptr = NULL, *token = NULL;
  114. const char *delim = " ";
  115. unsigned int len = 0;
  116. if (user_buf == NULL)
  117. return -EFAULT;
  118. len = min(count, sizeof(buf) - 1);
  119. if (copy_from_user(buf, user_buf, len))
  120. return -EFAULT;
  121. buf[len] = '\0';
  122. sptr = buf;
  123. token = strsep(&sptr, delim);
  124. if (!token)
  125. return -EINVAL;
  126. if (!sptr)
  127. return -EINVAL;
  128. if (kstrtou32(token, 0, &reg_info_ptr->mem_type))
  129. return -EINVAL;
  130. token = strsep(&sptr, delim);
  131. if (!token)
  132. return -EINVAL;
  133. if (!sptr)
  134. return -EINVAL;
  135. if (kstrtou32(token, 0, &reg_info_ptr->reg_offset))
  136. return -EINVAL;
  137. token = strsep(&sptr, delim);
  138. if (!token)
  139. return -EINVAL;
  140. if (kstrtou32(token, 0, &reg_info_ptr->data_len))
  141. return -EINVAL;
  142. if (reg_info_ptr->data_len == 0 ||
  143. reg_info_ptr->data_len > WLFW_MAX_DATA_SIZE)
  144. return -EINVAL;
  145. return 0;
  146. }
  147. static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf,
  148. size_t count, loff_t *off)
  149. {
  150. struct icnss_priv *priv =
  151. ((struct seq_file *)fp->private_data)->private;
  152. uint8_t *reg_buf = NULL;
  153. int ret = 0;
  154. struct icnss_reg_info reg_info;
  155. if (!test_bit(ICNSS_FW_READY, &priv->state) ||
  156. !test_bit(ICNSS_POWER_ON, &priv->state))
  157. return -EINVAL;
  158. ret = icnss_reg_parse(user_buf, count, &reg_info);
  159. if (ret)
  160. return ret;
  161. mutex_lock(&priv->dev_lock);
  162. kfree(priv->diag_reg_read_buf);
  163. priv->diag_reg_read_buf = NULL;
  164. reg_buf = kzalloc(reg_info.data_len, GFP_KERNEL);
  165. if (!reg_buf) {
  166. mutex_unlock(&priv->dev_lock);
  167. return -ENOMEM;
  168. }
  169. ret = wlfw_athdiag_read_send_sync_msg(priv, reg_info.reg_offset,
  170. reg_info.mem_type,
  171. reg_info.data_len,
  172. reg_buf);
  173. if (ret) {
  174. kfree(reg_buf);
  175. mutex_unlock(&priv->dev_lock);
  176. return ret;
  177. }
  178. priv->diag_reg_read_addr = reg_info.reg_offset;
  179. priv->diag_reg_read_mem_type = reg_info.mem_type;
  180. priv->diag_reg_read_len = reg_info.data_len;
  181. priv->diag_reg_read_buf = reg_buf;
  182. mutex_unlock(&priv->dev_lock);
  183. return count;
  184. }
  185. static const struct file_operations icnss_regread_fops = {
  186. .read = seq_read,
  187. .write = icnss_regread_write,
  188. .open = icnss_regread_open,
  189. .owner = THIS_MODULE,
  190. .llseek = seq_lseek,
  191. };
  192. static ssize_t icnss_stats_write(struct file *fp, const char __user *buf,
  193. size_t count, loff_t *off)
  194. {
  195. struct icnss_priv *priv =
  196. ((struct seq_file *)fp->private_data)->private;
  197. int ret;
  198. u32 val;
  199. ret = kstrtou32_from_user(buf, count, 0, &val);
  200. if (ret)
  201. return ret;
  202. if (ret == 0)
  203. memset(&priv->stats, 0, sizeof(priv->stats));
  204. return count;
  205. }
  206. static int icnss_stats_show_rejuvenate_info(struct seq_file *s,
  207. struct icnss_priv *priv)
  208. {
  209. if (priv->stats.rejuvenate_ind) {
  210. seq_puts(s, "\n<---------------- Rejuvenate Info ----------------->\n");
  211. seq_printf(s, "Number of Rejuvenations: %u\n",
  212. priv->stats.rejuvenate_ind);
  213. seq_printf(s, "Cause for Rejuvenation: 0x%x\n",
  214. priv->cause_for_rejuvenation);
  215. seq_printf(s, "Requesting Sub-System: 0x%x\n",
  216. priv->requesting_sub_system);
  217. seq_printf(s, "Line Number: %u\n",
  218. priv->line_number);
  219. seq_printf(s, "Function Name: %s\n",
  220. priv->function_name);
  221. }
  222. return 0;
  223. }
  224. static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_priv *priv)
  225. {
  226. int i;
  227. seq_puts(s, "\n<------------------ IRQ stats ------------------->\n");
  228. seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request",
  229. "Free", "Enable", "Disable");
  230. for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++)
  231. seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i,
  232. priv->ce_irqs[i], priv->stats.ce_irqs[i].request,
  233. priv->stats.ce_irqs[i].free,
  234. priv->stats.ce_irqs[i].enable,
  235. priv->stats.ce_irqs[i].disable);
  236. return 0;
  237. }
  238. static int icnss_stats_show_capability(struct seq_file *s,
  239. struct icnss_priv *priv)
  240. {
  241. if (test_bit(ICNSS_FW_READY, &priv->state)) {
  242. seq_puts(s, "\n<---------------- FW Capability ----------------->\n");
  243. seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id);
  244. seq_printf(s, "Chip family: 0x%x\n",
  245. priv->chip_info.chip_family);
  246. seq_printf(s, "Board ID: 0x%x\n", priv->board_id);
  247. seq_printf(s, "SOC Info: 0x%x\n", priv->soc_id);
  248. seq_printf(s, "Firmware Version: 0x%x\n",
  249. priv->fw_version_info.fw_version);
  250. seq_printf(s, "Firmware Build Timestamp: %s\n",
  251. priv->fw_version_info.fw_build_timestamp);
  252. seq_printf(s, "Firmware Build ID: %s\n",
  253. priv->fw_build_id);
  254. }
  255. return 0;
  256. }
  257. static int icnss_stats_show_events(struct seq_file *s, struct icnss_priv *priv)
  258. {
  259. int i;
  260. seq_puts(s, "\n<----------------- Events stats ------------------->\n");
  261. seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed");
  262. for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++)
  263. seq_printf(s, "%24s %16u %16u\n",
  264. icnss_driver_event_to_str(i),
  265. priv->stats.events[i].posted,
  266. priv->stats.events[i].processed);
  267. return 0;
  268. }
  269. static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv)
  270. {
  271. enum icnss_driver_state i;
  272. int skip = 0;
  273. unsigned long state;
  274. seq_printf(s, "\nState: 0x%lx(", priv->state);
  275. for (i = 0, state = priv->state; state != 0; state >>= 1, i++) {
  276. if (!(state & 0x1))
  277. continue;
  278. if (skip++)
  279. seq_puts(s, " | ");
  280. switch (i) {
  281. case ICNSS_WLFW_CONNECTED:
  282. seq_puts(s, "FW CONN");
  283. continue;
  284. case ICNSS_POWER_ON:
  285. seq_puts(s, "POWER ON");
  286. continue;
  287. case ICNSS_FW_READY:
  288. seq_puts(s, "FW READY");
  289. continue;
  290. case ICNSS_DRIVER_PROBED:
  291. seq_puts(s, "DRIVER PROBED");
  292. continue;
  293. case ICNSS_FW_TEST_MODE:
  294. seq_puts(s, "FW TEST MODE");
  295. continue;
  296. case ICNSS_PM_SUSPEND:
  297. seq_puts(s, "PM SUSPEND");
  298. continue;
  299. case ICNSS_PM_SUSPEND_NOIRQ:
  300. seq_puts(s, "PM SUSPEND NOIRQ");
  301. continue;
  302. case ICNSS_SSR_REGISTERED:
  303. seq_puts(s, "SSR REGISTERED");
  304. continue;
  305. case ICNSS_PDR_REGISTERED:
  306. seq_puts(s, "PDR REGISTERED");
  307. continue;
  308. case ICNSS_PD_RESTART:
  309. seq_puts(s, "PD RESTART");
  310. continue;
  311. case ICNSS_WLFW_EXISTS:
  312. seq_puts(s, "WLAN FW EXISTS");
  313. continue;
  314. case ICNSS_SHUTDOWN_DONE:
  315. seq_puts(s, "SHUTDOWN DONE");
  316. continue;
  317. case ICNSS_HOST_TRIGGERED_PDR:
  318. seq_puts(s, "HOST TRIGGERED PDR");
  319. continue;
  320. case ICNSS_FW_DOWN:
  321. seq_puts(s, "FW DOWN");
  322. continue;
  323. case ICNSS_DRIVER_UNLOADING:
  324. seq_puts(s, "DRIVER UNLOADING");
  325. continue;
  326. case ICNSS_REJUVENATE:
  327. seq_puts(s, "FW REJUVENATE");
  328. continue;
  329. case ICNSS_MODE_ON:
  330. seq_puts(s, "MODE ON DONE");
  331. continue;
  332. case ICNSS_BLOCK_SHUTDOWN:
  333. seq_puts(s, "BLOCK SHUTDOWN");
  334. continue;
  335. case ICNSS_PDR:
  336. seq_puts(s, "PDR TRIGGERED");
  337. continue;
  338. case ICNSS_DEL_SERVER:
  339. seq_puts(s, "DEL SERVER");
  340. continue;
  341. case ICNSS_COLD_BOOT_CAL:
  342. seq_puts(s, "COLD BOOT CALIBRATION");
  343. continue;
  344. case ICNSS_QMI_DMS_CONNECTED:
  345. seq_puts(s, "DMS_CONNECTED");
  346. }
  347. seq_printf(s, "UNKNOWN-%d", i);
  348. }
  349. seq_puts(s, ")\n");
  350. return 0;
  351. }
  352. #define ICNSS_STATS_DUMP(_s, _priv, _x) \
  353. seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x)
  354. static int icnss_stats_show(struct seq_file *s, void *data)
  355. {
  356. struct icnss_priv *priv = s->private;
  357. ICNSS_STATS_DUMP(s, priv, ind_register_req);
  358. ICNSS_STATS_DUMP(s, priv, ind_register_resp);
  359. ICNSS_STATS_DUMP(s, priv, ind_register_err);
  360. ICNSS_STATS_DUMP(s, priv, cap_req);
  361. ICNSS_STATS_DUMP(s, priv, cap_resp);
  362. ICNSS_STATS_DUMP(s, priv, cap_err);
  363. ICNSS_STATS_DUMP(s, priv, pin_connect_result);
  364. ICNSS_STATS_DUMP(s, priv, cfg_req);
  365. ICNSS_STATS_DUMP(s, priv, cfg_resp);
  366. ICNSS_STATS_DUMP(s, priv, cfg_req_err);
  367. ICNSS_STATS_DUMP(s, priv, mode_req);
  368. ICNSS_STATS_DUMP(s, priv, mode_resp);
  369. ICNSS_STATS_DUMP(s, priv, mode_req_err);
  370. ICNSS_STATS_DUMP(s, priv, ini_req);
  371. ICNSS_STATS_DUMP(s, priv, ini_resp);
  372. ICNSS_STATS_DUMP(s, priv, ini_req_err);
  373. ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash);
  374. ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error);
  375. ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash);
  376. ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown);
  377. seq_puts(s, "\n<------------------ PM stats ------------------->\n");
  378. ICNSS_STATS_DUMP(s, priv, pm_suspend);
  379. ICNSS_STATS_DUMP(s, priv, pm_suspend_err);
  380. ICNSS_STATS_DUMP(s, priv, pm_resume);
  381. ICNSS_STATS_DUMP(s, priv, pm_resume_err);
  382. ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq);
  383. ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err);
  384. ICNSS_STATS_DUMP(s, priv, pm_resume_noirq);
  385. ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err);
  386. ICNSS_STATS_DUMP(s, priv, pm_stay_awake);
  387. ICNSS_STATS_DUMP(s, priv, pm_relax);
  388. if (priv->device_id != WCN6750_DEVICE_ID) {
  389. seq_puts(s, "\n<------------------ MSA stats ------------------->\n");
  390. ICNSS_STATS_DUMP(s, priv, msa_info_req);
  391. ICNSS_STATS_DUMP(s, priv, msa_info_resp);
  392. ICNSS_STATS_DUMP(s, priv, msa_info_err);
  393. ICNSS_STATS_DUMP(s, priv, msa_ready_req);
  394. ICNSS_STATS_DUMP(s, priv, msa_ready_resp);
  395. ICNSS_STATS_DUMP(s, priv, msa_ready_err);
  396. ICNSS_STATS_DUMP(s, priv, msa_ready_ind);
  397. seq_puts(s, "\n<------------------ Rejuvenate stats ------------------->\n");
  398. ICNSS_STATS_DUMP(s, priv, rejuvenate_ind);
  399. ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
  400. ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
  401. ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
  402. icnss_stats_show_rejuvenate_info(s, priv);
  403. }
  404. icnss_stats_show_irqs(s, priv);
  405. icnss_stats_show_capability(s, priv);
  406. icnss_stats_show_events(s, priv);
  407. icnss_stats_show_state(s, priv);
  408. return 0;
  409. }
  410. static int icnss_stats_open(struct inode *inode, struct file *file)
  411. {
  412. return single_open(file, icnss_stats_show, inode->i_private);
  413. }
  414. static const struct file_operations icnss_stats_fops = {
  415. .read = seq_read,
  416. .write = icnss_stats_write,
  417. .release = single_release,
  418. .open = icnss_stats_open,
  419. .owner = THIS_MODULE,
  420. .llseek = seq_lseek,
  421. };
  422. static int icnss_fw_debug_show(struct seq_file *s, void *data)
  423. {
  424. struct icnss_priv *priv = s->private;
  425. seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
  426. seq_puts(s, "\nCMD: test_mode\n");
  427. seq_puts(s, " VAL: 0 (Test mode disable)\n");
  428. seq_puts(s, " VAL: 1 (WLAN FW test)\n");
  429. seq_puts(s, " VAL: 2 (CCPM test)\n");
  430. seq_puts(s, " VAL: 3 (Trigger Recovery)\n");
  431. seq_puts(s, " VAL: 4 (allow recursive recovery)\n");
  432. seq_puts(s, " VAL: 3 (Disallow recursive recovery)\n");
  433. seq_puts(s, "\nCMD: dynamic_feature_mask\n");
  434. seq_puts(s, " VAL: (64 bit feature mask)\n");
  435. if (!test_bit(ICNSS_FW_READY, &priv->state)) {
  436. seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
  437. goto out;
  438. }
  439. if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
  440. seq_puts(s, "Machine mode is running, can't run test_mode!\n");
  441. goto out;
  442. }
  443. if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
  444. seq_puts(s, "test_mode is running, can't run test_mode!\n");
  445. goto out;
  446. }
  447. out:
  448. seq_puts(s, "\n");
  449. return 0;
  450. }
  451. static int icnss_test_mode_fw_test_off(struct icnss_priv *priv)
  452. {
  453. int ret;
  454. if (!test_bit(ICNSS_FW_READY, &priv->state)) {
  455. icnss_pr_err("Firmware is not ready yet!, wait for FW READY: state: 0x%lx\n",
  456. priv->state);
  457. ret = -ENODEV;
  458. goto out;
  459. }
  460. if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
  461. icnss_pr_err("Machine mode is running, can't run test mode: state: 0x%lx\n",
  462. priv->state);
  463. ret = -EINVAL;
  464. goto out;
  465. }
  466. if (!test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
  467. icnss_pr_err("Test mode not started, state: 0x%lx\n",
  468. priv->state);
  469. ret = -EINVAL;
  470. goto out;
  471. }
  472. icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF);
  473. ret = icnss_hw_power_off(priv);
  474. clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
  475. out:
  476. return ret;
  477. }
  478. static int icnss_test_mode_fw_test(struct icnss_priv *priv,
  479. enum icnss_driver_mode mode)
  480. {
  481. int ret;
  482. if (!test_bit(ICNSS_FW_READY, &priv->state)) {
  483. icnss_pr_err("Firmware is not ready yet!, wait for FW READY, state: 0x%lx\n",
  484. priv->state);
  485. ret = -ENODEV;
  486. goto out;
  487. }
  488. if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
  489. icnss_pr_err("Machine mode is running, can't run test mode, state: 0x%lx\n",
  490. priv->state);
  491. ret = -EINVAL;
  492. goto out;
  493. }
  494. if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
  495. icnss_pr_err("Test mode already started, state: 0x%lx\n",
  496. priv->state);
  497. ret = -EBUSY;
  498. goto out;
  499. }
  500. ret = icnss_hw_power_on(priv);
  501. if (ret)
  502. goto out;
  503. set_bit(ICNSS_FW_TEST_MODE, &priv->state);
  504. ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL);
  505. if (ret)
  506. goto power_off;
  507. return 0;
  508. power_off:
  509. icnss_hw_power_off(priv);
  510. clear_bit(ICNSS_FW_TEST_MODE, &priv->state);
  511. out:
  512. return ret;
  513. }
  514. static ssize_t icnss_fw_debug_write(struct file *fp,
  515. const char __user *user_buf,
  516. size_t count, loff_t *off)
  517. {
  518. struct icnss_priv *priv =
  519. ((struct seq_file *)fp->private_data)->private;
  520. char buf[64];
  521. char *sptr, *token;
  522. unsigned int len = 0;
  523. char *cmd;
  524. uint64_t val;
  525. const char *delim = " ";
  526. int ret = 0;
  527. len = min(count, sizeof(buf) - 1);
  528. if (copy_from_user(buf, user_buf, len))
  529. return -EINVAL;
  530. buf[len] = '\0';
  531. sptr = buf;
  532. token = strsep(&sptr, delim);
  533. if (!token)
  534. return -EINVAL;
  535. if (!sptr)
  536. return -EINVAL;
  537. cmd = token;
  538. token = strsep(&sptr, delim);
  539. if (!token)
  540. return -EINVAL;
  541. if (kstrtou64(token, 0, &val))
  542. return -EINVAL;
  543. if (strcmp(cmd, "test_mode") == 0) {
  544. switch (val) {
  545. case 0:
  546. ret = icnss_test_mode_fw_test_off(priv);
  547. break;
  548. case 1:
  549. ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
  550. break;
  551. case 2:
  552. ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
  553. break;
  554. case 3:
  555. ret = icnss_trigger_recovery(&priv->pdev->dev);
  556. break;
  557. case 4:
  558. icnss_allow_recursive_recovery(&priv->pdev->dev);
  559. break;
  560. case 5:
  561. icnss_disallow_recursive_recovery(&priv->pdev->dev);
  562. break;
  563. default:
  564. return -EINVAL;
  565. }
  566. } else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
  567. ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
  568. } else {
  569. return -EINVAL;
  570. }
  571. if (ret)
  572. return ret;
  573. return count;
  574. }
  575. static int icnss_fw_debug_open(struct inode *inode, struct file *file)
  576. {
  577. return single_open(file, icnss_fw_debug_show, inode->i_private);
  578. }
  579. static const struct file_operations icnss_fw_debug_fops = {
  580. .read = seq_read,
  581. .write = icnss_fw_debug_write,
  582. .release = single_release,
  583. .open = icnss_fw_debug_open,
  584. .owner = THIS_MODULE,
  585. .llseek = seq_lseek,
  586. };
  587. static ssize_t icnss_control_params_debug_write(struct file *fp,
  588. const char __user *user_buf,
  589. size_t count, loff_t *off)
  590. {
  591. struct icnss_priv *priv =
  592. ((struct seq_file *)fp->private_data)->private;
  593. char buf[64];
  594. char *sptr, *token;
  595. char *cmd;
  596. u32 val;
  597. unsigned int len = 0;
  598. const char *delim = " ";
  599. if (!priv)
  600. return -ENODEV;
  601. len = min(count, sizeof(buf) - 1);
  602. if (copy_from_user(buf, user_buf, len))
  603. return -EINVAL;
  604. buf[len] = '\0';
  605. sptr = buf;
  606. token = strsep(&sptr, delim);
  607. if (!token)
  608. return -EINVAL;
  609. if (!sptr)
  610. return -EINVAL;
  611. cmd = token;
  612. token = strsep(&sptr, delim);
  613. if (!token)
  614. return -EINVAL;
  615. if (kstrtou32(token, 0, &val))
  616. return -EINVAL;
  617. if (strcmp(cmd, "qmi_timeout") == 0)
  618. priv->ctrl_params.qmi_timeout = msecs_to_jiffies(val);
  619. else
  620. return -EINVAL;
  621. return count;
  622. }
  623. static int icnss_control_params_debug_show(struct seq_file *s, void *data)
  624. {
  625. struct icnss_priv *priv = s->private;
  626. seq_puts(s, "\nUsage: echo <params_name> <value> > <debugfs>/icnss/control_params\n");
  627. seq_puts(s, "<params_name> can be from below:\n");
  628. seq_puts(s, "qmi_timeout: Timeout for QMI message in milliseconds\n");
  629. seq_puts(s, "\nCurrent value:\n");
  630. seq_printf(s, "qmi_timeout: %u\n", jiffies_to_msecs(priv->ctrl_params.qmi_timeout));
  631. return 0;
  632. }
  633. static int icnss_control_params_debug_open(struct inode *inode,
  634. struct file *file)
  635. {
  636. return single_open(file, icnss_control_params_debug_show,
  637. inode->i_private);
  638. }
  639. static const struct file_operations icnss_control_params_debug_fops = {
  640. .read = seq_read,
  641. .write = icnss_control_params_debug_write,
  642. .release = single_release,
  643. .open = icnss_control_params_debug_open,
  644. .owner = THIS_MODULE,
  645. .llseek = seq_lseek,
  646. };
  647. #ifdef CONFIG_ICNSS2_DEBUG
  648. int icnss_debugfs_create(struct icnss_priv *priv)
  649. {
  650. int ret = 0;
  651. struct dentry *root_dentry;
  652. root_dentry = debugfs_create_dir("icnss", NULL);
  653. if (IS_ERR(root_dentry)) {
  654. ret = PTR_ERR(root_dentry);
  655. icnss_pr_err("Unable to create debugfs %d\n", ret);
  656. goto out;
  657. }
  658. priv->root_dentry = root_dentry;
  659. debugfs_create_file("fw_debug", 0600, root_dentry, priv,
  660. &icnss_fw_debug_fops);
  661. debugfs_create_file("stats", 0600, root_dentry, priv,
  662. &icnss_stats_fops);
  663. debugfs_create_file("reg_read", 0600, root_dentry, priv,
  664. &icnss_regread_fops);
  665. debugfs_create_file("reg_write", 0600, root_dentry, priv,
  666. &icnss_regwrite_fops);
  667. debugfs_create_file("control_params", 0600, root_dentry, priv,
  668. &icnss_control_params_debug_fops);
  669. out:
  670. return ret;
  671. }
  672. #else
  673. int icnss_debugfs_create(struct icnss_priv *priv)
  674. {
  675. int ret = 0;
  676. struct dentry *root_dentry;
  677. root_dentry = debugfs_create_dir("icnss", NULL);
  678. if (IS_ERR(root_dentry)) {
  679. ret = PTR_ERR(root_dentry);
  680. icnss_pr_err("Unable to create debugfs %d\n", ret);
  681. return ret;
  682. }
  683. priv->root_dentry = root_dentry;
  684. debugfs_create_file("stats", 0600, root_dentry, priv,
  685. &icnss_stats_fops);
  686. return 0;
  687. }
  688. #endif
  689. void icnss_debugfs_destroy(struct icnss_priv *priv)
  690. {
  691. debugfs_remove_recursive(priv->root_dentry);
  692. }
  693. void icnss_debug_init(void)
  694. {
  695. icnss_ipc_log_context = ipc_log_context_create(NUM_LOG_PAGES,
  696. "icnss", 0);
  697. if (!icnss_ipc_log_context)
  698. icnss_pr_err("Unable to create log context\n");
  699. icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
  700. "icnss_long", 0);
  701. if (!icnss_ipc_log_long_context)
  702. icnss_pr_err("Unable to create log long context\n");
  703. icnss_ipc_log_smp2p_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
  704. "icnss_smp2p", 0);
  705. if (!icnss_ipc_log_smp2p_context)
  706. icnss_pr_err("Unable to create log smp2p context\n");
  707. icnss_ipc_soc_wake_context = ipc_log_context_create(NUM_LOG_LONG_PAGES,
  708. "icnss_soc_wake", 0);
  709. if (!icnss_ipc_soc_wake_context)
  710. icnss_pr_err("Unable to create log soc_wake context\n");
  711. }
  712. void icnss_debug_deinit(void)
  713. {
  714. if (icnss_ipc_log_context) {
  715. ipc_log_context_destroy(icnss_ipc_log_context);
  716. icnss_ipc_log_context = NULL;
  717. }
  718. if (icnss_ipc_log_long_context) {
  719. ipc_log_context_destroy(icnss_ipc_log_long_context);
  720. icnss_ipc_log_long_context = NULL;
  721. }
  722. if (icnss_ipc_log_smp2p_context) {
  723. ipc_log_context_destroy(icnss_ipc_log_smp2p_context);
  724. icnss_ipc_log_smp2p_context = NULL;
  725. }
  726. if (icnss_ipc_soc_wake_context) {
  727. ipc_log_context_destroy(icnss_ipc_soc_wake_context);
  728. icnss_ipc_soc_wake_context = NULL;
  729. }
  730. }