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