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#if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
  85        /* Use non-destructive register */
  86        status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
  87#elif defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
  88        /* Use non-destructive register */
  89        /* Same as 1250 except BUS_ERR_STATUS_DEBUG is in a different place. */
  90        status = csr_in32(IOADDR(A_BCM1480_BUS_ERR_STATUS_DEBUG));
  91#else
  92#error bus watcher being built for unknown Sibyte SOC!
  93#endif
  94        if (!(status & 0x7fffffff)) {
  95                printk("Using last values reaped by bus watcher driver\n");
  96                status = bw_stats.status;
  97                l2_err = bw_stats.l2_err;
  98                memio_err = bw_stats.memio_err;
  99        } else {
 100                l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
 101                memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
 102        }
 103        if (status & ~(1UL << 31))
 104                print_summary(status, l2_err, memio_err);
 105        else
 106                printk("Bus watcher indicates no error\n");
 107}
 108
 109#ifdef CONFIG_PROC_FS
 110
 111/* For simplicity, I want to assume a single read is required each
 112   time */
 113static int bw_proc_show(struct seq_file *m, void *v)
 114{
 115        struct bw_stats_struct *stats = m->private;
 116
 117        seq_puts(m, "SiByte Bus Watcher statistics\n");
 118        seq_puts(m, "-----------------------------\n");
 119        seq_printf(m, "L2-d-cor %8ld\nL2-d-bad %8ld\n",
 120                   stats->l2_cor_d, stats->l2_bad_d);
 121        seq_printf(m, "L2-t-cor %8ld\nL2-t-bad %8ld\n",
 122                   stats->l2_cor_t, stats->l2_bad_t);
 123        seq_printf(m, "MC-d-cor %8ld\nMC-d-bad %8ld\n",
 124                   stats->mem_cor_d, stats->mem_bad_d);
 125        seq_printf(m, "IO-err   %8ld\n", stats->bus_error);
 126        seq_puts(m, "\nLast recorded signature:\n");
 127        seq_printf(m, "Request %02x from %d, answered by %d with Dcode %d\n",
 128                   (unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f),
 129                   (int)(G_SCD_BERR_TID(stats->status) >> 6),
 130                   (int)G_SCD_BERR_RID(stats->status),
 131                   (int)G_SCD_BERR_DCODE(stats->status));
 132        /* XXXKW indicate multiple errors between printings, or stats
 133           collection (or both)? */
 134        if (stats->status & M_SCD_BERR_MULTERRS)
 135                seq_puts(m, "Multiple errors observed since last check.\n");
 136        if (stats->status_printed) {
 137                seq_puts(m, "(no change since last printing)\n");
 138        } else {
 139                stats->status_printed = 1;
 140        }
 141
 142        return 0;
 143}
 144
 145static int bw_proc_open(struct inode *inode, struct file *file)
 146{
 147        return single_open(file, bw_proc_show, PDE_DATA(inode));
 148}
 149
 150static const struct file_operations bw_proc_fops = {
 151        .open           = bw_proc_open,
 152        .read           = seq_read,
 153        .llseek         = seq_lseek,
 154        .release        = single_release,
 155};
 156
 157static void create_proc_decoder(struct bw_stats_struct *stats)
 158{
 159        struct proc_dir_entry *ent;
 160
 161        ent = proc_create_data("bus_watcher", S_IWUSR | S_IRUGO, NULL,
 162                               &bw_proc_fops, stats);
 163        if (!ent) {
 164                printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n");
 165                return;
 166        }
 167}
 168
 169#endif /* CONFIG_PROC_FS */
 170
 171/*
 172 * sibyte_bw_int - handle bus watcher interrupts and accumulate counts
 173 *
 174 * notes: possible re-entry due to multiple sources
 175 *        should check/indicate saturation
 176 */
 177static irqreturn_t sibyte_bw_int(int irq, void *data)
 178{
 179        struct bw_stats_struct *stats = data;
 180        unsigned long cntr;
 181#ifdef CONFIG_SIBYTE_BW_TRACE
 182        int i;
 183#endif
 184
 185#ifdef CONFIG_SIBYTE_BW_TRACE
 186        csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
 187        csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG));
 188
 189        for (i=0; i<256*6; i++)
 190                printk("%016llx\n",
 191                       (long long)__raw_readq(IOADDR(A_SCD_TRACE_READ)));
 192
 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
 197        /* Destructive read, clears register and interrupt */
 198        stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
 199        stats->status_printed = 0;
 200
 201        stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS));
 202        stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr);
 203        stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr);
 204        stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr);
 205        stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr);
 206        csr_out32(0, IOADDR(A_BUS_L2_ERRORS));
 207
 208        stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
 209        stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr);
 210        stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr);
 211        stats->bus_error += G_SCD_MEM_BUSERR(cntr);
 212        csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS));
 213
 214        return IRQ_HANDLED;
 215}
 216
 217int __init sibyte_bus_watcher(void)
 218{
 219        memset(&bw_stats, 0, sizeof(struct bw_stats_struct));
 220        bw_stats.status_printed = 1;
 221
 222        if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
 223                printk("Failed to register bus watcher BAD_ECC irq\n");
 224                return -1;
 225        }
 226        if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
 227                free_irq(K_INT_BAD_ECC, &bw_stats);
 228                printk("Failed to register bus watcher COR_ECC irq\n");
 229                return -1;
 230        }
 231        if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
 232                free_irq(K_INT_BAD_ECC, &bw_stats);
 233                free_irq(K_INT_COR_ECC, &bw_stats);
 234                printk("Failed to register bus watcher IO_BUS irq\n");
 235                return -1;
 236        }
 237
 238#ifdef CONFIG_PROC_FS
 239        create_proc_decoder(&bw_stats);
 240#endif
 241
 242#ifdef CONFIG_SIBYTE_BW_TRACE
 243        csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
 244                   K_SCD_TRSEQ_TRIGGER_ALL),
 245                  IOADDR(A_SCD_TRACE_SEQUENCE_0));
 246        csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
 247        csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
 248#endif
 249
 250        return 0;
 251}
 252
 253device_initcall(sibyte_bus_watcher);
 254