2 * linux/arch/arm/mach-nomadik/timer.c
4 * Copyright (C) 2008 STMicroelectronics
5 * Copyright (C) 2010 Alessandro Rubini
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2, as
9 * published by the Free Software Foundation.
11 #include <linux/init.h>
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
15 #include <linux/clockchips.h>
16 #include <linux/clk.h>
17 #include <linux/jiffies.h>
18 #include <linux/err.h>
19 #include <asm/mach/time.h>
23 void __iomem *mtu_base; /* ssigned by machine code */
26 * Kernel assumes that sched_clock can be called early
27 * but the MTU may not yet be initialized.
29 static cycle_t nmdk_read_timer_dummy(struct clocksource *cs)
34 /* clocksource: MTU decrements, so we negate the value being read. */
35 static cycle_t nmdk_read_timer(struct clocksource *cs)
37 return -readl(mtu_base + MTU_VAL(0));
40 static struct clocksource nmdk_clksrc = {
43 .read = nmdk_read_timer_dummy,
44 .mask = CLOCKSOURCE_MASK(32),
46 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
50 * Override the global weak sched_clock symbol with this
51 * local implementation which uses the clocksource to get some
52 * better resolution when scheduling the kernel. We accept that
53 * this wraps around for now, since it is just a relative time
54 * stamp. (Inspired by OMAP implementation.)
56 unsigned long long notrace sched_clock(void)
58 return clocksource_cyc2ns(nmdk_clksrc.read(
64 /* Clockevent device: use one-shot mode */
65 static void nmdk_clkevt_mode(enum clock_event_mode mode,
66 struct clock_event_device *dev)
71 case CLOCK_EVT_MODE_PERIODIC:
72 pr_err("%s: periodic mode not supported\n", __func__);
74 case CLOCK_EVT_MODE_ONESHOT:
75 /* Load highest value, enable device, enable interrupts */
76 cr = readl(mtu_base + MTU_CR(1));
77 writel(0, mtu_base + MTU_LR(1));
78 writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(1));
79 writel(0x2, mtu_base + MTU_IMSC);
81 case CLOCK_EVT_MODE_SHUTDOWN:
82 case CLOCK_EVT_MODE_UNUSED:
84 writel(0, mtu_base + MTU_IMSC);
86 case CLOCK_EVT_MODE_RESUME:
91 static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
93 /* writing the value has immediate effect */
94 writel(evt, mtu_base + MTU_LR(1));
98 static struct clock_event_device nmdk_clkevt = {
100 .features = CLOCK_EVT_FEAT_ONESHOT,
103 .set_mode = nmdk_clkevt_mode,
104 .set_next_event = nmdk_clkevt_next,
108 * IRQ Handler for timer 1 of the MTU block.
110 static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
112 struct clock_event_device *evdev = dev_id;
114 writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
115 evdev->event_handler(evdev);
119 static struct irqaction nmdk_timer_irq = {
120 .name = "Nomadik Timer Tick",
121 .flags = IRQF_DISABLED | IRQF_TIMER,
122 .handler = nmdk_timer_interrupt,
123 .dev_id = &nmdk_clkevt,
126 void __init nmdk_timer_init(void)
133 clk0 = clk_get_sys("mtu0", NULL);
134 BUG_ON(IS_ERR(clk0));
136 clk1 = clk_get_sys("mtu1", NULL);
137 BUG_ON(IS_ERR(clk1));
143 * Tick rate is 2.4MHz for Nomadik and 110MHz for ux500:
144 * use a divide-by-16 counter if it's more than 16MHz
146 cr = MTU_CRn_32BITS;;
147 rate = clk_get_rate(clk0);
148 if (rate > 16 << 20) {
150 cr |= MTU_CRn_PRESCALE_16;
152 cr |= MTU_CRn_PRESCALE_1;
155 /* Timer 0 is the free running clocksource */
156 writel(cr, mtu_base + MTU_CR(0));
157 writel(0, mtu_base + MTU_LR(0));
158 writel(0, mtu_base + MTU_BGLR(0));
159 writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
161 nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
162 /* Now the scheduling clock is ready */
163 nmdk_clksrc.read = nmdk_read_timer;
165 if (clocksource_register(&nmdk_clksrc))
166 pr_err("timer: failed to initialize clock source %s\n",
169 /* Timer 1 is used for events, fix according to rate */
171 rate = clk_get_rate(clk1);
172 if (rate > 16 << 20) {
174 cr |= MTU_CRn_PRESCALE_16;
176 cr |= MTU_CRn_PRESCALE_1;
178 writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */
179 nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
180 nmdk_clkevt.max_delta_ns =
181 clockevent_delta2ns(0xffffffff, &nmdk_clkevt);
182 nmdk_clkevt.min_delta_ns =
183 clockevent_delta2ns(0x00000002, &nmdk_clkevt);
184 nmdk_clkevt.cpumask = cpumask_of(0);
186 /* Register irq and clockevents */
187 setup_irq(IRQ_MTU0, &nmdk_timer_irq);
188 clockevents_register_device(&nmdk_clkevt);