2026-01-21 18:59:54 +08:00

229 lines
5.0 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/clk-provider.h>
#include <asm/debug.h>
#include "proto.h"
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);
DECLARE_PER_CPU(u64, tc_offset);
#define TICK_SIZE (tick_nsec / 1000)
/*
* Shift amount by which scaled_ticks_per_cycle is scaled. Shifting
* by 48 gives us 16 bits for HZ while keeping the accuracy good even
* for large CPU clock rates.
*/
#define FIX_SHIFT 48
unsigned long est_cycle_freq;
static u64 sc_start;
static u64 sc_shift;
static u64 sc_multi;
DEFINE_STATIC_KEY_FALSE(use_tc_as_sched_clock);
static int __init sched_clock_setup(char *opt)
{
if (!opt)
return -EINVAL;
if (!strncmp(opt, "on", 2)) {
static_branch_enable(&use_tc_as_sched_clock);
pr_info("Using TC instead of jiffies as source of sched_clock()\n");
}
return 0;
}
early_param("tc_sched_clock", sched_clock_setup);
#ifdef CONFIG_IRQ_WORK
DEFINE_PER_CPU(u8, irq_work_pending);
#define set_irq_work_pending_flag() __this_cpu_write(irq_work_pending, 1)
#define test_irq_work_pending() __this_cpu_read(irq_work_pending)
#define clear_irq_work_pending() __this_cpu_write(irq_work_pending, 0)
void arch_irq_work_raise(void)
{
set_irq_work_pending_flag();
}
#else /* CONFIG_IRQ_WORK */
#define test_irq_work_pending() 0
#define clear_irq_work_pending()
#endif /* CONFIG_IRQ_WORK */
#ifndef CONFIG_SMP
static u64 read_tc(struct clocksource *cs)
{
return rdtc();
}
static struct clocksource clocksource_tc = {
.name = "tc",
.rating = 300,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
.mask = CLOCKSOURCE_MASK(64),
.shift = 22,
.mult = 0, /* To be filled in */
.read = read_tc,
};
void setup_clocksource(void)
{
clocksource_register_hz(&clocksource_tc, get_cpu_freq());
pr_info("Setup clocksource TC, mult = %d\n", clocksource_tc.mult);
}
#else /* !CONFIG_SMP */
void setup_clocksource(void)
{
setup_chip_clocksource();
}
#endif /* !CONFIG_SMP */
void __init common_init_rtc(void)
{
setup_timer();
}
void __init
time_init(void)
{
unsigned long cycle_freq;
cycle_freq = get_cpu_freq();
pr_info("CPU Cycle frequency = %ld Hz\n", cycle_freq);
/* Register clocksource */
setup_clocksource();
of_clk_init(NULL);
/* Startup the timer source. */
common_init_rtc();
}
void calibrate_delay(void)
{
loops_per_jiffy = get_cpu_freq() / HZ;
pr_info("Clock rate yields %lu.%02lu BogoMIPS (lpj=%lu)\n",
loops_per_jiffy / (500000 / HZ),
(loops_per_jiffy / (5000 / HZ)) % 100, loops_per_jiffy);
}
static void __init calibrate_sched_clock(void)
{
sc_start = rdtc();
}
void __init setup_sched_clock(void)
{
unsigned long step;
sc_shift = 7;
step = 1UL << sc_shift;
sc_multi = step * NSEC_PER_SEC / get_cpu_freq();
calibrate_sched_clock();
pr_info("sched_clock: sc_multi=%llu, sc_shift=%llu\n", sc_multi, sc_shift);
}
#ifdef CONFIG_GENERIC_SCHED_CLOCK
static u64 notrace sched_clock_read(void)
{
return (rdtc() - sc_start) >> sc_shift;
}
void __init sw64_sched_clock_init(void)
{
sched_clock_register(sched_clock_read, BITS_PER_LONG, get_cpu_freq() >> sc_shift);
}
#else
unsigned long long sched_clock(void)
{
if (static_branch_likely(&use_tc_as_sched_clock))
return ((rdtc() - sc_start + __this_cpu_read(tc_offset)) >> sc_shift) * sc_multi;
else
return (jiffies - INITIAL_JIFFIES) * (NSEC_PER_SEC / HZ);
}
#ifdef CONFIG_DEBUG_FS
static ssize_t sched_clock_status_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[2];
if (static_key_enabled(&use_tc_as_sched_clock))
buf[0] = 'Y';
else
buf[0] = 'N';
buf[1] = '\n';
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t sched_clock_status_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
int r;
bool bv;
bool val = static_key_enabled(&use_tc_as_sched_clock);
r = kstrtobool_from_user(user_buf, count, &bv);
if (!r) {
if (val != bv) {
if (bv) {
static_branch_enable(&use_tc_as_sched_clock);
pr_info("source of sched_clock() switched from jiffies to TC\n");
} else {
static_branch_disable(&use_tc_as_sched_clock);
pr_info("source of sched_clock() switched from TC to jiffies\n");
}
} else {
if (val)
pr_info("source of sched_clock() unchanged (using TC)\n");
else
pr_info("source of sched_clock() unchanged (using jiffies)\n");
}
}
return count;
}
static const struct file_operations sched_clock_status_fops = {
.read = sched_clock_status_read,
.write = sched_clock_status_write,
.open = nonseekable_open,
.llseek = no_llseek,
};
static int __init sched_clock_debug_init(void)
{
struct dentry *sched_clock_status;
if (!sw64_debugfs_dir)
return -ENODEV;
sched_clock_status = debugfs_create_file("tc_sched_clock",
0644, sw64_debugfs_dir, NULL,
&sched_clock_status_fops);
if (!sched_clock_status)
return -ENOMEM;
return 0;
}
late_initcall(sched_clock_debug_init);
#endif /* CONFIG_DEBUG_FS */
#endif /* CONFIG_GENERIC_SCHED_CLOCK */