1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28ad200d7SPaul Mackerras /*
38ad200d7SPaul Mackerras * Smp timebase synchronization for ppc.
48ad200d7SPaul Mackerras *
58ad200d7SPaul Mackerras * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
68ad200d7SPaul Mackerras *
78ad200d7SPaul Mackerras */
88ad200d7SPaul Mackerras
98ad200d7SPaul Mackerras #include <linux/kernel.h>
108ad200d7SPaul Mackerras #include <linux/sched.h>
118ad200d7SPaul Mackerras #include <linux/smp.h>
128ad200d7SPaul Mackerras #include <linux/unistd.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
1460063497SArun Sharma #include <linux/atomic.h>
158ad200d7SPaul Mackerras #include <asm/smp.h>
168ad200d7SPaul Mackerras #include <asm/time.h>
178ad200d7SPaul Mackerras
188ad200d7SPaul Mackerras #define NUM_ITER 300
198ad200d7SPaul Mackerras
208ad200d7SPaul Mackerras enum {
218ad200d7SPaul Mackerras kExit=0, kSetAndTest, kTest
228ad200d7SPaul Mackerras };
238ad200d7SPaul Mackerras
248ad200d7SPaul Mackerras static struct {
258ad200d7SPaul Mackerras volatile u64 tb;
268ad200d7SPaul Mackerras volatile u64 mark;
278ad200d7SPaul Mackerras volatile int cmd;
288ad200d7SPaul Mackerras volatile int handshake;
298ad200d7SPaul Mackerras int filler[2];
308ad200d7SPaul Mackerras
318ad200d7SPaul Mackerras volatile int ack;
328ad200d7SPaul Mackerras int filler2[7];
338ad200d7SPaul Mackerras
348ad200d7SPaul Mackerras volatile int race_result;
358ad200d7SPaul Mackerras } *tbsync;
368ad200d7SPaul Mackerras
378ad200d7SPaul Mackerras static volatile int running;
388ad200d7SPaul Mackerras
enter_contest(u64 mark,long add)39cad5cef6SGreg Kroah-Hartman static void enter_contest(u64 mark, long add)
408ad200d7SPaul Mackerras {
418ad200d7SPaul Mackerras while (get_tb() < mark)
428ad200d7SPaul Mackerras tbsync->race_result = add;
438ad200d7SPaul Mackerras }
448ad200d7SPaul Mackerras
smp_generic_take_timebase(void)45cad5cef6SGreg Kroah-Hartman void smp_generic_take_timebase(void)
468ad200d7SPaul Mackerras {
478ad200d7SPaul Mackerras int cmd;
488ad200d7SPaul Mackerras u64 tb;
49467c3780SPaul Mackerras unsigned long flags;
508ad200d7SPaul Mackerras
51467c3780SPaul Mackerras local_irq_save(flags);
528ad200d7SPaul Mackerras while (!running)
538ad200d7SPaul Mackerras barrier();
548ad200d7SPaul Mackerras rmb();
558ad200d7SPaul Mackerras
568ad200d7SPaul Mackerras for (;;) {
578ad200d7SPaul Mackerras tbsync->ack = 1;
588ad200d7SPaul Mackerras while (!tbsync->handshake)
598ad200d7SPaul Mackerras barrier();
608ad200d7SPaul Mackerras rmb();
618ad200d7SPaul Mackerras
628ad200d7SPaul Mackerras cmd = tbsync->cmd;
638ad200d7SPaul Mackerras tb = tbsync->tb;
648ad200d7SPaul Mackerras mb();
658ad200d7SPaul Mackerras tbsync->ack = 0;
668ad200d7SPaul Mackerras if (cmd == kExit)
678ad200d7SPaul Mackerras break;
688ad200d7SPaul Mackerras
698ad200d7SPaul Mackerras while (tbsync->handshake)
708ad200d7SPaul Mackerras barrier();
718ad200d7SPaul Mackerras if (cmd == kSetAndTest)
728ad200d7SPaul Mackerras set_tb(tb >> 32, tb & 0xfffffffful);
738ad200d7SPaul Mackerras enter_contest(tbsync->mark, -1);
748ad200d7SPaul Mackerras }
75467c3780SPaul Mackerras local_irq_restore(flags);
768ad200d7SPaul Mackerras }
778ad200d7SPaul Mackerras
start_contest(int cmd,long offset,int num)78cad5cef6SGreg Kroah-Hartman static int start_contest(int cmd, long offset, int num)
798ad200d7SPaul Mackerras {
808ad200d7SPaul Mackerras int i, score=0;
818ad200d7SPaul Mackerras u64 tb;
829a06c3b1SAdrian Cox u64 mark;
838ad200d7SPaul Mackerras
848ad200d7SPaul Mackerras tbsync->cmd = cmd;
858ad200d7SPaul Mackerras
868ad200d7SPaul Mackerras local_irq_disable();
878ad200d7SPaul Mackerras for (i = -3; i < num; ) {
888ad200d7SPaul Mackerras tb = get_tb() + 400;
898ad200d7SPaul Mackerras tbsync->tb = tb + offset;
908ad200d7SPaul Mackerras tbsync->mark = mark = tb + 400;
918ad200d7SPaul Mackerras
928ad200d7SPaul Mackerras wmb();
938ad200d7SPaul Mackerras
948ad200d7SPaul Mackerras tbsync->handshake = 1;
958ad200d7SPaul Mackerras while (tbsync->ack)
968ad200d7SPaul Mackerras barrier();
978ad200d7SPaul Mackerras
988ad200d7SPaul Mackerras while (get_tb() <= tb)
998ad200d7SPaul Mackerras barrier();
1008ad200d7SPaul Mackerras tbsync->handshake = 0;
1018ad200d7SPaul Mackerras enter_contest(mark, 1);
1028ad200d7SPaul Mackerras
1038ad200d7SPaul Mackerras while (!tbsync->ack)
1048ad200d7SPaul Mackerras barrier();
1058ad200d7SPaul Mackerras
1068ad200d7SPaul Mackerras if (i++ > 0)
1078ad200d7SPaul Mackerras score += tbsync->race_result;
1088ad200d7SPaul Mackerras }
1098ad200d7SPaul Mackerras local_irq_enable();
1108ad200d7SPaul Mackerras return score;
1118ad200d7SPaul Mackerras }
1128ad200d7SPaul Mackerras
smp_generic_give_timebase(void)113cad5cef6SGreg Kroah-Hartman void smp_generic_give_timebase(void)
1148ad200d7SPaul Mackerras {
1158ad200d7SPaul Mackerras int i, score, score2, old, min=0, max=5000, offset=1000;
1168ad200d7SPaul Mackerras
117a6a8e009SBenjamin Herrenschmidt pr_debug("Software timebase sync\n");
1188ad200d7SPaul Mackerras
1198ad200d7SPaul Mackerras /* if this fails then this kernel won't work anyway... */
120f8485350SYan Burman tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL );
1218ad200d7SPaul Mackerras mb();
1228ad200d7SPaul Mackerras running = 1;
1238ad200d7SPaul Mackerras
1248ad200d7SPaul Mackerras while (!tbsync->ack)
1258ad200d7SPaul Mackerras barrier();
1268ad200d7SPaul Mackerras
127a6a8e009SBenjamin Herrenschmidt pr_debug("Got ack\n");
1288ad200d7SPaul Mackerras
1298ad200d7SPaul Mackerras /* binary search */
1308ad200d7SPaul Mackerras for (old = -1; old != offset ; offset = (min+max) / 2) {
1318ad200d7SPaul Mackerras score = start_contest(kSetAndTest, offset, NUM_ITER);
1328ad200d7SPaul Mackerras
133a6a8e009SBenjamin Herrenschmidt pr_debug("score %d, offset %d\n", score, offset );
1348ad200d7SPaul Mackerras
1358ad200d7SPaul Mackerras if( score > 0 )
1368ad200d7SPaul Mackerras max = offset;
1378ad200d7SPaul Mackerras else
1388ad200d7SPaul Mackerras min = offset;
1398ad200d7SPaul Mackerras old = offset;
1408ad200d7SPaul Mackerras }
1418ad200d7SPaul Mackerras score = start_contest(kSetAndTest, min, NUM_ITER);
1428ad200d7SPaul Mackerras score2 = start_contest(kSetAndTest, max, NUM_ITER);
1438ad200d7SPaul Mackerras
144a6a8e009SBenjamin Herrenschmidt pr_debug("Min %d (score %d), Max %d (score %d)\n",
1458ad200d7SPaul Mackerras min, score, max, score2);
1468ad200d7SPaul Mackerras score = abs(score);
1478ad200d7SPaul Mackerras score2 = abs(score2);
1488ad200d7SPaul Mackerras offset = (score < score2) ? min : max;
1498ad200d7SPaul Mackerras
1508ad200d7SPaul Mackerras /* guard against inaccurate mttb */
1518ad200d7SPaul Mackerras for (i = 0; i < 10; i++) {
1528ad200d7SPaul Mackerras start_contest(kSetAndTest, offset, NUM_ITER/10);
1538ad200d7SPaul Mackerras
1548ad200d7SPaul Mackerras if ((score2 = start_contest(kTest, offset, NUM_ITER)) < 0)
1558ad200d7SPaul Mackerras score2 = -score2;
1568ad200d7SPaul Mackerras if (score2 <= score || score2 < 20)
1578ad200d7SPaul Mackerras break;
1588ad200d7SPaul Mackerras }
159a6a8e009SBenjamin Herrenschmidt pr_debug("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
1608ad200d7SPaul Mackerras
1618ad200d7SPaul Mackerras /* exiting */
1628ad200d7SPaul Mackerras tbsync->cmd = kExit;
1638ad200d7SPaul Mackerras wmb();
1648ad200d7SPaul Mackerras tbsync->handshake = 1;
1658ad200d7SPaul Mackerras while (tbsync->ack)
1668ad200d7SPaul Mackerras barrier();
1678ad200d7SPaul Mackerras tbsync->handshake = 0;
1688ad200d7SPaul Mackerras kfree(tbsync);
1698ad200d7SPaul Mackerras tbsync = NULL;
1708ad200d7SPaul Mackerras running = 0;
1718ad200d7SPaul Mackerras }
172