Linus,

Please pull the latest timers/urgent git tree from:

   git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git 
timers-urgent-2020-07-25

   # HEAD: b4a25fb0e62990df467451744b22e0e24960a5e6 Merge tag 'timers-v5.8-rc7' 
of https://git.linaro.org/people/daniel.lezcano/linux into timers/urgent

Fix a suspend/resume regression (crash) on TI AM3/AM4 SoC's.

 Thanks,

        Ingo

------------------>
Tony Lindgren (1):
      clocksource/drivers/timer-ti-dm: Fix suspend and resume for am3 and am4


 drivers/bus/ti-sysc.c                      | 22 ++++++++++++++
 drivers/clocksource/timer-ti-dm-systimer.c | 46 +++++++++++++++++++++++-------
 2 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c
index bb54fb514e40..c6427d0bc94c 100644
--- a/drivers/bus/ti-sysc.c
+++ b/drivers/bus/ti-sysc.c
@@ -2864,6 +2864,24 @@ static int sysc_check_disabled_devices(struct sysc 
*ddata)
        return error;
 }
 
+/*
+ * Ignore timers tagged with no-reset and no-idle. These are likely in use,
+ * for example by drivers/clocksource/timer-ti-dm-systimer.c. If more checks
+ * are needed, we could also look at the timer register configuration.
+ */
+static int sysc_check_active_timer(struct sysc *ddata)
+{
+       if (ddata->cap->type != TI_SYSC_OMAP2_TIMER &&
+           ddata->cap->type != TI_SYSC_OMAP4_TIMER)
+               return 0;
+
+       if ((ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) &&
+           (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE))
+               return -EBUSY;
+
+       return 0;
+}
+
 static const struct of_device_id sysc_match_table[] = {
        { .compatible = "simple-bus", },
        { /* sentinel */ },
@@ -2920,6 +2938,10 @@ static int sysc_probe(struct platform_device *pdev)
        if (error)
                return error;
 
+       error = sysc_check_active_timer(ddata);
+       if (error)
+               return error;
+
        error = sysc_get_clocks(ddata);
        if (error)
                return error;
diff --git a/drivers/clocksource/timer-ti-dm-systimer.c 
b/drivers/clocksource/timer-ti-dm-systimer.c
index 6fd1f219a512..f6fd1c1cc527 100644
--- a/drivers/clocksource/timer-ti-dm-systimer.c
+++ b/drivers/clocksource/timer-ti-dm-systimer.c
@@ -19,7 +19,7 @@
 /* For type1, set SYSC_OMAP2_CLOCKACTIVITY for fck off on idle, l4 clock on */
 #define DMTIMER_TYPE1_ENABLE   ((1 << 9) | (SYSC_IDLE_SMART << 3) | \
                                 SYSC_OMAP2_ENAWAKEUP | SYSC_OMAP2_AUTOIDLE)
-
+#define DMTIMER_TYPE1_DISABLE  (SYSC_OMAP2_SOFTRESET | SYSC_OMAP2_AUTOIDLE)
 #define DMTIMER_TYPE2_ENABLE   (SYSC_IDLE_SMART_WKUP << 2)
 #define DMTIMER_RESET_WAIT     100000
 
@@ -44,6 +44,8 @@ struct dmtimer_systimer {
        u8 ctrl;
        u8 wakeup;
        u8 ifctrl;
+       struct clk *fck;
+       struct clk *ick;
        unsigned long rate;
 };
 
@@ -298,16 +300,20 @@ static void __init dmtimer_systimer_select_best(void)
 }
 
 /* Interface clocks are only available on some SoCs variants */
-static int __init dmtimer_systimer_init_clock(struct device_node *np,
+static int __init dmtimer_systimer_init_clock(struct dmtimer_systimer *t,
+                                             struct device_node *np,
                                              const char *name,
                                              unsigned long *rate)
 {
        struct clk *clock;
        unsigned long r;
+       bool is_ick = false;
        int error;
 
+       is_ick = !strncmp(name, "ick", 3);
+
        clock = of_clk_get_by_name(np, name);
-       if ((PTR_ERR(clock) == -EINVAL) && !strncmp(name, "ick", 3))
+       if ((PTR_ERR(clock) == -EINVAL) && is_ick)
                return 0;
        else if (IS_ERR(clock))
                return PTR_ERR(clock);
@@ -320,6 +326,11 @@ static int __init dmtimer_systimer_init_clock(struct 
device_node *np,
        if (!r)
                return -ENODEV;
 
+       if (is_ick)
+               t->ick = clock;
+       else
+               t->fck = clock;
+
        *rate = r;
 
        return 0;
@@ -339,7 +350,10 @@ static void dmtimer_systimer_enable(struct 
dmtimer_systimer *t)
 
 static void dmtimer_systimer_disable(struct dmtimer_systimer *t)
 {
-       writel_relaxed(0, t->base + t->sysc);
+       if (!dmtimer_systimer_revision1(t))
+               return;
+
+       writel_relaxed(DMTIMER_TYPE1_DISABLE, t->base + t->sysc);
 }
 
 static int __init dmtimer_systimer_setup(struct device_node *np,
@@ -366,13 +380,13 @@ static int __init dmtimer_systimer_setup(struct 
device_node *np,
                pr_err("%s: clock source init failed: %i\n", __func__, error);
 
        /* For ti-sysc, we have timer clocks at the parent module level */
-       error = dmtimer_systimer_init_clock(np->parent, "fck", &rate);
+       error = dmtimer_systimer_init_clock(t, np->parent, "fck", &rate);
        if (error)
                goto err_unmap;
 
        t->rate = rate;
 
-       error = dmtimer_systimer_init_clock(np->parent, "ick", &rate);
+       error = dmtimer_systimer_init_clock(t, np->parent, "ick", &rate);
        if (error)
                goto err_unmap;
 
@@ -496,12 +510,18 @@ static void omap_clockevent_idle(struct 
clock_event_device *evt)
        struct dmtimer_systimer *t = &clkevt->t;
 
        dmtimer_systimer_disable(t);
+       clk_disable(t->fck);
 }
 
 static void omap_clockevent_unidle(struct clock_event_device *evt)
 {
        struct dmtimer_clockevent *clkevt = to_dmtimer_clockevent(evt);
        struct dmtimer_systimer *t = &clkevt->t;
+       int error;
+
+       error = clk_enable(t->fck);
+       if (error)
+               pr_err("could not enable timer fck on resume: %i\n", error);
 
        dmtimer_systimer_enable(t);
        writel_relaxed(OMAP_TIMER_INT_OVERFLOW, t->base + t->irq_ena);
@@ -570,8 +590,8 @@ static int __init dmtimer_clockevent_init(struct 
device_node *np)
                                        3, /* Timer internal resynch latency */
                                        0xffffffff);
 
-       if (of_device_is_compatible(np, "ti,am33xx") ||
-           of_device_is_compatible(np, "ti,am43")) {
+       if (of_machine_is_compatible("ti,am33xx") ||
+           of_machine_is_compatible("ti,am43")) {
                dev->suspend = omap_clockevent_idle;
                dev->resume = omap_clockevent_unidle;
        }
@@ -616,12 +636,18 @@ static void dmtimer_clocksource_suspend(struct 
clocksource *cs)
 
        clksrc->loadval = readl_relaxed(t->base + t->counter);
        dmtimer_systimer_disable(t);
+       clk_disable(t->fck);
 }
 
 static void dmtimer_clocksource_resume(struct clocksource *cs)
 {
        struct dmtimer_clocksource *clksrc = to_dmtimer_clocksource(cs);
        struct dmtimer_systimer *t = &clksrc->t;
+       int error;
+
+       error = clk_enable(t->fck);
+       if (error)
+               pr_err("could not enable timer fck on resume: %i\n", error);
 
        dmtimer_systimer_enable(t);
        writel_relaxed(clksrc->loadval, t->base + t->counter);
@@ -653,8 +679,8 @@ static int __init dmtimer_clocksource_init(struct 
device_node *np)
        dev->mask = CLOCKSOURCE_MASK(32);
        dev->flags = CLOCK_SOURCE_IS_CONTINUOUS;
 
-       if (of_device_is_compatible(np, "ti,am33xx") ||
-           of_device_is_compatible(np, "ti,am43")) {
+       /* Unlike for clockevent, legacy code sets suspend only for am4 */
+       if (of_machine_is_compatible("ti,am43")) {
                dev->suspend = dmtimer_clocksource_suspend;
                dev->resume = dmtimer_clocksource_resume;
        }

Reply via email to