blob: 1b558efbe7320ee2c9436893c2d402d9f667666e [file] [log] [blame]
Paul Walmsleyaa218da2010-10-08 11:40:19 -06001/*
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>
Russell King5e06b642010-12-15 19:19:25 +000019#include <linux/sched.h>
Paul Walmsleyaa218da2010-10-08 11:40:19 -060020
Russell Kingdc548fb2010-12-15 21:53:51 +000021#include <asm/sched_clock.h>
22
Paul Walmsleyaa218da2010-10-08 11:40:19 -060023#include <plat/common.h>
24#include <plat/board.h>
25
26#include <plat/clock.h>
27
28
29/*
30 * 32KHz clocksource ... always available, on pretty most chips except
31 * OMAP 730 and 1510. Other timers could be used as clocksources, with
32 * higher resolution in free-running counter modes (e.g. 12 MHz xtal),
33 * but systems won't necessarily want to spend resources that way.
34 */
35
36#define OMAP16XX_TIMER_32K_SYNCHRONIZED 0xfffbc410
37
38#if !(defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP15XX))
39
40#include <linux/clocksource.h>
41
42/*
43 * offset_32k holds the init time counter value. It is then subtracted
44 * from every counter read to achieve a counter that counts time from the
45 * kernel boot (needed for sched_clock()).
46 */
47static u32 offset_32k __read_mostly;
48
49#ifdef CONFIG_ARCH_OMAP16XX
Russell King5e06b642010-12-15 19:19:25 +000050static cycle_t notrace omap16xx_32k_read(struct clocksource *cs)
Paul Walmsleyaa218da2010-10-08 11:40:19 -060051{
52 return omap_readl(OMAP16XX_TIMER_32K_SYNCHRONIZED) - offset_32k;
53}
54#else
55#define omap16xx_32k_read NULL
56#endif
57
58#ifdef CONFIG_ARCH_OMAP2420
Russell King5e06b642010-12-15 19:19:25 +000059static cycle_t notrace omap2420_32k_read(struct clocksource *cs)
Paul Walmsleyaa218da2010-10-08 11:40:19 -060060{
61 return omap_readl(OMAP2420_32KSYNCT_BASE + 0x10) - offset_32k;
62}
63#else
64#define omap2420_32k_read NULL
65#endif
66
67#ifdef CONFIG_ARCH_OMAP2430
Russell King5e06b642010-12-15 19:19:25 +000068static cycle_t notrace omap2430_32k_read(struct clocksource *cs)
Paul Walmsleyaa218da2010-10-08 11:40:19 -060069{
70 return omap_readl(OMAP2430_32KSYNCT_BASE + 0x10) - offset_32k;
71}
72#else
73#define omap2430_32k_read NULL
74#endif
75
76#ifdef CONFIG_ARCH_OMAP3
Russell King5e06b642010-12-15 19:19:25 +000077static cycle_t notrace omap34xx_32k_read(struct clocksource *cs)
Paul Walmsleyaa218da2010-10-08 11:40:19 -060078{
79 return omap_readl(OMAP3430_32KSYNCT_BASE + 0x10) - offset_32k;
80}
81#else
82#define omap34xx_32k_read NULL
83#endif
84
85#ifdef CONFIG_ARCH_OMAP4
Russell King5e06b642010-12-15 19:19:25 +000086static cycle_t notrace omap44xx_32k_read(struct clocksource *cs)
Paul Walmsleyaa218da2010-10-08 11:40:19 -060087{
88 return omap_readl(OMAP4430_32KSYNCT_BASE + 0x10) - offset_32k;
89}
90#else
91#define omap44xx_32k_read NULL
92#endif
93
94/*
95 * Kernel assumes that sched_clock can be called early but may not have
96 * things ready yet.
97 */
Russell King5e06b642010-12-15 19:19:25 +000098static cycle_t notrace omap_32k_read_dummy(struct clocksource *cs)
Paul Walmsleyaa218da2010-10-08 11:40:19 -060099{
100 return 0;
101}
102
103static struct clocksource clocksource_32k = {
104 .name = "32k_counter",
105 .rating = 250,
106 .read = omap_32k_read_dummy,
107 .mask = CLOCKSOURCE_MASK(32),
Paul Walmsleyaa218da2010-10-08 11:40:19 -0600108 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
109};
110
111/*
112 * Returns current time from boot in nsecs. It's OK for this to wrap
113 * around for now, as it's just a relative time stamp.
114 */
Russell Kingdc548fb2010-12-15 21:53:51 +0000115static DEFINE_CLOCK_DATA(cd);
116
117/*
118 * Constants generated by clocks_calc_mult_shift(m, s, 32768, NSEC_PER_SEC, 60).
119 * This gives a resolution of about 30us and a wrap period of about 36hrs.
120 */
121#define SC_MULT 4000000000u
122#define SC_SHIFT 17
123
Russell King5e06b642010-12-15 19:19:25 +0000124unsigned long long notrace sched_clock(void)
Paul Walmsleyaa218da2010-10-08 11:40:19 -0600125{
Russell Kingdc548fb2010-12-15 21:53:51 +0000126 u32 cyc = clocksource_32k.read(&clocksource_32k);
127 return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT);
128}
129
130static void notrace omap_update_sched_clock(void)
131{
132 u32 cyc = clocksource_32k.read(&clocksource_32k);
133 update_sched_clock(&cd, cyc, (u32)~0);
Paul Walmsleyaa218da2010-10-08 11:40:19 -0600134}
135
136/**
137 * read_persistent_clock - Return time from a persistent clock.
138 *
139 * Reads the time from a source which isn't disabled during PM, the
140 * 32k sync timer. Convert the cycles elapsed since last read into
141 * nsecs and adds to a monotonically increasing timespec.
142 */
143static struct timespec persistent_ts;
144static cycles_t cycles, last_cycles;
145void read_persistent_clock(struct timespec *ts)
146{
147 unsigned long long nsecs;
148 cycles_t delta;
149 struct timespec *tsp = &persistent_ts;
150
151 last_cycles = cycles;
152 cycles = clocksource_32k.read(&clocksource_32k);
153 delta = cycles - last_cycles;
154
155 nsecs = clocksource_cyc2ns(delta,
156 clocksource_32k.mult, clocksource_32k.shift);
157
158 timespec_add_ns(tsp, nsecs);
159 *ts = *tsp;
160}
161
162static int __init omap_init_clocksource_32k(void)
163{
164 static char err[] __initdata = KERN_ERR
165 "%s: can't register clocksource!\n";
166
167 if (cpu_is_omap16xx() || cpu_class_is_omap2()) {
168 struct clk *sync_32k_ick;
169
170 if (cpu_is_omap16xx())
171 clocksource_32k.read = omap16xx_32k_read;
172 else if (cpu_is_omap2420())
173 clocksource_32k.read = omap2420_32k_read;
174 else if (cpu_is_omap2430())
175 clocksource_32k.read = omap2430_32k_read;
176 else if (cpu_is_omap34xx())
177 clocksource_32k.read = omap34xx_32k_read;
178 else if (cpu_is_omap44xx())
179 clocksource_32k.read = omap44xx_32k_read;
180 else
181 return -ENODEV;
182
183 sync_32k_ick = clk_get(NULL, "omap_32ksync_ick");
184 if (sync_32k_ick)
185 clk_enable(sync_32k_ick);
186
Paul Walmsleyaa218da2010-10-08 11:40:19 -0600187 offset_32k = clocksource_32k.read(&clocksource_32k);
188
Russell King8437c252010-12-13 13:18:44 +0000189 if (clocksource_register_hz(&clocksource_32k, 32768))
Paul Walmsleyaa218da2010-10-08 11:40:19 -0600190 printk(err, clocksource_32k.name);
Russell Kingdc548fb2010-12-15 21:53:51 +0000191
192 init_fixed_sched_clock(&cd, omap_update_sched_clock, 32,
193 32768, SC_MULT, SC_SHIFT);
Paul Walmsleyaa218da2010-10-08 11:40:19 -0600194 }
195 return 0;
196}
197arch_initcall(omap_init_clocksource_32k);
198
199#endif /* !(defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP15XX)) */
200