]>
Commit | Line | Data |
---|---|---|
38b343dd MS |
1 | /* |
2 | * Copyright (C) 2012 Michal Simek <[email protected]> | |
3 | * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. | |
4 | * | |
5 | * (C) Copyright 2008 | |
6 | * Guennadi Liakhovetki, DENX Software Engineering, <[email protected]> | |
7 | * | |
8 | * (C) Copyright 2004 | |
9 | * Philippe Robin, ARM Ltd. <[email protected]> | |
10 | * | |
11 | * (C) Copyright 2002-2004 | |
12 | * Gary Jennejohn, DENX Software Engineering, <[email protected]> | |
13 | * | |
14 | * (C) Copyright 2003 | |
15 | * Texas Instruments <www.ti.com> | |
16 | * | |
17 | * (C) Copyright 2002 | |
18 | * Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
19 | * Marius Groeger <[email protected]> | |
20 | * | |
21 | * (C) Copyright 2002 | |
22 | * Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
23 | * Alex Zuepke <[email protected]> | |
24 | * | |
3765b3e7 | 25 | * SPDX-License-Identifier: GPL-2.0+ |
38b343dd MS |
26 | */ |
27 | ||
28 | #include <common.h> | |
29 | #include <div64.h> | |
30 | #include <asm/io.h> | |
4b21284b | 31 | #include <asm/arch/hardware.h> |
38b343dd MS |
32 | |
33 | DECLARE_GLOBAL_DATA_PTR; | |
34 | ||
35 | struct scu_timer { | |
36 | u32 load; /* Timer Load Register */ | |
37 | u32 counter; /* Timer Counter Register */ | |
38 | u32 control; /* Timer Control Register */ | |
39 | }; | |
40 | ||
41 | static struct scu_timer *timer_base = | |
4b21284b | 42 | (struct scu_timer *)ZYNQ_SCUTIMER_BASEADDR; |
38b343dd MS |
43 | |
44 | #define SCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ | |
45 | #define SCUTIMER_CONTROL_PRESCALER_SHIFT 8 | |
46 | #define SCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ | |
47 | #define SCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ | |
48 | ||
49 | #define TIMER_LOAD_VAL 0xFFFFFFFF | |
50 | #define TIMER_PRESCALE 255 | |
51 | #define TIMER_TICK_HZ (CONFIG_CPU_FREQ_HZ / 2 / TIMER_PRESCALE) | |
52 | ||
53 | int timer_init(void) | |
54 | { | |
55 | const u32 emask = SCUTIMER_CONTROL_AUTO_RELOAD_MASK | | |
56 | (TIMER_PRESCALE << SCUTIMER_CONTROL_PRESCALER_SHIFT) | | |
57 | SCUTIMER_CONTROL_ENABLE_MASK; | |
58 | ||
59 | /* Load the timer counter register */ | |
7ba69b7d | 60 | writel(0xFFFFFFFF, &timer_base->load); |
38b343dd MS |
61 | |
62 | /* | |
63 | * Start the A9Timer device | |
64 | * Enable Auto reload mode, Clear prescaler control bits | |
65 | * Set prescaler value, Enable the decrementer | |
66 | */ | |
67 | clrsetbits_le32(&timer_base->control, SCUTIMER_CONTROL_PRESCALER_MASK, | |
68 | emask); | |
69 | ||
70 | /* Reset time */ | |
582601da | 71 | gd->arch.lastinc = readl(&timer_base->counter) / |
38b343dd | 72 | (TIMER_TICK_HZ / CONFIG_SYS_HZ); |
66ee6923 | 73 | gd->arch.tbl = 0; |
38b343dd MS |
74 | |
75 | return 0; | |
76 | } | |
77 | ||
78 | /* | |
79 | * This function is derived from PowerPC code (read timebase as long long). | |
80 | * On ARM it just returns the timer value. | |
81 | */ | |
82 | ulong get_timer_masked(void) | |
83 | { | |
84 | ulong now; | |
85 | ||
86 | now = readl(&timer_base->counter) / (TIMER_TICK_HZ / CONFIG_SYS_HZ); | |
87 | ||
582601da | 88 | if (gd->arch.lastinc >= now) { |
38b343dd | 89 | /* Normal mode */ |
582601da | 90 | gd->arch.tbl += gd->arch.lastinc - now; |
38b343dd MS |
91 | } else { |
92 | /* We have an overflow ... */ | |
582601da | 93 | gd->arch.tbl += gd->arch.lastinc + TIMER_LOAD_VAL - now; |
38b343dd | 94 | } |
582601da | 95 | gd->arch.lastinc = now; |
38b343dd | 96 | |
66ee6923 | 97 | return gd->arch.tbl; |
38b343dd MS |
98 | } |
99 | ||
100 | void __udelay(unsigned long usec) | |
101 | { | |
d54cc007 DA |
102 | u32 countticks; |
103 | u32 timeend; | |
104 | u32 timediff; | |
105 | u32 timenow; | |
106 | ||
107 | if (usec == 0) | |
108 | return; | |
109 | ||
110 | countticks = (u32) (((unsigned long long) TIMER_TICK_HZ * usec) / | |
111 | 1000000); | |
112 | ||
113 | /* decrementing timer */ | |
114 | timeend = readl(&timer_base->counter) - countticks; | |
115 | ||
116 | #if TIMER_LOAD_VAL != 0xFFFFFFFF | |
117 | /* do not manage multiple overflow */ | |
118 | if (countticks >= TIMER_LOAD_VAL) | |
119 | countticks = TIMER_LOAD_VAL - 1; | |
120 | #endif | |
121 | ||
122 | do { | |
123 | timenow = readl(&timer_base->counter); | |
124 | ||
125 | if (timenow >= timeend) { | |
126 | /* normal case */ | |
127 | timediff = timenow - timeend; | |
128 | } else { | |
129 | if ((TIMER_LOAD_VAL - timeend + timenow) <= | |
130 | countticks) { | |
131 | /* overflow */ | |
132 | timediff = TIMER_LOAD_VAL - timeend + timenow; | |
133 | } else { | |
134 | /* missed the exact match */ | |
135 | break; | |
136 | } | |
137 | } | |
138 | } while (timediff > 0); | |
38b343dd MS |
139 | } |
140 | ||
141 | /* Timer without interrupts */ | |
142 | ulong get_timer(ulong base) | |
143 | { | |
144 | return get_timer_masked() - base; | |
145 | } | |
146 | ||
147 | /* | |
148 | * This function is derived from PowerPC code (read timebase as long long). | |
149 | * On ARM it just returns the timer value. | |
150 | */ | |
151 | unsigned long long get_ticks(void) | |
152 | { | |
153 | return get_timer(0); | |
154 | } | |
155 | ||
156 | /* | |
157 | * This function is derived from PowerPC code (timebase clock frequency). | |
158 | * On ARM it returns the number of timer ticks per second. | |
159 | */ | |
160 | ulong get_tbclk(void) | |
161 | { | |
162 | return CONFIG_SYS_HZ; | |
163 | } |