xref: /openbmc/u-boot/arch/arm/cpu/arm926ejs/mxs/timer.c (revision cf033e04)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
23a0398d7SOtavio Salvador /*
33a0398d7SOtavio Salvador  * Freescale i.MX28 timer driver
43a0398d7SOtavio Salvador  *
53a0398d7SOtavio Salvador  * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
63a0398d7SOtavio Salvador  * on behalf of DENX Software Engineering GmbH
73a0398d7SOtavio Salvador  *
83a0398d7SOtavio Salvador  * Based on code from LTIB:
93a0398d7SOtavio Salvador  * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
103a0398d7SOtavio Salvador  */
113a0398d7SOtavio Salvador 
123a0398d7SOtavio Salvador #include <common.h>
133a0398d7SOtavio Salvador #include <asm/io.h>
143a0398d7SOtavio Salvador #include <asm/arch/imx-regs.h>
153a0398d7SOtavio Salvador #include <asm/arch/sys_proto.h>
163a0398d7SOtavio Salvador 
173a0398d7SOtavio Salvador /* Maximum fixed count */
186ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
196ecd05d2SFadil Berisha #define TIMER_LOAD_VAL 0xffff
206ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
213a0398d7SOtavio Salvador #define TIMER_LOAD_VAL 0xffffffff
226ecd05d2SFadil Berisha #endif
233a0398d7SOtavio Salvador 
243a0398d7SOtavio Salvador DECLARE_GLOBAL_DATA_PTR;
253a0398d7SOtavio Salvador 
2666ee6923SSimon Glass #define timestamp (gd->arch.tbl)
27582601daSSimon Glass #define lastdec (gd->arch.lastinc)
283a0398d7SOtavio Salvador 
293a0398d7SOtavio Salvador /*
303a0398d7SOtavio Salvador  * This driver uses 1kHz clock source.
313a0398d7SOtavio Salvador  */
323e9dc930SFadil Berisha #define	MXS_INCREMENTER_HZ		1000
333a0398d7SOtavio Salvador 
tick_to_time(unsigned long tick)343a0398d7SOtavio Salvador static inline unsigned long tick_to_time(unsigned long tick)
353a0398d7SOtavio Salvador {
363e9dc930SFadil Berisha 	return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
373a0398d7SOtavio Salvador }
383a0398d7SOtavio Salvador 
time_to_tick(unsigned long time)393a0398d7SOtavio Salvador static inline unsigned long time_to_tick(unsigned long time)
403a0398d7SOtavio Salvador {
413e9dc930SFadil Berisha 	return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
423a0398d7SOtavio Salvador }
433a0398d7SOtavio Salvador 
443a0398d7SOtavio Salvador /* Calculate how many ticks happen in "us" microseconds */
us_to_tick(unsigned long us)453a0398d7SOtavio Salvador static inline unsigned long us_to_tick(unsigned long us)
463a0398d7SOtavio Salvador {
473e9dc930SFadil Berisha 	return (us * MXS_INCREMENTER_HZ) / 1000000;
483a0398d7SOtavio Salvador }
493a0398d7SOtavio Salvador 
timer_init(void)503a0398d7SOtavio Salvador int timer_init(void)
513a0398d7SOtavio Salvador {
529c471142SOtavio Salvador 	struct mxs_timrot_regs *timrot_regs =
539c471142SOtavio Salvador 		(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
543a0398d7SOtavio Salvador 
553a0398d7SOtavio Salvador 	/* Reset Timers and Rotary Encoder module */
56fa7a51cbSOtavio Salvador 	mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
573a0398d7SOtavio Salvador 
583a0398d7SOtavio Salvador 	/* Set fixed_count to 0 */
596ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
606ecd05d2SFadil Berisha 	writel(0, &timrot_regs->hw_timrot_timcount0);
616ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
623a0398d7SOtavio Salvador 	writel(0, &timrot_regs->hw_timrot_fixed_count0);
636ecd05d2SFadil Berisha #endif
643a0398d7SOtavio Salvador 
653a0398d7SOtavio Salvador 	/* Set UPDATE bit and 1Khz frequency */
663a0398d7SOtavio Salvador 	writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
673a0398d7SOtavio Salvador 		TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
683a0398d7SOtavio Salvador 		&timrot_regs->hw_timrot_timctrl0);
693a0398d7SOtavio Salvador 
703a0398d7SOtavio Salvador 	/* Set fixed_count to maximal value */
716ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
726ecd05d2SFadil Berisha 	writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0);
736ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
743a0398d7SOtavio Salvador 	writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
756ecd05d2SFadil Berisha #endif
763a0398d7SOtavio Salvador 
773a0398d7SOtavio Salvador 	return 0;
783a0398d7SOtavio Salvador }
793a0398d7SOtavio Salvador 
get_ticks(void)803a0398d7SOtavio Salvador unsigned long long get_ticks(void)
813a0398d7SOtavio Salvador {
829c471142SOtavio Salvador 	struct mxs_timrot_regs *timrot_regs =
839c471142SOtavio Salvador 		(struct mxs_timrot_regs *)MXS_TIMROT_BASE;
846ecd05d2SFadil Berisha 	uint32_t now;
853a0398d7SOtavio Salvador 
863a0398d7SOtavio Salvador 	/* Current tick value */
876ecd05d2SFadil Berisha #if defined(CONFIG_MX23)
886ecd05d2SFadil Berisha 	/* Upper bits are the valid ones. */
896ecd05d2SFadil Berisha 	now = readl(&timrot_regs->hw_timrot_timcount0) >>
906ecd05d2SFadil Berisha 		TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET;
916ecd05d2SFadil Berisha #elif defined(CONFIG_MX28)
926ecd05d2SFadil Berisha 	now = readl(&timrot_regs->hw_timrot_running_count0);
93feb8cf4aSWolfgang Denk #else
94feb8cf4aSWolfgang Denk #error "Don't know how to read timrot_regs"
956ecd05d2SFadil Berisha #endif
963a0398d7SOtavio Salvador 
973a0398d7SOtavio Salvador 	if (lastdec >= now) {
983a0398d7SOtavio Salvador 		/*
993a0398d7SOtavio Salvador 		 * normal mode (non roll)
1003a0398d7SOtavio Salvador 		 * move stamp forward with absolut diff ticks
1013a0398d7SOtavio Salvador 		 */
1023a0398d7SOtavio Salvador 		timestamp += (lastdec - now);
1033a0398d7SOtavio Salvador 	} else {
1043a0398d7SOtavio Salvador 		/* we have rollover of decrementer */
1053a0398d7SOtavio Salvador 		timestamp += (TIMER_LOAD_VAL - now) + lastdec;
1063a0398d7SOtavio Salvador 
1073a0398d7SOtavio Salvador 	}
1083a0398d7SOtavio Salvador 	lastdec = now;
1093a0398d7SOtavio Salvador 
1103a0398d7SOtavio Salvador 	return timestamp;
1113a0398d7SOtavio Salvador }
1123a0398d7SOtavio Salvador 
get_timer(ulong base)1133a0398d7SOtavio Salvador ulong get_timer(ulong base)
1143a0398d7SOtavio Salvador {
115*6180ea7eSPatrick Delaunay 	return tick_to_time(get_ticks()) - base;
1163a0398d7SOtavio Salvador }
1173a0398d7SOtavio Salvador 
1183a0398d7SOtavio Salvador /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
1193e9dc930SFadil Berisha #define	MXS_HW_DIGCTL_MICROSECONDS	0x8001c0c0
1203a0398d7SOtavio Salvador 
__udelay(unsigned long usec)1213a0398d7SOtavio Salvador void __udelay(unsigned long usec)
1223a0398d7SOtavio Salvador {
1233a0398d7SOtavio Salvador 	uint32_t old, new, incr;
1243a0398d7SOtavio Salvador 	uint32_t counter = 0;
1253a0398d7SOtavio Salvador 
1263e9dc930SFadil Berisha 	old = readl(MXS_HW_DIGCTL_MICROSECONDS);
1273a0398d7SOtavio Salvador 
1283a0398d7SOtavio Salvador 	while (counter < usec) {
1293e9dc930SFadil Berisha 		new = readl(MXS_HW_DIGCTL_MICROSECONDS);
1303a0398d7SOtavio Salvador 
1313a0398d7SOtavio Salvador 		/* Check if the timer wrapped. */
1323a0398d7SOtavio Salvador 		if (new < old) {
1333a0398d7SOtavio Salvador 			incr = 0xffffffff - old;
1343a0398d7SOtavio Salvador 			incr += new;
1353a0398d7SOtavio Salvador 		} else {
1363a0398d7SOtavio Salvador 			incr = new - old;
1373a0398d7SOtavio Salvador 		}
1383a0398d7SOtavio Salvador 
1393a0398d7SOtavio Salvador 		/*
1403a0398d7SOtavio Salvador 		 * Check if we are close to the maximum time and the counter
1413a0398d7SOtavio Salvador 		 * would wrap if incremented. If that's the case, break out
1423a0398d7SOtavio Salvador 		 * from the loop as the requested delay time passed.
1433a0398d7SOtavio Salvador 		 */
1443a0398d7SOtavio Salvador 		if (counter + incr < counter)
1453a0398d7SOtavio Salvador 			break;
1463a0398d7SOtavio Salvador 
1473a0398d7SOtavio Salvador 		counter += incr;
1483a0398d7SOtavio Salvador 		old = new;
1493a0398d7SOtavio Salvador 	}
1503a0398d7SOtavio Salvador }
1513a0398d7SOtavio Salvador 
get_tbclk(void)1523a0398d7SOtavio Salvador ulong get_tbclk(void)
1533a0398d7SOtavio Salvador {
1543e9dc930SFadil Berisha 	return MXS_INCREMENTER_HZ;
1553a0398d7SOtavio Salvador }
156