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