1 /**
2 * @file op_rtc.c
3 * Setup and handling of RTC interrupts
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Bob Montgomery
9 * @author Philippe Elie
10 * @author John Levon
11 */
12
13 #include <linux/ioport.h>
14 #include <linux/mc146818rtc.h>
15 #include <asm/ptrace.h>
16
17 #include "oprofile.h"
18 #include "op_arch.h"
19 #include "op_util.h"
20
21 #define RTC_IO_PORTS 2
22
23 /* not in 2.2 */
24 #ifndef RTC_IRQ
25 #define RTC_IRQ 8
26 #endif
27
28 /* ---------------- RTC handler ------------------ */
29
do_rtc_interrupt(int irq,void * dev_id,struct pt_regs * regs)30 static void do_rtc_interrupt(int irq, void * dev_id, struct pt_regs * regs)
31 {
32 uint cpu = op_cpu_id();
33 unsigned char intr_flags;
34 unsigned long flags;
35
36 int usermode = user_mode(regs);
37 if ((sysctl.ctr[0].kernel && usermode)
38 || (sysctl.ctr[0].user && !usermode))
39 return;
40
41 lock_rtc(flags);
42
43 /* read and ack the interrupt */
44 intr_flags = CMOS_READ(RTC_INTR_FLAGS);
45 /* Is this my type of interrupt? */
46 if (intr_flags & RTC_PF) {
47 op_do_profile(cpu, instruction_pointer(regs), IRQ_ENABLED(regs), 0);
48 }
49
50 unlock_rtc(flags);
51
52 return;
53 }
54
rtc_setup(void)55 static int rtc_setup(void)
56 {
57 unsigned char tmp_control;
58 unsigned long flags;
59 unsigned char tmp_freq_select;
60 unsigned long target;
61 unsigned int exp, freq;
62
63 lock_rtc(flags);
64
65 /* disable periodic interrupts */
66 tmp_control = CMOS_READ(RTC_CONTROL);
67 tmp_control &= ~RTC_PIE;
68 CMOS_WRITE(tmp_control, RTC_CONTROL);
69 CMOS_READ(RTC_INTR_FLAGS);
70
71 /* Set the frequency for periodic interrupts by finding the
72 * closest power of two within the allowed range.
73 */
74
75 target = sysctl.ctr[0].count;
76
77 exp = 0;
78 while (target > (1 << exp) + ((1 << exp) >> 1))
79 exp++;
80 freq = 16 - exp;
81
82 tmp_freq_select = CMOS_READ(RTC_FREQ_SELECT);
83 tmp_freq_select = (tmp_freq_select & 0xf0) | freq;
84 CMOS_WRITE(tmp_freq_select, RTC_FREQ_SELECT);
85
86 /* Update /proc with the actual frequency. */
87 sysctl_parms.ctr[0].count = sysctl.ctr[0].count = 1 << exp;
88
89 unlock_rtc(flags);
90 return 0;
91 }
92
rtc_start(void)93 static void rtc_start(void)
94 {
95 unsigned char tmp_control;
96 unsigned long flags;
97
98 lock_rtc(flags);
99
100 /* Enable periodic interrupts */
101 tmp_control = CMOS_READ(RTC_CONTROL);
102 tmp_control |= RTC_PIE;
103 CMOS_WRITE(tmp_control, RTC_CONTROL);
104
105 /* read the flags register to start interrupts */
106 CMOS_READ(RTC_INTR_FLAGS);
107
108 unlock_rtc(flags);
109 }
110
rtc_stop(void)111 static void rtc_stop(void)
112 {
113 unsigned char tmp_control;
114 unsigned long flags;
115
116 lock_rtc(flags);
117
118 /* disable periodic interrupts */
119 tmp_control = CMOS_READ(RTC_CONTROL);
120 tmp_control &= ~RTC_PIE;
121 CMOS_WRITE(tmp_control, RTC_CONTROL);
122 CMOS_READ(RTC_INTR_FLAGS);
123
124 unlock_rtc(flags);
125 }
126
rtc_start_cpu(uint cpu)127 static void rtc_start_cpu(uint cpu)
128 {
129 rtc_start();
130 }
131
rtc_stop_cpu(uint cpu)132 static void rtc_stop_cpu(uint cpu)
133 {
134 rtc_stop();
135 }
136
rtc_check_params(void)137 static int rtc_check_params(void)
138 {
139 int target = sysctl.ctr[0].count;
140
141 if (check_range(target, OP_MIN_RTC_COUNT, OP_MAX_RTC_COUNT,
142 "RTC value %d is out of range (%d-%d)\n"))
143 return -EINVAL;
144
145 return 0;
146 }
147
rtc_init(void)148 static int rtc_init(void)
149 {
150 /* request_region returns 0 on **failure** */
151 if (!request_region_check(RTC_PORT(0), RTC_IO_PORTS, "oprofile")) {
152 printk(KERN_ERR "oprofile: can't get RTC I/O Ports\n");
153 return -EBUSY;
154 }
155
156 /* request_irq returns 0 on **success** */
157 if (request_irq(RTC_IRQ, do_rtc_interrupt,
158 SA_INTERRUPT, "oprofile", NULL)) {
159 printk(KERN_ERR "oprofile: IRQ%d busy \n", RTC_IRQ);
160 release_region(RTC_PORT(0), RTC_IO_PORTS);
161 return -EBUSY;
162 }
163 return 0;
164 }
165
rtc_deinit(void)166 static void rtc_deinit(void)
167 {
168 free_irq(RTC_IRQ, NULL);
169 release_region(RTC_PORT(0), RTC_IO_PORTS);
170 }
171
rtc_add_sysctls(ctl_table * next)172 static int rtc_add_sysctls(ctl_table * next)
173 {
174 *next = ((ctl_table) { 1, "rtc_value", &sysctl_parms.ctr[0].count, sizeof(int), 0600, NULL, lproc_dointvec, NULL, });
175 return 0;
176 }
177
rtc_remove_sysctls(ctl_table * next)178 static void rtc_remove_sysctls(ctl_table * next)
179 {
180 /* nothing to do */
181 }
182
183 struct op_int_operations op_rtc_ops = {
184 init: rtc_init,
185 deinit: rtc_deinit,
186 add_sysctls: rtc_add_sysctls,
187 remove_sysctls: rtc_remove_sysctls,
188 check_params: rtc_check_params,
189 setup: rtc_setup,
190 start: rtc_start,
191 stop: rtc_stop,
192 start_cpu: rtc_start_cpu,
193 stop_cpu: rtc_stop_cpu,
194 };
195