xref: /OK3568_Linux_fs/kernel/arch/mips/sibyte/common/sb_tbprof.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2001, 2002, 2003 Broadcom Corporation
5*4882a593Smuzhiyun  * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org>
6*4882a593Smuzhiyun  * Copyright (C) 2007 MIPS Technologies, Inc.
7*4882a593Smuzhiyun  *    written by Ralf Baechle <ralf@linux-mips.org>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #undef DEBUG
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/device.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/types.h>
16*4882a593Smuzhiyun #include <linux/init.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/sched.h>
19*4882a593Smuzhiyun #include <linux/vmalloc.h>
20*4882a593Smuzhiyun #include <linux/fs.h>
21*4882a593Smuzhiyun #include <linux/errno.h>
22*4882a593Smuzhiyun #include <linux/wait.h>
23*4882a593Smuzhiyun #include <asm/io.h>
24*4882a593Smuzhiyun #include <asm/sibyte/sb1250.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
27*4882a593Smuzhiyun #include <asm/sibyte/bcm1480_regs.h>
28*4882a593Smuzhiyun #include <asm/sibyte/bcm1480_scd.h>
29*4882a593Smuzhiyun #include <asm/sibyte/bcm1480_int.h>
30*4882a593Smuzhiyun #elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
31*4882a593Smuzhiyun #include <asm/sibyte/sb1250_regs.h>
32*4882a593Smuzhiyun #include <asm/sibyte/sb1250_scd.h>
33*4882a593Smuzhiyun #include <asm/sibyte/sb1250_int.h>
34*4882a593Smuzhiyun #else
35*4882a593Smuzhiyun #error invalid SiByte UART configuration
36*4882a593Smuzhiyun #endif
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
39*4882a593Smuzhiyun #undef K_INT_TRACE_FREEZE
40*4882a593Smuzhiyun #define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE
41*4882a593Smuzhiyun #undef K_INT_PERF_CNT
42*4882a593Smuzhiyun #define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT
43*4882a593Smuzhiyun #endif
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #include <linux/uaccess.h>
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #define SBPROF_TB_MAJOR 240
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun typedef u64 tb_sample_t[6*256];
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun enum open_status {
52*4882a593Smuzhiyun 	SB_CLOSED,
53*4882a593Smuzhiyun 	SB_OPENING,
54*4882a593Smuzhiyun 	SB_OPEN
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun struct sbprof_tb {
58*4882a593Smuzhiyun 	wait_queue_head_t	tb_sync;
59*4882a593Smuzhiyun 	wait_queue_head_t	tb_read;
60*4882a593Smuzhiyun 	struct mutex		lock;
61*4882a593Smuzhiyun 	enum open_status	open;
62*4882a593Smuzhiyun 	tb_sample_t		*sbprof_tbbuf;
63*4882a593Smuzhiyun 	int			next_tb_sample;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	volatile int		tb_enable;
66*4882a593Smuzhiyun 	volatile int		tb_armed;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun static struct sbprof_tb sbp;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #define MAX_SAMPLE_BYTES (24*1024*1024)
73*4882a593Smuzhiyun #define MAX_TBSAMPLE_BYTES (12*1024*1024)
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun #define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t))
76*4882a593Smuzhiyun #define TB_SAMPLE_SIZE (sizeof(tb_sample_t))
77*4882a593Smuzhiyun #define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE)
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /* ioctls */
80*4882a593Smuzhiyun #define SBPROF_ZBSTART		_IOW('s', 0, int)
81*4882a593Smuzhiyun #define SBPROF_ZBSTOP		_IOW('s', 1, int)
82*4882a593Smuzhiyun #define SBPROF_ZBWAITFULL	_IOW('s', 2, int)
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /*
85*4882a593Smuzhiyun  * Routines for using 40-bit SCD cycle counter
86*4882a593Smuzhiyun  *
87*4882a593Smuzhiyun  * Client responsible for either handling interrupts or making sure
88*4882a593Smuzhiyun  * the cycles counter never saturates, e.g., by doing
89*4882a593Smuzhiyun  * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs.
90*4882a593Smuzhiyun  */
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun /*
93*4882a593Smuzhiyun  * Configures SCD counter 0 to count ZCLKs starting from val;
94*4882a593Smuzhiyun  * Configures SCD counters1,2,3 to count nothing.
95*4882a593Smuzhiyun  * Must not be called while gathering ZBbus profiles.
96*4882a593Smuzhiyun  */
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun #define zclk_timer_init(val) \
99*4882a593Smuzhiyun   __asm__ __volatile__ (".set push;" \
100*4882a593Smuzhiyun 			".set mips64;" \
101*4882a593Smuzhiyun 			"la   $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \
102*4882a593Smuzhiyun 			"sd   %0, 0x10($8);"   /* write val to counter0 */ \
103*4882a593Smuzhiyun 			"sd   %1, 0($8);"      /* config counter0 for zclks*/ \
104*4882a593Smuzhiyun 			".set pop" \
105*4882a593Smuzhiyun 			: /* no outputs */ \
106*4882a593Smuzhiyun 						     /* enable, counter0 */ \
107*4882a593Smuzhiyun 			: /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \
108*4882a593Smuzhiyun 			: /* modifies */ "$8" )
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun /* Reads SCD counter 0 and puts result in value
112*4882a593Smuzhiyun    unsigned long long val; */
113*4882a593Smuzhiyun #define zclk_get(val) \
114*4882a593Smuzhiyun   __asm__ __volatile__ (".set push;" \
115*4882a593Smuzhiyun 			".set mips64;" \
116*4882a593Smuzhiyun 			"la   $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \
117*4882a593Smuzhiyun 			"ld   %0, 0x10($8);"   /* write val to counter0 */ \
118*4882a593Smuzhiyun 			".set pop" \
119*4882a593Smuzhiyun 			: /* outputs */ "=r"(val) \
120*4882a593Smuzhiyun 			: /* inputs */ \
121*4882a593Smuzhiyun 			: /* modifies */ "$8" )
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun #define DEVNAME "sb_tbprof"
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun #define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun /*
128*4882a593Smuzhiyun  * Support for ZBbus sampling using the trace buffer
129*4882a593Smuzhiyun  *
130*4882a593Smuzhiyun  * We use the SCD performance counter interrupt, caused by a Zclk counter
131*4882a593Smuzhiyun  * overflow, to trigger the start of tracing.
132*4882a593Smuzhiyun  *
133*4882a593Smuzhiyun  * We set the trace buffer to sample everything and freeze on
134*4882a593Smuzhiyun  * overflow.
135*4882a593Smuzhiyun  *
136*4882a593Smuzhiyun  * We map the interrupt for trace_buffer_freeze to handle it on CPU 0.
137*4882a593Smuzhiyun  *
138*4882a593Smuzhiyun  */
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun static u64 tb_period;
141*4882a593Smuzhiyun 
arm_tb(void)142*4882a593Smuzhiyun static void arm_tb(void)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun 	u64 scdperfcnt;
145*4882a593Smuzhiyun 	u64 next = (1ULL << 40) - tb_period;
146*4882a593Smuzhiyun 	u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	/*
149*4882a593Smuzhiyun 	 * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to
150*4882a593Smuzhiyun 	 * trigger start of trace.  XXX vary sampling period
151*4882a593Smuzhiyun 	 */
152*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1));
153*4882a593Smuzhiyun 	scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	/*
156*4882a593Smuzhiyun 	 * Unfortunately, in Pass 2 we must clear all counters to knock down
157*4882a593Smuzhiyun 	 * a previous interrupt request.  This means that bus profiling
158*4882a593Smuzhiyun 	 * requires ALL of the SCD perf counters.
159*4882a593Smuzhiyun 	 */
160*4882a593Smuzhiyun #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
161*4882a593Smuzhiyun 	__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
162*4882a593Smuzhiyun 						/* keep counters 0,2,3,4,5,6,7 as is */
163*4882a593Smuzhiyun 		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */
164*4882a593Smuzhiyun 		     IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0));
165*4882a593Smuzhiyun 	__raw_writeq(
166*4882a593Smuzhiyun 		     M_SPC_CFG_ENABLE |		/* enable counting */
167*4882a593Smuzhiyun 		     M_SPC_CFG_CLEAR |		/* clear all counters */
168*4882a593Smuzhiyun 		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */
169*4882a593Smuzhiyun 		     IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1));
170*4882a593Smuzhiyun #else
171*4882a593Smuzhiyun 	__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
172*4882a593Smuzhiyun 						/* keep counters 0,2,3 as is */
173*4882a593Smuzhiyun 		     M_SPC_CFG_ENABLE |		/* enable counting */
174*4882a593Smuzhiyun 		     M_SPC_CFG_CLEAR |		/* clear all counters */
175*4882a593Smuzhiyun 		     V_SPC_CFG_SRC1(1),		/* counter 1 counts cycles */
176*4882a593Smuzhiyun 		     IOADDR(A_SCD_PERF_CNT_CFG));
177*4882a593Smuzhiyun #endif
178*4882a593Smuzhiyun 	__raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1));
179*4882a593Smuzhiyun 	/* Reset the trace buffer */
180*4882a593Smuzhiyun 	__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
181*4882a593Smuzhiyun #if 0 && defined(M_SCD_TRACE_CFG_FORCECNT)
182*4882a593Smuzhiyun 	/* XXXKW may want to expose control to the data-collector */
183*4882a593Smuzhiyun 	tb_options |= M_SCD_TRACE_CFG_FORCECNT;
184*4882a593Smuzhiyun #endif
185*4882a593Smuzhiyun 	__raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG));
186*4882a593Smuzhiyun 	sbp.tb_armed = 1;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
sbprof_tb_intr(int irq,void * dev_id)189*4882a593Smuzhiyun static irqreturn_t sbprof_tb_intr(int irq, void *dev_id)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	int i;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	pr_debug(DEVNAME ": tb_intr\n");
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	if (sbp.next_tb_sample < MAX_TB_SAMPLES) {
196*4882a593Smuzhiyun 		/* XXX should use XKPHYS to make writes bypass L2 */
197*4882a593Smuzhiyun 		u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++];
198*4882a593Smuzhiyun 		/* Read out trace */
199*4882a593Smuzhiyun 		__raw_writeq(M_SCD_TRACE_CFG_START_READ,
200*4882a593Smuzhiyun 			     IOADDR(A_SCD_TRACE_CFG));
201*4882a593Smuzhiyun 		__asm__ __volatile__ ("sync" : : : "memory");
202*4882a593Smuzhiyun 		/* Loop runs backwards because bundles are read out in reverse order */
203*4882a593Smuzhiyun 		for (i = 256 * 6; i > 0; i -= 6) {
204*4882a593Smuzhiyun 			/* Subscripts decrease to put bundle in the order */
205*4882a593Smuzhiyun 			/*   t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */
206*4882a593Smuzhiyun 			p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
207*4882a593Smuzhiyun 			/* read t2 hi */
208*4882a593Smuzhiyun 			p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
209*4882a593Smuzhiyun 			/* read t2 lo */
210*4882a593Smuzhiyun 			p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
211*4882a593Smuzhiyun 			/* read t1 hi */
212*4882a593Smuzhiyun 			p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
213*4882a593Smuzhiyun 			/* read t1 lo */
214*4882a593Smuzhiyun 			p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
215*4882a593Smuzhiyun 			/* read t0 hi */
216*4882a593Smuzhiyun 			p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
217*4882a593Smuzhiyun 			/* read t0 lo */
218*4882a593Smuzhiyun 		}
219*4882a593Smuzhiyun 		if (!sbp.tb_enable) {
220*4882a593Smuzhiyun 			pr_debug(DEVNAME ": tb_intr shutdown\n");
221*4882a593Smuzhiyun 			__raw_writeq(M_SCD_TRACE_CFG_RESET,
222*4882a593Smuzhiyun 				     IOADDR(A_SCD_TRACE_CFG));
223*4882a593Smuzhiyun 			sbp.tb_armed = 0;
224*4882a593Smuzhiyun 			wake_up_interruptible(&sbp.tb_sync);
225*4882a593Smuzhiyun 		} else {
226*4882a593Smuzhiyun 			/* knock down current interrupt and get another one later */
227*4882a593Smuzhiyun 			arm_tb();
228*4882a593Smuzhiyun 		}
229*4882a593Smuzhiyun 	} else {
230*4882a593Smuzhiyun 		/* No more trace buffer samples */
231*4882a593Smuzhiyun 		pr_debug(DEVNAME ": tb_intr full\n");
232*4882a593Smuzhiyun 		__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
233*4882a593Smuzhiyun 		sbp.tb_armed = 0;
234*4882a593Smuzhiyun 		if (!sbp.tb_enable)
235*4882a593Smuzhiyun 			wake_up_interruptible(&sbp.tb_sync);
236*4882a593Smuzhiyun 		wake_up_interruptible(&sbp.tb_read);
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 	return IRQ_HANDLED;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
sbprof_pc_intr(int irq,void * dev_id)241*4882a593Smuzhiyun static irqreturn_t sbprof_pc_intr(int irq, void *dev_id)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	printk(DEVNAME ": unexpected pc_intr");
244*4882a593Smuzhiyun 	return IRQ_NONE;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun /*
248*4882a593Smuzhiyun  * Requires: Already called zclk_timer_init with a value that won't
249*4882a593Smuzhiyun  *	     saturate 40 bits.	No subsequent use of SCD performance counters
250*4882a593Smuzhiyun  *	     or trace buffer.
251*4882a593Smuzhiyun  */
252*4882a593Smuzhiyun 
sbprof_zbprof_start(struct file * filp)253*4882a593Smuzhiyun static int sbprof_zbprof_start(struct file *filp)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	u64 scdperfcnt;
256*4882a593Smuzhiyun 	int err;
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	if (xchg(&sbp.tb_enable, 1))
259*4882a593Smuzhiyun 		return -EBUSY;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	pr_debug(DEVNAME ": starting\n");
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	sbp.next_tb_sample = 0;
264*4882a593Smuzhiyun 	filp->f_pos = 0;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0,
267*4882a593Smuzhiyun 			  DEVNAME " trace freeze", &sbp);
268*4882a593Smuzhiyun 	if (err)
269*4882a593Smuzhiyun 		return -EBUSY;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	/* Make sure there isn't a perf-cnt interrupt waiting */
272*4882a593Smuzhiyun 	scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));
273*4882a593Smuzhiyun 	/* Disable and clear counters, override SRC_1 */
274*4882a593Smuzhiyun 	__raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) |
275*4882a593Smuzhiyun 		     M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1),
276*4882a593Smuzhiyun 		     IOADDR(A_SCD_PERF_CNT_CFG));
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	/*
279*4882a593Smuzhiyun 	 * We grab this interrupt to prevent others from trying to use
280*4882a593Smuzhiyun 	 * it, even though we don't want to service the interrupts
281*4882a593Smuzhiyun 	 * (they only feed into the trace-on-interrupt mechanism)
282*4882a593Smuzhiyun 	 */
283*4882a593Smuzhiyun 	if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) {
284*4882a593Smuzhiyun 		free_irq(K_INT_TRACE_FREEZE, &sbp);
285*4882a593Smuzhiyun 		return -EBUSY;
286*4882a593Smuzhiyun 	}
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	/*
289*4882a593Smuzhiyun 	 * I need the core to mask these, but the interrupt mapper to
290*4882a593Smuzhiyun 	 *  pass them through.	I am exploiting my knowledge that
291*4882a593Smuzhiyun 	 *  cp0_status masks out IP[5]. krw
292*4882a593Smuzhiyun 	 */
293*4882a593Smuzhiyun #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
294*4882a593Smuzhiyun 	__raw_writeq(K_BCM1480_INT_MAP_I3,
295*4882a593Smuzhiyun 		     IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) +
296*4882a593Smuzhiyun 			    ((K_BCM1480_INT_PERF_CNT & 0x3f) << 3)));
297*4882a593Smuzhiyun #else
298*4882a593Smuzhiyun 	__raw_writeq(K_INT_MAP_I3,
299*4882a593Smuzhiyun 		     IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
300*4882a593Smuzhiyun 			    (K_INT_PERF_CNT << 3)));
301*4882a593Smuzhiyun #endif
302*4882a593Smuzhiyun 
303*4882a593Smuzhiyun 	/* Initialize address traps */
304*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_0));
305*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_1));
306*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_2));
307*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_3));
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0));
310*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1));
311*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2));
312*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3));
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0));
315*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1));
316*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2));
317*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3));
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	/* Initialize Trace Event 0-7 */
320*4882a593Smuzhiyun 	/*				when interrupt	*/
321*4882a593Smuzhiyun 	__raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0));
322*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1));
323*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2));
324*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3));
325*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4));
326*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5));
327*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6));
328*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7));
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	/* Initialize Trace Sequence 0-7 */
331*4882a593Smuzhiyun 	/*				     Start on event 0 (interrupt) */
332*4882a593Smuzhiyun 	__raw_writeq(V_SCD_TRSEQ_FUNC_START | 0x0fff,
333*4882a593Smuzhiyun 		     IOADDR(A_SCD_TRACE_SEQUENCE_0));
334*4882a593Smuzhiyun 	/*			  dsamp when d used | asamp when a used */
335*4882a593Smuzhiyun 	__raw_writeq(M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
336*4882a593Smuzhiyun 		     K_SCD_TRSEQ_TRIGGER_ALL,
337*4882a593Smuzhiyun 		     IOADDR(A_SCD_TRACE_SEQUENCE_1));
338*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2));
339*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3));
340*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4));
341*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5));
342*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6));
343*4882a593Smuzhiyun 	__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7));
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	/* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
346*4882a593Smuzhiyun #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
347*4882a593Smuzhiyun 	__raw_writeq(1ULL << (K_BCM1480_INT_PERF_CNT & 0x3f),
348*4882a593Smuzhiyun 		     IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_TRACE_L)));
349*4882a593Smuzhiyun #else
350*4882a593Smuzhiyun 	__raw_writeq(1ULL << K_INT_PERF_CNT,
351*4882a593Smuzhiyun 		     IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE)));
352*4882a593Smuzhiyun #endif
353*4882a593Smuzhiyun 	arm_tb();
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun 	pr_debug(DEVNAME ": done starting\n");
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 	return 0;
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun 
sbprof_zbprof_stop(void)360*4882a593Smuzhiyun static int sbprof_zbprof_stop(void)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun 	int err = 0;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	pr_debug(DEVNAME ": stopping\n");
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	if (sbp.tb_enable) {
367*4882a593Smuzhiyun 		/*
368*4882a593Smuzhiyun 		 * XXXKW there is a window here where the intr handler may run,
369*4882a593Smuzhiyun 		 * see the disable, and do the wake_up before this sleep
370*4882a593Smuzhiyun 		 * happens.
371*4882a593Smuzhiyun 		 */
372*4882a593Smuzhiyun 		pr_debug(DEVNAME ": wait for disarm\n");
373*4882a593Smuzhiyun 		err = wait_event_interruptible(sbp.tb_sync, !sbp.tb_armed);
374*4882a593Smuzhiyun 		pr_debug(DEVNAME ": disarm complete, stat %d\n", err);
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 		if (err)
377*4882a593Smuzhiyun 			return err;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 		sbp.tb_enable = 0;
380*4882a593Smuzhiyun 		free_irq(K_INT_TRACE_FREEZE, &sbp);
381*4882a593Smuzhiyun 		free_irq(K_INT_PERF_CNT, &sbp);
382*4882a593Smuzhiyun 	}
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	pr_debug(DEVNAME ": done stopping\n");
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	return err;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun 
sbprof_tb_open(struct inode * inode,struct file * filp)389*4882a593Smuzhiyun static int sbprof_tb_open(struct inode *inode, struct file *filp)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun 	int minor;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	minor = iminor(inode);
394*4882a593Smuzhiyun 	if (minor != 0)
395*4882a593Smuzhiyun 		return -ENODEV;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	if (xchg(&sbp.open, SB_OPENING) != SB_CLOSED)
398*4882a593Smuzhiyun 		return -EBUSY;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	memset(&sbp, 0, sizeof(struct sbprof_tb));
401*4882a593Smuzhiyun 	sbp.sbprof_tbbuf = vzalloc(MAX_TBSAMPLE_BYTES);
402*4882a593Smuzhiyun 	if (!sbp.sbprof_tbbuf) {
403*4882a593Smuzhiyun 		sbp.open = SB_CLOSED;
404*4882a593Smuzhiyun 		wmb();
405*4882a593Smuzhiyun 		return -ENOMEM;
406*4882a593Smuzhiyun 	}
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	init_waitqueue_head(&sbp.tb_sync);
409*4882a593Smuzhiyun 	init_waitqueue_head(&sbp.tb_read);
410*4882a593Smuzhiyun 	mutex_init(&sbp.lock);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	sbp.open = SB_OPEN;
413*4882a593Smuzhiyun 	wmb();
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	return 0;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun 
sbprof_tb_release(struct inode * inode,struct file * filp)418*4882a593Smuzhiyun static int sbprof_tb_release(struct inode *inode, struct file *filp)
419*4882a593Smuzhiyun {
420*4882a593Smuzhiyun 	int minor;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	minor = iminor(inode);
423*4882a593Smuzhiyun 	if (minor != 0 || sbp.open != SB_CLOSED)
424*4882a593Smuzhiyun 		return -ENODEV;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	mutex_lock(&sbp.lock);
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	if (sbp.tb_armed || sbp.tb_enable)
429*4882a593Smuzhiyun 		sbprof_zbprof_stop();
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	vfree(sbp.sbprof_tbbuf);
432*4882a593Smuzhiyun 	sbp.open = SB_CLOSED;
433*4882a593Smuzhiyun 	wmb();
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	mutex_unlock(&sbp.lock);
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	return 0;
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun 
sbprof_tb_read(struct file * filp,char * buf,size_t size,loff_t * offp)440*4882a593Smuzhiyun static ssize_t sbprof_tb_read(struct file *filp, char *buf,
441*4882a593Smuzhiyun 			      size_t size, loff_t *offp)
442*4882a593Smuzhiyun {
443*4882a593Smuzhiyun 	int cur_sample, sample_off, cur_count, sample_left;
444*4882a593Smuzhiyun 	char *src;
445*4882a593Smuzhiyun 	int   count   =	 0;
446*4882a593Smuzhiyun 	char *dest    =	 buf;
447*4882a593Smuzhiyun 	long  cur_off = *offp;
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	if (!access_ok(buf, size))
450*4882a593Smuzhiyun 		return -EFAULT;
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	mutex_lock(&sbp.lock);
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	count = 0;
455*4882a593Smuzhiyun 	cur_sample = cur_off / TB_SAMPLE_SIZE;
456*4882a593Smuzhiyun 	sample_off = cur_off % TB_SAMPLE_SIZE;
457*4882a593Smuzhiyun 	sample_left = TB_SAMPLE_SIZE - sample_off;
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	while (size && (cur_sample < sbp.next_tb_sample)) {
460*4882a593Smuzhiyun 		int err;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 		cur_count = size < sample_left ? size : sample_left;
463*4882a593Smuzhiyun 		src = (char *)(((long)sbp.sbprof_tbbuf[cur_sample])+sample_off);
464*4882a593Smuzhiyun 		err = __copy_to_user(dest, src, cur_count);
465*4882a593Smuzhiyun 		if (err) {
466*4882a593Smuzhiyun 			*offp = cur_off + cur_count - err;
467*4882a593Smuzhiyun 			mutex_unlock(&sbp.lock);
468*4882a593Smuzhiyun 			return err;
469*4882a593Smuzhiyun 		}
470*4882a593Smuzhiyun 		pr_debug(DEVNAME ": read from sample %d, %d bytes\n",
471*4882a593Smuzhiyun 			 cur_sample, cur_count);
472*4882a593Smuzhiyun 		size -= cur_count;
473*4882a593Smuzhiyun 		sample_left -= cur_count;
474*4882a593Smuzhiyun 		if (!sample_left) {
475*4882a593Smuzhiyun 			cur_sample++;
476*4882a593Smuzhiyun 			sample_off = 0;
477*4882a593Smuzhiyun 			sample_left = TB_SAMPLE_SIZE;
478*4882a593Smuzhiyun 		} else {
479*4882a593Smuzhiyun 			sample_off += cur_count;
480*4882a593Smuzhiyun 		}
481*4882a593Smuzhiyun 		cur_off += cur_count;
482*4882a593Smuzhiyun 		dest += cur_count;
483*4882a593Smuzhiyun 		count += cur_count;
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 	*offp = cur_off;
486*4882a593Smuzhiyun 	mutex_unlock(&sbp.lock);
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	return count;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun 
sbprof_tb_ioctl(struct file * filp,unsigned int command,unsigned long arg)491*4882a593Smuzhiyun static long sbprof_tb_ioctl(struct file *filp,
492*4882a593Smuzhiyun 			    unsigned int command,
493*4882a593Smuzhiyun 			    unsigned long arg)
494*4882a593Smuzhiyun {
495*4882a593Smuzhiyun 	int err = 0;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	switch (command) {
498*4882a593Smuzhiyun 	case SBPROF_ZBSTART:
499*4882a593Smuzhiyun 		mutex_lock(&sbp.lock);
500*4882a593Smuzhiyun 		err = sbprof_zbprof_start(filp);
501*4882a593Smuzhiyun 		mutex_unlock(&sbp.lock);
502*4882a593Smuzhiyun 		break;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	case SBPROF_ZBSTOP:
505*4882a593Smuzhiyun 		mutex_lock(&sbp.lock);
506*4882a593Smuzhiyun 		err = sbprof_zbprof_stop();
507*4882a593Smuzhiyun 		mutex_unlock(&sbp.lock);
508*4882a593Smuzhiyun 		break;
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 	case SBPROF_ZBWAITFULL: {
511*4882a593Smuzhiyun 		err = wait_event_interruptible(sbp.tb_read, TB_FULL);
512*4882a593Smuzhiyun 		if (err)
513*4882a593Smuzhiyun 			break;
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 		err = put_user(TB_FULL, (int *) arg);
516*4882a593Smuzhiyun 		break;
517*4882a593Smuzhiyun 	}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 	default:
520*4882a593Smuzhiyun 		err = -EINVAL;
521*4882a593Smuzhiyun 		break;
522*4882a593Smuzhiyun 	}
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 	return err;
525*4882a593Smuzhiyun }
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun static const struct file_operations sbprof_tb_fops = {
528*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
529*4882a593Smuzhiyun 	.open		= sbprof_tb_open,
530*4882a593Smuzhiyun 	.release	= sbprof_tb_release,
531*4882a593Smuzhiyun 	.read		= sbprof_tb_read,
532*4882a593Smuzhiyun 	.unlocked_ioctl = sbprof_tb_ioctl,
533*4882a593Smuzhiyun 	.compat_ioctl	= sbprof_tb_ioctl,
534*4882a593Smuzhiyun 	.mmap		= NULL,
535*4882a593Smuzhiyun 	.llseek		= default_llseek,
536*4882a593Smuzhiyun };
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun static struct class *tb_class;
539*4882a593Smuzhiyun static struct device *tb_dev;
540*4882a593Smuzhiyun 
sbprof_tb_init(void)541*4882a593Smuzhiyun static int __init sbprof_tb_init(void)
542*4882a593Smuzhiyun {
543*4882a593Smuzhiyun 	struct device *dev;
544*4882a593Smuzhiyun 	struct class *tbc;
545*4882a593Smuzhiyun 	int err;
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 	if (register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) {
548*4882a593Smuzhiyun 		printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n",
549*4882a593Smuzhiyun 		       SBPROF_TB_MAJOR);
550*4882a593Smuzhiyun 		return -EIO;
551*4882a593Smuzhiyun 	}
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	tbc = class_create(THIS_MODULE, "sb_tracebuffer");
554*4882a593Smuzhiyun 	if (IS_ERR(tbc)) {
555*4882a593Smuzhiyun 		err = PTR_ERR(tbc);
556*4882a593Smuzhiyun 		goto out_chrdev;
557*4882a593Smuzhiyun 	}
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	tb_class = tbc;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), NULL, "tb");
562*4882a593Smuzhiyun 	if (IS_ERR(dev)) {
563*4882a593Smuzhiyun 		err = PTR_ERR(dev);
564*4882a593Smuzhiyun 		goto out_class;
565*4882a593Smuzhiyun 	}
566*4882a593Smuzhiyun 	tb_dev = dev;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun 	sbp.open = SB_CLOSED;
569*4882a593Smuzhiyun 	wmb();
570*4882a593Smuzhiyun 	tb_period = zbbus_mhz * 10000LL;
571*4882a593Smuzhiyun 	pr_info(DEVNAME ": initialized - tb_period = %lld\n",
572*4882a593Smuzhiyun 		(long long) tb_period);
573*4882a593Smuzhiyun 	return 0;
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun out_class:
576*4882a593Smuzhiyun 	class_destroy(tb_class);
577*4882a593Smuzhiyun out_chrdev:
578*4882a593Smuzhiyun 	unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	return err;
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun 
sbprof_tb_cleanup(void)583*4882a593Smuzhiyun static void __exit sbprof_tb_cleanup(void)
584*4882a593Smuzhiyun {
585*4882a593Smuzhiyun 	device_destroy(tb_class, MKDEV(SBPROF_TB_MAJOR, 0));
586*4882a593Smuzhiyun 	unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
587*4882a593Smuzhiyun 	class_destroy(tb_class);
588*4882a593Smuzhiyun }
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun module_init(sbprof_tb_init);
591*4882a593Smuzhiyun module_exit(sbprof_tb_cleanup);
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun MODULE_ALIAS_CHARDEV_MAJOR(SBPROF_TB_MAJOR);
594*4882a593Smuzhiyun MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
595*4882a593Smuzhiyun MODULE_LICENSE("GPL");
596