?? rtc.c
字號:
CMOS_WRITE(val, RTC_FREQ_SELECT); restore_flags(flags); return 0; }#ifdef __alpha__ case RTC_EPOCH_READ: /* Read the epoch. */ { return put_user (epoch, (unsigned long *)arg); } case RTC_EPOCH_SET: /* Set the epoch. */ { /* * There were no RTC clocks before 1900. */ if (arg < 1900) return -EINVAL; if (!capable(CAP_SYS_TIME)) return -EACCES; epoch = arg; return 0; }#endif default: return -EINVAL; } return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;}/* * We enforce only one user at a time here with the open/close. * Also clear the previous interrupt data on an open, and clean * up things on a close. */static int rtc_open(struct inode *inode, struct file *file){ if(rtc_status & RTC_IS_OPEN) return -EBUSY; rtc_status |= RTC_IS_OPEN; rtc_irq_data = 0; return 0;}static int rtc_release(struct inode *inode, struct file *file){ /* * Turn off all interrupts once the device is no longer * in use, and clear the data. */ unsigned char tmp; unsigned long flags; save_flags(flags); cli(); tmp = CMOS_READ(RTC_CONTROL); tmp &= ~RTC_PIE; tmp &= ~RTC_AIE; tmp &= ~RTC_UIE; CMOS_WRITE(tmp, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); restore_flags(flags); if (rtc_status & RTC_TIMER_ON) { rtc_status &= ~RTC_TIMER_ON; del_timer(&rtc_irq_timer); } rtc_irq_data = 0; rtc_status &= ~RTC_IS_OPEN; return 0;}static unsigned int rtc_poll(struct file *file, poll_table *wait){ poll_wait(file, &rtc_wait, wait); if (rtc_irq_data != 0) return POLLIN | POLLRDNORM; return 0;}/* * The various file operations we support. */static struct file_operations rtc_fops = { rtc_llseek, rtc_read, NULL, /* No write */ NULL, /* No readdir */ rtc_poll, rtc_ioctl, NULL, /* No mmap */ rtc_open, NULL, /* flush */ rtc_release};static struct miscdevice rtc_dev={ RTC_MINOR, "rtc", &rtc_fops};__initfunc(int rtc_init(void)){ unsigned long flags;#ifdef __alpha__ unsigned int year, ctrl; unsigned long uip_watchdog; char *guess = NULL;#endif printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION); if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL)) { /* Yeah right, seeing as irq 8 doesn't even hit the bus. */ printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ); return -EIO; } misc_register(&rtc_dev); /* Check region? Naaah! Just snarf it up. */ request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");#ifdef __alpha__ rtc_freq = HZ; /* Each operating system on an Alpha uses its own epoch. Let's try to guess which one we are using now. */ uip_watchdog = jiffies; if (rtc_is_updating() != 0) while (jiffies - uip_watchdog < 2*HZ/100) barrier(); save_flags(flags); cli(); year = CMOS_READ(RTC_YEAR); ctrl = CMOS_READ(RTC_CONTROL); restore_flags(flags); if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) BCD_TO_BIN(year); /* This should never happen... */ if (year > 10 && year < 44) { epoch = 1980; guess = "ARC console"; } else if (year < 96) { epoch = 1952; guess = "Digital UNIX"; } if (guess) printk("rtc: %s epoch (%lu) detected\n", guess, epoch);#endif init_timer(&rtc_irq_timer); rtc_irq_timer.function = rtc_dropped_irq; rtc_wait = NULL; save_flags(flags); cli(); /* Initialize periodic freq. to CMOS reset default, which is 1024Hz */ CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT); restore_flags(flags); rtc_freq = 1024; return 0;}/* * At IRQ rates >= 4096Hz, an interrupt may get lost altogether. * (usually during an IDE disk interrupt, with IRQ unmasking off) * Since the interrupt handler doesn't get called, the IRQ status * byte doesn't get read, and the RTC stops generating interrupts. * A timer is set, and will call this function if/when that happens. * To get it out of this stalled state, we just read the status. * At least a jiffy of interrupts (rtc_freq/HZ) will have been lost. * (You *really* shouldn't be trying to use a non-realtime system * for something that requires a steady > 1KHz signal anyways.) */void rtc_dropped_irq(unsigned long data){ unsigned long flags; printk(KERN_INFO "rtc: lost some interrupts at %ldHz.\n", rtc_freq); mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); save_flags(flags); cli(); rtc_irq_data += ((rtc_freq/HZ)<<8); rtc_irq_data &= ~0xff; rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */ restore_flags(flags);}/* * Info exported via "/proc/rtc". */int get_rtc_status(char *buf){ char *p; struct rtc_time tm; unsigned char batt, ctrl; unsigned long flags; save_flags(flags); cli(); batt = CMOS_READ(RTC_VALID) & RTC_VRT; ctrl = CMOS_READ(RTC_CONTROL); restore_flags(flags); p = buf; get_rtc_time(&tm); /* * There is no way to tell if the luser has the RTC set for local * time or for Universal Standard Time (GMT). Probably local though. */ p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" "rtc_date\t: %04d-%02d-%02d\n" "rtc_epoch\t: %04lu\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); get_rtc_alm_time(&tm); /* * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will * match any value for that particular field. Values that are * greater than a valid time, but less than 0xc0 shouldn't appear. */ p += sprintf(p, "alarm\t\t: "); if (tm.tm_hour <= 24) p += sprintf(p, "%02d:", tm.tm_hour); else p += sprintf(p, "**:"); if (tm.tm_min <= 59) p += sprintf(p, "%02d:", tm.tm_min); else p += sprintf(p, "**:"); if (tm.tm_sec <= 59) p += sprintf(p, "%02d\n", tm.tm_sec); else p += sprintf(p, "**\n"); p += sprintf(p, "DST_enable\t: %s\n" "BCD\t\t: %s\n" "24hr\t\t: %s\n" "square_wave\t: %s\n" "alarm_IRQ\t: %s\n" "update_IRQ\t: %s\n" "periodic_IRQ\t: %s\n" "periodic_freq\t: %ld\n" "batt_status\t: %s\n", (ctrl & RTC_DST_EN) ? "yes" : "no", (ctrl & RTC_DM_BINARY) ? "no" : "yes", (ctrl & RTC_24H) ? "yes" : "no", (ctrl & RTC_SQWE) ? "yes" : "no", (ctrl & RTC_AIE) ? "yes" : "no", (ctrl & RTC_UIE) ? "yes" : "no", (ctrl & RTC_PIE) ? "yes" : "no", rtc_freq, batt ? "okay" : "dead"); return p - buf;}/* * Returns true if a clock update is in progress */static inline unsigned char rtc_is_updating(void){ unsigned long flags; unsigned char uip; save_flags(flags); cli(); uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); restore_flags(flags); return uip;}void get_rtc_time(struct rtc_time *rtc_tm){ unsigned long flags, uip_watchdog = jiffies; unsigned char ctrl; /* * read RTC once any update in progress is done. The update * can take just over 2ms. We wait 10 to 20ms. There is no need to * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. * If you need to know *exactly* when a second has started, enable * periodic update complete interrupts, (via ioctl) and then * immediately read /dev/rtc which will block until you get the IRQ. * Once the read clears, read the RTC time (again via ioctl). Easy. */ if (rtc_is_updating() != 0) while (jiffies - uip_watchdog < 2*HZ/100) barrier(); /* * Only the values that we read from the RTC are set. We leave * tm_wday, tm_yday and tm_isdst untouched. Even though the * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated * by the RTC when initially set to a non-zero value. */ save_flags(flags); cli(); rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); rtc_tm->tm_year = CMOS_READ(RTC_YEAR); ctrl = CMOS_READ(RTC_CONTROL); restore_flags(flags); if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BCD_TO_BIN(rtc_tm->tm_sec); BCD_TO_BIN(rtc_tm->tm_min); BCD_TO_BIN(rtc_tm->tm_hour); BCD_TO_BIN(rtc_tm->tm_mday); BCD_TO_BIN(rtc_tm->tm_mon); BCD_TO_BIN(rtc_tm->tm_year); } /* * Account for differences between how the RTC uses the values * and how they are defined in a struct rtc_time; */ if ((rtc_tm->tm_year += (epoch - 1900)) <= 69) rtc_tm->tm_year += 100; rtc_tm->tm_mon--;}void get_rtc_alm_time(struct rtc_time *alm_tm){ unsigned long flags; unsigned char ctrl; /* * Only the values that we read from the RTC are set. That * means only tm_hour, tm_min, and tm_sec. */ save_flags(flags); cli(); alm_tm->tm_sec = CMOS_READ(RTC_SECONDS_ALARM); alm_tm->tm_min = CMOS_READ(RTC_MINUTES_ALARM); alm_tm->tm_hour = CMOS_READ(RTC_HOURS_ALARM); ctrl = CMOS_READ(RTC_CONTROL); restore_flags(flags); if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { BCD_TO_BIN(alm_tm->tm_sec); BCD_TO_BIN(alm_tm->tm_min); BCD_TO_BIN(alm_tm->tm_hour); }}/* * Used to disable/enable interrupts for any one of UIE, AIE, PIE. * Rumour has it that if you frob the interrupt enable/disable * bits in RTC_CONTROL, you should read RTC_INTR_FLAGS, to * ensure you actually start getting interrupts. Probably for * compatibility with older/broken chipset RTC implementations. * We also clear out any old irq data after an ioctl() that * meddles with the interrupt enable/disable bits. */void mask_rtc_irq_bit(unsigned char bit){ unsigned char val; unsigned long flags; save_flags(flags); cli(); val = CMOS_READ(RTC_CONTROL); val &= ~bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); restore_flags(flags); rtc_irq_data = 0;}void set_rtc_irq_bit(unsigned char bit){ unsigned char val; unsigned long flags; save_flags(flags); cli(); val = CMOS_READ(RTC_CONTROL); val |= bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); rtc_irq_data = 0; restore_flags(flags);}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -