162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2001, 2002, 2003 Broadcom Corporation 562306a36Sopenharmony_ci * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org> 662306a36Sopenharmony_ci * Copyright (C) 2007 MIPS Technologies, Inc. 762306a36Sopenharmony_ci * written by Ralf Baechle <ralf@linux-mips.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#undef DEBUG 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/vmalloc.h> 2062306a36Sopenharmony_ci#include <linux/fs.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/wait.h> 2362306a36Sopenharmony_ci#include <asm/io.h> 2462306a36Sopenharmony_ci#include <asm/sibyte/sb1250.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_BCM1x80 2762306a36Sopenharmony_ci#include <asm/sibyte/bcm1480_regs.h> 2862306a36Sopenharmony_ci#include <asm/sibyte/bcm1480_scd.h> 2962306a36Sopenharmony_ci#include <asm/sibyte/bcm1480_int.h> 3062306a36Sopenharmony_ci#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X) 3162306a36Sopenharmony_ci#include <asm/sibyte/sb1250_regs.h> 3262306a36Sopenharmony_ci#include <asm/sibyte/sb1250_scd.h> 3362306a36Sopenharmony_ci#include <asm/sibyte/sb1250_int.h> 3462306a36Sopenharmony_ci#else 3562306a36Sopenharmony_ci#error invalid SiByte UART configuration 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_BCM1x80 3962306a36Sopenharmony_ci#undef K_INT_TRACE_FREEZE 4062306a36Sopenharmony_ci#define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE 4162306a36Sopenharmony_ci#undef K_INT_PERF_CNT 4262306a36Sopenharmony_ci#define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT 4362306a36Sopenharmony_ci#endif 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include <linux/uaccess.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define SBPROF_TB_MAJOR 240 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_citypedef u64 tb_sample_t[6*256]; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cienum open_status { 5262306a36Sopenharmony_ci SB_CLOSED, 5362306a36Sopenharmony_ci SB_OPENING, 5462306a36Sopenharmony_ci SB_OPEN 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistruct sbprof_tb { 5862306a36Sopenharmony_ci wait_queue_head_t tb_sync; 5962306a36Sopenharmony_ci wait_queue_head_t tb_read; 6062306a36Sopenharmony_ci struct mutex lock; 6162306a36Sopenharmony_ci enum open_status open; 6262306a36Sopenharmony_ci tb_sample_t *sbprof_tbbuf; 6362306a36Sopenharmony_ci int next_tb_sample; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci volatile int tb_enable; 6662306a36Sopenharmony_ci volatile int tb_armed; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct sbprof_tb sbp; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define MAX_SAMPLE_BYTES (24*1024*1024) 7362306a36Sopenharmony_ci#define MAX_TBSAMPLE_BYTES (12*1024*1024) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t)) 7662306a36Sopenharmony_ci#define TB_SAMPLE_SIZE (sizeof(tb_sample_t)) 7762306a36Sopenharmony_ci#define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* ioctls */ 8062306a36Sopenharmony_ci#define SBPROF_ZBSTART _IOW('s', 0, int) 8162306a36Sopenharmony_ci#define SBPROF_ZBSTOP _IOW('s', 1, int) 8262306a36Sopenharmony_ci#define SBPROF_ZBWAITFULL _IOW('s', 2, int) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * Routines for using 40-bit SCD cycle counter 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Client responsible for either handling interrupts or making sure 8862306a36Sopenharmony_ci * the cycles counter never saturates, e.g., by doing 8962306a36Sopenharmony_ci * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * Configures SCD counter 0 to count ZCLKs starting from val; 9462306a36Sopenharmony_ci * Configures SCD counters1,2,3 to count nothing. 9562306a36Sopenharmony_ci * Must not be called while gathering ZBbus profiles. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define zclk_timer_init(val) \ 9962306a36Sopenharmony_ci __asm__ __volatile__ (".set push;" \ 10062306a36Sopenharmony_ci ".set mips64;" \ 10162306a36Sopenharmony_ci "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ 10262306a36Sopenharmony_ci "sd %0, 0x10($8);" /* write val to counter0 */ \ 10362306a36Sopenharmony_ci "sd %1, 0($8);" /* config counter0 for zclks*/ \ 10462306a36Sopenharmony_ci ".set pop" \ 10562306a36Sopenharmony_ci : /* no outputs */ \ 10662306a36Sopenharmony_ci /* enable, counter0 */ \ 10762306a36Sopenharmony_ci : /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \ 10862306a36Sopenharmony_ci : /* modifies */ "$8" ) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Reads SCD counter 0 and puts result in value 11262306a36Sopenharmony_ci unsigned long long val; */ 11362306a36Sopenharmony_ci#define zclk_get(val) \ 11462306a36Sopenharmony_ci __asm__ __volatile__ (".set push;" \ 11562306a36Sopenharmony_ci ".set mips64;" \ 11662306a36Sopenharmony_ci "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \ 11762306a36Sopenharmony_ci "ld %0, 0x10($8);" /* write val to counter0 */ \ 11862306a36Sopenharmony_ci ".set pop" \ 11962306a36Sopenharmony_ci : /* outputs */ "=r"(val) \ 12062306a36Sopenharmony_ci : /* inputs */ \ 12162306a36Sopenharmony_ci : /* modifies */ "$8" ) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define DEVNAME "sb_tbprof" 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * Support for ZBbus sampling using the trace buffer 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * We use the SCD performance counter interrupt, caused by a Zclk counter 13162306a36Sopenharmony_ci * overflow, to trigger the start of tracing. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * We set the trace buffer to sample everything and freeze on 13462306a36Sopenharmony_ci * overflow. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * We map the interrupt for trace_buffer_freeze to handle it on CPU 0. 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic u64 tb_period; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void arm_tb(void) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci u64 scdperfcnt; 14562306a36Sopenharmony_ci u64 next = (1ULL << 40) - tb_period; 14662306a36Sopenharmony_ci u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to 15062306a36Sopenharmony_ci * trigger start of trace. XXX vary sampling period 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1)); 15362306a36Sopenharmony_ci scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * Unfortunately, in Pass 2 we must clear all counters to knock down 15762306a36Sopenharmony_ci * a previous interrupt request. This means that bus profiling 15862306a36Sopenharmony_ci * requires ALL of the SCD perf counters. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_BCM1x80 16162306a36Sopenharmony_ci __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) | 16262306a36Sopenharmony_ci /* keep counters 0,2,3,4,5,6,7 as is */ 16362306a36Sopenharmony_ci V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ 16462306a36Sopenharmony_ci IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0)); 16562306a36Sopenharmony_ci __raw_writeq( 16662306a36Sopenharmony_ci M_SPC_CFG_ENABLE | /* enable counting */ 16762306a36Sopenharmony_ci M_SPC_CFG_CLEAR | /* clear all counters */ 16862306a36Sopenharmony_ci V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ 16962306a36Sopenharmony_ci IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1)); 17062306a36Sopenharmony_ci#else 17162306a36Sopenharmony_ci __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) | 17262306a36Sopenharmony_ci /* keep counters 0,2,3 as is */ 17362306a36Sopenharmony_ci M_SPC_CFG_ENABLE | /* enable counting */ 17462306a36Sopenharmony_ci M_SPC_CFG_CLEAR | /* clear all counters */ 17562306a36Sopenharmony_ci V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */ 17662306a36Sopenharmony_ci IOADDR(A_SCD_PERF_CNT_CFG)); 17762306a36Sopenharmony_ci#endif 17862306a36Sopenharmony_ci __raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1)); 17962306a36Sopenharmony_ci /* Reset the trace buffer */ 18062306a36Sopenharmony_ci __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); 18162306a36Sopenharmony_ci#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT) 18262306a36Sopenharmony_ci /* XXXKW may want to expose control to the data-collector */ 18362306a36Sopenharmony_ci tb_options |= M_SCD_TRACE_CFG_FORCECNT; 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci __raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG)); 18662306a36Sopenharmony_ci sbp.tb_armed = 1; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic irqreturn_t sbprof_tb_intr(int irq, void *dev_id) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int i; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci pr_debug(DEVNAME ": tb_intr\n"); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (sbp.next_tb_sample < MAX_TB_SAMPLES) { 19662306a36Sopenharmony_ci /* XXX should use XKPHYS to make writes bypass L2 */ 19762306a36Sopenharmony_ci u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++]; 19862306a36Sopenharmony_ci /* Read out trace */ 19962306a36Sopenharmony_ci __raw_writeq(M_SCD_TRACE_CFG_START_READ, 20062306a36Sopenharmony_ci IOADDR(A_SCD_TRACE_CFG)); 20162306a36Sopenharmony_ci __asm__ __volatile__ ("sync" : : : "memory"); 20262306a36Sopenharmony_ci /* Loop runs backwards because bundles are read out in reverse order */ 20362306a36Sopenharmony_ci for (i = 256 * 6; i > 0; i -= 6) { 20462306a36Sopenharmony_ci /* Subscripts decrease to put bundle in the order */ 20562306a36Sopenharmony_ci /* t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */ 20662306a36Sopenharmony_ci p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); 20762306a36Sopenharmony_ci /* read t2 hi */ 20862306a36Sopenharmony_ci p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); 20962306a36Sopenharmony_ci /* read t2 lo */ 21062306a36Sopenharmony_ci p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); 21162306a36Sopenharmony_ci /* read t1 hi */ 21262306a36Sopenharmony_ci p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); 21362306a36Sopenharmony_ci /* read t1 lo */ 21462306a36Sopenharmony_ci p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); 21562306a36Sopenharmony_ci /* read t0 hi */ 21662306a36Sopenharmony_ci p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ)); 21762306a36Sopenharmony_ci /* read t0 lo */ 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci if (!sbp.tb_enable) { 22062306a36Sopenharmony_ci pr_debug(DEVNAME ": tb_intr shutdown\n"); 22162306a36Sopenharmony_ci __raw_writeq(M_SCD_TRACE_CFG_RESET, 22262306a36Sopenharmony_ci IOADDR(A_SCD_TRACE_CFG)); 22362306a36Sopenharmony_ci sbp.tb_armed = 0; 22462306a36Sopenharmony_ci wake_up_interruptible(&sbp.tb_sync); 22562306a36Sopenharmony_ci } else { 22662306a36Sopenharmony_ci /* knock down current interrupt and get another one later */ 22762306a36Sopenharmony_ci arm_tb(); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } else { 23062306a36Sopenharmony_ci /* No more trace buffer samples */ 23162306a36Sopenharmony_ci pr_debug(DEVNAME ": tb_intr full\n"); 23262306a36Sopenharmony_ci __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG)); 23362306a36Sopenharmony_ci sbp.tb_armed = 0; 23462306a36Sopenharmony_ci if (!sbp.tb_enable) 23562306a36Sopenharmony_ci wake_up_interruptible(&sbp.tb_sync); 23662306a36Sopenharmony_ci wake_up_interruptible(&sbp.tb_read); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci return IRQ_HANDLED; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic irqreturn_t sbprof_pc_intr(int irq, void *dev_id) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci printk(DEVNAME ": unexpected pc_intr"); 24462306a36Sopenharmony_ci return IRQ_NONE; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Requires: Already called zclk_timer_init with a value that won't 24962306a36Sopenharmony_ci * saturate 40 bits. No subsequent use of SCD performance counters 25062306a36Sopenharmony_ci * or trace buffer. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int sbprof_zbprof_start(struct file *filp) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci u64 scdperfcnt; 25662306a36Sopenharmony_ci int err; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (xchg(&sbp.tb_enable, 1)) 25962306a36Sopenharmony_ci return -EBUSY; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci pr_debug(DEVNAME ": starting\n"); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci sbp.next_tb_sample = 0; 26462306a36Sopenharmony_ci filp->f_pos = 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0, 26762306a36Sopenharmony_ci DEVNAME " trace freeze", &sbp); 26862306a36Sopenharmony_ci if (err) 26962306a36Sopenharmony_ci return -EBUSY; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Make sure there isn't a perf-cnt interrupt waiting */ 27262306a36Sopenharmony_ci scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG)); 27362306a36Sopenharmony_ci /* Disable and clear counters, override SRC_1 */ 27462306a36Sopenharmony_ci __raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) | 27562306a36Sopenharmony_ci M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1), 27662306a36Sopenharmony_ci IOADDR(A_SCD_PERF_CNT_CFG)); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* 27962306a36Sopenharmony_ci * We grab this interrupt to prevent others from trying to use 28062306a36Sopenharmony_ci * it, even though we don't want to service the interrupts 28162306a36Sopenharmony_ci * (they only feed into the trace-on-interrupt mechanism) 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) { 28462306a36Sopenharmony_ci free_irq(K_INT_TRACE_FREEZE, &sbp); 28562306a36Sopenharmony_ci return -EBUSY; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * I need the core to mask these, but the interrupt mapper to 29062306a36Sopenharmony_ci * pass them through. I am exploiting my knowledge that 29162306a36Sopenharmony_ci * cp0_status masks out IP[5]. krw 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_BCM1x80 29462306a36Sopenharmony_ci __raw_writeq(K_BCM1480_INT_MAP_I3, 29562306a36Sopenharmony_ci IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) + 29662306a36Sopenharmony_ci ((K_BCM1480_INT_PERF_CNT & 0x3f) << 3))); 29762306a36Sopenharmony_ci#else 29862306a36Sopenharmony_ci __raw_writeq(K_INT_MAP_I3, 29962306a36Sopenharmony_ci IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + 30062306a36Sopenharmony_ci (K_INT_PERF_CNT << 3))); 30162306a36Sopenharmony_ci#endif 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Initialize address traps */ 30462306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_0)); 30562306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_1)); 30662306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_2)); 30762306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_3)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0)); 31062306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1)); 31162306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2)); 31262306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3)); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0)); 31562306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1)); 31662306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2)); 31762306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3)); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Initialize Trace Event 0-7 */ 32062306a36Sopenharmony_ci /* when interrupt */ 32162306a36Sopenharmony_ci __raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0)); 32262306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1)); 32362306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2)); 32462306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3)); 32562306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4)); 32662306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5)); 32762306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6)); 32862306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7)); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Initialize Trace Sequence 0-7 */ 33162306a36Sopenharmony_ci /* Start on event 0 (interrupt) */ 33262306a36Sopenharmony_ci __raw_writeq(V_SCD_TRSEQ_FUNC_START | 0x0fff, 33362306a36Sopenharmony_ci IOADDR(A_SCD_TRACE_SEQUENCE_0)); 33462306a36Sopenharmony_ci /* dsamp when d used | asamp when a used */ 33562306a36Sopenharmony_ci __raw_writeq(M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE | 33662306a36Sopenharmony_ci K_SCD_TRSEQ_TRIGGER_ALL, 33762306a36Sopenharmony_ci IOADDR(A_SCD_TRACE_SEQUENCE_1)); 33862306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2)); 33962306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3)); 34062306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4)); 34162306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5)); 34262306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6)); 34362306a36Sopenharmony_ci __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7)); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */ 34662306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_BCM1x80 34762306a36Sopenharmony_ci __raw_writeq(1ULL << (K_BCM1480_INT_PERF_CNT & 0x3f), 34862306a36Sopenharmony_ci IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_TRACE_L))); 34962306a36Sopenharmony_ci#else 35062306a36Sopenharmony_ci __raw_writeq(1ULL << K_INT_PERF_CNT, 35162306a36Sopenharmony_ci IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE))); 35262306a36Sopenharmony_ci#endif 35362306a36Sopenharmony_ci arm_tb(); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci pr_debug(DEVNAME ": done starting\n"); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int sbprof_zbprof_stop(void) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci int err = 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci pr_debug(DEVNAME ": stopping\n"); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (sbp.tb_enable) { 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * XXXKW there is a window here where the intr handler may run, 36962306a36Sopenharmony_ci * see the disable, and do the wake_up before this sleep 37062306a36Sopenharmony_ci * happens. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci pr_debug(DEVNAME ": wait for disarm\n"); 37362306a36Sopenharmony_ci err = wait_event_interruptible(sbp.tb_sync, !sbp.tb_armed); 37462306a36Sopenharmony_ci pr_debug(DEVNAME ": disarm complete, stat %d\n", err); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (err) 37762306a36Sopenharmony_ci return err; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci sbp.tb_enable = 0; 38062306a36Sopenharmony_ci free_irq(K_INT_TRACE_FREEZE, &sbp); 38162306a36Sopenharmony_ci free_irq(K_INT_PERF_CNT, &sbp); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci pr_debug(DEVNAME ": done stopping\n"); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return err; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int sbprof_tb_open(struct inode *inode, struct file *filp) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int minor; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci minor = iminor(inode); 39462306a36Sopenharmony_ci if (minor != 0) 39562306a36Sopenharmony_ci return -ENODEV; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (xchg(&sbp.open, SB_OPENING) != SB_CLOSED) 39862306a36Sopenharmony_ci return -EBUSY; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci memset(&sbp, 0, sizeof(struct sbprof_tb)); 40162306a36Sopenharmony_ci sbp.sbprof_tbbuf = vzalloc(MAX_TBSAMPLE_BYTES); 40262306a36Sopenharmony_ci if (!sbp.sbprof_tbbuf) { 40362306a36Sopenharmony_ci sbp.open = SB_CLOSED; 40462306a36Sopenharmony_ci wmb(); 40562306a36Sopenharmony_ci return -ENOMEM; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci init_waitqueue_head(&sbp.tb_sync); 40962306a36Sopenharmony_ci init_waitqueue_head(&sbp.tb_read); 41062306a36Sopenharmony_ci mutex_init(&sbp.lock); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci sbp.open = SB_OPEN; 41362306a36Sopenharmony_ci wmb(); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int sbprof_tb_release(struct inode *inode, struct file *filp) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci int minor; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci minor = iminor(inode); 42362306a36Sopenharmony_ci if (minor != 0 || sbp.open != SB_CLOSED) 42462306a36Sopenharmony_ci return -ENODEV; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci mutex_lock(&sbp.lock); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (sbp.tb_armed || sbp.tb_enable) 42962306a36Sopenharmony_ci sbprof_zbprof_stop(); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci vfree(sbp.sbprof_tbbuf); 43262306a36Sopenharmony_ci sbp.open = SB_CLOSED; 43362306a36Sopenharmony_ci wmb(); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci mutex_unlock(&sbp.lock); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic ssize_t sbprof_tb_read(struct file *filp, char __user *buf, 44162306a36Sopenharmony_ci size_t size, loff_t *offp) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int cur_sample, sample_off, cur_count, sample_left; 44462306a36Sopenharmony_ci char *src; 44562306a36Sopenharmony_ci int count = 0; 44662306a36Sopenharmony_ci char __user *dest = buf; 44762306a36Sopenharmony_ci long cur_off = *offp; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!access_ok(buf, size)) 45062306a36Sopenharmony_ci return -EFAULT; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci mutex_lock(&sbp.lock); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci count = 0; 45562306a36Sopenharmony_ci cur_sample = cur_off / TB_SAMPLE_SIZE; 45662306a36Sopenharmony_ci sample_off = cur_off % TB_SAMPLE_SIZE; 45762306a36Sopenharmony_ci sample_left = TB_SAMPLE_SIZE - sample_off; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci while (size && (cur_sample < sbp.next_tb_sample)) { 46062306a36Sopenharmony_ci int err; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci cur_count = size < sample_left ? size : sample_left; 46362306a36Sopenharmony_ci src = (char *)(((long)sbp.sbprof_tbbuf[cur_sample])+sample_off); 46462306a36Sopenharmony_ci err = __copy_to_user(dest, src, cur_count); 46562306a36Sopenharmony_ci if (err) { 46662306a36Sopenharmony_ci *offp = cur_off + cur_count - err; 46762306a36Sopenharmony_ci mutex_unlock(&sbp.lock); 46862306a36Sopenharmony_ci return err; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci pr_debug(DEVNAME ": read from sample %d, %d bytes\n", 47162306a36Sopenharmony_ci cur_sample, cur_count); 47262306a36Sopenharmony_ci size -= cur_count; 47362306a36Sopenharmony_ci sample_left -= cur_count; 47462306a36Sopenharmony_ci if (!sample_left) { 47562306a36Sopenharmony_ci cur_sample++; 47662306a36Sopenharmony_ci sample_off = 0; 47762306a36Sopenharmony_ci sample_left = TB_SAMPLE_SIZE; 47862306a36Sopenharmony_ci } else { 47962306a36Sopenharmony_ci sample_off += cur_count; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci cur_off += cur_count; 48262306a36Sopenharmony_ci dest += cur_count; 48362306a36Sopenharmony_ci count += cur_count; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci *offp = cur_off; 48662306a36Sopenharmony_ci mutex_unlock(&sbp.lock); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return count; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic long sbprof_tb_ioctl(struct file *filp, 49262306a36Sopenharmony_ci unsigned int command, 49362306a36Sopenharmony_ci unsigned long arg) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci int err = 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci switch (command) { 49862306a36Sopenharmony_ci case SBPROF_ZBSTART: 49962306a36Sopenharmony_ci mutex_lock(&sbp.lock); 50062306a36Sopenharmony_ci err = sbprof_zbprof_start(filp); 50162306a36Sopenharmony_ci mutex_unlock(&sbp.lock); 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci case SBPROF_ZBSTOP: 50562306a36Sopenharmony_ci mutex_lock(&sbp.lock); 50662306a36Sopenharmony_ci err = sbprof_zbprof_stop(); 50762306a36Sopenharmony_ci mutex_unlock(&sbp.lock); 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci case SBPROF_ZBWAITFULL: { 51162306a36Sopenharmony_ci err = wait_event_interruptible(sbp.tb_read, TB_FULL); 51262306a36Sopenharmony_ci if (err) 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci err = put_user(TB_FULL, (int __user *) arg); 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci default: 52062306a36Sopenharmony_ci err = -EINVAL; 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return err; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic const struct file_operations sbprof_tb_fops = { 52862306a36Sopenharmony_ci .owner = THIS_MODULE, 52962306a36Sopenharmony_ci .open = sbprof_tb_open, 53062306a36Sopenharmony_ci .release = sbprof_tb_release, 53162306a36Sopenharmony_ci .read = sbprof_tb_read, 53262306a36Sopenharmony_ci .unlocked_ioctl = sbprof_tb_ioctl, 53362306a36Sopenharmony_ci .compat_ioctl = sbprof_tb_ioctl, 53462306a36Sopenharmony_ci .mmap = NULL, 53562306a36Sopenharmony_ci .llseek = default_llseek, 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic struct class *tb_class; 53962306a36Sopenharmony_cistatic struct device *tb_dev; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int __init sbprof_tb_init(void) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct device *dev; 54462306a36Sopenharmony_ci struct class *tbc; 54562306a36Sopenharmony_ci int err; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) { 54862306a36Sopenharmony_ci printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n", 54962306a36Sopenharmony_ci SBPROF_TB_MAJOR); 55062306a36Sopenharmony_ci return -EIO; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci tbc = class_create("sb_tracebuffer"); 55462306a36Sopenharmony_ci if (IS_ERR(tbc)) { 55562306a36Sopenharmony_ci err = PTR_ERR(tbc); 55662306a36Sopenharmony_ci goto out_chrdev; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci tb_class = tbc; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), NULL, "tb"); 56262306a36Sopenharmony_ci if (IS_ERR(dev)) { 56362306a36Sopenharmony_ci err = PTR_ERR(dev); 56462306a36Sopenharmony_ci goto out_class; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci tb_dev = dev; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci sbp.open = SB_CLOSED; 56962306a36Sopenharmony_ci wmb(); 57062306a36Sopenharmony_ci tb_period = zbbus_mhz * 10000LL; 57162306a36Sopenharmony_ci pr_info(DEVNAME ": initialized - tb_period = %lld\n", 57262306a36Sopenharmony_ci (long long) tb_period); 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ciout_class: 57662306a36Sopenharmony_ci class_destroy(tb_class); 57762306a36Sopenharmony_ciout_chrdev: 57862306a36Sopenharmony_ci unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return err; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic void __exit sbprof_tb_cleanup(void) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci device_destroy(tb_class, MKDEV(SBPROF_TB_MAJOR, 0)); 58662306a36Sopenharmony_ci unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME); 58762306a36Sopenharmony_ci class_destroy(tb_class); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cimodule_init(sbprof_tb_init); 59162306a36Sopenharmony_cimodule_exit(sbprof_tb_cleanup); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(SBPROF_TB_MAJOR); 59462306a36Sopenharmony_ciMODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); 59562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 596