.set_mode = omap2_gp_timer_set_mode,
};
+/**
+ * omap_dm_timer_get_errata - get errata flags for a timer
+ *
+ * Get the timer errata flags that are specific to the OMAP device being used.
+ */
+u32 __init omap_dm_timer_get_errata(void)
+{
+ if (cpu_is_omap24xx())
+ return 0;
+
+ return OMAP_TIMER_ERRATA_I103_I767;
+}
+
static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
int gptimer_id,
- const char *fck_source)
+ const char *fck_source,
+ int posted)
{
char name[10]; /* 10 = sizeof("gptXX_Xck0") */
struct omap_hwmod *oh;
}
__omap_dm_timer_init_regs(timer);
__omap_dm_timer_reset(timer, 1, 1);
- timer->posted = 1;
- timer->rate = clk_get_rate(timer->fclk);
+ if (posted)
+ __omap_dm_timer_enable_posted(timer);
+ /* Check that the intended posted configuration matches the actual */
+ if (posted != timer->posted)
+ return -EINVAL;
+
+ timer->rate = clk_get_rate(timer->fclk);
timer->reserved = 1;
return res;
{
int res;
- res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source);
+ clkev.errata = omap_dm_timer_get_errata();
+
+ /*
+ * For clock-event timers we never read the timer counter and
+ * so we are not impacted by errata i103 and i767. Therefore,
+ * we can safely ignore this errata for clock-event timers.
+ */
+ __omap_dm_timer_override_errata(&clkev, OMAP_TIMER_ERRATA_I103_I767);
+
+ res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source,
+ OMAP_TIMER_POSTED);
BUG_ON(res);
omap2_gp_timer_irq.dev_id = (void *)&clkev;
static cycle_t clocksource_read_cycles(struct clocksource *cs)
{
return (cycle_t)__omap_dm_timer_read_counter(&clksrc,
- OMAP_TIMER_POSTED);
+ OMAP_TIMER_NONPOSTED);
}
static struct clocksource clocksource_gpt = {
{
if (clksrc.reserved)
return __omap_dm_timer_read_counter(&clksrc,
- OMAP_TIMER_POSTED);
+ OMAP_TIMER_NONPOSTED);
return 0;
}
{
int res;
- res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source);
+ clksrc.errata = omap_dm_timer_get_errata();
+
+ res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source,
+ OMAP_TIMER_NONPOSTED);
BUG_ON(res);
pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n",
__omap_dm_timer_load_start(&clksrc,
OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0,
- OMAP_TIMER_POSTED);
+ OMAP_TIMER_NONPOSTED);
setup_sched_clock(dmtimer_read_sched_clock, 32, clksrc.rate);
if (clocksource_register_hz(&clocksource_gpt, clksrc.rate))
if ((sys_timer_reserved >> (id - 1)) & 0x1)
pdata->reserved = 1;
+ pdata->timer_errata = omap_dm_timer_get_errata();
pwrdm = omap_hwmod_get_pwrdm(oh);
pdata->loses_context = pwrdm_can_ever_lose_context(pwrdm);
#ifdef CONFIG_PM
#define OMAP_TIMER_ALWON 0x40000000
#define OMAP_TIMER_HAS_PWM 0x20000000
+/*
+ * timer errata flags
+ *
+ * Errata i103/i767 impacts all OMAP3/4/5 devices including AM33xx. This
+ * errata prevents us from using posted mode on these devices, unless the
+ * timer counter register is never read. For more details please refer to
+ * the OMAP3/4/5 errata documents.
+ */
+#define OMAP_TIMER_ERRATA_I103_I767 0x80000000
+
struct omap_timer_capability_dev_attr {
u32 timer_capability;
};
bool reserved;
bool loses_context;
+ u32 timer_errata;
int (*get_context_loss_count)(struct device *dev);
};
bool loses_context;
int ctx_loss_count;
int revision;
+ u32 errata;
struct platform_device *pdev;
struct list_head node;
l |= 1 << 2;
__raw_writel(l, timer->io_base + OMAP_TIMER_OCP_CFG_OFFSET);
+}
+
+/*
+ * __omap_dm_timer_enable_posted - enables write posted mode
+ * @timer: pointer to timer instance handle
+ *
+ * Enables the write posted mode for the timer. When posted mode is enabled
+ * writes to certain timer registers are immediately acknowledged by the
+ * internal bus and hence prevents stalling the CPU waiting for the write to
+ * complete. Enabling this feature can improve performance for writing to the
+ * timer registers.
+ */
+static inline void __omap_dm_timer_enable_posted(struct omap_dm_timer *timer)
+{
+ if (timer->posted)
+ return;
+
+ if (timer->errata & OMAP_TIMER_ERRATA_I103_I767)
+ return;
- /* Match hardware reset default of posted mode */
__omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG,
- OMAP_TIMER_CTRL_POSTED, 0);
+ OMAP_TIMER_CTRL_POSTED, 0);
+ timer->context.tsicr = OMAP_TIMER_CTRL_POSTED;
+ timer->posted = OMAP_TIMER_POSTED;
+}
+
+/**
+ * __omap_dm_timer_override_errata - override errata flags for a timer
+ * @timer: pointer to timer handle
+ * @errata: errata flags to be ignored
+ *
+ * For a given timer, override a timer errata by clearing the flags
+ * specified by the errata argument. A specific erratum should only be
+ * overridden for a timer if the timer is used in such a way the erratum
+ * has no impact.
+ */
+static inline void __omap_dm_timer_override_errata(struct omap_dm_timer *timer,
+ u32 errata)
+{
+ timer->errata &= ~errata;
}
static inline int __omap_dm_timer_set_source(struct clk *timer_fck,