bus_watcher.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2002,2003 Broadcom Corporation
  4. */
  5. /*
  6. * The Bus Watcher monitors internal bus transactions and maintains
  7. * counts of transactions with error status, logging details and
  8. * causing one of several interrupts. This driver provides a handler
  9. * for those interrupts which aggregates the counts (to avoid
  10. * saturating the 8-bit counters) and provides a presence in
  11. * /proc/bus_watcher if PROC_FS is on.
  12. */
  13. #include <linux/init.h>
  14. #include <linux/kernel.h>
  15. #include <linux/interrupt.h>
  16. #include <linux/sched.h>
  17. #include <linux/proc_fs.h>
  18. #include <linux/seq_file.h>
  19. #include <asm/io.h>
  20. #include <asm/sibyte/sb1250.h>
  21. #include <asm/sibyte/sb1250_regs.h>
  22. #include <asm/sibyte/sb1250_int.h>
  23. #include <asm/sibyte/sb1250_scd.h>
  24. #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
  25. #include <asm/sibyte/bcm1480_regs.h>
  26. #endif
  27. struct bw_stats_struct {
  28. uint64_t status;
  29. uint32_t l2_err;
  30. uint32_t memio_err;
  31. int status_printed;
  32. unsigned long l2_cor_d;
  33. unsigned long l2_bad_d;
  34. unsigned long l2_cor_t;
  35. unsigned long l2_bad_t;
  36. unsigned long mem_cor_d;
  37. unsigned long mem_bad_d;
  38. unsigned long bus_error;
  39. } bw_stats;
  40. static void print_summary(uint32_t status, uint32_t l2_err,
  41. uint32_t memio_err)
  42. {
  43. printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);
  44. printk("\nLast recorded signature:\n");
  45. printk("Request %02x from %d, answered by %d with Dcode %d\n",
  46. (unsigned int)(G_SCD_BERR_TID(status) & 0x3f),
  47. (int)(G_SCD_BERR_TID(status) >> 6),
  48. (int)G_SCD_BERR_RID(status),
  49. (int)G_SCD_BERR_DCODE(status));
  50. }
  51. /*
  52. * check_bus_watcher is exported for use in situations where we want
  53. * to see the most recent status of the bus watcher, which might have
  54. * already been destructively read out of the registers.
  55. *
  56. * notes: this is currently used by the cache error handler
  57. * should provide locking against the interrupt handler
  58. */
  59. void check_bus_watcher(void)
  60. {
  61. u32 status, l2_err, memio_err;
  62. #if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
  63. /* Use non-destructive register */
  64. status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
  65. #elif defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
  66. /* Use non-destructive register */
  67. /* Same as 1250 except BUS_ERR_STATUS_DEBUG is in a different place. */
  68. status = csr_in32(IOADDR(A_BCM1480_BUS_ERR_STATUS_DEBUG));
  69. #else
  70. #error bus watcher being built for unknown Sibyte SOC!
  71. #endif
  72. if (!(status & 0x7fffffff)) {
  73. printk("Using last values reaped by bus watcher driver\n");
  74. status = bw_stats.status;
  75. l2_err = bw_stats.l2_err;
  76. memio_err = bw_stats.memio_err;
  77. } else {
  78. l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
  79. memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
  80. }
  81. if (status & ~(1UL << 31))
  82. print_summary(status, l2_err, memio_err);
  83. else
  84. printk("Bus watcher indicates no error\n");
  85. }
  86. #ifdef CONFIG_PROC_FS
  87. /* For simplicity, I want to assume a single read is required each
  88. time */
  89. static int bw_proc_show(struct seq_file *m, void *v)
  90. {
  91. struct bw_stats_struct *stats = m->private;
  92. seq_puts(m, "SiByte Bus Watcher statistics\n");
  93. seq_puts(m, "-----------------------------\n");
  94. seq_printf(m, "L2-d-cor %8ld\nL2-d-bad %8ld\n",
  95. stats->l2_cor_d, stats->l2_bad_d);
  96. seq_printf(m, "L2-t-cor %8ld\nL2-t-bad %8ld\n",
  97. stats->l2_cor_t, stats->l2_bad_t);
  98. seq_printf(m, "MC-d-cor %8ld\nMC-d-bad %8ld\n",
  99. stats->mem_cor_d, stats->mem_bad_d);
  100. seq_printf(m, "IO-err %8ld\n", stats->bus_error);
  101. seq_puts(m, "\nLast recorded signature:\n");
  102. seq_printf(m, "Request %02x from %d, answered by %d with Dcode %d\n",
  103. (unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f),
  104. (int)(G_SCD_BERR_TID(stats->status) >> 6),
  105. (int)G_SCD_BERR_RID(stats->status),
  106. (int)G_SCD_BERR_DCODE(stats->status));
  107. /* XXXKW indicate multiple errors between printings, or stats
  108. collection (or both)? */
  109. if (stats->status & M_SCD_BERR_MULTERRS)
  110. seq_puts(m, "Multiple errors observed since last check.\n");
  111. if (stats->status_printed) {
  112. seq_puts(m, "(no change since last printing)\n");
  113. } else {
  114. stats->status_printed = 1;
  115. }
  116. return 0;
  117. }
  118. static void create_proc_decoder(struct bw_stats_struct *stats)
  119. {
  120. struct proc_dir_entry *ent;
  121. ent = proc_create_single_data("bus_watcher", S_IWUSR | S_IRUGO, NULL,
  122. bw_proc_show, stats);
  123. if (!ent) {
  124. printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n");
  125. return;
  126. }
  127. }
  128. #endif /* CONFIG_PROC_FS */
  129. /*
  130. * sibyte_bw_int - handle bus watcher interrupts and accumulate counts
  131. *
  132. * notes: possible re-entry due to multiple sources
  133. * should check/indicate saturation
  134. */
  135. static irqreturn_t sibyte_bw_int(int irq, void *data)
  136. {
  137. struct bw_stats_struct *stats = data;
  138. unsigned long cntr;
  139. #ifdef CONFIG_SIBYTE_BW_TRACE
  140. int i;
  141. #endif
  142. #ifdef CONFIG_SIBYTE_BW_TRACE
  143. csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
  144. csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG));
  145. for (i=0; i<256*6; i++)
  146. printk("%016llx\n",
  147. (long long)__raw_readq(IOADDR(A_SCD_TRACE_READ)));
  148. csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
  149. csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
  150. #endif
  151. /* Destructive read, clears register and interrupt */
  152. stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
  153. stats->status_printed = 0;
  154. stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS));
  155. stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr);
  156. stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr);
  157. stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr);
  158. stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr);
  159. csr_out32(0, IOADDR(A_BUS_L2_ERRORS));
  160. stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
  161. stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr);
  162. stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr);
  163. stats->bus_error += G_SCD_MEM_BUSERR(cntr);
  164. csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS));
  165. return IRQ_HANDLED;
  166. }
  167. int __init sibyte_bus_watcher(void)
  168. {
  169. memset(&bw_stats, 0, sizeof(struct bw_stats_struct));
  170. bw_stats.status_printed = 1;
  171. if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
  172. printk("Failed to register bus watcher BAD_ECC irq\n");
  173. return -1;
  174. }
  175. if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
  176. free_irq(K_INT_BAD_ECC, &bw_stats);
  177. printk("Failed to register bus watcher COR_ECC irq\n");
  178. return -1;
  179. }
  180. if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
  181. free_irq(K_INT_BAD_ECC, &bw_stats);
  182. free_irq(K_INT_COR_ECC, &bw_stats);
  183. printk("Failed to register bus watcher IO_BUS irq\n");
  184. return -1;
  185. }
  186. #ifdef CONFIG_PROC_FS
  187. create_proc_decoder(&bw_stats);
  188. #endif
  189. #ifdef CONFIG_SIBYTE_BW_TRACE
  190. csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
  191. K_SCD_TRSEQ_TRIGGER_ALL),
  192. IOADDR(A_SCD_TRACE_SEQUENCE_0));
  193. csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
  194. csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
  195. #endif
  196. return 0;
  197. }
  198. device_initcall(sibyte_bus_watcher);