summaryrefslogtreecommitdiffstats
path: root/sys/arm/at91/at91_rtc.c
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2012-10-07 20:36:46 +0000
committerimp <imp@FreeBSD.org>2012-10-07 20:36:46 +0000
commitcfdf328b7d456b26d4044dd7fa503e1f1a10809d (patch)
tree7bb88009639b9e4c251df0075db15d8595f3295c /sys/arm/at91/at91_rtc.c
parent182cb719bb68f018cf5cbb54891a83a9d5a9058b (diff)
downloadFreeBSD-src-cfdf328b7d456b26d4044dd7fa503e1f1a10809d.zip
FreeBSD-src-cfdf328b7d456b26d4044dd7fa503e1f1a10809d.tar.gz
Loop reading the RTC registers until the same values are obtained
twice, as advised in the atmel docs. Submitted by: Ian Lapore
Diffstat (limited to 'sys/arm/at91/at91_rtc.c')
-rw-r--r--sys/arm/at91/at91_rtc.c17
1 files changed, 14 insertions, 3 deletions
diff --git a/sys/arm/at91/at91_rtc.c b/sys/arm/at91/at91_rtc.c
index e7a63a1..6ed044a 100644
--- a/sys/arm/at91/at91_rtc.c
+++ b/sys/arm/at91/at91_rtc.c
@@ -256,7 +256,7 @@ static int
at91_rtc_gettime(device_t dev, struct timespec *ts)
{
struct clocktime ct;
- uint32_t timr, calr;
+ uint32_t calr, calr2, timr, timr2;
struct at91_rtc_softc *sc;
sc = device_get_softc(dev);
@@ -266,8 +266,19 @@ at91_rtc_gettime(device_t dev, struct timespec *ts)
if (RD4(sc, RTC_VER) & (RTC_VER_NVTIM | RTC_VER_NVCAL))
return EINVAL;
- timr = RD4(sc, RTC_TIMR);
- calr = RD4(sc, RTC_CALR);
+ /*
+ * The RTC hardware can update registers while the CPU is reading them.
+ * The manual advises reading until you obtain the same values twice.
+ * Interleaving the reads (rather than timr, timr2, calr, calr2 order)
+ * also ensures we don't miss a midnight rollover/carry between reads.
+ */
+ do {
+ timr = RD4(sc, RTC_TIMR);
+ calr = RD4(sc, RTC_CALR);
+ timr2 = RD4(sc, RTC_TIMR);
+ calr2 = RD4(sc, RTC_CALR);
+ } while (timr != timr2 || calr != calr2);
+
ct.nsec = 0;
ct.sec = RTC_TIMR_SEC(timr);
ct.min = RTC_TIMR_MIN(timr);
OpenPOWER on IntegriCloud