Commit | Line | Data |
---|---|---|
aa218daf PW |
1 | /* |
2 | * OMAP 32ksynctimer/counter_32k-related code | |
3 | * | |
4 | * Copyright (C) 2009 Texas Instruments | |
5 | * Copyright (C) 2010 Nokia Corporation | |
6 | * Tony Lindgren <tony@atomide.com> | |
7 | * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * NOTE: This timer is not the same timer as the old OMAP1 MPU timer. | |
14 | */ | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/clk.h> | |
cb9675f3 | 18 | #include <linux/err.h> |
aa218daf | 19 | #include <linux/io.h> |
5e06b649 | 20 | #include <linux/sched.h> |
aa218daf | 21 | |
dc548fbb | 22 | #include <asm/sched_clock.h> |
aa218daf PW |
23 | |
24 | #include <plat/common.h> | |
25 | #include <plat/board.h> | |
26 | ||
27 | #include <plat/clock.h> | |
28 | ||
29 | ||
30 | /* | |
31 | * 32KHz clocksource ... always available, on pretty most chips except | |
32 | * OMAP 730 and 1510. Other timers could be used as clocksources, with | |
33 | * higher resolution in free-running counter modes (e.g. 12 MHz xtal), | |
34 | * but systems won't necessarily want to spend resources that way. | |
35 | */ | |
36 | ||
37 | #define OMAP16XX_TIMER_32K_SYNCHRONIZED 0xfffbc410 | |
38 | ||
aa218daf PW |
39 | #include <linux/clocksource.h> |
40 | ||
41 | /* | |
42 | * offset_32k holds the init time counter value. It is then subtracted | |
43 | * from every counter read to achieve a counter that counts time from the | |
44 | * kernel boot (needed for sched_clock()). | |
45 | */ | |
46 | static u32 offset_32k __read_mostly; | |
47 | ||
48 | #ifdef CONFIG_ARCH_OMAP16XX | |
5e06b649 | 49 | static cycle_t notrace omap16xx_32k_read(struct clocksource *cs) |
aa218daf PW |
50 | { |
51 | return omap_readl(OMAP16XX_TIMER_32K_SYNCHRONIZED) - offset_32k; | |
52 | } | |
53 | #else | |
54 | #define omap16xx_32k_read NULL | |
55 | #endif | |
56 | ||
57 | #ifdef CONFIG_ARCH_OMAP2420 | |
5e06b649 | 58 | static cycle_t notrace omap2420_32k_read(struct clocksource *cs) |
aa218daf PW |
59 | { |
60 | return omap_readl(OMAP2420_32KSYNCT_BASE + 0x10) - offset_32k; | |
61 | } | |
62 | #else | |
63 | #define omap2420_32k_read NULL | |
64 | #endif | |
65 | ||
66 | #ifdef CONFIG_ARCH_OMAP2430 | |
5e06b649 | 67 | static cycle_t notrace omap2430_32k_read(struct clocksource *cs) |
aa218daf PW |
68 | { |
69 | return omap_readl(OMAP2430_32KSYNCT_BASE + 0x10) - offset_32k; | |
70 | } | |
71 | #else | |
72 | #define omap2430_32k_read NULL | |
73 | #endif | |
74 | ||
75 | #ifdef CONFIG_ARCH_OMAP3 | |
5e06b649 | 76 | static cycle_t notrace omap34xx_32k_read(struct clocksource *cs) |
aa218daf PW |
77 | { |
78 | return omap_readl(OMAP3430_32KSYNCT_BASE + 0x10) - offset_32k; | |
79 | } | |
80 | #else | |
81 | #define omap34xx_32k_read NULL | |
82 | #endif | |
83 | ||
84 | #ifdef CONFIG_ARCH_OMAP4 | |
5e06b649 | 85 | static cycle_t notrace omap44xx_32k_read(struct clocksource *cs) |
aa218daf PW |
86 | { |
87 | return omap_readl(OMAP4430_32KSYNCT_BASE + 0x10) - offset_32k; | |
88 | } | |
89 | #else | |
90 | #define omap44xx_32k_read NULL | |
91 | #endif | |
92 | ||
93 | /* | |
94 | * Kernel assumes that sched_clock can be called early but may not have | |
95 | * things ready yet. | |
96 | */ | |
5e06b649 | 97 | static cycle_t notrace omap_32k_read_dummy(struct clocksource *cs) |
aa218daf PW |
98 | { |
99 | return 0; | |
100 | } | |
101 | ||
102 | static struct clocksource clocksource_32k = { | |
103 | .name = "32k_counter", | |
104 | .rating = 250, | |
105 | .read = omap_32k_read_dummy, | |
106 | .mask = CLOCKSOURCE_MASK(32), | |
aa218daf PW |
107 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
108 | }; | |
109 | ||
110 | /* | |
111 | * Returns current time from boot in nsecs. It's OK for this to wrap | |
112 | * around for now, as it's just a relative time stamp. | |
113 | */ | |
dc548fbb RK |
114 | static DEFINE_CLOCK_DATA(cd); |
115 | ||
116 | /* | |
117 | * Constants generated by clocks_calc_mult_shift(m, s, 32768, NSEC_PER_SEC, 60). | |
118 | * This gives a resolution of about 30us and a wrap period of about 36hrs. | |
119 | */ | |
120 | #define SC_MULT 4000000000u | |
121 | #define SC_SHIFT 17 | |
122 | ||
4912cf04 | 123 | static inline unsigned long long notrace _omap_32k_sched_clock(void) |
aa218daf | 124 | { |
dc548fbb RK |
125 | u32 cyc = clocksource_32k.read(&clocksource_32k); |
126 | return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT); | |
127 | } | |
128 | ||
4912cf04 TL |
129 | #ifndef CONFIG_OMAP_MPU_TIMER |
130 | unsigned long long notrace sched_clock(void) | |
131 | { | |
132 | return _omap_32k_sched_clock(); | |
133 | } | |
134 | #else | |
135 | unsigned long long notrace omap_32k_sched_clock(void) | |
136 | { | |
137 | return _omap_32k_sched_clock(); | |
138 | } | |
139 | #endif | |
140 | ||
dc548fbb | 141 | static void notrace omap_update_sched_clock(void) |
aa218daf | 142 | { |
dc548fbb RK |
143 | u32 cyc = clocksource_32k.read(&clocksource_32k); |
144 | update_sched_clock(&cd, cyc, (u32)~0); | |
aa218daf PW |
145 | } |
146 | ||
147 | /** | |
148 | * read_persistent_clock - Return time from a persistent clock. | |
149 | * | |
150 | * Reads the time from a source which isn't disabled during PM, the | |
151 | * 32k sync timer. Convert the cycles elapsed since last read into | |
152 | * nsecs and adds to a monotonically increasing timespec. | |
153 | */ | |
154 | static struct timespec persistent_ts; | |
155 | static cycles_t cycles, last_cycles; | |
156 | void read_persistent_clock(struct timespec *ts) | |
157 | { | |
158 | unsigned long long nsecs; | |
159 | cycles_t delta; | |
160 | struct timespec *tsp = &persistent_ts; | |
161 | ||
162 | last_cycles = cycles; | |
163 | cycles = clocksource_32k.read(&clocksource_32k); | |
164 | delta = cycles - last_cycles; | |
165 | ||
166 | nsecs = clocksource_cyc2ns(delta, | |
167 | clocksource_32k.mult, clocksource_32k.shift); | |
168 | ||
169 | timespec_add_ns(tsp, nsecs); | |
170 | *ts = *tsp; | |
171 | } | |
172 | ||
d8328f3b | 173 | int __init omap_init_clocksource_32k(void) |
aa218daf PW |
174 | { |
175 | static char err[] __initdata = KERN_ERR | |
176 | "%s: can't register clocksource!\n"; | |
177 | ||
178 | if (cpu_is_omap16xx() || cpu_class_is_omap2()) { | |
179 | struct clk *sync_32k_ick; | |
180 | ||
181 | if (cpu_is_omap16xx()) | |
182 | clocksource_32k.read = omap16xx_32k_read; | |
183 | else if (cpu_is_omap2420()) | |
184 | clocksource_32k.read = omap2420_32k_read; | |
185 | else if (cpu_is_omap2430()) | |
186 | clocksource_32k.read = omap2430_32k_read; | |
187 | else if (cpu_is_omap34xx()) | |
188 | clocksource_32k.read = omap34xx_32k_read; | |
189 | else if (cpu_is_omap44xx()) | |
190 | clocksource_32k.read = omap44xx_32k_read; | |
191 | else | |
192 | return -ENODEV; | |
193 | ||
194 | sync_32k_ick = clk_get(NULL, "omap_32ksync_ick"); | |
cb9675f3 | 195 | if (!IS_ERR(sync_32k_ick)) |
aa218daf PW |
196 | clk_enable(sync_32k_ick); |
197 | ||
aa218daf PW |
198 | offset_32k = clocksource_32k.read(&clocksource_32k); |
199 | ||
8437c25e | 200 | if (clocksource_register_hz(&clocksource_32k, 32768)) |
aa218daf | 201 | printk(err, clocksource_32k.name); |
dc548fbb RK |
202 | |
203 | init_fixed_sched_clock(&cd, omap_update_sched_clock, 32, | |
204 | 32768, SC_MULT, SC_SHIFT); | |
aa218daf PW |
205 | } |
206 | return 0; | |
207 | } |