[PATCH v16 09/12] OMAP: dmtimer: low-power mode support
DebBarma, Tarun Kanti
tarun.kanti at ti.com
Thu Sep 22 02:07:35 EDT 2011
On Thu, Sep 22, 2011 at 6:30 AM, Tony Lindgren <tony at atomide.com> wrote:
> * Tarun Kanti DebBarma <tarun.kanti at ti.com> [110920 03:57]:
>> Clock is enabled only when timer is started and disabled when the the timer
>> is stopped. Therefore before accessing registers in functions clock is enabled
>> and then disabled back at the end of access. Context save is done dynamically
>> whenever the registers are modified. Context restore is called when context is
>> lost.
>
> I've updated this to use revision instead of tidr. Updated patch below.
Ok.
--
Tarun
>
> Regards,
>
> Tony
>
>
> From: Tarun Kanti DebBarma <tarun.kanti at ti.com>
> Date: Tue, 20 Sep 2011 17:00:24 +0530
> Subject: [PATCH] ARM: OMAP: dmtimer: low-power mode support
>
> Clock is enabled only when timer is started and disabled when the the timer
> is stopped. Therefore before accessing registers in functions clock is enabled
> and then disabled back at the end of access. Context save is done dynamically
> whenever the registers are modified. Context restore is called when context is
> lost.
>
> Signed-off-by: Tarun Kanti DebBarma <tarun.kanti at ti.com>
> Reviewed-by: Santosh Shilimkar <santosh.shilimkar at ti.com>
> [tony at atomide.com: updated to use revision instead of tidr]
> Signed-off-by: Tony Lindgren <tony at atomide.com>
>
> diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
> index f1e3ec1..1140e98 100644
> --- a/arch/arm/mach-omap2/timer.c
> +++ b/arch/arm/mach-omap2/timer.c
> @@ -44,6 +44,9 @@
> #include <plat/common.h>
> #include <plat/omap_hwmod.h>
> #include <plat/omap_device.h>
> +#include <plat/omap-pm.h>
> +
> +#include "powerdomain.h"
>
> /* Parent clocks, eventually these will come from the clock framework */
>
> @@ -433,6 +436,7 @@ static int __init omap_timer_init(struct omap_hwmod *oh, void *unused)
> struct dmtimer_platform_data *pdata;
> struct omap_device *od;
> struct omap_timer_capability_dev_attr *timer_dev_attr;
> + struct powerdomain *pwrdm;
>
> pr_debug("%s: %s\n", __func__, oh->name);
>
> @@ -467,6 +471,11 @@ static int __init omap_timer_init(struct omap_hwmod *oh, void *unused)
> if ((sys_timer_reserved >> (id - 1)) & 0x1)
> pdata->reserved = 1;
>
> + pwrdm = omap_hwmod_get_pwrdm(oh);
> + pdata->loses_context = pwrdm_can_ever_lose_context(pwrdm);
> +#ifdef CONFIG_PM
> + pdata->get_context_loss_count = omap_pm_get_dev_context_loss_count;
> +#endif
> od = omap_device_build(name, id, oh, pdata, sizeof(*pdata),
> omap2_dmtimer_latency,
> ARRAY_SIZE(omap2_dmtimer_latency),
> diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
> index c8df3c3..43eb750 100644
> --- a/arch/arm/plat-omap/dmtimer.c
> +++ b/arch/arm/plat-omap/dmtimer.c
> @@ -77,6 +77,29 @@ static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
> __omap_dm_timer_write(timer, reg, value, timer->posted);
> }
>
> +static void omap_timer_restore_context(struct omap_dm_timer *timer)
> +{
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_OFFSET,
> + timer->context.tiocp_cfg);
> + if (timer->revision > 1)
> + __raw_writel(timer->context.tistat, timer->sys_stat);
> +
> + __raw_writel(timer->context.tisr, timer->irq_stat);
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,
> + timer->context.twer);
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG,
> + timer->context.tcrr);
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG,
> + timer->context.tldr);
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG,
> + timer->context.tmar);
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
> + timer->context.tsicr);
> + __raw_writel(timer->context.tier, timer->irq_ena);
> + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG,
> + timer->context.tclr);
> +}
> +
> static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
> {
> int c;
> @@ -96,12 +119,14 @@ static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
>
> static void omap_dm_timer_reset(struct omap_dm_timer *timer)
> {
> + omap_dm_timer_enable(timer);
> if (timer->pdev->id != 1) {
> omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
> omap_dm_timer_wait_for_reset(timer);
> }
>
> __omap_dm_timer_reset(timer, 0, 0);
> + omap_dm_timer_disable(timer);
> timer->posted = 1;
> }
>
> @@ -117,8 +142,6 @@ int omap_dm_timer_prepare(struct omap_dm_timer *timer)
> return -EINVAL;
> }
>
> - omap_dm_timer_enable(timer);
> -
> if (pdata->needs_manual_reset)
> omap_dm_timer_reset(timer);
>
> @@ -193,7 +216,6 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific);
>
> void omap_dm_timer_free(struct omap_dm_timer *timer)
> {
> - omap_dm_timer_disable(timer);
> clk_put(timer->fclk);
>
> WARN_ON(!timer->reserved);
> @@ -275,6 +297,11 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);
>
> void omap_dm_timer_trigger(struct omap_dm_timer *timer)
> {
> + if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
> + pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
> + return;
> + }
> +
> omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_trigger);
> @@ -283,11 +310,23 @@ void omap_dm_timer_start(struct omap_dm_timer *timer)
> {
> u32 l;
>
> + omap_dm_timer_enable(timer);
> +
> + if (timer->loses_context) {
> + u32 ctx_loss_cnt_after =
> + timer->get_context_loss_count(&timer->pdev->dev);
> + if (ctx_loss_cnt_after != timer->ctx_loss_count)
> + omap_timer_restore_context(timer);
> + }
> +
> l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
> if (!(l & OMAP_TIMER_CTRL_ST)) {
> l |= OMAP_TIMER_CTRL_ST;
> omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
> }
> +
> + /* Save the context */
> + timer->context.tclr = l;
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_start);
>
> @@ -311,9 +350,7 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)
> if (source < 0 || source >= 3)
> return -EINVAL;
>
> - omap_dm_timer_disable(timer);
> ret = pdata->set_timer_src(timer->pdev, source);
> - omap_dm_timer_enable(timer);
>
> return ret;
> }
> @@ -324,6 +361,7 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
> {
> u32 l;
>
> + omap_dm_timer_enable(timer);
> l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
> if (autoreload)
> l |= OMAP_TIMER_CTRL_AR;
> @@ -333,6 +371,10 @@ void 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);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
>
> @@ -342,6 +384,15 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
> {
> u32 l;
>
> + omap_dm_timer_enable(timer);
> +
> + if (timer->loses_context) {
> + u32 ctx_loss_cnt_after =
> + timer->get_context_loss_count(&timer->pdev->dev);
> + if (ctx_loss_cnt_after != timer->ctx_loss_count)
> + omap_timer_restore_context(timer);
> + }
> +
> l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
> if (autoreload) {
> l |= OMAP_TIMER_CTRL_AR;
> @@ -352,6 +403,11 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
> l |= OMAP_TIMER_CTRL_ST;
>
> __omap_dm_timer_load_start(timer, l, load, timer->posted);
> +
> + /* Save the context */
> + timer->context.tclr = l;
> + timer->context.tldr = load;
> + timer->context.tcrr = load;
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start);
>
> @@ -360,6 +416,7 @@ void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
> {
> u32 l;
>
> + omap_dm_timer_enable(timer);
> l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
> if (enable)
> l |= OMAP_TIMER_CTRL_CE;
> @@ -367,6 +424,11 @@ void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,
> l &= ~OMAP_TIMER_CTRL_CE;
> omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
> omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match);
> +
> + /* Save the context */
> + timer->context.tclr = l;
> + timer->context.tmar = match;
> + omap_dm_timer_disable(timer);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
>
> @@ -375,6 +437,7 @@ void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
> {
> u32 l;
>
> + omap_dm_timer_enable(timer);
> l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
> l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
> OMAP_TIMER_CTRL_PT | (0x03 << 10));
> @@ -384,6 +447,10 @@ void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,
> l |= OMAP_TIMER_CTRL_PT;
> 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);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
>
> @@ -391,6 +458,7 @@ void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
> {
> u32 l;
>
> + omap_dm_timer_enable(timer);
> l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
> l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));
> if (prescaler >= 0x00 && prescaler <= 0x07) {
> @@ -398,13 +466,23 @@ void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)
> l |= prescaler << 2;
> }
> omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
> +
> + /* Save the context */
> + timer->context.tclr = l;
> + omap_dm_timer_disable(timer);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler);
>
> void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
> unsigned int value)
> {
> + 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);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
>
> @@ -412,6 +490,11 @@ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
> {
> unsigned int l;
>
> + if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
> + pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
> + return 0;
> + }
> +
> l = __raw_readl(timer->irq_stat);
>
> return l;
> @@ -421,18 +504,33 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_read_status);
> void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)
> {
> __omap_dm_timer_write_status(timer, value);
> + /* Save the context */
> + timer->context.tisr = value;
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
>
> unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
> {
> + if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
> + pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
> + return 0;
> + }
> +
> return __omap_dm_timer_read_counter(timer, timer->posted);
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
>
> void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
> {
> + if (unlikely(pm_runtime_suspended(&timer->pdev->dev))) {
> + pr_err("%s: timer%d not enabled.\n", __func__, timer->id);
> + return;
> + }
> +
> omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
> +
> + /* Save the context */
> + timer->context.tcrr = value;
> }
> EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
>
> @@ -511,6 +609,8 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
> timer->irq = irq->start;
> timer->reserved = pdata->reserved;
> timer->pdev = pdev;
> + timer->loses_context = pdata->loses_context;
> + timer->get_context_loss_count = pdata->get_context_loss_count;
>
> /* Skip pm_runtime_enable for OMAP1 */
> if (!pdata->needs_manual_reset) {
> diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h
> index 29764c3..9519d87 100644
> --- a/arch/arm/plat-omap/include/plat/dmtimer.h
> +++ b/arch/arm/plat-omap/include/plat/dmtimer.h
> @@ -73,11 +73,38 @@ struct omap_timer_capability_dev_attr {
> struct omap_dm_timer;
> struct clk;
>
> +struct timer_regs {
> + u32 tidr;
> + u32 tiocp_cfg;
> + u32 tistat;
> + u32 tisr;
> + u32 tier;
> + u32 twer;
> + u32 tclr;
> + u32 tcrr;
> + u32 tldr;
> + u32 ttrg;
> + u32 twps;
> + u32 tmar;
> + u32 tcar1;
> + u32 tsicr;
> + u32 tcar2;
> + u32 tpir;
> + u32 tnir;
> + u32 tcvr;
> + u32 tocr;
> + u32 towr;
> +};
> +
> struct dmtimer_platform_data {
> int (*set_timer_src)(struct platform_device *pdev, int source);
> int timer_ip_version;
> u32 needs_manual_reset:1;
> bool reserved;
> +
> + bool loses_context;
> +
> + u32 (*get_context_loss_count)(struct device *dev);
> };
>
> struct omap_dm_timer *omap_dm_timer_request(void);
> @@ -245,8 +272,14 @@ struct omap_dm_timer {
> unsigned long rate;
> unsigned reserved:1;
> unsigned posted:1;
> + struct timer_regs context;
> + bool loses_context;
> + int ctx_loss_count;
> + int revision;
> struct platform_device *pdev;
> struct list_head node;
> +
> + u32 (*get_context_loss_count)(struct device *dev);
> };
>
> int omap_dm_timer_prepare(struct omap_dm_timer *timer);
> @@ -278,6 +311,7 @@ static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
> /* Assume v1 ip if bits [31:16] are zero */
> tidr = __raw_readl(timer->io_base);
> if (!(tidr >> 16)) {
> + timer->revision = 1;
> timer->sys_stat = timer->io_base +
> OMAP_TIMER_V1_SYS_STAT_OFFSET;
> timer->irq_stat = timer->io_base + OMAP_TIMER_V1_STAT_OFFSET;
> @@ -286,6 +320,7 @@ static inline void __omap_dm_timer_init_regs(struct omap_dm_timer *timer)
> timer->pend = timer->io_base + _OMAP_TIMER_WRITE_PEND_OFFSET;
> timer->func_base = timer->io_base;
> } else {
> + timer->revision = 2;
> timer->sys_stat = 0;
> timer->irq_stat = timer->io_base + OMAP_TIMER_V2_IRQSTATUS;
> timer->irq_ena = timer->io_base + OMAP_TIMER_V2_IRQENABLE_SET;
>
More information about the linux-arm-kernel
mailing list