1/*
2 * Copyright (C) 2001 Broadcom Corporation
3 *
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.
8 *
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.
13 *
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.
17 */
18
19#define SBPROF_TB_DEBUG 0
20
21#include <linux/module.h>
22#include <linux/kernel.h>
23#include <linux/types.h>
24#include <linux/init.h>
25#include <linux/slab.h>
26#include <linux/vmalloc.h>
27#include <linux/fs.h>
28#include <linux/errno.h>
29#include <linux/reboot.h>
30#include <linux/devfs_fs_kernel.h>
31#include <asm/uaccess.h>
32#include <asm/smplock.h>
33#include <asm/sibyte/sb1250_regs.h>
34#include <asm/sibyte/sb1250_scd.h>
35#include <asm/sibyte/sb1250_int.h>
36#include <asm/sibyte/64bit.h>
37#include "bcm1250_tbprof.h"
38
39#define DEVNAME "sb1250_tbprof"
40
41static struct sbprof_tb *sbp;
42
43/************************************************************************
44 * Support for ZBbus sampling using the trace buffer
45 *
46 * We use the SCD performance counter interrupt, caused by a Zclk counter
47 * overflow, to trigger the start of tracing.
48 *
49 * We set the trace buffer to sample everything and freeze on
50 * overflow.
51 *
52 * We map the interrupt for trace_buffer_freeze to handle it on CPU 0.
53 *
54 ************************************************************************/
55
56/* 100 samples per second on a 500 Mhz 1250 */
57#define TB_PERIOD 2500000ULL
58
59static void arm_tb(void)
60{
61        unsigned long long scdperfcnt;
62	unsigned long long next = (1ULL << 40) - TB_PERIOD;
63	out64(0, KSEG1 + A_SCD_PERF_CNT_1);
64	scdperfcnt = in64(KSEG1 + A_SCD_PERF_CNT_CFG);
65	/* Unfortunately, in Pass 2 we must clear all counters to knock down
66	   a previous interrupt request.  This means that bus profiling
67	   requires ALL of the SCD perf counters. */
68	out64((scdperfcnt & ~M_SPC_CFG_SRC1) | // keep counters 0,2,3 as is
69		   M_SPC_CFG_ENABLE |		 // enable counting
70		   M_SPC_CFG_CLEAR |		 // clear all counters
71		   V_SPC_CFG_SRC1(1),		 // counter 1 counts cycles
72	      KSEG1 + A_SCD_PERF_CNT_CFG);
73	out64(next, KSEG1 + A_SCD_PERF_CNT_1);
74	/* Reset the trace buffer */
75	out64(M_SCD_TRACE_CFG_RESET, KSEG1 + A_SCD_TRACE_CFG);
76	out64(M_SCD_TRACE_CFG_FREEZE_FULL, KSEG1 + A_SCD_TRACE_CFG);
77	sbp->tb_armed = 1;
78}
79
80static void sbprof_tb_intr(int irq, void *dev_id, struct pt_regs *regs)
81{
82	int i;
83	DBG(printk(DEVNAME ": tb_intr\n"));
84	if (sbp->next_tb_sample < MAX_TB_SAMPLES) {
85		unsigned long long *p = sbp->sbprof_tbbuf[sbp->next_tb_sample++];
86		/* Read out trace */
87		out64(M_SCD_TRACE_CFG_START_READ, KSEG1 + A_SCD_TRACE_CFG);
88		__asm__ __volatile__ ("sync" : : : "memory");
89		/* Loop runs backwards because bundles are read out in reverse order */
90		for (i = 256 * 6; i > 0; i -= 6) {
91			// Subscripts decrease to put bundle in the order
92			//   t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi
93			p[i-1] = in64(KSEG1 + A_SCD_TRACE_READ); // read t2 hi
94			p[i-2] = in64(KSEG1 + A_SCD_TRACE_READ); // read t2 lo
95			p[i-3] = in64(KSEG1 + A_SCD_TRACE_READ); // read t1 hi
96			p[i-4] = in64(KSEG1 + A_SCD_TRACE_READ); // read t1 lo
97			p[i-5] = in64(KSEG1 + A_SCD_TRACE_READ); // read t0 hi
98			p[i-6] = in64(KSEG1 + A_SCD_TRACE_READ); // read t0 lo
99		}
100		if (!sbp->tb_enable) {
101			DBG(printk(DEVNAME ": tb_intr shutdown\n"));
102			out64(M_SCD_TRACE_CFG_RESET, KSEG1 + A_SCD_TRACE_CFG);
103			sbp->tb_armed = 0;
104			wake_up(&sbp->tb_sync);
105		} else {
106			arm_tb();	// knock down current interrupt and get another one later
107		}
108	} else {
109		/* No more trace buffer samples */
110		DBG(printk(DEVNAME ": tb_intr full\n"));
111		out64(M_SCD_TRACE_CFG_RESET, KSEG1 + A_SCD_TRACE_CFG);
112		sbp->tb_armed = 0;
113		if (!sbp->tb_enable) {
114			wake_up(&sbp->tb_sync);
115		}
116	}
117}
118
119static void sbprof_pc_intr(int irq, void *dev_id, struct pt_regs *regs)
120{
121	panic(DEVNAME ": pc_intr");
122}
123
124static int sbprof_zbprof_start(struct file *filp)
125{
126	if (sbp->tb_enable)
127		return -EBUSY;
128
129	DBG(printk(DEVNAME ": starting\n"));
130
131	sbp->tb_enable = 1;
132	sbp->next_tb_sample = 0;
133	filp->f_pos = 0;
134
135	if (request_irq
136	    (K_INT_TRACE_FREEZE, sbprof_tb_intr, 0, "sbprof_tb trace freeze", sbp)) {
137		return -EBUSY;
138	}
139	if (request_irq
140	    (K_INT_PERF_CNT, sbprof_pc_intr, 0, "sbprof_tb scd perfcnt", sbp)) {
141		free_irq(K_INT_TRACE_FREEZE, sbp);
142		return -EBUSY;
143	}
144
145	/* I need the core to mask these, but the interrupt mapper to
146	   pass them through.  I am exploiting my knowledge that
147	   cp0_status masks out IP[5]. krw */
148	out64(K_INT_MAP_I3,
149	      KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) + (K_INT_PERF_CNT<<3));
150
151	/* Initialize address traps */
152	out64(0, KSEG1 + A_ADDR_TRAP_UP_0);
153	out64(0, KSEG1 + A_ADDR_TRAP_UP_1);
154	out64(0, KSEG1 + A_ADDR_TRAP_UP_2);
155	out64(0, KSEG1 + A_ADDR_TRAP_UP_3);
156
157	out64(0, KSEG1 + A_ADDR_TRAP_DOWN_0);
158	out64(0, KSEG1 + A_ADDR_TRAP_DOWN_1);
159	out64(0, KSEG1 + A_ADDR_TRAP_DOWN_2);
160	out64(0, KSEG1 + A_ADDR_TRAP_DOWN_3);
161
162	out64(0, KSEG1 + A_ADDR_TRAP_CFG_0);
163	out64(0, KSEG1 + A_ADDR_TRAP_CFG_1);
164	out64(0, KSEG1 + A_ADDR_TRAP_CFG_2);
165	out64(0, KSEG1 + A_ADDR_TRAP_CFG_3);
166
167	/* Initialize Trace Event 0-7 */
168	//				when interrupt
169	out64(M_SCD_TREVT_INTERRUPT, KSEG1 + A_SCD_TRACE_EVENT_0);
170	out64(0, KSEG1 + A_SCD_TRACE_EVENT_1);
171	out64(0, KSEG1 + A_SCD_TRACE_EVENT_2);
172	out64(0, KSEG1 + A_SCD_TRACE_EVENT_3);
173	out64(0, KSEG1 + A_SCD_TRACE_EVENT_4);
174	out64(0, KSEG1 + A_SCD_TRACE_EVENT_5);
175	out64(0, KSEG1 + A_SCD_TRACE_EVENT_6);
176	out64(0, KSEG1 + A_SCD_TRACE_EVENT_7);
177
178	/* Initialize Trace Sequence 0-7 */
179	//				     Start on event 0 (interrupt)
180	out64(V_SCD_TRSEQ_FUNC_START|0x0fff,
181	      KSEG1 + A_SCD_TRACE_SEQUENCE_0);
182	//			  dsamp when d used | asamp when a used
183	out64(M_SCD_TRSEQ_ASAMPLE|M_SCD_TRSEQ_DSAMPLE|K_SCD_TRSEQ_TRIGGER_ALL,
184	      KSEG1 + A_SCD_TRACE_SEQUENCE_1);
185	out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_2);
186	out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_3);
187	out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_4);
188	out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_5);
189	out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_6);
190	out64(0, KSEG1 + A_SCD_TRACE_SEQUENCE_7);
191
192	/* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
193	out64((1ULL << K_INT_PERF_CNT), KSEG1 + A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE));
194
195	arm_tb();
196
197	DBG(printk(DEVNAME ": done starting\n"));
198
199	return 0;
200}
201
202static int sbprof_zbprof_stop(void)
203{
204	DBG(printk(DEVNAME ": stopping\n"));
205
206	if (sbp->tb_enable) {
207		sbp->tb_enable = 0;
208		/* XXXKW there is a window here where the intr handler
209		   may run, see the disable, and do the wake_up before
210		   this sleep happens. */
211		if (sbp->tb_armed) {
212			DBG(printk(DEVNAME ": wait for disarm\n"));
213			interruptible_sleep_on(&sbp->tb_sync);
214			DBG(printk(DEVNAME ": disarm complete\n"));
215		}
216		free_irq(K_INT_TRACE_FREEZE, sbp);
217		free_irq(K_INT_PERF_CNT, sbp);
218	}
219
220	DBG(printk(DEVNAME ": done stopping\n"));
221
222	return 0;
223}
224
225static int sbprof_tb_open(struct inode *inode, struct file *filp)
226{
227	int minor;
228
229	minor = MINOR(inode->i_rdev);
230	if (minor != 0) {
231		return -ENODEV;
232	}
233	if (sbp != NULL) {
234		return -EBUSY;
235	}
236
237	/* XXXKW spinlock? */
238	sbp = kmalloc(sizeof(struct sbprof_tb), GFP_KERNEL);
239	if (!sbp) {
240		return -ENOMEM;
241	}
242	memset(sbp, 0, sizeof(struct sbprof_tb));
243	sbp->sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES);
244	if (!sbp->sbprof_tbbuf) {
245		kfree(sbp);
246		sbp = NULL;
247		return -ENOMEM;
248	}
249	memset(sbp->sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES);
250	init_waitqueue_head(&sbp->tb_sync);
251
252	return 0;
253}
254
255static int sbprof_tb_release(struct inode *inode, struct file *filp)
256{
257	int minor;
258
259	minor = MINOR(inode->i_rdev);
260	if (minor != 0 || sbp == NULL) {
261		return -ENODEV;
262	}
263
264	if (sbp->tb_armed || sbp->tb_enable) {
265		sbprof_zbprof_stop();
266	}
267
268	vfree(sbp->sbprof_tbbuf);
269	kfree(sbp);
270	sbp = NULL;
271
272	return 0;
273}
274
275static ssize_t sbprof_tb_read(struct file *filp, char *buf,
276			   size_t size, loff_t *offp)
277{
278	int cur_sample, sample_off, cur_count, sample_left;
279	char *src;
280	int   count   =	 0;
281	char *dest    =	 buf;
282	long  cur_off = *offp;
283
284	count = 0;
285	cur_sample = cur_off / TB_SAMPLE_SIZE;
286	sample_off = cur_off % TB_SAMPLE_SIZE;
287	sample_left = TB_SAMPLE_SIZE - sample_off;
288	while (size && (cur_sample < sbp->next_tb_sample)) {
289		cur_count = size < sample_left ? size : sample_left;
290		src = (char *)(((long)sbp->sbprof_tbbuf[cur_sample])+sample_off);
291		copy_to_user(dest, src, cur_count);
292		DBG(printk(DEVNAME ": read from sample %d, %d bytes\n", cur_sample, cur_count));
293		size -= cur_count;
294		sample_left -= cur_count;
295		if (!sample_left) {
296			cur_sample++;
297			sample_off = 0;
298			sample_left = TB_SAMPLE_SIZE;
299		} else {
300			sample_off += cur_count;
301		}
302		cur_off += cur_count;
303		dest += cur_count;
304		count += cur_count;
305	}
306	*offp = cur_off;
307
308	return count;
309}
310
311#define SBPROF_ZBSTART	_IOW('s', 0, int)
312#define SBPROF_ZBSTOP	_IOW('s', 1, int)
313#define SBPROF_ZBFULL	_IOW('s', 2, int)
314
315static int sbprof_tb_ioctl(struct inode *inode,
316			struct file *filp,
317			unsigned int command,
318			unsigned long arg)
319{
320	int error = 0;
321	int full;
322
323	switch (command) {
324	case SBPROF_ZBSTART:
325		error = sbprof_zbprof_start(filp);
326		break;
327	case SBPROF_ZBSTOP:
328		error = sbprof_zbprof_stop();
329		break;
330	case SBPROF_ZBFULL:
331		full = (sbp->next_tb_sample == MAX_TB_SAMPLES);
332		return put_user(full, (int *) arg);
333	default:
334		error = -EINVAL;
335		break;
336	}
337
338	return error;
339}
340
341static struct file_operations sbprof_tb_fops = {
342	.owner		= THIS_MODULE,
343	.open		= sbprof_tb_open,
344	.release	= sbprof_tb_release,
345	.read		= sbprof_tb_read,
346	.ioctl		= sbprof_tb_ioctl,
347	.mmap		= NULL,
348};
349
350static devfs_handle_t devfs_handle;
351
352static int __init sbprof_tb_init(void)
353{
354	if (devfs_register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) {
355		printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n",
356		       SBPROF_TB_MAJOR);
357		return -EIO;
358	}
359	devfs_handle = devfs_register(NULL, DEVNAME,
360				      DEVFS_FL_DEFAULT, SBPROF_TB_MAJOR, 0,
361				      S_IFCHR | S_IRUGO | S_IWUGO,
362				      &sbprof_tb_fops, NULL);
363	sbp = NULL;
364	printk(KERN_INFO DEVNAME ": initialized\n");
365	return 0;
366}
367
368static void __exit sbprof_tb_cleanup(void)
369{
370	devfs_unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
371	devfs_unregister(devfs_handle);
372}
373
374module_init(sbprof_tb_init);
375module_exit(sbprof_tb_cleanup);
376