Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

m68k: Fix off-by-one calendar month

This fixes a bug in read_persistent_clock() which causes the system
clock to lag the Real Time Clock by one month. The problem was noticed
on a Mac, but theoretically it must also affect Atari, BVME6000 and Q40.

The tm_mon value in the struct rtc_time passed to mach_hwclk() is
zero-based, and atari_mste_hwclk(), atari_tt_hwclk(), bvme6000_hwclk(),
mac_hwclk() and q40_hwclk() all make this adjustment. Unfortunately,
dn_dummy_hwclk(), mvme147_hwclk(), mvme16x_hwclk(), sun3_hwclk() and
sun3x_hwclk() fail to decrement tm_mon. Also m68328_hwclk() assumes
a one-based tm_mon.

Bring these platforms into line and fix read_persistent_clock() so it
works correctly on all m68k platforms.

The datasheets for the RTC devices found on the affected platforms
all confirm that the year is stored as a value in the range 0-99 and
the month is stored as a value in the range 1-12. Please refer to the
datasheets for MC146818 (Apollo), DS1643 (MVME), ICM7170 (Sun 3)
and M48T02 (Sun 3x).

Reported-by: Stan Johnson <userm57@yahoo.com>
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>

authored by

Finn Thain and committed by
Geert Uytterhoeven
b65769fc 4eee57d6

+31 -19
+3 -1
arch/m68k/68000/timers.c
··· 125 125 { 126 126 if (!set) { 127 127 long now = RTCTIME; 128 - t->tm_year = t->tm_mon = t->tm_mday = 1; 128 + t->tm_year = 1; 129 + t->tm_mon = 0; 130 + t->tm_mday = 1; 129 131 t->tm_hour = (now >> 24) % 24; 130 132 t->tm_min = (now >> 16) % 60; 131 133 t->tm_sec = now % 60;
+5 -3
arch/m68k/apollo/config.c
··· 221 221 t->tm_hour=rtc->hours; 222 222 t->tm_mday=rtc->day_of_month; 223 223 t->tm_wday=rtc->day_of_week; 224 - t->tm_mon=rtc->month; 224 + t->tm_mon = rtc->month - 1; 225 225 t->tm_year=rtc->year; 226 + if (t->tm_year < 70) 227 + t->tm_year += 100; 226 228 } else { 227 229 rtc->second=t->tm_sec; 228 230 rtc->minute=t->tm_min; ··· 232 230 rtc->day_of_month=t->tm_mday; 233 231 if(t->tm_wday!=-1) 234 232 rtc->day_of_week=t->tm_wday; 235 - rtc->month=t->tm_mon; 236 - rtc->year=t->tm_year; 233 + rtc->month = t->tm_mon + 1; 234 + rtc->year = t->tm_year % 100; 237 235 } 238 236 239 237 return 0;
+7 -7
arch/m68k/kernel/time.c
··· 74 74 void read_persistent_clock(struct timespec *ts) 75 75 { 76 76 struct rtc_time time; 77 + 77 78 ts->tv_sec = 0; 78 79 ts->tv_nsec = 0; 79 80 80 - if (mach_hwclk) { 81 - mach_hwclk(0, &time); 81 + if (!mach_hwclk) 82 + return; 82 83 83 - if ((time.tm_year += 1900) < 1970) 84 - time.tm_year += 100; 85 - ts->tv_sec = mktime(time.tm_year, time.tm_mon, time.tm_mday, 86 - time.tm_hour, time.tm_min, time.tm_sec); 87 - } 84 + mach_hwclk(0, &time); 85 + 86 + ts->tv_sec = mktime(time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, 87 + time.tm_hour, time.tm_min, time.tm_sec); 88 88 } 89 89 90 90 #if defined(CONFIG_ARCH_USES_GETTIMEOFFSET) && IS_ENABLED(CONFIG_RTC_DRV_GENERIC)
+3 -1
arch/m68k/mvme147/config.c
··· 153 153 if (!op) { 154 154 m147_rtc->ctrl = RTC_READ; 155 155 t->tm_year = bcd2int (m147_rtc->bcd_year); 156 - t->tm_mon = bcd2int (m147_rtc->bcd_mth); 156 + t->tm_mon = bcd2int(m147_rtc->bcd_mth) - 1; 157 157 t->tm_mday = bcd2int (m147_rtc->bcd_dom); 158 158 t->tm_hour = bcd2int (m147_rtc->bcd_hr); 159 159 t->tm_min = bcd2int (m147_rtc->bcd_min); 160 160 t->tm_sec = bcd2int (m147_rtc->bcd_sec); 161 161 m147_rtc->ctrl = 0; 162 + if (t->tm_year < 70) 163 + t->tm_year += 100; 162 164 } 163 165 return 0; 164 166 }
+3 -1
arch/m68k/mvme16x/config.c
··· 400 400 if (!op) { 401 401 rtc->ctrl = RTC_READ; 402 402 t->tm_year = bcd2int (rtc->bcd_year); 403 - t->tm_mon = bcd2int (rtc->bcd_mth); 403 + t->tm_mon = bcd2int(rtc->bcd_mth) - 1; 404 404 t->tm_mday = bcd2int (rtc->bcd_dom); 405 405 t->tm_hour = bcd2int (rtc->bcd_hr); 406 406 t->tm_min = bcd2int (rtc->bcd_min); 407 407 t->tm_sec = bcd2int (rtc->bcd_sec); 408 408 rtc->ctrl = 0; 409 + if (t->tm_year < 70) 410 + t->tm_year += 100; 409 411 } 410 412 return 0; 411 413 }
+5 -3
arch/m68k/sun3/intersil.c
··· 48 48 todintersil->hour = t->tm_hour; 49 49 todintersil->minute = t->tm_min; 50 50 todintersil->second = t->tm_sec; 51 - todintersil->month = t->tm_mon; 51 + todintersil->month = t->tm_mon + 1; 52 52 todintersil->day = t->tm_mday; 53 - todintersil->year = t->tm_year - 68; 53 + todintersil->year = (t->tm_year - 68) % 100; 54 54 todintersil->weekday = t->tm_wday; 55 55 } else { 56 56 /* read clock */ ··· 58 58 t->tm_hour = todintersil->hour; 59 59 t->tm_min = todintersil->minute; 60 60 t->tm_sec = todintersil->second; 61 - t->tm_mon = todintersil->month; 61 + t->tm_mon = todintersil->month - 1; 62 62 t->tm_mday = todintersil->day; 63 63 t->tm_year = todintersil->year + 68; 64 64 t->tm_wday = todintersil->weekday; 65 + if (t->tm_year < 70) 66 + t->tm_year += 100; 65 67 } 66 68 67 69 intersil_clock->cmd_reg = START_VAL;
+5 -3
arch/m68k/sun3x/time.c
··· 52 52 h->hour = bin2bcd(t->tm_hour); 53 53 h->wday = bin2bcd(t->tm_wday); 54 54 h->mday = bin2bcd(t->tm_mday); 55 - h->month = bin2bcd(t->tm_mon); 56 - h->year = bin2bcd(t->tm_year); 55 + h->month = bin2bcd(t->tm_mon + 1); 56 + h->year = bin2bcd(t->tm_year % 100); 57 57 h->csr &= ~C_WRITE; 58 58 } else { 59 59 h->csr |= C_READ; ··· 62 62 t->tm_hour = bcd2bin(h->hour); 63 63 t->tm_wday = bcd2bin(h->wday); 64 64 t->tm_mday = bcd2bin(h->mday); 65 - t->tm_mon = bcd2bin(h->month); 65 + t->tm_mon = bcd2bin(h->month) - 1; 66 66 t->tm_year = bcd2bin(h->year); 67 67 h->csr &= ~C_READ; 68 + if (t->tm_year < 70) 69 + t->tm_year += 100; 68 70 } 69 71 70 72 local_irq_restore(flags);