aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2018-06-19 10:02:27 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2018-06-26 23:48:49 -0400
commit22db552b50fa11d8c1d171de908a1f9ef62172b7 (patch)
treebf879e8c8e8cff4d4a147cc371f8df85e04f27d3
parent941c06d58503b9f2718b20bc45ee7f1d701a1e1e (diff)
powerpc/powermac: Fix rtc read/write functions
As Mathieu pointed out, my conversion to time64_t was incorrect and resulted in negative times to be read from the RTC. The problem is that during the conversion from a byte array to a time64_t, the 'unsigned char' variable holding the top byte gets turned into a negative signed 32-bit integer before being assigned to the 64-bit variable for any times after 1972. This changes the logic to cast to an unsigned 32-bit number first for the Macintosh time and then convert that to the Unix time, which then gives us a time in the documented 1904..2040 year range. I decided not to use the longer 1970..2106 range that other drivers use, for consistency with the literal interpretation of the register, but that could be easily changed if we decide we want to support any Mac after 2040. Just to be on the safe side, I'm also adding a WARN_ON that will trigger if either the year 2040 has come and is observed by this driver, or we run into an RTC that got set back to a pre-1970 date for some reason (the two are indistinguishable). For the RTC write functions, Andreas found another problem: both pmu_request() and cuda_request() are varargs functions, so changing the type of the arguments passed into them from 32 bit to 64 bit breaks the API for the set_rtc_time functions. This changes it back to 32 bits. The same code exists in arch/m68k/ and is patched in an identical way now in a separate patch. Fixes: 5bfd643583b2 ("powerpc: use time64_t in read_persistent_clock") Reported-by: Mathieu Malaterre <malat@debian.org> Reported-by: Andreas Schwab <schwab@linux-m68k.org> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Tested-by: Mathieu Malaterre <malat@debian.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/platforms/powermac/time.c29
1 files changed, 20 insertions, 9 deletions
diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c
index 7c968e46736f..12e6e4d30602 100644
--- a/arch/powerpc/platforms/powermac/time.c
+++ b/arch/powerpc/platforms/powermac/time.c
@@ -42,7 +42,11 @@
42#define DBG(x...) 42#define DBG(x...)
43#endif 43#endif
44 44
45/* Apparently the RTC stores seconds since 1 Jan 1904 */ 45/*
46 * Offset between Unix time (1970-based) and Mac time (1904-based). Cuda and PMU
47 * times wrap in 2040. If we need to handle later times, the read_time functions
48 * need to be changed to interpret wrapped times as post-2040.
49 */
46#define RTC_OFFSET 2082844800 50#define RTC_OFFSET 2082844800
47 51
48/* 52/*
@@ -97,8 +101,11 @@ static time64_t cuda_get_time(void)
97 if (req.reply_len != 7) 101 if (req.reply_len != 7)
98 printk(KERN_ERR "cuda_get_time: got %d byte reply\n", 102 printk(KERN_ERR "cuda_get_time: got %d byte reply\n",
99 req.reply_len); 103 req.reply_len);
100 now = (req.reply[3] << 24) + (req.reply[4] << 16) 104 now = (u32)((req.reply[3] << 24) + (req.reply[4] << 16) +
101 + (req.reply[5] << 8) + req.reply[6]; 105 (req.reply[5] << 8) + req.reply[6]);
106 /* it's either after year 2040, or the RTC has gone backwards */
107 WARN_ON(now < RTC_OFFSET);
108
102 return now - RTC_OFFSET; 109 return now - RTC_OFFSET;
103} 110}
104 111
@@ -106,10 +113,10 @@ static time64_t cuda_get_time(void)
106 113
107static int cuda_set_rtc_time(struct rtc_time *tm) 114static int cuda_set_rtc_time(struct rtc_time *tm)
108{ 115{
109 time64_t nowtime; 116 u32 nowtime;
110 struct adb_request req; 117 struct adb_request req;
111 118
112 nowtime = rtc_tm_to_time64(tm) + RTC_OFFSET; 119 nowtime = lower_32_bits(rtc_tm_to_time64(tm) + RTC_OFFSET);
113 if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME, 120 if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,
114 nowtime >> 24, nowtime >> 16, nowtime >> 8, 121 nowtime >> 24, nowtime >> 16, nowtime >> 8,
115 nowtime) < 0) 122 nowtime) < 0)
@@ -140,8 +147,12 @@ static time64_t pmu_get_time(void)
140 if (req.reply_len != 4) 147 if (req.reply_len != 4)
141 printk(KERN_ERR "pmu_get_time: got %d byte reply from PMU\n", 148 printk(KERN_ERR "pmu_get_time: got %d byte reply from PMU\n",
142 req.reply_len); 149 req.reply_len);
143 now = (req.reply[0] << 24) + (req.reply[1] << 16) 150 now = (u32)((req.reply[0] << 24) + (req.reply[1] << 16) +
144 + (req.reply[2] << 8) + req.reply[3]; 151 (req.reply[2] << 8) + req.reply[3]);
152
153 /* it's either after year 2040, or the RTC has gone backwards */
154 WARN_ON(now < RTC_OFFSET);
155
145 return now - RTC_OFFSET; 156 return now - RTC_OFFSET;
146} 157}
147 158
@@ -149,10 +160,10 @@ static time64_t pmu_get_time(void)
149 160
150static int pmu_set_rtc_time(struct rtc_time *tm) 161static int pmu_set_rtc_time(struct rtc_time *tm)
151{ 162{
152 time64_t nowtime; 163 u32 nowtime;
153 struct adb_request req; 164 struct adb_request req;
154 165
155 nowtime = rtc_tm_to_time64(tm) + RTC_OFFSET; 166 nowtime = lower_32_bits(rtc_tm_to_time64(tm) + RTC_OFFSET);
156 if (pmu_request(&req, NULL, 5, PMU_SET_RTC, nowtime >> 24, 167 if (pmu_request(&req, NULL, 5, PMU_SET_RTC, nowtime >> 24,
157 nowtime >> 16, nowtime >> 8, nowtime) < 0) 168 nowtime >> 16, nowtime >> 8, nowtime) < 0)
158 return -ENXIO; 169 return -ENXIO;