Commit a7b36c2b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'timers-urgent-2020-07-25' of...

Merge tag 'timers-urgent-2020-07-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into master

Pull timer fix from Ingo Molnar:
 "Fix a suspend/resume regression (crash) on TI AM3/AM4 SoC's"

* tag 'timers-urgent-2020-07-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  clocksource/drivers/timer-ti-dm: Fix suspend and resume for am3 and am4
parents 3077805e b4a25fb0
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -2865,6 +2865,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 */ },
@@ -2921,6 +2939,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;
+36 −10
Original line number Diff line number Diff line
@@ -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;
	}