xref: /openbmc/u-boot/arch/arm/cpu/arm926ejs/mxs/timer.c (revision fa7a51cb)
13a0398d7SOtavio Salvador /*
23a0398d7SOtavio Salvador  * Freescale i.MX28 timer driver
33a0398d7SOtavio Salvador  *
43a0398d7SOtavio Salvador  * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
53a0398d7SOtavio Salvador  * on behalf of DENX Software Engineering GmbH
63a0398d7SOtavio Salvador  *
73a0398d7SOtavio Salvador  * Based on code from LTIB:
83a0398d7SOtavio Salvador  * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
93a0398d7SOtavio Salvador  *
103a0398d7SOtavio Salvador  * See file CREDITS for list of people who contributed to this
113a0398d7SOtavio Salvador  * project.
123a0398d7SOtavio Salvador  *
133a0398d7SOtavio Salvador  * This program is free software; you can redistribute it and/or
143a0398d7SOtavio Salvador  * modify it under the terms of the GNU General Public License as
153a0398d7SOtavio Salvador  * published by the Free Software Foundation; either version 2 of
163a0398d7SOtavio Salvador  * the License, or (at your option) any later version.
173a0398d7SOtavio Salvador  *
183a0398d7SOtavio Salvador  * This program is distributed in the hope that it will be useful,
193a0398d7SOtavio Salvador  * but WITHOUT ANY WARRANTY; without even the implied warranty of
203a0398d7SOtavio Salvador  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
213a0398d7SOtavio Salvador  * GNU General Public License for more details.
223a0398d7SOtavio Salvador  *
233a0398d7SOtavio Salvador  * You should have received a copy of the GNU General Public License
243a0398d7SOtavio Salvador  * along with this program; if not, write to the Free Software
253a0398d7SOtavio Salvador  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
263a0398d7SOtavio Salvador  * MA 02111-1307 USA
273a0398d7SOtavio Salvador  */
283a0398d7SOtavio Salvador 
293a0398d7SOtavio Salvador #include <common.h>
303a0398d7SOtavio Salvador #include <asm/io.h>
313a0398d7SOtavio Salvador #include <asm/arch/imx-regs.h>
323a0398d7SOtavio Salvador #include <asm/arch/sys_proto.h>
333a0398d7SOtavio Salvador 
343a0398d7SOtavio Salvador /* Maximum fixed count */
353a0398d7SOtavio Salvador #define TIMER_LOAD_VAL	0xffffffff
363a0398d7SOtavio Salvador 
373a0398d7SOtavio Salvador DECLARE_GLOBAL_DATA_PTR;
383a0398d7SOtavio Salvador 
393a0398d7SOtavio Salvador #define timestamp (gd->tbl)
403a0398d7SOtavio Salvador #define lastdec (gd->lastinc)
413a0398d7SOtavio Salvador 
423a0398d7SOtavio Salvador /*
433a0398d7SOtavio Salvador  * This driver uses 1kHz clock source.
443a0398d7SOtavio Salvador  */
453a0398d7SOtavio Salvador #define	MX28_INCREMENTER_HZ		1000
463a0398d7SOtavio Salvador 
473a0398d7SOtavio Salvador static inline unsigned long tick_to_time(unsigned long tick)
483a0398d7SOtavio Salvador {
493a0398d7SOtavio Salvador 	return tick / (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
503a0398d7SOtavio Salvador }
513a0398d7SOtavio Salvador 
523a0398d7SOtavio Salvador static inline unsigned long time_to_tick(unsigned long time)
533a0398d7SOtavio Salvador {
543a0398d7SOtavio Salvador 	return time * (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
553a0398d7SOtavio Salvador }
563a0398d7SOtavio Salvador 
573a0398d7SOtavio Salvador /* Calculate how many ticks happen in "us" microseconds */
583a0398d7SOtavio Salvador static inline unsigned long us_to_tick(unsigned long us)
593a0398d7SOtavio Salvador {
603a0398d7SOtavio Salvador 	return (us * MX28_INCREMENTER_HZ) / 1000000;
613a0398d7SOtavio Salvador }
623a0398d7SOtavio Salvador 
633a0398d7SOtavio Salvador int timer_init(void)
643a0398d7SOtavio Salvador {
659c471142SOtavio Salvador 	struct mxs_timrot_regs *timrot_regs =
669c471142SOtavio Salvador 		(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
673a0398d7SOtavio Salvador 
683a0398d7SOtavio Salvador 	/* Reset Timers and Rotary Encoder module */
69*fa7a51cbSOtavio Salvador 	mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
703a0398d7SOtavio Salvador 
713a0398d7SOtavio Salvador 	/* Set fixed_count to 0 */
723a0398d7SOtavio Salvador 	writel(0, &timrot_regs->hw_timrot_fixed_count0);
733a0398d7SOtavio Salvador 
743a0398d7SOtavio Salvador 	/* Set UPDATE bit and 1Khz frequency */
753a0398d7SOtavio Salvador 	writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
763a0398d7SOtavio Salvador 		TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
773a0398d7SOtavio Salvador 		&timrot_regs->hw_timrot_timctrl0);
783a0398d7SOtavio Salvador 
793a0398d7SOtavio Salvador 	/* Set fixed_count to maximal value */
803a0398d7SOtavio Salvador 	writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
813a0398d7SOtavio Salvador 
823a0398d7SOtavio Salvador 	return 0;
833a0398d7SOtavio Salvador }
843a0398d7SOtavio Salvador 
853a0398d7SOtavio Salvador unsigned long long get_ticks(void)
863a0398d7SOtavio Salvador {
879c471142SOtavio Salvador 	struct mxs_timrot_regs *timrot_regs =
889c471142SOtavio Salvador 		(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
893a0398d7SOtavio Salvador 
903a0398d7SOtavio Salvador 	/* Current tick value */
913a0398d7SOtavio Salvador 	uint32_t now = readl(&timrot_regs->hw_timrot_running_count0);
923a0398d7SOtavio Salvador 
933a0398d7SOtavio Salvador 	if (lastdec >= now) {
943a0398d7SOtavio Salvador 		/*
953a0398d7SOtavio Salvador 		 * normal mode (non roll)
963a0398d7SOtavio Salvador 		 * move stamp forward with absolut diff ticks
973a0398d7SOtavio Salvador 		 */
983a0398d7SOtavio Salvador 		timestamp += (lastdec - now);
993a0398d7SOtavio Salvador 	} else {
1003a0398d7SOtavio Salvador 		/* we have rollover of decrementer */
1013a0398d7SOtavio Salvador 		timestamp += (TIMER_LOAD_VAL - now) + lastdec;
1023a0398d7SOtavio Salvador 
1033a0398d7SOtavio Salvador 	}
1043a0398d7SOtavio Salvador 	lastdec = now;
1053a0398d7SOtavio Salvador 
1063a0398d7SOtavio Salvador 	return timestamp;
1073a0398d7SOtavio Salvador }
1083a0398d7SOtavio Salvador 
1093a0398d7SOtavio Salvador ulong get_timer_masked(void)
1103a0398d7SOtavio Salvador {
1113a0398d7SOtavio Salvador 	return tick_to_time(get_ticks());
1123a0398d7SOtavio Salvador }
1133a0398d7SOtavio Salvador 
1143a0398d7SOtavio Salvador ulong get_timer(ulong base)
1153a0398d7SOtavio Salvador {
1163a0398d7SOtavio Salvador 	return get_timer_masked() - base;
1173a0398d7SOtavio Salvador }
1183a0398d7SOtavio Salvador 
1193a0398d7SOtavio Salvador /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
1203a0398d7SOtavio Salvador #define	MX28_HW_DIGCTL_MICROSECONDS	0x8001c0c0
1213a0398d7SOtavio Salvador 
1223a0398d7SOtavio Salvador void __udelay(unsigned long usec)
1233a0398d7SOtavio Salvador {
1243a0398d7SOtavio Salvador 	uint32_t old, new, incr;
1253a0398d7SOtavio Salvador 	uint32_t counter = 0;
1263a0398d7SOtavio Salvador 
1273a0398d7SOtavio Salvador 	old = readl(MX28_HW_DIGCTL_MICROSECONDS);
1283a0398d7SOtavio Salvador 
1293a0398d7SOtavio Salvador 	while (counter < usec) {
1303a0398d7SOtavio Salvador 		new = readl(MX28_HW_DIGCTL_MICROSECONDS);
1313a0398d7SOtavio Salvador 
1323a0398d7SOtavio Salvador 		/* Check if the timer wrapped. */
1333a0398d7SOtavio Salvador 		if (new < old) {
1343a0398d7SOtavio Salvador 			incr = 0xffffffff - old;
1353a0398d7SOtavio Salvador 			incr += new;
1363a0398d7SOtavio Salvador 		} else {
1373a0398d7SOtavio Salvador 			incr = new - old;
1383a0398d7SOtavio Salvador 		}
1393a0398d7SOtavio Salvador 
1403a0398d7SOtavio Salvador 		/*
1413a0398d7SOtavio Salvador 		 * Check if we are close to the maximum time and the counter
1423a0398d7SOtavio Salvador 		 * would wrap if incremented. If that's the case, break out
1433a0398d7SOtavio Salvador 		 * from the loop as the requested delay time passed.
1443a0398d7SOtavio Salvador 		 */
1453a0398d7SOtavio Salvador 		if (counter + incr < counter)
1463a0398d7SOtavio Salvador 			break;
1473a0398d7SOtavio Salvador 
1483a0398d7SOtavio Salvador 		counter += incr;
1493a0398d7SOtavio Salvador 		old = new;
1503a0398d7SOtavio Salvador 	}
1513a0398d7SOtavio Salvador }
1523a0398d7SOtavio Salvador 
1533a0398d7SOtavio Salvador ulong get_tbclk(void)
1543a0398d7SOtavio Salvador {
1553a0398d7SOtavio Salvador 	return MX28_INCREMENTER_HZ;
1563a0398d7SOtavio Salvador }
157