blob: acc5c6f8dd2866d646701075433d20e7d06961a0 [file] [log] [blame]
Mike Dodd8cfa7022010-11-17 11:12:26 -08001/**
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
30static 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
55static 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
93static 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
111static 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
127static void rtc_start_cpu(uint cpu)
128{
129 rtc_start();
130}
131
132static void rtc_stop_cpu(uint cpu)
133{
134 rtc_stop();
135}
136
137static 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
148static 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
166static void rtc_deinit(void)
167{
168 free_irq(RTC_IRQ, NULL);
169 release_region(RTC_PORT(0), RTC_IO_PORTS);
170}
171
172static 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
178static void rtc_remove_sysctls(ctl_table * next)
179{
180 /* nothing to do */
181}
182
183struct 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};