^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Smp timebase synchronization for ppc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2003 Samuel Rydh (samuel@ibrium.se)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/sched.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/smp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/unistd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/atomic.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <asm/smp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <asm/time.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define NUM_ITER 300
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) kExit=0, kSetAndTest, kTest
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) volatile u64 tb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) volatile u64 mark;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) volatile int cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) volatile int handshake;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) int filler[2];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) volatile int ack;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) int filler2[7];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) volatile int race_result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) } *tbsync;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static volatile int running;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static void enter_contest(u64 mark, long add)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) while (get_tb() < mark)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) tbsync->race_result = add;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) void smp_generic_take_timebase(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) int cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) u64 tb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) unsigned long flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) local_irq_save(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) while (!running)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) rmb();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) for (;;) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) tbsync->ack = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) while (!tbsync->handshake)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) rmb();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) cmd = tbsync->cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) tb = tbsync->tb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) mb();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) tbsync->ack = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) if (cmd == kExit)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) while (tbsync->handshake)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) if (cmd == kSetAndTest)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) set_tb(tb >> 32, tb & 0xfffffffful);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) enter_contest(tbsync->mark, -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) local_irq_restore(flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static int start_contest(int cmd, long offset, int num)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) int i, score=0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) u64 tb;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) u64 mark;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) tbsync->cmd = cmd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) for (i = -3; i < num; ) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) tb = get_tb() + 400;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) tbsync->tb = tb + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) tbsync->mark = mark = tb + 400;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) wmb();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) tbsync->handshake = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) while (tbsync->ack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) while (get_tb() <= tb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) tbsync->handshake = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) enter_contest(mark, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) while (!tbsync->ack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) if (i++ > 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) score += tbsync->race_result;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) return score;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) void smp_generic_give_timebase(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) int i, score, score2, old, min=0, max=5000, offset=1000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) pr_debug("Software timebase sync\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /* if this fails then this kernel won't work anyway... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) tbsync = kzalloc( sizeof(*tbsync), GFP_KERNEL );
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) mb();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) running = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) while (!tbsync->ack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) pr_debug("Got ack\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) /* binary search */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) for (old = -1; old != offset ; offset = (min+max) / 2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) score = start_contest(kSetAndTest, offset, NUM_ITER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) pr_debug("score %d, offset %d\n", score, offset );
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) if( score > 0 )
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) max = offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) min = offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) old = offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) score = start_contest(kSetAndTest, min, NUM_ITER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) score2 = start_contest(kSetAndTest, max, NUM_ITER);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) pr_debug("Min %d (score %d), Max %d (score %d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) min, score, max, score2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) score = abs(score);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) score2 = abs(score2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) offset = (score < score2) ? min : max;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) /* guard against inaccurate mttb */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) for (i = 0; i < 10; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) start_contest(kSetAndTest, offset, NUM_ITER/10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if ((score2 = start_contest(kTest, offset, NUM_ITER)) < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) score2 = -score2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) if (score2 <= score || score2 < 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) pr_debug("Final offset: %d (%d/%d)\n", offset, score2, NUM_ITER );
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) /* exiting */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) tbsync->cmd = kExit;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) wmb();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) tbsync->handshake = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) while (tbsync->ack)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) barrier();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) tbsync->handshake = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) kfree(tbsync);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) tbsync = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) running = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) }