162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * OR1K timer synchronisation 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Based on work from MIPS implementation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * All CPUs will have their count registers synchronised to the CPU0 next time 762306a36Sopenharmony_ci * value. This can cause a small timewarp for CPU0. All other CPU's should 862306a36Sopenharmony_ci * not have done anything significant (but they may have had interrupts 962306a36Sopenharmony_ci * enabled briefly - prom_smp_finish() should not be responsible for enabling 1062306a36Sopenharmony_ci * interrupts...) 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/irqflags.h> 1562306a36Sopenharmony_ci#include <linux/cpumask.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/time.h> 1862306a36Sopenharmony_ci#include <asm/timex.h> 1962306a36Sopenharmony_ci#include <linux/atomic.h> 2062306a36Sopenharmony_ci#include <asm/barrier.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/spr.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic unsigned int initcount; 2562306a36Sopenharmony_cistatic atomic_t count_count_start = ATOMIC_INIT(0); 2662306a36Sopenharmony_cistatic atomic_t count_count_stop = ATOMIC_INIT(0); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define COUNTON 100 2962306a36Sopenharmony_ci#define NR_LOOPS 3 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_civoid synchronise_count_master(int cpu) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci int i; 3462306a36Sopenharmony_ci unsigned long flags; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci pr_info("Synchronize counters for CPU %u: ", cpu); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci local_irq_save(flags); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* 4162306a36Sopenharmony_ci * We loop a few times to get a primed instruction cache, 4262306a36Sopenharmony_ci * then the last pass is more or less synchronised and 4362306a36Sopenharmony_ci * the master and slaves each set their cycle counters to a known 4462306a36Sopenharmony_ci * value all at once. This reduces the chance of having random offsets 4562306a36Sopenharmony_ci * between the processors, and guarantees that the maximum 4662306a36Sopenharmony_ci * delay between the cycle counters is never bigger than 4762306a36Sopenharmony_ci * the latency of information-passing (cachelines) between 4862306a36Sopenharmony_ci * two CPUs. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci for (i = 0; i < NR_LOOPS; i++) { 5262306a36Sopenharmony_ci /* slaves loop on '!= 2' */ 5362306a36Sopenharmony_ci while (atomic_read(&count_count_start) != 1) 5462306a36Sopenharmony_ci mb(); 5562306a36Sopenharmony_ci atomic_set(&count_count_stop, 0); 5662306a36Sopenharmony_ci smp_wmb(); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* Let the slave writes its count register */ 5962306a36Sopenharmony_ci atomic_inc(&count_count_start); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Count will be initialised to current timer */ 6262306a36Sopenharmony_ci if (i == 1) 6362306a36Sopenharmony_ci initcount = get_cycles(); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Everyone initialises count in the last loop: 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci if (i == NR_LOOPS-1) 6962306a36Sopenharmony_ci openrisc_timer_set(initcount); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * Wait for slave to leave the synchronization point: 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci while (atomic_read(&count_count_stop) != 1) 7562306a36Sopenharmony_ci mb(); 7662306a36Sopenharmony_ci atomic_set(&count_count_start, 0); 7762306a36Sopenharmony_ci smp_wmb(); 7862306a36Sopenharmony_ci atomic_inc(&count_count_stop); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci /* Arrange for an interrupt in a short while */ 8162306a36Sopenharmony_ci openrisc_timer_set_next(COUNTON); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci local_irq_restore(flags); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * i386 code reported the skew here, but the 8762306a36Sopenharmony_ci * count registers were almost certainly out of sync 8862306a36Sopenharmony_ci * so no point in alarming people 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci pr_cont("done.\n"); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid synchronise_count_slave(int cpu) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci int i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * Not every cpu is online at the time this gets called, 9962306a36Sopenharmony_ci * so we first wait for the master to say everyone is ready 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (i = 0; i < NR_LOOPS; i++) { 10362306a36Sopenharmony_ci atomic_inc(&count_count_start); 10462306a36Sopenharmony_ci while (atomic_read(&count_count_start) != 2) 10562306a36Sopenharmony_ci mb(); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * Everyone initialises count in the last loop: 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (i == NR_LOOPS-1) 11162306a36Sopenharmony_ci openrisc_timer_set(initcount); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci atomic_inc(&count_count_stop); 11462306a36Sopenharmony_ci while (atomic_read(&count_count_stop) != 2) 11562306a36Sopenharmony_ci mb(); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci /* Arrange for an interrupt in a short while */ 11862306a36Sopenharmony_ci openrisc_timer_set_next(COUNTON); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci#undef NR_LOOPS 121