blob: 3b311fd22a56f627dd80d7a05ea9d34d6550a5af [file] [log] [blame]
Stefan Bosch6563ea22020-07-10 19:07:26 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2016 Nexell
4 * Hyunseok, Jung <hsjung@nexell.co.kr>
5 */
6
7#include <common.h>
8#include <log.h>
9
10#include <asm/io.h>
11#include <asm/arch/nexell.h>
12#include <asm/arch/clk.h>
13#if defined(CONFIG_ARCH_S5P4418)
14#include <asm/arch/reset.h>
15#endif
16
17#if (CONFIG_TIMER_SYS_TICK_CH > 3)
18#error Not support timer channel. Please use "0~3" channels.
19#endif
20
21/* global variables to save timer count
22 *
23 * Section ".data" must be used because BSS is not available before relocation,
24 * in board_init_f(), respectively! I.e. global variables can not be used!
25 */
Marek BehĂșn4bebdd32021-05-20 13:23:52 +020026static unsigned long timestamp __section(".data");
27static unsigned long lastdec __section(".data");
28static int timerinit __section(".data");
Stefan Bosch6563ea22020-07-10 19:07:26 +020029
30/* macro to hw timer tick config */
31static long TIMER_FREQ = 1000000;
32static long TIMER_HZ = 1000000 / CONFIG_SYS_HZ;
33static long TIMER_COUNT = 0xFFFFFFFF;
34
35#define REG_TCFG0 (0x00)
36#define REG_TCFG1 (0x04)
37#define REG_TCON (0x08)
38#define REG_TCNTB0 (0x0C)
39#define REG_TCMPB0 (0x10)
40#define REG_TCNT0 (0x14)
41#define REG_CSTAT (0x44)
42
43#define TCON_BIT_AUTO (1 << 3)
44#define TCON_BIT_INVT (1 << 2)
45#define TCON_BIT_UP (1 << 1)
46#define TCON_BIT_RUN (1 << 0)
47#define TCFG0_BIT_CH(ch) ((ch) == 0 || (ch) == 1 ? 0 : 8)
48#define TCFG1_BIT_CH(ch) ((ch) * 4)
49#define TCON_BIT_CH(ch) ((ch) ? (ch) * 4 + 4 : 0)
50#define TINT_CH(ch) (ch)
51#define TINT_CSTAT_BIT_CH(ch) ((ch) + 5)
52#define TINT_CSTAT_MASK (0x1F)
53#define TIMER_TCNT_OFFS (0xC)
54
55void reset_timer_masked(void);
56unsigned long get_timer_masked(void);
57
58/*
59 * Timer HW
60 */
61static inline void timer_clock(void __iomem *base, int ch, int mux, int scl)
62{
63 u32 val = readl(base + REG_TCFG0) & ~(0xFF << TCFG0_BIT_CH(ch));
64
65 writel(val | ((scl - 1) << TCFG0_BIT_CH(ch)), base + REG_TCFG0);
66 val = readl(base + REG_TCFG1) & ~(0xF << TCFG1_BIT_CH(ch));
67 writel(val | (mux << TCFG1_BIT_CH(ch)), base + REG_TCFG1);
68}
69
70static inline void timer_count(void __iomem *base, int ch, unsigned int cnt)
71{
72 writel((cnt - 1), base + REG_TCNTB0 + (TIMER_TCNT_OFFS * ch));
73 writel((cnt - 1), base + REG_TCMPB0 + (TIMER_TCNT_OFFS * ch));
74}
75
76static inline void timer_start(void __iomem *base, int ch)
77{
78 int on = 0;
79 u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch);
80
81 writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch),
82 base + REG_CSTAT);
83 val = readl(base + REG_TCON) & ~(0xE << TCON_BIT_CH(ch));
84 writel(val | (TCON_BIT_UP << TCON_BIT_CH(ch)), base + REG_TCON);
85
86 val &= ~(TCON_BIT_UP << TCON_BIT_CH(ch));
87 val |= ((TCON_BIT_AUTO | TCON_BIT_RUN) << TCON_BIT_CH(ch));
88 writel(val, base + REG_TCON);
89 dmb();
90}
91
92static inline void timer_stop(void __iomem *base, int ch)
93{
94 int on = 0;
95 u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch);
96
97 writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch),
98 base + REG_CSTAT);
99 val = readl(base + REG_TCON) & ~(TCON_BIT_RUN << TCON_BIT_CH(ch));
100 writel(val, base + REG_TCON);
101}
102
103static inline unsigned long timer_read(void __iomem *base, int ch)
104{
105 unsigned long ret;
106
107 ret = TIMER_COUNT - readl(base + REG_TCNT0 + (TIMER_TCNT_OFFS * ch));
108 return ret;
109}
110
111int timer_init(void)
112{
113 struct clk *clk = NULL;
114 char name[16] = "pclk";
115 int ch = CONFIG_TIMER_SYS_TICK_CH;
116 unsigned long rate, tclk = 0;
117 unsigned long mout, thz, cmp = -1UL;
118 int tcnt, tscl = 0, tmux = 0;
119 int mux = 0, scl = 0;
120 void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
121
122 if (timerinit)
123 return 0;
124
125 /* get with PCLK */
126 clk = clk_get(name);
127 rate = clk_get_rate(clk);
128 for (mux = 0; mux < 5; mux++) {
129 mout = rate / (1 << mux), scl = mout / TIMER_FREQ,
130 thz = mout / scl;
131 if (!(mout % TIMER_FREQ) && 256 > scl) {
132 tclk = thz, tmux = mux, tscl = scl;
133 break;
134 }
135 if (scl > 256)
136 continue;
137 if (abs(thz - TIMER_FREQ) >= cmp)
138 continue;
139 tclk = thz, tmux = mux, tscl = scl;
140 cmp = abs(thz - TIMER_FREQ);
141 }
142 tcnt = tclk; /* Timer Count := 1 Mhz counting */
143
144 TIMER_FREQ = tcnt; /* Timer Count := 1 Mhz counting */
145 TIMER_HZ = TIMER_FREQ / CONFIG_SYS_HZ;
146 tcnt = TIMER_COUNT == 0xFFFFFFFF ? TIMER_COUNT + 1 : tcnt;
147
148 timer_stop(base, ch);
149 timer_clock(base, ch, tmux, tscl);
150 timer_count(base, ch, tcnt);
151 timer_start(base, ch);
152
153 reset_timer_masked();
154 timerinit = 1;
155
156 return 0;
157}
158
159void reset_timer(void)
160{
161 reset_timer_masked();
162}
163
164unsigned long get_timer(unsigned long base)
165{
166 long ret;
167 unsigned long time = get_timer_masked();
168 unsigned long hz = TIMER_HZ;
169
170 ret = time / hz - base;
171 return ret;
172}
173
174void set_timer(unsigned long t)
175{
176 timestamp = (unsigned long)t;
177}
178
179void reset_timer_masked(void)
180{
181 void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
182 int ch = CONFIG_TIMER_SYS_TICK_CH;
183
184 /* reset time */
185 /* capure current decrementer value time */
186 lastdec = timer_read(base, ch);
187 /* start "advancing" time stamp from 0 */
188 timestamp = 0;
189}
190
191unsigned long get_timer_masked(void)
192{
193 void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
194 int ch = CONFIG_TIMER_SYS_TICK_CH;
195
196 unsigned long now = timer_read(base, ch); /* current tick value */
197
198 if (now >= lastdec) { /* normal mode (non roll) */
199 /* move stamp fordward with absolute diff ticks */
200 timestamp += now - lastdec;
201 } else {
202 /* we have overflow of the count down timer */
203 /* nts = ts + ld + (TLV - now)
204 * ts=old stamp, ld=time that passed before passing through -1
205 * (TLV-now) amount of time after passing though -1
206 * nts = new "advancing time stamp"...
207 * it could also roll and cause problems.
208 */
209 timestamp += now + TIMER_COUNT - lastdec;
210 }
211 /* save last */
212 lastdec = now;
213
214 debug("now=%lu, last=%lu, timestamp=%lu\n", now, lastdec, timestamp);
215 return (unsigned long)timestamp;
216}
217
218void __udelay(unsigned long usec)
219{
220 unsigned long tmo, tmp;
221
222 debug("+udelay=%ld\n", usec);
223
224 if (!timerinit)
225 timer_init();
226
227 /* if "big" number, spread normalization to seconds */
228 if (usec >= 1000) {
229 /* start to normalize for usec to ticks per sec */
230 tmo = usec / 1000;
231 /* find number of "ticks" to wait to achieve target */
232 tmo *= TIMER_FREQ;
233 /* finish normalize. */
234 tmo /= 1000;
235 /* else small number, don't kill it prior to HZ multiply */
236 } else {
237 tmo = usec * TIMER_FREQ;
238 tmo /= (1000 * 1000);
239 }
240
241 tmp = get_timer_masked(); /* get current timestamp */
242 debug("A. tmo=%ld, tmp=%ld\n", tmo, tmp);
243
244 /* if setting this fordward will roll time stamp */
245 if (tmp > (tmo + tmp + 1))
246 /* reset "advancing" timestamp to 0, set lastdec value */
247 reset_timer_masked();
248 else
249 /* set advancing stamp wake up time */
250 tmo += tmp;
251
252 debug("B. tmo=%ld, tmp=%ld\n", tmo, tmp);
253
254 /* loop till event */
255 do {
256 tmp = get_timer_masked();
257 } while (tmo > tmp);
258 debug("-udelay=%ld\n", usec);
259}
260
261void udelay_masked(unsigned long usec)
262{
263 unsigned long tmo, endtime;
264 signed long diff;
265
266 /* if "big" number, spread normalization to seconds */
267 if (usec >= 1000) {
268 /* start to normalize for usec to ticks per sec */
269 tmo = usec / 1000;
270 /* find number of "ticks" to wait to achieve target */
271 tmo *= TIMER_FREQ;
272 /* finish normalize. */
273 tmo /= 1000;
274 } else { /* else small number, don't kill it prior to HZ multiply */
275 tmo = usec * TIMER_FREQ;
276 tmo /= (1000 * 1000);
277 }
278
279 endtime = get_timer_masked() + tmo;
280
281 do {
282 unsigned long now = get_timer_masked();
283
284 diff = endtime - now;
285 } while (diff >= 0);
286}
287
288unsigned long long get_ticks(void)
289{
290 return get_timer_masked();
291}
292
293#if defined(CONFIG_ARCH_S5P4418)
294ulong get_tbclk(void)
295{
296 ulong tbclk = TIMER_FREQ;
297 return tbclk;
298}
299#endif