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 )
52 local_irq_save(flags
);
59 while( !tbsync
->handshake
)
70 if( cmd
== kSetAndTest
) {
71 while( tbsync
->handshake
)
73 asm volatile ("mttbl %0" :: "r" (tbl
) );
74 asm volatile ("mttbu %0" :: "r" (tbu
) );
76 while( tbsync
->handshake
)
79 enter_contest( tbsync
->mark
, -1 );
81 local_irq_restore(flags
);
85 start_contest( int cmd
, int offset
, int num
)
87 int i
, tbu
, tbl
, mark
, score
=0;
93 tbl
= get_tbl() + 400;
94 tbsync
->tbu
= tbu
= get_tbu();
95 tbsync
->tbl
= tbl
+ offset
;
96 tbsync
->mark
= mark
= tbl
+ 400;
100 tbsync
->handshake
= 1;
104 while( (int)(get_tbl() - tbl
) <= 0 )
106 tbsync
->handshake
= 0;
107 enter_contest( mark
, 1 );
109 while( !tbsync
->ack
)
112 if( tbsync
->tbu
!= get_tbu() || ((tbsync
->tbl
^ get_tbl()) & 0x80000000) )
115 score
+= tbsync
->race_result
;
122 smp_generic_give_timebase( void )
124 int i
, score
, score2
, old
, min
=0, max
=5000, offset
=1000;
126 printk("Synchronizing timebase\n");
128 /* if this fails then this kernel won't work anyway... */
129 tbsync
= kzalloc( sizeof(*tbsync
), GFP_KERNEL
);
133 while( !tbsync
->ack
)
137 for( old
=-1 ; old
!= offset
; offset
=(min
+max
)/2 ) {
138 score
= start_contest( kSetAndTest
, offset
, NUM_ITER
);
140 printk("score %d, offset %d\n", score
, offset
);
148 score
= start_contest( kSetAndTest
, min
, NUM_ITER
);
149 score2
= start_contest( kSetAndTest
, max
, NUM_ITER
);
151 printk( "Min %d (score %d), Max %d (score %d)\n", min
, score
, max
, score2
);
152 score
= abs( score
);
153 score2
= abs( score2
);
154 offset
= (score
< score2
) ? min
: max
;
156 /* guard against inaccurate mttb */
157 for( i
=0; i
<10; i
++ ) {
158 start_contest( kSetAndTest
, offset
, NUM_ITER
/10 );
160 if( (score2
=start_contest(kTest
, offset
, NUM_ITER
)) < 0 )
162 if( score2
<= score
|| score2
< 20 )
165 printk("Final offset: %d (%d/%d)\n", offset
, score2
, NUM_ITER
);
170 tbsync
->handshake
= 1;
173 tbsync
->handshake
= 0;
179 smp_tb_synchronized
= 1;