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 * See file CREDITS for list of people who contributed to this
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
31 #include <asm/arch/imx-regs.h>
32 #include <asm/arch/sys_proto.h>
34 /* Maximum fixed count */
35 #define TIMER_LOAD_VAL 0xffffffff
37 DECLARE_GLOBAL_DATA_PTR;
39 #define timestamp (gd->tbl)
40 #define lastdec (gd->lastinc)
43 * This driver uses 1kHz clock source.
45 #define MX28_INCREMENTER_HZ 1000
47 static inline unsigned long tick_to_time(unsigned long tick)
49 return tick / (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
52 static inline unsigned long time_to_tick(unsigned long time)
54 return time * (MX28_INCREMENTER_HZ / CONFIG_SYS_HZ);
57 /* Calculate how many ticks happen in "us" microseconds */
58 static inline unsigned long us_to_tick(unsigned long us)
60 return (us * MX28_INCREMENTER_HZ) / 1000000;
65 struct mx28_timrot_regs *timrot_regs =
66 (struct mx28_timrot_regs *)MXS_TIMROT_BASE;
68 /* Reset Timers and Rotary Encoder module */
69 mx28_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
71 /* Set fixed_count to 0 */
72 writel(0, &timrot_regs->hw_timrot_fixed_count0);
74 /* Set UPDATE bit and 1Khz frequency */
75 writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
76 TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
77 &timrot_regs->hw_timrot_timctrl0);
79 /* Set fixed_count to maximal value */
80 writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
85 unsigned long long get_ticks(void)
87 struct mx28_timrot_regs *timrot_regs =
88 (struct mx28_timrot_regs *)MXS_TIMROT_BASE;
90 /* Current tick value */
91 uint32_t now = readl(&timrot_regs->hw_timrot_running_count0);
95 * normal mode (non roll)
96 * move stamp forward with absolut diff ticks
98 timestamp += (lastdec - now);
100 /* we have rollover of decrementer */
101 timestamp += (TIMER_LOAD_VAL - now) + lastdec;
109 ulong get_timer_masked(void)
111 return tick_to_time(get_ticks());
114 ulong get_timer(ulong base)
116 return get_timer_masked() - base;
119 /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
120 #define MX28_HW_DIGCTL_MICROSECONDS 0x8001c0c0
122 void __udelay(unsigned long usec)
124 uint32_t old, new, incr;
125 uint32_t counter = 0;
127 old = readl(MX28_HW_DIGCTL_MICROSECONDS);
129 while (counter < usec) {
130 new = readl(MX28_HW_DIGCTL_MICROSECONDS);
132 /* Check if the timer wrapped. */
134 incr = 0xffffffff - old;
141 * Check if we are close to the maximum time and the counter
142 * would wrap if incremented. If that's the case, break out
143 * from the loop as the requested delay time passed.
145 if (counter + incr < counter)
153 ulong get_tbclk(void)
155 return MX28_INCREMENTER_HZ;