aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel/time.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/kernel/time.c')
-rw-r--r--arch/powerpc/kernel/time.c48
1 files changed, 34 insertions, 14 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 2a7ddc579379..86f7e3d154d8 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -283,9 +283,9 @@ static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec,
283 * the two values of tb_update_count match and are even then the 283 * the two values of tb_update_count match and are even then the
284 * tb_to_xs and stamp_xsec values are consistent. If not, then it 284 * tb_to_xs and stamp_xsec values are consistent. If not, then it
285 * loops back and reads them again until this criteria is met. 285 * loops back and reads them again until this criteria is met.
286 * We expect the caller to have done the first increment of
287 * vdso_data->tb_update_count already.
286 */ 288 */
287 ++(vdso_data->tb_update_count);
288 smp_wmb();
289 vdso_data->tb_orig_stamp = new_tb_stamp; 289 vdso_data->tb_orig_stamp = new_tb_stamp;
290 vdso_data->stamp_xsec = new_stamp_xsec; 290 vdso_data->stamp_xsec = new_stamp_xsec;
291 vdso_data->tb_to_xs = new_tb_to_xs; 291 vdso_data->tb_to_xs = new_tb_to_xs;
@@ -310,20 +310,15 @@ static __inline__ void timer_recalc_offset(u64 cur_tb)
310 unsigned long offset; 310 unsigned long offset;
311 u64 new_stamp_xsec; 311 u64 new_stamp_xsec;
312 u64 tlen, t2x; 312 u64 tlen, t2x;
313 u64 tb, xsec_old, xsec_new;
314 struct gettimeofday_vars *varp;
313 315
314 if (__USE_RTC()) 316 if (__USE_RTC())
315 return; 317 return;
316 tlen = current_tick_length(); 318 tlen = current_tick_length();
317 offset = cur_tb - do_gtod.varp->tb_orig_stamp; 319 offset = cur_tb - do_gtod.varp->tb_orig_stamp;
318 if (tlen == last_tick_len && offset < 0x80000000u) { 320 if (tlen == last_tick_len && offset < 0x80000000u)
319 /* check that we're still in sync; if not, resync */ 321 return;
320 struct timeval tv;
321 __do_gettimeofday(&tv, cur_tb);
322 if (tv.tv_sec <= xtime.tv_sec &&
323 (tv.tv_sec < xtime.tv_sec ||
324 tv.tv_usec * 1000 <= xtime.tv_nsec))
325 return;
326 }
327 if (tlen != last_tick_len) { 322 if (tlen != last_tick_len) {
328 t2x = mulhdu(tlen << TICKLEN_SHIFT, ticklen_to_xs); 323 t2x = mulhdu(tlen << TICKLEN_SHIFT, ticklen_to_xs);
329 last_tick_len = tlen; 324 last_tick_len = tlen;
@@ -332,6 +327,21 @@ static __inline__ void timer_recalc_offset(u64 cur_tb)
332 new_stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC; 327 new_stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC;
333 do_div(new_stamp_xsec, 1000000000); 328 do_div(new_stamp_xsec, 1000000000);
334 new_stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC; 329 new_stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC;
330
331 ++vdso_data->tb_update_count;
332 smp_mb();
333
334 /*
335 * Make sure time doesn't go backwards for userspace gettimeofday.
336 */
337 tb = get_tb();
338 varp = do_gtod.varp;
339 xsec_old = mulhdu(tb - varp->tb_orig_stamp, varp->tb_to_xs)
340 + varp->stamp_xsec;
341 xsec_new = mulhdu(tb - cur_tb, t2x) + new_stamp_xsec;
342 if (xsec_new < xsec_old)
343 new_stamp_xsec += xsec_old - xsec_new;
344
335 update_gtod(cur_tb, new_stamp_xsec, t2x); 345 update_gtod(cur_tb, new_stamp_xsec, t2x);
336} 346}
337 347
@@ -564,6 +574,10 @@ int do_settimeofday(struct timespec *tv)
564 } 574 }
565#endif 575#endif
566 576
577 /* Make userspace gettimeofday spin until we're done. */
578 ++vdso_data->tb_update_count;
579 smp_mb();
580
567 /* 581 /*
568 * Subtract off the number of nanoseconds since the 582 * Subtract off the number of nanoseconds since the
569 * beginning of the last tick. 583 * beginning of the last tick.
@@ -724,10 +738,16 @@ void __init time_init(void)
724 * It is computed as: 738 * It is computed as:
725 * ticklen_to_xs = 2^N / (tb_ticks_per_jiffy * 1e9) 739 * ticklen_to_xs = 2^N / (tb_ticks_per_jiffy * 1e9)
726 * where N = 64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT 740 * where N = 64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT
727 * so as to give the result as a 0.64 fixed-point fraction. 741 * which turns out to be N = 51 - SHIFT_HZ.
742 * This gives the result as a 0.64 fixed-point fraction.
743 * That value is reduced by an offset amounting to 1 xsec per
744 * 2^31 timebase ticks to avoid problems with time going backwards
745 * by 1 xsec when we do timer_recalc_offset due to losing the
746 * fractional xsec. That offset is equal to ppc_tb_freq/2^51
747 * since there are 2^20 xsec in a second.
728 */ 748 */
729 div128_by_32(1ULL << (64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT), 0, 749 div128_by_32((1ULL << 51) - ppc_tb_freq, 0,
730 tb_ticks_per_jiffy, &res); 750 tb_ticks_per_jiffy << SHIFT_HZ, &res);
731 div128_by_32(res.result_high, res.result_low, NSEC_PER_SEC, &res); 751 div128_by_32(res.result_high, res.result_low, NSEC_PER_SEC, &res);
732 ticklen_to_xs = res.result_low; 752 ticklen_to_xs = res.result_low;
733 753