summaryrefslogtreecommitdiff
path: root/drivers/clocksource/mips-gic-timer.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-06-03 10:10:07 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2020-06-03 10:10:07 -0700
commitdabc4df27c628866ede130a09121f255ca894d8c (patch)
tree4bf4c55b17443e8f1ada676c8339dbd058f66d05 /drivers/clocksource/mips-gic-timer.c
parentf6606d0c0010953e4c28c8662623662b5108b4ce (diff)
parent809eb4e9bf9d84eb5b703358afd0d564d514f6d2 (diff)
Merge tag 'timers-core-2020-06-02' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer updates from Thomas Gleixner: "The truly boring timer and clocksource updates for 5.8: - Not a single new clocksource or clockevent driver! - Device tree updates for various chips - Fixes and improvements and cleanups all over the place" * tag 'timers-core-2020-06-02' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (27 commits) dt-bindings: timer: Add renesas,em-sti bindings clocksource/drivers/timer-versatile: Clear OF_POPULATED flag clocksource: mips-gic-timer: Mark GIC timer as unstable if ref clock changes clocksource: mips-gic-timer: Register as sched_clock clocksource: dw_apb_timer_of: Fix missing clockevent timers clocksource: dw_apb_timer: Affiliate of-based timer with any CPU clocksource: dw_apb_timer: Make CPU-affiliation being optional dt-bindings: timer: Move snps,dw-apb-timer DT schema from rtc dt-bindings: rtc: Convert snps,dw-apb-timer to DT schema clocksource/drivers/timer-ti-dm: Do one override clock parent in prepare() clocksource/drivers/timer-ti-dm: Fix spelling mistake "detectt" -> "detect" clocksource/drivers/timer-ti-dm: Fix warning for set but not used clocksource/drivers/timer-ti-dm: Add clockevent and clocksource support clocksource/drivers/timer-ti-32k: Add support for initializing directly drivers/clocksource/arm_arch_timer: Remove duplicate error message clocksource/drivers/arc_timer: Remove duplicate error message clocksource/drivers/rda: drop redundant Kconfig dependency clocksource/drivers/timer-ti-dm: Fix warning for set but not used clocksource/drivers/timer-ti-dm: Add clockevent and clocksource support clocksource/drivers/timer-ti-32k: Add support for initializing directly ...
Diffstat (limited to 'drivers/clocksource/mips-gic-timer.c')
-rw-r--r--drivers/clocksource/mips-gic-timer.c50
1 files changed, 45 insertions, 5 deletions
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index 8b5f8ae723cb..be4175f415ba 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -16,6 +16,7 @@
#include <linux/notifier.h>
#include <linux/of_irq.h>
#include <linux/percpu.h>
+#include <linux/sched_clock.h>
#include <linux/smp.h>
#include <linux/time.h>
#include <asm/mips-cps.h>
@@ -23,14 +24,14 @@
static DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device);
static int gic_timer_irq;
static unsigned int gic_frequency;
+static bool __read_mostly gic_clock_unstable;
-static u64 notrace gic_read_count(void)
+static void gic_clocksource_unstable(char *reason);
+
+static u64 notrace gic_read_count_2x32(void)
{
unsigned int hi, hi2, lo;
- if (mips_cm_is64)
- return read_gic_counter();
-
do {
hi = read_gic_counter_32h();
lo = read_gic_counter_32l();
@@ -40,6 +41,19 @@ static u64 notrace gic_read_count(void)
return (((u64) hi) << 32) + lo;
}
+static u64 notrace gic_read_count_64(void)
+{
+ return read_gic_counter();
+}
+
+static u64 notrace gic_read_count(void)
+{
+ if (mips_cm_is64)
+ return gic_read_count_64();
+
+ return gic_read_count_2x32();
+}
+
static int gic_next_event(unsigned long delta, struct clock_event_device *evt)
{
int cpu = cpumask_first(evt->cpumask);
@@ -114,8 +128,10 @@ static int gic_clk_notifier(struct notifier_block *nb, unsigned long action,
{
struct clk_notifier_data *cnd = data;
- if (action == POST_RATE_CHANGE)
+ if (action == POST_RATE_CHANGE) {
+ gic_clocksource_unstable("ref clock rate change");
on_each_cpu(gic_update_frequency, (void *)cnd->new_rate, 1);
+ }
return NOTIFY_OK;
}
@@ -161,6 +177,18 @@ static struct clocksource gic_clocksource = {
.vdso_clock_mode = VDSO_CLOCKMODE_GIC,
};
+static void gic_clocksource_unstable(char *reason)
+{
+ if (gic_clock_unstable)
+ return;
+
+ gic_clock_unstable = true;
+
+ pr_info("GIC timer is unstable due to %s\n", reason);
+
+ clocksource_mark_unstable(&gic_clocksource);
+}
+
static int __init __gic_clocksource_init(void)
{
unsigned int count_width;
@@ -228,6 +256,18 @@ static int __init gic_clocksource_of_init(struct device_node *node)
/* And finally start the counter */
clear_gic_config(GIC_CONFIG_COUNTSTOP);
+ /*
+ * It's safe to use the MIPS GIC timer as a sched clock source only if
+ * its ticks are stable, which is true on either the platforms with
+ * 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)) {
+ sched_clock_register(mips_cm_is64 ?
+ gic_read_count_64 : gic_read_count_2x32,
+ 64, gic_frequency);
+ }
+
return 0;
}
TIMER_OF_DECLARE(mips_gic_timer, "mti,gic-timer",