1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2007-2011
4  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
5  * Tom Cubie <tangliang@allwinnertech.com>
6  */
7 
8 #include <common.h>
9 #include <init.h>
10 #include <time.h>
11 #include <asm/global_data.h>
12 #include <asm/io.h>
13 #include <asm/arch/timer.h>
14 #include <linux/delay.h>
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
18 #define TIMER_MODE   (0x0 << 7)	/* continuous mode */
19 #define TIMER_DIV    (0x0 << 4)	/* pre scale 1 */
20 #define TIMER_SRC    (0x1 << 2)	/* osc24m */
21 #define TIMER_RELOAD (0x1 << 1)	/* reload internal value */
22 #define TIMER_EN     (0x1 << 0)	/* enable timer */
23 
24 #define TIMER_CLOCK		(24 * 1000 * 1000)
25 #define COUNT_TO_USEC(x)	((x) / 24)
26 #define USEC_TO_COUNT(x)	((x) * 24)
27 #define TICKS_PER_HZ		(TIMER_CLOCK / CONFIG_SYS_HZ)
28 #define TICKS_TO_HZ(x)		((x) / TICKS_PER_HZ)
29 
30 #define TIMER_LOAD_VAL		0xffffffff
31 
32 #define TIMER_NUM		0	/* we use timer 0 */
33 
34 /* read the 32-bit timer */
read_timer(void)35 static ulong read_timer(void)
36 {
37 	struct sunxi_timer_reg *timers =
38 		(struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
39 	struct sunxi_timer *timer = &timers->timer[TIMER_NUM];
40 
41 	/*
42 	 * The hardware timer counts down, therefore we invert to
43 	 * produce an incrementing timer.
44 	 */
45 	return ~readl(&timer->val);
46 }
47 
48 /* init timer register */
timer_init(void)49 int timer_init(void)
50 {
51 	struct sunxi_timer_reg *timers =
52 		(struct sunxi_timer_reg *)SUNXI_TIMER_BASE;
53 	struct sunxi_timer *timer = &timers->timer[TIMER_NUM];
54 	writel(TIMER_LOAD_VAL, &timer->inter);
55 	writel(TIMER_MODE | TIMER_DIV | TIMER_SRC | TIMER_RELOAD | TIMER_EN,
56 	       &timer->ctl);
57 
58 	return 0;
59 }
60 
61 /* timer without interrupts */
get_timer_masked(void)62 static ulong get_timer_masked(void)
63 {
64 	/* current tick value */
65 	ulong now = TICKS_TO_HZ(read_timer());
66 
67 	if (now >= gd->arch.lastinc)	/* normal (non rollover) */
68 		gd->arch.tbl += (now - gd->arch.lastinc);
69 	else {
70 		/* rollover */
71 		gd->arch.tbl += (TICKS_TO_HZ(TIMER_LOAD_VAL)
72 				- gd->arch.lastinc) + now;
73 	}
74 	gd->arch.lastinc = now;
75 
76 	return gd->arch.tbl;
77 }
78 
get_timer(ulong base)79 ulong get_timer(ulong base)
80 {
81 	return get_timer_masked() - base;
82 }
83 
84 /* delay x useconds */
__udelay(unsigned long usec)85 void __udelay(unsigned long usec)
86 {
87 	long tmo = USEC_TO_COUNT(usec);
88 	ulong now, last = read_timer();
89 
90 	while (tmo > 0) {
91 		now = read_timer();
92 		if (now > last)	/* normal (non rollover) */
93 			tmo -= now - last;
94 		else		/* rollover */
95 			tmo -= TIMER_LOAD_VAL - last + now;
96 		last = now;
97 	}
98 }
99 
100 /*
101  * This function is derived from PowerPC code (read timebase as long long).
102  * On ARM it just returns the timer value.
103  */
get_ticks(void)104 unsigned long long get_ticks(void)
105 {
106 	return get_timer(0);
107 }
108 
109 /*
110  * This function is derived from PowerPC code (timebase clock frequency).
111  * On ARM it returns the number of timer ticks per second.
112  */
get_tbclk(void)113 ulong get_tbclk(void)
114 {
115 	return CONFIG_SYS_HZ;
116 }
117