aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-nomadik/timer.c
diff options
context:
space:
mode:
authorJonas Aaberg <jonas.aberg@stericsson.com>2011-09-14 04:32:51 -0400
committerLinus Walleij <linus.walleij@linaro.org>2011-09-22 09:44:10 -0400
commit2f73a06843e357190a7a3924c7afb0534cf6adef (patch)
tree098cc733897c0d182eadcbb445d04d3f129e733e /arch/arm/plat-nomadik/timer.c
parent5f5663a436a4c54ede441b4f8ab3f947e78db345 (diff)
ARM: plat-nomadik: timer: Add support for periodic timers
This adds support for a periodic mode in the MTU (Nomadik) timer clockevent driver. It also include changes needed for deeper powerstates where MTU block gets powered off. Cc: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Jonas Aaberg <jonas.aberg@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'arch/arm/plat-nomadik/timer.c')
-rw-r--r--arch/arm/plat-nomadik/timer.c88
1 files changed, 60 insertions, 28 deletions
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c
index bd638c552f0..a04b5215b6d 100644
--- a/arch/arm/plat-nomadik/timer.c
+++ b/arch/arm/plat-nomadik/timer.c
@@ -23,7 +23,12 @@
23 23
24#include <plat/mtu.h> 24#include <plat/mtu.h>
25 25
26static bool clkevt_periodic;
27static u32 clk_prescale;
28static u32 nmdk_cycle; /* write-once */
29
26void __iomem *mtu_base; /* Assigned by machine code */ 30void __iomem *mtu_base; /* Assigned by machine code */
31
27#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK 32#ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK
28/* 33/*
29 * Override the global weak sched_clock symbol with this 34 * Override the global weak sched_clock symbol with this
@@ -49,31 +54,55 @@ static void notrace nomadik_update_sched_clock(void)
49 update_sched_clock(&cd, cyc, (u32)~0); 54 update_sched_clock(&cd, cyc, (u32)~0);
50} 55}
51#endif 56#endif
57
52/* Clockevent device: use one-shot mode */ 58/* Clockevent device: use one-shot mode */
59static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
60{
61 writel(1 << 1, mtu_base + MTU_IMSC);
62 writel(evt, mtu_base + MTU_LR(1));
63 /* Load highest value, enable device, enable interrupts */
64 writel(MTU_CRn_ONESHOT | clk_prescale |
65 MTU_CRn_32BITS | MTU_CRn_ENA,
66 mtu_base + MTU_CR(1));
67
68 return 0;
69}
70
71static void nmdk_clkevt_reset(void)
72{
73 if (clkevt_periodic) {
74
75 /* Timer: configure load and background-load, and fire it up */
76 writel(nmdk_cycle, mtu_base + MTU_LR(1));
77 writel(nmdk_cycle, mtu_base + MTU_BGLR(1));
78
79 writel(MTU_CRn_PERIODIC | clk_prescale |
80 MTU_CRn_32BITS | MTU_CRn_ENA,
81 mtu_base + MTU_CR(1));
82 writel(1 << 1, mtu_base + MTU_IMSC);
83 } else {
84 /* Generate an interrupt to start the clockevent again */
85 (void) nmdk_clkevt_next(nmdk_cycle, NULL);
86 }
87}
88
53static void nmdk_clkevt_mode(enum clock_event_mode mode, 89static void nmdk_clkevt_mode(enum clock_event_mode mode,
54 struct clock_event_device *dev) 90 struct clock_event_device *dev)
55{ 91{
56 u32 cr;
57 92
58 switch (mode) { 93 switch (mode) {
59 case CLOCK_EVT_MODE_PERIODIC: 94 case CLOCK_EVT_MODE_PERIODIC:
60 pr_err("%s: periodic mode not supported\n", __func__); 95 clkevt_periodic = true;
96 nmdk_clkevt_reset();
61 break; 97 break;
62 case CLOCK_EVT_MODE_ONESHOT: 98 case CLOCK_EVT_MODE_ONESHOT:
63 /* Load highest value, enable device, enable interrupts */ 99 clkevt_periodic = false;
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));
67 writel(1 << 1, mtu_base + MTU_IMSC);
68 break; 100 break;
69 case CLOCK_EVT_MODE_SHUTDOWN: 101 case CLOCK_EVT_MODE_SHUTDOWN:
70 case CLOCK_EVT_MODE_UNUSED: 102 case CLOCK_EVT_MODE_UNUSED:
71 /* disable irq */
72 writel(0, mtu_base + MTU_IMSC); 103 writel(0, mtu_base + MTU_IMSC);
73 /* disable timer */ 104 /* disable timer */
74 cr = readl(mtu_base + MTU_CR(1)); 105 writel(0, mtu_base + MTU_CR(1));
75 cr &= ~MTU_CRn_ENA;
76 writel(cr, mtu_base + MTU_CR(1));
77 /* load some high default value */ 106 /* load some high default value */
78 writel(0xffffffff, mtu_base + MTU_LR(1)); 107 writel(0xffffffff, mtu_base + MTU_LR(1));
79 break; 108 break;
@@ -82,16 +111,9 @@ static void nmdk_clkevt_mode(enum clock_event_mode mode,
82 } 111 }
83} 112}
84 113
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
92static struct clock_event_device nmdk_clkevt = { 114static struct clock_event_device nmdk_clkevt = {
93 .name = "mtu_1", 115 .name = "mtu_1",
94 .features = CLOCK_EVT_FEAT_ONESHOT, 116 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
95 .rating = 200, 117 .rating = 200,
96 .set_mode = nmdk_clkevt_mode, 118 .set_mode = nmdk_clkevt_mode,
97 .set_next_event = nmdk_clkevt_next, 119 .set_next_event = nmdk_clkevt_next,
@@ -116,11 +138,23 @@ static struct irqaction nmdk_timer_irq = {
116 .dev_id = &nmdk_clkevt, 138 .dev_id = &nmdk_clkevt,
117}; 139};
118 140
141static void nmdk_clksrc_reset(void)
142{
143 /* Disable */
144 writel(0, mtu_base + MTU_CR(0));
145
146 /* ClockSource: configure load and background-load, and fire it up */
147 writel(nmdk_cycle, mtu_base + MTU_LR(0));
148 writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
149
150 writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
151 mtu_base + MTU_CR(0));
152}
153
119void __init nmdk_timer_init(void) 154void __init nmdk_timer_init(void)
120{ 155{
121 unsigned long rate; 156 unsigned long rate;
122 struct clk *clk0; 157 struct clk *clk0;
123 u32 cr = MTU_CRn_32BITS;
124 158
125 clk0 = clk_get_sys("mtu0", NULL); 159 clk0 = clk_get_sys("mtu0", NULL);
126 BUG_ON(IS_ERR(clk0)); 160 BUG_ON(IS_ERR(clk0));
@@ -138,16 +172,16 @@ void __init nmdk_timer_init(void)
138 rate = clk_get_rate(clk0); 172 rate = clk_get_rate(clk0);
139 if (rate > 32000000) { 173 if (rate > 32000000) {
140 rate /= 16; 174 rate /= 16;
141 cr |= MTU_CRn_PRESCALE_16; 175 clk_prescale = MTU_CRn_PRESCALE_16;
142 } else { 176 } else {
143 cr |= MTU_CRn_PRESCALE_1; 177 clk_prescale = MTU_CRn_PRESCALE_1;
144 } 178 }
145 179
180 nmdk_cycle = (rate + HZ/2) / HZ;
181
182
146 /* Timer 0 is the free running clocksource */ 183 /* Timer 0 is the free running clocksource */
147 writel(cr, mtu_base + MTU_CR(0)); 184 nmdk_clksrc_reset();
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));
151 185
152 if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0", 186 if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
153 rate, 200, 32, clocksource_mmio_readl_down)) 187 rate, 200, 32, clocksource_mmio_readl_down))
@@ -160,8 +194,6 @@ void __init nmdk_timer_init(void)
160 194
161 clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE); 195 clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE);
162 196
163 writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */
164
165 nmdk_clkevt.max_delta_ns = 197 nmdk_clkevt.max_delta_ns =
166 clockevent_delta2ns(0xffffffff, &nmdk_clkevt); 198 clockevent_delta2ns(0xffffffff, &nmdk_clkevt);
167 nmdk_clkevt.min_delta_ns = 199 nmdk_clkevt.min_delta_ns =