]>
Commit | Line | Data |
---|---|---|
72a087e0 WD |
1 | /* |
2 | * Copyright (C) 2004-2006 Atmel Corporation | |
3 | * | |
1a459660 | 4 | * SPDX-License-Identifier: GPL-2.0+ |
72a087e0 WD |
5 | */ |
6 | #include <common.h> | |
0ba8eed2 | 7 | #include <div64.h> |
72a087e0 | 8 | |
72a087e0 WD |
9 | #include <asm/errno.h> |
10 | #include <asm/io.h> | |
11 | #include <asm/processor.h> | |
12 | #include <asm/sysreg.h> | |
13 | ||
5d73bc7a | 14 | #include <asm/arch/hardware.h> |
72a087e0 WD |
15 | |
16 | #define HANDLER_MASK 0x00ffffff | |
17 | #define INTLEV_SHIFT 30 | |
18 | #define INTLEV_MASK 0x00000003 | |
19 | ||
20 | DECLARE_GLOBAL_DATA_PTR; | |
21 | ||
22 | /* Incremented whenever COUNT reaches 0xffffffff by timer_interrupt_handler */ | |
23 | volatile unsigned long timer_overflow; | |
24 | ||
25 | /* | |
26 | * Instead of dividing by get_tbclk(), multiply by this constant and | |
27 | * right-shift the result by 32 bits. | |
28 | */ | |
29 | static unsigned long tb_factor; | |
30 | ||
72a087e0 WD |
31 | unsigned long get_tbclk(void) |
32 | { | |
3d0f8c8f | 33 | return gd->arch.cpu_hz; |
72a087e0 WD |
34 | } |
35 | ||
36 | unsigned long long get_ticks(void) | |
37 | { | |
38 | unsigned long lo, hi_now, hi_prev; | |
39 | ||
40 | do { | |
41 | hi_prev = timer_overflow; | |
42 | lo = sysreg_read(COUNT); | |
43 | hi_now = timer_overflow; | |
44 | } while (hi_prev != hi_now); | |
45 | ||
46 | return ((unsigned long long)hi_now << 32) | lo; | |
47 | } | |
48 | ||
72a087e0 WD |
49 | unsigned long get_timer(unsigned long base) |
50 | { | |
51 | u64 now = get_ticks(); | |
52 | ||
53 | now *= tb_factor; | |
54 | return (unsigned long)(now >> 32) - base; | |
55 | } | |
56 | ||
72a087e0 WD |
57 | /* |
58 | * For short delays only. It will overflow after a few seconds. | |
59 | */ | |
3eb90bad | 60 | void __udelay(unsigned long usec) |
72a087e0 | 61 | { |
a8092c02 HS |
62 | unsigned long cycles; |
63 | unsigned long base; | |
64 | unsigned long now; | |
72a087e0 | 65 | |
a8092c02 HS |
66 | base = sysreg_read(COUNT); |
67 | cycles = ((usec * (get_tbclk() / 10000)) + 50) / 100; | |
72a087e0 | 68 | |
a8092c02 | 69 | do { |
72a087e0 | 70 | now = sysreg_read(COUNT); |
a8092c02 | 71 | } while ((now - base) < cycles); |
72a087e0 WD |
72 | } |
73 | ||
74 | static int set_interrupt_handler(unsigned int nr, void (*handler)(void), | |
75 | unsigned int priority) | |
76 | { | |
1f4f2121 | 77 | extern void _evba(void); |
72a087e0 WD |
78 | unsigned long intpr; |
79 | unsigned long handler_addr = (unsigned long)handler; | |
80 | ||
1f4f2121 HS |
81 | handler_addr -= (unsigned long)&_evba; |
82 | ||
72a087e0 WD |
83 | if ((handler_addr & HANDLER_MASK) != handler_addr |
84 | || (priority & INTLEV_MASK) != priority) | |
85 | return -EINVAL; | |
86 | ||
87 | intpr = (handler_addr & HANDLER_MASK); | |
88 | intpr |= (priority & INTLEV_MASK) << INTLEV_SHIFT; | |
f4278b71 | 89 | writel(intpr, (void *)ATMEL_BASE_INTC + 4 * nr); |
72a087e0 WD |
90 | |
91 | return 0; | |
92 | } | |
93 | ||
bf0b6313 | 94 | int timer_init(void) |
72a087e0 WD |
95 | { |
96 | extern void timer_interrupt_handler(void); | |
97 | u64 tmp; | |
98 | ||
99 | sysreg_write(COUNT, 0); | |
100 | ||
6d0f6bcf | 101 | tmp = (u64)CONFIG_SYS_HZ << 32; |
3d0f8c8f SG |
102 | tmp += gd->arch.cpu_hz / 2; |
103 | do_div(tmp, gd->arch.cpu_hz); | |
72a087e0 WD |
104 | tb_factor = (u32)tmp; |
105 | ||
df548d3c | 106 | if (set_interrupt_handler(0, &timer_interrupt_handler, 3)) |
bf0b6313 | 107 | return -EINVAL; |
72a087e0 WD |
108 | |
109 | /* For all practical purposes, this gives us an overflow interrupt */ | |
110 | sysreg_write(COMPARE, 0xffffffff); | |
bf0b6313 | 111 | return 0; |
72a087e0 | 112 | } |