1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2003 Josef Baumgartner <josef.baumgartner@telex.de>
4  *
5  * (C) Copyright 2000
6  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7  */
8 
9 #include <common.h>
10 #include <init.h>
11 #include <irq_func.h>
12 #include <time.h>
13 #include <asm/global_data.h>
14 #include <linux/delay.h>
15 
16 #include <asm/timer.h>
17 #include <asm/immap.h>
18 #include <watchdog.h>
19 
20 DECLARE_GLOBAL_DATA_PTR;
21 
22 static volatile ulong timestamp = 0;
23 
24 #ifndef CFG_SYS_WATCHDOG_FREQ
25 #define CFG_SYS_WATCHDOG_FREQ (CONFIG_SYS_HZ / 2)
26 #endif
27 
28 #if CONFIG_IS_ENABLED(MCFTMR)
29 #ifndef CFG_SYS_UDELAY_BASE
30 #	error	"uDelay base not defined!"
31 #endif
32 
33 #if !defined(CFG_SYS_TMR_BASE) || !defined(CFG_SYS_INTR_BASE) || !defined(CFG_SYS_TMRINTR_NO) || !defined(CFG_SYS_TMRINTR_MASK)
34 #	error	"TMR_BASE, INTR_BASE, TMRINTR_NO or TMRINTR_MASk not defined!"
35 #endif
36 extern void dtimer_intr_setup(void);
37 
__udelay(unsigned long usec)38 void __udelay(unsigned long usec)
39 {
40 	volatile dtmr_t *timerp = (dtmr_t *) (CFG_SYS_UDELAY_BASE);
41 	uint start, now, tmp;
42 
43 	while (usec > 0) {
44 		if (usec > 65000)
45 			tmp = 65000;
46 		else
47 			tmp = usec;
48 		usec = usec - tmp;
49 
50 		/* Set up TIMER 3 as timebase clock */
51 		timerp->tmr = DTIM_DTMR_RST_RST;
52 		timerp->tcn = 0;
53 		/* set period to 1 us */
54 		timerp->tmr =
55 		    CFG_SYS_TIMER_PRESCALER | DTIM_DTMR_CLK_DIV1 | DTIM_DTMR_FRR |
56 		    DTIM_DTMR_RST_EN;
57 
58 		start = now = timerp->tcn;
59 		while (now < start + tmp)
60 			now = timerp->tcn;
61 	}
62 }
63 
dtimer_interrupt(void * not_used)64 void dtimer_interrupt(void *not_used)
65 {
66 	volatile dtmr_t *timerp = (dtmr_t *) (CFG_SYS_TMR_BASE);
67 
68 	/* check for timer interrupt asserted */
69 	if ((CFG_SYS_TMRPND_REG & CFG_SYS_TMRINTR_MASK) == CFG_SYS_TMRINTR_PEND) {
70 		timerp->ter = (DTIM_DTER_CAP | DTIM_DTER_REF);
71 		timestamp++;
72 
73 		#if defined(CONFIG_WATCHDOG) || defined (CONFIG_HW_WATCHDOG)
74 		if (CFG_SYS_WATCHDOG_FREQ && (timestamp % (CFG_SYS_WATCHDOG_FREQ)) == 0) {
75 			schedule();
76 		}
77 		#endif    /* CONFIG_WATCHDOG || CONFIG_HW_WATCHDOG */
78 		return;
79 	}
80 }
81 
timer_init(void)82 int timer_init(void)
83 {
84 	volatile dtmr_t *timerp = (dtmr_t *) (CFG_SYS_TMR_BASE);
85 
86 	timestamp = 0;
87 
88 	timerp->tcn = 0;
89 	timerp->trr = 0;
90 
91 	/* Set up TIMER 4 as clock */
92 	timerp->tmr = DTIM_DTMR_RST_RST;
93 
94 	/* initialize and enable timer interrupt */
95 	irq_install_handler(CFG_SYS_TMRINTR_NO, dtimer_interrupt, 0);
96 
97 	timerp->tcn = 0;
98 	timerp->trr = 1000;	/* Interrupt every ms */
99 
100 	dtimer_intr_setup();
101 
102 	/* set a period of 1us, set timer mode to restart and enable timer and interrupt */
103 	timerp->tmr = CFG_SYS_TIMER_PRESCALER | DTIM_DTMR_CLK_DIV1 |
104 	    DTIM_DTMR_FRR | DTIM_DTMR_ORRI | DTIM_DTMR_RST_EN;
105 
106 	return 0;
107 }
108 
get_timer(ulong base)109 ulong get_timer(ulong base)
110 {
111 	return (timestamp - base);
112 }
113 
114 /*
115  * This function is derived from PowerPC code (read timebase as long long).
116  * On M68K it just returns the timer value.
117  */
get_ticks(void)118 unsigned long long get_ticks(void)
119 {
120 	return get_timer(0);
121 }
122 #else
123 static u64 timer64 __section(".data");
124 static u16 timer16 __section(".data");
125 
get_ticks(void)126 uint64_t __weak get_ticks(void)
127 {
128 	volatile pit_t *timerp = (pit_t *) (CFG_SYS_UDELAY_BASE);
129 	u16 val = ~timerp->pcntr;
130 
131 	if (timer16 > val)
132 		timer64 += 0xffff - timer16 + val;
133 	else
134 		timer64 += val - timer16;
135 
136 	timer16 = val;
137 
138 	return timer64;
139 }
140 
141 /* PIT timer */
timer_init(void)142 int timer_init(void)
143 {
144 	volatile pit_t *timerp = (pit_t *) (CFG_SYS_UDELAY_BASE);
145 
146 	timer16 = 0;
147 	timer64 = 0;
148 
149 	/* Set up PIT as timebase clock */
150 	timerp->pmr = 0xffff;
151 	timerp->pcsr = PIT_PCSR_EN | PIT_PCSR_OVW;
152 
153 	return 0;
154 }
155 #endif				/* CONFIG_MCFTMR */
156 
usec2ticks(unsigned long usec)157 unsigned long usec2ticks(unsigned long usec)
158 {
159 	return get_timer(usec);
160 }
161 
162 /*
163  * This function is derived from PowerPC code (timebase clock frequency).
164  * On M68K it returns the number of timer ticks per second.
165  */
get_tbclk(void)166 ulong get_tbclk(void)
167 {
168 	return CONFIG_SYS_HZ;
169 }
170