Path: blob/master/arch/mips/sibyte/common/bus_watcher.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (C) 2002,2003 Broadcom Corporation3*/45/*6* The Bus Watcher monitors internal bus transactions and maintains7* counts of transactions with error status, logging details and8* causing one of several interrupts. This driver provides a handler9* for those interrupts which aggregates the counts (to avoid10* saturating the 8-bit counters) and provides a presence in11* /proc/bus_watcher if PROC_FS is on.12*/1314#include <linux/init.h>15#include <linux/kernel.h>16#include <linux/interrupt.h>17#include <linux/sched.h>18#include <linux/proc_fs.h>19#include <linux/seq_file.h>20#include <asm/io.h>2122#include <asm/sibyte/sb1250.h>23#include <asm/sibyte/sb1250_regs.h>24#include <asm/sibyte/sb1250_int.h>25#include <asm/sibyte/sb1250_scd.h>26#ifdef CONFIG_SIBYTE_BCM1x8027#include <asm/sibyte/bcm1480_regs.h>28#endif293031struct bw_stats_struct {32uint64_t status;33uint32_t l2_err;34uint32_t memio_err;35int status_printed;36unsigned long l2_cor_d;37unsigned long l2_bad_d;38unsigned long l2_cor_t;39unsigned long l2_bad_t;40unsigned long mem_cor_d;41unsigned long mem_bad_d;42unsigned long bus_error;43} bw_stats;444546static void print_summary(uint32_t status, uint32_t l2_err,47uint32_t memio_err)48{49printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);50printk("\nLast recorded signature:\n");51printk("Request %02x from %d, answered by %d with Dcode %d\n",52(unsigned int)(G_SCD_BERR_TID(status) & 0x3f),53(int)(G_SCD_BERR_TID(status) >> 6),54(int)G_SCD_BERR_RID(status),55(int)G_SCD_BERR_DCODE(status));56}5758/*59* check_bus_watcher is exported for use in situations where we want60* to see the most recent status of the bus watcher, which might have61* already been destructively read out of the registers.62*63* notes: this is currently used by the cache error handler64* should provide locking against the interrupt handler65*/66void check_bus_watcher(void)67{68u32 status, l2_err, memio_err;6970#if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)71/* Use non-destructive register */72status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));73#elif defined(CONFIG_SIBYTE_BCM1x80)74/* Use non-destructive register */75/* Same as 1250 except BUS_ERR_STATUS_DEBUG is in a different place. */76status = csr_in32(IOADDR(A_BCM1480_BUS_ERR_STATUS_DEBUG));77#else78#error bus watcher being built for unknown Sibyte SOC!79#endif80if (!(status & 0x7fffffff)) {81printk("Using last values reaped by bus watcher driver\n");82status = bw_stats.status;83l2_err = bw_stats.l2_err;84memio_err = bw_stats.memio_err;85} else {86l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));87memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));88}89if (status & ~(1UL << 31))90print_summary(status, l2_err, memio_err);91else92printk("Bus watcher indicates no error\n");93}9495#ifdef CONFIG_PROC_FS9697/* For simplicity, I want to assume a single read is required each98time */99static int bw_proc_show(struct seq_file *m, void *v)100{101struct bw_stats_struct *stats = m->private;102103seq_puts(m, "SiByte Bus Watcher statistics\n");104seq_puts(m, "-----------------------------\n");105seq_printf(m, "L2-d-cor %8ld\nL2-d-bad %8ld\n",106stats->l2_cor_d, stats->l2_bad_d);107seq_printf(m, "L2-t-cor %8ld\nL2-t-bad %8ld\n",108stats->l2_cor_t, stats->l2_bad_t);109seq_printf(m, "MC-d-cor %8ld\nMC-d-bad %8ld\n",110stats->mem_cor_d, stats->mem_bad_d);111seq_printf(m, "IO-err %8ld\n", stats->bus_error);112seq_puts(m, "\nLast recorded signature:\n");113seq_printf(m, "Request %02x from %d, answered by %d with Dcode %d\n",114(unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f),115(int)(G_SCD_BERR_TID(stats->status) >> 6),116(int)G_SCD_BERR_RID(stats->status),117(int)G_SCD_BERR_DCODE(stats->status));118/* XXXKW indicate multiple errors between printings, or stats119collection (or both)? */120if (stats->status & M_SCD_BERR_MULTERRS)121seq_puts(m, "Multiple errors observed since last check.\n");122if (stats->status_printed) {123seq_puts(m, "(no change since last printing)\n");124} else {125stats->status_printed = 1;126}127128return 0;129}130131static void create_proc_decoder(struct bw_stats_struct *stats)132{133struct proc_dir_entry *ent;134135ent = proc_create_single_data("bus_watcher", S_IWUSR | S_IRUGO, NULL,136bw_proc_show, stats);137if (!ent) {138printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n");139return;140}141}142143#endif /* CONFIG_PROC_FS */144145/*146* sibyte_bw_int - handle bus watcher interrupts and accumulate counts147*148* notes: possible re-entry due to multiple sources149* should check/indicate saturation150*/151static irqreturn_t sibyte_bw_int(int irq, void *data)152{153struct bw_stats_struct *stats = data;154unsigned long cntr;155#ifdef CONFIG_SIBYTE_BW_TRACE156int i;157#endif158159#ifdef CONFIG_SIBYTE_BW_TRACE160csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));161csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG));162163for (i=0; i<256*6; i++)164printk("%016llx\n",165(long long)__raw_readq(IOADDR(A_SCD_TRACE_READ)));166167csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));168csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));169#endif170171/* Destructive read, clears register and interrupt */172stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));173stats->status_printed = 0;174175stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS));176stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr);177stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr);178stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr);179stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr);180csr_out32(0, IOADDR(A_BUS_L2_ERRORS));181182stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));183stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr);184stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr);185stats->bus_error += G_SCD_MEM_BUSERR(cntr);186csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS));187188return IRQ_HANDLED;189}190191int __init sibyte_bus_watcher(void)192{193memset(&bw_stats, 0, sizeof(struct bw_stats_struct));194bw_stats.status_printed = 1;195196if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {197printk("Failed to register bus watcher BAD_ECC irq\n");198return -1;199}200if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {201free_irq(K_INT_BAD_ECC, &bw_stats);202printk("Failed to register bus watcher COR_ECC irq\n");203return -1;204}205if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {206free_irq(K_INT_BAD_ECC, &bw_stats);207free_irq(K_INT_COR_ECC, &bw_stats);208printk("Failed to register bus watcher IO_BUS irq\n");209return -1;210}211212#ifdef CONFIG_PROC_FS213create_proc_decoder(&bw_stats);214#endif215216#ifdef CONFIG_SIBYTE_BW_TRACE217csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |218K_SCD_TRSEQ_TRIGGER_ALL),219IOADDR(A_SCD_TRACE_SEQUENCE_0));220csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));221csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));222#endif223224return 0;225}226227device_initcall(sibyte_bus_watcher);228229230