aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig1
-rw-r--r--drivers/clocksource/asm9260_timer.c4
-rw-r--r--drivers/clocksource/hyperv_timer.c154
-rw-r--r--drivers/clocksource/renesas-ostm.c189
-rw-r--r--drivers/clocksource/samsung_pwm_timer.c3
-rw-r--r--drivers/clocksource/sh_cmt.c13
-rw-r--r--drivers/clocksource/sh_mtu2.c29
-rw-r--r--drivers/clocksource/sh_tmu.c14
-rw-r--r--drivers/clocksource/timer-mediatek.c10
-rw-r--r--drivers/clocksource/timer-of.c6
-rw-r--r--drivers/clocksource/timer-riscv.c31
11 files changed, 272 insertions, 182 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index f35a53ce8988..5fdd76cb1768 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -528,6 +528,7 @@ config SH_TIMER_MTU2
config RENESAS_OSTM
bool "Renesas OSTM timer driver" if COMPILE_TEST
select CLKSRC_MMIO
+ select TIMER_OF
help
Enables the support for the Renesas OSTM.
diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c
index 9f09a59161e7..5b39d3701fa3 100644
--- a/drivers/clocksource/asm9260_timer.c
+++ b/drivers/clocksource/asm9260_timer.c
@@ -194,6 +194,10 @@ static int __init asm9260_timer_init(struct device_node *np)
}
clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ pr_err("Failed to get clk!\n");
+ return PTR_ERR(clk);
+ }
ret = clk_prepare_enable(clk);
if (ret) {
diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c
index 2317d4e3daaf..287d8d58c21a 100644
--- a/drivers/clocksource/hyperv_timer.c
+++ b/drivers/clocksource/hyperv_timer.c
@@ -17,6 +17,7 @@
#include <linux/clocksource.h>
#include <linux/sched_clock.h>
#include <linux/mm.h>
+#include <linux/cpuhotplug.h>
#include <clocksource/hyperv_timer.h>
#include <asm/hyperv-tlfs.h>
#include <asm/mshyperv.h>
@@ -30,6 +31,15 @@ static u64 hv_sched_clock_offset __ro_after_init;
* mechanism is used when running on older versions of Hyper-V
* that don't support Direct Mode. While Hyper-V provides
* four stimer's per CPU, Linux uses only stimer0.
+ *
+ * Because Direct Mode does not require processing a VMbus
+ * message, stimer interrupts can be enabled earlier in the
+ * process of booting a CPU, and consistent with when timer
+ * interrupts are enabled for other clocksource drivers.
+ * However, for legacy versions of Hyper-V when Direct Mode
+ * is not enabled, setting up stimer interrupts must be
+ * delayed until VMbus is initialized and can process the
+ * interrupt message.
*/
static bool direct_mode_enabled;
@@ -102,17 +112,12 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
/*
* hv_stimer_init - Per-cpu initialization of the clockevent
*/
-void hv_stimer_init(unsigned int cpu)
+static int hv_stimer_init(unsigned int cpu)
{
struct clock_event_device *ce;
- /*
- * Synthetic timers are always available except on old versions of
- * Hyper-V on x86. In that case, just return as Linux will use a
- * clocksource based on emulated PIT or LAPIC timer hardware.
- */
- if (!(ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE))
- return;
+ if (!hv_clock_event)
+ return 0;
ce = per_cpu_ptr(hv_clock_event, cpu);
ce->name = "Hyper-V clockevent";
@@ -127,28 +132,55 @@ void hv_stimer_init(unsigned int cpu)
HV_CLOCK_HZ,
HV_MIN_DELTA_TICKS,
HV_MAX_MAX_DELTA_TICKS);
+ return 0;
}
-EXPORT_SYMBOL_GPL(hv_stimer_init);
/*
* hv_stimer_cleanup - Per-cpu cleanup of the clockevent
*/
-void hv_stimer_cleanup(unsigned int cpu)
+int hv_stimer_cleanup(unsigned int cpu)
{
struct clock_event_device *ce;
- /* Turn off clockevent device */
- if (ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE) {
- ce = per_cpu_ptr(hv_clock_event, cpu);
+ if (!hv_clock_event)
+ return 0;
+
+ /*
+ * In the legacy case where Direct Mode is not enabled
+ * (which can only be on x86/64), stimer cleanup happens
+ * relatively early in the CPU offlining process. We
+ * must unbind the stimer-based clockevent device so
+ * that the LAPIC timer can take over until clockevents
+ * are no longer needed in the offlining process. Note
+ * that clockevents_unbind_device() eventually calls
+ * hv_ce_shutdown().
+ *
+ * The unbind should not be done when Direct Mode is
+ * enabled because we may be on an architecture where
+ * there are no other clockevent devices to fallback to.
+ */
+ ce = per_cpu_ptr(hv_clock_event, cpu);
+ if (direct_mode_enabled)
hv_ce_shutdown(ce);
- }
+ else
+ clockevents_unbind_device(ce, cpu);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(hv_stimer_cleanup);
/* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */
-int hv_stimer_alloc(int sint)
+int hv_stimer_alloc(void)
{
- int ret;
+ int ret = 0;
+
+ /*
+ * Synthetic timers are always available except on old versions of
+ * Hyper-V on x86. In that case, return as error as Linux will use a
+ * clockevent based on emulated LAPIC timer hardware.
+ */
+ if (!(ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE))
+ return -EINVAL;
hv_clock_event = alloc_percpu(struct clock_event_device);
if (!hv_clock_event)
@@ -159,22 +191,78 @@ int hv_stimer_alloc(int sint)
if (direct_mode_enabled) {
ret = hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector,
hv_stimer0_isr);
- if (ret) {
- free_percpu(hv_clock_event);
- hv_clock_event = NULL;
- return ret;
- }
+ if (ret)
+ goto free_percpu;
+
+ /*
+ * Since we are in Direct Mode, stimer initialization
+ * can be done now with a CPUHP value in the same range
+ * as other clockevent devices.
+ */
+ ret = cpuhp_setup_state(CPUHP_AP_HYPERV_TIMER_STARTING,
+ "clockevents/hyperv/stimer:starting",
+ hv_stimer_init, hv_stimer_cleanup);
+ if (ret < 0)
+ goto free_stimer0_irq;
}
+ return ret;
- stimer0_message_sint = sint;
- return 0;
+free_stimer0_irq:
+ hv_remove_stimer0_irq(stimer0_irq);
+ stimer0_irq = 0;
+free_percpu:
+ free_percpu(hv_clock_event);
+ hv_clock_event = NULL;
+ return ret;
}
EXPORT_SYMBOL_GPL(hv_stimer_alloc);
+/*
+ * hv_stimer_legacy_init -- Called from the VMbus driver to handle
+ * the case when Direct Mode is not enabled, and the stimer
+ * must be initialized late in the CPU onlining process.
+ *
+ */
+void hv_stimer_legacy_init(unsigned int cpu, int sint)
+{
+ if (direct_mode_enabled)
+ return;
+
+ /*
+ * This function gets called by each vCPU, so setting the
+ * global stimer_message_sint value each time is conceptually
+ * not ideal, but the value passed in is always the same and
+ * it avoids introducing yet another interface into this
+ * clocksource driver just to set the sint in the legacy case.
+ */
+ stimer0_message_sint = sint;
+ (void)hv_stimer_init(cpu);
+}
+EXPORT_SYMBOL_GPL(hv_stimer_legacy_init);
+
+/*
+ * hv_stimer_legacy_cleanup -- Called from the VMbus driver to
+ * handle the case when Direct Mode is not enabled, and the
+ * stimer must be cleaned up early in the CPU offlining
+ * process.
+ */
+void hv_stimer_legacy_cleanup(unsigned int cpu)
+{
+ if (direct_mode_enabled)
+ return;
+ (void)hv_stimer_cleanup(cpu);
+}
+EXPORT_SYMBOL_GPL(hv_stimer_legacy_cleanup);
+
+
/* hv_stimer_free - Free global resources allocated by hv_stimer_alloc() */
void hv_stimer_free(void)
{
- if (direct_mode_enabled && (stimer0_irq != 0)) {
+ if (!hv_clock_event)
+ return;
+
+ if (direct_mode_enabled) {
+ cpuhp_remove_state(CPUHP_AP_HYPERV_TIMER_STARTING);
hv_remove_stimer0_irq(stimer0_irq);
stimer0_irq = 0;
}
@@ -190,14 +278,20 @@ EXPORT_SYMBOL_GPL(hv_stimer_free);
void hv_stimer_global_cleanup(void)
{
int cpu;
- struct clock_event_device *ce;
- if (ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE) {
- for_each_present_cpu(cpu) {
- ce = per_cpu_ptr(hv_clock_event, cpu);
- clockevents_unbind_device(ce, cpu);
- }
+ /*
+ * hv_stime_legacy_cleanup() will stop the stimer if Direct
+ * Mode is not enabled, and fallback to the LAPIC timer.
+ */
+ for_each_present_cpu(cpu) {
+ hv_stimer_legacy_cleanup(cpu);
}
+
+ /*
+ * If Direct Mode is enabled, the cpuhp teardown callback
+ * (hv_stimer_cleanup) will be run on all CPUs to stop the
+ * stimers.
+ */
hv_stimer_free();
}
EXPORT_SYMBOL_GPL(hv_stimer_global_cleanup);
diff --git a/drivers/clocksource/renesas-ostm.c b/drivers/clocksource/renesas-ostm.c
index 37c39b901bb1..3d06ba66008c 100644
--- a/drivers/clocksource/renesas-ostm.c
+++ b/drivers/clocksource/renesas-ostm.c
@@ -6,14 +6,14 @@
* Copyright (C) 2017 Chris Brandt
*/
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/sched_clock.h>
#include <linux/slab.h>
+#include "timer-of.h"
+
/*
* The OSTM contains independent channels.
* The first OSTM channel probed will be set up as a free running
@@ -24,12 +24,6 @@
* driven clock event.
*/
-struct ostm_device {
- void __iomem *base;
- unsigned long ticks_per_jiffy;
- struct clock_event_device ced;
-};
-
static void __iomem *system_clock; /* For sched_clock() */
/* OSTM REGISTERS */
@@ -47,41 +41,32 @@ static void __iomem *system_clock; /* For sched_clock() */
#define CTL_ONESHOT 0x02
#define CTL_FREERUN 0x02
-static struct ostm_device *ced_to_ostm(struct clock_event_device *ced)
-{
- return container_of(ced, struct ostm_device, ced);
-}
-
-static void ostm_timer_stop(struct ostm_device *ostm)
+static void ostm_timer_stop(struct timer_of *to)
{
- if (readb(ostm->base + OSTM_TE) & TE) {
- writeb(TT, ostm->base + OSTM_TT);
+ if (readb(timer_of_base(to) + OSTM_TE) & TE) {
+ writeb(TT, timer_of_base(to) + OSTM_TT);
/*
* Read back the register simply to confirm the write operation
* has completed since I/O writes can sometimes get queued by
* the bus architecture.
*/
- while (readb(ostm->base + OSTM_TE) & TE)
+ while (readb(timer_of_base(to) + OSTM_TE) & TE)
;
}
}
-static int __init ostm_init_clksrc(struct ostm_device *ostm, unsigned long rate)
+static int __init ostm_init_clksrc(struct timer_of *to)
{
- /*
- * irq not used (clock sources don't use interrupts)
- */
-
- ostm_timer_stop(ostm);
+ ostm_timer_stop(to);
- writel(0, ostm->base + OSTM_CMP);
- writeb(CTL_FREERUN, ostm->base + OSTM_CTL);
- writeb(TS, ostm->base + OSTM_TS);
+ writel(0, timer_of_base(to) + OSTM_CMP);
+ writeb(CTL_FREERUN, timer_of_base(to) + OSTM_CTL);
+ writeb(TS, timer_of_base(to) + OSTM_TS);
- return clocksource_mmio_init(ostm->base + OSTM_CNT,
- "ostm", rate,
- 300, 32, clocksource_mmio_readl_up);
+ return clocksource_mmio_init(timer_of_base(to) + OSTM_CNT,
+ to->np->full_name, timer_of_rate(to), 300,
+ 32, clocksource_mmio_readl_up);
}
static u64 notrace ostm_read_sched_clock(void)
@@ -89,87 +74,75 @@ static u64 notrace ostm_read_sched_clock(void)
return readl(system_clock);
}
-static void __init ostm_init_sched_clock(struct ostm_device *ostm,
- unsigned long rate)
+static void __init ostm_init_sched_clock(struct timer_of *to)
{
- system_clock = ostm->base + OSTM_CNT;
- sched_clock_register(ostm_read_sched_clock, 32, rate);
+ system_clock = timer_of_base(to) + OSTM_CNT;
+ sched_clock_register(ostm_read_sched_clock, 32, timer_of_rate(to));
}
static int ostm_clock_event_next(unsigned long delta,
- struct clock_event_device *ced)
+ struct clock_event_device *ced)
{
- struct ostm_device *ostm = ced_to_ostm(ced);
+ struct timer_of *to = to_timer_of(ced);
- ostm_timer_stop(ostm);
+ ostm_timer_stop(to);
- writel(delta, ostm->base + OSTM_CMP);
- writeb(CTL_ONESHOT, ostm->base + OSTM_CTL);
- writeb(TS, ostm->base + OSTM_TS);
+ writel(delta, timer_of_base(to) + OSTM_CMP);
+ writeb(CTL_ONESHOT, timer_of_base(to) + OSTM_CTL);
+ writeb(TS, timer_of_base(to) + OSTM_TS);
return 0;
}
static int ostm_shutdown(struct clock_event_device *ced)
{
- struct ostm_device *ostm = ced_to_ostm(ced);
+ struct timer_of *to = to_timer_of(ced);
- ostm_timer_stop(ostm);
+ ostm_timer_stop(to);
return 0;
}
static int ostm_set_periodic(struct clock_event_device *ced)
{
- struct ostm_device *ostm = ced_to_ostm(ced);
+ struct timer_of *to = to_timer_of(ced);
if (clockevent_state_oneshot(ced) || clockevent_state_periodic(ced))
- ostm_timer_stop(ostm);
+ ostm_timer_stop(to);
- writel(ostm->ticks_per_jiffy - 1, ostm->base + OSTM_CMP);
- writeb(CTL_PERIODIC, ostm->base + OSTM_CTL);
- writeb(TS, ostm->base + OSTM_TS);
+ writel(timer_of_period(to) - 1, timer_of_base(to) + OSTM_CMP);
+ writeb(CTL_PERIODIC, timer_of_base(to) + OSTM_CTL);
+ writeb(TS, timer_of_base(to) + OSTM_TS);
return 0;
}
static int ostm_set_oneshot(struct clock_event_device *ced)
{
- struct ostm_device *ostm = ced_to_ostm(ced);
+ struct timer_of *to = to_timer_of(ced);
- ostm_timer_stop(ostm);
+ ostm_timer_stop(to);
return 0;
}
static irqreturn_t ostm_timer_interrupt(int irq, void *dev_id)
{
- struct ostm_device *ostm = dev_id;
+ struct clock_event_device *ced = dev_id;
- if (clockevent_state_oneshot(&ostm->ced))
- ostm_timer_stop(ostm);
+ if (clockevent_state_oneshot(ced))
+ ostm_timer_stop(to_timer_of(ced));
/* notify clockevent layer */
- if (ostm->ced.event_handler)
- ostm->ced.event_handler(&ostm->ced);
+ if (ced->event_handler)
+ ced->event_handler(ced);
return IRQ_HANDLED;
}
-static int __init ostm_init_clkevt(struct ostm_device *ostm, int irq,
- unsigned long rate)
+static int __init ostm_init_clkevt(struct timer_of *to)
{
- struct clock_event_device *ced = &ostm->ced;
- int ret = -ENXIO;
-
- ret = request_irq(irq, ostm_timer_interrupt,
- IRQF_TIMER | IRQF_IRQPOLL,
- "ostm", ostm);
- if (ret) {
- pr_err("ostm: failed to request irq\n");
- return ret;
- }
+ struct clock_event_device *ced = &to->clkevt;
- ced->name = "ostm";
ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC;
ced->set_state_shutdown = ostm_shutdown;
ced->set_state_periodic = ostm_set_periodic;
@@ -178,79 +151,61 @@ static int __init ostm_init_clkevt(struct ostm_device *ostm, int irq,
ced->shift = 32;
ced->rating = 300;
ced->cpumask = cpumask_of(0);
- clockevents_config_and_register(ced, rate, 0xf, 0xffffffff);
+ clockevents_config_and_register(ced, timer_of_rate(to), 0xf,
+ 0xffffffff);
return 0;
}
static int __init ostm_init(struct device_node *np)
{
- struct ostm_device *ostm;
- int ret = -EFAULT;
- struct clk *ostm_clk = NULL;
- int irq;
- unsigned long rate;
-
- ostm = kzalloc(sizeof(*ostm), GFP_KERNEL);
- if (!ostm)
- return -ENOMEM;
-
- ostm->base = of_iomap(np, 0);
- if (!ostm->base) {
- pr_err("ostm: failed to remap I/O memory\n");
- goto err;
- }
-
- irq = irq_of_parse_and_map(np, 0);
- if (irq < 0) {
- pr_err("ostm: Failed to get irq\n");
- goto err;
- }
+ struct timer_of *to;
+ int ret;
- ostm_clk = of_clk_get(np, 0);
- if (IS_ERR(ostm_clk)) {
- pr_err("ostm: Failed to get clock\n");
- ostm_clk = NULL;
- goto err;
- }
+ to = kzalloc(sizeof(*to), GFP_KERNEL);
+ if (!to)
+ return -ENOMEM;
- ret = clk_prepare_enable(ostm_clk);
- if (ret) {
- pr_err("ostm: Failed to enable clock\n");
- goto err;
+ to->flags = TIMER_OF_BASE | TIMER_OF_CLOCK;
+ if (system_clock) {
+ /*
+ * clock sources don't use interrupts, clock events do
+ */
+ to->flags |= TIMER_OF_IRQ;
+ to->of_irq.flags = IRQF_TIMER | IRQF_IRQPOLL;
+ to->of_irq.handler = ostm_timer_interrupt;
}
- rate = clk_get_rate(ostm_clk);
- ostm->ticks_per_jiffy = DIV_ROUND_CLOSEST(rate, HZ);
+ ret = timer_of_init(np, to);
+ if (ret)
+ goto err_free;
/*
* First probed device will be used as system clocksource. Any
* additional devices will be used as clock events.
*/
if (!system_clock) {
- ret = ostm_init_clksrc(ostm, rate);
-
- if (!ret) {
- ostm_init_sched_clock(ostm, rate);
- pr_info("ostm: used for clocksource\n");
- }
+ ret = ostm_init_clksrc(to);
+ if (ret)
+ goto err_cleanup;
+ ostm_init_sched_clock(to);
+ pr_info("%pOF: used for clocksource\n", np);
} else {
- ret = ostm_init_clkevt(ostm, irq, rate);
+ ret = ostm_init_clkevt(to);
+ if (ret)
+ goto err_cleanup;
- if (!ret)
- pr_info("ostm: used for clock events\n");
- }
-
-err:
- if (ret) {
- clk_disable_unprepare(ostm_clk);
- iounmap(ostm->base);
- kfree(ostm);
- return ret;
+ pr_info("%pOF: used for clock events\n", np);
}
return 0;
+
+err_cleanup:
+ timer_of_cleanup(to);
+err_free:
+ kfree(to);
+ return ret;
}
TIMER_OF_DECLARE(ostm, "renesas,ostm", ostm_init);
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
index 895f53eb5771..dae1b2b5a0c5 100644
--- a/drivers/clocksource/samsung_pwm_timer.c
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -430,8 +430,7 @@ static int __init samsung_pwm_alloc(struct device_node *np,
of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) {
if (val >= SAMSUNG_PWM_NUM) {
- pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
- __func__);
+ pr_warn("%s: invalid channel index in samsung,pwm-outputs property\n", __func__);
continue;
}
pwm.variant.output_mask |= 1 << val;
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index ef773db080e9..9cde50cb3220 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -25,6 +25,10 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#ifdef CONFIG_SUPERH
+#include <asm/platform_early.h>
+#endif
+
struct sh_cmt_device;
/*
@@ -1052,7 +1056,7 @@ static int sh_cmt_probe(struct platform_device *pdev)
struct sh_cmt_device *cmt = platform_get_drvdata(pdev);
int ret;
- if (!is_early_platform_device(pdev)) {
+ if (!is_sh_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
@@ -1072,7 +1076,7 @@ static int sh_cmt_probe(struct platform_device *pdev)
pm_runtime_idle(&pdev->dev);
return ret;
}
- if (is_early_platform_device(pdev))
+ if (is_sh_early_platform_device(pdev))
return 0;
out:
@@ -1109,7 +1113,10 @@ static void __exit sh_cmt_exit(void)
platform_driver_unregister(&sh_cmt_device_driver);
}
-early_platform_init("earlytimer", &sh_cmt_device_driver);
+#ifdef CONFIG_SUPERH
+sh_early_platform_init("earlytimer", &sh_cmt_device_driver);
+#endif
+
subsys_initcall(sh_cmt_init);
module_exit(sh_cmt_exit);
diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c
index 354b27d14a19..64526e50d471 100644
--- a/drivers/clocksource/sh_mtu2.c
+++ b/drivers/clocksource/sh_mtu2.c
@@ -23,6 +23,10 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#ifdef CONFIG_SUPERH
+#include <asm/platform_early.h>
+#endif
+
struct sh_mtu2_device;
struct sh_mtu2_channel {
@@ -328,12 +332,13 @@ static int sh_mtu2_register(struct sh_mtu2_channel *ch, const char *name)
return 0;
}
+static const unsigned int sh_mtu2_channel_offsets[] = {
+ 0x300, 0x380, 0x000,
+};
+
static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
struct sh_mtu2_device *mtu)
{
- static const unsigned int channel_offsets[] = {
- 0x300, 0x380, 0x000,
- };
char name[6];
int irq;
int ret;
@@ -356,7 +361,7 @@ static int sh_mtu2_setup_channel(struct sh_mtu2_channel *ch, unsigned int index,
return ret;
}
- ch->base = mtu->mapbase + channel_offsets[index];
+ ch->base = mtu->mapbase + sh_mtu2_channel_offsets[index];
ch->index = index;
return sh_mtu2_register(ch, dev_name(&mtu->pdev->dev));
@@ -408,7 +413,12 @@ static int sh_mtu2_setup(struct sh_mtu2_device *mtu,
}
/* Allocate and setup the channels. */
- mtu->num_channels = 3;
+ ret = platform_irq_count(pdev);
+ if (ret < 0)
+ goto err_unmap;
+
+ mtu->num_channels = min_t(unsigned int, ret,
+ ARRAY_SIZE(sh_mtu2_channel_offsets));
mtu->channels = kcalloc(mtu->num_channels, sizeof(*mtu->channels),
GFP_KERNEL);
@@ -442,7 +452,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
struct sh_mtu2_device *mtu = platform_get_drvdata(pdev);
int ret;
- if (!is_early_platform_device(pdev)) {
+ if (!is_sh_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
@@ -462,7 +472,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
pm_runtime_idle(&pdev->dev);
return ret;
}
- if (is_early_platform_device(pdev))
+ if (is_sh_early_platform_device(pdev))
return 0;
out:
@@ -511,7 +521,10 @@ static void __exit sh_mtu2_exit(void)
platform_driver_unregister(&sh_mtu2_device_driver);
}
-early_platform_init("earlytimer", &sh_mtu2_device_driver);
+#ifdef CONFIG_SUPERH
+sh_early_platform_init("earlytimer", &sh_mtu2_device_driver);
+#endif
+
subsys_initcall(sh_mtu2_init);
module_exit(sh_mtu2_exit);
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c
index 8c4f3753b36e..d49690d15536 100644
--- a/drivers/clocksource/sh_tmu.c
+++ b/drivers/clocksource/sh_tmu.c
@@ -24,6 +24,10 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#ifdef CONFIG_SUPERH
+#include <asm/platform_early.h>
+#endif
+
enum sh_tmu_model {
SH_TMU,
SH_TMU_SH3,
@@ -595,7 +599,7 @@ static int sh_tmu_probe(struct platform_device *pdev)
struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
int ret;
- if (!is_early_platform_device(pdev)) {
+ if (!is_sh_early_platform_device(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
@@ -615,7 +619,8 @@ static int sh_tmu_probe(struct platform_device *pdev)
pm_runtime_idle(&pdev->dev);
return ret;
}
- if (is_early_platform_device(pdev))
+
+ if (is_sh_early_platform_device(pdev))
return 0;
out:
@@ -665,7 +670,10 @@ static void __exit sh_tmu_exit(void)
platform_driver_unregister(&sh_tmu_device_driver);
}
-early_platform_init("earlytimer", &sh_tmu_device_driver);
+#ifdef CONFIG_SUPERH
+sh_early_platform_init("earlytimer", &sh_tmu_device_driver);
+#endif
+
subsys_initcall(sh_tmu_init);
module_exit(sh_tmu_exit);
diff --git a/drivers/clocksource/timer-mediatek.c b/drivers/clocksource/timer-mediatek.c
index a562f491b0f8..9318edcd8963 100644
--- a/drivers/clocksource/timer-mediatek.c
+++ b/drivers/clocksource/timer-mediatek.c
@@ -268,15 +268,12 @@ static int __init mtk_syst_init(struct device_node *node)
ret = timer_of_init(node, &to);
if (ret)
- goto err;
+ return ret;
clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
TIMER_SYNC_TICKS, 0xffffffff);
return 0;
-err:
- timer_of_cleanup(&to);
- return ret;
}
static int __init mtk_gpt_init(struct device_node *node)
@@ -293,7 +290,7 @@ static int __init mtk_gpt_init(struct device_node *node)
ret = timer_of_init(node, &to);
if (ret)
- goto err;
+ return ret;
/* Configure clock source */
mtk_gpt_setup(&to, TIMER_CLK_SRC, GPT_CTRL_OP_FREERUN);
@@ -311,9 +308,6 @@ static int __init mtk_gpt_init(struct device_node *node)
mtk_gpt_enable_irq(&to, TIMER_CLK_EVT);
return 0;
-err:
- timer_of_cleanup(&to);
- return ret;
}
TIMER_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_gpt_init);
TIMER_OF_DECLARE(mtk_mt6765, "mediatek,mt6765-timer", mtk_syst_init);
diff --git a/drivers/clocksource/timer-of.c b/drivers/clocksource/timer-of.c
index 11ff701ff4bb..572da477c6d3 100644
--- a/drivers/clocksource/timer-of.c
+++ b/drivers/clocksource/timer-of.c
@@ -57,8 +57,8 @@ static __init int timer_of_irq_init(struct device_node *np,
if (of_irq->name) {
of_irq->irq = ret = of_irq_get_byname(np, of_irq->name);
if (ret < 0) {
- pr_err("Failed to get interrupt %s for %s\n",
- of_irq->name, np->full_name);
+ pr_err("Failed to get interrupt %s for %pOF\n",
+ of_irq->name, np);
return ret;
}
} else {
@@ -192,7 +192,7 @@ int __init timer_of_init(struct device_node *np, struct timer_of *to)
}
if (!to->clkevt.name)
- to->clkevt.name = np->name;
+ to->clkevt.name = np->full_name;
to->np = np;
diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c
index 470c7ef02ea4..4e54856ce2a5 100644
--- a/drivers/clocksource/timer-riscv.c
+++ b/drivers/clocksource/timer-riscv.c
@@ -3,9 +3,9 @@
* Copyright (C) 2012 Regents of the University of California
* Copyright (C) 2017 SiFive
*
- * All RISC-V systems have a timer attached to every hart. These timers can be
- * read from the "time" and "timeh" CSRs, and can use the SBI to setup
- * events.
+ * All RISC-V systems have a timer attached to every hart. These timers can
+ * either be read from the "time" and "timeh" CSRs, and can use the SBI to
+ * setup events, or directly accessed using MMIO registers.
*/
#include <linux/clocksource.h>
#include <linux/clockchips.h>
@@ -13,14 +13,29 @@
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/sched_clock.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <asm/smp.h>
#include <asm/sbi.h>
+u64 __iomem *riscv_time_cmp;
+u64 __iomem *riscv_time_val;
+
+static inline void mmio_set_timer(u64 val)
+{
+ void __iomem *r;
+
+ r = riscv_time_cmp + cpuid_to_hartid_map(smp_processor_id());
+ writeq_relaxed(val, r);
+}
+
static int riscv_clock_next_event(unsigned long delta,
struct clock_event_device *ce)
{
- csr_set(sie, SIE_STIE);
- sbi_set_timer(get_cycles64() + delta);
+ csr_set(CSR_IE, IE_TIE);
+ if (IS_ENABLED(CONFIG_RISCV_SBI))
+ sbi_set_timer(get_cycles64() + delta);
+ else
+ mmio_set_timer(get_cycles64() + delta);
return 0;
}
@@ -61,13 +76,13 @@ static int riscv_timer_starting_cpu(unsigned int cpu)
ce->cpumask = cpumask_of(cpu);
clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff);
- csr_set(sie, SIE_STIE);
+ csr_set(CSR_IE, IE_TIE);
return 0;
}
static int riscv_timer_dying_cpu(unsigned int cpu)
{
- csr_clear(sie, SIE_STIE);
+ csr_clear(CSR_IE, IE_TIE);
return 0;
}
@@ -76,7 +91,7 @@ void riscv_timer_interrupt(void)
{
struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event);
- csr_clear(sie, SIE_STIE);
+ csr_clear(CSR_IE, IE_TIE);
evdev->event_handler(evdev);
}