xref: /openbmc/linux/arch/powerpc/kernel/smp-tbsync.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
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