• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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