2 * Copyright (C) 2001, 2002, 2003 Broadcom Corporation
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 #define SBPROF_TB_DEBUG 0
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/types.h>
24 #include <linux/init.h>
25 #include <linux/interrupt.h>
26 #include <linux/slab.h>
27 #include <linux/vmalloc.h>
29 #include <linux/errno.h>
30 #include <linux/reboot.h>
31 #include <asm/uaccess.h>
33 #include <asm/sibyte/sb1250.h>
34 #include <asm/sibyte/sb1250_regs.h>
35 #include <asm/sibyte/sb1250_scd.h>
36 #include <asm/sibyte/sb1250_int.h>
37 #include <asm/sibyte/trace_prof.h>
39 #define DEVNAME "bcm1250_tbprof"
41 static struct sbprof_tb sbp
;
43 #define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)
45 /************************************************************************
46 * Support for ZBbus sampling using the trace buffer
48 * We use the SCD performance counter interrupt, caused by a Zclk counter
49 * overflow, to trigger the start of tracing.
51 * We set the trace buffer to sample everything and freeze on
54 * We map the interrupt for trace_buffer_freeze to handle it on CPU 0.
56 ************************************************************************/
58 static u_int64_t tb_period
;
60 static void arm_tb(void)
63 u_int64_t next
= (1ULL << 40) - tb_period
;
64 u_int64_t tb_options
= M_SCD_TRACE_CFG_FREEZE_FULL
;
65 /* Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to
66 trigger start of trace. XXX vary sampling period */
67 bus_writeq(0, IOADDR(A_SCD_PERF_CNT_1
));
68 scdperfcnt
= bus_readq(IOADDR(A_SCD_PERF_CNT_CFG
));
69 /* Unfortunately, in Pass 2 we must clear all counters to knock down
70 a previous interrupt request. This means that bus profiling
71 requires ALL of the SCD perf counters. */
72 bus_writeq((scdperfcnt
& ~M_SPC_CFG_SRC1
) | // keep counters 0,2,3 as is
73 M_SPC_CFG_ENABLE
| // enable counting
74 M_SPC_CFG_CLEAR
| // clear all counters
75 V_SPC_CFG_SRC1(1), // counter 1 counts cycles
76 IOADDR(A_SCD_PERF_CNT_CFG
));
77 bus_writeq(next
, IOADDR(A_SCD_PERF_CNT_1
));
78 /* Reset the trace buffer */
79 bus_writeq(M_SCD_TRACE_CFG_RESET
, IOADDR(A_SCD_TRACE_CFG
));
80 #if 0 && defined(M_SCD_TRACE_CFG_FORCECNT)
81 /* XXXKW may want to expose control to the data-collector */
82 tb_options
|= M_SCD_TRACE_CFG_FORCECNT
;
84 bus_writeq(tb_options
, IOADDR(A_SCD_TRACE_CFG
));
88 static irqreturn_t
sbprof_tb_intr(int irq
, void *dev_id
, struct pt_regs
*regs
)
91 DBG(printk(DEVNAME
": tb_intr\n"));
92 if (sbp
.next_tb_sample
< MAX_TB_SAMPLES
) {
93 /* XXX should use XKPHYS to make writes bypass L2 */
94 u_int64_t
*p
= sbp
.sbprof_tbbuf
[sbp
.next_tb_sample
++];
96 bus_writeq(M_SCD_TRACE_CFG_START_READ
, IOADDR(A_SCD_TRACE_CFG
));
97 __asm__
__volatile__ ("sync" : : : "memory");
98 /* Loop runs backwards because bundles are read out in reverse order */
99 for (i
= 256 * 6; i
> 0; i
-= 6) {
100 // Subscripts decrease to put bundle in the order
101 // t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi
102 p
[i
-1] = bus_readq(IOADDR(A_SCD_TRACE_READ
)); // read t2 hi
103 p
[i
-2] = bus_readq(IOADDR(A_SCD_TRACE_READ
)); // read t2 lo
104 p
[i
-3] = bus_readq(IOADDR(A_SCD_TRACE_READ
)); // read t1 hi
105 p
[i
-4] = bus_readq(IOADDR(A_SCD_TRACE_READ
)); // read t1 lo
106 p
[i
-5] = bus_readq(IOADDR(A_SCD_TRACE_READ
)); // read t0 hi
107 p
[i
-6] = bus_readq(IOADDR(A_SCD_TRACE_READ
)); // read t0 lo
109 if (!sbp
.tb_enable
) {
110 DBG(printk(DEVNAME
": tb_intr shutdown\n"));
111 bus_writeq(M_SCD_TRACE_CFG_RESET
,
112 IOADDR(A_SCD_TRACE_CFG
));
114 wake_up(&sbp
.tb_sync
);
116 arm_tb(); // knock down current interrupt and get another one later
119 /* No more trace buffer samples */
120 DBG(printk(DEVNAME
": tb_intr full\n"));
121 bus_writeq(M_SCD_TRACE_CFG_RESET
, IOADDR(A_SCD_TRACE_CFG
));
123 if (!sbp
.tb_enable
) {
124 wake_up(&sbp
.tb_sync
);
126 wake_up(&sbp
.tb_read
);
131 static irqreturn_t
sbprof_pc_intr(int irq
, void *dev_id
, struct pt_regs
*regs
)
133 printk(DEVNAME
": unexpected pc_intr");
137 int sbprof_zbprof_start(struct file
*filp
)
139 u_int64_t scdperfcnt
;
144 DBG(printk(DEVNAME
": starting\n"));
147 sbp
.next_tb_sample
= 0;
151 (K_INT_TRACE_FREEZE
, sbprof_tb_intr
, 0, DEVNAME
" trace freeze", &sbp
)) {
154 /* Make sure there isn't a perf-cnt interrupt waiting */
155 scdperfcnt
= bus_readq(IOADDR(A_SCD_PERF_CNT_CFG
));
156 /* Disable and clear counters, override SRC_1 */
157 bus_writeq((scdperfcnt
& ~(M_SPC_CFG_SRC1
| M_SPC_CFG_ENABLE
)) |
161 IOADDR(A_SCD_PERF_CNT_CFG
));
163 /* We grab this interrupt to prevent others from trying to use
164 it, even though we don't want to service the interrupts
165 (they only feed into the trace-on-interrupt mechanism) */
167 (K_INT_PERF_CNT
, sbprof_pc_intr
, 0, DEVNAME
" scd perfcnt", &sbp
)) {
168 free_irq(K_INT_TRACE_FREEZE
, &sbp
);
172 /* I need the core to mask these, but the interrupt mapper to
173 pass them through. I am exploiting my knowledge that
174 cp0_status masks out IP[5]. krw */
175 bus_writeq(K_INT_MAP_I3
,
176 IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE
) +
177 (K_INT_PERF_CNT
<< 3)));
179 /* Initialize address traps */
180 bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_0
));
181 bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_1
));
182 bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_2
));
183 bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_3
));
185 bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0
));
186 bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1
));
187 bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2
));
188 bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3
));
190 bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0
));
191 bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1
));
192 bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2
));
193 bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3
));
195 /* Initialize Trace Event 0-7 */
197 bus_writeq(M_SCD_TREVT_INTERRUPT
, IOADDR(A_SCD_TRACE_EVENT_0
));
198 bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1
));
199 bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2
));
200 bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3
));
201 bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4
));
202 bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5
));
203 bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6
));
204 bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7
));
206 /* Initialize Trace Sequence 0-7 */
207 // Start on event 0 (interrupt)
208 bus_writeq(V_SCD_TRSEQ_FUNC_START
| 0x0fff,
209 IOADDR(A_SCD_TRACE_SEQUENCE_0
));
210 // dsamp when d used | asamp when a used
211 bus_writeq(M_SCD_TRSEQ_ASAMPLE
| M_SCD_TRSEQ_DSAMPLE
|
212 K_SCD_TRSEQ_TRIGGER_ALL
,
213 IOADDR(A_SCD_TRACE_SEQUENCE_1
));
214 bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2
));
215 bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3
));
216 bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4
));
217 bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5
));
218 bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6
));
219 bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7
));
221 /* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
222 bus_writeq((1ULL << K_INT_PERF_CNT
),
223 IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE
)));
227 DBG(printk(DEVNAME
": done starting\n"));
232 int sbprof_zbprof_stop(void)
234 DBG(printk(DEVNAME
": stopping\n"));
238 /* XXXKW there is a window here where the intr handler
239 may run, see the disable, and do the wake_up before
240 this sleep happens. */
242 DBG(printk(DEVNAME
": wait for disarm\n"));
243 interruptible_sleep_on(&sbp
.tb_sync
);
244 DBG(printk(DEVNAME
": disarm complete\n"));
246 free_irq(K_INT_TRACE_FREEZE
, &sbp
);
247 free_irq(K_INT_PERF_CNT
, &sbp
);
250 DBG(printk(DEVNAME
": done stopping\n"));
255 static int sbprof_tb_open(struct inode
*inode
, struct file
*filp
)
259 minor
= iminor(inode
);
267 memset(&sbp
, 0, sizeof(struct sbprof_tb
));
268 sbp
.sbprof_tbbuf
= vmalloc(MAX_TBSAMPLE_BYTES
);
269 if (!sbp
.sbprof_tbbuf
) {
272 memset(sbp
.sbprof_tbbuf
, 0, MAX_TBSAMPLE_BYTES
);
273 init_waitqueue_head(&sbp
.tb_sync
);
274 init_waitqueue_head(&sbp
.tb_read
);
280 static int sbprof_tb_release(struct inode
*inode
, struct file
*filp
)
284 minor
= iminor(inode
);
285 if (minor
!= 0 || !sbp
.open
) {
289 if (sbp
.tb_armed
|| sbp
.tb_enable
) {
290 sbprof_zbprof_stop();
293 vfree(sbp
.sbprof_tbbuf
);
299 static ssize_t
sbprof_tb_read(struct file
*filp
, char *buf
,
300 size_t size
, loff_t
*offp
)
302 int cur_sample
, sample_off
, cur_count
, sample_left
;
306 long cur_off
= *offp
;
309 cur_sample
= cur_off
/ TB_SAMPLE_SIZE
;
310 sample_off
= cur_off
% TB_SAMPLE_SIZE
;
311 sample_left
= TB_SAMPLE_SIZE
- sample_off
;
312 while (size
&& (cur_sample
< sbp
.next_tb_sample
)) {
313 cur_count
= size
< sample_left
? size
: sample_left
;
314 src
= (char *)(((long)sbp
.sbprof_tbbuf
[cur_sample
])+sample_off
);
315 copy_to_user(dest
, src
, cur_count
);
316 DBG(printk(DEVNAME
": read from sample %d, %d bytes\n",
317 cur_sample
, cur_count
));
319 sample_left
-= cur_count
;
323 sample_left
= TB_SAMPLE_SIZE
;
325 sample_off
+= cur_count
;
327 cur_off
+= cur_count
;
336 static int sbprof_tb_ioctl(struct inode
*inode
,
338 unsigned int command
,
345 error
= sbprof_zbprof_start(filp
);
348 error
= sbprof_zbprof_stop();
350 case SBPROF_ZBWAITFULL
:
351 interruptible_sleep_on(&sbp
.tb_read
);
352 /* XXXKW check if interrupted? */
353 return put_user(TB_FULL
, (int *) arg
);
362 static struct file_operations sbprof_tb_fops
= {
363 .owner
= THIS_MODULE
,
364 .open
= sbprof_tb_open
,
365 .release
= sbprof_tb_release
,
366 .read
= sbprof_tb_read
,
367 .ioctl
= sbprof_tb_ioctl
,
371 static int __init
sbprof_tb_init(void)
373 if (register_chrdev(SBPROF_TB_MAJOR
, DEVNAME
, &sbprof_tb_fops
)) {
374 printk(KERN_WARNING DEVNAME
": initialization failed (dev %d)\n",
379 tb_period
= zbbus_mhz
* 10000LL;
380 printk(KERN_INFO DEVNAME
": initialized - tb_period = %lld\n", tb_period
);
384 static void __exit
sbprof_tb_cleanup(void)
386 unregister_chrdev(SBPROF_TB_MAJOR
, DEVNAME
);
389 module_init(sbprof_tb_init
);
390 module_exit(sbprof_tb_cleanup
);