op_rtc.c revision cc2ee177dbb3befca43e36cfc56778b006c3d050
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
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};
195