diff options
Diffstat (limited to 'arch/arm/cpu/arm926ejs/mxs/timer.c')
| -rw-r--r-- | arch/arm/cpu/arm926ejs/mxs/timer.c | 156 | 
1 files changed, 156 insertions, 0 deletions
| diff --git a/arch/arm/cpu/arm926ejs/mxs/timer.c b/arch/arm/cpu/arm926ejs/mxs/timer.c new file mode 100644 index 000000000..4ed75e604 --- /dev/null +++ b/arch/arm/cpu/arm926ejs/mxs/timer.c @@ -0,0 +1,156 @@ +/* + * Freescale i.MX28 timer driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +/* Maximum fixed count */ +#define TIMER_LOAD_VAL	0xffffffff + +DECLARE_GLOBAL_DATA_PTR; + +#define timestamp (gd->tbl) +#define lastdec (gd->lastinc) + +/* + * This driver uses 1kHz clock source. + */ +#define	MX28_INCREMENTER_HZ		1000 + +static inline unsigned long tick_to_time(unsigned long tick) +{ +	return tick / (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ); +} + +static inline unsigned long time_to_tick(unsigned long time) +{ +	return time * (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ); +} + +/* Calculate how many ticks happen in "us" microseconds */ +static inline unsigned long us_to_tick(unsigned long us) +{ +	return (us * MX28_INCREMENTER_HZ) / 1000000; +} + +int timer_init(void) +{ +	struct mxs_timrot_regs *timrot_regs = +		(struct mxs_timrot_regs *)MXS_TIMROT_BASE; + +	/* Reset Timers and Rotary Encoder module */ +	mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); + +	/* Set fixed_count to 0 */ +	writel(0, &timrot_regs->hw_timrot_fixed_count0); + +	/* Set UPDATE bit and 1Khz frequency */ +	writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | +		TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, +		&timrot_regs->hw_timrot_timctrl0); + +	/* Set fixed_count to maximal value */ +	writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); + +	return 0; +} + +unsigned long long get_ticks(void) +{ +	struct mxs_timrot_regs *timrot_regs = +		(struct mxs_timrot_regs *)MXS_TIMROT_BASE; + +	/* Current tick value */ +	uint32_t now = readl(&timrot_regs->hw_timrot_running_count0); + +	if (lastdec >= now) { +		/* +		 * normal mode (non roll) +		 * move stamp forward with absolut diff ticks +		 */ +		timestamp += (lastdec - now); +	} else { +		/* we have rollover of decrementer */ +		timestamp += (TIMER_LOAD_VAL - now) + lastdec; + +	} +	lastdec = now; + +	return timestamp; +} + +ulong get_timer_masked(void) +{ +	return tick_to_time(get_ticks()); +} + +ulong get_timer(ulong base) +{ +	return get_timer_masked() - base; +} + +/* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ +#define	MX28_HW_DIGCTL_MICROSECONDS	0x8001c0c0 + +void __udelay(unsigned long usec) +{ +	uint32_t old, new, incr; +	uint32_t counter = 0; + +	old = readl(MX28_HW_DIGCTL_MICROSECONDS); + +	while (counter < usec) { +		new = readl(MX28_HW_DIGCTL_MICROSECONDS); + +		/* Check if the timer wrapped. */ +		if (new < old) { +			incr = 0xffffffff - old; +			incr += new; +		} else { +			incr = new - old; +		} + +		/* +		 * Check if we are close to the maximum time and the counter +		 * would wrap if incremented. If that's the case, break out +		 * from the loop as the requested delay time passed. +		 */ +		if (counter + incr < counter) +			break; + +		counter += incr; +		old = new; +	} +} + +ulong get_tbclk(void) +{ +	return MX28_INCREMENTER_HZ; +} |