150276Speter// SPDX-License-Identifier: GPL-2.0+
2262629Sdelphij/*
350276Speter * (C) Copyright 2016 Nexell
450276Speter * Hyunseok, Jung <hsjung@nexell.co.kr>
550276Speter */
650276Speter
750276Speter#include <common.h>
850276Speter#include <log.h>
950276Speter
1050276Speter#include <asm/io.h>
1150276Speter#include <asm/arch/nexell.h>
1250276Speter#include <asm/arch/clk.h>
1350276Speter#if defined(CONFIG_ARCH_S5P4418)
1450276Speter#include <asm/arch/reset.h>
1550276Speter#endif
1650276Speter
1750276Speter#if (CONFIG_TIMER_SYS_TICK_CH > 3)
1850276Speter#error Not support timer channel. Please use "0~3" channels.
1950276Speter#endif
2050276Speter
2150276Speter/* global variables to save timer count
2250276Speter *
2350276Speter * Section ".data" must be used because BSS is not available before relocation,
2450276Speter * in board_init_f(), respectively! I.e. global variables can not be used!
2550276Speter */
2650276Speterstatic unsigned long timestamp __section(".data");
2750276Speterstatic unsigned long lastdec __section(".data");
2850276Speterstatic int	timerinit __section(".data");
2950276Speter
3050276Speter/* macro to hw timer tick config */
3150276Speterstatic long	TIMER_FREQ  = 1000000;
3250276Speterstatic long	TIMER_HZ    = 1000000 / CONFIG_SYS_HZ;
3350276Speterstatic long	TIMER_COUNT = 0xFFFFFFFF;
3450276Speter
3550276Speter#define	REG_TCFG0			(0x00)
3650276Speter#define	REG_TCFG1			(0x04)
3750276Speter#define	REG_TCON			(0x08)
3850276Speter#define	REG_TCNTB0			(0x0C)
3950276Speter#define	REG_TCMPB0			(0x10)
4050276Speter#define	REG_TCNT0			(0x14)
4150276Speter#define	REG_CSTAT			(0x44)
42262629Sdelphij
4350276Speter#define	TCON_BIT_AUTO			(1 << 3)
4476726Speter#define	TCON_BIT_INVT			(1 << 2)
4576726Speter#define	TCON_BIT_UP			(1 << 1)
4650276Speter#define	TCON_BIT_RUN			(1 << 0)
4750276Speter#define TCFG0_BIT_CH(ch)		((ch) == 0 || (ch) == 1 ? 0 : 8)
4876726Speter#define TCFG1_BIT_CH(ch)		((ch) * 4)
4976726Speter#define TCON_BIT_CH(ch)			((ch) ? (ch) * 4  + 4 : 0)
5076726Speter#define TINT_CH(ch)			(ch)
5176726Speter#define TINT_CSTAT_BIT_CH(ch)		((ch) + 5)
5250276Speter#define	TINT_CSTAT_MASK			(0x1F)
5350276Speter#define TIMER_TCNT_OFFS			(0xC)
5476726Speter
5576726Spetervoid reset_timer_masked(void);
5650276Speterunsigned long get_timer_masked(void);
5750276Speter
5876726Speter/*
5976726Speter * Timer HW
60262629Sdelphij */
6150276Speterstatic inline void timer_clock(void __iomem *base, int ch, int mux, int scl)
62262629Sdelphij{
6350276Speter	u32 val = readl(base + REG_TCFG0) & ~(0xFF << TCFG0_BIT_CH(ch));
64262629Sdelphij
65262629Sdelphij	writel(val | ((scl - 1) << TCFG0_BIT_CH(ch)), base + REG_TCFG0);
66262629Sdelphij	val = readl(base + REG_TCFG1) & ~(0xF << TCFG1_BIT_CH(ch));
67262629Sdelphij	writel(val | (mux << TCFG1_BIT_CH(ch)), base + REG_TCFG1);
68262629Sdelphij}
69262629Sdelphij
70262629Sdelphijstatic inline void timer_count(void __iomem *base, int ch, unsigned int cnt)
71262629Sdelphij{
72262629Sdelphij	writel((cnt - 1), base + REG_TCNTB0 + (TIMER_TCNT_OFFS * ch));
73262629Sdelphij	writel((cnt - 1), base + REG_TCMPB0 + (TIMER_TCNT_OFFS * ch));
74262629Sdelphij}
75262629Sdelphij
76262629Sdelphijstatic inline void timer_start(void __iomem *base, int ch)
7750276Speter{
7850276Speter	int on = 0;
7976726Speter	u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch);
8076726Speter
8150276Speter	writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch),
8250276Speter	       base + REG_CSTAT);
83262629Sdelphij	val = readl(base + REG_TCON) & ~(0xE << TCON_BIT_CH(ch));
8450276Speter	writel(val | (TCON_BIT_UP << TCON_BIT_CH(ch)), base + REG_TCON);
8576726Speter
8676726Speter	val &= ~(TCON_BIT_UP << TCON_BIT_CH(ch));
8776726Speter	val |= ((TCON_BIT_AUTO | TCON_BIT_RUN) << TCON_BIT_CH(ch));
8876726Speter	writel(val, base + REG_TCON);
8976726Speter	dmb();
9050276Speter}
9150276Speter
9276726Speterstatic inline void timer_stop(void __iomem *base, int ch)
9376726Speter{
9450276Speter	int on = 0;
9550276Speter	u32 val = readl(base + REG_CSTAT) & ~(TINT_CSTAT_MASK << 5 | 0x1 << ch);
9650276Speter
9776726Speter	writel(val | (0x1 << TINT_CSTAT_BIT_CH(ch) | on << ch),
9850276Speter	       base + REG_CSTAT);
99262629Sdelphij	val = readl(base + REG_TCON) & ~(TCON_BIT_RUN << TCON_BIT_CH(ch));
10097049Speter	writel(val, base + REG_TCON);
10176726Speter}
10276726Speter
10376726Speterstatic inline unsigned long timer_read(void __iomem *base, int ch)
10450276Speter{
10576726Speter	unsigned long ret;
10676726Speter
10750276Speter	ret = TIMER_COUNT - readl(base + REG_TCNT0 + (TIMER_TCNT_OFFS * ch));
10876726Speter	return ret;
10976726Speter}
11076726Speter
11176726Speterint timer_init(void)
11276726Speter{
11376726Speter	struct clk *clk = NULL;
11476726Speter	char name[16] = "pclk";
11550276Speter	int ch = CONFIG_TIMER_SYS_TICK_CH;
11676726Speter	unsigned long rate, tclk = 0;
11776726Speter	unsigned long mout, thz, cmp = -1UL;
11876726Speter	int tcnt, tscl = 0, tmux = 0;
11976726Speter	int mux = 0, scl = 0;
12097049Speter	void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
12197049Speter
12250276Speter	if (timerinit)
12350276Speter		return 0;
12476726Speter
12576726Speter	/* get with PCLK */
12650276Speter	clk  = clk_get(name);
12750276Speter	rate = clk_get_rate(clk);
12850276Speter	for (mux = 0; mux < 5; mux++) {
129262629Sdelphij		mout = rate / (1 << mux), scl = mout / TIMER_FREQ,
13097049Speter		thz = mout / scl;
13176726Speter		if (!(mout % TIMER_FREQ) && 256 > scl) {
13276726Speter			tclk = thz, tmux = mux, tscl = scl;
13376726Speter			break;
13450276Speter		}
13576726Speter		if (scl > 256)
13676726Speter			continue;
13776726Speter		if (abs(thz - TIMER_FREQ) >= cmp)
13850276Speter			continue;
13976726Speter		tclk = thz, tmux = mux, tscl = scl;
14076726Speter		cmp = abs(thz - TIMER_FREQ);
14176726Speter	}
14276726Speter	tcnt = tclk;	/* Timer Count := 1 Mhz counting */
14350276Speter
14476726Speter	TIMER_FREQ = tcnt;	/* Timer Count := 1 Mhz counting */
14576726Speter	TIMER_HZ = TIMER_FREQ / CONFIG_SYS_HZ;
14676726Speter	tcnt = TIMER_COUNT == 0xFFFFFFFF ? TIMER_COUNT + 1 : tcnt;
14776726Speter
14876726Speter	timer_stop(base, ch);
14976726Speter	timer_clock(base, ch, tmux, tscl);
150174993Srafan	timer_count(base, ch, tcnt);
15176726Speter	timer_start(base, ch);
15276726Speter
15376726Speter	reset_timer_masked();
15476726Speter	timerinit = 1;
15576726Speter
15650276Speter	return 0;
15750276Speter}
15850276Speter
15997049Spetervoid reset_timer(void)
16050276Speter{
16150276Speter	reset_timer_masked();
16276726Speter}
16376726Speter
16450276Speterunsigned long get_timer(unsigned long base)
16550276Speter{
16676726Speter	long ret;
16797049Speter	unsigned long time = get_timer_masked();
168262629Sdelphij	unsigned long hz = TIMER_HZ;
16976726Speter
17076726Speter	ret = time / hz - base;
17176726Speter	return ret;
17297049Speter}
17350276Speter
17450276Spetervoid set_timer(unsigned long t)
17576726Speter{
17676726Speter	timestamp = (unsigned long)t;
17750276Speter}
17850276Speter
179166124Srafanvoid reset_timer_masked(void)
18076726Speter{
18176726Speter	void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
18250276Speter	int ch = CONFIG_TIMER_SYS_TICK_CH;
183262629Sdelphij
18450276Speter	/* reset time */
185166124Srafan	/* capure current decrementer value time */
186262629Sdelphij	lastdec = timer_read(base, ch);
187262629Sdelphij	/* start "advancing" time stamp from 0 */
188262629Sdelphij	timestamp = 0;
189184989Srafan}
190166124Srafan
191262629Sdelphijunsigned long get_timer_masked(void)
192262629Sdelphij{
193262629Sdelphij	void __iomem *base = (void __iomem *)PHY_BASEADDR_TIMER;
194166124Srafan	int ch = CONFIG_TIMER_SYS_TICK_CH;
195262629Sdelphij
196262629Sdelphij	unsigned long now = timer_read(base, ch); /* current tick value */
197262629Sdelphij
198262629Sdelphij	if (now >= lastdec) {			  /* normal mode (non roll) */
199262629Sdelphij		/* move stamp fordward with absolute diff ticks */
200166124Srafan		timestamp += now - lastdec;
20150276Speter	} else {
202166124Srafan		/* we have overflow of the count down timer */
20350276Speter		/* nts = ts + ld + (TLV - now)
204166124Srafan		 * ts=old stamp, ld=time that passed before passing through -1
205166124Srafan		 * (TLV-now) amount of time after passing though -1
206166124Srafan		 * nts = new "advancing time stamp"...
207166124Srafan		 * it could also roll and cause problems.
208166124Srafan		 */
209166124Srafan		timestamp += now + TIMER_COUNT - lastdec;
210166124Srafan	}
21150276Speter	/* save last */
212166124Srafan	lastdec = now;
213166124Srafan
214166124Srafan	debug("now=%lu, last=%lu, timestamp=%lu\n", now, lastdec, timestamp);
215166124Srafan	return (unsigned long)timestamp;
21650276Speter}
217166124Srafan
218166124Srafanvoid __udelay(unsigned long usec)
21950276Speter{
220166124Srafan	unsigned long tmo, tmp;
221166124Srafan
222166124Srafan	debug("+udelay=%ld\n", usec);
223166124Srafan
224166124Srafan	if (!timerinit)
225166124Srafan		timer_init();
226166124Srafan
227166124Srafan	/* if "big" number, spread normalization to seconds */
228166124Srafan	if (usec >= 1000) {
229166124Srafan		/* start to normalize for usec to ticks per sec */
23050276Speter		tmo  = usec / 1000;
231166124Srafan		/* find number of "ticks" to wait to achieve target */
232166124Srafan		tmo *= TIMER_FREQ;
233166124Srafan		/* finish normalize. */
234166124Srafan		tmo /= 1000;
235166124Srafan	/* else small number, don't kill it prior to HZ multiply */
236166124Srafan	} else {
237166124Srafan		tmo = usec * TIMER_FREQ;
238166124Srafan		tmo /= (1000 * 1000);
239166124Srafan	}
240166124Srafan
241166124Srafan	tmp = get_timer_masked();	/* get current timestamp */
242262629Sdelphij	debug("A. tmo=%ld, tmp=%ld\n", tmo, tmp);
243166124Srafan
244166124Srafan	/* if setting this fordward will roll time stamp */
245166124Srafan	if (tmp > (tmo + tmp + 1))
246166124Srafan		/* reset "advancing" timestamp to 0, set lastdec value */
247166124Srafan		reset_timer_masked();
248166124Srafan	else
249184989Srafan		/* set advancing stamp wake up time */
25076726Speter		tmo += tmp;
25176726Speter
25250276Speter	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
300