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