Linux 2.6.13-rc3
[linux-2.6/next.git] / arch / ppc / kernel / smp-tbsync.c
blob2c9cd95bcea6d9eb3cecd613826763c8ec6c5d36
1 /*
2 * Smp timebase synchronization for ppc.
4 * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
6 */
8 #include <linux/config.h>
9 #include <linux/kernel.h>
10 #include <linux/sched.h>
11 #include <linux/smp.h>
12 #include <linux/unistd.h>
13 #include <linux/init.h>
14 #include <asm/atomic.h>
15 #include <asm/smp.h>
16 #include <asm/time.h>
18 #define NUM_ITER 300
20 enum {
21 kExit=0, kSetAndTest, kTest
24 static struct {
25 volatile int tbu;
26 volatile int tbl;
27 volatile int mark;
28 volatile int cmd;
29 volatile int handshake;
30 int filler[3];
32 volatile int ack;
33 int filler2[7];
35 volatile int race_result;
36 } *tbsync;
38 static volatile int running;
40 static void __devinit
41 enter_contest( int mark, int add )
43 while( (int)(get_tbl() - mark) < 0 )
44 tbsync->race_result = add;
47 void __devinit
48 smp_generic_take_timebase( void )
50 int cmd, tbl, tbu;
52 local_irq_disable();
53 while( !running )
55 rmb();
57 for( ;; ) {
58 tbsync->ack = 1;
59 while( !tbsync->handshake )
61 rmb();
63 cmd = tbsync->cmd;
64 tbl = tbsync->tbl;
65 tbu = tbsync->tbu;
66 tbsync->ack = 0;
67 if( cmd == kExit )
68 return;
70 if( cmd == kSetAndTest ) {
71 while( tbsync->handshake )
73 asm volatile ("mttbl %0" :: "r" (tbl) );
74 asm volatile ("mttbu %0" :: "r" (tbu) );
75 } else {
76 while( tbsync->handshake )
79 enter_contest( tbsync->mark, -1 );
81 local_irq_enable();
84 static int __devinit
85 start_contest( int cmd, int offset, int num )
87 int i, tbu, tbl, mark, score=0;
89 tbsync->cmd = cmd;
91 local_irq_disable();
92 for( i=-3; i<num; ) {
93 tbl = get_tbl() + 400;
94 tbsync->tbu = tbu = get_tbu();
95 tbsync->tbl = tbl + offset;
96 tbsync->mark = mark = tbl + 400;
98 wmb();
100 tbsync->handshake = 1;
101 while( tbsync->ack )
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) )
113 continue;
114 if( i++ > 0 )
115 score += tbsync->race_result;
117 local_irq_enable();
118 return score;
121 void __devinit
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 = kmalloc( sizeof(*tbsync), GFP_KERNEL );
130 memset( tbsync, 0, sizeof(*tbsync) );
131 mb();
132 running = 1;
134 while( !tbsync->ack )
137 /* binary search */
138 for( old=-1 ; old != offset ; offset=(min+max)/2 ) {
139 score = start_contest( kSetAndTest, offset, NUM_ITER );
141 printk("score %d, offset %d\n", score, offset );
143 if( score > 0 )
144 max = offset;
145 else
146 min = offset;
147 old = offset;
149 score = start_contest( kSetAndTest, min, NUM_ITER );
150 score2 = start_contest( kSetAndTest, max, NUM_ITER );
152 printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 );
153 score = abs( score );
154 score2 = abs( score2 );
155 offset = (score < score2) ? min : max;
157 /* guard against inaccurate mttb */
158 for( i=0; i<10; i++ ) {
159 start_contest( kSetAndTest, offset, NUM_ITER/10 );
161 if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 )
162 score2 = -score2;
163 if( score2 <= score || score2 < 20 )
164 break;
166 printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
168 /* exiting */
169 tbsync->cmd = kExit;
170 wmb();
171 tbsync->handshake = 1;
172 while( tbsync->ack )
174 tbsync->handshake = 0;
175 kfree( tbsync );
176 tbsync = NULL;
177 running = 0;
179 /* all done */
180 smp_tb_synchronized = 1;