]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - arch/arm/plat-nomadik/timer.c
Linux 3.1-rc7
[mirror_ubuntu-zesty-kernel.git] / arch / arm / plat-nomadik / timer.c
CommitLineData
28ad94ec 1/*
a0719f52 2 * linux/arch/arm/plat-nomadik/timer.c
28ad94ec
AR
3 *
4 * Copyright (C) 2008 STMicroelectronics
b102c01f 5 * Copyright (C) 2010 Alessandro Rubini
8fbb97a2 6 * Copyright (C) 2010 Linus Walleij for ST-Ericsson
28ad94ec
AR
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2, as
10 * published by the Free Software Foundation.
11 */
12#include <linux/init.h>
13#include <linux/interrupt.h>
14#include <linux/irq.h>
15#include <linux/io.h>
16#include <linux/clockchips.h>
ba327b1e 17#include <linux/clk.h>
28ad94ec 18#include <linux/jiffies.h>
ba327b1e 19#include <linux/err.h>
5e06b649 20#include <linux/sched.h>
28ad94ec 21#include <asm/mach/time.h>
ec05aa13 22#include <asm/sched_clock.h>
28ad94ec 23
59b559d7 24#include <plat/mtu.h>
28ad94ec 25
8fbb97a2 26void __iomem *mtu_base; /* Assigned by machine code */
59b559d7 27
2a847513
LW
28/*
29 * Override the global weak sched_clock symbol with this
30 * local implementation which uses the clocksource to get some
8fbb97a2 31 * better resolution when scheduling the kernel.
2a847513 32 */
ec05aa13 33static DEFINE_CLOCK_DATA(cd);
8fbb97a2 34
2a847513
LW
35unsigned long long notrace sched_clock(void)
36{
ec05aa13 37 u32 cyc;
8fbb97a2
LW
38
39 if (unlikely(!mtu_base))
40 return 0;
41
ec05aa13
RK
42 cyc = -readl(mtu_base + MTU_VAL(0));
43 return cyc_to_sched_clock(&cd, cyc, (u32)~0);
8fbb97a2
LW
44}
45
ec05aa13 46static void notrace nomadik_update_sched_clock(void)
8fbb97a2 47{
ec05aa13
RK
48 u32 cyc = -readl(mtu_base + MTU_VAL(0));
49 update_sched_clock(&cd, cyc, (u32)~0);
2a847513
LW
50}
51
b102c01f 52/* Clockevent device: use one-shot mode */
28ad94ec
AR
53static void nmdk_clkevt_mode(enum clock_event_mode mode,
54 struct clock_event_device *dev)
55{
b102c01f
AR
56 u32 cr;
57
28ad94ec
AR
58 switch (mode) {
59 case CLOCK_EVT_MODE_PERIODIC:
b102c01f 60 pr_err("%s: periodic mode not supported\n", __func__);
28ad94ec
AR
61 break;
62 case CLOCK_EVT_MODE_ONESHOT:
b102c01f
AR
63 /* Load highest value, enable device, enable interrupts */
64 cr = readl(mtu_base + MTU_CR(1));
65 writel(0, mtu_base + MTU_LR(1));
66 writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(1));
a0719f52 67 writel(1 << 1, mtu_base + MTU_IMSC);
b102c01f 68 break;
28ad94ec
AR
69 case CLOCK_EVT_MODE_SHUTDOWN:
70 case CLOCK_EVT_MODE_UNUSED:
b102c01f
AR
71 /* disable irq */
72 writel(0, mtu_base + MTU_IMSC);
2917947a
LW
73 /* disable timer */
74 cr = readl(mtu_base + MTU_CR(1));
75 cr &= ~MTU_CRn_ENA;
76 writel(cr, mtu_base + MTU_CR(1));
77 /* load some high default value */
78 writel(0xffffffff, mtu_base + MTU_LR(1));
28ad94ec
AR
79 break;
80 case CLOCK_EVT_MODE_RESUME:
81 break;
82 }
83}
84
b102c01f
AR
85static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
86{
87 /* writing the value has immediate effect */
88 writel(evt, mtu_base + MTU_LR(1));
89 return 0;
90}
91
28ad94ec 92static struct clock_event_device nmdk_clkevt = {
b102c01f
AR
93 .name = "mtu_1",
94 .features = CLOCK_EVT_FEAT_ONESHOT,
b102c01f 95 .rating = 200,
28ad94ec 96 .set_mode = nmdk_clkevt_mode,
b102c01f 97 .set_next_event = nmdk_clkevt_next,
28ad94ec
AR
98};
99
100/*
b102c01f 101 * IRQ Handler for timer 1 of the MTU block.
28ad94ec
AR
102 */
103static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
104{
b102c01f 105 struct clock_event_device *evdev = dev_id;
28ad94ec 106
b102c01f
AR
107 writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
108 evdev->event_handler(evdev);
28ad94ec
AR
109 return IRQ_HANDLED;
110}
111
28ad94ec
AR
112static struct irqaction nmdk_timer_irq = {
113 .name = "Nomadik Timer Tick",
114 .flags = IRQF_DISABLED | IRQF_TIMER,
115 .handler = nmdk_timer_interrupt,
b102c01f 116 .dev_id = &nmdk_clkevt,
28ad94ec
AR
117};
118
59b559d7 119void __init nmdk_timer_init(void)
28ad94ec 120{
28ad94ec 121 unsigned long rate;
ba327b1e 122 struct clk *clk0;
a0719f52 123 u32 cr = MTU_CRn_32BITS;
ba327b1e
LW
124
125 clk0 = clk_get_sys("mtu0", NULL);
126 BUG_ON(IS_ERR(clk0));
127
ba327b1e 128 clk_enable(clk0);
b102c01f
AR
129
130 /*
a0719f52
LW
131 * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
132 * for ux500.
133 * Use a divide-by-16 counter if the tick rate is more than 32MHz.
134 * At 32 MHz, the timer (with 32 bit counter) can be programmed
135 * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
136 * with 16 gives too low timer resolution.
b102c01f 137 */
ba327b1e 138 rate = clk_get_rate(clk0);
a0719f52 139 if (rate > 32000000) {
b102c01f
AR
140 rate /= 16;
141 cr |= MTU_CRn_PRESCALE_16;
142 } else {
143 cr |= MTU_CRn_PRESCALE_1;
144 }
28ad94ec 145
b102c01f
AR
146 /* Timer 0 is the free running clocksource */
147 writel(cr, mtu_base + MTU_CR(0));
148 writel(0, mtu_base + MTU_LR(0));
149 writel(0, mtu_base + MTU_BGLR(0));
150 writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
28ad94ec 151
bfe45e0b
RK
152 if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
153 rate, 200, 32, clocksource_mmio_readl_down))
b102c01f 154 pr_err("timer: failed to initialize clock source %s\n",
bfe45e0b 155 "mtu_0");
b102c01f 156
ec05aa13 157 init_sched_clock(&cd, nomadik_update_sched_clock, 32, rate);
8fbb97a2 158
99f76891
LW
159 /* Timer 1 is used for events */
160
2917947a
LW
161 clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE);
162
b102c01f 163 writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */
2917947a 164
b102c01f
AR
165 nmdk_clkevt.max_delta_ns =
166 clockevent_delta2ns(0xffffffff, &nmdk_clkevt);
167 nmdk_clkevt.min_delta_ns =
168 clockevent_delta2ns(0x00000002, &nmdk_clkevt);
169 nmdk_clkevt.cpumask = cpumask_of(0);
28ad94ec
AR
170
171 /* Register irq and clockevents */
172 setup_irq(IRQ_MTU0, &nmdk_timer_irq);
28ad94ec
AR
173 clockevents_register_device(&nmdk_clkevt);
174}