|  | @@ -54,3 +54,136 @@ static bool arch_timer_use_virtual = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define ARCH_TIMER_REG_CTRL		0
 | 
	
		
			
				|  |  |  #define ARCH_TIMER_REG_FREQ		1
 | 
	
		
			
				|  |  | +#define ARCH_TIMER_REG_TVAL		2
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#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)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	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 inline u32 arch_timer_reg_read(const int access, const int reg)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	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;
 | 
	
		
			
				|  |  | +		case ARCH_TIMER_REG_FREQ:
 | 
	
		
			
				|  |  | +			asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
 | 
	
		
			
				|  |  | +			break;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	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 inline cycle_t arch_timer_counter_read(const int access)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	cycle_t cval = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (access == ARCH_TIMER_PHYS_ACCESS)
 | 
	
		
			
				|  |  | +		asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (access == ARCH_TIMER_VIRT_ACCESS)
 | 
	
		
			
				|  |  | +		asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return cval;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static inline cycle_t arch_counter_get_cntpct(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	return arch_timer_counter_read(ARCH_TIMER_PHYS_ACCESS);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static inline cycle_t arch_counter_get_cntvct(void)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	return arch_timer_counter_read(ARCH_TIMER_VIRT_ACCESS);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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(access, ARCH_TIMER_REG_CTRL, ctrl);
 | 
	
		
			
				|  |  | +		evt->event_handler(evt);
 | 
	
		
			
				|  |  | +		return IRQ_HANDLED;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return IRQ_NONE;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static irqreturn_t arch_timer_handler_virt(int irq, void *dev_id)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct clock_event_device *evt = *(struct clock_event_device **)dev_id;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return timer_handler(ARCH_TIMER_VIRT_ACCESS, evt);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +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:
 | 
	
		
			
				|  |  | +		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;
 |