aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarun Kanti DebBarma <tarun.kanti@ti.com>2011-07-09 18:55:39 -0500
committerNishanth Menon <nm@ti.com>2011-07-14 11:53:05 -0700
commit4cfe86f1a50a878ddd553b9ee4b3c3e88bf18ac6 (patch)
treee3b5de6317c8239bf74f4c280d6324ce73404dcc
parent684857b7cd69b10400a11bf96bf995a0dd2c8e90 (diff)
downloadkernel_samsung_tuna-4cfe86f1a50a878ddd553b9ee4b3c3e88bf18ac6.zip
kernel_samsung_tuna-4cfe86f1a50a878ddd553b9ee4b3c3e88bf18ac6.tar.gz
kernel_samsung_tuna-4cfe86f1a50a878ddd553b9ee4b3c3e88bf18ac6.tar.bz2
OMAP: dmtimer: Off 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 and restore functions are called as needed. Change-Id: I307fa54925f16e61491decb7101879dade211b7e [girishsg@ti.com: Fixed review comments and rebased] Signed-off-by: Girish S G <girishsg@ti.com> Signed-off-by: Tarun Kanti DebBarma <tarun.kanti@ti.com>
-rw-r--r--arch/arm/mach-omap2/dmtimer.c22
-rw-r--r--arch/arm/plat-omap/dmtimer.c160
-rw-r--r--arch/arm/plat-omap/include/plat/dmtimer.h35
3 files changed, 188 insertions, 29 deletions
diff --git a/arch/arm/mach-omap2/dmtimer.c b/arch/arm/mach-omap2/dmtimer.c
index fda070c..9b27d4f 100644
--- a/arch/arm/mach-omap2/dmtimer.c
+++ b/arch/arm/mach-omap2/dmtimer.c
@@ -27,6 +27,9 @@
#include <plat/omap_device.h>
#include <plat/cpu.h>
#include <plat/omap_hwmod.h>
+#include <plat/omap-pm.h>
+
+#include "powerdomain.h"
static u8 __initdata system_timer_id;
@@ -91,6 +94,16 @@ static int omap2_dm_timer_set_src(struct platform_device *pdev, int source)
return ret;
}
+#ifdef CONFIG_PM
+static int omap_timer_get_context_loss(struct device *dev)
+{
+ return omap_pm_get_dev_context_loss_count(dev);
+}
+
+#else
+#define omap_gpio_get_context_loss NULL
+#endif
+
struct omap_device_pm_latency omap2_dmtimer_latency[] = {
{
.deactivate_func = omap_device_idle_hwmods,
@@ -119,6 +132,7 @@ static int __init omap_timer_init(struct omap_hwmod *oh, void *unused)
struct dmtimer_platform_data *pdata;
struct omap_device *od;
struct omap_secure_timer_dev_attr *secure_timer_dev_attr;
+ struct powerdomain *pwrdm;
/*
* Extract the IDs from name field in hwmod database
@@ -148,6 +162,14 @@ static int __init omap_timer_init(struct omap_hwmod *oh, void *unused)
}
pdata->set_timer_src = omap2_dm_timer_set_src;
pdata->timer_ip_type = oh->class->rev;
+ pwrdm = omap_hwmod_get_pwrdm(oh);
+ if (!pwrdm) {
+ pr_debug("%s: could not find pwrdm for (%s) in omap hwmod!\n",
+ __func__, oh->name);
+ return -EINVAL;
+ }
+ pdata->loses_context = pwrdm_can_ever_lose_context(pwrdm);
+ pdata->get_context_loss_count = omap_timer_get_context_loss;
od = omap_device_build(name, id, oh, pdata, sizeof(*pdata),
omap2_dmtimer_latency,
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 022a867..997e1a0 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -231,6 +231,54 @@ static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,
writel(value, timer->io_base + (reg & 0xff));
}
+static void omap_timer_save_context(struct omap_dm_timer *timer)
+{
+ timer->context.tiocp_cfg =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_OCP_CFG_REG);
+ timer->context.tistat =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_SYS_STAT_REG);
+ timer->context.tisr =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
+ timer->context.tier =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_INT_EN_REG);
+ timer->context.twer =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG);
+ timer->context.tclr =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
+ timer->context.tcrr =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_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.tsicr =
+ omap_dm_timer_read_reg(timer, OMAP_TIMER_IF_CTRL_REG);
+}
+
+static void omap_timer_restore_context(struct omap_dm_timer *timer)
+{
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG,
+ timer->context.tiocp_cfg);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_SYS_STAT_REG,
+ timer->context.tistat);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG,
+ timer->context.tisr);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG,
+ timer->context.tier);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,
+ timer->context.twer);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG,
+ timer->context.tclr);
+ 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);
+}
+
static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)
{
int c;
@@ -249,6 +297,9 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
{
u32 l;
+ if (!timer->is_early_init)
+ 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);
@@ -258,12 +309,13 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
l |= 0x02 << 3; /* Set to smart-idle mode */
l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */
omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l);
+
+ if (!timer->is_early_init)
+ omap_dm_timer_disable(timer);
}
static void omap_dm_timer_prepare(struct omap_dm_timer *timer)
{
- struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
-
timer->fclk = clk_get(&timer->pdev->dev, "fck");
if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) {
timer->fclk = NULL;
@@ -271,22 +323,27 @@ static void omap_dm_timer_prepare(struct omap_dm_timer *timer)
return;
}
- if (unlikely(pdata->is_early_init)) {
+ if (unlikely(timer->is_early_init)) {
clk_enable(timer->fclk);
goto end;
}
- omap_dm_timer_enable(timer);
-
- if (pdata->needs_manual_reset)
+ if (timer->needs_manual_reset)
omap_dm_timer_reset(timer);
omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);
end:
+ if (!timer->is_early_init)
+ omap_dm_timer_enable(timer);
+
/* Match hardware reset default of posted mode */
omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG,
OMAP_TIMER_CTRL_POSTED);
+
+ if (!timer->is_early_init)
+ omap_dm_timer_disable(timer);
+
timer->posted = 1;
}
@@ -350,22 +407,14 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_free);
void omap_dm_timer_enable(struct omap_dm_timer *timer)
{
- if (timer->enabled)
- return;
-
pm_runtime_get_sync(&timer->pdev->dev);
-
timer->enabled = 1;
}
EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
void omap_dm_timer_disable(struct omap_dm_timer *timer)
{
- if (!timer->enabled)
- return;
-
pm_runtime_put_sync(&timer->pdev->dev);
-
timer->enabled = 0;
}
EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
@@ -440,6 +489,19 @@ void omap_dm_timer_start(struct omap_dm_timer *timer)
{
u32 l;
+ if (timer->loses_context) {
+ u32 ctx_loss_cnt_after;
+
+ omap_dm_timer_enable(timer);
+ ctx_loss_cnt_after =
+ timer->get_context_loss_count(&timer->pdev->dev);
+ if ((ctx_loss_cnt_after != timer->ctx_loss_count) &&
+ timer->context_saved) {
+ omap_timer_restore_context(timer);
+ timer->context_saved = false;
+ }
+ }
+
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
if (!(l & OMAP_TIMER_CTRL_ST)) {
l |= OMAP_TIMER_CTRL_ST;
@@ -471,6 +533,15 @@ void omap_dm_timer_stop(struct omap_dm_timer *timer)
/* Ack possibly pending interrupt */
omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG,
OMAP_TIMER_INT_OVERFLOW);
+
+ if (timer->loses_context) {
+ if (timer->get_context_loss_count)
+ timer->ctx_loss_count =
+ timer->get_context_loss_count(&timer->pdev->dev);
+ omap_timer_save_context(timer);
+ timer->context_saved = true;
+ omap_dm_timer_disable(timer);
+ }
}
EXPORT_SYMBOL_GPL(omap_dm_timer_stop);
@@ -482,10 +553,11 @@ 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);
+ if (timer->enabled)
+ omap_dm_timer_disable(timer);
+
/* change the timer clock source */
ret = pdata->set_timer_src(timer->pdev, source);
- omap_dm_timer_enable(timer);
/*
* When the functional clock disappears, too quick writes seem
@@ -502,6 +574,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;
@@ -511,6 +584,7 @@ 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);
+ omap_dm_timer_disable(timer);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);
@@ -520,6 +594,19 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
{
u32 l;
+ if (timer->loses_context) {
+ u32 ctx_loss_cnt_after;
+
+ omap_dm_timer_enable(timer);
+ ctx_loss_cnt_after =
+ timer->get_context_loss_count(&timer->pdev->dev);
+ if ((ctx_loss_cnt_after != timer->ctx_loss_count) &&
+ timer->context_saved) {
+ omap_timer_restore_context(timer);
+ timer->context_saved = false;
+ }
+ }
+
l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);
if (autoreload) {
l |= OMAP_TIMER_CTRL_AR;
@@ -539,6 +626,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;
@@ -546,6 +634,7 @@ 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);
+ omap_dm_timer_disable(timer);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_set_match);
@@ -554,6 +643,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));
@@ -563,6 +653,7 @@ 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);
+ omap_dm_timer_disable(timer);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm);
@@ -570,6 +661,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) {
@@ -577,46 +669,53 @@ 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);
+ 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)
{
+ if (!timer->is_early_init && !timer->enabled)
+ omap_dm_timer_enable(timer);
+
omap_dm_timer_write_reg(timer, OMAP_TIMER_INT_EN_REG, value);
omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, value);
+
+ if (!timer->is_early_init && !timer->enabled)
+ omap_dm_timer_disable(timer);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable);
unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
{
- unsigned int l;
-
- l = omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
+ if (timer->is_early_init || timer->enabled)
+ return omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG);
- return l;
+ return -EINVAL;
}
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_reg(timer, OMAP_TIMER_STAT_REG, value);
+ if (timer->is_early_init || timer->enabled)
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_STAT_REG, value);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);
unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)
{
- unsigned int l;
+ if (timer->is_early_init || timer->enabled)
+ return omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
- l = omap_dm_timer_read_reg(timer, OMAP_TIMER_COUNTER_REG);
-
- return l;
+ return -EINVAL;
}
EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter);
void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)
{
- omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
+ if (timer->is_early_init || timer->enabled)
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);
@@ -695,13 +794,18 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
timer->intr_offset = VERSION2_TIMER_STAT_REG_OFFSET;
}
- timer->id = pdev->id;
timer->irq = irq->start;
timer->pdev = pdev;
+ timer->is_early_init = pdata->is_early_init;
+ timer->needs_manual_reset = pdata->needs_manual_reset;
+ timer->loses_context = pdata->loses_context;
+ timer->get_context_loss_count = pdata->get_context_loss_count;
/* Skip pm_runtime_enable during early boot and for OMAP1 */
- if (!pdata->is_early_init && !pdata->needs_manual_reset)
+ if (!pdata->is_early_init && !pdata->needs_manual_reset) {
pm_runtime_enable(&pdev->dev);
+ pm_runtime_irq_safe(&pdev->dev);
+ }
/* add the timer element to the list */
mutex_lock(&dm_timer_mutex);
diff --git a/arch/arm/plat-omap/include/plat/dmtimer.h b/arch/arm/plat-omap/include/plat/dmtimer.h
index 11a734f..4849f10 100644
--- a/arch/arm/plat-omap/include/plat/dmtimer.h
+++ b/arch/arm/plat-omap/include/plat/dmtimer.h
@@ -61,18 +61,48 @@ struct omap_secure_timer_dev_attr {
bool is_secure_timer;
};
+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 omap_dm_timer {
- int id;
int irq;
struct clk *fclk;
void __iomem *io_base;
unsigned reserved:1;
unsigned enabled:1;
unsigned posted:1;
+ unsigned is_early_init:1;
+ unsigned needs_manual_reset:1;
u8 func_offset;
u8 intr_offset;
+ bool loses_context;
+ bool context_saved;
+ u32 ctx_loss_count;
+ struct timer_regs context;
struct platform_device *pdev;
struct list_head node;
+
+ int (*get_context_loss_count)(struct device *dev);
};
extern struct omap_dm_timer *gptimer_wakeup;
@@ -84,6 +114,9 @@ struct dmtimer_platform_data {
int timer_ip_type;
u32 is_early_init:1;
u32 needs_manual_reset:1;
+ bool loses_context;
+
+ int (*get_context_loss_count)(struct device *dev);
};
struct omap_dm_timer *omap_dm_timer_request(void);