Commit dfe101bc authored by Paul Burton's avatar Paul Burton Committed by Daniel Lezcano
Browse files

clocksource/drivers/mips-gic-timer: Always use cluster 0 counter as clocksource



In a multi-cluster MIPS system, there are multiple GICs - one in each
cluster - each of which has its independent counter. The counters in
each GIC are not synchronized in any way, so they can drift relative
to one another through the lifetime of the system. This is problematic
for a clock source which ought to be global.

Avoid problems by always accessing cluster 0's counter, using
cross-cluster register access. This adds overhead so it is applied only
on multi-cluster systems.

Signed-off-by: default avatarPaul Burton <paulburton@kernel.org>
Signed-off-by: default avatarChao-ying Fu <cfu@wavecomp.com>
Signed-off-by: default avatarDragan Mladjenovic <dragan.mladjenovic@syrmia.com>
Signed-off-by: default avatarAleksandar Rikalo <arikalo@gmail.com>
Tested-by: default avatarSerge Semin <fancer.lancer@gmail.com>
Acked-by: default avatarThomas Bogendoerfer <tsbogend@alpha.franken.de>
Tested-by: default avatarGregory CLEMENT <gregory.clement@bootlin.com>
Link: https://lore.kernel.org/r/20241019071037.145314-6-arikalo@gmail.com


Signed-off-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
parent 31441331
Loading
Loading
Loading
Loading
+38 −1
Original line number Diff line number Diff line
@@ -166,6 +166,37 @@ static u64 gic_hpt_read(struct clocksource *cs)
	return gic_read_count();
}

static u64 gic_hpt_read_multicluster(struct clocksource *cs)
{
	unsigned int hi, hi2, lo;
	u64 count;

	mips_cm_lock_other(0, 0, 0, CM_GCR_Cx_OTHER_BLOCK_GLOBAL);

	if (mips_cm_is64) {
		count = read_gic_redir_counter();
		goto out;
	}

	hi = read_gic_redir_counter_32h();
	while (true) {
		lo = read_gic_redir_counter_32l();

		/* If hi didn't change then lo didn't wrap & we're done */
		hi2 = read_gic_redir_counter_32h();
		if (hi2 == hi)
			break;

		/* Otherwise, repeat with the latest hi value */
		hi = hi2;
	}

	count = (((u64)hi) << 32) + lo;
out:
	mips_cm_unlock_other();
	return count;
}

static struct clocksource gic_clocksource = {
	.name			= "GIC",
	.read			= gic_hpt_read,
@@ -203,6 +234,11 @@ static int __init __gic_clocksource_init(void)
		gic_clocksource.rating = 200;
	gic_clocksource.rating += clamp(gic_frequency / 10000000, 0, 99);

	if (mips_cps_multicluster_cpus()) {
		gic_clocksource.read = &gic_hpt_read_multicluster;
		gic_clocksource.vdso_clock_mode = VDSO_CLOCKMODE_NONE;
	}

	ret = clocksource_register_hz(&gic_clocksource, gic_frequency);
	if (ret < 0)
		pr_warn("Unable to register clocksource\n");
@@ -261,7 +297,8 @@ static int __init gic_clocksource_of_init(struct device_node *node)
	 * stable CPU frequency or on the platforms with CM3 and CPU frequency
	 * change performed by the CPC core clocks divider.
	 */
	if (mips_cm_revision() >= CM_REV_CM3 || !IS_ENABLED(CONFIG_CPU_FREQ)) {
	if ((mips_cm_revision() >= CM_REV_CM3 || !IS_ENABLED(CONFIG_CPU_FREQ)) &&
	     !mips_cps_multicluster_cpus()) {
		sched_clock_register(mips_cm_is64 ?
				     gic_read_count_64 : gic_read_count_2x32,
				     gic_count_width, gic_frequency);