1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2004, 2005 Ralf Baechle
7  * Copyright (C) 2005 MIPS Technologies, Inc.
8  */
9 #include <linux/compiler.h>
10 #include <linux/errno.h>
11 #include <linux/init.h>
12 #include <linux/oprofile.h>
13 #include <linux/smp.h>
14 #include <asm/cpu-info.h>
15 
16 #include "op_impl.h"
17 
18 extern struct op_mips_model op_model_mipsxx_ops __weak;
19 extern struct op_mips_model op_model_rm9000_ops __weak;
20 extern struct op_mips_model op_model_loongson2_ops __weak;
21 
22 static struct op_mips_model *model;
23 
24 static struct op_counter_config ctr[20];
25 
op_mips_setup(void)26 static int op_mips_setup(void)
27 {
28 	/* Pre-compute the values to stuff in the hardware registers.  */
29 	model->reg_setup(ctr);
30 
31 	/* Configure the registers on all cpus.  */
32 	on_each_cpu(model->cpu_setup, NULL, 1);
33 
34         return 0;
35 }
36 
op_mips_create_files(struct super_block * sb,struct dentry * root)37 static int op_mips_create_files(struct super_block *sb, struct dentry *root)
38 {
39 	int i;
40 
41 	for (i = 0; i < model->num_counters; ++i) {
42 		struct dentry *dir;
43 		char buf[4];
44 
45 		snprintf(buf, sizeof buf, "%d", i);
46 		dir = oprofilefs_mkdir(sb, root, buf);
47 
48 		oprofilefs_create_ulong(sb, dir, "enabled", &ctr[i].enabled);
49 		oprofilefs_create_ulong(sb, dir, "event", &ctr[i].event);
50 		oprofilefs_create_ulong(sb, dir, "count", &ctr[i].count);
51 		oprofilefs_create_ulong(sb, dir, "kernel", &ctr[i].kernel);
52 		oprofilefs_create_ulong(sb, dir, "user", &ctr[i].user);
53 		oprofilefs_create_ulong(sb, dir, "exl", &ctr[i].exl);
54 		/* Dummy.  */
55 		oprofilefs_create_ulong(sb, dir, "unit_mask", &ctr[i].unit_mask);
56 	}
57 
58 	return 0;
59 }
60 
op_mips_start(void)61 static int op_mips_start(void)
62 {
63 	on_each_cpu(model->cpu_start, NULL, 1);
64 
65 	return 0;
66 }
67 
op_mips_stop(void)68 static void op_mips_stop(void)
69 {
70 	/* Disable performance monitoring for all counters.  */
71 	on_each_cpu(model->cpu_stop, NULL, 1);
72 }
73 
oprofile_arch_init(struct oprofile_operations * ops)74 int __init oprofile_arch_init(struct oprofile_operations *ops)
75 {
76 	struct op_mips_model *lmodel = NULL;
77 	int res;
78 
79 	switch (current_cpu_type()) {
80 	case CPU_5KC:
81 	case CPU_20KC:
82 	case CPU_24K:
83 	case CPU_25KF:
84 	case CPU_34K:
85 	case CPU_1004K:
86 	case CPU_74K:
87 	case CPU_SB1:
88 	case CPU_SB1A:
89 	case CPU_R10000:
90 	case CPU_R12000:
91 	case CPU_R14000:
92 		lmodel = &op_model_mipsxx_ops;
93 		break;
94 
95 	case CPU_RM9000:
96 		lmodel = &op_model_rm9000_ops;
97 		break;
98 	case CPU_LOONGSON2:
99 		lmodel = &op_model_loongson2_ops;
100 		break;
101 	};
102 
103 	if (!lmodel)
104 		return -ENODEV;
105 
106 	res = lmodel->init();
107 	if (res)
108 		return res;
109 
110 	model = lmodel;
111 
112 	ops->create_files	= op_mips_create_files;
113 	ops->setup		= op_mips_setup;
114 	//ops->shutdown         = op_mips_shutdown;
115 	ops->start		= op_mips_start;
116 	ops->stop		= op_mips_stop;
117 	ops->cpu_type		= lmodel->cpu_type;
118 	ops->backtrace		= op_mips_backtrace;
119 
120 	printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
121 	       lmodel->cpu_type);
122 
123 	return 0;
124 }
125 
oprofile_arch_exit(void)126 void oprofile_arch_exit(void)
127 {
128 	if (model)
129 		model->exit();
130 }
131