Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[deliverable/linux.git] / arch / ppc / kernel / smp-tbsync.c
CommitLineData
1da177e4
LT
1/*
2 * Smp timebase synchronization for ppc.
3 *
4 * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
5 *
6 */
7
1da177e4
LT
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>
14#include <asm/smp.h>
15#include <asm/time.h>
16
17#define NUM_ITER 300
18
19enum {
20 kExit=0, kSetAndTest, kTest
21};
22
23static struct {
24 volatile int tbu;
25 volatile int tbl;
26 volatile int mark;
27 volatile int cmd;
28 volatile int handshake;
29 int filler[3];
30
31 volatile int ack;
32 int filler2[7];
33
34 volatile int race_result;
35} *tbsync;
36
37static volatile int running;
38
39static void __devinit
40enter_contest( int mark, int add )
41{
42 while( (int)(get_tbl() - mark) < 0 )
43 tbsync->race_result = add;
44}
45
46void __devinit
47smp_generic_take_timebase( void )
48{
49 int cmd, tbl, tbu;
467c3780 50 unsigned long flags;
1da177e4 51
467c3780 52 local_irq_save(flags);
1da177e4
LT
53 while( !running )
54 ;
55 rmb();
56
57 for( ;; ) {
58 tbsync->ack = 1;
59 while( !tbsync->handshake )
60 ;
61 rmb();
62
63 cmd = tbsync->cmd;
64 tbl = tbsync->tbl;
65 tbu = tbsync->tbu;
66 tbsync->ack = 0;
67 if( cmd == kExit )
467c3780 68 break;
1da177e4
LT
69
70 if( cmd == kSetAndTest ) {
71 while( tbsync->handshake )
72 ;
73 asm volatile ("mttbl %0" :: "r" (tbl) );
74 asm volatile ("mttbu %0" :: "r" (tbu) );
75 } else {
76 while( tbsync->handshake )
77 ;
78 }
79 enter_contest( tbsync->mark, -1 );
80 }
467c3780 81 local_irq_restore(flags);
1da177e4
LT
82}
83
84static int __devinit
85start_contest( int cmd, int offset, int num )
86{
87 int i, tbu, tbl, mark, score=0;
88
89 tbsync->cmd = cmd;
90
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;
97
98 wmb();
99
100 tbsync->handshake = 1;
101 while( tbsync->ack )
102 ;
103
104 while( (int)(get_tbl() - tbl) <= 0 )
105 ;
106 tbsync->handshake = 0;
107 enter_contest( mark, 1 );
108
109 while( !tbsync->ack )
110 ;
111
112 if( tbsync->tbu != get_tbu() || ((tbsync->tbl ^ get_tbl()) & 0x80000000) )
113 continue;
114 if( i++ > 0 )
115 score += tbsync->race_result;
116 }
117 local_irq_enable();
118 return score;
119}
120
121void __devinit
122smp_generic_give_timebase( void )
123{
124 int i, score, score2, old, min=0, max=5000, offset=1000;
125
126 printk("Synchronizing timebase\n");
127
128 /* if this fails then this kernel won't work anyway... */
d116fe5a 129 tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL );
1da177e4
LT
130 mb();
131 running = 1;
132
133 while( !tbsync->ack )
134 ;
135
136 /* binary search */
137 for( old=-1 ; old != offset ; offset=(min+max)/2 ) {
138 score = start_contest( kSetAndTest, offset, NUM_ITER );
139
140 printk("score %d, offset %d\n", score, offset );
141
142 if( score > 0 )
143 max = offset;
144 else
145 min = offset;
146 old = offset;
147 }
148 score = start_contest( kSetAndTest, min, NUM_ITER );
149 score2 = start_contest( kSetAndTest, max, NUM_ITER );
150
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;
155
156 /* guard against inaccurate mttb */
157 for( i=0; i<10; i++ ) {
158 start_contest( kSetAndTest, offset, NUM_ITER/10 );
159
160 if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 )
161 score2 = -score2;
162 if( score2 <= score || score2 < 20 )
163 break;
164 }
165 printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
166
167 /* exiting */
168 tbsync->cmd = kExit;
169 wmb();
170 tbsync->handshake = 1;
171 while( tbsync->ack )
172 ;
173 tbsync->handshake = 0;
174 kfree( tbsync );
175 tbsync = NULL;
176 running = 0;
177
178 /* all done */
179 smp_tb_synchronized = 1;
180}
This page took 0.252158 seconds and 5 git commands to generate.