forked from mirrors/linux
		
	clocksource/drivers/timer-ti-dm: Handle dra7 timer wrap errata i940
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
This commit is contained in:
		
							parent
							
								
									3efe7a878a
								
							
						
					
					
						commit
						25de4ce5ed
					
				
					 4 changed files with 99 additions and 2 deletions
				
			
		|  | @ -1168,7 +1168,7 @@ timer2: timer@0 { | |||
| 			}; | ||||
| 		}; | ||||
| 
 | ||||
| 		target-module@34000 {			/* 0x48034000, ap 7 46.0 */ | ||||
| 		timer3_target: target-module@34000 {	/* 0x48034000, ap 7 46.0 */ | ||||
| 			compatible = "ti,sysc-omap4-timer", "ti,sysc"; | ||||
| 			reg = <0x34000 0x4>, | ||||
| 			      <0x34010 0x4>; | ||||
|  | @ -1195,7 +1195,7 @@ timer3: timer@0 { | |||
| 			}; | ||||
| 		}; | ||||
| 
 | ||||
| 		target-module@36000 {			/* 0x48036000, ap 9 4e.0 */ | ||||
| 		timer4_target: target-module@36000 {	/* 0x48036000, ap 9 4e.0 */ | ||||
| 			compatible = "ti,sysc-omap4-timer", "ti,sysc"; | ||||
| 			reg = <0x36000 0x4>, | ||||
| 			      <0x36010 0x4>; | ||||
|  |  | |||
|  | @ -46,6 +46,7 @@ aliases { | |||
| 
 | ||||
| 	timer { | ||||
| 		compatible = "arm,armv7-timer"; | ||||
| 		status = "disabled";	/* See ARM architected timer wrap erratum i940 */ | ||||
| 		interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, | ||||
| 			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, | ||||
| 			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(2) | IRQ_TYPE_LEVEL_LOW)>, | ||||
|  | @ -1241,3 +1242,22 @@ timer@0 { | |||
| 		assigned-clock-parents = <&sys_32k_ck>; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| /* Local timers, see ARM architected timer wrap erratum i940 */ | ||||
| &timer3_target { | ||||
| 	ti,no-reset-on-init; | ||||
| 	ti,no-idle; | ||||
| 	timer@0 { | ||||
| 		assigned-clocks = <&l4per_clkctrl DRA7_L4PER_TIMER3_CLKCTRL 24>; | ||||
| 		assigned-clock-parents = <&timer_sys_clk_div>; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| &timer4_target { | ||||
| 	ti,no-reset-on-init; | ||||
| 	ti,no-idle; | ||||
| 	timer@0 { | ||||
| 		assigned-clocks = <&l4per_clkctrl DRA7_L4PER_TIMER4_CLKCTRL 24>; | ||||
| 		assigned-clock-parents = <&timer_sys_clk_div>; | ||||
| 	}; | ||||
| }; | ||||
|  |  | |||
|  | @ -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 @@ static int __init dmtimer_clockevent_init(struct device_node *np) | |||
| 	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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -135,6 +135,7 @@ enum cpuhp_state { | |||
| 	CPUHP_AP_RISCV_TIMER_STARTING, | ||||
| 	CPUHP_AP_CLINT_TIMER_STARTING, | ||||
| 	CPUHP_AP_CSKY_TIMER_STARTING, | ||||
| 	CPUHP_AP_TI_GP_TIMER_STARTING, | ||||
| 	CPUHP_AP_HYPERV_TIMER_STARTING, | ||||
| 	CPUHP_AP_KVM_STARTING, | ||||
| 	CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Tony Lindgren
						Tony Lindgren