2 * Freescale i.MX28 timer driver
4 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
5 * on behalf of DENX Software Engineering GmbH
7 * Based on code from LTIB:
8 * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
10 * SPDX-License-Identifier: GPL-2.0+
15 #include <asm/arch/imx-regs.h>
16 #include <asm/arch/sys_proto.h>
18 /* Maximum fixed count */
19 #if defined(CONFIG_MX23)
20 #define TIMER_LOAD_VAL 0xffff
21 #elif defined(CONFIG_MX28)
22 #define TIMER_LOAD_VAL 0xffffffff
25 DECLARE_GLOBAL_DATA_PTR;
27 /* Enable this to verify that the code can correctly
28 * handle the timer rollover
30 /* #define DEBUG_TIMER_WRAP */
32 #ifdef DEBUG_TIMER_WRAP
34 * Let the timer wrap 15 seconds after start to catch misbehaving
35 * timer related code early
37 #define TIMER_START (-time_to_tick(15 * CONFIG_SYS_HZ))
39 #define TIMER_START 0UL
43 * This driver uses 1kHz clock source.
45 #define MXS_INCREMENTER_HZ 1000
47 static inline unsigned long tick_to_time(unsigned long tick)
49 return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
52 static inline unsigned long time_to_tick(unsigned long time)
54 return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
59 struct mxs_timrot_regs *timrot_regs =
60 (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
62 /* Reset Timers and Rotary Encoder module */
63 mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
65 /* Set fixed_count to 0 */
66 #if defined(CONFIG_MX23)
67 writel(0, &timrot_regs->hw_timrot_timcount0);
68 #elif defined(CONFIG_MX28)
69 writel(0, &timrot_regs->hw_timrot_fixed_count0);
72 /* Set UPDATE bit and 1Khz frequency */
73 writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
74 TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
75 &timrot_regs->hw_timrot_timctrl0);
77 #ifndef DEBUG_TIMER_WRAP
78 /* Set fixed_count to maximum value */
79 #if defined(CONFIG_MX23)
80 writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0);
81 #elif defined(CONFIG_MX28)
82 writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
84 #else /* DEBUG_TIMER_WRAP */
85 /* Set fixed_count so that the counter will wrap after 20 seconds */
86 #if defined(CONFIG_MX23)
87 writel(20 * MXS_INCREMENTER_HZ - 1, &timrot_regs->hw_timrot_timcount0);
88 #elif defined(CONFIG_MX28)
89 writel(20 * MXS_INCREMENTER_HZ,
90 &timrot_regs->hw_timrot_fixed_count0);
92 gd->arch.lastinc = TIMER_LOAD_VAL - 20 * MXS_INCREMENTER_HZ;
94 /* Make the usec counter roll over 30 seconds after startup */
95 writel(-30000000, MXS_HW_DIGCTL_MICROSECONDS);
96 #endif /* DEBUG_TIMER_WRAP */
97 writel(TIMROT_TIMCTRLn_UPDATE,
98 &timrot_regs->hw_timrot_timctrl0_clr);
99 #ifdef DEBUG_TIMER_WRAP
100 /* Set fixed_count to maximum value for subsequent loads */
101 #if defined(CONFIG_MX23)
102 writel(20 * MXS_INCREMENTER_HZ - 1, &timrot_regs->hw_timrot_timcount0);
103 #elif defined(CONFIG_MX28)
104 writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
106 #endif /* DEBUG_TIMER_WRAP */
107 gd->arch.timer_rate_hz = MXS_INCREMENTER_HZ;
108 gd->arch.tbl = TIMER_START;
113 /* Note: This function works correctly for TIMER_LOAD_VAL == 0xffffffff!
114 * The rollover is handled automagically due to the properties of
115 * two's complement arithmetic.
116 * For any other value of TIMER_LOAD_VAL the calculations would have
117 * to be done modulus(TIMER_LOAD_VAL + 1).
119 unsigned long long get_ticks(void)
121 struct mxs_timrot_regs *timrot_regs =
122 (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
124 #if defined(CONFIG_MX23)
125 /* Upper bits are the valid ones. */
126 now = readl(&timrot_regs->hw_timrot_timcount0) >>
127 TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET;
128 #elif defined(CONFIG_MX28)
129 /* The timer is counting down, so subtract the register value from
130 * the counter period length (implicitly 2^32) to get an incrementing
133 now = -readl(&timrot_regs->hw_timrot_running_count0);
135 #error "Don't know how to read timrot_regs"
137 ulong inc = now - gd->arch.lastinc;
139 if (gd->arch.tbl + inc < gd->arch.tbl)
142 gd->arch.lastinc = now;
143 return ((unsigned long long)gd->arch.tbu << 32) | gd->arch.tbl;
146 ulong get_timer_masked(void)
148 return tick_to_time(get_ticks());
151 ulong get_timer(ulong base)
153 /* NOTE: time_to_tick(base) is required to correctly handle rollover! */
154 return tick_to_time(get_ticks() - time_to_tick(base));
157 /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
158 #define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0
160 void __udelay(unsigned long usec)
162 uint32_t start = readl(MXS_HW_DIGCTL_MICROSECONDS);
164 while (readl(MXS_HW_DIGCTL_MICROSECONDS) - start <= usec)
165 /* use '<=' to guarantee a delay of _at least_
166 * the given number of microseconds.
167 * No need for fancy rollover checks
168 * Two's complement arithmetic applied correctly
169 * does everything that's needed automagically!
174 ulong get_tbclk(void)
176 return gd->arch.timer_rate_hz;