smp-tbsync.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Smp timebase synchronization for ppc.
  3. *
  4. * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
  5. *
  6. */
  7. #include <linux/config.h>
  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. #define NUM_ITER 300
  17. enum {
  18. kExit=0, kSetAndTest, kTest
  19. };
  20. static struct {
  21. volatile int tbu;
  22. volatile int tbl;
  23. volatile int mark;
  24. volatile int cmd;
  25. volatile int handshake;
  26. int filler[3];
  27. volatile int ack;
  28. int filler2[7];
  29. volatile int race_result;
  30. } *tbsync;
  31. static volatile int running;
  32. static void __devinit
  33. enter_contest( int mark, int add )
  34. {
  35. while( (int)(get_tbl() - mark) < 0 )
  36. tbsync->race_result = add;
  37. }
  38. void __devinit
  39. smp_generic_take_timebase( void )
  40. {
  41. int cmd, tbl, tbu;
  42. local_irq_disable();
  43. while( !running )
  44. ;
  45. rmb();
  46. for( ;; ) {
  47. tbsync->ack = 1;
  48. while( !tbsync->handshake )
  49. ;
  50. rmb();
  51. cmd = tbsync->cmd;
  52. tbl = tbsync->tbl;
  53. tbu = tbsync->tbu;
  54. tbsync->ack = 0;
  55. if( cmd == kExit )
  56. return;
  57. if( cmd == kSetAndTest ) {
  58. while( tbsync->handshake )
  59. ;
  60. asm volatile ("mttbl %0" :: "r" (tbl) );
  61. asm volatile ("mttbu %0" :: "r" (tbu) );
  62. } else {
  63. while( tbsync->handshake )
  64. ;
  65. }
  66. enter_contest( tbsync->mark, -1 );
  67. }
  68. local_irq_enable();
  69. }
  70. static int __devinit
  71. start_contest( int cmd, int offset, int num )
  72. {
  73. int i, tbu, tbl, mark, score=0;
  74. tbsync->cmd = cmd;
  75. local_irq_disable();
  76. for( i=-3; i<num; ) {
  77. tbl = get_tbl() + 400;
  78. tbsync->tbu = tbu = get_tbu();
  79. tbsync->tbl = tbl + offset;
  80. tbsync->mark = mark = tbl + 400;
  81. wmb();
  82. tbsync->handshake = 1;
  83. while( tbsync->ack )
  84. ;
  85. while( (int)(get_tbl() - tbl) <= 0 )
  86. ;
  87. tbsync->handshake = 0;
  88. enter_contest( mark, 1 );
  89. while( !tbsync->ack )
  90. ;
  91. if( tbsync->tbu != get_tbu() || ((tbsync->tbl ^ get_tbl()) & 0x80000000) )
  92. continue;
  93. if( i++ > 0 )
  94. score += tbsync->race_result;
  95. }
  96. local_irq_enable();
  97. return score;
  98. }
  99. void __devinit
  100. smp_generic_give_timebase( void )
  101. {
  102. int i, score, score2, old, min=0, max=5000, offset=1000;
  103. printk("Synchronizing timebase\n");
  104. /* if this fails then this kernel won't work anyway... */
  105. tbsync = kmalloc( sizeof(*tbsync), GFP_KERNEL );
  106. memset( tbsync, 0, sizeof(*tbsync) );
  107. mb();
  108. running = 1;
  109. while( !tbsync->ack )
  110. ;
  111. /* binary search */
  112. for( old=-1 ; old != offset ; offset=(min+max)/2 ) {
  113. score = start_contest( kSetAndTest, offset, NUM_ITER );
  114. printk("score %d, offset %d\n", score, offset );
  115. if( score > 0 )
  116. max = offset;
  117. else
  118. min = offset;
  119. old = offset;
  120. }
  121. score = start_contest( kSetAndTest, min, NUM_ITER );
  122. score2 = start_contest( kSetAndTest, max, NUM_ITER );
  123. printk( "Min %d (score %d), Max %d (score %d)\n", min, score, max, score2 );
  124. score = abs( score );
  125. score2 = abs( score2 );
  126. offset = (score < score2) ? min : max;
  127. /* guard against inaccurate mttb */
  128. for( i=0; i<10; i++ ) {
  129. start_contest( kSetAndTest, offset, NUM_ITER/10 );
  130. if( (score2=start_contest(kTest, offset, NUM_ITER)) < 0 )
  131. score2 = -score2;
  132. if( score2 <= score || score2 < 20 )
  133. break;
  134. }
  135. printk("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
  136. /* exiting */
  137. tbsync->cmd = kExit;
  138. wmb();
  139. tbsync->handshake = 1;
  140. while( tbsync->ack )
  141. ;
  142. tbsync->handshake = 0;
  143. kfree( tbsync );
  144. tbsync = NULL;
  145. running = 0;
  146. /* all done */
  147. smp_tb_synchronized = 1;
  148. }