| .. | .. | 
|---|
 | 1 | +// SPDX-License-Identifier: GPL-2.0+  | 
|---|
| 1 | 2 |  /* | 
|---|
| 2 |  | - * linux/kernel/time/clocksource.c  | 
|---|
| 3 |  | - *  | 
|---|
| 4 | 3 |   * This file contains the functions which manage clocksource drivers. | 
|---|
| 5 | 4 |   * | 
|---|
| 6 | 5 |   * Copyright (C) 2004, 2005 IBM, John Stultz (johnstul@us.ibm.com) | 
|---|
| 7 |  | - *  | 
|---|
| 8 |  | - * This program is free software; you can redistribute it and/or modify  | 
|---|
| 9 |  | - * it under the terms of the GNU General Public License as published by  | 
|---|
| 10 |  | - * the Free Software Foundation; either version 2 of the License, or  | 
|---|
| 11 |  | - * (at your option) any later version.  | 
|---|
| 12 |  | - *  | 
|---|
| 13 |  | - * This program is distributed in the hope that it will be useful,  | 
|---|
| 14 |  | - * but WITHOUT ANY WARRANTY; without even the implied warranty of  | 
|---|
| 15 |  | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  | 
|---|
| 16 |  | - * GNU General Public License for more details.  | 
|---|
| 17 |  | - *  | 
|---|
| 18 |  | - * You should have received a copy of the GNU General Public License  | 
|---|
| 19 |  | - * along with this program; if not, write to the Free Software  | 
|---|
| 20 |  | - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  | 
|---|
| 21 |  | - *  | 
|---|
| 22 |  | - * TODO WishList:  | 
|---|
| 23 |  | - *   o Allow clocksource drivers to be unregistered  | 
|---|
| 24 | 6 |   */ | 
|---|
| 25 | 7 |   | 
|---|
| 26 | 8 |  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|---|
| .. | .. | 
|---|
| 123 | 105 |  static int watchdog_running; | 
|---|
| 124 | 106 |  static atomic_t watchdog_reset_pending; | 
|---|
| 125 | 107 |   | 
|---|
| 126 |  | -static void inline clocksource_watchdog_lock(unsigned long *flags)  | 
|---|
 | 108 | +static inline void clocksource_watchdog_lock(unsigned long *flags)  | 
|---|
| 127 | 109 |  { | 
|---|
| 128 | 110 |  	spin_lock_irqsave(&watchdog_lock, *flags); | 
|---|
| 129 | 111 |  } | 
|---|
| 130 | 112 |   | 
|---|
| 131 |  | -static void inline clocksource_watchdog_unlock(unsigned long *flags)  | 
|---|
 | 113 | +static inline void clocksource_watchdog_unlock(unsigned long *flags)  | 
|---|
| 132 | 114 |  { | 
|---|
| 133 | 115 |  	spin_unlock_irqrestore(&watchdog_lock, *flags); | 
|---|
| 134 | 116 |  } | 
|---|
| .. | .. | 
|---|
| 240 | 222 |  	pr_warn("timekeeping watchdog on CPU%d: %s read-back delay of %lldns, attempt %d, marking unstable\n", | 
|---|
| 241 | 223 |  		smp_processor_id(), watchdog->name, wd_delay, nretries); | 
|---|
| 242 | 224 |  	return false; | 
|---|
 | 225 | +}  | 
|---|
 | 226 | +  | 
|---|
 | 227 | +static u64 csnow_mid;  | 
|---|
 | 228 | +static cpumask_t cpus_ahead;  | 
|---|
 | 229 | +static cpumask_t cpus_behind;  | 
|---|
 | 230 | +  | 
|---|
 | 231 | +static void clocksource_verify_one_cpu(void *csin)  | 
|---|
 | 232 | +{  | 
|---|
 | 233 | +	struct clocksource *cs = (struct clocksource *)csin;  | 
|---|
 | 234 | +  | 
|---|
 | 235 | +	csnow_mid = cs->read(cs);  | 
|---|
 | 236 | +}  | 
|---|
 | 237 | +  | 
|---|
 | 238 | +static void clocksource_verify_percpu(struct clocksource *cs)  | 
|---|
 | 239 | +{  | 
|---|
 | 240 | +	int64_t cs_nsec, cs_nsec_max = 0, cs_nsec_min = LLONG_MAX;  | 
|---|
 | 241 | +	u64 csnow_begin, csnow_end;  | 
|---|
 | 242 | +	int cpu, testcpu;  | 
|---|
 | 243 | +	s64 delta;  | 
|---|
 | 244 | +  | 
|---|
 | 245 | +	cpumask_clear(&cpus_ahead);  | 
|---|
 | 246 | +	cpumask_clear(&cpus_behind);  | 
|---|
 | 247 | +	preempt_disable();  | 
|---|
 | 248 | +	testcpu = smp_processor_id();  | 
|---|
 | 249 | +	pr_warn("Checking clocksource %s synchronization from CPU %d.\n", cs->name, testcpu);  | 
|---|
 | 250 | +	for_each_online_cpu(cpu) {  | 
|---|
 | 251 | +		if (cpu == testcpu)  | 
|---|
 | 252 | +			continue;  | 
|---|
 | 253 | +		csnow_begin = cs->read(cs);  | 
|---|
 | 254 | +		smp_call_function_single(cpu, clocksource_verify_one_cpu, cs, 1);  | 
|---|
 | 255 | +		csnow_end = cs->read(cs);  | 
|---|
 | 256 | +		delta = (s64)((csnow_mid - csnow_begin) & cs->mask);  | 
|---|
 | 257 | +		if (delta < 0)  | 
|---|
 | 258 | +			cpumask_set_cpu(cpu, &cpus_behind);  | 
|---|
 | 259 | +		delta = (csnow_end - csnow_mid) & cs->mask;  | 
|---|
 | 260 | +		if (delta < 0)  | 
|---|
 | 261 | +			cpumask_set_cpu(cpu, &cpus_ahead);  | 
|---|
 | 262 | +		delta = clocksource_delta(csnow_end, csnow_begin, cs->mask);  | 
|---|
 | 263 | +		cs_nsec = clocksource_cyc2ns(delta, cs->mult, cs->shift);  | 
|---|
 | 264 | +		if (cs_nsec > cs_nsec_max)  | 
|---|
 | 265 | +			cs_nsec_max = cs_nsec;  | 
|---|
 | 266 | +		if (cs_nsec < cs_nsec_min)  | 
|---|
 | 267 | +			cs_nsec_min = cs_nsec;  | 
|---|
 | 268 | +	}  | 
|---|
 | 269 | +	preempt_enable();  | 
|---|
 | 270 | +	if (!cpumask_empty(&cpus_ahead))  | 
|---|
 | 271 | +		pr_warn("        CPUs %*pbl ahead of CPU %d for clocksource %s.\n",  | 
|---|
 | 272 | +			cpumask_pr_args(&cpus_ahead), testcpu, cs->name);  | 
|---|
 | 273 | +	if (!cpumask_empty(&cpus_behind))  | 
|---|
 | 274 | +		pr_warn("        CPUs %*pbl behind CPU %d for clocksource %s.\n",  | 
|---|
 | 275 | +			cpumask_pr_args(&cpus_behind), testcpu, cs->name);  | 
|---|
 | 276 | +	if (!cpumask_empty(&cpus_ahead) || !cpumask_empty(&cpus_behind))  | 
|---|
 | 277 | +		pr_warn("        CPU %d check durations %lldns - %lldns for clocksource %s.\n",  | 
|---|
 | 278 | +			testcpu, cs_nsec_min, cs_nsec_max, cs->name);  | 
|---|
| 243 | 279 |  } | 
|---|
| 244 | 280 |   | 
|---|
| 245 | 281 |  static void clocksource_watchdog(struct timer_list *unused) | 
|---|
| .. | .. | 
|---|
| 465 | 501 |  	struct clocksource *cs, *tmp; | 
|---|
| 466 | 502 |  	unsigned long flags; | 
|---|
| 467 | 503 |  	int select = 0; | 
|---|
 | 504 | +  | 
|---|
 | 505 | +	/* Do any required per-CPU skew verification. */  | 
|---|
 | 506 | +	if (curr_clocksource &&  | 
|---|
 | 507 | +	    curr_clocksource->flags & CLOCK_SOURCE_UNSTABLE &&  | 
|---|
 | 508 | +	    curr_clocksource->flags & CLOCK_SOURCE_VERIFY_PERCPU)  | 
|---|
 | 509 | +		clocksource_verify_percpu(curr_clocksource);  | 
|---|
| 468 | 510 |   | 
|---|
| 469 | 511 |  	spin_lock_irqsave(&watchdog_lock, flags); | 
|---|
| 470 | 512 |  	list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { | 
|---|
| .. | .. | 
|---|
| 883 | 925 |  	mutex_unlock(&clocksource_mutex); | 
|---|
| 884 | 926 |  	return 0; | 
|---|
| 885 | 927 |  } | 
|---|
| 886 |  | -#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT  | 
|---|
| 887 |  | -pure_initcall(clocksource_done_booting);  | 
|---|
| 888 |  | -#else  | 
|---|
| 889 | 928 |  fs_initcall(clocksource_done_booting); | 
|---|
| 890 |  | -#endif  | 
|---|
| 891 | 929 |   | 
|---|
| 892 | 930 |  /* | 
|---|
| 893 | 931 |   * Enqueue the clocksource sorted by rating | 
|---|
| .. | .. | 
|---|
| 989 | 1027 |  { | 
|---|
| 990 | 1028 |  	unsigned long flags; | 
|---|
| 991 | 1029 |   | 
|---|
 | 1030 | +	clocksource_arch_init(cs);  | 
|---|
 | 1031 | +  | 
|---|
 | 1032 | +	if (cs->vdso_clock_mode < 0 ||  | 
|---|
 | 1033 | +	    cs->vdso_clock_mode >= VDSO_CLOCKMODE_MAX) {  | 
|---|
 | 1034 | +		pr_warn("clocksource %s registered with invalid VDSO mode %d. Disabling VDSO support.\n",  | 
|---|
 | 1035 | +			cs->name, cs->vdso_clock_mode);  | 
|---|
 | 1036 | +		cs->vdso_clock_mode = VDSO_CLOCKMODE_NONE;  | 
|---|
 | 1037 | +	}  | 
|---|
 | 1038 | +  | 
|---|
| 992 | 1039 |  	/* Initialize mult/shift and max_idle_ns */ | 
|---|
| 993 | 1040 |  	__clocksource_update_freq_scale(cs, scale, freq); | 
|---|
| 994 | 1041 |   | 
|---|