rtc.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,353 行 · 第 1/3 页
C
1,353 行
for_each_ebusdev(edev, ebus) { if(strcmp(edev->prom_name, "rtc") == 0) { rtc_port = edev->resource[0].start; rtc_irq = edev->irqs[0]; goto found; } } }#ifdef __sparc_v9__ for_each_isa(isa_br) { for_each_isadev(isa_dev, isa_br) { if (strcmp(isa_dev->prom_name, "rtc") == 0) { rtc_port = isa_dev->resource.start; rtc_irq = isa_dev->irq; goto found; } } }#endif printk(KERN_ERR "rtc_init: no PC rtc found\n"); return -EIO;found: if (rtc_irq == PCI_IRQ_NONE) { rtc_has_irq = 0; goto no_irq; } /* * XXX Interrupt pin #7 in Espresso is shared between RTC and * PCI Slot 2 INTA# (and some INTx# in Slot 1). SA_INTERRUPT here * is asking for trouble with add-on boards. Change to SA_SHIRQ. */ if (request_irq(rtc_irq, rtc_interrupt, SA_INTERRUPT, "rtc", (void *)&rtc_port)) { /* * Standard way for sparc to print irq's is to use * __irq_itoa(). I think for EBus it's ok to use %d. */ printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq); return -EIO; }no_irq:#else if (!request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc")) { printk(KERN_ERR "rtc: I/O port %d is not free.\n", RTC_PORT (0)); return -EIO; }#ifdef RTC_IRQ if (is_hpet_enabled()) { rtc_int_handler_ptr = hpet_rtc_interrupt; } else { rtc_int_handler_ptr = rtc_interrupt; } if(request_irq(RTC_IRQ, rtc_int_handler_ptr, 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); release_region(RTC_PORT(0), RTC_IO_EXTENT); return -EIO; } hpet_rtc_timer_init();#endif#endif /* __sparc__ vs. others */ if (misc_register(&rtc_dev)) {#ifdef RTC_IRQ free_irq(RTC_IRQ, NULL);#endif release_region(RTC_PORT(0), RTC_IO_EXTENT); return -ENODEV; } if (!create_proc_read_entry ("driver/rtc", 0, NULL, rtc_read_proc, NULL)) {#ifdef RTC_IRQ free_irq(RTC_IRQ, NULL);#endif release_region(RTC_PORT(0), RTC_IO_EXTENT); misc_deregister(&rtc_dev); return -ENOMEM; }#if defined(__alpha__) || defined(__mips__) 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(); cpu_relax(); } spin_lock_irq(&rtc_lock); year = CMOS_READ(RTC_YEAR); ctrl = CMOS_READ(RTC_CONTROL); spin_unlock_irq(&rtc_lock); if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) BCD_TO_BIN(year); /* This should never happen... */ if (year < 20) { epoch = 2000; guess = "SRM (post-2000)"; } else if (year >= 20 && year < 48) { epoch = 1980; guess = "ARC console"; } else if (year >= 48 && year < 72) { epoch = 1952; guess = "Digital UNIX";#if defined(__mips__) } else if (year >= 72 && year < 74) { epoch = 2000; guess = "Digital DECstation";#else } else if (year >= 70) { epoch = 1900; guess = "Standard PC (1900)";#endif } if (guess) printk(KERN_INFO "rtc: %s epoch (%lu) detected\n", guess, epoch);#endif#ifdef RTC_IRQ if (rtc_has_irq == 0) goto no_irq2; init_timer(&rtc_irq_timer); rtc_irq_timer.function = rtc_dropped_irq; spin_lock_irq(&rtc_lock); rtc_freq = 1024; if (!hpet_set_periodic_freq(rtc_freq)) { /* Initialize periodic freq. to CMOS reset default, which is 1024Hz */ CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT); } spin_unlock_irq(&rtc_lock);no_irq2:#endif (void) init_sysctl(); printk(KERN_INFO "Real Time Clock Driver v" RTC_VERSION "\n"); return 0;}static void __exit rtc_exit (void){ cleanup_sysctl(); remove_proc_entry ("driver/rtc", NULL); misc_deregister(&rtc_dev);#ifdef __sparc__ if (rtc_has_irq) free_irq (rtc_irq, &rtc_port);#else release_region (RTC_PORT (0), RTC_IO_EXTENT);#ifdef RTC_IRQ if (rtc_has_irq) free_irq (RTC_IRQ, NULL);#endif#endif /* __sparc__ */}module_init(rtc_init);module_exit(rtc_exit);#ifdef RTC_IRQ/* * 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.) */static void rtc_dropped_irq(unsigned long data){ unsigned long freq; spin_lock_irq (&rtc_lock); if (hpet_rtc_dropped_irq()) { spin_unlock_irq(&rtc_lock); return; } /* Just in case someone disabled the timer from behind our back... */ if (rtc_status & RTC_TIMER_ON) mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100); rtc_irq_data += ((rtc_freq/HZ)<<8); rtc_irq_data &= ~0xff; rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0); /* restart */ freq = rtc_freq; spin_unlock_irq(&rtc_lock); printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", freq); /* Now we have new data */ wake_up_interruptible(&rtc_wait); kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);}#endif/* * Info exported via "/proc/driver/rtc". */static int rtc_proc_output (char *buf){#define YN(bit) ((ctrl & bit) ? "yes" : "no")#define NY(bit) ((ctrl & bit) ? "no" : "yes") char *p; struct rtc_time tm; unsigned char batt, ctrl; unsigned long freq; spin_lock_irq(&rtc_lock); batt = CMOS_READ(RTC_VALID) & RTC_VRT; ctrl = CMOS_READ(RTC_CONTROL); freq = rtc_freq; spin_unlock_irq(&rtc_lock); p = buf; rtc_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", YN(RTC_DST_EN), NY(RTC_DM_BINARY), YN(RTC_24H), YN(RTC_SQWE), YN(RTC_AIE), YN(RTC_UIE), YN(RTC_PIE), freq, batt ? "okay" : "dead"); return p - buf;#undef YN#undef NY}static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ int len = rtc_proc_output (page); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len>count) len = count; if (len<0) len = 0; return len;}void rtc_get_rtc_time(struct rtc_time *rtc_tm){ unsigned long uip_watchdog = jiffies; unsigned char ctrl;#ifdef CONFIG_MACH_DECSTATION unsigned int real_year;#endif /* * 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(); cpu_relax(); } /* * 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. */ spin_lock_irq(&rtc_lock); 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);#ifdef CONFIG_MACH_DECSTATION real_year = CMOS_READ(RTC_DEC_YEAR);#endif ctrl = CMOS_READ(RTC_CONTROL); spin_unlock_irq(&rtc_lock); 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); }#ifdef CONFIG_MACH_DECSTATION rtc_tm->tm_year += real_year - 72;#endif /* * 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--;}static void get_rtc_alm_time(struct rtc_time *alm_tm){ unsigned char ctrl; /* * Only the values that we read from the RTC are set. That * means only tm_hour, tm_min, and tm_sec. */ spin_lock_irq(&rtc_lock); 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); spin_unlock_irq(&rtc_lock); 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); }}#ifdef RTC_IRQ/* * 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. */static void mask_rtc_irq_bit(unsigned char bit){ unsigned char val; spin_lock_irq(&rtc_lock); if (hpet_mask_rtc_irq_bit(bit)) { spin_unlock_irq(&rtc_lock); return; } val = CMOS_READ(RTC_CONTROL); val &= ~bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); rtc_irq_data = 0; spin_unlock_irq(&rtc_lock);}static void set_rtc_irq_bit(unsigned char bit){ unsigned char val; spin_lock_irq(&rtc_lock); if (hpet_set_rtc_irq_bit(bit)) { spin_unlock_irq(&rtc_lock); return; } val = CMOS_READ(RTC_CONTROL); val |= bit; CMOS_WRITE(val, RTC_CONTROL); CMOS_READ(RTC_INTR_FLAGS); rtc_irq_data = 0; spin_unlock_irq(&rtc_lock);}#endifMODULE_AUTHOR("Paul Gortmaker");MODULE_LICENSE("GPL");MODULE_ALIAS_MISCDEV(RTC_MINOR);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?