1 // SPDX-License-Identifier: GPL-2.0+
3 * Freescale i.MX28 timer driver
5 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
6 * on behalf of DENX Software Engineering GmbH
8 * Based on code from LTIB:
9 * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
15 #include <asm/global_data.h>
17 #include <asm/arch/imx-regs.h>
18 #include <asm/arch/sys_proto.h>
19 #include <linux/delay.h>
21 /* Maximum fixed count */
22 #if defined(CONFIG_MX23)
23 #define TIMER_LOAD_VAL 0xffff
24 #elif defined(CONFIG_MX28)
25 #define TIMER_LOAD_VAL 0xffffffff
28 DECLARE_GLOBAL_DATA_PTR;
30 #define timestamp (gd->arch.tbl)
31 #define lastdec (gd->arch.lastinc)
34 * This driver uses 1kHz clock source.
36 #define MXS_INCREMENTER_HZ 1000
38 static inline unsigned long tick_to_time(unsigned long tick)
40 return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
43 static inline unsigned long time_to_tick(unsigned long time)
45 return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
48 /* Calculate how many ticks happen in "us" microseconds */
49 static inline unsigned long us_to_tick(unsigned long us)
51 return (us * MXS_INCREMENTER_HZ) / 1000000;
56 struct mxs_timrot_regs *timrot_regs =
57 (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
59 /* Reset Timers and Rotary Encoder module */
60 mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
62 /* Set fixed_count to 0 */
63 #if defined(CONFIG_MX23)
64 writel(0, &timrot_regs->hw_timrot_timcount0);
65 #elif defined(CONFIG_MX28)
66 writel(0, &timrot_regs->hw_timrot_fixed_count0);
69 /* Set UPDATE bit and 1Khz frequency */
70 writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
71 TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
72 &timrot_regs->hw_timrot_timctrl0);
74 /* Set fixed_count to maximal value */
75 #if defined(CONFIG_MX23)
76 writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0);
77 #elif defined(CONFIG_MX28)
78 writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
84 unsigned long long get_ticks(void)
86 struct mxs_timrot_regs *timrot_regs =
87 (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
90 /* Current tick value */
91 #if defined(CONFIG_MX23)
92 /* Upper bits are the valid ones. */
93 now = readl(&timrot_regs->hw_timrot_timcount0) >>
94 TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET;
95 #elif defined(CONFIG_MX28)
96 now = readl(&timrot_regs->hw_timrot_running_count0);
98 #error "Don't know how to read timrot_regs"
101 if (lastdec >= now) {
103 * normal mode (non roll)
104 * move stamp forward with absolut diff ticks
106 timestamp += (lastdec - now);
108 /* we have rollover of decrementer */
109 timestamp += (TIMER_LOAD_VAL - now) + lastdec;
117 ulong get_timer(ulong base)
119 return tick_to_time(get_ticks()) - base;
122 /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
123 #define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0
125 void __udelay(unsigned long usec)
127 uint32_t old, new, incr;
128 uint32_t counter = 0;
130 old = readl(MXS_HW_DIGCTL_MICROSECONDS);
132 while (counter < usec) {
133 new = readl(MXS_HW_DIGCTL_MICROSECONDS);
135 /* Check if the timer wrapped. */
137 incr = 0xffffffff - old;
144 * Check if we are close to the maximum time and the counter
145 * would wrap if incremented. If that's the case, break out
146 * from the loop as the requested delay time passed.
148 if (counter + incr < counter)
156 ulong get_tbclk(void)
158 return MXS_INCREMENTER_HZ;