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 20 enum { 21 kExit=0, kSetAndTest, kTest 22 }; 23 24 static struct { 25 volatile u64 tb; 26 volatile u64 mark; 27 volatile int cmd; 28 volatile int handshake; 29 int filler[2]; 30 31 volatile int ack; 32 int filler2[7]; 33 34 volatile int race_result; 35 } *tbsync; 36 37 static volatile int running; 38 39 static void __devinit enter_contest(u64 mark, long add) 40 { 41 while (get_tb() < mark) 42 tbsync->race_result = add; 43 } 44 45 void __devinit smp_generic_take_timebase(void) 46 { 47 int cmd; 48 u64 tb; 49 50 local_irq_disable(); 51 while (!running) 52 barrier(); 53 rmb(); 54 55 for (;;) { 56 tbsync->ack = 1; 57 while (!tbsync->handshake) 58 barrier(); 59 rmb(); 60 61 cmd = tbsync->cmd; 62 tb = tbsync->tb; 63 mb(); 64 tbsync->ack = 0; 65 if (cmd == kExit) 66 break; 67 68 while (tbsync->handshake) 69 barrier(); 70 if (cmd == kSetAndTest) 71 set_tb(tb >> 32, tb & 0xfffffffful); 72 enter_contest(tbsync->mark, -1); 73 } 74 local_irq_enable(); 75 } 76 77 static int __devinit start_contest(int cmd, long offset, int num) 78 { 79 int i, score=0; 80 u64 tb; 81 long mark; 82 83 tbsync->cmd = cmd; 84 85 local_irq_disable(); 86 for (i = -3; i < num; ) { 87 tb = get_tb() + 400; 88 tbsync->tb = tb + offset; 89 tbsync->mark = mark = tb + 400; 90 91 wmb(); 92 93 tbsync->handshake = 1; 94 while (tbsync->ack) 95 barrier(); 96 97 while (get_tb() <= tb) 98 barrier(); 99 tbsync->handshake = 0; 100 enter_contest(mark, 1); 101 102 while (!tbsync->ack) 103 barrier(); 104 105 if (i++ > 0) 106 score += tbsync->race_result; 107 } 108 local_irq_enable(); 109 return score; 110 } 111 112 void __devinit smp_generic_give_timebase(void) 113 { 114 int i, score, score2, old, min=0, max=5000, offset=1000; 115 116 printk("Synchronizing timebase\n"); 117 118 /* if this fails then this kernel won't work anyway... */ 119 tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL ); 120 memset( tbsync, 0, sizeof(*tbsync) ); 121 mb(); 122 running = 1; 123 124 while (!tbsync->ack) 125 barrier(); 126 127 printk("Got ack\n"); 128 129 /* binary search */ 130 for (old = -1; old != offset ; offset = (min+max) / 2) { 131 score = start_contest(kSetAndTest, offset, NUM_ITER); 132 133 printk("score %d, offset %d\n", score, offset ); 134 135 if( score > 0 ) 136 max = offset; 137 else 138 min = offset; 139 old = offset; 140 } 141 score = start_contest(kSetAndTest, min, NUM_ITER); 142 score2 = start_contest(kSetAndTest, max, NUM_ITER); 143 144 printk("Min %d (score %d), Max %d (score %d)\n", 145 min, score, max, score2); 146 score = abs(score); 147 score2 = abs(score2); 148 offset = (score < score2) ? min : max; 149 150 /* guard against inaccurate mttb */ 151 for (i = 0; i < 10; i++) { 152 start_contest(kSetAndTest, offset, NUM_ITER/10); 153 154 if ((score2 = start_contest(kTest, offset, NUM_ITER)) < 0) 155 score2 = -score2; 156 if (score2 <= score || score2 < 20) 157 break; 158 } 159 printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER ); 160 161 /* exiting */ 162 tbsync->cmd = kExit; 163 wmb(); 164 tbsync->handshake = 1; 165 while (tbsync->ack) 166 barrier(); 167 tbsync->handshake = 0; 168 kfree(tbsync); 169 tbsync = NULL; 170 running = 0; 171 } 172