diff options
Diffstat (limited to 'drivers/clocksource/timer-ti-dm.c')
-rw-r--r-- | drivers/clocksource/timer-ti-dm.c | 97 |
1 files changed, 57 insertions, 40 deletions
diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c index fe939d1c0b38..1d1bea79cbf1 100644 --- a/drivers/clocksource/timer-ti-dm.c +++ b/drivers/clocksource/timer-ti-dm.c @@ -20,6 +20,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/cpu_pm.h> #include <linux/module.h> #include <linux/io.h> #include <linux/device.h> @@ -92,6 +93,47 @@ static void omap_timer_restore_context(struct omap_dm_timer *timer) timer->context.tclr); } +static void omap_timer_save_context(struct omap_dm_timer *timer) +{ + timer->context.tclr = + omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); + timer->context.twer = + omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG); + timer->context.tldr = + omap_dm_timer_read_reg(timer, OMAP_TIMER_LOAD_REG); + timer->context.tmar = + omap_dm_timer_read_reg(timer, OMAP_TIMER_MATCH_REG); + timer->context.tier = readl_relaxed(timer->irq_ena); + timer->context.tsicr = + omap_dm_timer_read_reg(timer, OMAP_TIMER_IF_CTRL_REG); +} + +static int omap_timer_context_notifier(struct notifier_block *nb, + unsigned long cmd, void *v) +{ + struct omap_dm_timer *timer; + + timer = container_of(nb, struct omap_dm_timer, nb); + + switch (cmd) { + case CPU_CLUSTER_PM_ENTER: + if ((timer->capability & OMAP_TIMER_ALWON) || + !atomic_read(&timer->enabled)) + break; + omap_timer_save_context(timer); + break; + case CPU_CLUSTER_PM_ENTER_FAILED: + case CPU_CLUSTER_PM_EXIT: + if ((timer->capability & OMAP_TIMER_ALWON) || + !atomic_read(&timer->enabled)) + break; + omap_timer_restore_context(timer); + break; + } + + return NOTIFY_OK; +} + static int omap_dm_timer_reset(struct omap_dm_timer *timer) { u32 l, timeout = 100000; @@ -208,21 +250,7 @@ static int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) static void omap_dm_timer_enable(struct omap_dm_timer *timer) { - int c; - pm_runtime_get_sync(&timer->pdev->dev); - - if (!(timer->capability & OMAP_TIMER_ALWON)) { - if (timer->get_context_loss_count) { - c = timer->get_context_loss_count(&timer->pdev->dev); - if (c != timer->ctx_loss_count) { - omap_timer_restore_context(timer); - timer->ctx_loss_count = c; - } - } else { - omap_timer_restore_context(timer); - } - } } static void omap_dm_timer_disable(struct omap_dm_timer *timer) @@ -515,8 +543,6 @@ static int omap_dm_timer_start(struct omap_dm_timer *timer) omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); } - /* Save the context */ - timer->context.tclr = l; return 0; } @@ -532,13 +558,6 @@ static int omap_dm_timer_stop(struct omap_dm_timer *timer) __omap_dm_timer_stop(timer, timer->posted, rate); - /* - * Since the register values are computed and written within - * __omap_dm_timer_stop, we need to use read to retrieve the - * context. - */ - timer->context.tclr = - omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); omap_dm_timer_disable(timer); return 0; } @@ -561,9 +580,6 @@ static int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load); omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); - /* Save the context */ - timer->context.tclr = l; - timer->context.tldr = load; omap_dm_timer_disable(timer); return 0; } @@ -585,9 +601,6 @@ static int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - /* Save the context */ - timer->context.tclr = l; - timer->context.tmar = match; omap_dm_timer_disable(timer); return 0; } @@ -611,8 +624,6 @@ static int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, l |= trigger << 10; omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - /* Save the context */ - timer->context.tclr = l; omap_dm_timer_disable(timer); return 0; } @@ -634,8 +645,6 @@ static int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, } omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); - /* Save the context */ - timer->context.tclr = l; omap_dm_timer_disable(timer); return 0; } @@ -649,9 +658,6 @@ static int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, omap_dm_timer_enable(timer); __omap_dm_timer_int_enable(timer, value); - /* Save the context */ - timer->context.tier = value; - timer->context.twer = value; omap_dm_timer_disable(timer); return 0; } @@ -679,9 +685,6 @@ static int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l); - /* Save the context */ - timer->context.tier &= ~mask; - timer->context.twer &= ~mask; omap_dm_timer_disable(timer); return 0; } @@ -756,6 +759,11 @@ static int __maybe_unused omap_dm_timer_runtime_suspend(struct device *dev) atomic_set(&timer->enabled, 0); + if (timer->capability & OMAP_TIMER_ALWON || !timer->func_base) + return 0; + + omap_timer_save_context(timer); + return 0; } @@ -763,6 +771,9 @@ static int __maybe_unused omap_dm_timer_runtime_resume(struct device *dev) { struct omap_dm_timer *timer = dev_get_drvdata(dev); + if (!(timer->capability & OMAP_TIMER_ALWON) && timer->func_base) + omap_timer_restore_context(timer); + atomic_set(&timer->enabled, 1); return 0; @@ -829,7 +840,11 @@ static int omap_dm_timer_probe(struct platform_device *pdev) timer->id = pdev->id; timer->capability = pdata->timer_capability; timer->reserved = omap_dm_timer_reserved_systimer(timer->id); - timer->get_context_loss_count = pdata->get_context_loss_count; + } + + if (!(timer->capability & OMAP_TIMER_ALWON)) { + timer->nb.notifier_call = omap_timer_context_notifier; + cpu_pm_register_notifier(&timer->nb); } if (pdata) @@ -883,6 +898,8 @@ static int omap_dm_timer_remove(struct platform_device *pdev) list_for_each_entry(timer, &omap_timer_list, node) if (!strcmp(dev_name(&timer->pdev->dev), dev_name(&pdev->dev))) { + if (!(timer->capability & OMAP_TIMER_ALWON)) + cpu_pm_unregister_notifier(&timer->nb); list_del(&timer->node); ret = 0; break; |