diff options
Diffstat (limited to 'arch/arm/plat-omap/counter_32k.c')
-rw-r--r-- | arch/arm/plat-omap/counter_32k.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/arch/arm/plat-omap/counter_32k.c b/arch/arm/plat-omap/counter_32k.c new file mode 100644 index 000000000000..155fe43a672b --- /dev/null +++ b/arch/arm/plat-omap/counter_32k.c | |||
@@ -0,0 +1,183 @@ | |||
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> | ||
18 | #include <linux/io.h> | ||
19 | |||
20 | #include <plat/common.h> | ||
21 | #include <plat/board.h> | ||
22 | |||
23 | #include <plat/clock.h> | ||
24 | |||
25 | |||
26 | /* | ||
27 | * 32KHz clocksource ... always available, on pretty most chips except | ||
28 | * OMAP 730 and 1510. Other timers could be used as clocksources, with | ||
29 | * higher resolution in free-running counter modes (e.g. 12 MHz xtal), | ||
30 | * but systems won't necessarily want to spend resources that way. | ||
31 | */ | ||
32 | |||
33 | #define OMAP16XX_TIMER_32K_SYNCHRONIZED 0xfffbc410 | ||
34 | |||
35 | #if !(defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP15XX)) | ||
36 | |||
37 | #include <linux/clocksource.h> | ||
38 | |||
39 | /* | ||
40 | * offset_32k holds the init time counter value. It is then subtracted | ||
41 | * from every counter read to achieve a counter that counts time from the | ||
42 | * kernel boot (needed for sched_clock()). | ||
43 | */ | ||
44 | static u32 offset_32k __read_mostly; | ||
45 | |||
46 | #ifdef CONFIG_ARCH_OMAP16XX | ||
47 | static cycle_t omap16xx_32k_read(struct clocksource *cs) | ||
48 | { | ||
49 | return omap_readl(OMAP16XX_TIMER_32K_SYNCHRONIZED) - offset_32k; | ||
50 | } | ||
51 | #else | ||
52 | #define omap16xx_32k_read NULL | ||
53 | #endif | ||
54 | |||
55 | #ifdef CONFIG_ARCH_OMAP2420 | ||
56 | static cycle_t omap2420_32k_read(struct clocksource *cs) | ||
57 | { | ||
58 | return omap_readl(OMAP2420_32KSYNCT_BASE + 0x10) - offset_32k; | ||
59 | } | ||
60 | #else | ||
61 | #define omap2420_32k_read NULL | ||
62 | #endif | ||
63 | |||
64 | #ifdef CONFIG_ARCH_OMAP2430 | ||
65 | static cycle_t omap2430_32k_read(struct clocksource *cs) | ||
66 | { | ||
67 | return omap_readl(OMAP2430_32KSYNCT_BASE + 0x10) - offset_32k; | ||
68 | } | ||
69 | #else | ||
70 | #define omap2430_32k_read NULL | ||
71 | #endif | ||
72 | |||
73 | #ifdef CONFIG_ARCH_OMAP3 | ||
74 | static cycle_t omap34xx_32k_read(struct clocksource *cs) | ||
75 | { | ||
76 | return omap_readl(OMAP3430_32KSYNCT_BASE + 0x10) - offset_32k; | ||
77 | } | ||
78 | #else | ||
79 | #define omap34xx_32k_read NULL | ||
80 | #endif | ||
81 | |||
82 | #ifdef CONFIG_ARCH_OMAP4 | ||
83 | static cycle_t omap44xx_32k_read(struct clocksource *cs) | ||
84 | { | ||
85 | return omap_readl(OMAP4430_32KSYNCT_BASE + 0x10) - offset_32k; | ||
86 | } | ||
87 | #else | ||
88 | #define omap44xx_32k_read NULL | ||
89 | #endif | ||
90 | |||
91 | /* | ||
92 | * Kernel assumes that sched_clock can be called early but may not have | ||
93 | * things ready yet. | ||
94 | */ | ||
95 | static cycle_t omap_32k_read_dummy(struct clocksource *cs) | ||
96 | { | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static struct clocksource clocksource_32k = { | ||
101 | .name = "32k_counter", | ||
102 | .rating = 250, | ||
103 | .read = omap_32k_read_dummy, | ||
104 | .mask = CLOCKSOURCE_MASK(32), | ||
105 | .shift = 10, | ||
106 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
107 | }; | ||
108 | |||
109 | /* | ||
110 | * Returns current time from boot in nsecs. It's OK for this to wrap | ||
111 | * around for now, as it's just a relative time stamp. | ||
112 | */ | ||
113 | unsigned long long sched_clock(void) | ||
114 | { | ||
115 | return clocksource_cyc2ns(clocksource_32k.read(&clocksource_32k), | ||
116 | clocksource_32k.mult, clocksource_32k.shift); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * read_persistent_clock - Return time from a persistent clock. | ||
121 | * | ||
122 | * Reads the time from a source which isn't disabled during PM, the | ||
123 | * 32k sync timer. Convert the cycles elapsed since last read into | ||
124 | * nsecs and adds to a monotonically increasing timespec. | ||
125 | */ | ||
126 | static struct timespec persistent_ts; | ||
127 | static cycles_t cycles, last_cycles; | ||
128 | void read_persistent_clock(struct timespec *ts) | ||
129 | { | ||
130 | unsigned long long nsecs; | ||
131 | cycles_t delta; | ||
132 | struct timespec *tsp = &persistent_ts; | ||
133 | |||
134 | last_cycles = cycles; | ||
135 | cycles = clocksource_32k.read(&clocksource_32k); | ||
136 | delta = cycles - last_cycles; | ||
137 | |||
138 | nsecs = clocksource_cyc2ns(delta, | ||
139 | clocksource_32k.mult, clocksource_32k.shift); | ||
140 | |||
141 | timespec_add_ns(tsp, nsecs); | ||
142 | *ts = *tsp; | ||
143 | } | ||
144 | |||
145 | static int __init omap_init_clocksource_32k(void) | ||
146 | { | ||
147 | static char err[] __initdata = KERN_ERR | ||
148 | "%s: can't register clocksource!\n"; | ||
149 | |||
150 | if (cpu_is_omap16xx() || cpu_class_is_omap2()) { | ||
151 | struct clk *sync_32k_ick; | ||
152 | |||
153 | if (cpu_is_omap16xx()) | ||
154 | clocksource_32k.read = omap16xx_32k_read; | ||
155 | else if (cpu_is_omap2420()) | ||
156 | clocksource_32k.read = omap2420_32k_read; | ||
157 | else if (cpu_is_omap2430()) | ||
158 | clocksource_32k.read = omap2430_32k_read; | ||
159 | else if (cpu_is_omap34xx()) | ||
160 | clocksource_32k.read = omap34xx_32k_read; | ||
161 | else if (cpu_is_omap44xx()) | ||
162 | clocksource_32k.read = omap44xx_32k_read; | ||
163 | else | ||
164 | return -ENODEV; | ||
165 | |||
166 | sync_32k_ick = clk_get(NULL, "omap_32ksync_ick"); | ||
167 | if (sync_32k_ick) | ||
168 | clk_enable(sync_32k_ick); | ||
169 | |||
170 | clocksource_32k.mult = clocksource_hz2mult(32768, | ||
171 | clocksource_32k.shift); | ||
172 | |||
173 | offset_32k = clocksource_32k.read(&clocksource_32k); | ||
174 | |||
175 | if (clocksource_register(&clocksource_32k)) | ||
176 | printk(err, clocksource_32k.name); | ||
177 | } | ||
178 | return 0; | ||
179 | } | ||
180 | arch_initcall(omap_init_clocksource_32k); | ||
181 | |||
182 | #endif /* !(defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP15XX)) */ | ||
183 | |||