From f6d12eefcda2e4135aa529ab8b5bf2766b4a78dd Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 23 Oct 2013 23:29:16 +0200 Subject: parisc: make udelay() SMP-safe Each CPU has it's own Control Register 16 (CR16) which is used as time source for the udelay() function. But since the CR16 registers across different CPUs are not synced, we need to recalculate the loop count if we get switched away to ensure that we really delay as much time as requested. Signed-off-by: Helge Deller --- arch/parisc/lib/delay.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 arch/parisc/lib/delay.c (limited to 'arch/parisc/lib/delay.c') diff --git a/arch/parisc/lib/delay.c b/arch/parisc/lib/delay.c new file mode 100644 index 000000000000..ec9255f27a81 --- /dev/null +++ b/arch/parisc/lib/delay.c @@ -0,0 +1,73 @@ +/* + * Precise Delay Loops for parisc + * + * based on code by: + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares + * Copyright (C) 2008 Jiri Hladky + * + * parisc implementation: + * Copyright (C) 2013 Helge Deller + */ + + +#include +#include +#include + +#include +#include + +#include /* for mfctl() */ +#include /* for boot_cpu_data */ + +/* CR16 based delay: */ +static void __cr16_delay(unsigned long __loops) +{ + /* + * Note: Due to unsigned math, cr16 rollovers shouldn't be + * a problem here. However, on 32 bit, we need to make sure + * we don't pass in too big a value. The current default + * value of MAX_UDELAY_MS should help prevent this. + */ + u32 bclock, now, loops = __loops; + int cpu; + + preempt_disable(); + cpu = smp_processor_id(); + bclock = mfctl(16); + for (;;) { + now = mfctl(16); + if ((now - bclock) >= loops) + break; + + /* Allow RT tasks to run */ + preempt_enable(); + asm volatile(" nop\n"); + barrier(); + preempt_disable(); + + /* + * It is possible that we moved to another CPU, and + * since CR16's are per-cpu we need to calculate + * that. The delay must guarantee that we wait "at + * least" the amount of time. Being moved to another + * CPU could make the wait longer but we just need to + * make sure we waited long enough. Rebalance the + * counter for this CPU. + */ + if (unlikely(cpu != smp_processor_id())) { + loops -= (now - bclock); + cpu = smp_processor_id(); + bclock = mfctl(16); + } + } + preempt_enable(); +} + + +void __udelay(unsigned long usecs) +{ + __cr16_delay(usecs * ((unsigned long)boot_cpu_data.cpu_hz / 1000000UL)); +} +EXPORT_SYMBOL(__udelay); -- cgit v1.2.3-59-g8ed1b