diff options
Diffstat (limited to 'arch/powerpc/kernel/time.c')
-rw-r--r-- | arch/powerpc/kernel/time.c | 65 |
1 files changed, 41 insertions, 24 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 2c8564d54e4d..e5df167f7824 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c | |||
@@ -77,9 +77,8 @@ | |||
77 | /* keep track of when we need to update the rtc */ | 77 | /* keep track of when we need to update the rtc */ |
78 | time_t last_rtc_update; | 78 | time_t last_rtc_update; |
79 | #ifdef CONFIG_PPC_ISERIES | 79 | #ifdef CONFIG_PPC_ISERIES |
80 | unsigned long iSeries_recal_titan = 0; | 80 | static unsigned long __initdata iSeries_recal_titan; |
81 | unsigned long iSeries_recal_tb = 0; | 81 | static signed long __initdata iSeries_recal_tb; |
82 | static unsigned long first_settimeofday = 1; | ||
83 | #endif | 82 | #endif |
84 | 83 | ||
85 | /* The decrementer counts down by 128 every 128ns on a 601. */ | 84 | /* The decrementer counts down by 128 every 128ns on a 601. */ |
@@ -113,8 +112,9 @@ u64 ticklen_to_xs; /* 0.64 fraction */ | |||
113 | DEFINE_SPINLOCK(rtc_lock); | 112 | DEFINE_SPINLOCK(rtc_lock); |
114 | EXPORT_SYMBOL_GPL(rtc_lock); | 113 | EXPORT_SYMBOL_GPL(rtc_lock); |
115 | 114 | ||
116 | u64 tb_to_ns_scale; | 115 | static u64 tb_to_ns_scale __read_mostly; |
117 | unsigned tb_to_ns_shift; | 116 | static unsigned tb_to_ns_shift __read_mostly; |
117 | static unsigned long boot_tb __read_mostly; | ||
118 | 118 | ||
119 | struct gettimeofday_struct do_gtod; | 119 | struct gettimeofday_struct do_gtod; |
120 | 120 | ||
@@ -214,7 +214,6 @@ static void account_process_time(struct pt_regs *regs) | |||
214 | run_posix_cpu_timers(current); | 214 | run_posix_cpu_timers(current); |
215 | } | 215 | } |
216 | 216 | ||
217 | #ifdef CONFIG_PPC_SPLPAR | ||
218 | /* | 217 | /* |
219 | * Stuff for accounting stolen time. | 218 | * Stuff for accounting stolen time. |
220 | */ | 219 | */ |
@@ -222,19 +221,28 @@ struct cpu_purr_data { | |||
222 | int initialized; /* thread is running */ | 221 | int initialized; /* thread is running */ |
223 | u64 tb; /* last TB value read */ | 222 | u64 tb; /* last TB value read */ |
224 | u64 purr; /* last PURR value read */ | 223 | u64 purr; /* last PURR value read */ |
225 | spinlock_t lock; | ||
226 | }; | 224 | }; |
227 | 225 | ||
226 | /* | ||
227 | * Each entry in the cpu_purr_data array is manipulated only by its | ||
228 | * "owner" cpu -- usually in the timer interrupt but also occasionally | ||
229 | * in process context for cpu online. As long as cpus do not touch | ||
230 | * each others' cpu_purr_data, disabling local interrupts is | ||
231 | * sufficient to serialize accesses. | ||
232 | */ | ||
228 | static DEFINE_PER_CPU(struct cpu_purr_data, cpu_purr_data); | 233 | static DEFINE_PER_CPU(struct cpu_purr_data, cpu_purr_data); |
229 | 234 | ||
230 | static void snapshot_tb_and_purr(void *data) | 235 | static void snapshot_tb_and_purr(void *data) |
231 | { | 236 | { |
237 | unsigned long flags; | ||
232 | struct cpu_purr_data *p = &__get_cpu_var(cpu_purr_data); | 238 | struct cpu_purr_data *p = &__get_cpu_var(cpu_purr_data); |
233 | 239 | ||
240 | local_irq_save(flags); | ||
234 | p->tb = mftb(); | 241 | p->tb = mftb(); |
235 | p->purr = mfspr(SPRN_PURR); | 242 | p->purr = mfspr(SPRN_PURR); |
236 | wmb(); | 243 | wmb(); |
237 | p->initialized = 1; | 244 | p->initialized = 1; |
245 | local_irq_restore(flags); | ||
238 | } | 246 | } |
239 | 247 | ||
240 | /* | 248 | /* |
@@ -242,15 +250,14 @@ static void snapshot_tb_and_purr(void *data) | |||
242 | */ | 250 | */ |
243 | void snapshot_timebases(void) | 251 | void snapshot_timebases(void) |
244 | { | 252 | { |
245 | int cpu; | ||
246 | |||
247 | if (!cpu_has_feature(CPU_FTR_PURR)) | 253 | if (!cpu_has_feature(CPU_FTR_PURR)) |
248 | return; | 254 | return; |
249 | for_each_possible_cpu(cpu) | ||
250 | spin_lock_init(&per_cpu(cpu_purr_data, cpu).lock); | ||
251 | on_each_cpu(snapshot_tb_and_purr, NULL, 0, 1); | 255 | on_each_cpu(snapshot_tb_and_purr, NULL, 0, 1); |
252 | } | 256 | } |
253 | 257 | ||
258 | /* | ||
259 | * Must be called with interrupts disabled. | ||
260 | */ | ||
254 | void calculate_steal_time(void) | 261 | void calculate_steal_time(void) |
255 | { | 262 | { |
256 | u64 tb, purr; | 263 | u64 tb, purr; |
@@ -262,7 +269,6 @@ void calculate_steal_time(void) | |||
262 | pme = &per_cpu(cpu_purr_data, smp_processor_id()); | 269 | pme = &per_cpu(cpu_purr_data, smp_processor_id()); |
263 | if (!pme->initialized) | 270 | if (!pme->initialized) |
264 | return; /* this can happen in early boot */ | 271 | return; /* this can happen in early boot */ |
265 | spin_lock(&pme->lock); | ||
266 | tb = mftb(); | 272 | tb = mftb(); |
267 | purr = mfspr(SPRN_PURR); | 273 | purr = mfspr(SPRN_PURR); |
268 | stolen = (tb - pme->tb) - (purr - pme->purr); | 274 | stolen = (tb - pme->tb) - (purr - pme->purr); |
@@ -270,9 +276,9 @@ void calculate_steal_time(void) | |||
270 | account_steal_time(current, stolen); | 276 | account_steal_time(current, stolen); |
271 | pme->tb = tb; | 277 | pme->tb = tb; |
272 | pme->purr = purr; | 278 | pme->purr = purr; |
273 | spin_unlock(&pme->lock); | ||
274 | } | 279 | } |
275 | 280 | ||
281 | #ifdef CONFIG_PPC_SPLPAR | ||
276 | /* | 282 | /* |
277 | * Must be called before the cpu is added to the online map when | 283 | * Must be called before the cpu is added to the online map when |
278 | * a cpu is being brought up at runtime. | 284 | * a cpu is being brought up at runtime. |
@@ -284,12 +290,12 @@ static void snapshot_purr(void) | |||
284 | 290 | ||
285 | if (!cpu_has_feature(CPU_FTR_PURR)) | 291 | if (!cpu_has_feature(CPU_FTR_PURR)) |
286 | return; | 292 | return; |
293 | local_irq_save(flags); | ||
287 | pme = &per_cpu(cpu_purr_data, smp_processor_id()); | 294 | pme = &per_cpu(cpu_purr_data, smp_processor_id()); |
288 | spin_lock_irqsave(&pme->lock, flags); | ||
289 | pme->tb = mftb(); | 295 | pme->tb = mftb(); |
290 | pme->purr = mfspr(SPRN_PURR); | 296 | pme->purr = mfspr(SPRN_PURR); |
291 | pme->initialized = 1; | 297 | pme->initialized = 1; |
292 | spin_unlock_irqrestore(&pme->lock, flags); | 298 | local_irq_restore(flags); |
293 | } | 299 | } |
294 | 300 | ||
295 | #endif /* CONFIG_PPC_SPLPAR */ | 301 | #endif /* CONFIG_PPC_SPLPAR */ |
@@ -550,10 +556,15 @@ EXPORT_SYMBOL(profile_pc); | |||
550 | * returned by the service processor for the timebase frequency. | 556 | * returned by the service processor for the timebase frequency. |
551 | */ | 557 | */ |
552 | 558 | ||
553 | static void iSeries_tb_recal(void) | 559 | static int __init iSeries_tb_recal(void) |
554 | { | 560 | { |
555 | struct div_result divres; | 561 | struct div_result divres; |
556 | unsigned long titan, tb; | 562 | unsigned long titan, tb; |
563 | |||
564 | /* Make sure we only run on iSeries */ | ||
565 | if (!firmware_has_feature(FW_FEATURE_ISERIES)) | ||
566 | return -ENODEV; | ||
567 | |||
557 | tb = get_tb(); | 568 | tb = get_tb(); |
558 | titan = HvCallXm_loadTod(); | 569 | titan = HvCallXm_loadTod(); |
559 | if ( iSeries_recal_titan ) { | 570 | if ( iSeries_recal_titan ) { |
@@ -594,8 +605,18 @@ static void iSeries_tb_recal(void) | |||
594 | } | 605 | } |
595 | iSeries_recal_titan = titan; | 606 | iSeries_recal_titan = titan; |
596 | iSeries_recal_tb = tb; | 607 | iSeries_recal_tb = tb; |
608 | |||
609 | return 0; | ||
597 | } | 610 | } |
598 | #endif | 611 | late_initcall(iSeries_tb_recal); |
612 | |||
613 | /* Called from platform early init */ | ||
614 | void __init iSeries_time_init_early(void) | ||
615 | { | ||
616 | iSeries_recal_tb = get_tb(); | ||
617 | iSeries_recal_titan = HvCallXm_loadTod(); | ||
618 | } | ||
619 | #endif /* CONFIG_PPC_ISERIES */ | ||
599 | 620 | ||
600 | /* | 621 | /* |
601 | * For iSeries shared processors, we have to let the hypervisor | 622 | * For iSeries shared processors, we have to let the hypervisor |
@@ -735,7 +756,7 @@ unsigned long long sched_clock(void) | |||
735 | { | 756 | { |
736 | if (__USE_RTC()) | 757 | if (__USE_RTC()) |
737 | return get_rtc(); | 758 | return get_rtc(); |
738 | return mulhdu(get_tb(), tb_to_ns_scale) << tb_to_ns_shift; | 759 | return mulhdu(get_tb() - boot_tb, tb_to_ns_scale) << tb_to_ns_shift; |
739 | } | 760 | } |
740 | 761 | ||
741 | int do_settimeofday(struct timespec *tv) | 762 | int do_settimeofday(struct timespec *tv) |
@@ -759,12 +780,6 @@ int do_settimeofday(struct timespec *tv) | |||
759 | * to the RTC again, or write to the RTC but then they don't call | 780 | * to the RTC again, or write to the RTC but then they don't call |
760 | * settimeofday to perform this operation. | 781 | * settimeofday to perform this operation. |
761 | */ | 782 | */ |
762 | #ifdef CONFIG_PPC_ISERIES | ||
763 | if (firmware_has_feature(FW_FEATURE_ISERIES) && first_settimeofday) { | ||
764 | iSeries_tb_recal(); | ||
765 | first_settimeofday = 0; | ||
766 | } | ||
767 | #endif | ||
768 | 783 | ||
769 | /* Make userspace gettimeofday spin until we're done. */ | 784 | /* Make userspace gettimeofday spin until we're done. */ |
770 | ++vdso_data->tb_update_count; | 785 | ++vdso_data->tb_update_count; |
@@ -960,6 +975,8 @@ void __init time_init(void) | |||
960 | } | 975 | } |
961 | tb_to_ns_scale = scale; | 976 | tb_to_ns_scale = scale; |
962 | tb_to_ns_shift = shift; | 977 | tb_to_ns_shift = shift; |
978 | /* Save the current timebase to pretty up CONFIG_PRINTK_TIME */ | ||
979 | boot_tb = get_tb(); | ||
963 | 980 | ||
964 | tm = get_boot_time(); | 981 | tm = get_boot_time(); |
965 | 982 | ||