544 lines
15 KiB
C
544 lines
15 KiB
C
/*
|
|
<:copyright-gpl
|
|
Copyright 2007 Broadcom Corp. All Rights Reserved.
|
|
|
|
This program is free software; you can distribute it and/or modify it
|
|
under the terms of the GNU General Public License (Version 2) as
|
|
published by the Free Software Foundation.
|
|
|
|
This program is distributed in the hope 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.
|
|
:>
|
|
*/
|
|
|
|
/************************************************************
|
|
proc_brcm.c
|
|
|
|
procfs entries like proc/shirkmem, proc/brcm/pagewalk and proc/brcm/cstat
|
|
|
|
9/27/2006 Xi Wang Created
|
|
11/12/2008 Xi Wang Updated for 2.6.21
|
|
|
|
|
|
************************************************************/
|
|
|
|
#include <linux/autoconf.h>
|
|
#include <linux/types.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/time.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/tty.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/mmzone.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/times.h>
|
|
#include <linux/profile.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/sysrq.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/pci.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/io.h>
|
|
#include <asm/tlb.h>
|
|
#include <asm/div64.h>
|
|
|
|
|
|
//extern int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len);
|
|
int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len)
|
|
{
|
|
if (len <= off+count) *eof = 1;
|
|
*start = page + off;
|
|
len -= off;
|
|
if (len>count) len = count;
|
|
if (len<0) len = 0;
|
|
return len;
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_PREEMPT_SOFTIRQS)
|
|
|
|
|
|
extern int softirq_preemption;
|
|
|
|
|
|
static int softirq_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
int len=0;
|
|
|
|
len += sprintf(page, "%d\n", softirq_preemption);
|
|
return proc_calc_metrics(page, start, off, count, eof, len);
|
|
}
|
|
|
|
|
|
static int softirq_proc_write(struct file *file, const char *buf, unsigned long count, void *data)
|
|
{
|
|
char ibuf[20];
|
|
int arg;
|
|
|
|
if (count<1 || count>sizeof(ibuf)) {
|
|
return -EFAULT;
|
|
}
|
|
if (copy_from_user(ibuf, buf, count)) {
|
|
return -EFAULT;
|
|
}
|
|
ibuf[count] = 0;
|
|
if (sscanf(ibuf, "%d\n", &arg) == 1) {
|
|
if (arg>=0 && arg <=1) {
|
|
softirq_preemption = arg;
|
|
}
|
|
return count;
|
|
}
|
|
return -EFAULT;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_BRCM_CSTAT
|
|
|
|
|
|
#define PERF_C_INTERVAL (HZ*1)
|
|
#define DIV 1000
|
|
#define N_INST
|
|
#define COUNTER_RESET_V 0xffffffffu
|
|
|
|
#define BRCM_PERFREG_BASE 0xff420000
|
|
typedef struct {
|
|
unsigned global_ctrl;
|
|
unsigned ctrl[2];
|
|
unsigned donottouch[1];
|
|
unsigned counters[4];
|
|
} PerformanceControl;
|
|
#define BRCM_PERF ((volatile PerformanceControl *) BRCM_PERFREG_BASE)
|
|
|
|
struct timer_list cachestat_timer;
|
|
int cachestat_interval = 0;
|
|
|
|
void static brcm_perf_timer_func(unsigned long data);
|
|
static int perf_counters_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
|
|
|
|
|
|
static void cachestat_timer_func(unsigned long data)
|
|
{
|
|
static int tp = 0;
|
|
static int item = 0;
|
|
register unsigned long temp;
|
|
int i,ratio;
|
|
unsigned tdiv = cachestat_interval*DIV;
|
|
unsigned counters[4];
|
|
|
|
for (i=0;i<4;i++) {
|
|
counters[i] = COUNTER_RESET_V - BRCM_PERF->counters[i];
|
|
BRCM_PERF->counters[i]=COUNTER_RESET_V;
|
|
}
|
|
|
|
if (item == 0) {
|
|
printk("TP %d instruction miss %uk/sec\n", tp, counters[0]/tdiv);
|
|
printk("TP %d instruction hit %uk/sec\n", tp, counters[1]/tdiv);
|
|
ratio = (counters[0]+counters[1])? counters[0]*1000/(counters[0]+counters[1]) : 0;
|
|
printk("TP %d miss ratio %u\n", tp, ratio);
|
|
}
|
|
|
|
if (item == 1) {
|
|
printk("TP %d data miss %uk/sec\n", tp, counters[0]/tdiv);
|
|
printk("TP %d data hit %uk/sec\n", tp, counters[1]/tdiv);
|
|
ratio = (counters[0]+counters[1])? counters[0]*1000/(counters[0]+counters[1]) : 0;
|
|
printk("TP %d miss ratio %u\n", tp, ratio);
|
|
}
|
|
|
|
#if defined(N_INST)
|
|
printk("TP %d number of instructions %uk/sec\n", tp, counters[2]/tdiv);
|
|
printk("TP %d number of cycles %uk/sec\n", tp, counters[3]/tdiv);
|
|
#endif
|
|
|
|
if (tp >= 1) {
|
|
printk("\n");
|
|
tp = 0;
|
|
if (item >= 1) {
|
|
item = 0;
|
|
}
|
|
else {
|
|
item++;
|
|
}
|
|
}
|
|
else {
|
|
tp++;
|
|
}
|
|
|
|
if (tp ==0) {
|
|
asm("mfc0 %0,$22,2" : "=d" (temp));
|
|
temp &= 0x3fffffff;
|
|
temp |= 0x00000000;
|
|
asm("mtc0 %0,$22,2" :: "d" (temp));
|
|
}
|
|
else {
|
|
asm("mfc0 %0,$22,2" : "=d" (temp));
|
|
temp &= 0x3fffffff;
|
|
temp |= 0x40000000;
|
|
asm("mtc0 %0,$22,2" :: "d" (temp));
|
|
}
|
|
|
|
if (item == 0) {
|
|
BRCM_PERF->global_ctrl = 0x0;
|
|
BRCM_PERF->global_ctrl = 0x80000018;
|
|
if (tp == 0) {
|
|
BRCM_PERF->ctrl[0] = 0x80188014;
|
|
}
|
|
else {
|
|
BRCM_PERF->ctrl[0] = 0xa018a014;
|
|
}
|
|
}
|
|
|
|
if (item == 1) {
|
|
BRCM_PERF->global_ctrl = 0x0;
|
|
BRCM_PERF->global_ctrl = 0x80000011;
|
|
if (tp == 0) {
|
|
BRCM_PERF->ctrl[0] = 0x80288024;
|
|
}
|
|
else {
|
|
BRCM_PERF->ctrl[0] = 0xa028a024;
|
|
}
|
|
}
|
|
|
|
#if defined(N_INST)
|
|
if (tp ==0) {
|
|
BRCM_PERF->ctrl[1] = 0x80488044;
|
|
}
|
|
else {
|
|
BRCM_PERF->ctrl[1] = 0xa048a044;
|
|
}
|
|
#endif
|
|
|
|
cachestat_timer.expires = jiffies+cachestat_interval*HZ;
|
|
add_timer(&cachestat_timer);
|
|
}
|
|
|
|
|
|
static void cachestat_start()
|
|
{
|
|
int i;
|
|
|
|
printk("Starting cache performance counters..\n\n");
|
|
|
|
init_timer(&cachestat_timer);
|
|
cachestat_timer.expires = jiffies+HZ;
|
|
cachestat_timer.data = 0;
|
|
cachestat_timer.function = cachestat_timer_func;
|
|
|
|
for (i=0;i<4;i++) {
|
|
BRCM_PERF->counters[i]=COUNTER_RESET_V;
|
|
}
|
|
|
|
BRCM_PERF->global_ctrl = 0x80000018;
|
|
BRCM_PERF->global_ctrl = 0x80000011;
|
|
|
|
add_timer(&cachestat_timer);
|
|
}
|
|
|
|
static void cachestat_stop()
|
|
{
|
|
del_timer_sync(&cachestat_timer);
|
|
printk("Cache performance counting stopped..\n");
|
|
}
|
|
|
|
static int cachestat_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
int len=0;
|
|
|
|
len += sprintf(page, "%d\n", cachestat_interval);
|
|
|
|
return proc_calc_metrics(page, start, off, count, eof, len);
|
|
|
|
}
|
|
|
|
|
|
static int cachestat_write_proc(struct file *file, const char *buf, unsigned long count, void *data)
|
|
{
|
|
char ibuf[20];
|
|
int arg;
|
|
|
|
if (count<1 || count>sizeof(ibuf)) {
|
|
return -EFAULT;
|
|
}
|
|
if (copy_from_user(ibuf, buf, count)) {
|
|
return -EFAULT;
|
|
}
|
|
ibuf[count] = 0;
|
|
|
|
if (sscanf(ibuf, "%d\n", &arg) == 1) {
|
|
if (arg>=0) {
|
|
if (arg && !cachestat_interval) {
|
|
cachestat_interval = arg;
|
|
cachestat_start();
|
|
}
|
|
else if (!arg && cachestat_interval) {
|
|
cachestat_interval = arg;
|
|
cachestat_stop();
|
|
}
|
|
else {
|
|
cachestat_interval = arg;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
return -EFAULT;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_BRCM_OLT_FPGA_RESTORE)
|
|
/* These functions save and restore the state of the olt fpga */
|
|
static int olt_fpga_restore_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
char *op;
|
|
struct pci_dev *dev;
|
|
op = page + off;
|
|
*op = '\0';
|
|
*eof = 1;
|
|
dev = pci_get_device(0x1172, 0x0004, NULL);
|
|
if (dev != NULL) {
|
|
printk("found fpga\n");
|
|
printk("save fpga config\n");
|
|
(void)pci_save_state(dev);
|
|
} else {
|
|
printk("no fpga\n");
|
|
return -EFAULT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int olt_fpga_restore_write_proc(struct file *file, const char *buf, unsigned long count, void *data)
|
|
{
|
|
struct pci_dev *dev;
|
|
dev = pci_get_device(0x1172, 0x0004, NULL);
|
|
if (dev != NULL) {
|
|
printk("found fpga\n");
|
|
} else {
|
|
printk("no fpga\n");
|
|
return -EFAULT;
|
|
}
|
|
printk("restore fpga config\n");
|
|
(void)pci_restore_state(dev);
|
|
return count;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if 0
|
|
static int cp0regs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
register unsigned long temp;
|
|
unsigned long *mips_core_base = NULL;
|
|
int len=0;
|
|
|
|
len += sprintf(page+len, "Running on processor %d\n", smp_processor_id());
|
|
|
|
len += sprintf(page+len, "Status = %x\n", __read_32bit_c0_register($12, 0));
|
|
len += sprintf(page+len, "Cause = %x\n", __read_32bit_c0_register($13, 0));
|
|
|
|
len += sprintf(page+len, "BRCM Config_0 = %x\n", __read_32bit_c0_register($22, 0));
|
|
len += sprintf(page+len, "CMT Interrupt = %x\n", __read_32bit_c0_register($22, 1));
|
|
len += sprintf(page+len, "CMT Control = %x\n", __read_32bit_c0_register($22, 2));
|
|
len += sprintf(page+len, "CMT Local = %x\n", __read_32bit_c0_register($22, 3));
|
|
len += sprintf(page+len, "BRCM Config_1 = %x\n", __read_32bit_c0_register($22, 5));
|
|
|
|
temp = __read_32bit_c0_register($22, 6);
|
|
mips_core_base =(unsigned long *) (temp & 0xfffc0000);
|
|
len += sprintf(page+len, "Core Base = %x\n", temp);
|
|
len += sprintf(page+len, "RAC Config (%x) = %x\n", mips_core_base, *mips_core_base);
|
|
len += sprintf(page+len, "RAC Range (%x) = %x\n", mips_core_base+1, *(mips_core_base+1));
|
|
len += sprintf(page+len, "RAC Config1 (%x) = %x\n", mips_core_base+2, *(mips_core_base+2));
|
|
len += sprintf(page+len, "LMB (%x) = %x\n", mips_core_base+7, *(mips_core_base+7));
|
|
|
|
len += sprintf(page+len, "\n");
|
|
|
|
*(page+len) = 0;
|
|
len++;
|
|
return proc_calc_metrics(page, start, off, count, eof, len);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_BRCM_VMTOOLS
|
|
|
|
|
|
#define ALLPAGES (1024*1024)
|
|
|
|
extern int pagewalk(char *print);
|
|
extern int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len);
|
|
extern int meminfo_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
|
|
|
|
|
|
static int shrinkmem_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
int len;
|
|
|
|
len = sprintf(page, "\nTry to free as many pages as possible (most of pages left will be data pages), then show memory info.\n\n");
|
|
|
|
shrink_all_memory(ALLPAGES);
|
|
|
|
len += meminfo_read_proc(page+len, start, off, count, eof, data);
|
|
|
|
return proc_calc_metrics(page, start, off, count, eof, len);
|
|
}
|
|
|
|
|
|
static int pagewalk_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
int len;
|
|
|
|
printk("\nList all occupied memory pages in the system and show what they are used for. ");
|
|
printk("(output generated by printk - i.e. not for file operations)\n\n");
|
|
|
|
len = pagewalk(page);
|
|
|
|
return proc_calc_metrics(page, start, off, count, eof, len);
|
|
}
|
|
|
|
|
|
static int shrinkpagewalk_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
|
|
{
|
|
int len;
|
|
|
|
printk("\nTry to free as many pages as possible (most of pages left will be data pages), then:\n");
|
|
printk("List all occupied memory pages in the system and show what they are used for. ");
|
|
printk("(output generated by printk - i.e. not for file operations)\n\n");
|
|
|
|
shrink_all_memory(ALLPAGES);
|
|
|
|
len = pagewalk(page);
|
|
|
|
return proc_calc_metrics(page, start, off, count, eof, len);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/****************************************************************************
|
|
* This section is for reporting kernel configs
|
|
****************************************************************************/
|
|
|
|
|
|
int bcm_kernel_config_smp=0;
|
|
int bcm_kernel_config_preempt=0;
|
|
int bcm_kernel_config_debug_spinlock=0;
|
|
int bcm_kernel_config_debug_mutexes=0;
|
|
|
|
EXPORT_SYMBOL(bcm_kernel_config_smp);
|
|
EXPORT_SYMBOL(bcm_kernel_config_preempt);
|
|
EXPORT_SYMBOL(bcm_kernel_config_debug_spinlock);
|
|
EXPORT_SYMBOL(bcm_kernel_config_debug_mutexes);
|
|
|
|
static int bcm_kernel_config_show(struct seq_file *m, void *v)
|
|
{
|
|
seq_printf(m, "CONFIG_SMP=%d\n", bcm_kernel_config_smp);
|
|
seq_printf(m, "CONFIG_PREEMPT=%d\n", bcm_kernel_config_preempt);
|
|
seq_printf(m, "CONFIG_DEBUG_SPINLOCK=%d\n", bcm_kernel_config_debug_spinlock);
|
|
seq_printf(m, "CONFIG_DEBUG_MUTEXES=%d\n", bcm_kernel_config_debug_mutexes);
|
|
return 0;
|
|
}
|
|
|
|
static int bcm_kernel_config_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, bcm_kernel_config_show, NULL);
|
|
}
|
|
|
|
static const struct file_operations proc_kernel_config_operations = {
|
|
.open = bcm_kernel_config_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release,
|
|
};
|
|
|
|
static int __init bcm_kernel_config_init(struct proc_dir_entry *pentry)
|
|
{
|
|
|
|
#ifdef CONFIG_SMP
|
|
bcm_kernel_config_smp=1;
|
|
#endif
|
|
#ifdef CONFIG_PREEMPT
|
|
bcm_kernel_config_preempt=1;
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_SPINLOCK
|
|
bcm_kernel_config_debug_spinlock=1;
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_MUTEXES
|
|
bcm_kernel_config_debug_mutexes=1;
|
|
#endif
|
|
|
|
printk(KERN_INFO "--Kernel Config--\n");
|
|
printk(KERN_INFO " SMP=%d\n", bcm_kernel_config_smp);
|
|
printk(KERN_INFO " PREEMPT=%d\n", bcm_kernel_config_preempt);
|
|
printk(KERN_INFO " DEBUG_SPINLOCK=%d\n", bcm_kernel_config_debug_spinlock);
|
|
printk(KERN_INFO " DEBUG_MUTEXES=%d\n", bcm_kernel_config_debug_mutexes);
|
|
|
|
proc_create("kernel_config", 0, pentry, &proc_kernel_config_operations);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* Entry point from proc_root_init
|
|
****************************************************************************/
|
|
void __init proc_brcm_init(struct proc_dir_entry *pentry)
|
|
{
|
|
|
|
#if defined(CONFIG_PREEMPT_SOFTIRQS) || defined(CONFIG_BRCM_CSTAT)
|
|
struct proc_dir_entry *entry;
|
|
#endif
|
|
|
|
#ifdef CONFIG_BRCM_VMTOOLS
|
|
create_proc_read_entry("shrinkmem", 0, pentry, shrinkmem_read_proc, NULL);
|
|
create_proc_read_entry("pagewalk", 0, pentry, pagewalk_read_proc, NULL);
|
|
create_proc_read_entry("shrinkpagewalk", 0, pentry, shrinkpagewalk_read_proc, NULL);
|
|
#endif
|
|
|
|
#if defined(CONFIG_PREEMPT_SOFTIRQS)
|
|
entry = create_proc_entry("softirq_in_kthread", 0, pentry);
|
|
entry->read_proc = softirq_proc_read;
|
|
entry->write_proc = softirq_proc_write;
|
|
#endif
|
|
|
|
#ifdef CONFIG_BRCM_CSTAT
|
|
entry = create_proc_entry("cstat", 0, pentry);
|
|
entry->read_proc = cachestat_read_proc;
|
|
entry->write_proc = cachestat_write_proc;
|
|
#endif
|
|
|
|
#if defined(CONFIG_BRCM_OLT_FPGA_RESTORE)
|
|
entry = create_proc_entry("olt_fpga_restore", 0, pentry);
|
|
entry->read_proc = olt_fpga_restore_read_proc;
|
|
entry->write_proc = olt_fpga_restore_write_proc;
|
|
#endif
|
|
|
|
#if 0
|
|
create_proc_read_entry("cp0regs", 0, pentry, cp0regs_read_proc, NULL);
|
|
#endif
|
|
|
|
bcm_kernel_config_init(pentry);
|
|
}
|
|
|