diff options
author | John Bowler <jbowler@nslu2-linux.org> | 2005-05-25 20:32:45 +0000 |
---|---|---|
committer | John Bowler <jbowler@nslu2-linux.org> | 2005-05-25 20:32:45 +0000 |
commit | 10f216ff3811c1d0a4345790c6642476a66470e6 (patch) | |
tree | 25b85136b67565d6ab4b874a65eb6c5b3ecf62b7 | |
parent | 8fdc4292897a207e39d84fc3eb33508a6dfccf7a (diff) |
Complete RTC clock fixes so tha the sync API is fully functional.
BKrev: 4294e0edD2vuJeMjgJ79D02-WIUu4w
-rw-r--r-- | packages/linux/openslug-kernel-2.6.11.2/x1205-rtc.c | 149 | ||||
-rw-r--r-- | packages/linux/openslug-kernel_2.6.11.2.bb | 2 |
2 files changed, 93 insertions, 58 deletions
diff --git a/packages/linux/openslug-kernel-2.6.11.2/x1205-rtc.c b/packages/linux/openslug-kernel-2.6.11.2/x1205-rtc.c index 7330037491..acac095f38 100644 --- a/packages/linux/openslug-kernel-2.6.11.2/x1205-rtc.c +++ b/packages/linux/openslug-kernel-2.6.11.2/x1205-rtc.c @@ -351,62 +351,37 @@ static int x1205_detach(struct i2c_client *client) // make sure the rtc_time values are in bounds static int x1205_validate_tm(struct rtc_time *tm, int datetoo) { - int result = NOERR; - - tm->tm_year += epoch; - - /* The RTC uses a byte containing a BCD year value, so this is - * limited to the range 0..99 from rtc_epoch. - */ - if (tm->tm_year < rtc_epoch || tm->tm_year > rtc_epoch + 99) - goto baddate; - - if ((tm->tm_mon > 11) || tm->tm_mon < 0 || (tm->tm_mday == 0)) - goto baddate; - - if (tm->tm_mday < 0) - goto baddate; - - if (tm->tm_mday > (days_in_mo[tm->tm_mon] + ( (tm->tm_mon == 1) && - ((!(tm->tm_year % 4) && (tm->tm_year % 100) ) || !(tm->tm_year % 400))))) - goto baddate; - - /* This special check shouldn't fire, it's here to detect a possible invalid - * input which seems to happen to the clock on shutdown (detach?) - */ - if (tm->tm_year == rtc_epoch && tm->tm_mon == 0 && tm->tm_mday == 0) { - printk(KERN_DEBUG "x1205_validate_tm: date is being zapped\n"); - /* FIXME: this is a hack to test the problem. */ - tm->tm_year = rtc_epoch+45-epoch; - return NOERR; - - baddate: - if (datetoo) { + if (datetoo) { + /* This used to be 1900, not epoch, but all the read APIs subtract + * epoch, not 1900, and the result of these APIs *is* fed back in + * to x1205_command (which calls this.) + */ + tm->tm_year += epoch; + + /* The RTC uses a byte containing a BCD year value, so this is + * limited to the range 0..99 from rtc_epoch. + */ + if ((tm->tm_year < rtc_epoch || tm->tm_year > rtc_epoch + 99) || + ((tm->tm_mon > 11) || tm->tm_mon < 0 || (tm->tm_mday <= 0)) || + (tm->tm_mday > (days_in_mo[tm->tm_mon] + ( (tm->tm_mon == 1) && + ((!(tm->tm_year % 4) && (tm->tm_year % 100) ) || !(tm->tm_year % 400))))) + ) { printk(KERN_DEBUG "x1205_validate_tm: invalid date:\t%04d,%02d,%02d\n", - tm->tm_year, tm->tm_mon, tm->tm_mday); + tm->tm_year, tm->tm_mon, tm->tm_mday); return -EINVAL; - } else { - /* I believe this case is the one where the kernel crashed - * because tm_mon is large and negative (uninitialised). - */ - printk(KERN_DEBUG "x1205_validate_tm: unset date:\t%04d,%02d,%02d\n", - tm->tm_year, tm->tm_mon, tm->tm_mday); } - } - - tm->tm_year -= epoch; - if ((tm->tm_hour < 0) || (tm->tm_min < 0) || (tm->tm_sec < 0)) - result = -EINVAL; - if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60)) - result = -EINVAL; + tm->tm_year -= epoch; + } - if (result == -EINVAL) { + if (((tm->tm_hour < 0) || (tm->tm_min < 0) || (tm->tm_sec < 0)) || + ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60))) { printk(KERN_DEBUG "x1205_validate_tm: invalid time:\t%02d,%02d,%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec); + return -EINVAL; } - return result; + return NOERR; } static int x1205_command(struct i2c_client *client, unsigned int cmd, void *tm) @@ -486,20 +461,80 @@ static int x1205_sync_rtc(void) * that this will tend to fire for small drifts close to UTC midnight. */ cmd = RTC_SETTIME; - div = new_s - mktime(tm.tm_year+epoch, tm.tm_mon+1, tm.tm_mday, + rem = new_s - mktime(tm.tm_year+epoch, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + if (rem != 0) { + int dif; + + /* Make an approximation to year/month/day. */ + rem = div; + div = (2*div)/61; // 30.5 days/month + tm.tm_mday = 1 + rem - (div*61)/2; + rem = div; + div /= 12; + rem -= 12*div; + while (tm.tm_mday > days_in_mo[rem]) { + tm.tm_mday -= days_in_mo[rem]; + if (++rem >= 12) { + rem -= 12; + ++div; + } + } + tm.tm_mon = rem; + div += 1970; // base of tv_sec + + /* Calculate the error in the approximation as a signed + * int value. + */ + dif = new_s - mktime(div, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - if (div != 0) { - /* Convert to days. */ - printk(KERN_DEBUG "x1205_sync_rtc exit (change tm_mday %d)\n", - div); - div /= 86400; - if (div <= 0) --div; + while (dif < 0) { + --(tm.tm_mday); + dif += 86400; + } + while (dif >= 86400) { + ++(tm.tm_mday); + dif -= 86400; + } + if (dif != 0) + printk(KERN_ERR "x1205_sync_rtc (error in date %d)\n", dif); + + /* Normalise the result. */ + while (tm.tm_mday <= 0) { + if (--(tm.tm_mon) < 0) { + tm.tm_mon += 12; + --div; + } + tm.tm_mday += days_in_mo[tm.tm_mon] + (tm.tm_mon==1 && + ((!(div % 4) && (div % 100) ) || !(div % 400))); + } + + do { + rem = days_in_mo[tm.tm_mon] + (tm.tm_mon==1 && + ((!(div % 4) && (div % 100) ) || !(div % 400))); + if (tm.tm_mday > rem) { + tm.tm_mday -= rem; + if (++(tm.tm_mon) >= 12) { + tm.tm_mon -= 12; + ++div; + } + } else { + break; + } + } while (1); + + tm.tm_year = div-epoch; cmd = RTC_SETDATETIME; - return 1; + printk(KERN_DEBUG "x1205_sync_rtc exit (change date %d)\n", new_s-old_s); + } else { + printk(KERN_DEBUG "x1205_sync_rtc exit (change seconds %d)\n", new_s-old_s); + /* But avoid the race condition when the date is about to + * change. + */ + if (tm.tm_min == 59 && tm.tm_hour == 23) + cmd = RTC_SETDATETIME; } - printk(KERN_DEBUG "x1205_sync_rtc exit (change seconds %d)\n", new_s-old_s); - return x1205_command(&x1205_i2c_client, cmd, &tm); } diff --git a/packages/linux/openslug-kernel_2.6.11.2.bb b/packages/linux/openslug-kernel_2.6.11.2.bb index 0bf4317ec4..d91149dfd9 100644 --- a/packages/linux/openslug-kernel_2.6.11.2.bb +++ b/packages/linux/openslug-kernel_2.6.11.2.bb @@ -2,7 +2,7 @@ SECTION = "kernel" DESCRIPTION = "Linux kernel for the Linksys NSLU2 device" LICENSE = "GPL" MAINTAINER = "Chris Larson <kergoth@handhelds.org>" -PR = "r8" +PR = "r9" KERNEL_SUFFIX = "openslug" |