aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ppc64/kernel/smp-tbsync.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc64/kernel/smp-tbsync.c')
-rw-r--r--arch/ppc64/kernel/smp-tbsync.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/smp-tbsync.c b/arch/ppc64/kernel/smp-tbsync.c
new file mode 100644
index 000000000000..7d8ec9996b3e
--- /dev/null
+++ b/arch/ppc64/kernel/smp-tbsync.c
@@ -0,0 +1,179 @@
1/*
2 * Smp timebase synchronization for ppc.
3 *
4 * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
5 *
6 */
7
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>
17
18#define NUM_ITER 300
19
20enum {
21 kExit=0, kSetAndTest, kTest
22};
23
24static struct {
25 volatile long tb;
26 volatile long 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( long mark, long add )
41{
42 while( (long)(mftb() - mark) < 0 )
43 tbsync->race_result = add;
44}
45
46void __devinit
47smp_generic_take_timebase( void )
48{
49 int cmd;
50 long tb;
51
52 local_irq_disable();
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 tb = tbsync->tb;
65 tbsync->ack = 0;
66 if( cmd == kExit )
67 return;
68
69 if( cmd == kSetAndTest ) {
70 while( tbsync->handshake )
71 ;
72 asm volatile ("mttbl %0" :: "r" (tb & 0xfffffffful) );
73 asm volatile ("mttbu %0" :: "r" (tb >> 32) );
74 } else {
75 while( tbsync->handshake )
76 ;
77 }
78 enter_contest( tbsync->mark, -1 );
79 }
80 local_irq_enable();
81}
82
83static int __devinit
84start_contest( int cmd, long offset, long num )
85{
86 int i, score=0;
87 long tb, mark;
88
89 tbsync->cmd = cmd;
90
91 local_irq_disable();
92 for( i=-3; i<num; ) {
93 tb = (long)mftb() + 400;
94 tbsync->tb = tb + offset;
95 tbsync->mark = mark = tb + 400;
96
97 wmb();
98
99 tbsync->handshake = 1;
100 while( tbsync->ack )
101 ;
102
103 while( (long)(mftb() - tb) <= 0 )
104 ;
105 tbsync->handshake = 0;
106 enter_contest( mark, 1 );
107
108 while( !tbsync->ack )
109 ;
110
111 if ((tbsync->tb ^ (long)mftb()) & 0x8000000000000000ul)
112 continue;
113 if( i++ > 0 )
114 score += tbsync->race_result;
115 }
116 local_irq_enable();
117 return score;
118}
119
120void __devinit
121smp_generic_give_timebase( void )
122{
123 int i, score, score2, old, min=0, max=5000, offset=1000;
124
125 printk("Synchronizing timebase\n");
126
127 /* if this fails then this kernel won't work anyway... */
128 tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL );
129 memset( tbsync, 0, sizeof(*tbsync) );
130 mb();
131 running = 1;
132
133 while( !tbsync->ack )
134 ;
135
136 printk("Got ack\n");
137
138 /* binary search */
139 for( old=-1 ; old != offset ; offset=(min+max)/2 ) {
140 score = start_contest( kSetAndTest, offset, NUM_ITER );
141
142 printk("score %d, offset %d\n", score, offset );
143
144 if( score > 0 )
145 max = offset;
146 else
147 min = offset;
148 old = offset;
149 }
150 score = start_contest( kSetAndTest, min, NUM_ITER );
151 score2 = start_contest( kSetAndTest, max, NUM_ITER );
152
153 printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 );
154 score = abs( score );
155 score2 = abs( score2 );
156 offset = (score < score2) ? min : max;
157
158 /* guard against inaccurate mttb */
159 for( i=0; i<10; i++ ) {
160 start_contest( kSetAndTest, offset, NUM_ITER/10 );
161
162 if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 )
163 score2 = -score2;
164 if( score2 <= score || score2 < 20 )
165 break;
166 }
167 printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
168
169 /* exiting */
170 tbsync->cmd = kExit;
171 wmb();
172 tbsync->handshake = 1;
173 while( tbsync->ack )
174 ;
175 tbsync->handshake = 0;
176 kfree( tbsync );
177 tbsync = NULL;
178 running = 0;
179}