Messages in this thread Patches in this message | | | Date | Mon, 6 Dec 1999 10:26:39 +0100 (CET) | From | Ulrich Windl <> | Subject | code sketch: updating RTC in 2.2.12 |
| |
Hello,
recently there was some heaty discussion on kernel bloat and setting the RTC (CMOS clock). I did think on it several days, but then I implemented it:
The patch difference from below sketches the essential changes (i386 architecture). two new routines ktime_to_rtc() and rtc_to_ktime() do the work, supported by sys_tz and rtc_runs_localtime (default: no).
These variables are settable directly via sysctl(), because we need it. I don't like the sysctl implementation a lot, but it's a quick patch anyway.
I think that pattern isn't much of a kernel bloat, and it fixes more problems than it creates.
I know it will never go into the kernel, but it's there. For a complete patch see linux.kernel.org in pub/linux/daemons/ntp/PPS for PPSkit-0.8.1-0.9.0-cand1.diff.gz. Make sure to read the first 10 lines of the patch...
Regards, Ulrich
Index: linux/arch/i386/kernel/time.c -diff -u linux/arch/i386/kernel/time.c:1.1.1.7 linux/arch/i386/kernel/time.c:1.1.1.7.6.4 +diff -u linux/arch/i386/kernel/time.c:1.1.1.7 linux/arch/i386/kernel/time.c:1.1.1.7.6.6 --- linux/arch/i386/kernel/time.c:1.1.1.7 Wed May 12 21:42:36 1999 -+++ linux/arch/i386/kernel/time.c Thu Oct 14 21:17:43 1999 ++++ linux/arch/i386/kernel/time.c Sat Dec 4 21:30:51 1999 -@@ -377,6 +379,7 @@ +- * In order to set the CMOS clock precisely, set_rtc_mmss has to be +- * called 500 ms after the second nowtime has started, because when +- * nowtime is written into the registers of the CMOS clock, it will ++ * In order to set the CMOS clock precisely, update_rtc has to be ++ * called 500 ms after the current second has started, because when ++ * the time is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Motorola + * MC146818A or Dallas DS12887 data sheet for details. +- * +- * BUG: This routine does not handle hour overflow properly; it just +- * sets the minutes. Usually you'll only notice that after reboot! + */ +-static int set_rtc_mmss(unsigned long nowtime) ++static void update_rtc(void) + { +- int retval = 0; +- int real_seconds, real_minutes, cmos_minutes; ++ int ss, mi, hh, dd, mo, yy; + unsigned char save_control, save_freq_select; + +- save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ ++ /* tell the clock it's being set */ ++ save_control = CMOS_READ(RTC_CONTROL); + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + +- save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ ++ /* stop and reset prescaler */ ++ save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + +- cmos_minutes = CMOS_READ(RTC_MINUTES); ++ mi = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) +- BCD_TO_BIN(cmos_minutes); ++ BCD_TO_BIN(mi); + +- /* +- * since we're only adjusting minutes and seconds, +- * don't interfere with hour overflow. This avoids +- * messing with unknown time zones but requires your +- * RTC not to be off by more than 15 minutes +- */ +- real_seconds = nowtime % 60; +- real_minutes = nowtime / 60; +- if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) +- real_minutes += 30; /* correct for half hour time zone */ +- real_minutes %= 60; +- +- if (abs(real_minutes - cmos_minutes) < 30) { +- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { +- BIN_TO_BCD(real_seconds); +- BIN_TO_BCD(real_minutes); +- } +- CMOS_WRITE(real_seconds,RTC_SECONDS); +- CMOS_WRITE(real_minutes,RTC_MINUTES); +- } else { +- printk(KERN_WARNING +- "set_rtc_mmss: can't update from %d to %d\n", +- cmos_minutes, real_minutes); +- retval = -1; ++ ktime_to_rtc(xtime.tv_sec, &ss, &dd); ++ hh = ss >> 16; ++ mi = (ss >> 8) & 0xff; ++ ss &= 0xff; ++ yy = (dd >> 16) % 100; ++ mo = (dd >> 8) & 0xff; ++ dd &= 0xff; ++ printk(KERN_DEBUG "update_rtc: %d-%02d-%02d %02d:%02d:%02d\n", ++ yy, mo, dd, hh, mi, ss); ++ ++ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { ++ BIN_TO_BCD(ss); ++ BIN_TO_BCD(mi); ++ BIN_TO_BCD(hh); ++ BIN_TO_BCD(dd); ++ BIN_TO_BCD(mo); ++ BIN_TO_BCD(yy); + } ++ CMOS_WRITE(ss, RTC_SECONDS); ++ CMOS_WRITE(mi, RTC_MINUTES); ++ CMOS_WRITE(hh, RTC_HOURS); ++ CMOS_WRITE(dd, RTC_DAY_OF_MONTH); ++ CMOS_WRITE(mo, RTC_MONTH); ++ CMOS_WRITE(yy, RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated +@@ -346,8 +353,6 @@ + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); +- +- return retval; + } + + /* last time the cmos clock got updated */ +@@ -377,20 +382,21 @@ smp_local_timer_interrupt(regs); #endif +#ifdef CONFIG_NTP /* * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be -@@ -384,13 +387,14 @@ +- * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be ++ * CMOS clock accordingly every ~11 minutes. Update_rtc() has to be + * called as close as possible to 500 ms before the new second starts. */ - if ((time_status & STA_UNSYNC) == 0 && - xtime.tv_sec > last_rtc_update + 660 && +- if ((time_status & STA_UNSYNC) == 0 && +- xtime.tv_sec > last_rtc_update + 660 && - xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && - xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { -+ xtime.tv_nsec >= NANOSECOND / 2 - ((unsigned long) time_tick) / 2 && -+ xtime.tv_nsec <= NANOSECOND / 2 + ((unsigned long) time_tick) / 2) { - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; +- if (set_rtc_mmss(xtime.tv_sec) == 0) +- last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ -+ else /* do it again in 10 minutes */ -+ last_rtc_update = xtime.tv_sec - 600; ++ if (rtc_update_slave && ++ xtime.tv_sec > last_rtc_update + rtc_update_slave && ++ xtime.tv_nsec >= NANOSECOND / 2 - ((unsigned long) time_tick) / 2 && ++ xtime.tv_nsec <= NANOSECOND / 2 + ((unsigned long) time_tick) / 2) { ++ update_rtc(); ++ last_rtc_update = xtime.tv_sec; ++ rtc_update_slave = 0; } +#endif #ifdef CONFIG_MCA if( MCA_bus ) { @@ -1629,6 +1777,7 @@ - } - if ((year += 1900) < 1970) - year += 100; +- return mktime(year, mon, day, hour, min, sec); + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); @@ -1639,7 +1788,7 @@ + } + if ((year += 1900) < 1970) /* Fix for year 2000 rollover */ + year += 100; /* Doesn't the CMOS have a century? */ - return mktime(year, mon, day, hour, min, sec); ++ return rtc_to_ktime(year, mon, day, hour, min, sec); } static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL}; Index: linux/kernel/time.c -diff -u linux/kernel/time.c:1.1.1.3 linux/kernel/time.c:1.1.1.3.14.6 +diff -u linux/kernel/time.c:1.1.1.3 linux/kernel/time.c:1.1.1.3.14.14 --- linux/kernel/time.c:1.1.1.3 Wed Mar 24 20:31:47 1999 -+++ linux/kernel/time.c Thu Oct 7 20:13:58 1999 -@@ -7,53 +7,457 @@ ++++ linux/kernel/time.c Sat Dec 4 21:19:04 1999 @@ -6018,6 +6562,31 @@ +#define ADJ_SERVED_MODE_BITS (ADJ_TICKADJ|ADJ_TIMETICK|ADJ_ADJTIME) +#endif + ++/* Define entries for `time' subdirectory. ++ * WARNING: writable values are not checked for plausibility! ++ */ ++ctl_table kern_time_table[] = { ++ {KERN_TIME_TIMEZONE, "timezone", &sys_tz, sizeof(sys_tz), ++ 0644, NULL, &proc_dointvec}, ++ {KERN_TIME_RTC_UPDATE, "rtc_runs_localtime", &rtc_runs_localtime, ++ sizeof(rtc_runs_localtime), ++ 0644, NULL, &proc_dointvec}, ++ {KERN_TIME_RTC_UPDATE, "rtc_update", &rtc_update, sizeof(rtc_update), ++ 0644, NULL, &proc_dointvec}, ++ {KERN_TIME_TIME_TICK, "time_tick", &time_tick, sizeof(time_tick), ++ 0444, NULL, &proc_dointvec}, ++ {KERN_TIME_TICKADJ, "tickadj", &tickadj, sizeof(tickadj), ++ 0444, NULL, &proc_dointvec}, ++#ifdef CONFIG_NTP ++#ifdef CONFIG_NTP_PPS ++ {KERN_TIME_PPS_VAR, "pps", &pps, sizeof(pps), ++ 0444, NULL, &proc_dointvec}, ++#endif ++#endif ++ {0} ++}; ++ ++ +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. @@ -6051,7 +6620,61 @@ } -void (*do_get_fast_time)(struct timeval *) = do_normal_gettime; -- ++/* Convert Gregorian calendar information from RTC to kernel time ++ * (uses mktime). ++ */ ++time_t rtc_to_ktime(unsigned int year, unsigned int mon, ++ unsigned int day, unsigned int hour, ++ unsigned int min, unsigned int sec) ++{ ++ time_t result = mktime(year, mon, day, hour, min, sec); ++ if (rtc_runs_localtime) { /* apply effective timezone */ ++ result += sys_tz.tz_minuteswest * 60; ++ if (sys_tz.tz_dsttime) ++ result -= 3600; ++ } ++ return result; ++} + ++/* Build time and date components from system time, intended to set the RTC ++ * (based on code found in fs/fat/misc.c): ++ * ``*hhmmss = seconds + (minutes << 8) + (hour << 16)'' ++ * ``*yyyymmdd = mday + (month << 8) + (year << 16)'' ++ */ ++void ktime_to_rtc(time_t t, int *hhmmss, int *yyyymmdd) ++{ ++ /* Table to map days in year to months (non leap-years, zero-based) */ ++ static int dmt[] = { ++ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0,0,0,0 ++ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ ++ }; ++ int day, year, nl_day, month; ++ if (rtc_runs_localtime) { /* apply effective timezone */ ++ t -= sys_tz.tz_minuteswest * 60; ++ if (sys_tz.tz_dsttime) ++ t += 3600; ++ } ++ *hhmmss = (t % 60) + (((t / 60) % 60) << 8) + (((t / 3600) % 24) << 16); ++ day = t / 86400; /* days since epoch (1970) */ ++ year = day / 365; /* non-leap years since epoch */ ++ /* correct for leap years in epoch: 1972 (2) was the first */ ++ if ((year + 2) / 4 + 365 * year > day) ++ year--; ++ /* reduce to day in current year */ ++ day -= (year + 2) / 4 + 365 * year; ++ /* map March, 1st to Feburary, 29nd for leap years */ ++ if (day == 59 && ((year + 2) & 3) == 0) { ++ nl_day = day; ++ month = 2; ++ } else { /* determine month and day of month */ ++ nl_day = ((year + 2) & 3) || day < 59 ? day : day - 1; ++ for (month = 0; month < 12; month++) ++ if (dmt[month] > nl_day) ++ break; ++ } ++ *yyyymmdd = nl_day - dmt[month - 1] + 1 + (month << 8) + ((year + 1970) << 16); ++} ++ /* - * Generic way to access 'xtime' (the current time of day). - * This can be changed if the platform provides a more accurate (and fast!)
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |