2 * Smp timebase synchronization for ppc.
4 * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
8 #include <linux/kernel.h>
9 #include <linux/sched.h>
10 #include <linux/smp.h>
11 #include <linux/unistd.h>
12 #include <linux/init.h>
13 #include <asm/atomic.h>
20 kExit=0, kSetAndTest, kTest
28 volatile int handshake;
34 volatile int race_result;
37 static volatile int running;
40 enter_contest( int mark, int add )
42 while( (int)(get_tbl() - mark) < 0 )
43 tbsync->race_result = add;
47 smp_generic_take_timebase( void )
58 while( !tbsync->handshake )
69 if( cmd == kSetAndTest ) {
70 while( tbsync->handshake )
72 asm volatile ("mttbl %0" :: "r" (tbl) );
73 asm volatile ("mttbu %0" :: "r" (tbu) );
75 while( tbsync->handshake )
78 enter_contest( tbsync->mark, -1 );
84 start_contest( int cmd, int offset, int num )
86 int i, tbu, tbl, mark, score=0;
92 tbl = get_tbl() + 400;
93 tbsync->tbu = tbu = get_tbu();
94 tbsync->tbl = tbl + offset;
95 tbsync->mark = mark = tbl + 400;
99 tbsync->handshake = 1;
103 while( (int)(get_tbl() - tbl) <= 0 )
105 tbsync->handshake = 0;
106 enter_contest( mark, 1 );
108 while( !tbsync->ack )
111 if( tbsync->tbu != get_tbu() || ((tbsync->tbl ^ get_tbl()) & 0x80000000) )
114 score += tbsync->race_result;
121 smp_generic_give_timebase( void )
123 int i, score, score2, old, min=0, max=5000, offset=1000;
125 printk("Synchronizing timebase\n");
127 /* if this fails then this kernel won't work anyway... */
128 tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL );
132 while( !tbsync->ack )
136 for( old=-1 ; old != offset ; offset=(min+max)/2 ) {
137 score = start_contest( kSetAndTest, offset, NUM_ITER );
139 printk("score %d, offset %d\n", score, offset );
147 score = start_contest( kSetAndTest, min, NUM_ITER );
148 score2 = start_contest( kSetAndTest, max, NUM_ITER );
150 printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 );
151 score = abs( score );
152 score2 = abs( score2 );
153 offset = (score < score2) ? min : max;
155 /* guard against inaccurate mttb */
156 for( i=0; i<10; i++ ) {
157 start_contest( kSetAndTest, offset, NUM_ITER/10 );
159 if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 )
161 if( score2 <= score || score2 < 20 )
164 printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
169 tbsync->handshake = 1;
172 tbsync->handshake = 0;
178 smp_tb_synchronized = 1;