Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Paul Burton <paulburton@kernel.org>
Signed-off-by: Chao-ying Fu <cfu@wavecomp.com>
Signed-off-by: Dragan Mladjenovic <dragan.mladjenovic@syrmia.com>
Signed-off-by: Aleksandar Rikalo <arikalo@gmail.com>
Tested-by: Serge Semin <fancer.lancer@gmail.com>
Acked-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Tested-by: Gregory CLEMENT <gregory.clement@bootlin.com>
Link: https://lore.kernel.org/r/20241019071037.145314-6-arikalo@gmail.com
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>

authored by

Paul Burton and committed by
Daniel Lezcano
dfe101bc 31441331

+38 -1
+38 -1
drivers/clocksource/mips-gic-timer.c
··· 166 166 return gic_read_count(); 167 167 } 168 168 169 + static u64 gic_hpt_read_multicluster(struct clocksource *cs) 170 + { 171 + unsigned int hi, hi2, lo; 172 + u64 count; 173 + 174 + mips_cm_lock_other(0, 0, 0, CM_GCR_Cx_OTHER_BLOCK_GLOBAL); 175 + 176 + if (mips_cm_is64) { 177 + count = read_gic_redir_counter(); 178 + goto out; 179 + } 180 + 181 + hi = read_gic_redir_counter_32h(); 182 + while (true) { 183 + lo = read_gic_redir_counter_32l(); 184 + 185 + /* If hi didn't change then lo didn't wrap & we're done */ 186 + hi2 = read_gic_redir_counter_32h(); 187 + if (hi2 == hi) 188 + break; 189 + 190 + /* Otherwise, repeat with the latest hi value */ 191 + hi = hi2; 192 + } 193 + 194 + count = (((u64)hi) << 32) + lo; 195 + out: 196 + mips_cm_unlock_other(); 197 + return count; 198 + } 199 + 169 200 static struct clocksource gic_clocksource = { 170 201 .name = "GIC", 171 202 .read = gic_hpt_read, ··· 233 202 else 234 203 gic_clocksource.rating = 200; 235 204 gic_clocksource.rating += clamp(gic_frequency / 10000000, 0, 99); 205 + 206 + if (mips_cps_multicluster_cpus()) { 207 + gic_clocksource.read = &gic_hpt_read_multicluster; 208 + gic_clocksource.vdso_clock_mode = VDSO_CLOCKMODE_NONE; 209 + } 236 210 237 211 ret = clocksource_register_hz(&gic_clocksource, gic_frequency); 238 212 if (ret < 0) ··· 297 261 * stable CPU frequency or on the platforms with CM3 and CPU frequency 298 262 * change performed by the CPC core clocks divider. 299 263 */ 300 - if (mips_cm_revision() >= CM_REV_CM3 || !IS_ENABLED(CONFIG_CPU_FREQ)) { 264 + if ((mips_cm_revision() >= CM_REV_CM3 || !IS_ENABLED(CONFIG_CPU_FREQ)) && 265 + !mips_cps_multicluster_cpus()) { 301 266 sched_clock_register(mips_cm_is64 ? 302 267 gic_read_count_64 : gic_read_count_2x32, 303 268 gic_count_width, gic_frequency);