]>
Commit | Line | Data |
---|---|---|
6e9a0a39 MV |
1 | /* |
2 | * Freescale i.MX28 timer driver | |
3 | * | |
4 | * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> | |
5 | * on behalf of DENX Software Engineering GmbH | |
6 | * | |
7 | * Based on code from LTIB: | |
8 | * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. | |
9 | * | |
3765b3e7 | 10 | * SPDX-License-Identifier: GPL-2.0+ |
6e9a0a39 MV |
11 | */ |
12 | ||
13 | #include <common.h> | |
14 | #include <asm/io.h> | |
15 | #include <asm/arch/imx-regs.h> | |
16 | #include <asm/arch/sys_proto.h> | |
17 | ||
18 | /* Maximum fixed count */ | |
6ecd05d2 FB |
19 | #if defined(CONFIG_MX23) |
20 | #define TIMER_LOAD_VAL 0xffff | |
21 | #elif defined(CONFIG_MX28) | |
22 | #define TIMER_LOAD_VAL 0xffffffff | |
23 | #endif | |
6e9a0a39 MV |
24 | |
25 | DECLARE_GLOBAL_DATA_PTR; | |
26 | ||
66ee6923 | 27 | #define timestamp (gd->arch.tbl) |
582601da | 28 | #define lastdec (gd->arch.lastinc) |
6e9a0a39 MV |
29 | |
30 | /* | |
31 | * This driver uses 1kHz clock source. | |
32 | */ | |
3e9dc930 | 33 | #define MXS_INCREMENTER_HZ 1000 |
6e9a0a39 MV |
34 | |
35 | static inline unsigned long tick_to_time(unsigned long tick) | |
36 | { | |
3e9dc930 | 37 | return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); |
6e9a0a39 MV |
38 | } |
39 | ||
40 | static inline unsigned long time_to_tick(unsigned long time) | |
41 | { | |
3e9dc930 | 42 | return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ); |
6e9a0a39 MV |
43 | } |
44 | ||
45 | /* Calculate how many ticks happen in "us" microseconds */ | |
46 | static inline unsigned long us_to_tick(unsigned long us) | |
47 | { | |
3e9dc930 | 48 | return (us * MXS_INCREMENTER_HZ) / 1000000; |
6e9a0a39 MV |
49 | } |
50 | ||
51 | int timer_init(void) | |
52 | { | |
9c471142 OS |
53 | struct mxs_timrot_regs *timrot_regs = |
54 | (struct mxs_timrot_regs *)MXS_TIMROT_BASE; | |
6e9a0a39 MV |
55 | |
56 | /* Reset Timers and Rotary Encoder module */ | |
fa7a51cb | 57 | mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg); |
6e9a0a39 MV |
58 | |
59 | /* Set fixed_count to 0 */ | |
6ecd05d2 FB |
60 | #if defined(CONFIG_MX23) |
61 | writel(0, &timrot_regs->hw_timrot_timcount0); | |
62 | #elif defined(CONFIG_MX28) | |
6e9a0a39 | 63 | writel(0, &timrot_regs->hw_timrot_fixed_count0); |
6ecd05d2 | 64 | #endif |
6e9a0a39 MV |
65 | |
66 | /* Set UPDATE bit and 1Khz frequency */ | |
67 | writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD | | |
68 | TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL, | |
69 | &timrot_regs->hw_timrot_timctrl0); | |
70 | ||
71 | /* Set fixed_count to maximal value */ | |
6ecd05d2 FB |
72 | #if defined(CONFIG_MX23) |
73 | writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0); | |
74 | #elif defined(CONFIG_MX28) | |
6e9a0a39 | 75 | writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0); |
6ecd05d2 | 76 | #endif |
6e9a0a39 MV |
77 | |
78 | return 0; | |
79 | } | |
80 | ||
ff250f3e | 81 | unsigned long long get_ticks(void) |
6e9a0a39 | 82 | { |
9c471142 OS |
83 | struct mxs_timrot_regs *timrot_regs = |
84 | (struct mxs_timrot_regs *)MXS_TIMROT_BASE; | |
6ecd05d2 | 85 | uint32_t now; |
6e9a0a39 MV |
86 | |
87 | /* Current tick value */ | |
6ecd05d2 FB |
88 | #if defined(CONFIG_MX23) |
89 | /* Upper bits are the valid ones. */ | |
90 | now = readl(&timrot_regs->hw_timrot_timcount0) >> | |
91 | TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET; | |
92 | #elif defined(CONFIG_MX28) | |
93 | now = readl(&timrot_regs->hw_timrot_running_count0); | |
94 | #endif | |
6e9a0a39 MV |
95 | |
96 | if (lastdec >= now) { | |
97 | /* | |
98 | * normal mode (non roll) | |
99 | * move stamp forward with absolut diff ticks | |
100 | */ | |
101 | timestamp += (lastdec - now); | |
102 | } else { | |
103 | /* we have rollover of decrementer */ | |
104 | timestamp += (TIMER_LOAD_VAL - now) + lastdec; | |
105 | ||
106 | } | |
107 | lastdec = now; | |
108 | ||
ff250f3e MV |
109 | return timestamp; |
110 | } | |
111 | ||
112 | ulong get_timer_masked(void) | |
113 | { | |
114 | return tick_to_time(get_ticks()); | |
115 | } | |
116 | ||
117 | ulong get_timer(ulong base) | |
118 | { | |
119 | return get_timer_masked() - base; | |
6e9a0a39 MV |
120 | } |
121 | ||
122 | /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */ | |
3e9dc930 | 123 | #define MXS_HW_DIGCTL_MICROSECONDS 0x8001c0c0 |
6e9a0a39 MV |
124 | |
125 | void __udelay(unsigned long usec) | |
126 | { | |
127 | uint32_t old, new, incr; | |
128 | uint32_t counter = 0; | |
129 | ||
3e9dc930 | 130 | old = readl(MXS_HW_DIGCTL_MICROSECONDS); |
6e9a0a39 MV |
131 | |
132 | while (counter < usec) { | |
3e9dc930 | 133 | new = readl(MXS_HW_DIGCTL_MICROSECONDS); |
6e9a0a39 MV |
134 | |
135 | /* Check if the timer wrapped. */ | |
136 | if (new < old) { | |
137 | incr = 0xffffffff - old; | |
138 | incr += new; | |
139 | } else { | |
140 | incr = new - old; | |
141 | } | |
142 | ||
143 | /* | |
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. | |
147 | */ | |
148 | if (counter + incr < counter) | |
149 | break; | |
150 | ||
151 | counter += incr; | |
152 | old = new; | |
153 | } | |
154 | } | |
ff250f3e MV |
155 | |
156 | ulong get_tbclk(void) | |
157 | { | |
3e9dc930 | 158 | return MXS_INCREMENTER_HZ; |
ff250f3e | 159 | } |