diff options
Diffstat (limited to 'roms/skiboot/core/timebase.c')
-rw-r--r-- | roms/skiboot/core/timebase.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/roms/skiboot/core/timebase.c b/roms/skiboot/core/timebase.c new file mode 100644 index 000000000..451e3710e --- /dev/null +++ b/roms/skiboot/core/timebase.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * Wait for things, by waiting for timebase to tick over + * + * Copyright 2013-2019 IBM Corp. + */ + +#include <timebase.h> +#include <opal.h> +#include <cpu.h> +#include <chip.h> +#include <debug_descriptor.h> + +unsigned long tb_hz = 512000000; + +static void time_wait_poll(unsigned long duration) +{ + unsigned long now = mftb(); + unsigned long end = now + duration; + unsigned long period = msecs_to_tb(5); + + if (this_cpu()->tb_invalid) { + /* + * Run pollers to allow some backends to process response. + * + * In TOD failure case where TOD is unrecoverable, running + * pollers allows ipmi backend to deal with ipmi response + * from bmc and helps ipmi_queue_msg_sync() to get un-stuck. + * Thus it avoids linux kernel to hang during panic due to + * TOD failure. + */ + opal_run_pollers(); + cpu_relax(); + return; + } + + while (tb_compare(now, end) != TB_AAFTERB) { + + unsigned long remaining = end - now; + + /* Call pollers periodically but not continually to avoid + * bouncing cachelines due to lock contention. */ + if (remaining >= period) { + opal_run_pollers(); + time_wait_nopoll(period); + } else + time_wait_nopoll(remaining); + + now = mftb(); + } +} + +void time_wait(unsigned long duration) +{ + struct cpu_thread *c = this_cpu(); + + if (!list_empty(&this_cpu()->locks_held)) { + time_wait_nopoll(duration); + return; + } + + if (c != boot_cpu && opal_booting()) + time_wait_nopoll(duration); + else + time_wait_poll(duration); +} + +void time_wait_nopoll(unsigned long duration) +{ + if (this_cpu()->tb_invalid) { + cpu_relax(); + return; + } + + cpu_idle_delay(duration); +} + +void time_wait_ms(unsigned long ms) +{ + time_wait(msecs_to_tb(ms)); +} + +void time_wait_ms_nopoll(unsigned long ms) +{ + time_wait_nopoll(msecs_to_tb(ms)); +} + +void time_wait_us(unsigned long us) +{ + time_wait(usecs_to_tb(us)); +} + +void time_wait_us_nopoll(unsigned long us) +{ + time_wait_nopoll(usecs_to_tb(us)); +} + +unsigned long timespec_to_tb(const struct timespec *ts) +{ + unsigned long ns; + + /* First convert to ns */ + ns = ts->tv_sec * 1000000000ul; + ns += ts->tv_nsec; + + /* + * This is a very rough approximation, it works provided + * we never try to pass too long delays here and the TB + * frequency isn't significantly lower than 512Mhz. + * + * We could improve the precision by shifting less bits + * at the expense of capacity or do 128 bit math which + * I'm not eager to do :-) + */ + if (chip_quirk(QUIRK_SLOW_SIM)) + return (ns * (tb_hz >> 16)) / (1000000000ul >> 16); + else + return (ns * (tb_hz >> 24)) / (1000000000ul >> 24); +} + +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + time_wait(timespec_to_tb(req)); + + if (rem) { + rem->tv_sec = 0; + rem->tv_nsec = 0; + } + return 0; +} + +int nanosleep_nopoll(const struct timespec *req, struct timespec *rem) +{ + time_wait_nopoll(timespec_to_tb(req)); + + if (rem) { + rem->tv_sec = 0; + rem->tv_nsec = 0; + } + return 0; +} |