Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2002,2003 Broadcom Corporation
0004  */
0005 
0006 /*
0007  * The Bus Watcher monitors internal bus transactions and maintains
0008  * counts of transactions with error status, logging details and
0009  * causing one of several interrupts.  This driver provides a handler
0010  * for those interrupts which aggregates the counts (to avoid
0011  * saturating the 8-bit counters) and provides a presence in
0012  * /proc/bus_watcher if PROC_FS is on.
0013  */
0014 
0015 #include <linux/init.h>
0016 #include <linux/kernel.h>
0017 #include <linux/interrupt.h>
0018 #include <linux/sched.h>
0019 #include <linux/proc_fs.h>
0020 #include <linux/seq_file.h>
0021 #include <asm/io.h>
0022 
0023 #include <asm/sibyte/sb1250.h>
0024 #include <asm/sibyte/sb1250_regs.h>
0025 #include <asm/sibyte/sb1250_int.h>
0026 #include <asm/sibyte/sb1250_scd.h>
0027 #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
0028 #include <asm/sibyte/bcm1480_regs.h>
0029 #endif
0030 
0031 
0032 struct bw_stats_struct {
0033     uint64_t status;
0034     uint32_t l2_err;
0035     uint32_t memio_err;
0036     int status_printed;
0037     unsigned long l2_cor_d;
0038     unsigned long l2_bad_d;
0039     unsigned long l2_cor_t;
0040     unsigned long l2_bad_t;
0041     unsigned long mem_cor_d;
0042     unsigned long mem_bad_d;
0043     unsigned long bus_error;
0044 } bw_stats;
0045 
0046 
0047 static void print_summary(uint32_t status, uint32_t l2_err,
0048               uint32_t memio_err)
0049 {
0050     printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);
0051     printk("\nLast recorded signature:\n");
0052     printk("Request %02x from %d, answered by %d with Dcode %d\n",
0053            (unsigned int)(G_SCD_BERR_TID(status) & 0x3f),
0054            (int)(G_SCD_BERR_TID(status) >> 6),
0055            (int)G_SCD_BERR_RID(status),
0056            (int)G_SCD_BERR_DCODE(status));
0057 }
0058 
0059 /*
0060  * check_bus_watcher is exported for use in situations where we want
0061  * to see the most recent status of the bus watcher, which might have
0062  * already been destructively read out of the registers.
0063  *
0064  * notes: this is currently used by the cache error handler
0065  *    should provide locking against the interrupt handler
0066  */
0067 void check_bus_watcher(void)
0068 {
0069     u32 status, l2_err, memio_err;
0070 
0071 #if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
0072     /* Use non-destructive register */
0073     status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
0074 #elif defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
0075     /* Use non-destructive register */
0076     /* Same as 1250 except BUS_ERR_STATUS_DEBUG is in a different place. */
0077     status = csr_in32(IOADDR(A_BCM1480_BUS_ERR_STATUS_DEBUG));
0078 #else
0079 #error bus watcher being built for unknown Sibyte SOC!
0080 #endif
0081     if (!(status & 0x7fffffff)) {
0082         printk("Using last values reaped by bus watcher driver\n");
0083         status = bw_stats.status;
0084         l2_err = bw_stats.l2_err;
0085         memio_err = bw_stats.memio_err;
0086     } else {
0087         l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
0088         memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
0089     }
0090     if (status & ~(1UL << 31))
0091         print_summary(status, l2_err, memio_err);
0092     else
0093         printk("Bus watcher indicates no error\n");
0094 }
0095 
0096 #ifdef CONFIG_PROC_FS
0097 
0098 /* For simplicity, I want to assume a single read is required each
0099    time */
0100 static int bw_proc_show(struct seq_file *m, void *v)
0101 {
0102     struct bw_stats_struct *stats = m->private;
0103 
0104     seq_puts(m, "SiByte Bus Watcher statistics\n");
0105     seq_puts(m, "-----------------------------\n");
0106     seq_printf(m, "L2-d-cor %8ld\nL2-d-bad %8ld\n",
0107            stats->l2_cor_d, stats->l2_bad_d);
0108     seq_printf(m, "L2-t-cor %8ld\nL2-t-bad %8ld\n",
0109            stats->l2_cor_t, stats->l2_bad_t);
0110     seq_printf(m, "MC-d-cor %8ld\nMC-d-bad %8ld\n",
0111            stats->mem_cor_d, stats->mem_bad_d);
0112     seq_printf(m, "IO-err   %8ld\n", stats->bus_error);
0113     seq_puts(m, "\nLast recorded signature:\n");
0114     seq_printf(m, "Request %02x from %d, answered by %d with Dcode %d\n",
0115            (unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f),
0116            (int)(G_SCD_BERR_TID(stats->status) >> 6),
0117            (int)G_SCD_BERR_RID(stats->status),
0118            (int)G_SCD_BERR_DCODE(stats->status));
0119     /* XXXKW indicate multiple errors between printings, or stats
0120        collection (or both)? */
0121     if (stats->status & M_SCD_BERR_MULTERRS)
0122         seq_puts(m, "Multiple errors observed since last check.\n");
0123     if (stats->status_printed) {
0124         seq_puts(m, "(no change since last printing)\n");
0125     } else {
0126         stats->status_printed = 1;
0127     }
0128 
0129     return 0;
0130 }
0131 
0132 static void create_proc_decoder(struct bw_stats_struct *stats)
0133 {
0134     struct proc_dir_entry *ent;
0135 
0136     ent = proc_create_single_data("bus_watcher", S_IWUSR | S_IRUGO, NULL,
0137             bw_proc_show, stats);
0138     if (!ent) {
0139         printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n");
0140         return;
0141     }
0142 }
0143 
0144 #endif /* CONFIG_PROC_FS */
0145 
0146 /*
0147  * sibyte_bw_int - handle bus watcher interrupts and accumulate counts
0148  *
0149  * notes: possible re-entry due to multiple sources
0150  *    should check/indicate saturation
0151  */
0152 static irqreturn_t sibyte_bw_int(int irq, void *data)
0153 {
0154     struct bw_stats_struct *stats = data;
0155     unsigned long cntr;
0156 #ifdef CONFIG_SIBYTE_BW_TRACE
0157     int i;
0158 #endif
0159 
0160 #ifdef CONFIG_SIBYTE_BW_TRACE
0161     csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
0162     csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG));
0163 
0164     for (i=0; i<256*6; i++)
0165         printk("%016llx\n",
0166                (long long)__raw_readq(IOADDR(A_SCD_TRACE_READ)));
0167 
0168     csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
0169     csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
0170 #endif
0171 
0172     /* Destructive read, clears register and interrupt */
0173     stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
0174     stats->status_printed = 0;
0175 
0176     stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS));
0177     stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr);
0178     stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr);
0179     stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr);
0180     stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr);
0181     csr_out32(0, IOADDR(A_BUS_L2_ERRORS));
0182 
0183     stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
0184     stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr);
0185     stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr);
0186     stats->bus_error += G_SCD_MEM_BUSERR(cntr);
0187     csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS));
0188 
0189     return IRQ_HANDLED;
0190 }
0191 
0192 int __init sibyte_bus_watcher(void)
0193 {
0194     memset(&bw_stats, 0, sizeof(struct bw_stats_struct));
0195     bw_stats.status_printed = 1;
0196 
0197     if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
0198         printk("Failed to register bus watcher BAD_ECC irq\n");
0199         return -1;
0200     }
0201     if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
0202         free_irq(K_INT_BAD_ECC, &bw_stats);
0203         printk("Failed to register bus watcher COR_ECC irq\n");
0204         return -1;
0205     }
0206     if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
0207         free_irq(K_INT_BAD_ECC, &bw_stats);
0208         free_irq(K_INT_COR_ECC, &bw_stats);
0209         printk("Failed to register bus watcher IO_BUS irq\n");
0210         return -1;
0211     }
0212 
0213 #ifdef CONFIG_PROC_FS
0214     create_proc_decoder(&bw_stats);
0215 #endif
0216 
0217 #ifdef CONFIG_SIBYTE_BW_TRACE
0218     csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
0219            K_SCD_TRSEQ_TRIGGER_ALL),
0220           IOADDR(A_SCD_TRACE_SEQUENCE_0));
0221     csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
0222     csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
0223 #endif
0224 
0225     return 0;
0226 }
0227 
0228 device_initcall(sibyte_bus_watcher);