1a57fb870SKevin Hilman /*
25c8388e5STony Lindgren * linux/arch/arm/mach-omap1/timer32k.c
3a57fb870SKevin Hilman *
4a57fb870SKevin Hilman * OMAP 32K Timer
5a57fb870SKevin Hilman *
6a57fb870SKevin Hilman * Copyright (C) 2004 - 2005 Nokia Corporation
7a57fb870SKevin Hilman * Partial timer rewrite and additional dynamic tick timer support by
8a57fb870SKevin Hilman * Tony Lindgen <tony@atomide.com> and
9a57fb870SKevin Hilman * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
10a57fb870SKevin Hilman * OMAP Dual-mode timer framework support by Timo Teras
11a57fb870SKevin Hilman *
12a57fb870SKevin Hilman * MPU timer code based on the older MPU timer code for OMAP
13a57fb870SKevin Hilman * Copyright (C) 2000 RidgeRun, Inc.
14a57fb870SKevin Hilman * Author: Greg Lonnon <glonnon@ridgerun.com>
15a57fb870SKevin Hilman *
16a57fb870SKevin Hilman * This program is free software; you can redistribute it and/or modify it
17a57fb870SKevin Hilman * under the terms of the GNU General Public License as published by the
18a57fb870SKevin Hilman * Free Software Foundation; either version 2 of the License, or (at your
19a57fb870SKevin Hilman * option) any later version.
20a57fb870SKevin Hilman *
21a57fb870SKevin Hilman * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
22a57fb870SKevin Hilman * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23a57fb870SKevin Hilman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
24a57fb870SKevin Hilman * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25a57fb870SKevin Hilman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26a57fb870SKevin Hilman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27a57fb870SKevin Hilman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28a57fb870SKevin Hilman * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29a57fb870SKevin Hilman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30a57fb870SKevin Hilman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31a57fb870SKevin Hilman *
32a57fb870SKevin Hilman * You should have received a copy of the GNU General Public License along
33a57fb870SKevin Hilman * with this program; if not, write to the Free Software Foundation, Inc.,
34a57fb870SKevin Hilman * 675 Mass Ave, Cambridge, MA 02139, USA.
35a57fb870SKevin Hilman */
36a57fb870SKevin Hilman
37a57fb870SKevin Hilman #include <linux/kernel.h>
38a57fb870SKevin Hilman #include <linux/init.h>
39a57fb870SKevin Hilman #include <linux/delay.h>
40a57fb870SKevin Hilman #include <linux/interrupt.h>
41a57fb870SKevin Hilman #include <linux/sched.h>
42a57fb870SKevin Hilman #include <linux/spinlock.h>
43a57fb870SKevin Hilman #include <linux/err.h>
44a57fb870SKevin Hilman #include <linux/clk.h>
45a57fb870SKevin Hilman #include <linux/clocksource.h>
46a57fb870SKevin Hilman #include <linux/clockchips.h>
47fced80c7SRussell King #include <linux/io.h>
48d379e889SArnd Bergmann #include <linux/sched_clock.h>
49a57fb870SKevin Hilman
50a57fb870SKevin Hilman #include <asm/irq.h>
51a57fb870SKevin Hilman #include <asm/mach/irq.h>
52a57fb870SKevin Hilman #include <asm/mach/time.h>
532e3ee9f4STony Lindgren
547e0a9e62SArnd Bergmann #include "hardware.h"
552e3ee9f4STony Lindgren #include "common.h"
562e3ee9f4STony Lindgren
57a57fb870SKevin Hilman /*
58a57fb870SKevin Hilman * ---------------------------------------------------------------------------
59a57fb870SKevin Hilman * 32KHz OS timer
60a57fb870SKevin Hilman *
61a57fb870SKevin Hilman * This currently works only on 16xx, as 1510 does not have the continuous
62a57fb870SKevin Hilman * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track
63a57fb870SKevin Hilman * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer
64a57fb870SKevin Hilman * on 1510 would be possible, but the timer would not be as accurate as
65a57fb870SKevin Hilman * with the 32KHz synchronized timer.
66a57fb870SKevin Hilman * ---------------------------------------------------------------------------
67a57fb870SKevin Hilman */
68a57fb870SKevin Hilman
69a57fb870SKevin Hilman /* 16xx specific defines */
70a57fb870SKevin Hilman #define OMAP1_32K_TIMER_BASE 0xfffb9000
711fe97c8fSVaibhav Hiremath #define OMAP1_32KSYNC_TIMER_BASE 0xfffbc400
72a57fb870SKevin Hilman #define OMAP1_32K_TIMER_CR 0x08
73a57fb870SKevin Hilman #define OMAP1_32K_TIMER_TVR 0x00
74a57fb870SKevin Hilman #define OMAP1_32K_TIMER_TCR 0x04
75a57fb870SKevin Hilman
76a57fb870SKevin Hilman #define OMAP_32K_TICKS_PER_SEC (32768)
77a57fb870SKevin Hilman
78a57fb870SKevin Hilman /*
79a57fb870SKevin Hilman * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1
80a57fb870SKevin Hilman * so with HZ = 128, TVR = 255.
81a57fb870SKevin Hilman */
82a57fb870SKevin Hilman #define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1)
83a57fb870SKevin Hilman
84a57fb870SKevin Hilman #define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \
85a57fb870SKevin Hilman (((nr_jiffies) * (clock_rate)) / HZ)
86a57fb870SKevin Hilman
omap_32k_timer_write(int val,int reg)87a57fb870SKevin Hilman static inline void omap_32k_timer_write(int val, int reg)
88a57fb870SKevin Hilman {
89a57fb870SKevin Hilman omap_writew(val, OMAP1_32K_TIMER_BASE + reg);
90a57fb870SKevin Hilman }
91a57fb870SKevin Hilman
omap_32k_timer_start(unsigned long load_val)92a57fb870SKevin Hilman static inline void omap_32k_timer_start(unsigned long load_val)
93a57fb870SKevin Hilman {
94a57fb870SKevin Hilman if (!load_val)
95a57fb870SKevin Hilman load_val = 1;
96a57fb870SKevin Hilman omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR);
97a57fb870SKevin Hilman omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR);
98a57fb870SKevin Hilman }
99a57fb870SKevin Hilman
omap_32k_timer_stop(void)100a57fb870SKevin Hilman static inline void omap_32k_timer_stop(void)
101a57fb870SKevin Hilman {
102a57fb870SKevin Hilman omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR);
103a57fb870SKevin Hilman }
104a57fb870SKevin Hilman
105a57fb870SKevin Hilman #define omap_32k_timer_ack_irq()
106a57fb870SKevin Hilman
omap_32k_timer_set_next_event(unsigned long delta,struct clock_event_device * dev)1075c8388e5STony Lindgren static int omap_32k_timer_set_next_event(unsigned long delta,
1085c8388e5STony Lindgren struct clock_event_device *dev)
1095c8388e5STony Lindgren {
1105c8388e5STony Lindgren omap_32k_timer_start(delta);
1115c8388e5STony Lindgren
1125c8388e5STony Lindgren return 0;
1135c8388e5STony Lindgren }
1145c8388e5STony Lindgren
omap_32k_timer_shutdown(struct clock_event_device * evt)11569ec063fSViresh Kumar static int omap_32k_timer_shutdown(struct clock_event_device *evt)
116a57fb870SKevin Hilman {
117a57fb870SKevin Hilman omap_32k_timer_stop();
11869ec063fSViresh Kumar return 0;
119a57fb870SKevin Hilman }
12069ec063fSViresh Kumar
omap_32k_timer_set_periodic(struct clock_event_device * evt)12169ec063fSViresh Kumar static int omap_32k_timer_set_periodic(struct clock_event_device *evt)
12269ec063fSViresh Kumar {
12369ec063fSViresh Kumar omap_32k_timer_stop();
12469ec063fSViresh Kumar omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD);
12569ec063fSViresh Kumar return 0;
126a57fb870SKevin Hilman }
127a57fb870SKevin Hilman
128a57fb870SKevin Hilman static struct clock_event_device clockevent_32k_timer = {
129a57fb870SKevin Hilman .name = "32k-timer",
13069ec063fSViresh Kumar .features = CLOCK_EVT_FEAT_PERIODIC |
13169ec063fSViresh Kumar CLOCK_EVT_FEAT_ONESHOT,
1325c8388e5STony Lindgren .set_next_event = omap_32k_timer_set_next_event,
13369ec063fSViresh Kumar .set_state_shutdown = omap_32k_timer_shutdown,
13469ec063fSViresh Kumar .set_state_periodic = omap_32k_timer_set_periodic,
13569ec063fSViresh Kumar .set_state_oneshot = omap_32k_timer_shutdown,
13669ec063fSViresh Kumar .tick_resume = omap_32k_timer_shutdown,
137a57fb870SKevin Hilman };
138a57fb870SKevin Hilman
omap_32k_timer_interrupt(int irq,void * dev_id)139a57fb870SKevin Hilman static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id)
140a57fb870SKevin Hilman {
141a57fb870SKevin Hilman struct clock_event_device *evt = &clockevent_32k_timer;
142a57fb870SKevin Hilman omap_32k_timer_ack_irq();
143a57fb870SKevin Hilman
144a57fb870SKevin Hilman evt->event_handler(evt);
145a57fb870SKevin Hilman
146a57fb870SKevin Hilman return IRQ_HANDLED;
147a57fb870SKevin Hilman }
148a57fb870SKevin Hilman
omap_init_32k_timer(void)149a57fb870SKevin Hilman static __init void omap_init_32k_timer(void)
150a57fb870SKevin Hilman {
151b75ca521Safzal mohammed if (request_irq(INT_OS_TIMER, omap_32k_timer_interrupt,
152b75ca521Safzal mohammed IRQF_TIMER | IRQF_IRQPOLL, "32KHz timer", NULL))
153b75ca521Safzal mohammed pr_err("Failed to request irq %d(32KHz timer)\n", INT_OS_TIMER);
1545c8388e5STony Lindgren
155320ab2b0SRusty Russell clockevent_32k_timer.cpumask = cpumask_of(0);
156838a2ae8SShawn Guo clockevents_config_and_register(&clockevent_32k_timer,
157838a2ae8SShawn Guo OMAP_32K_TICKS_PER_SEC, 1, 0xfffffffe);
158a57fb870SKevin Hilman }
159a57fb870SKevin Hilman
160d379e889SArnd Bergmann /* OMAP2_32KSYNCNT_CR_OFF: offset of 32ksync counter register */
161d379e889SArnd Bergmann #define OMAP2_32KSYNCNT_REV_OFF 0x0
162d379e889SArnd Bergmann #define OMAP2_32KSYNCNT_REV_SCHEME (0x3 << 30)
163d379e889SArnd Bergmann #define OMAP2_32KSYNCNT_CR_OFF_LOW 0x10
164d379e889SArnd Bergmann #define OMAP2_32KSYNCNT_CR_OFF_HIGH 0x30
165d379e889SArnd Bergmann
166d379e889SArnd Bergmann /*
167d379e889SArnd Bergmann * 32KHz clocksource ... always available, on pretty most chips except
168d379e889SArnd Bergmann * OMAP 730 and 1510. Other timers could be used as clocksources, with
169d379e889SArnd Bergmann * higher resolution in free-running counter modes (e.g. 12 MHz xtal),
170d379e889SArnd Bergmann * but systems won't necessarily want to spend resources that way.
171d379e889SArnd Bergmann */
172d379e889SArnd Bergmann static void __iomem *sync32k_cnt_reg;
173d379e889SArnd Bergmann
omap_32k_read_sched_clock(void)174d379e889SArnd Bergmann static u64 notrace omap_32k_read_sched_clock(void)
175d379e889SArnd Bergmann {
176d379e889SArnd Bergmann return sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
177d379e889SArnd Bergmann }
178d379e889SArnd Bergmann
179*7eeca8ccSRandy Dunlap static struct timespec64 persistent_ts;
180*7eeca8ccSRandy Dunlap static cycles_t cycles;
181*7eeca8ccSRandy Dunlap static unsigned int persistent_mult, persistent_shift;
182*7eeca8ccSRandy Dunlap
183d379e889SArnd Bergmann /**
184d379e889SArnd Bergmann * omap_read_persistent_clock64 - Return time from a persistent clock.
185*7eeca8ccSRandy Dunlap * @ts: &struct timespec64 for the returned time
186d379e889SArnd Bergmann *
187d379e889SArnd Bergmann * Reads the time from a source which isn't disabled during PM, the
188d379e889SArnd Bergmann * 32k sync timer. Convert the cycles elapsed since last read into
189d379e889SArnd Bergmann * nsecs and adds to a monotonically increasing timespec64.
190d379e889SArnd Bergmann */
omap_read_persistent_clock64(struct timespec64 * ts)191d379e889SArnd Bergmann static void omap_read_persistent_clock64(struct timespec64 *ts)
192d379e889SArnd Bergmann {
193d379e889SArnd Bergmann unsigned long long nsecs;
194d379e889SArnd Bergmann cycles_t last_cycles;
195d379e889SArnd Bergmann
196d379e889SArnd Bergmann last_cycles = cycles;
197d379e889SArnd Bergmann cycles = sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
198d379e889SArnd Bergmann
199d379e889SArnd Bergmann nsecs = clocksource_cyc2ns(cycles - last_cycles,
200d379e889SArnd Bergmann persistent_mult, persistent_shift);
201d379e889SArnd Bergmann
202d379e889SArnd Bergmann timespec64_add_ns(&persistent_ts, nsecs);
203d379e889SArnd Bergmann
204d379e889SArnd Bergmann *ts = persistent_ts;
205d379e889SArnd Bergmann }
206d379e889SArnd Bergmann
207d379e889SArnd Bergmann /**
208d379e889SArnd Bergmann * omap_init_clocksource_32k - setup and register counter 32k as a
209d379e889SArnd Bergmann * kernel clocksource
210*7eeca8ccSRandy Dunlap * @vbase: base addr of counter_32k module
211d379e889SArnd Bergmann *
212*7eeca8ccSRandy Dunlap * Returns: %0 upon success or negative error code upon failure.
213d379e889SArnd Bergmann *
214d379e889SArnd Bergmann */
omap_init_clocksource_32k(void __iomem * vbase)215e514f1fdSArnd Bergmann static int __init omap_init_clocksource_32k(void __iomem *vbase)
216d379e889SArnd Bergmann {
217d379e889SArnd Bergmann int ret;
218d379e889SArnd Bergmann
219d379e889SArnd Bergmann /*
220d379e889SArnd Bergmann * 32k sync Counter IP register offsets vary between the
221d379e889SArnd Bergmann * highlander version and the legacy ones.
222d379e889SArnd Bergmann * The 'SCHEME' bits(30-31) of the revision register is used
223d379e889SArnd Bergmann * to identify the version.
224d379e889SArnd Bergmann */
225d379e889SArnd Bergmann if (readl_relaxed(vbase + OMAP2_32KSYNCNT_REV_OFF) &
226d379e889SArnd Bergmann OMAP2_32KSYNCNT_REV_SCHEME)
227d379e889SArnd Bergmann sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_HIGH;
228d379e889SArnd Bergmann else
229d379e889SArnd Bergmann sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_LOW;
230d379e889SArnd Bergmann
231d379e889SArnd Bergmann /*
232d379e889SArnd Bergmann * 120000 rough estimate from the calculations in
233d379e889SArnd Bergmann * __clocksource_update_freq_scale.
234d379e889SArnd Bergmann */
235d379e889SArnd Bergmann clocks_calc_mult_shift(&persistent_mult, &persistent_shift,
236d379e889SArnd Bergmann 32768, NSEC_PER_SEC, 120000);
237d379e889SArnd Bergmann
238d379e889SArnd Bergmann ret = clocksource_mmio_init(sync32k_cnt_reg, "32k_counter", 32768,
239d379e889SArnd Bergmann 250, 32, clocksource_mmio_readl_up);
240d379e889SArnd Bergmann if (ret) {
241d379e889SArnd Bergmann pr_err("32k_counter: can't register clocksource\n");
242d379e889SArnd Bergmann return ret;
243d379e889SArnd Bergmann }
244d379e889SArnd Bergmann
245d379e889SArnd Bergmann sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
246d379e889SArnd Bergmann register_persistent_clock(omap_read_persistent_clock64);
247d379e889SArnd Bergmann pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
248d379e889SArnd Bergmann
249d379e889SArnd Bergmann return 0;
250d379e889SArnd Bergmann }
251d379e889SArnd Bergmann
252a57fb870SKevin Hilman /*
253a57fb870SKevin Hilman * ---------------------------------------------------------------------------
254a57fb870SKevin Hilman * Timer initialization
255a57fb870SKevin Hilman * ---------------------------------------------------------------------------
256a57fb870SKevin Hilman */
omap_32k_timer_init(void)25718799911SVaibhav Hiremath int __init omap_32k_timer_init(void)
258a57fb870SKevin Hilman {
25918799911SVaibhav Hiremath int ret = -ENODEV;
26018799911SVaibhav Hiremath
2611fe97c8fSVaibhav Hiremath if (cpu_is_omap16xx()) {
2621fe97c8fSVaibhav Hiremath void __iomem *base;
2631fe97c8fSVaibhav Hiremath struct clk *sync32k_ick;
2641fe97c8fSVaibhav Hiremath
2651fe97c8fSVaibhav Hiremath base = ioremap(OMAP1_32KSYNC_TIMER_BASE, SZ_1K);
2661fe97c8fSVaibhav Hiremath if (!base) {
2671fe97c8fSVaibhav Hiremath pr_err("32k_counter: failed to map base addr\n");
2681fe97c8fSVaibhav Hiremath return -ENODEV;
2691fe97c8fSVaibhav Hiremath }
2701fe97c8fSVaibhav Hiremath
2711fe97c8fSVaibhav Hiremath sync32k_ick = clk_get(NULL, "omap_32ksync_ick");
2721fe97c8fSVaibhav Hiremath if (!IS_ERR(sync32k_ick))
27398e0f634SJanusz Krzysztofik clk_prepare_enable(sync32k_ick);
2741fe97c8fSVaibhav Hiremath
2751fe97c8fSVaibhav Hiremath ret = omap_init_clocksource_32k(base);
2761fe97c8fSVaibhav Hiremath }
27718799911SVaibhav Hiremath
27818799911SVaibhav Hiremath if (!ret)
279a57fb870SKevin Hilman omap_init_32k_timer();
280a57fb870SKevin Hilman
28118799911SVaibhav Hiremath return ret;
28205b5ca9bSTony Lindgren }
283