Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/oprofile/op_model_loongson2.c
10820 views
1
/*
2
* Loongson2 performance counter driver for oprofile
3
*
4
* Copyright (C) 2009 Lemote Inc.
5
* Author: Yanhua <[email protected]>
6
* Author: Wu Zhangjin <[email protected]>
7
*
8
* This file is subject to the terms and conditions of the GNU General Public
9
* License. See the file "COPYING" in the main directory of this archive
10
* for more details.
11
*/
12
#include <linux/init.h>
13
#include <linux/oprofile.h>
14
#include <linux/interrupt.h>
15
16
#include <loongson.h> /* LOONGSON2_PERFCNT_IRQ */
17
#include "op_impl.h"
18
19
#define LOONGSON2_CPU_TYPE "mips/loongson2"
20
21
#define LOONGSON2_PERFCNT_OVERFLOW (1ULL << 31)
22
23
#define LOONGSON2_PERFCTRL_EXL (1UL << 0)
24
#define LOONGSON2_PERFCTRL_KERNEL (1UL << 1)
25
#define LOONGSON2_PERFCTRL_SUPERVISOR (1UL << 2)
26
#define LOONGSON2_PERFCTRL_USER (1UL << 3)
27
#define LOONGSON2_PERFCTRL_ENABLE (1UL << 4)
28
#define LOONGSON2_PERFCTRL_EVENT(idx, event) \
29
(((event) & 0x0f) << ((idx) ? 9 : 5))
30
31
#define read_c0_perfctrl() __read_64bit_c0_register($24, 0)
32
#define write_c0_perfctrl(val) __write_64bit_c0_register($24, 0, val)
33
#define read_c0_perfcnt() __read_64bit_c0_register($25, 0)
34
#define write_c0_perfcnt(val) __write_64bit_c0_register($25, 0, val)
35
36
static struct loongson2_register_config {
37
unsigned int ctrl;
38
unsigned long long reset_counter1;
39
unsigned long long reset_counter2;
40
int cnt1_enabled, cnt2_enabled;
41
} reg;
42
43
static char *oprofid = "LoongsonPerf";
44
static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id);
45
46
static void reset_counters(void *arg)
47
{
48
write_c0_perfctrl(0);
49
write_c0_perfcnt(0);
50
}
51
52
static void loongson2_reg_setup(struct op_counter_config *cfg)
53
{
54
unsigned int ctrl = 0;
55
56
reg.reset_counter1 = 0;
57
reg.reset_counter2 = 0;
58
59
/*
60
* Compute the performance counter ctrl word.
61
* For now, count kernel and user mode.
62
*/
63
if (cfg[0].enabled) {
64
ctrl |= LOONGSON2_PERFCTRL_EVENT(0, cfg[0].event);
65
reg.reset_counter1 = 0x80000000ULL - cfg[0].count;
66
}
67
68
if (cfg[1].enabled) {
69
ctrl |= LOONGSON2_PERFCTRL_EVENT(1, cfg[1].event);
70
reg.reset_counter2 = 0x80000000ULL - cfg[1].count;
71
}
72
73
if (cfg[0].enabled || cfg[1].enabled) {
74
ctrl |= LOONGSON2_PERFCTRL_EXL | LOONGSON2_PERFCTRL_ENABLE;
75
if (cfg[0].kernel || cfg[1].kernel)
76
ctrl |= LOONGSON2_PERFCTRL_KERNEL;
77
if (cfg[0].user || cfg[1].user)
78
ctrl |= LOONGSON2_PERFCTRL_USER;
79
}
80
81
reg.ctrl = ctrl;
82
83
reg.cnt1_enabled = cfg[0].enabled;
84
reg.cnt2_enabled = cfg[1].enabled;
85
}
86
87
static void loongson2_cpu_setup(void *args)
88
{
89
write_c0_perfcnt((reg.reset_counter2 << 32) | reg.reset_counter1);
90
}
91
92
static void loongson2_cpu_start(void *args)
93
{
94
/* Start all counters on current CPU */
95
if (reg.cnt1_enabled || reg.cnt2_enabled)
96
write_c0_perfctrl(reg.ctrl);
97
}
98
99
static void loongson2_cpu_stop(void *args)
100
{
101
/* Stop all counters on current CPU */
102
write_c0_perfctrl(0);
103
memset(&reg, 0, sizeof(reg));
104
}
105
106
static irqreturn_t loongson2_perfcount_handler(int irq, void *dev_id)
107
{
108
uint64_t counter, counter1, counter2;
109
struct pt_regs *regs = get_irq_regs();
110
int enabled;
111
112
/* Check whether the irq belongs to me */
113
enabled = read_c0_perfctrl() & LOONGSON2_PERFCTRL_ENABLE;
114
if (!enabled)
115
return IRQ_NONE;
116
enabled = reg.cnt1_enabled | reg.cnt2_enabled;
117
if (!enabled)
118
return IRQ_NONE;
119
120
counter = read_c0_perfcnt();
121
counter1 = counter & 0xffffffff;
122
counter2 = counter >> 32;
123
124
if (counter1 & LOONGSON2_PERFCNT_OVERFLOW) {
125
if (reg.cnt1_enabled)
126
oprofile_add_sample(regs, 0);
127
counter1 = reg.reset_counter1;
128
}
129
if (counter2 & LOONGSON2_PERFCNT_OVERFLOW) {
130
if (reg.cnt2_enabled)
131
oprofile_add_sample(regs, 1);
132
counter2 = reg.reset_counter2;
133
}
134
135
write_c0_perfcnt((counter2 << 32) | counter1);
136
137
return IRQ_HANDLED;
138
}
139
140
static int __init loongson2_init(void)
141
{
142
return request_irq(LOONGSON2_PERFCNT_IRQ, loongson2_perfcount_handler,
143
IRQF_SHARED, "Perfcounter", oprofid);
144
}
145
146
static void loongson2_exit(void)
147
{
148
reset_counters(NULL);
149
free_irq(LOONGSON2_PERFCNT_IRQ, oprofid);
150
}
151
152
struct op_mips_model op_model_loongson2_ops = {
153
.reg_setup = loongson2_reg_setup,
154
.cpu_setup = loongson2_cpu_setup,
155
.init = loongson2_init,
156
.exit = loongson2_exit,
157
.cpu_start = loongson2_cpu_start,
158
.cpu_stop = loongson2_cpu_stop,
159
.cpu_type = LOONGSON2_CPU_TYPE,
160
.num_counters = 2
161
};
162
163