clocksource/drivers/timer-ti-dm: Handle dra7 timer wrap errata i940
commit 25de4ce5ed02994aea8bc111d133308f6fd62566 upstream. There is a timer wrap issue on dra7 for the ARM architected timer. In a typical clock configuration the timer fails to wrap after 388 days. To work around the issue, we need to use timer-ti-dm percpu timers instead. Let's configure dmtimer3 and 4 as percpu timers by default, and warn about the issue if the dtb is not configured properly. Let's do this as a single patch so it can be backported to v5.8 and later kernels easily. Note that this patch depends on earlier timer-ti-dm systimer posted mode fixes, and a preparatory clockevent patch "clocksource/drivers/timer-ti-dm: Prepare to handle dra7 timer wrap issue". For more information, please see the errata for "AM572x Sitara Processors Silicon Revisions 1.1, 2.0": https://www.ti.com/lit/er/sprz429m/sprz429m.pdf The concept is based on earlier reference patches done by Tero Kristo and Keerthy. Cc: Keerthy <j-keerthy@ti.com> Cc: Tero Kristo <kristo@kernel.org> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Link: https://lore.kernel.org/r/20210323074326.28302-3-tony@atomide.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
06470de53e
commit
6d5fda434b
@@ -2,6 +2,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
@@ -630,6 +631,78 @@ err_out_free:
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Dmtimer as percpu timer. See dra7 ARM architected timer wrap erratum i940 */
|
||||
static DEFINE_PER_CPU(struct dmtimer_clockevent, dmtimer_percpu_timer);
|
||||
|
||||
static int __init dmtimer_percpu_timer_init(struct device_node *np, int cpu)
|
||||
{
|
||||
struct dmtimer_clockevent *clkevt;
|
||||
int error;
|
||||
|
||||
if (!cpu_possible(cpu))
|
||||
return -EINVAL;
|
||||
|
||||
if (!of_property_read_bool(np->parent, "ti,no-reset-on-init") ||
|
||||
!of_property_read_bool(np->parent, "ti,no-idle"))
|
||||
pr_warn("Incomplete dtb for percpu dmtimer %pOF\n", np->parent);
|
||||
|
||||
clkevt = per_cpu_ptr(&dmtimer_percpu_timer, cpu);
|
||||
|
||||
error = dmtimer_clkevt_init_common(clkevt, np, CLOCK_EVT_FEAT_ONESHOT,
|
||||
cpumask_of(cpu), "percpu-dmtimer",
|
||||
500);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See TRM for timer internal resynch latency */
|
||||
static int omap_dmtimer_starting_cpu(unsigned int cpu)
|
||||
{
|
||||
struct dmtimer_clockevent *clkevt = per_cpu_ptr(&dmtimer_percpu_timer, cpu);
|
||||
struct clock_event_device *dev = &clkevt->dev;
|
||||
struct dmtimer_systimer *t = &clkevt->t;
|
||||
|
||||
clockevents_config_and_register(dev, t->rate, 3, ULONG_MAX);
|
||||
irq_force_affinity(dev->irq, cpumask_of(cpu));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init dmtimer_percpu_timer_startup(void)
|
||||
{
|
||||
struct dmtimer_clockevent *clkevt = per_cpu_ptr(&dmtimer_percpu_timer, 0);
|
||||
struct dmtimer_systimer *t = &clkevt->t;
|
||||
|
||||
if (t->sysc) {
|
||||
cpuhp_setup_state(CPUHP_AP_TI_GP_TIMER_STARTING,
|
||||
"clockevents/omap/gptimer:starting",
|
||||
omap_dmtimer_starting_cpu, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(dmtimer_percpu_timer_startup);
|
||||
|
||||
static int __init dmtimer_percpu_quirk_init(struct device_node *np, u32 pa)
|
||||
{
|
||||
struct device_node *arm_timer;
|
||||
|
||||
arm_timer = of_find_compatible_node(NULL, NULL, "arm,armv7-timer");
|
||||
if (of_device_is_available(arm_timer)) {
|
||||
pr_warn_once("ARM architected timer wrap issue i940 detected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pa == 0x48034000) /* dra7 dmtimer3 */
|
||||
return dmtimer_percpu_timer_init(np, 0);
|
||||
else if (pa == 0x48036000) /* dra7 dmtimer4 */
|
||||
return dmtimer_percpu_timer_init(np, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clocksource */
|
||||
static struct dmtimer_clocksource *
|
||||
to_dmtimer_clocksource(struct clocksource *cs)
|
||||
@@ -763,6 +836,9 @@ static int __init dmtimer_systimer_init(struct device_node *np)
|
||||
if (clockevent == pa)
|
||||
return dmtimer_clockevent_init(np);
|
||||
|
||||
if (of_machine_is_compatible("ti,dra7"))
|
||||
return dmtimer_percpu_quirk_init(np, pa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user