Merge branch 'fix' of git://git.kernel.org/pub/scm/linux/kernel/git/ycmiao/pxa-linux-2.6
[pandora-kernel.git] / arch / arm / plat-nomadik / timer.c
1 /*
2  *  linux/arch/arm/mach-nomadik/timer.c
3  *
4  * Copyright (C) 2008 STMicroelectronics
5  * Copyright (C) 2010 Alessandro Rubini
6  *
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.
10  */
11 #include <linux/init.h>
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
14 #include <linux/io.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>
20
21 #include <plat/mtu.h>
22
23 void __iomem *mtu_base; /* ssigned by machine code */
24
25 /*
26  * Kernel assumes that sched_clock can be called early
27  * but the MTU may not yet be initialized.
28  */
29 static cycle_t nmdk_read_timer_dummy(struct clocksource *cs)
30 {
31         return 0;
32 }
33
34 /* clocksource: MTU decrements, so we negate the value being read. */
35 static cycle_t nmdk_read_timer(struct clocksource *cs)
36 {
37         return -readl(mtu_base + MTU_VAL(0));
38 }
39
40 static struct clocksource nmdk_clksrc = {
41         .name           = "mtu_0",
42         .rating         = 200,
43         .read           = nmdk_read_timer_dummy,
44         .mask           = CLOCKSOURCE_MASK(32),
45         .shift          = 20,
46         .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
47 };
48
49 /*
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.)
55  */
56 unsigned long long notrace sched_clock(void)
57 {
58         return clocksource_cyc2ns(nmdk_clksrc.read(
59                                   &nmdk_clksrc),
60                                   nmdk_clksrc.mult,
61                                   nmdk_clksrc.shift);
62 }
63
64 /* Clockevent device: use one-shot mode */
65 static void nmdk_clkevt_mode(enum clock_event_mode mode,
66                              struct clock_event_device *dev)
67 {
68         u32 cr;
69
70         switch (mode) {
71         case CLOCK_EVT_MODE_PERIODIC:
72                 pr_err("%s: periodic mode not supported\n", __func__);
73                 break;
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);
80                 break;
81         case CLOCK_EVT_MODE_SHUTDOWN:
82         case CLOCK_EVT_MODE_UNUSED:
83                 /* disable irq */
84                 writel(0, mtu_base + MTU_IMSC);
85                 break;
86         case CLOCK_EVT_MODE_RESUME:
87                 break;
88         }
89 }
90
91 static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
92 {
93         /* writing the value has immediate effect */
94         writel(evt, mtu_base + MTU_LR(1));
95         return 0;
96 }
97
98 static struct clock_event_device nmdk_clkevt = {
99         .name           = "mtu_1",
100         .features       = CLOCK_EVT_FEAT_ONESHOT,
101         .shift          = 32,
102         .rating         = 200,
103         .set_mode       = nmdk_clkevt_mode,
104         .set_next_event = nmdk_clkevt_next,
105 };
106
107 /*
108  * IRQ Handler for timer 1 of the MTU block.
109  */
110 static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
111 {
112         struct clock_event_device *evdev = dev_id;
113
114         writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
115         evdev->event_handler(evdev);
116         return IRQ_HANDLED;
117 }
118
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,
124 };
125
126 void __init nmdk_timer_init(void)
127 {
128         unsigned long rate;
129         struct clk *clk0;
130         struct clk *clk1;
131         u32 cr;
132
133         clk0 = clk_get_sys("mtu0", NULL);
134         BUG_ON(IS_ERR(clk0));
135
136         clk1 = clk_get_sys("mtu1", NULL);
137         BUG_ON(IS_ERR(clk1));
138
139         clk_enable(clk0);
140         clk_enable(clk1);
141
142         /*
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
145          */
146         cr = MTU_CRn_32BITS;;
147         rate = clk_get_rate(clk0);
148         if (rate > 16 << 20) {
149                 rate /= 16;
150                 cr |= MTU_CRn_PRESCALE_16;
151         } else {
152                 cr |= MTU_CRn_PRESCALE_1;
153         }
154
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));
160
161         nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
162         /* Now the scheduling clock is ready */
163         nmdk_clksrc.read = nmdk_read_timer;
164
165         if (clocksource_register(&nmdk_clksrc))
166                 pr_err("timer: failed to initialize clock source %s\n",
167                        nmdk_clksrc.name);
168
169         /* Timer 1 is used for events, fix according to rate */
170         cr = MTU_CRn_32BITS;
171         rate = clk_get_rate(clk1);
172         if (rate > 16 << 20) {
173                 rate /= 16;
174                 cr |= MTU_CRn_PRESCALE_16;
175         } else {
176                 cr |= MTU_CRn_PRESCALE_1;
177         }
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);
185
186         /* Register irq and clockevents */
187         setup_irq(IRQ_MTU0, &nmdk_timer_irq);
188         clockevents_register_device(&nmdk_clkevt);
189 }