18cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/** 28cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @file op_rtc.c 38cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * Setup and handling of RTC interrupts 48cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 58cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @remark Copyright 2002 OProfile authors 68cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @remark Read the file COPYING 78cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * 88cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @author Bob Montgomery 98cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @author Philippe Elie 108cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * @author John Levon 118cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */ 128cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 138cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <linux/ioport.h> 148cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <linux/mc146818rtc.h> 158cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include <asm/ptrace.h> 168cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 178cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "oprofile.h" 188cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "op_arch.h" 198cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#include "op_util.h" 208cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 218cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#define RTC_IO_PORTS 2 228cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 238cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/* not in 2.2 */ 248cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#ifndef RTC_IRQ 258cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#define RTC_IRQ 8 268cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd#endif 278cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 288cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd/* ---------------- RTC handler ------------------ */ 298cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 308cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void do_rtc_interrupt(int irq, void * dev_id, struct pt_regs * regs) 318cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 328cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd uint cpu = op_cpu_id(); 338cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned char intr_flags; 348cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned long flags; 358cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 368cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd int usermode = user_mode(regs); 378cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if ((sysctl.ctr[0].kernel && usermode) 388cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd || (sysctl.ctr[0].user && !usermode)) 398cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return; 408cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 418cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd lock_rtc(flags); 428cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 438cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* read and ack the interrupt */ 448cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd intr_flags = CMOS_READ(RTC_INTR_FLAGS); 458cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* Is this my type of interrupt? */ 468cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (intr_flags & RTC_PF) { 478cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd op_do_profile(cpu, instruction_pointer(regs), IRQ_ENABLED(regs), 0); 488cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 498cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 508cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unlock_rtc(flags); 518cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 528cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return; 538cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 548cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 558cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic int rtc_setup(void) 568cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 578cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned char tmp_control; 588cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned long flags; 598cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned char tmp_freq_select; 608cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned long target; 618cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned int exp, freq; 628cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 638cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd lock_rtc(flags); 648cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 658cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* disable periodic interrupts */ 668cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_control = CMOS_READ(RTC_CONTROL); 678cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_control &= ~RTC_PIE; 688cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd CMOS_WRITE(tmp_control, RTC_CONTROL); 698cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd CMOS_READ(RTC_INTR_FLAGS); 708cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 718cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* Set the frequency for periodic interrupts by finding the 728cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd * closest power of two within the allowed range. 738cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd */ 748cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 758cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd target = sysctl.ctr[0].count; 768cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 778cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd exp = 0; 788cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd while (target > (1 << exp) + ((1 << exp) >> 1)) 798cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd exp++; 808cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd freq = 16 - exp; 818cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 828cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_freq_select = CMOS_READ(RTC_FREQ_SELECT); 838cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_freq_select = (tmp_freq_select & 0xf0) | freq; 848cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd CMOS_WRITE(tmp_freq_select, RTC_FREQ_SELECT); 858cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 868cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* Update /proc with the actual frequency. */ 878cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd sysctl_parms.ctr[0].count = sysctl.ctr[0].count = 1 << exp; 888cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 898cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unlock_rtc(flags); 908cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 918cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 928cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 938cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void rtc_start(void) 948cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 958cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned char tmp_control; 968cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned long flags; 978cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 988cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd lock_rtc(flags); 998cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1008cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* Enable periodic interrupts */ 1018cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_control = CMOS_READ(RTC_CONTROL); 1028cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_control |= RTC_PIE; 1038cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd CMOS_WRITE(tmp_control, RTC_CONTROL); 1048cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1058cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* read the flags register to start interrupts */ 1068cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd CMOS_READ(RTC_INTR_FLAGS); 1078cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1088cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unlock_rtc(flags); 1098cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1108cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1118cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void rtc_stop(void) 1128cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1138cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned char tmp_control; 1148cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unsigned long flags; 1158cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1168cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd lock_rtc(flags); 1178cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1188cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* disable periodic interrupts */ 1198cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_control = CMOS_READ(RTC_CONTROL); 1208cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd tmp_control &= ~RTC_PIE; 1218cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd CMOS_WRITE(tmp_control, RTC_CONTROL); 1228cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd CMOS_READ(RTC_INTR_FLAGS); 1238cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1248cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd unlock_rtc(flags); 1258cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1268cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1278cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void rtc_start_cpu(uint cpu) 1288cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1298cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd rtc_start(); 1308cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1318cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1328cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void rtc_stop_cpu(uint cpu) 1338cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1348cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd rtc_stop(); 1358cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1368cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1378cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic int rtc_check_params(void) 1388cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1398cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd int target = sysctl.ctr[0].count; 1408cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1418cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (check_range(target, OP_MIN_RTC_COUNT, OP_MAX_RTC_COUNT, 1428cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd "RTC value %d is out of range (%d-%d)\n")) 1438cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return -EINVAL; 1448cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1458cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 1468cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1478cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1488cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic int rtc_init(void) 1498cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1508cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* request_region returns 0 on **failure** */ 1518cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (!request_region_check(RTC_PORT(0), RTC_IO_PORTS, "oprofile")) { 1528cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd printk(KERN_ERR "oprofile: can't get RTC I/O Ports\n"); 1538cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return -EBUSY; 1548cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1558cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1568cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* request_irq returns 0 on **success** */ 1578cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd if (request_irq(RTC_IRQ, do_rtc_interrupt, 1588cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd SA_INTERRUPT, "oprofile", NULL)) { 1598cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd printk(KERN_ERR "oprofile: IRQ%d busy \n", RTC_IRQ); 1608cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd release_region(RTC_PORT(0), RTC_IO_PORTS); 1618cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return -EBUSY; 1628cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd } 1638cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 1648cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1658cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1668cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void rtc_deinit(void) 1678cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1688cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd free_irq(RTC_IRQ, NULL); 1698cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd release_region(RTC_PORT(0), RTC_IO_PORTS); 1708cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1718cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1728cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic int rtc_add_sysctls(ctl_table * next) 1738cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1748cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd *next = ((ctl_table) { 1, "rtc_value", &sysctl_parms.ctr[0].count, sizeof(int), 0600, NULL, lproc_dointvec, NULL, }); 1758cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd return 0; 1768cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1778cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1788cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstatic void rtc_remove_sysctls(ctl_table * next) 1798cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd{ 1808cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd /* nothing to do */ 1818cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd} 1828cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd 1838cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Doddstruct op_int_operations op_rtc_ops = { 1848cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd init: rtc_init, 1858cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd deinit: rtc_deinit, 1868cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd add_sysctls: rtc_add_sysctls, 1878cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd remove_sysctls: rtc_remove_sysctls, 1888cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd check_params: rtc_check_params, 1898cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd setup: rtc_setup, 1908cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd start: rtc_start, 1918cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd stop: rtc_stop, 1928cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd start_cpu: rtc_start_cpu, 1938cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd stop_cpu: rtc_stop_cpu, 1948cfa702f803c5ef6a2b062a489a1b2cf66b45b5eMike Dodd}; 195