📄 sb_tbprof.c
字号:
/* * 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. * * Copyright (C) 2001, 2002, 2003 Broadcom Corporation * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org> * Copyright (C) 2007 MIPS Technologies, Inc. * written by Ralf Baechle <ralf@linux-mips.org> */#undef DEBUG#include <linux/device.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/wait.h>#include <asm/io.h>#include <asm/sibyte/sb1250.h>#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)#include <asm/sibyte/bcm1480_regs.h>#include <asm/sibyte/bcm1480_scd.h>#include <asm/sibyte/bcm1480_int.h>#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)#include <asm/sibyte/sb1250_regs.h>#include <asm/sibyte/sb1250_scd.h>#include <asm/sibyte/sb1250_int.h>#else#error invalid SiByte UART configuation#endif#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)#undef K_INT_TRACE_FREEZE#define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE#undef K_INT_PERF_CNT#define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT#endif#include <asm/system.h>#include <asm/uaccess.h>#define SBPROF_TB_MAJOR 240typedef u64 tb_sample_t[6*256];enum open_status { SB_CLOSED, SB_OPENING, SB_OPEN};struct sbprof_tb { wait_queue_head_t tb_sync; wait_queue_head_t tb_read; struct mutex lock; enum open_status open; tb_sample_t *sbprof_tbbuf; int next_tb_sample; volatile int tb_enable; volatile int tb_armed;};static struct sbprof_tb sbp;#define MAX_SAMPLE_BYTES (24*1024*1024)#define MAX_TBSAMPLE_BYTES (12*1024*1024)#define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t))#define TB_SAMPLE_SIZE (sizeof(tb_sample_t))#define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE)/* ioctls */#define SBPROF_ZBSTART _IOW('s', 0, int)#define SBPROF_ZBSTOP _IOW('s', 1, int)#define SBPROF_ZBWAITFULL _IOW('s', 2, int)/* * Routines for using 40-bit SCD cycle counter * * Client responsible for either handling interrupts or making sure * the cycles counter never saturates, e.g., by doing * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs. *//* * Configures SCD counter 0 to count ZCLKs starting from val; * Configures SCD counters1,2,3 to count nothing. * Must not be called while gathering ZBbus profiles. */#define zclk_timer_init(val) \ __asm__ __volatile__ (".set push;" \ ".set mips64;" \ "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ "sd %0, 0x10($8);" /* write val to counter0 */ \ "sd %1, 0($8);" /* config counter0 for zclks*/ \ ".set pop" \ : /* no outputs */ \ /* enable, counter0 */ \ : /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \ : /* modifies */ "$8" )/* Reads SCD counter 0 and puts result in value unsigned long long val; */#define zclk_get(val) \ __asm__ __volatile__ (".set push;" \ ".set mips64;" \ "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ "ld %0, 0x10($8);" /* write val to counter0 */ \ ".set pop" \ : /* outputs */ "=r"(val) \ : /* inputs */ \ : /* modifies */ "$8" )#define DEVNAME "sb_tbprof"#define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)/* * Support for ZBbus sampling using the trace buffer * * We use the SCD performance counter interrupt, caused by a Zclk counter * overflow, to trigger the start of tracing. * * We set the trace buffer to sample everything and freeze on * overflow. * * We map the interrupt for trace_buffer_freeze to handle it on CPU 0. * */static u64 tb_period;static void arm_tb(void){ u64 scdperfcnt; u64 next = (1ULL << 40) - tb_period; u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL; /* * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to * trigger start of trace. XXX vary sampling period */ __raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1)); scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); /* * Unfortunately, in Pass 2 we must clear all counters to knock down * a previous interrupt request. This means that bus profiling * requires ALL of the SCD perf counters. */#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) | /* keep counters 0,2,3,4,5,6,7 as is */ V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0)); __raw_writeq( M_SPC_CFG_ENABLE | /* enable counting */ M_SPC_CFG_CLEAR | /* clear all counters */ V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1));#else __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) | /* keep counters 0,2,3 as is */ M_SPC_CFG_ENABLE | /* enable counting */ M_SPC_CFG_CLEAR | /* clear all counters */ V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ IOADDR(A_SCD_PERF_CNT_CFG));#endif __raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1)); /* Reset the trace buffer */ __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT) /* XXXKW may want to expose control to the data-collector */ tb_options |= M_SCD_TRACE_CFG_FORCECNT;#endif __raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG)); sbp.tb_armed = 1;}static irqreturn_t sbprof_tb_intr(int irq, void *dev_id){ int i; pr_debug(DEVNAME ": tb_intr\n"); if (sbp.next_tb_sample < MAX_TB_SAMPLES) { /* XXX should use XKPHYS to make writes bypass L2 */ u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++]; /* Read out trace */ __raw_writeq(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG)); __asm__ __volatile__ ("sync" : : : "memory"); /* Loop runs backwards because bundles are read out in reverse order */ for (i = 256 * 6; i > 0; i -= 6) { /* Subscripts decrease to put bundle in the order */ /* t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */ p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); /* read t2 hi */ p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); /* read t2 lo */ p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); /* read t1 hi */ p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); /* read t1 lo */ p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); /* read t0 hi */ p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); /* read t0 lo */ } if (!sbp.tb_enable) { pr_debug(DEVNAME ": tb_intr shutdown\n"); __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); sbp.tb_armed = 0; wake_up_interruptible(&sbp.tb_sync); } else { /* knock down current interrupt and get another one later */ arm_tb(); } } else { /* No more trace buffer samples */ pr_debug(DEVNAME ": tb_intr full\n"); __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); sbp.tb_armed = 0; if (!sbp.tb_enable) wake_up_interruptible(&sbp.tb_sync); wake_up_interruptible(&sbp.tb_read); } return IRQ_HANDLED;}static irqreturn_t sbprof_pc_intr(int irq, void *dev_id){ printk(DEVNAME ": unexpected pc_intr"); return IRQ_NONE;}/* * 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. */static int sbprof_zbprof_start(struct file *filp){ u64 scdperfcnt; int err; if (xchg(&sbp.tb_enable, 1)) return -EBUSY; pr_debug(DEVNAME ": starting\n"); sbp.next_tb_sample = 0; filp->f_pos = 0; err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0, DEVNAME " trace freeze", &sbp); if (err) return -EBUSY; /* Make sure there isn't a perf-cnt interrupt waiting */ scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); /* Disable and clear counters, override SRC_1 */ __raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) | M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1), IOADDR(A_SCD_PERF_CNT_CFG)); /* * 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) */ if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) { free_irq(K_INT_TRACE_FREEZE, &sbp); return -EBUSY; } /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -