diff options
Diffstat (limited to 'arch/mips/sibyte/common')
| -rw-r--r-- | arch/mips/sibyte/common/Makefile | 1 | ||||
| -rw-r--r-- | arch/mips/sibyte/common/bus_watcher.c | 256 | ||||
| -rw-r--r-- | arch/mips/sibyte/common/cfe.c | 10 | ||||
| -rw-r--r-- | arch/mips/sibyte/common/sb_tbprof.c | 23 | 
4 files changed, 273 insertions, 17 deletions
diff --git a/arch/mips/sibyte/common/Makefile b/arch/mips/sibyte/common/Makefile index 36aa700cc40..b3d6bf23a66 100644 --- a/arch/mips/sibyte/common/Makefile +++ b/arch/mips/sibyte/common/Makefile @@ -1,3 +1,4 @@  obj-y := cfe.o +obj-$(CONFIG_SIBYTE_BUS_WATCHER)	+= bus_watcher.o  obj-$(CONFIG_SIBYTE_CFE_CONSOLE)	+= cfe_console.o  obj-$(CONFIG_SIBYTE_TBPROF)		+= sb_tbprof.o diff --git a/arch/mips/sibyte/common/bus_watcher.c b/arch/mips/sibyte/common/bus_watcher.c new file mode 100644 index 00000000000..5581844c919 --- /dev/null +++ b/arch/mips/sibyte/common/bus_watcher.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2002,2003 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. + */ + +/* + * The Bus Watcher monitors internal bus transactions and maintains + * counts of transactions with error status, logging details and + * causing one of several interrupts.  This driver provides a handler + * for those interrupts which aggregates the counts (to avoid + * saturating the 8-bit counters) and provides a presence in + * /proc/bus_watcher if PROC_FS is on. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <asm/io.h> + +#include <asm/sibyte/sb1250.h> +#include <asm/sibyte/sb1250_regs.h> +#include <asm/sibyte/sb1250_int.h> +#include <asm/sibyte/sb1250_scd.h> +#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) +#include <asm/sibyte/bcm1480_regs.h> +#endif + + +struct bw_stats_struct { +	uint64_t status; +	uint32_t l2_err; +	uint32_t memio_err; +	int status_printed; +	unsigned long l2_cor_d; +	unsigned long l2_bad_d; +	unsigned long l2_cor_t; +	unsigned long l2_bad_t; +	unsigned long mem_cor_d; +	unsigned long mem_bad_d; +	unsigned long bus_error; +} bw_stats; + + +static void print_summary(uint32_t status, uint32_t l2_err, +			  uint32_t memio_err) +{ +	printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err); +	printk("\nLast recorded signature:\n"); +	printk("Request %02x from %d, answered by %d with Dcode %d\n", +	       (unsigned int)(G_SCD_BERR_TID(status) & 0x3f), +	       (int)(G_SCD_BERR_TID(status) >> 6), +	       (int)G_SCD_BERR_RID(status), +	       (int)G_SCD_BERR_DCODE(status)); +} + +/* + * check_bus_watcher is exported for use in situations where we want + * to see the most recent status of the bus watcher, which might have + * already been destructively read out of the registers. + * + * notes: this is currently used by the cache error handler + *	  should provide locking against the interrupt handler + */ +void check_bus_watcher(void) +{ +	u32 status, l2_err, memio_err; + +#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS +	/* Destructive read, clears register and interrupt */ +	status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS)); +#elif defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250) +	/* Use non-destructive register */ +	status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG)); +#elif defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) +	/* Use non-destructive register */ +	/* Same as 1250 except BUS_ERR_STATUS_DEBUG is in a different place. */ +	status = csr_in32(IOADDR(A_BCM1480_BUS_ERR_STATUS_DEBUG)); +#else +#error bus watcher being built for unknown Sibyte SOC! +#endif +	if (!(status & 0x7fffffff)) { +		printk("Using last values reaped by bus watcher driver\n"); +		status = bw_stats.status; +		l2_err = bw_stats.l2_err; +		memio_err = bw_stats.memio_err; +	} else { +		l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS)); +		memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS)); +	} +	if (status & ~(1UL << 31)) +		print_summary(status, l2_err, memio_err); +	else +		printk("Bus watcher indicates no error\n"); +} + +#ifdef CONFIG_PROC_FS + +/* For simplicity, I want to assume a single read is required each +   time */ +static int bw_proc_show(struct seq_file *m, void *v) +{ +	struct bw_stats_struct *stats = m->private; + +	seq_puts(m, "SiByte Bus Watcher statistics\n"); +	seq_puts(m, "-----------------------------\n"); +	seq_printf(m, "L2-d-cor %8ld\nL2-d-bad %8ld\n", +		   stats->l2_cor_d, stats->l2_bad_d); +	seq_printf(m, "L2-t-cor %8ld\nL2-t-bad %8ld\n", +		   stats->l2_cor_t, stats->l2_bad_t); +	seq_printf(m, "MC-d-cor %8ld\nMC-d-bad %8ld\n", +		   stats->mem_cor_d, stats->mem_bad_d); +	seq_printf(m, "IO-err   %8ld\n", stats->bus_error); +	seq_puts(m, "\nLast recorded signature:\n"); +	seq_printf(m, "Request %02x from %d, answered by %d with Dcode %d\n", +		   (unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f), +		   (int)(G_SCD_BERR_TID(stats->status) >> 6), +		   (int)G_SCD_BERR_RID(stats->status), +		   (int)G_SCD_BERR_DCODE(stats->status)); +	/* XXXKW indicate multiple errors between printings, or stats +	   collection (or both)? */ +	if (stats->status & M_SCD_BERR_MULTERRS) +		seq_puts(m, "Multiple errors observed since last check.\n"); +	if (stats->status_printed) { +		seq_puts(m, "(no change since last printing)\n"); +	} else { +		stats->status_printed = 1; +	} + +	return 0; +} + +static int bw_proc_open(struct inode *inode, struct file *file) +{ +	return single_open(file, bw_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations bw_proc_fops = { +	.open		= bw_proc_open, +	.read		= seq_read, +	.llseek		= seq_lseek, +	.release	= single_release, +}; + +static void create_proc_decoder(struct bw_stats_struct *stats) +{ +	struct proc_dir_entry *ent; + +	ent = proc_create_data("bus_watcher", S_IWUSR | S_IRUGO, NULL, +			       &bw_proc_fops, stats); +	if (!ent) { +		printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n"); +		return; +	} +} + +#endif /* CONFIG_PROC_FS */ + +/* + * sibyte_bw_int - handle bus watcher interrupts and accumulate counts + * + * notes: possible re-entry due to multiple sources + *	  should check/indicate saturation + */ +static irqreturn_t sibyte_bw_int(int irq, void *data) +{ +	struct bw_stats_struct *stats = data; +	unsigned long cntr; +#ifdef CONFIG_SIBYTE_BW_TRACE +	int i; +#endif + +#ifdef CONFIG_SIBYTE_BW_TRACE +	csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG)); +	csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG)); + +	for (i=0; i<256*6; i++) +		printk("%016llx\n", +		       (long long)__raw_readq(IOADDR(A_SCD_TRACE_READ))); + +	csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); +	csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG)); +#endif + +	/* Destructive read, clears register and interrupt */ +	stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS)); +	stats->status_printed = 0; + +	stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS)); +	stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr); +	stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr); +	stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr); +	stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr); +	csr_out32(0, IOADDR(A_BUS_L2_ERRORS)); + +	stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS)); +	stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr); +	stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr); +	stats->bus_error += G_SCD_MEM_BUSERR(cntr); +	csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS)); + +	return IRQ_HANDLED; +} + +int __init sibyte_bus_watcher(void) +{ +	memset(&bw_stats, 0, sizeof(struct bw_stats_struct)); +	bw_stats.status_printed = 1; + +	if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) { +		printk("Failed to register bus watcher BAD_ECC irq\n"); +		return -1; +	} +	if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) { +		free_irq(K_INT_BAD_ECC, &bw_stats); +		printk("Failed to register bus watcher COR_ECC irq\n"); +		return -1; +	} +	if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) { +		free_irq(K_INT_BAD_ECC, &bw_stats); +		free_irq(K_INT_COR_ECC, &bw_stats); +		printk("Failed to register bus watcher IO_BUS irq\n"); +		return -1; +	} + +#ifdef CONFIG_PROC_FS +	create_proc_decoder(&bw_stats); +#endif + +#ifdef CONFIG_SIBYTE_BW_TRACE +	csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE | +		   K_SCD_TRSEQ_TRIGGER_ALL), +		  IOADDR(A_SCD_TRACE_SEQUENCE_0)); +	csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); +	csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG)); +#endif + +	return 0; +} + +__initcall(sibyte_bus_watcher); diff --git a/arch/mips/sibyte/common/cfe.c b/arch/mips/sibyte/common/cfe.c index 6343011e990..588e1806a1a 100644 --- a/arch/mips/sibyte/common/cfe.c +++ b/arch/mips/sibyte/common/cfe.c @@ -127,8 +127,8 @@ static __init void prom_meminit(void)  				if ((initrd_pstart > addr) &&  				    (initrd_pstart < (addr + size))) {  					add_memory_region(addr, -					                  initrd_pstart - addr, -					                  BOOT_MEM_RAM); +							  initrd_pstart - addr, +							  BOOT_MEM_RAM);  					rd_flag = 1;  				}  				if ((initrd_pend > addr) && @@ -195,7 +195,7 @@ static int __init initrd_setup(char *str)  	/*  	 *Initrd location comes in the form "<hex size of ramdisk in bytes>@<location in memory>" -	 *  e.g. initrd=3abfd@80010000.  This is set up by the loader. +	 *  e.g. initrd=3abfd@80010000.	 This is set up by the loader.  	 */  	for (tmp = str; *tmp != '@'; tmp++) {  		if (!*tmp) { @@ -244,7 +244,7 @@ void __init prom_init(void)  	int *prom_vec = (int *) fw_arg3;  	_machine_restart   = cfe_linux_restart; -	_machine_halt      = cfe_linux_halt; +	_machine_halt	   = cfe_linux_halt;  	pm_power_off = cfe_linux_halt;  	/* @@ -299,7 +299,7 @@ void __init prom_init(void)  #ifdef CONFIG_BLK_DEV_INITRD  	{  		char *ptr; -		/* Need to find out early whether we've got an initrd.  So scan +		/* Need to find out early whether we've got an initrd.	So scan  		   the list looking now */  		for (ptr = arcs_cmdline; *ptr; ptr++) {  			while (*ptr == ' ') { diff --git a/arch/mips/sibyte/common/sb_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c index 87ccdb4b5ac..059e28c8fd9 100644 --- a/arch/mips/sibyte/common/sb_tbprof.c +++ b/arch/mips/sibyte/common/sb_tbprof.c @@ -27,6 +27,7 @@  #include <linux/types.h>  #include <linux/init.h>  #include <linux/interrupt.h> +#include <linux/sched.h>  #include <linux/vmalloc.h>  #include <linux/fs.h>  #include <linux/errno.h> @@ -53,7 +54,6 @@  #define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT  #endif -#include <asm/system.h>  #include <asm/uaccess.h>  #define SBPROF_TB_MAJOR 240 @@ -153,7 +153,7 @@ static u64 tb_period;  static void arm_tb(void)  { -        u64 scdperfcnt; +	u64 scdperfcnt;  	u64 next = (1ULL << 40) - tb_period;  	u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL; @@ -258,8 +258,8 @@ static irqreturn_t sbprof_pc_intr(int irq, void *dev_id)  /*   * Requires: Already called zclk_timer_init with a value that won't - *           saturate 40 bits.  No subsequent use of SCD performance counters - *           or trace buffer. + *	     saturate 40 bits.	No subsequent use of SCD performance counters + *	     or trace buffer.   */  static int sbprof_zbprof_start(struct file *filp) @@ -289,8 +289,8 @@ static int sbprof_zbprof_start(struct file *filp)  	/*  	 * We grab this interrupt to prevent others from trying to use -         * it, even though we don't want to service the interrupts -         * (they only feed into the trace-on-interrupt mechanism) +	 * it, even though we don't want to service the interrupts +	 * (they only feed into the trace-on-interrupt mechanism)  	 */  	if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) {  		free_irq(K_INT_TRACE_FREEZE, &sbp); @@ -299,7 +299,7 @@ static int sbprof_zbprof_start(struct file *filp)  	/*  	 * I need the core to mask these, but the interrupt mapper to -	 *  pass them through.  I am exploiting my knowledge that +	 *  pass them through.	I am exploiting my knowledge that  	 *  cp0_status masks out IP[5]. krw  	 */  #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) @@ -329,7 +329,7 @@ static int sbprof_zbprof_start(struct file *filp)  	__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3));  	/* Initialize Trace Event 0-7 */ -	/*				when interrupt  */ +	/*				when interrupt	*/  	__raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0));  	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1));  	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2)); @@ -410,14 +410,13 @@ static int sbprof_tb_open(struct inode *inode, struct file *filp)  		return -EBUSY;  	memset(&sbp, 0, sizeof(struct sbprof_tb)); -	sbp.sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES); +	sbp.sbprof_tbbuf = vzalloc(MAX_TBSAMPLE_BYTES);  	if (!sbp.sbprof_tbbuf) {  		sbp.open = SB_CLOSED;  		wmb();  		return -ENOMEM;  	} -	memset(sbp.sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES);  	init_waitqueue_head(&sbp.tb_sync);  	init_waitqueue_head(&sbp.tb_read);  	mutex_init(&sbp.lock); @@ -481,7 +480,7 @@ static ssize_t sbprof_tb_read(struct file *filp, char *buf,  			return err;  		}  		pr_debug(DEVNAME ": read from sample %d, %d bytes\n", -		         cur_sample, cur_count); +			 cur_sample, cur_count);  		size -= cur_count;  		sample_left -= cur_count;  		if (!sample_left) { @@ -542,7 +541,7 @@ static const struct file_operations sbprof_tb_fops = {  	.open		= sbprof_tb_open,  	.release	= sbprof_tb_release,  	.read		= sbprof_tb_read, -	.unlocked_ioctl	= sbprof_tb_ioctl, +	.unlocked_ioctl = sbprof_tb_ioctl,  	.compat_ioctl	= sbprof_tb_ioctl,  	.mmap		= NULL,  	.llseek		= default_llseek,  | 
