Commit 506c079b8421c901cbf81ebd4c8c27f1315dc769

Authored by Jon Hunter
Committed by Vaibhav Hiremath
1 parent f11c4c97f6

ARM: OMAP3+: Implement timer workaround for errata i103 and i767

Errata Titles:
i103: Delay needed to read some GP timer, WD timer and sync timer
      registers after wakeup (OMAP3/4)
i767: Delay needed to read some GP timer registers after wakeup (OMAP5)

Description (i103/i767):
If a General Purpose Timer (GPTimer) is in posted mode
(TSICR [2].POSTED=1), due to internal resynchronizations, values read in
TCRR, TCAR1 and TCAR2 registers right after the timer interface clock
(L4) goes from stopped to active may not return the expected values. The
most common event leading to this situation occurs upon wake up from
idle.

GPTimer non-posted synchronization mode is not impacted by this
limitation.

Workarounds:
1). Disable posted mode
2). Use static dependency between timer clock domain and MPUSS clock
    domain
3). Use no-idle mode when the timer is active

Workarounds #2 and #3 are not pratical from a power standpoint and so
workaround #1 has been implemented. Disabling posted mode adds some CPU
overhead for configuring and reading the timers as the CPU has to wait
for accesses to be re-synchronised within the timer. However, disabling
posted mode guarantees correct operation.

Please note that it is safe to use posted mode for timers if the counter
(TCRR) and capture (TCARx) registers will never be read. An example of
this is the clock-event system timer. This is used by the kernel to
schedule events however, the timers counter is never read and capture
registers are not used. Given that the kernel configures this timer
often yet never reads the counter register it is safe to enable posted
mode in this case. Hence, for the timer used for kernel clock-events,
posted mode is enabled by overriding the errata for devices that are
impacted by this defect.

For drivers using the timers that do not read the counter or capture
registers and wish to use posted mode, can override the errata and
enable posted mode by making the following function calls.

	__omap_dm_timer_override_errata(timer, OMAP_TIMER_ERRATA_I103_I767);
	__omap_dm_timer_enable_posted(timer);

Both dmtimers and watchdogs are impacted by this defect this patch only
implements the workaround for the dmtimer. Currently the watchdog driver
does not read the counter register and so no workaround is necessary.

Posted mode will be disabled for all OMAP2+ devices (including AM33xx)
using a GP timer as a clock-source timer to guarantee correct operation.
This is not necessary for OMAP24xx devices but the default clock-source
timer for OMAP24xx devices is the 32k-sync timer and not the GP timer
and so should not have any impact. This should be re-visited for future
devices if this errata is fixed.

Confirmed with Vaibhav Hiremath that this bug also impacts AM33xx
devices.

Signed-off-by: Jon Hunter <jon-hunter@ti.com>
Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
[hvaibhav@ti.com: Backported to v3.2 PSP kernel, also merged
commit 7b44cf2c15f (ARM: OMAP: Fix timer posted mode support)]
Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com>

Showing 3 changed files with 107 additions and 14 deletions Side-by-side Diff

arch/arm/mach-omap2/timer.c
... ... @@ -204,10 +204,24 @@
204 204  
205 205 return res;
206 206 }
  207 +/**
  208 + * omap_dm_timer_get_errata - get errata flags for a timer
  209 + *
  210 + * Get the timer errata flags that are specific to the OMAP device being used.
  211 + */
  212 +u32 __init omap_dm_timer_get_errata(void)
  213 +{
  214 + if (cpu_is_omap24xx())
  215 + return 0;
207 216  
  217 + return OMAP_TIMER_ERRATA_I103_I767;
  218 +}
  219 +
  220 +
208 221 static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
209 222 int gptimer_id,
210   - const char *fck_source)
  223 + const char *fck_source,
  224 + int posted)
211 225 {
212 226 char name[10]; /* 10 = sizeof("gptXX_Xck0") */
213 227 struct omap_hwmod *oh;
214 228  
... ... @@ -252,8 +266,14 @@
252 266 }
253 267 __omap_dm_timer_init_regs(timer);
254 268 __omap_dm_timer_reset(timer, 1, 1);
255   - timer->posted = 1;
256 269  
  270 + if (posted)
  271 + __omap_dm_timer_enable_posted(timer);
  272 +
  273 + /* Check that the intended posted configuration matches the actual */
  274 + if (posted != timer->posted)
  275 + return -EINVAL;
  276 +
257 277 timer->rate = clk_get_rate(timer->fclk);
258 278  
259 279 timer->reserved = 1;
... ... @@ -266,7 +286,17 @@
266 286 {
267 287 int res;
268 288  
269   - res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source);
  289 + clkev.errata = omap_dm_timer_get_errata();
  290 +
  291 + /*
  292 + * For clock-event timers we never read the timer counter and
  293 + * so we are not impacted by errata i103 and i767. Therefore,
  294 + * we can safely ignore this errata for clock-event timers.
  295 + */
  296 + __omap_dm_timer_override_errata(&clkev, OMAP_TIMER_ERRATA_I103_I767);
  297 +
  298 + res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source,
  299 + OMAP_TIMER_POSTED);
270 300 BUG_ON(res);
271 301  
272 302 omap2_gp_timer_irq.dev_id = (void *)&clkev;
... ... @@ -313,7 +343,8 @@
313 343 static DEFINE_CLOCK_DATA(cd);
314 344 static cycle_t clocksource_read_cycles(struct clocksource *cs)
315 345 {
316   - return (cycle_t)__omap_dm_timer_read_counter(&clksrc, 1);
  346 + return (cycle_t)__omap_dm_timer_read_counter(&clksrc,
  347 + OMAP_TIMER_NONPOSTED);
317 348 }
318 349  
319 350 static struct clocksource clocksource_gpt = {
... ... @@ -328,7 +359,7 @@
328 359 {
329 360 u32 cyc;
330 361  
331   - cyc = __omap_dm_timer_read_counter(&clksrc, 1);
  362 + cyc = __omap_dm_timer_read_counter(&clksrc, OMAP_TIMER_NONPOSTED);
332 363  
333 364 update_sched_clock(&cd, cyc, (u32)~0);
334 365 }
... ... @@ -338,7 +369,8 @@
338 369 u32 cyc = 0;
339 370  
340 371 if (clksrc.reserved)
341   - cyc = __omap_dm_timer_read_counter(&clksrc, 1);
  372 + cyc = __omap_dm_timer_read_counter(&clksrc,
  373 + OMAP_TIMER_NONPOSTED);
342 374  
343 375 return cyc_to_sched_clock(&cd, cyc, (u32)~0);
344 376 }
345 377  
... ... @@ -349,14 +381,18 @@
349 381 {
350 382 int res;
351 383  
352   - res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source);
  384 + clksrc.errata = omap_dm_timer_get_errata();
  385 +
  386 + res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source,
  387 + OMAP_TIMER_NONPOSTED);
353 388 BUG_ON(res);
354 389  
355 390 pr_info("OMAP clocksource: GPTIMER%d at %lu Hz\n",
356 391 gptimer_id, clksrc.rate);
357 392  
358 393 __omap_dm_timer_load_start(&clksrc,
359   - OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0, 1);
  394 + OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR, 0,
  395 + OMAP_TIMER_NONPOSTED);
360 396 init_sched_clock(&cd, dmtimer_update_sched_clock, 32, clksrc.rate);
361 397  
362 398 if (clocksource_register_hz(&clocksource_gpt, clksrc.rate))
... ... @@ -553,6 +589,7 @@
553 589 pdata->reserved = 1;
554 590  
555 591 pwrdm = omap_hwmod_get_pwrdm(oh);
  592 + pdata->timer_errata = omap_dm_timer_get_errata();
556 593 pdata->loses_context = pwrdm_can_ever_lose_context(pwrdm);
557 594 #ifdef CONFIG_PM
558 595 pdata->get_context_loss_count = omap_pm_get_dev_context_loss_count;
arch/arm/plat-omap/dmtimer.c
... ... @@ -115,21 +115,20 @@
115 115  
116 116 static void omap_dm_timer_reset(struct omap_dm_timer *timer)
117 117 {
118   - omap_dm_timer_enable(timer);
119 118 if (timer->pdev->id != 1) {
120 119 omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);
121 120 omap_dm_timer_wait_for_reset(timer);
122 121 }
123 122  
124 123 __omap_dm_timer_reset(timer, 0, 0);
125   - omap_dm_timer_disable(timer);
126   - timer->posted = 1;
127 124 }
128 125  
129 126 int omap_dm_timer_prepare(struct omap_dm_timer *timer)
130 127 {
131 128 struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data;
132 129  
  130 + omap_dm_timer_enable(timer);
  131 +
133 132 timer->fclk = clk_get(&timer->pdev->dev, "fck");
134 133 if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) {
135 134 timer->fclk = NULL;
... ... @@ -140,7 +139,8 @@
140 139 if (pdata->needs_manual_reset)
141 140 omap_dm_timer_reset(timer);
142 141  
143   - timer->posted = 1;
  142 + __omap_dm_timer_enable_posted(timer);
  143 + omap_dm_timer_disable(timer);
144 144 return 0;
145 145 }
146 146  
... ... @@ -685,6 +685,7 @@
685 685 }
686 686  
687 687 timer->id = pdev->id;
  688 + timer->errata = pdata->timer_errata;
688 689 timer->irq = irq->start;
689 690 timer->reserved = pdata->reserved;
690 691 timer->pdev = pdev;
arch/arm/plat-omap/include/plat/dmtimer.h
... ... @@ -56,6 +56,10 @@
56 56 #define OMAP_TIMER_TRIGGER_OVERFLOW 0x01
57 57 #define OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE 0x02
58 58  
  59 +/* posted mode types */
  60 +#define OMAP_TIMER_NONPOSTED 0x00
  61 +#define OMAP_TIMER_POSTED 0x01
  62 +
59 63 /*
60 64 * IP revision identifier so that Highlander IP
61 65 * in OMAP4 can be distinguished.
... ... @@ -71,6 +75,17 @@
71 75 u32 timer_capability;
72 76 };
73 77  
  78 +/*
  79 + * timer errata flags
  80 + *
  81 + * Errata i103/i767 impacts all OMAP3/4/5 devices including AM33xx. This
  82 + * errata prevents us from using posted mode on these devices, unless the
  83 + * timer counter register is never read. For more details please refer to
  84 + * the OMAP3/4/5 errata documents.
  85 + */
  86 +#define OMAP_TIMER_ERRATA_I103_I767 0x80000000
  87 +
  88 +
74 89 struct omap_dm_timer;
75 90 struct clk;
76 91  
... ... @@ -98,6 +113,7 @@
98 113 struct dmtimer_platform_data {
99 114 int (*set_timer_src)(struct platform_device *pdev, int source);
100 115 int timer_ip_version;
  116 + u32 timer_errata;
101 117 u32 needs_manual_reset:1;
102 118 bool reserved;
103 119  
... ... @@ -277,6 +293,7 @@
277 293 bool loses_context;
278 294 int ctx_loss_count;
279 295 int revision;
  296 + u32 errata;
280 297 struct platform_device *pdev;
281 298 struct list_head node;
282 299  
283 300  
... ... @@ -351,9 +368,30 @@
351 368  
352 369 __raw_writel(l, timer->io_base + OMAP_TIMER_OCP_CFG_OFFSET);
353 370  
354   - /* Match hardware reset default of posted mode */
  371 +}
  372 +
  373 +/*
  374 + * __omap_dm_timer_enable_posted - enables write posted mode
  375 + * @timer: pointer to timer instance handle
  376 + *
  377 + * Enables the write posted mode for the timer. When posted mode is enabled
  378 + * writes to certain timer registers are immediately acknowledged by the
  379 + * internal bus and hence prevents stalling the CPU waiting for the write to
  380 + * complete. Enabling this feature can improve performance for writing to the
  381 + * timer registers.
  382 + */
  383 +static inline void __omap_dm_timer_enable_posted(struct omap_dm_timer *timer)
  384 +{
  385 + if (timer->posted)
  386 + return;
  387 +
  388 + if (timer->errata & OMAP_TIMER_ERRATA_I103_I767)
  389 + return;
  390 +
355 391 __omap_dm_timer_write(timer, OMAP_TIMER_IF_CTRL_REG,
356   - OMAP_TIMER_CTRL_POSTED, 0);
  392 + OMAP_TIMER_CTRL_POSTED, 0);
  393 + timer->context.tsicr = OMAP_TIMER_CTRL_POSTED;
  394 + timer->posted = OMAP_TIMER_POSTED;
357 395 }
358 396  
359 397 static inline int __omap_dm_timer_set_source(struct clk *timer_fck,
... ... @@ -373,6 +411,23 @@
373 411  
374 412 return ret;
375 413 }
  414 +
  415 +/**
  416 + * __omap_dm_timer_override_errata - override errata flags for a timer
  417 + * @timer: pointer to timer handle
  418 + * @errata: errata flags to be ignored
  419 + *
  420 + * For a given timer, override a timer errata by clearing the flags
  421 + * specified by the errata argument. A specific erratum should only be
  422 + * overridden for a timer if the timer is used in such a way the erratum
  423 + * has no impact.
  424 + */
  425 +static inline void __omap_dm_timer_override_errata(struct omap_dm_timer *timer,
  426 + u32 errata)
  427 +{
  428 + timer->errata &= ~errata;
  429 +}
  430 +
376 431  
377 432 static inline void __omap_dm_timer_stop(struct omap_dm_timer *timer,
378 433 int posted, unsigned long rate)