mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
Fix a suspend/resume regression (crash) on TI AM3/AM4 SoC's.
Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAl8cDeIRHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1gVeA/8Cs67mJ9j3oto917z1GLElBVy7A+od/Pn TqWEtwmonJA/ZM/TvuvfFxHFg6kgPlDx0x5pHYUows/8DGsftdveDQw4bGhrCN0i Xghcn4gB+jdOaykpLQacEukYt/nPU0IOf79XqySiu774lWHvzwQI+2MIb7xBHBMO npUXFzgeSlgjFkMm1lVa9QVPwVc1zQ6gKPJdLpDlrgW3cCrenM7QCAg3NG3Gx5TT OXK4QZEvIuym+HWwbZRA2mOUSjm8U1Z1o95jiWUro7o5Xe+Agqi5zsLJc7lxX57+ LYwrXNx/zPZ+oBlzZhZZrAxn1bJP7DVS13ZCEL9Miy0Uslr7QBPyoGHiV4P8tL3v cOn7kY+P7YT96Fx3ahkK3pJN+nUZIAsRrwZbgOP4bZHKdVi3zqUEilSdZNcXBZ9y nj9mY/2pX9SOMIvoaosku2hkKs5EKIkh3qzhvbfA8fXsQC3lttrPGRW7hgPmxm7W Fa+6liOi+t+d0rGgmi1fhHSn/L2gzmsLy5R1l6XyK15nDSALd4TrdtDrlJ8vbbl5 oRAN9mt9qPqNCQQKjcnAIXuPeJp2Pt6KphlPApHVa3J/eVzug1dFWyjia/e+js8W Gejz1TV6SDdU137TmC4E6suAZ5nRSEBwiE+MiNnnG0DIvz7cp5FSIWCXOgt5WPl5 BXS4CSwnF24= =7P/h -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
a7b36c2b13
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user