xref: /openbmc/u-boot/arch/arm/cpu/arm926ejs/mxs/timer.c (revision feb8cf4a)
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  *
101a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
113a0398d7SOtavio Salvador  */
123a0398d7SOtavio Salvador 
133a0398d7SOtavio Salvador #include <common.h>
143a0398d7SOtavio Salvador #include <asm/io.h>
153a0398d7SOtavio Salvador #include <asm/arch/imx-regs.h>
163a0398d7SOtavio Salvador #include <asm/arch/sys_proto.h>
173a0398d7SOtavio Salvador 
183a0398d7SOtavio Salvador /* Maximum fixed count */
196ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
206ecd05d2SFadil Berisha #define TIMER_LOAD_VAL 0xffff
216ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
223a0398d7SOtavio Salvador #define TIMER_LOAD_VAL 0xffffffff
236ecd05d2SFadil Berisha #endif
243a0398d7SOtavio Salvador 
253a0398d7SOtavio Salvador DECLARE_GLOBAL_DATA_PTR;
263a0398d7SOtavio Salvador 
2766ee6923SSimon Glass #define timestamp (gd->arch.tbl)
28582601daSSimon Glass #define lastdec (gd->arch.lastinc)
293a0398d7SOtavio Salvador 
303a0398d7SOtavio Salvador /*
313a0398d7SOtavio Salvador  * This driver uses 1kHz clock source.
323a0398d7SOtavio Salvador  */
333e9dc930SFadil Berisha #define	MXS_INCREMENTER_HZ		1000
343a0398d7SOtavio Salvador 
353a0398d7SOtavio Salvador static inline unsigned long tick_to_time(unsigned long tick)
363a0398d7SOtavio Salvador {
373e9dc930SFadil Berisha 	return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
383a0398d7SOtavio Salvador }
393a0398d7SOtavio Salvador 
403a0398d7SOtavio Salvador static inline unsigned long time_to_tick(unsigned long time)
413a0398d7SOtavio Salvador {
423e9dc930SFadil Berisha 	return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
433a0398d7SOtavio Salvador }
443a0398d7SOtavio Salvador 
453a0398d7SOtavio Salvador /* Calculate how many ticks happen in "us" microseconds */
463a0398d7SOtavio Salvador static inline unsigned long us_to_tick(unsigned long us)
473a0398d7SOtavio Salvador {
483e9dc930SFadil Berisha 	return (us * MXS_INCREMENTER_HZ) / 1000000;
493a0398d7SOtavio Salvador }
503a0398d7SOtavio Salvador 
513a0398d7SOtavio Salvador int timer_init(void)
523a0398d7SOtavio Salvador {
539c471142SOtavio Salvador 	struct mxs_timrot_regs *timrot_regs =
549c471142SOtavio Salvador 		(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
553a0398d7SOtavio Salvador 
563a0398d7SOtavio Salvador 	/* Reset Timers and Rotary Encoder module */
57fa7a51cbSOtavio Salvador 	mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
583a0398d7SOtavio Salvador 
593a0398d7SOtavio Salvador 	/* Set fixed_count to 0 */
606ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
616ecd05d2SFadil Berisha 	writel(0, &timrot_regs->hw_timrot_timcount0);
626ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
633a0398d7SOtavio Salvador 	writel(0, &timrot_regs->hw_timrot_fixed_count0);
646ecd05d2SFadil Berisha #endif
653a0398d7SOtavio Salvador 
663a0398d7SOtavio Salvador 	/* Set UPDATE bit and 1Khz frequency */
673a0398d7SOtavio Salvador 	writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
683a0398d7SOtavio Salvador 		TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
693a0398d7SOtavio Salvador 		&timrot_regs->hw_timrot_timctrl0);
703a0398d7SOtavio Salvador 
713a0398d7SOtavio Salvador 	/* Set fixed_count to maximal value */
726ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
736ecd05d2SFadil Berisha 	writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0);
746ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
753a0398d7SOtavio Salvador 	writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
766ecd05d2SFadil Berisha #endif
773a0398d7SOtavio Salvador 
783a0398d7SOtavio Salvador 	return 0;
793a0398d7SOtavio Salvador }
803a0398d7SOtavio Salvador 
813a0398d7SOtavio Salvador unsigned long long get_ticks(void)
823a0398d7SOtavio Salvador {
839c471142SOtavio Salvador 	struct mxs_timrot_regs *timrot_regs =
849c471142SOtavio Salvador 		(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
856ecd05d2SFadil Berisha 	uint32_t now;
863a0398d7SOtavio Salvador 
873a0398d7SOtavio Salvador 	/* Current tick value */
886ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
896ecd05d2SFadil Berisha 	/* Upper bits are the valid ones. */
906ecd05d2SFadil Berisha 	now = readl(&timrot_regs->hw_timrot_timcount0) >>
916ecd05d2SFadil Berisha 		TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET;
926ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
936ecd05d2SFadil Berisha 	now = readl(&timrot_regs->hw_timrot_running_count0);
94*feb8cf4aSWolfgang Denk #else
95*feb8cf4aSWolfgang Denk #error "Don't know how to read timrot_regs"
966ecd05d2SFadil Berisha #endif
973a0398d7SOtavio Salvador 
983a0398d7SOtavio Salvador 	if (lastdec >= now) {
993a0398d7SOtavio Salvador 		/*
1003a0398d7SOtavio Salvador 		 * normal mode (non roll)
1013a0398d7SOtavio Salvador 		 * move stamp forward with absolut diff ticks
1023a0398d7SOtavio Salvador 		 */
1033a0398d7SOtavio Salvador 		timestamp += (lastdec - now);
1043a0398d7SOtavio Salvador 	} else {
1053a0398d7SOtavio Salvador 		/* we have rollover of decrementer */
1063a0398d7SOtavio Salvador 		timestamp += (TIMER_LOAD_VAL - now) + lastdec;
1073a0398d7SOtavio Salvador 
1083a0398d7SOtavio Salvador 	}
1093a0398d7SOtavio Salvador 	lastdec = now;
1103a0398d7SOtavio Salvador 
1113a0398d7SOtavio Salvador 	return timestamp;
1123a0398d7SOtavio Salvador }
1133a0398d7SOtavio Salvador 
1143a0398d7SOtavio Salvador ulong get_timer_masked(void)
1153a0398d7SOtavio Salvador {
1163a0398d7SOtavio Salvador 	return tick_to_time(get_ticks());
1173a0398d7SOtavio Salvador }
1183a0398d7SOtavio Salvador 
1193a0398d7SOtavio Salvador ulong get_timer(ulong base)
1203a0398d7SOtavio Salvador {
1213a0398d7SOtavio Salvador 	return get_timer_masked() - base;
1223a0398d7SOtavio Salvador }
1233a0398d7SOtavio Salvador 
1243a0398d7SOtavio Salvador /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
1253e9dc930SFadil Berisha #define	MXS_HW_DIGCTL_MICROSECONDS	0x8001c0c0
1263a0398d7SOtavio Salvador 
1273a0398d7SOtavio Salvador void __udelay(unsigned long usec)
1283a0398d7SOtavio Salvador {
1293a0398d7SOtavio Salvador 	uint32_t old, new, incr;
1303a0398d7SOtavio Salvador 	uint32_t counter = 0;
1313a0398d7SOtavio Salvador 
1323e9dc930SFadil Berisha 	old = readl(MXS_HW_DIGCTL_MICROSECONDS);
1333a0398d7SOtavio Salvador 
1343a0398d7SOtavio Salvador 	while (counter < usec) {
1353e9dc930SFadil Berisha 		new = readl(MXS_HW_DIGCTL_MICROSECONDS);
1363a0398d7SOtavio Salvador 
1373a0398d7SOtavio Salvador 		/* Check if the timer wrapped. */
1383a0398d7SOtavio Salvador 		if (new < old) {
1393a0398d7SOtavio Salvador 			incr = 0xffffffff - old;
1403a0398d7SOtavio Salvador 			incr += new;
1413a0398d7SOtavio Salvador 		} else {
1423a0398d7SOtavio Salvador 			incr = new - old;
1433a0398d7SOtavio Salvador 		}
1443a0398d7SOtavio Salvador 
1453a0398d7SOtavio Salvador 		/*
1463a0398d7SOtavio Salvador 		 * Check if we are close to the maximum time and the counter
1473a0398d7SOtavio Salvador 		 * would wrap if incremented. If that's the case, break out
1483a0398d7SOtavio Salvador 		 * from the loop as the requested delay time passed.
1493a0398d7SOtavio Salvador 		 */
1503a0398d7SOtavio Salvador 		if (counter + incr < counter)
1513a0398d7SOtavio Salvador 			break;
1523a0398d7SOtavio Salvador 
1533a0398d7SOtavio Salvador 		counter += incr;
1543a0398d7SOtavio Salvador 		old = new;
1553a0398d7SOtavio Salvador 	}
1563a0398d7SOtavio Salvador }
1573a0398d7SOtavio Salvador 
1583a0398d7SOtavio Salvador ulong get_tbclk(void)
1593a0398d7SOtavio Salvador {
1603e9dc930SFadil Berisha 	return MXS_INCREMENTER_HZ;
1613a0398d7SOtavio Salvador }
162