Prepare v2023.10
[platform/kernel/u-boot.git] / arch / arm / cpu / arm926ejs / mxs / timer.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Freescale i.MX28 timer driver
4  *
5  * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
6  * on behalf of DENX Software Engineering GmbH
7  *
8  * Based on code from LTIB:
9  * (C) Copyright 2009-2010 Freescale Semiconductor, Inc.
10  */
11
12 #include <common.h>
13 #include <init.h>
14 #include <time.h>
15 #include <asm/global_data.h>
16 #include <asm/io.h>
17 #include <asm/arch/imx-regs.h>
18 #include <asm/arch/sys_proto.h>
19 #include <linux/delay.h>
20
21 /* Maximum fixed count */
22 #if defined(CONFIG_MX23)
23 #define TIMER_LOAD_VAL 0xffff
24 #elif defined(CONFIG_MX28)
25 #define TIMER_LOAD_VAL 0xffffffff
26 #endif
27
28 DECLARE_GLOBAL_DATA_PTR;
29
30 #define timestamp (gd->arch.tbl)
31 #define lastdec (gd->arch.lastinc)
32
33 /*
34  * This driver uses 1kHz clock source.
35  */
36 #define MXS_INCREMENTER_HZ              1000
37
38 static inline unsigned long tick_to_time(unsigned long tick)
39 {
40         return tick / (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
41 }
42
43 static inline unsigned long time_to_tick(unsigned long time)
44 {
45         return time * (MXS_INCREMENTER_HZ / CONFIG_SYS_HZ);
46 }
47
48 /* Calculate how many ticks happen in "us" microseconds */
49 static inline unsigned long us_to_tick(unsigned long us)
50 {
51         return (us * MXS_INCREMENTER_HZ) / 1000000;
52 }
53
54 int timer_init(void)
55 {
56         struct mxs_timrot_regs *timrot_regs =
57                 (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
58
59         /* Reset Timers and Rotary Encoder module */
60         mxs_reset_block(&timrot_regs->hw_timrot_rotctrl_reg);
61
62         /* Set fixed_count to 0 */
63 #if defined(CONFIG_MX23)
64         writel(0, &timrot_regs->hw_timrot_timcount0);
65 #elif defined(CONFIG_MX28)
66         writel(0, &timrot_regs->hw_timrot_fixed_count0);
67 #endif
68
69         /* Set UPDATE bit and 1Khz frequency */
70         writel(TIMROT_TIMCTRLn_UPDATE | TIMROT_TIMCTRLn_RELOAD |
71                 TIMROT_TIMCTRLn_SELECT_1KHZ_XTAL,
72                 &timrot_regs->hw_timrot_timctrl0);
73
74         /* Set fixed_count to maximal value */
75 #if defined(CONFIG_MX23)
76         writel(TIMER_LOAD_VAL - 1, &timrot_regs->hw_timrot_timcount0);
77 #elif defined(CONFIG_MX28)
78         writel(TIMER_LOAD_VAL, &timrot_regs->hw_timrot_fixed_count0);
79 #endif
80
81         return 0;
82 }
83
84 unsigned long long get_ticks(void)
85 {
86         struct mxs_timrot_regs *timrot_regs =
87                 (struct mxs_timrot_regs *)MXS_TIMROT_BASE;
88         uint32_t now;
89
90         /* Current tick value */
91 #if defined(CONFIG_MX23)
92         /* Upper bits are the valid ones. */
93         now = readl(&timrot_regs->hw_timrot_timcount0) >>
94                 TIMROT_RUNNING_COUNTn_RUNNING_COUNT_OFFSET;
95 #elif defined(CONFIG_MX28)
96         now = readl(&timrot_regs->hw_timrot_running_count0);
97 #else
98 #error "Don't know how to read timrot_regs"
99 #endif
100
101         if (lastdec >= now) {
102                 /*
103                  * normal mode (non roll)
104                  * move stamp forward with absolut diff ticks
105                  */
106                 timestamp += (lastdec - now);
107         } else {
108                 /* we have rollover of decrementer */
109                 timestamp += (TIMER_LOAD_VAL - now) + lastdec;
110
111         }
112         lastdec = now;
113
114         return timestamp;
115 }
116
117 ulong get_timer(ulong base)
118 {
119         return tick_to_time(get_ticks()) - base;
120 }
121
122 /* We use the HW_DIGCTL_MICROSECONDS register for sub-millisecond timer. */
123 #define MXS_HW_DIGCTL_MICROSECONDS      0x8001c0c0
124
125 void __udelay(unsigned long usec)
126 {
127         uint32_t old, new, incr;
128         uint32_t counter = 0;
129
130         old = readl(MXS_HW_DIGCTL_MICROSECONDS);
131
132         while (counter < usec) {
133                 new = readl(MXS_HW_DIGCTL_MICROSECONDS);
134
135                 /* Check if the timer wrapped. */
136                 if (new < old) {
137                         incr = 0xffffffff - old;
138                         incr += new;
139                 } else {
140                         incr = new - old;
141                 }
142
143                 /*
144                  * Check if we are close to the maximum time and the counter
145                  * would wrap if incremented. If that's the case, break out
146                  * from the loop as the requested delay time passed.
147                  */
148                 if (counter + incr < counter)
149                         break;
150
151                 counter += incr;
152                 old = new;
153         }
154 }
155
156 ulong get_tbclk(void)
157 {
158         return MXS_INCREMENTER_HZ;
159 }