diff options
Diffstat (limited to 'arch/arm/plat-nomadik')
-rw-r--r-- | arch/arm/plat-nomadik/Kconfig | 8 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/include/plat/mtu.h | 47 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/timer.c | 138 |
3 files changed, 116 insertions, 77 deletions
diff --git a/arch/arm/plat-nomadik/Kconfig b/arch/arm/plat-nomadik/Kconfig index ce659015535e..bca4914b4b9d 100644 --- a/arch/arm/plat-nomadik/Kconfig +++ b/arch/arm/plat-nomadik/Kconfig | |||
@@ -15,10 +15,16 @@ if PLAT_NOMADIK | |||
15 | 15 | ||
16 | config HAS_MTU | 16 | config HAS_MTU |
17 | bool | 17 | bool |
18 | select HAVE_SCHED_CLOCK | ||
19 | help | 18 | help |
20 | Support for Multi Timer Unit. MTU provides access | 19 | Support for Multi Timer Unit. MTU provides access |
21 | to multiple interrupt generating programmable | 20 | to multiple interrupt generating programmable |
22 | 32-bit free running decrementing counters. | 21 | 32-bit free running decrementing counters. |
23 | 22 | ||
23 | config NOMADIK_MTU_SCHED_CLOCK | ||
24 | bool | ||
25 | depends on HAS_MTU | ||
26 | select HAVE_SCHED_CLOCK | ||
27 | help | ||
28 | Use the Multi Timer Unit as the sched_clock. | ||
29 | |||
24 | endif | 30 | endif |
diff --git a/arch/arm/plat-nomadik/include/plat/mtu.h b/arch/arm/plat-nomadik/include/plat/mtu.h index 65704a3d4241..6508e7694a4b 100644 --- a/arch/arm/plat-nomadik/include/plat/mtu.h +++ b/arch/arm/plat-nomadik/include/plat/mtu.h | |||
@@ -1,54 +1,11 @@ | |||
1 | #ifndef __PLAT_MTU_H | 1 | #ifndef __PLAT_MTU_H |
2 | #define __PLAT_MTU_H | 2 | #define __PLAT_MTU_H |
3 | 3 | ||
4 | /* | ||
5 | * Guaranteed runtime conversion range in seconds for | ||
6 | * the clocksource and clockevent. | ||
7 | */ | ||
8 | #define MTU_MIN_RANGE 4 | ||
9 | |||
10 | /* should be set by the platform code */ | 4 | /* should be set by the platform code */ |
11 | extern void __iomem *mtu_base; | 5 | extern void __iomem *mtu_base; |
12 | 6 | ||
13 | /* | 7 | void nmdk_clkevt_reset(void); |
14 | * The MTU device hosts four different counters, with 4 set of | 8 | void nmdk_clksrc_reset(void); |
15 | * registers. These are register names. | ||
16 | */ | ||
17 | |||
18 | #define MTU_IMSC 0x00 /* Interrupt mask set/clear */ | ||
19 | #define MTU_RIS 0x04 /* Raw interrupt status */ | ||
20 | #define MTU_MIS 0x08 /* Masked interrupt status */ | ||
21 | #define MTU_ICR 0x0C /* Interrupt clear register */ | ||
22 | |||
23 | /* per-timer registers take 0..3 as argument */ | ||
24 | #define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */ | ||
25 | #define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */ | ||
26 | #define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */ | ||
27 | #define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */ | ||
28 | |||
29 | /* bits for the control register */ | ||
30 | #define MTU_CRn_ENA 0x80 | ||
31 | #define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */ | ||
32 | #define MTU_CRn_PRESCALE_MASK 0x0c | ||
33 | #define MTU_CRn_PRESCALE_1 0x00 | ||
34 | #define MTU_CRn_PRESCALE_16 0x04 | ||
35 | #define MTU_CRn_PRESCALE_256 0x08 | ||
36 | #define MTU_CRn_32BITS 0x02 | ||
37 | #define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/ | ||
38 | |||
39 | /* Other registers are usual amba/primecell registers, currently not used */ | ||
40 | #define MTU_ITCR 0xff0 | ||
41 | #define MTU_ITOP 0xff4 | ||
42 | |||
43 | #define MTU_PERIPH_ID0 0xfe0 | ||
44 | #define MTU_PERIPH_ID1 0xfe4 | ||
45 | #define MTU_PERIPH_ID2 0xfe8 | ||
46 | #define MTU_PERIPH_ID3 0xfeC | ||
47 | |||
48 | #define MTU_PCELL0 0xff0 | ||
49 | #define MTU_PCELL1 0xff4 | ||
50 | #define MTU_PCELL2 0xff8 | ||
51 | #define MTU_PCELL3 0xffC | ||
52 | 9 | ||
53 | #endif /* __PLAT_MTU_H */ | 10 | #endif /* __PLAT_MTU_H */ |
54 | 11 | ||
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c index ef74e157a9d5..30b6433d910d 100644 --- a/arch/arm/plat-nomadik/timer.c +++ b/arch/arm/plat-nomadik/timer.c | |||
@@ -21,10 +21,59 @@ | |||
21 | #include <asm/mach/time.h> | 21 | #include <asm/mach/time.h> |
22 | #include <asm/sched_clock.h> | 22 | #include <asm/sched_clock.h> |
23 | 23 | ||
24 | #include <plat/mtu.h> | 24 | /* |
25 | * Guaranteed runtime conversion range in seconds for | ||
26 | * the clocksource and clockevent. | ||
27 | */ | ||
28 | #define MTU_MIN_RANGE 4 | ||
29 | |||
30 | /* | ||
31 | * The MTU device hosts four different counters, with 4 set of | ||
32 | * registers. These are register names. | ||
33 | */ | ||
34 | |||
35 | #define MTU_IMSC 0x00 /* Interrupt mask set/clear */ | ||
36 | #define MTU_RIS 0x04 /* Raw interrupt status */ | ||
37 | #define MTU_MIS 0x08 /* Masked interrupt status */ | ||
38 | #define MTU_ICR 0x0C /* Interrupt clear register */ | ||
39 | |||
40 | /* per-timer registers take 0..3 as argument */ | ||
41 | #define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */ | ||
42 | #define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */ | ||
43 | #define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */ | ||
44 | #define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */ | ||
45 | |||
46 | /* bits for the control register */ | ||
47 | #define MTU_CRn_ENA 0x80 | ||
48 | #define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */ | ||
49 | #define MTU_CRn_PRESCALE_MASK 0x0c | ||
50 | #define MTU_CRn_PRESCALE_1 0x00 | ||
51 | #define MTU_CRn_PRESCALE_16 0x04 | ||
52 | #define MTU_CRn_PRESCALE_256 0x08 | ||
53 | #define MTU_CRn_32BITS 0x02 | ||
54 | #define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/ | ||
55 | |||
56 | /* Other registers are usual amba/primecell registers, currently not used */ | ||
57 | #define MTU_ITCR 0xff0 | ||
58 | #define MTU_ITOP 0xff4 | ||
59 | |||
60 | #define MTU_PERIPH_ID0 0xfe0 | ||
61 | #define MTU_PERIPH_ID1 0xfe4 | ||
62 | #define MTU_PERIPH_ID2 0xfe8 | ||
63 | #define MTU_PERIPH_ID3 0xfeC | ||
64 | |||
65 | #define MTU_PCELL0 0xff0 | ||
66 | #define MTU_PCELL1 0xff4 | ||
67 | #define MTU_PCELL2 0xff8 | ||
68 | #define MTU_PCELL3 0xffC | ||
69 | |||
70 | static bool clkevt_periodic; | ||
71 | static u32 clk_prescale; | ||
72 | static u32 nmdk_cycle; /* write-once */ | ||
25 | 73 | ||
26 | void __iomem *mtu_base; /* Assigned by machine code */ | 74 | void __iomem *mtu_base; /* Assigned by machine code */ |
27 | 75 | ||
76 | #ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK | ||
28 | /* | 77 | /* |
29 | * Override the global weak sched_clock symbol with this | 78 | * Override the global weak sched_clock symbol with this |
30 | * local implementation which uses the clocksource to get some | 79 | * local implementation which uses the clocksource to get some |
@@ -48,32 +97,56 @@ static void notrace nomadik_update_sched_clock(void) | |||
48 | u32 cyc = -readl(mtu_base + MTU_VAL(0)); | 97 | u32 cyc = -readl(mtu_base + MTU_VAL(0)); |
49 | update_sched_clock(&cd, cyc, (u32)~0); | 98 | update_sched_clock(&cd, cyc, (u32)~0); |
50 | } | 99 | } |
100 | #endif | ||
51 | 101 | ||
52 | /* Clockevent device: use one-shot mode */ | 102 | /* Clockevent device: use one-shot mode */ |
103 | static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev) | ||
104 | { | ||
105 | writel(1 << 1, mtu_base + MTU_IMSC); | ||
106 | writel(evt, mtu_base + MTU_LR(1)); | ||
107 | /* Load highest value, enable device, enable interrupts */ | ||
108 | writel(MTU_CRn_ONESHOT | clk_prescale | | ||
109 | MTU_CRn_32BITS | MTU_CRn_ENA, | ||
110 | mtu_base + MTU_CR(1)); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | void nmdk_clkevt_reset(void) | ||
116 | { | ||
117 | if (clkevt_periodic) { | ||
118 | |||
119 | /* Timer: configure load and background-load, and fire it up */ | ||
120 | writel(nmdk_cycle, mtu_base + MTU_LR(1)); | ||
121 | writel(nmdk_cycle, mtu_base + MTU_BGLR(1)); | ||
122 | |||
123 | writel(MTU_CRn_PERIODIC | clk_prescale | | ||
124 | MTU_CRn_32BITS | MTU_CRn_ENA, | ||
125 | mtu_base + MTU_CR(1)); | ||
126 | writel(1 << 1, mtu_base + MTU_IMSC); | ||
127 | } else { | ||
128 | /* Generate an interrupt to start the clockevent again */ | ||
129 | (void) nmdk_clkevt_next(nmdk_cycle, NULL); | ||
130 | } | ||
131 | } | ||
132 | |||
53 | static void nmdk_clkevt_mode(enum clock_event_mode mode, | 133 | static void nmdk_clkevt_mode(enum clock_event_mode mode, |
54 | struct clock_event_device *dev) | 134 | struct clock_event_device *dev) |
55 | { | 135 | { |
56 | u32 cr; | ||
57 | 136 | ||
58 | switch (mode) { | 137 | switch (mode) { |
59 | case CLOCK_EVT_MODE_PERIODIC: | 138 | case CLOCK_EVT_MODE_PERIODIC: |
60 | pr_err("%s: periodic mode not supported\n", __func__); | 139 | clkevt_periodic = true; |
140 | nmdk_clkevt_reset(); | ||
61 | break; | 141 | break; |
62 | case CLOCK_EVT_MODE_ONESHOT: | 142 | case CLOCK_EVT_MODE_ONESHOT: |
63 | /* Load highest value, enable device, enable interrupts */ | 143 | 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; | 144 | break; |
69 | case CLOCK_EVT_MODE_SHUTDOWN: | 145 | case CLOCK_EVT_MODE_SHUTDOWN: |
70 | case CLOCK_EVT_MODE_UNUSED: | 146 | case CLOCK_EVT_MODE_UNUSED: |
71 | /* disable irq */ | ||
72 | writel(0, mtu_base + MTU_IMSC); | 147 | writel(0, mtu_base + MTU_IMSC); |
73 | /* disable timer */ | 148 | /* disable timer */ |
74 | cr = readl(mtu_base + MTU_CR(1)); | 149 | 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 */ | 150 | /* load some high default value */ |
78 | writel(0xffffffff, mtu_base + MTU_LR(1)); | 151 | writel(0xffffffff, mtu_base + MTU_LR(1)); |
79 | break; | 152 | break; |
@@ -82,16 +155,9 @@ static void nmdk_clkevt_mode(enum clock_event_mode mode, | |||
82 | } | 155 | } |
83 | } | 156 | } |
84 | 157 | ||
85 | static 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 | |||
92 | static struct clock_event_device nmdk_clkevt = { | 158 | static struct clock_event_device nmdk_clkevt = { |
93 | .name = "mtu_1", | 159 | .name = "mtu_1", |
94 | .features = CLOCK_EVT_FEAT_ONESHOT, | 160 | .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, |
95 | .rating = 200, | 161 | .rating = 200, |
96 | .set_mode = nmdk_clkevt_mode, | 162 | .set_mode = nmdk_clkevt_mode, |
97 | .set_next_event = nmdk_clkevt_next, | 163 | .set_next_event = nmdk_clkevt_next, |
@@ -116,11 +182,23 @@ static struct irqaction nmdk_timer_irq = { | |||
116 | .dev_id = &nmdk_clkevt, | 182 | .dev_id = &nmdk_clkevt, |
117 | }; | 183 | }; |
118 | 184 | ||
185 | void nmdk_clksrc_reset(void) | ||
186 | { | ||
187 | /* Disable */ | ||
188 | writel(0, mtu_base + MTU_CR(0)); | ||
189 | |||
190 | /* ClockSource: configure load and background-load, and fire it up */ | ||
191 | writel(nmdk_cycle, mtu_base + MTU_LR(0)); | ||
192 | writel(nmdk_cycle, mtu_base + MTU_BGLR(0)); | ||
193 | |||
194 | writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA, | ||
195 | mtu_base + MTU_CR(0)); | ||
196 | } | ||
197 | |||
119 | void __init nmdk_timer_init(void) | 198 | void __init nmdk_timer_init(void) |
120 | { | 199 | { |
121 | unsigned long rate; | 200 | unsigned long rate; |
122 | struct clk *clk0; | 201 | struct clk *clk0; |
123 | u32 cr = MTU_CRn_32BITS; | ||
124 | 202 | ||
125 | clk0 = clk_get_sys("mtu0", NULL); | 203 | clk0 = clk_get_sys("mtu0", NULL); |
126 | BUG_ON(IS_ERR(clk0)); | 204 | BUG_ON(IS_ERR(clk0)); |
@@ -138,30 +216,28 @@ void __init nmdk_timer_init(void) | |||
138 | rate = clk_get_rate(clk0); | 216 | rate = clk_get_rate(clk0); |
139 | if (rate > 32000000) { | 217 | if (rate > 32000000) { |
140 | rate /= 16; | 218 | rate /= 16; |
141 | cr |= MTU_CRn_PRESCALE_16; | 219 | clk_prescale = MTU_CRn_PRESCALE_16; |
142 | } else { | 220 | } else { |
143 | cr |= MTU_CRn_PRESCALE_1; | 221 | clk_prescale = MTU_CRn_PRESCALE_1; |
144 | } | 222 | } |
145 | 223 | ||
224 | nmdk_cycle = (rate + HZ/2) / HZ; | ||
225 | |||
226 | |||
146 | /* Timer 0 is the free running clocksource */ | 227 | /* Timer 0 is the free running clocksource */ |
147 | writel(cr, mtu_base + MTU_CR(0)); | 228 | 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 | 229 | ||
152 | if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0", | 230 | if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0", |
153 | rate, 200, 32, clocksource_mmio_readl_down)) | 231 | rate, 200, 32, clocksource_mmio_readl_down)) |
154 | pr_err("timer: failed to initialize clock source %s\n", | 232 | pr_err("timer: failed to initialize clock source %s\n", |
155 | "mtu_0"); | 233 | "mtu_0"); |
156 | 234 | #ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK | |
157 | init_sched_clock(&cd, nomadik_update_sched_clock, 32, rate); | 235 | init_sched_clock(&cd, nomadik_update_sched_clock, 32, rate); |
158 | 236 | #endif | |
159 | /* Timer 1 is used for events */ | 237 | /* Timer 1 is used for events */ |
160 | 238 | ||
161 | clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE); | 239 | clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE); |
162 | 240 | ||
163 | writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */ | ||
164 | |||
165 | nmdk_clkevt.max_delta_ns = | 241 | nmdk_clkevt.max_delta_ns = |
166 | clockevent_delta2ns(0xffffffff, &nmdk_clkevt); | 242 | clockevent_delta2ns(0xffffffff, &nmdk_clkevt); |
167 | nmdk_clkevt.min_delta_ns = | 243 | nmdk_clkevt.min_delta_ns = |