systracker.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/device.h>
  4. #include <linux/platform_device.h>
  5. #include <linux/kallsyms.h>
  6. #include <linux/interrupt.h>
  7. #include <mach/mt_reg_base.h>
  8. #include <mach/systracker.h>
  9. #include <mt-plat/sync_write.h>
  10. #include <asm/signal.h>
  11. #include <mtk_ram_console.h>
  12. #include <linux/sched.h>
  13. #include "backtrace.h"
  14. #define TRACKER_DEBUG 1
  15. struct systracker_entry_t {
  16. unsigned int dbg_con;
  17. unsigned int ar_track_l[BUS_DBG_NUM_TRACKER];
  18. unsigned int ar_track_h[BUS_DBG_NUM_TRACKER];
  19. unsigned int ar_trans_tid[BUS_DBG_NUM_TRACKER];
  20. unsigned int aw_track_l[BUS_DBG_NUM_TRACKER];
  21. unsigned int aw_track_h[BUS_DBG_NUM_TRACKER];
  22. unsigned int aw_trans_tid[BUS_DBG_NUM_TRACKER];
  23. };
  24. /* Some chip do not have reg dump, define a weak to avoid build error */
  25. int __weak mt_reg_dump(char *buf)
  26. {
  27. return 1;
  28. }
  29. EXPORT_SYMBOL(mt_reg_dump);
  30. static int set_watch_point_address(unsigned int wp_phy_address);
  31. static int systracker_probe(struct platform_device *pdev);
  32. static int systracker_remove(struct platform_device *pdev);
  33. static int systracker_suspend(struct platform_device *pdev, pm_message_t state);
  34. static int systracker_resume(struct platform_device *pdev);
  35. static void test_systracker(void);
  36. struct systracker_config_t {
  37. int state;
  38. int enable_timeout;
  39. int enable_slave_err;
  40. int enable_wp;
  41. int enable_irq;
  42. int timeout_ms;
  43. int wp_phy_address;
  44. };
  45. static struct systracker_config_t track_config;
  46. static struct systracker_entry_t track_entry;
  47. static struct platform_driver systracker_driver = {
  48. .probe = systracker_probe,
  49. .remove = systracker_remove,
  50. .suspend = systracker_suspend,
  51. .resume = systracker_resume,
  52. .driver = {
  53. .name = "systracker",
  54. .bus = &platform_bus_type,
  55. .owner = THIS_MODULE,
  56. },
  57. };
  58. unsigned int is_systracker_device_registered = 0;
  59. unsigned int is_systracker_irq_registered = 0;
  60. int enable_watch_point(void)
  61. {
  62. /* systracker interrupt registration */
  63. if (!is_systracker_irq_registered) {
  64. if (request_irq
  65. (BUS_DBG_TRACKER_IRQ_BIT_ID, systracker_isr, IRQF_TRIGGER_LOW, "SYSTRACKER",
  66. NULL)) {
  67. pr_err("SYSTRACKER IRQ LINE NOT AVAILABLE!!\n");
  68. } else {
  69. is_systracker_irq_registered = 1;
  70. }
  71. }
  72. writel(track_config.wp_phy_address, IOMEM(BUS_DBG_WP));
  73. writel(0x0000000F, IOMEM(BUS_DBG_WP_MASK));
  74. track_config.enable_wp = 1;
  75. writel(readl(IOMEM(BUS_DBG_CON)) | BUS_DBG_CON_WP_EN, IOMEM(BUS_DBG_CON));
  76. mb();
  77. return 0;
  78. }
  79. int disable_watch_point(void)
  80. {
  81. track_config.enable_wp = 0;
  82. writel(readl(IOMEM(BUS_DBG_CON)) & ~BUS_DBG_CON_WP_EN, IOMEM(BUS_DBG_CON));
  83. mb();
  84. return 0;
  85. }
  86. int tracker_dump(char *buf)
  87. {
  88. char *ptr = buf;
  89. unsigned int reg_value;
  90. int i;
  91. unsigned int entry_valid;
  92. unsigned int entry_tid;
  93. unsigned int entry_id;
  94. unsigned int entry_address;
  95. unsigned int entry_data_size;
  96. unsigned int entry_burst_length;
  97. /* if(is_systracker_device_registered) */
  98. {
  99. /* Get tracker info and save to buf */
  100. /* BUS_DBG_AR_TRACK_L(__n)
  101. * [31:0] ARADDR: DBG read tracker entry read address
  102. */
  103. /* BUS_DBG_AR_TRACK_H(__n)
  104. * [14] Valid:DBG read tracker entry valid
  105. * [13:7] ARID:DBG read tracker entry read ID
  106. * [6:4] ARSIZE:DBG read tracker entry read data size
  107. * [3:0] ARLEN: DBG read tracker entry read burst length
  108. */
  109. /* BUS_DBG_AR_TRACK_TID(__n)
  110. * [2:0] BUS_DBG_AR_TRANS0_ENTRY_ID: DBG read tracker entry ID of 1st transaction
  111. */
  112. #ifdef TRACKER_DEBUG
  113. pr_notice("Sys Tracker Dump\n");
  114. #endif
  115. for (i = 0; i < BUS_DBG_NUM_TRACKER; i++) {
  116. entry_address = track_entry.ar_track_l[i];
  117. reg_value = track_entry.ar_track_h[i];
  118. entry_valid = extract_n2mbits(reg_value, 19, 19);
  119. entry_id = extract_n2mbits(reg_value, 7, 18);
  120. entry_data_size = extract_n2mbits(reg_value, 4, 6);
  121. entry_burst_length = extract_n2mbits(reg_value, 0, 3);
  122. entry_tid = track_entry.ar_trans_tid[i];
  123. ptr += sprintf(ptr,
  124. "read entry = %d, valid = 0x%x, tid = 0x%x, read id = 0x%x, address = 0x%x, data_size = 0x%x, burst_length = 0x%x\n",
  125. i, entry_valid, entry_tid, entry_id, entry_address, entry_data_size, entry_burst_length);
  126. #ifdef TRACKER_DEBUG
  127. pr_notice("read entry = %d, valid = 0x%x, tid = 0x%x, read id = 0x%x, address = 0x%x, data_size = 0x%x, burst_length = 0x%x\n",
  128. i, entry_valid, entry_tid, entry_id, entry_address, entry_data_size, entry_burst_length);
  129. #endif
  130. }
  131. /* BUS_DBG_AW_TRACK_L(__n)
  132. * [31:0] AWADDR: DBG write tracker entry write address
  133. */
  134. /* BUS_DBG_AW_TRACK_H(__n)
  135. * [14] Valid:DBG write tracker entry valid
  136. * [13:7] ARID:DBG write tracker entry write ID
  137. * [6:4] ARSIZE:DBG write tracker entry write data size
  138. * [3:0] ARLEN: DBG write tracker entry write burst length
  139. */
  140. /* BUS_DBG_AW_TRACK_TID(__n)
  141. * [2:0] BUS_DBG_AW_TRANS0_ENTRY_ID: DBG write tracker entry ID of 1st transaction
  142. */
  143. for (i = 0; i < BUS_DBG_NUM_TRACKER; i++) {
  144. entry_address = track_entry.aw_track_l[i];
  145. reg_value = track_entry.aw_track_h[i];
  146. entry_valid = extract_n2mbits(reg_value, 19, 19);
  147. entry_id = extract_n2mbits(reg_value, 7, 18);
  148. entry_data_size = extract_n2mbits(reg_value, 4, 6);
  149. entry_burst_length = extract_n2mbits(reg_value, 0, 3);
  150. entry_tid = track_entry.aw_trans_tid[i];
  151. ptr += sprintf(ptr,
  152. "write entry = %d, valid = 0x%x, tid = 0x%x, write id = 0x%x, address = 0x%x, data_size = 0x%x, burst_length = 0x%x\n",
  153. i, entry_valid, entry_tid, entry_id, entry_address, entry_data_size, entry_burst_length);
  154. #ifdef TRACKER_DEBUG
  155. pr_notice("write entry = %d, valid = 0x%x, tid = 0x%x, write id = 0x%x, address = 0x%x, data_size = 0x%x, burst_length = 0x%x\n",
  156. i, entry_valid, entry_tid, entry_id, entry_address, entry_data_size, entry_burst_length);
  157. #endif
  158. }
  159. return strlen(buf);
  160. }
  161. return -1;
  162. }
  163. static ssize_t tracker_run_show(struct device_driver *driver, char *buf)
  164. {
  165. return snprintf(buf, PAGE_SIZE, "%x\n", readl(IOMEM(BUS_DBG_CON)));
  166. }
  167. static ssize_t tracker_run_store(struct device_driver *driver, const char *buf, size_t count)
  168. {
  169. unsigned int value;
  170. if (unlikely(kstrtou32(buf, 10, &value) != 1))
  171. return -EINVAL;
  172. if (value == 1)
  173. enable_systracker();
  174. else if (value == 0)
  175. disable_systracker();
  176. else
  177. return -EINVAL;
  178. return count;
  179. }
  180. DRIVER_ATTR(tracker_run, 0644, tracker_run_show, tracker_run_store);
  181. static ssize_t enable_wp_show(struct device_driver *driver, char *buf)
  182. {
  183. return snprintf(buf, PAGE_SIZE, "%x\n", track_config.enable_wp);
  184. }
  185. static ssize_t enable_wp_store(struct device_driver *driver, const char *buf, size_t count)
  186. {
  187. unsigned int value;
  188. if (unlikely(kstrtou32(buf, 10, &value) != 1))
  189. return -EINVAL;
  190. if (value == 1)
  191. enable_watch_point();
  192. else if (value == 0)
  193. disable_watch_point();
  194. else
  195. return -EINVAL;
  196. return count;
  197. }
  198. DRIVER_ATTR(enable_wp, 0644, enable_wp_show, enable_wp_store);
  199. static ssize_t set_wp_address_show(struct device_driver *driver, char *buf)
  200. {
  201. return snprintf(buf, PAGE_SIZE, "%x\n", track_config.wp_phy_address);
  202. }
  203. static ssize_t set_wp_address_store(struct device_driver *driver, const char *buf, size_t count)
  204. {
  205. unsigned int value;
  206. if (unlikely(kstrtou32(buf, 16, &value) != 1))
  207. return -EINVAL;
  208. pr_notice("watch address:0x%x\n", value);
  209. set_watch_point_address(value);
  210. return count;
  211. }
  212. DRIVER_ATTR(set_wp_address, 0644, set_wp_address_show, set_wp_address_store);
  213. static ssize_t tracker_entry_dump_show(struct device_driver *driver, char *buf)
  214. {
  215. int ret = tracker_dump(buf);
  216. if (ret == -1)
  217. pr_crit("Dump error in %s, %d\n", __func__, __LINE__);
  218. /* FOR test
  219. test_systracker();
  220. */
  221. return strlen(buf);
  222. }
  223. static ssize_t tracker_entry_dump_store(struct device_driver *driver, const char *buf, size_t count)
  224. {
  225. #ifdef TRACKER_DEBUG
  226. test_systracker();
  227. #endif
  228. return count;
  229. }
  230. DRIVER_ATTR(tracker_entry_dump, 0664, tracker_entry_dump_show, tracker_entry_dump_store);
  231. static ssize_t tracker_last_status_show(struct device_driver *driver, char *buf)
  232. {
  233. if (track_entry.dbg_con & (BUS_DBG_CON_IRQ_AR_STA | BUS_DBG_CON_IRQ_AW_STA))
  234. return snprintf(buf, PAGE_SIZE, "1\n");
  235. else
  236. return snprintf(buf, PAGE_SIZE, "0\n");
  237. }
  238. static ssize_t tracker_last_status_store(struct device_driver *driver, const char *buf,
  239. size_t count)
  240. {
  241. return count;
  242. }
  243. DRIVER_ATTR(tracker_last_status, 0664, tracker_last_status_show, tracker_last_status_store);
  244. static irqreturn_t systracker_isr(int irq, void *dev_id)
  245. {
  246. unsigned int con;
  247. static char reg_buf[512];
  248. pr_notice("Sys Tracker ISR\n");
  249. con = readl(IOMEM(BUS_DBG_CON));
  250. writel(con | BUS_DBG_CON_IRQ_CLR, IOMEM(BUS_DBG_CON));
  251. mb();
  252. if (con & BUS_DBG_CON_IRQ_WP_STA) {
  253. pr_notice("[TRACKER] Watch address: 0x%x was touched\n", track_config.wp_phy_address);
  254. if (mt_reg_dump(reg_buf) == 0)
  255. pr_notice("%s\n", reg_buf);
  256. }
  257. return IRQ_HANDLED;
  258. }
  259. static int set_watch_point_address(unsigned int wp_phy_address)
  260. {
  261. track_config.wp_phy_address = wp_phy_address;
  262. return 0;
  263. }
  264. void enable_systracker(void)
  265. {
  266. unsigned int timer_control_value;
  267. /* prescale = (266 * (10 ^ 6)) / 16 = 16625000/s = 16625/ms */
  268. timer_control_value = (BUS_DBG_BUS_MHZ * 1000 / 16) * track_config.timeout_ms;
  269. writel(timer_control_value, IOMEM(BUS_DBG_TIMER_CON));
  270. track_config.state = 1;
  271. writel(BUS_DBG_CON_DEFAULT_VAL & (~BUS_DBG_CON_SW_RST_DN), IOMEM(BUS_DBG_CON));
  272. writel(readl(IOMEM(BUS_DBG_CON)) | BUS_DBG_CON_SW_RST_DN, IOMEM(BUS_DBG_CON));
  273. writel(readl(IOMEM(BUS_DBG_CON)) | BUS_DBG_CON_SW_RST, IOMEM(BUS_DBG_CON));
  274. writel(readl(IOMEM(BUS_DBG_CON)) | BUS_DBG_CON_IRQ_CLR, IOMEM(BUS_DBG_CON));
  275. writel(readl(IOMEM(BUS_DBG_CON)) | BUS_DBG_CON_BUS_DBG_EN, IOMEM(BUS_DBG_CON));
  276. mb();
  277. }
  278. void disable_systracker(void)
  279. {
  280. track_config.state = 0;
  281. writel(readl(IOMEM(BUS_DBG_CON)) & ~BUS_DBG_CON_BUS_DBG_EN, IOMEM(BUS_DBG_CON));
  282. mb();
  283. }
  284. static int systracker_probe(struct platform_device *pdev)
  285. {
  286. int ret;
  287. #if 0
  288. /* FOR test */
  289. static char buf[4096];
  290. tracker_dump(buf);
  291. #endif
  292. pr_notice("systracker probe\n");
  293. is_systracker_device_registered = 1;
  294. memset(&track_config, 0, sizeof(struct systracker_config_t));
  295. /* To latch last PC when tracker timeout, we need to enable interrupt mode */
  296. track_config.enable_timeout = 1;
  297. track_config.timeout_ms = 100;
  298. enable_systracker();
  299. /* Create sysfs entry */
  300. ret = driver_create_file(&systracker_driver.driver, &driver_attr_tracker_entry_dump);
  301. ret |= driver_create_file(&systracker_driver.driver, &driver_attr_tracker_run);
  302. ret |= driver_create_file(&systracker_driver.driver, &driver_attr_enable_wp);
  303. ret |= driver_create_file(&systracker_driver.driver, &driver_attr_tracker_last_status);
  304. ret |= driver_create_file(&systracker_driver.driver, &driver_attr_set_wp_address);
  305. if (ret)
  306. pr_err("Fail to create systracker_drv sysfs files");
  307. /* FOR test
  308. static char buf[4096];
  309. tracker_dump(buf);
  310. */
  311. return 0;
  312. }
  313. static int systracker_remove(struct platform_device *pdev)
  314. {
  315. return 0;
  316. }
  317. static int systracker_suspend(struct platform_device *pdev, pm_message_t state)
  318. {
  319. return 0;
  320. }
  321. static int systracker_resume(struct platform_device *pdev)
  322. {
  323. if (track_config.state)
  324. enable_systracker();
  325. if (track_config.enable_wp)
  326. enable_watch_point();
  327. return 0;
  328. }
  329. #ifdef TRACKER_DEBUG
  330. static void test_systracker(void)
  331. {
  332. *(volatile unsigned int *)0xF00062c4 &= 0xfffffffe;
  333. mb();
  334. *(volatile unsigned int *)0xf3000000;
  335. while (1)
  336. NULL;
  337. }
  338. #endif
  339. void dump_backtrace_entry_ramconsole_print(unsigned long where, unsigned long from,
  340. unsigned long frame)
  341. {
  342. char str_buf[256];
  343. #ifdef CONFIG_KALLSYMS
  344. snprintf(str_buf, sizeof(str_buf), "[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where,
  345. (void *)where, from, (void *)from);
  346. #else
  347. snprintf(str_buf, sizeof(str_buf), "Function entered at [<%08lx>] from [<%08lx>]\n", where,
  348. from);
  349. #endif
  350. aee_sram_fiq_log(str_buf);
  351. }
  352. void dump_regs(const char *fmt, const char v1, const unsigned int reg, const unsigned int reg_val)
  353. {
  354. char str_buf[256];
  355. snprintf(str_buf, sizeof(str_buf), fmt, v1, reg, reg_val);
  356. aee_sram_fiq_log(str_buf);
  357. }
  358. static int verify_stack(unsigned long sp)
  359. {
  360. if (sp < PAGE_OFFSET || (sp > (unsigned long)high_memory && high_memory != NULL))
  361. return -EFAULT;
  362. return 0;
  363. }
  364. static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
  365. {
  366. char str_buf[256];
  367. unsigned int fp, mode;
  368. int ok = 1;
  369. snprintf(str_buf, sizeof(str_buf), "PC is 0x%lx, LR is 0x%lx\n", regs->ARM_pc,
  370. regs->ARM_lr);
  371. aee_sram_fiq_log(str_buf);
  372. if (!tsk)
  373. tsk = current;
  374. if (regs) {
  375. fp = regs->ARM_fp;
  376. mode = processor_mode(regs);
  377. } else if (tsk != current) {
  378. fp = thread_saved_fp(tsk);
  379. mode = 0x10;
  380. } else {
  381. asm("mov %0, fp" : "=r"(fp) : : "cc");
  382. mode = 0x10;
  383. }
  384. if (!fp) {
  385. aee_sram_fiq_log("no frame pointer");
  386. ok = 0;
  387. } else if (verify_stack(fp)) {
  388. aee_sram_fiq_log("invalid frame pointer");
  389. ok = 0;
  390. } else if (fp < (unsigned long)end_of_stack(tsk))
  391. aee_sram_fiq_log("frame pointer underflow");
  392. aee_sram_fiq_log("\n");
  393. if (ok)
  394. c_backtrace_ramconsole_print(fp, mode);
  395. }
  396. int systracker_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
  397. {
  398. int i;
  399. dump_backtrace(regs, NULL);
  400. if (readl(IOMEM(BUS_DBG_CON)) & BUS_DBG_CON_IRQ_AR_STA) {
  401. for (i = 0; i < BUS_DBG_NUM_TRACKER; i++) {
  402. pr_alert("AR_TRACKER Timeout Entry[%d]: ReadAddr:0x%x, Length:0x%x, TransactionID:0x%x!\n",
  403. i, readl(IOMEM(BUS_DBG_AR_TRACK_L(i))),
  404. readl(IOMEM(BUS_DBG_AR_TRACK_H(i))),
  405. readl(IOMEM(BUS_DBG_AR_TRANS_TID(i))));
  406. }
  407. }
  408. if (readl(IOMEM(BUS_DBG_CON)) & BUS_DBG_CON_IRQ_AW_STA) {
  409. for (i = 0; i < BUS_DBG_NUM_TRACKER; i++) {
  410. pr_alert("AW_TRACKER Timeout Entry[%d]: WriteAddr:0x%x, Length:0x%x, TransactionID:0x%x!\n",
  411. i, readl(IOMEM(BUS_DBG_AW_TRACK_L(i))),
  412. readl(IOMEM(BUS_DBG_AW_TRACK_H(i))),
  413. readl(IOMEM(BUS_DBG_AW_TRANS_TID(i))));
  414. }
  415. }
  416. return -1;
  417. }
  418. /*
  419. * save entry info early
  420. */
  421. void save_entry(void)
  422. {
  423. int i;
  424. track_entry.dbg_con = readl(IOMEM(BUS_DBG_CON));
  425. for (i = 0; i < BUS_DBG_NUM_TRACKER; i++) {
  426. track_entry.ar_track_l[i] = readl(IOMEM(BUS_DBG_AR_TRACK_L(i)));
  427. track_entry.ar_track_h[i] = readl(IOMEM(BUS_DBG_AR_TRACK_H(i)));
  428. track_entry.ar_trans_tid[i] = readl(IOMEM(BUS_DBG_AR_TRANS_TID(i)));
  429. track_entry.aw_track_l[i] = readl(IOMEM(BUS_DBG_AW_TRACK_L(i)));
  430. track_entry.aw_track_h[i] = readl(IOMEM(BUS_DBG_AW_TRACK_H(i)));
  431. track_entry.aw_trans_tid[i] = readl(IOMEM(BUS_DBG_AW_TRANS_TID(i)));
  432. }
  433. }
  434. /*
  435. * driver initialization entry point
  436. */
  437. static int __init systracker_init(void)
  438. {
  439. int err;
  440. save_entry();
  441. err = platform_driver_register(&systracker_driver);
  442. if (err)
  443. return err;
  444. #ifdef CONFIG_ARM_LPAE
  445. hook_fault_code(0x11, systracker_handler, SIGTRAP, 0, "Systracker debug exception");
  446. #else
  447. hook_fault_code(0x16, systracker_handler, SIGTRAP, 0, "Systracker debug exception");
  448. #endif
  449. pr_alert("systracker init done\n");
  450. return 0;
  451. }
  452. /*
  453. * driver exit point
  454. */
  455. static void __exit systracker_exit(void)
  456. {
  457. }
  458. module_init(systracker_init);
  459. module_exit(systracker_exit);