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