Home | History | Annotate | Download | only in x86
      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 
     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 
     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 
     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 
    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 
    127 static void rtc_start_cpu(uint cpu)
    128 {
    129 	rtc_start();
    130 }
    131 
    132 static void rtc_stop_cpu(uint cpu)
    133 {
    134 	rtc_stop();
    135 }
    136 
    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 
    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 
    166 static void rtc_deinit(void)
    167 {
    168 	free_irq(RTC_IRQ, NULL);
    169 	release_region(RTC_PORT(0), RTC_IO_PORTS);
    170 }
    171 
    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 
    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