[PATCH v3 1/2] ARM: arch_timers: enable the use of the virtual timer

Cyril Chemparathy cyril at ti.com
Thu Aug 30 14:28:17 EDT 2012


Hi Marc,

On 8/24/2012 10:52 AM, Marc Zyngier wrote:
> At the moment, the arch_timer driver only uses the physical timer,
> which can cause problem if PL2 hasn't enabled PL1 access in CNTHCTL,
> which is likely in a virtualized environment. Instead, the virtual
> timer is always available.
>
> This patch enables the use of the virtual timer, unless no
> interrupt is provided in the DT for it, in which case it falls
> back to the physical timer.
>
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>

Looks very nice...  Minor comments below.

> ---
>   arch/arm/kernel/arch_timer.c | 337 ++++++++++++++++++++++++++++++-------------
>   1 file changed, 233 insertions(+), 104 deletions(-)
>
> diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
> index cf25880..3f63b90 100644
> --- a/arch/arm/kernel/arch_timer.c
> +++ b/arch/arm/kernel/arch_timer.c
> @@ -27,13 +27,23 @@
>   #include <asm/sched_clock.h>
>
>   static unsigned long arch_timer_rate;
> -static int arch_timer_ppi;
> -static int arch_timer_ppi2;
> +
> +enum ppi_nr {
> +	PHYS_SECURE_PPI,
> +	PHYS_NONSECURE_PPI,
> +	VIRT_PPI,
> +	HYP_PPI,
> +	MAX_TIMER_PPI
> +};
> +
> +static int arch_timer_ppi[MAX_TIMER_PPI];
>
>   static struct clock_event_device __percpu **arch_timer_evt;
>
>   extern void init_current_timer_delay(unsigned long freq);
>
> +static bool arch_timer_use_virtual = true;
> +
>   /*
>    * Architected system timer support.
>    */
> @@ -43,53 +53,98 @@ extern void init_current_timer_delay(unsigned long freq);
>   #define ARCH_TIMER_CTRL_IT_STAT		(1 << 2)
>
>   #define ARCH_TIMER_REG_CTRL		0
> -#define ARCH_TIMER_REG_FREQ		1
> -#define ARCH_TIMER_REG_TVAL		2
> +#define ARCH_TIMER_REG_TVAL		1
>
> -static void arch_timer_reg_write(int reg, u32 val)
> +#define ARCH_TIMER_PHYS_ACCESS		0
> +#define ARCH_TIMER_VIRT_ACCESS		1
> +
> +/*
> + * These register accessors are marked inline so the compiler can
> + * nicely work out which register we want, and chuck away the rest of
> + * the code. At least it does so with a recent GCC (4.6.3).
> + */
> +static inline void arch_timer_reg_write(const int access, const int reg, u32 val)
>   {
> -	switch (reg) {
> -	case ARCH_TIMER_REG_CTRL:
> -		asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
> -		break;
> -	case ARCH_TIMER_REG_TVAL:
> -		asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
> -		break;
> +	if (access == ARCH_TIMER_PHYS_ACCESS) {
> +		switch (reg) {
> +		case ARCH_TIMER_REG_CTRL:
> +			asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
> +			break;
> +		case ARCH_TIMER_REG_TVAL:
> +			asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
> +			break;
> +		}
> +	}
> +
> +	if (access == ARCH_TIMER_VIRT_ACCESS) {
> +		switch (reg) {
> +		case ARCH_TIMER_REG_CTRL:
> +			asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" (val));
> +			break;
> +		case ARCH_TIMER_REG_TVAL:
> +			asm volatile("mcr p15, 0, %0, c14, c3, 0" : : "r" (val));
> +			break;
> +		}
>   	}
>
>   	isb();
>   }
>
> -static u32 arch_timer_reg_read(int reg)
> +static inline u32 arch_timer_reg_read(const int access, const int reg)
>   {
> -	u32 val;
> +	u32 val = 0;
> +
> +	if (access == ARCH_TIMER_PHYS_ACCESS) {
> +		switch (reg) {
> +		case ARCH_TIMER_REG_CTRL:
> +			asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
> +			break;
> +		case ARCH_TIMER_REG_TVAL:
> +			asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
> +			break;
> +		}
> +	}
>
> -	switch (reg) {
> -	case ARCH_TIMER_REG_CTRL:
> -		asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
> -		break;
> -	case ARCH_TIMER_REG_FREQ:
> -		asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
> -		break;
> -	case ARCH_TIMER_REG_TVAL:
> -		asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
> -		break;
> -	default:
> -		BUG();
> +	if (access == ARCH_TIMER_VIRT_ACCESS) {
> +		switch (reg) {
> +		case ARCH_TIMER_REG_CTRL:
> +			asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val));
> +			break;
> +		case ARCH_TIMER_REG_TVAL:
> +			asm volatile("mrc p15, 0, %0, c14, c3, 0" : "=r" (val));
> +			break;
> +		}
>   	}
>
>   	return val;
>   }
>
> -static irqreturn_t arch_timer_handler(int irq, void *dev_id)
> +static inline cycle_t arch_counter_get_cntpct(void)
>   {
> -	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
> -	unsigned long ctrl;
> +	u32 cvall, cvalh;
> +
> +	asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
> +
> +	return ((cycle_t) cvalh << 32) | cvall;
> +}
> +
> +static inline cycle_t arch_counter_get_cntvct(void)
> +{
> +	u32 cvall, cvalh;
> +
> +	asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
> +
> +	return ((cycle_t) cvalh << 32) | cvall;
> +}

We should use the Q and R qualifiers to avoid compiler misbehavior:
	u64 cval;
	asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cvall));

The compiler generates sad looking code when constructing 64-bit 
quantities with shifts and ORs.  We found this while implementing the 
phys-virt patching for 64-bit phys_addr_t.

Is there value in unifying the physical and virtual counter read 
functions using the access specifier as you've done for the register 
read and write functions?

>
> -	ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
> +static irqreturn_t inline timer_handler(const int access,
> +					struct clock_event_device *evt)
> +{
> +	unsigned long ctrl;
> +	ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
>   	if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
>   		ctrl |= ARCH_TIMER_CTRL_IT_MASK;
> -		arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
> +		arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
>   		evt->event_handler(evt);
>   		return IRQ_HANDLED;
>   	}
> @@ -97,63 +152,100 @@ static irqreturn_t arch_timer_handler(int irq, void *dev_id)
>   	return IRQ_NONE;
>   }
>
> -static void arch_timer_disable(void)
> +static irqreturn_t arch_timer_handler_virt(int irq, void *dev_id)
>   {
> -	unsigned long ctrl;
> +	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
>
> -	ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
> -	ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
> -	arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
> +	return timer_handler(ARCH_TIMER_VIRT_ACCESS, evt);
>   }
>
> -static void arch_timer_set_mode(enum clock_event_mode mode,
> -				struct clock_event_device *clk)
> +static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id)
>   {
> +	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
> +
> +	return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt);
> +}
> +
> +static inline void timer_set_mode(const int access, int mode)
> +{
> +	unsigned long ctrl;
>   	switch (mode) {
>   	case CLOCK_EVT_MODE_UNUSED:
>   	case CLOCK_EVT_MODE_SHUTDOWN:
> -		arch_timer_disable();
> +		ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
> +		ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
> +		arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
>   		break;
>   	default:
>   		break;
>   	}
>   }
>
> -static int arch_timer_set_next_event(unsigned long evt,
> -				     struct clock_event_device *unused)
> +static void arch_timer_set_mode_virt(enum clock_event_mode mode,
> +				     struct clock_event_device *clk)
>   {
> -	unsigned long ctrl;
> +	timer_set_mode(ARCH_TIMER_VIRT_ACCESS, mode);
> +}
> +
> +static void arch_timer_set_mode_phys(enum clock_event_mode mode,
> +				     struct clock_event_device *clk)
> +{
> +	timer_set_mode(ARCH_TIMER_PHYS_ACCESS, mode);
> +}
>
> -	ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
> +static inline void set_next_event(const int access, unsigned long evt)
> +{
> +	unsigned long ctrl;
> +	ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL);
>   	ctrl |= ARCH_TIMER_CTRL_ENABLE;
>   	ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
> +	arch_timer_reg_write(access, ARCH_TIMER_REG_TVAL, evt);
> +	arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl);
> +}
>
> -	arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
> -	arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
> +static int arch_timer_set_next_event_virt(unsigned long evt,
> +					  struct clock_event_device *unused)
> +{
> +	set_next_event(ARCH_TIMER_VIRT_ACCESS, evt);
> +	return 0;
> +}
>
> +static int arch_timer_set_next_event_phys(unsigned long evt,
> +					  struct clock_event_device *unused)
> +{
> +	set_next_event(ARCH_TIMER_PHYS_ACCESS, evt);
>   	return 0;
>   }
>
>   static int __cpuinit arch_timer_setup(struct clock_event_device *clk)
>   {
> -	/* Be safe... */
> -	arch_timer_disable();
> -
>   	clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP;
>   	clk->name = "arch_sys_timer";
>   	clk->rating = 450;
> -	clk->set_mode = arch_timer_set_mode;
> -	clk->set_next_event = arch_timer_set_next_event;
> -	clk->irq = arch_timer_ppi;
> +	if (arch_timer_use_virtual) {
> +		arch_timer_set_mode_virt(CLOCK_EVT_MODE_SHUTDOWN, NULL);
> +		clk->set_mode = arch_timer_set_mode_virt;
> +		clk->set_next_event = arch_timer_set_next_event_virt;
> +	} else {
> +		arch_timer_set_mode_phys(CLOCK_EVT_MODE_SHUTDOWN, NULL);
> +		clk->set_mode = arch_timer_set_mode_phys;
> +		clk->set_next_event = arch_timer_set_next_event_phys;
> +	}
> +		
> +	clk->irq = arch_timer_ppi[PHYS_SECURE_PPI];
>
>   	clockevents_config_and_register(clk, arch_timer_rate,
>   					0xf, 0x7fffffff);
>
>   	*__this_cpu_ptr(arch_timer_evt) = clk;
>
> -	enable_percpu_irq(clk->irq, 0);
> -	if (arch_timer_ppi2)
> -		enable_percpu_irq(arch_timer_ppi2, 0);
> +	if (arch_timer_use_virtual)
> +		enable_percpu_irq(arch_timer_ppi[VIRT_PPI], 0);
> +	else {
> +		enable_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI], 0);
> +		if (arch_timer_ppi[PHYS_NONSECURE_PPI])
> +			enable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], 0);
> +	}
>
>   	return 0;
>   }
> @@ -173,8 +265,7 @@ static int arch_timer_available(void)
>   		return -ENXIO;
>
>   	if (arch_timer_rate == 0) {
> -		arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
> -		freq = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
> +		asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
>
>   		/* Check the timer frequency. */
>   		if (freq == 0) {
> @@ -185,43 +276,42 @@ static int arch_timer_available(void)
>   		arch_timer_rate = freq;
>   	}
>
> -	pr_info_once("Architected local timer running at %lu.%02luMHz.\n",
> -		     arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100);
> +	pr_info_once("Architected local timer running at %lu.%02luMHz (%s).\n",
> +		     arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100,
> +		     arch_timer_use_virtual ? "virt" : "phys");
>   	return 0;
>   }
>
> -static inline cycle_t arch_counter_get_cntpct(void)
> +static u32 notrace arch_counter_get_cntpct32(void)
>   {
> -	u32 cvall, cvalh;
> +	cycle_t cnt = arch_counter_get_cntpct();
>
> -	asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
> -
> -	return ((cycle_t) cvalh << 32) | cvall;
> -}
> -
> -static inline cycle_t arch_counter_get_cntvct(void)
> -{
> -	u32 cvall, cvalh;
> -
> -	asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
> -
> -	return ((cycle_t) cvalh << 32) | cvall;
> +	/*
> +	 * The sched_clock infrastructure only knows about counters
> +	 * with at most 32bits. Forget about the upper 24 bits for the
> +	 * time being...
> +	 */
> +	return (u32)(cnt & (u32)~0);

Wouldn't a return (u32)cnt be sufficient here?

>   }
>
>   static u32 notrace arch_counter_get_cntvct32(void)
>   {
> -	cycle_t cntvct = arch_counter_get_cntvct();
> +	cycle_t cnt = arch_counter_get_cntvct();
>
>   	/*
>   	 * The sched_clock infrastructure only knows about counters
>   	 * with at most 32bits. Forget about the upper 24 bits for the
>   	 * time being...
>   	 */
> -	return (u32)(cntvct & (u32)~0);
> +	return (u32)(cnt & (u32)~0);
>   }
>
>   static cycle_t arch_counter_read(struct clocksource *cs)
>   {
> +	/*
> +	 * Always use the physical counter for the clocksource.
> +	 * CNTHCTL.PL1PCTEN must be set to 1.
> +	 */
>   	return arch_counter_get_cntpct();
>   }
>
> @@ -245,10 +335,16 @@ static void __cpuinit arch_timer_stop(struct clock_event_device *clk)
>   {
>   	pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
>   		 clk->irq, smp_processor_id());
> -	disable_percpu_irq(clk->irq);
> -	if (arch_timer_ppi2)
> -		disable_percpu_irq(arch_timer_ppi2);
> -	arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
> +
> +	if (arch_timer_use_virtual) {
> +		disable_percpu_irq(arch_timer_ppi[VIRT_PPI]);
> +		arch_timer_set_mode_virt(CLOCK_EVT_MODE_UNUSED, clk);
> +	} else {
> +		disable_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI]);
> +		if (arch_timer_ppi[PHYS_NONSECURE_PPI])
> +			disable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI]);
> +		arch_timer_set_mode_phys(CLOCK_EVT_MODE_UNUSED, clk);
> +	}
>   }
>
>   static struct local_timer_ops arch_timer_ops __cpuinitdata = {
> @@ -261,36 +357,44 @@ static struct clock_event_device arch_timer_global_evt;
>   static int __init arch_timer_register(void)
>   {
>   	int err;
> +	int ppi;
>
>   	err = arch_timer_available();
>   	if (err)
> -		return err;
> +		goto out;
>
>   	arch_timer_evt = alloc_percpu(struct clock_event_device *);
> -	if (!arch_timer_evt)
> -		return -ENOMEM;
> +	if (!arch_timer_evt) {
> +		err = -ENOMEM;
> +		goto out;
> +	}
>
>   	clocksource_register_hz(&clocksource_counter, arch_timer_rate);
>
> -	err = request_percpu_irq(arch_timer_ppi, arch_timer_handler,
> -				 "arch_timer", arch_timer_evt);
> +	if (arch_timer_use_virtual) {
> +		ppi = arch_timer_ppi[VIRT_PPI];
> +		err = request_percpu_irq(ppi, arch_timer_handler_virt,
> +					 "arch_timer", arch_timer_evt);
> +	} else {
> +		ppi = arch_timer_ppi[PHYS_SECURE_PPI];
> +		err = request_percpu_irq(ppi, arch_timer_handler_phys,
> +					 "arch_timer", arch_timer_evt);
> +		if (!err && arch_timer_ppi[PHYS_NONSECURE_PPI]) {
> +			ppi = arch_timer_ppi[PHYS_NONSECURE_PPI];
> +			err = request_percpu_irq(ppi, arch_timer_handler_phys,
> +						 "arch_timer", arch_timer_evt);
> +			if (err)
> +				free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
> +						arch_timer_evt);
> +		}
> +	}
> +
>   	if (err) {
>   		pr_err("arch_timer: can't register interrupt %d (%d)\n",
> -		       arch_timer_ppi, err);
> +		       ppi, err);
>   		goto out_free;
>   	}
>
> -	if (arch_timer_ppi2) {
> -		err = request_percpu_irq(arch_timer_ppi2, arch_timer_handler,
> -					 "arch_timer", arch_timer_evt);
> -		if (err) {
> -			pr_err("arch_timer: can't register interrupt %d (%d)\n",
> -			       arch_timer_ppi2, err);
> -			arch_timer_ppi2 = 0;
> -			goto out_free_irq;
> -		}
> -	}
> -
>   	err = local_timer_register(&arch_timer_ops);
>   	if (err) {
>   		/*
> @@ -310,13 +414,19 @@ static int __init arch_timer_register(void)
>   	return 0;
>
>   out_free_irq:
> -	free_percpu_irq(arch_timer_ppi, arch_timer_evt);
> -	if (arch_timer_ppi2)
> -		free_percpu_irq(arch_timer_ppi2, arch_timer_evt);
> +	if (arch_timer_use_virtual)
> +		free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt);
> +	else {
> +		free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
> +				arch_timer_evt);
> +		if (arch_timer_ppi[PHYS_NONSECURE_PPI])
> +			free_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI],
> +					arch_timer_evt);
> +	}
>
>   out_free:
>   	free_percpu(arch_timer_evt);
> -
> +out:
>   	return err;
>   }
>
> @@ -329,6 +439,7 @@ int __init arch_timer_of_register(void)
>   {
>   	struct device_node *np;
>   	u32 freq;
> +	int i;
>
>   	np = of_find_matching_node(NULL, arch_timer_of_match);
>   	if (!np) {
> @@ -340,22 +451,40 @@ int __init arch_timer_of_register(void)
>   	if (!of_property_read_u32(np, "clock-frequency", &freq))
>   		arch_timer_rate = freq;
>
> -	arch_timer_ppi = irq_of_parse_and_map(np, 0);
> -	arch_timer_ppi2 = irq_of_parse_and_map(np, 1);
> -	pr_info("arch_timer: found %s irqs %d %d\n",
> -		np->name, arch_timer_ppi, arch_timer_ppi2);
> +	for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
> +		arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
> +
> +	/*
> +	 * If no interrupt provided for virtual timer, we'll have to
> +	 * stick to the physical timer. It'd better be accessible...
> +	 */
> +	if (!arch_timer_ppi[VIRT_PPI]) {
> +		arch_timer_use_virtual = false;
> +
> +		if (!arch_timer_ppi[PHYS_SECURE_PPI] ||
> +		    !arch_timer_ppi[PHYS_NONSECURE_PPI]) {
> +			pr_warn("arch_timer: No interrupt available, giving up\n");
> +			return -EINVAL;
> +		}
> +	}
>
>   	return arch_timer_register();
>   }
>
>   int __init arch_timer_sched_clock_init(void)
>   {
> +	u32 (*cnt32)(void);
>   	int err;
>
>   	err = arch_timer_available();
>   	if (err)
>   		return err;
>
> -	setup_sched_clock(arch_counter_get_cntvct32, 32, arch_timer_rate);
> +	if (arch_timer_use_virtual)
> +		cnt32 = arch_counter_get_cntvct32;
> +	else
> +		cnt32 = arch_counter_get_cntpct32;
> +
> +	setup_sched_clock(cnt32, 32, arch_timer_rate);
>   	return 0;
>   }
>

-- 
Thanks
- Cyril



More information about the linux-arm-kernel mailing list