Blame view
drivers/clocksource/exynos_mct.c
15.7 KB
30d8bead5 ARM: EXYNOS4: Imp... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* linux/arch/arm/mach-exynos4/mct.c * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * * EXYNOS4 MCT(Multi-Core Timer) support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/clockchips.h> |
ee98d27df ARM: EXYNOS4: Div... |
19 |
#include <linux/cpu.h> |
30d8bead5 ARM: EXYNOS4: Imp... |
20 21 22 |
#include <linux/platform_device.h> #include <linux/delay.h> #include <linux/percpu.h> |
2edb36c4e ARM: EXYNOS: add ... |
23 |
#include <linux/of.h> |
36ba5d527 ARM: EXYNOS: add ... |
24 25 |
#include <linux/of_irq.h> #include <linux/of_address.h> |
9fbf0c85a ARM: EXYNOS: allo... |
26 |
#include <linux/clocksource.h> |
93bfb7697 clocksource: exyn... |
27 |
#include <linux/sched_clock.h> |
30d8bead5 ARM: EXYNOS4: Imp... |
28 |
|
a1ba7a7a9 ARM: EXYNOS: add ... |
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#define EXYNOS4_MCTREG(x) (x) #define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100) #define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104) #define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110) #define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200) #define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204) #define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208) #define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240) #define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244) #define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248) #define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C) #define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300) #define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x)) #define EXYNOS4_MCT_L_MASK (0xffffff00) #define MCT_L_TCNTB_OFFSET (0x00) #define MCT_L_ICNTB_OFFSET (0x08) #define MCT_L_TCON_OFFSET (0x20) #define MCT_L_INT_CSTAT_OFFSET (0x30) #define MCT_L_INT_ENB_OFFSET (0x34) #define MCT_L_WSTAT_OFFSET (0x40) #define MCT_G_TCON_START (1 << 8) #define MCT_G_TCON_COMP0_AUTO_INC (1 << 1) #define MCT_G_TCON_COMP0_ENABLE (1 << 0) #define MCT_L_TCON_INTERVAL_MODE (1 << 2) #define MCT_L_TCON_INT_START (1 << 1) #define MCT_L_TCON_TIMER_START (1 << 0) |
4d2e4d7f2 ARM: EXYNOS: fix ... |
56 |
#define TICK_BASE_CNT 1 |
3a0622811 ARM: EXYNOS4: Add... |
57 58 59 60 |
enum { MCT_INT_SPI, MCT_INT_PPI }; |
c371dc60a ARM: EXYNOS: prep... |
61 62 63 64 65 66 67 68 69 |
enum { MCT_G0_IRQ, MCT_G1_IRQ, MCT_G2_IRQ, MCT_G3_IRQ, MCT_L0_IRQ, MCT_L1_IRQ, MCT_L2_IRQ, MCT_L3_IRQ, |
6c16dedfd clocksource: mct:... |
70 71 72 73 |
MCT_L4_IRQ, MCT_L5_IRQ, MCT_L6_IRQ, MCT_L7_IRQ, |
c371dc60a ARM: EXYNOS: prep... |
74 75 |
MCT_NR_IRQS, }; |
a1ba7a7a9 ARM: EXYNOS: add ... |
76 |
static void __iomem *reg_base; |
30d8bead5 ARM: EXYNOS4: Imp... |
77 |
static unsigned long clk_rate; |
3a0622811 ARM: EXYNOS4: Add... |
78 |
static unsigned int mct_int_type; |
c371dc60a ARM: EXYNOS: prep... |
79 |
static int mct_irqs[MCT_NR_IRQS]; |
30d8bead5 ARM: EXYNOS4: Imp... |
80 81 |
struct mct_clock_event_device { |
ee98d27df ARM: EXYNOS4: Div... |
82 |
struct clock_event_device evt; |
a1ba7a7a9 ARM: EXYNOS: add ... |
83 |
unsigned long base; |
c8987470a ARM: EXYNOS4: Add... |
84 |
char name[10]; |
30d8bead5 ARM: EXYNOS4: Imp... |
85 |
}; |
a1ba7a7a9 ARM: EXYNOS: add ... |
86 |
static void exynos4_mct_write(unsigned int value, unsigned long offset) |
30d8bead5 ARM: EXYNOS4: Imp... |
87 |
{ |
a1ba7a7a9 ARM: EXYNOS: add ... |
88 |
unsigned long stat_addr; |
30d8bead5 ARM: EXYNOS4: Imp... |
89 90 |
u32 mask; u32 i; |
fdb06f66d clocksource: exyn... |
91 |
writel_relaxed(value, reg_base + offset); |
30d8bead5 ARM: EXYNOS4: Imp... |
92 |
|
a1ba7a7a9 ARM: EXYNOS: add ... |
93 |
if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) { |
8c38d28ba clocksource: exyn... |
94 95 |
stat_addr = (offset & EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; switch (offset & ~EXYNOS4_MCT_L_MASK) { |
a1ba7a7a9 ARM: EXYNOS: add ... |
96 |
case MCT_L_TCON_OFFSET: |
c8987470a ARM: EXYNOS4: Add... |
97 98 |
mask = 1 << 3; /* L_TCON write status */ break; |
a1ba7a7a9 ARM: EXYNOS: add ... |
99 |
case MCT_L_ICNTB_OFFSET: |
c8987470a ARM: EXYNOS4: Add... |
100 101 |
mask = 1 << 1; /* L_ICNTB write status */ break; |
a1ba7a7a9 ARM: EXYNOS: add ... |
102 |
case MCT_L_TCNTB_OFFSET: |
c8987470a ARM: EXYNOS4: Add... |
103 104 105 106 107 108 |
mask = 1 << 0; /* L_TCNTB write status */ break; default: return; } } else { |
a1ba7a7a9 ARM: EXYNOS: add ... |
109 110 |
switch (offset) { case EXYNOS4_MCT_G_TCON: |
c8987470a ARM: EXYNOS4: Add... |
111 112 113 |
stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 16; /* G_TCON write status */ break; |
a1ba7a7a9 ARM: EXYNOS: add ... |
114 |
case EXYNOS4_MCT_G_COMP0_L: |
c8987470a ARM: EXYNOS4: Add... |
115 116 117 |
stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 0; /* G_COMP0_L write status */ break; |
a1ba7a7a9 ARM: EXYNOS: add ... |
118 |
case EXYNOS4_MCT_G_COMP0_U: |
c8987470a ARM: EXYNOS4: Add... |
119 120 121 |
stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 1; /* G_COMP0_U write status */ break; |
a1ba7a7a9 ARM: EXYNOS: add ... |
122 |
case EXYNOS4_MCT_G_COMP0_ADD_INCR: |
c8987470a ARM: EXYNOS4: Add... |
123 124 125 |
stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 2; /* G_COMP0_ADD_INCR w status */ break; |
a1ba7a7a9 ARM: EXYNOS: add ... |
126 |
case EXYNOS4_MCT_G_CNT_L: |
c8987470a ARM: EXYNOS4: Add... |
127 128 129 |
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; mask = 1 << 0; /* G_CNT_L write status */ break; |
a1ba7a7a9 ARM: EXYNOS: add ... |
130 |
case EXYNOS4_MCT_G_CNT_U: |
c8987470a ARM: EXYNOS4: Add... |
131 132 133 134 135 136 |
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; mask = 1 << 1; /* G_CNT_U write status */ break; default: return; } |
30d8bead5 ARM: EXYNOS4: Imp... |
137 138 139 140 |
} /* Wait maximum 1 ms until written values are applied */ for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++) |
fdb06f66d clocksource: exyn... |
141 142 |
if (readl_relaxed(reg_base + stat_addr) & mask) { writel_relaxed(mask, reg_base + stat_addr); |
30d8bead5 ARM: EXYNOS4: Imp... |
143 144 |
return; } |
a1ba7a7a9 ARM: EXYNOS: add ... |
145 146 |
panic("MCT hangs after writing %d (offset:0x%lx) ", value, offset); |
30d8bead5 ARM: EXYNOS4: Imp... |
147 148 149 |
} /* Clocksource handling */ |
1d80415db clocksource: exyn... |
150 |
static void exynos4_mct_frc_start(void) |
30d8bead5 ARM: EXYNOS4: Imp... |
151 152 |
{ u32 reg; |
fdb06f66d clocksource: exyn... |
153 |
reg = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); |
30d8bead5 ARM: EXYNOS4: Imp... |
154 155 156 |
reg |= MCT_G_TCON_START; exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON); } |
3252a646a clocksource: exyn... |
157 158 159 160 161 162 163 164 165 166 167 |
/** * exynos4_read_count_64 - Read all 64-bits of the global counter * * This will read all 64-bits of the global counter taking care to make sure * that the upper and lower half match. Note that reading the MCT can be quite * slow (hundreds of nanoseconds) so you should use the 32-bit (lower half * only) version when possible. * * Returns the number of cycles in the global counter. */ static u64 exynos4_read_count_64(void) |
30d8bead5 ARM: EXYNOS4: Imp... |
168 169 |
{ unsigned int lo, hi; |
fdb06f66d clocksource: exyn... |
170 |
u32 hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U); |
30d8bead5 ARM: EXYNOS4: Imp... |
171 172 173 |
do { hi = hi2; |
fdb06f66d clocksource: exyn... |
174 175 |
lo = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L); hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U); |
30d8bead5 ARM: EXYNOS4: Imp... |
176 177 178 179 |
} while (hi != hi2); return ((cycle_t)hi << 32) | lo; } |
3252a646a clocksource: exyn... |
180 181 182 183 184 185 186 187 188 189 190 191 |
/** * exynos4_read_count_32 - Read the lower 32-bits of the global counter * * This will read just the lower 32-bits of the global counter. This is marked * as notrace so it can be used by the scheduler clock. * * Returns the number of cycles in the global counter (lower 32 bits). */ static u32 notrace exynos4_read_count_32(void) { return readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L); } |
89e6a13b8 clocksource: exyn... |
192 193 |
static cycle_t exynos4_frc_read(struct clocksource *cs) { |
3252a646a clocksource: exyn... |
194 |
return exynos4_read_count_32(); |
89e6a13b8 clocksource: exyn... |
195 |
} |
aa421c13a ARM: EXYNOS4: res... |
196 197 |
static void exynos4_frc_resume(struct clocksource *cs) { |
1d80415db clocksource: exyn... |
198 |
exynos4_mct_frc_start(); |
aa421c13a ARM: EXYNOS4: res... |
199 |
} |
6c10bf637 clocksource/drive... |
200 |
static struct clocksource mct_frc = { |
30d8bead5 ARM: EXYNOS4: Imp... |
201 202 203 |
.name = "mct-frc", .rating = 400, .read = exynos4_frc_read, |
3252a646a clocksource: exyn... |
204 |
.mask = CLOCKSOURCE_MASK(32), |
30d8bead5 ARM: EXYNOS4: Imp... |
205 |
.flags = CLOCK_SOURCE_IS_CONTINUOUS, |
aa421c13a ARM: EXYNOS4: res... |
206 |
.resume = exynos4_frc_resume, |
30d8bead5 ARM: EXYNOS4: Imp... |
207 |
}; |
93bfb7697 clocksource: exyn... |
208 209 |
static u64 notrace exynos4_read_sched_clock(void) { |
3252a646a clocksource: exyn... |
210 |
return exynos4_read_count_32(); |
93bfb7697 clocksource: exyn... |
211 |
} |
f1a4c1f33 clocksource: exyn... |
212 |
#if defined(CONFIG_ARM) |
8bf13a434 clocksource: exyn... |
213 214 215 216 |
static struct delay_timer exynos4_delay_timer; static cycles_t exynos4_read_current_timer(void) { |
3252a646a clocksource: exyn... |
217 218 219 |
BUILD_BUG_ON_MSG(sizeof(cycles_t) != sizeof(u32), "cycles_t needs to move to 32-bit for ARM64 usage"); return exynos4_read_count_32(); |
8bf13a434 clocksource: exyn... |
220 |
} |
f1a4c1f33 clocksource: exyn... |
221 |
#endif |
8bf13a434 clocksource: exyn... |
222 |
|
5e558ebd3 clocksource/drive... |
223 |
static int __init exynos4_clocksource_init(void) |
30d8bead5 ARM: EXYNOS4: Imp... |
224 |
{ |
1d80415db clocksource: exyn... |
225 |
exynos4_mct_frc_start(); |
30d8bead5 ARM: EXYNOS4: Imp... |
226 |
|
f1a4c1f33 clocksource: exyn... |
227 |
#if defined(CONFIG_ARM) |
8bf13a434 clocksource: exyn... |
228 229 230 |
exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer; exynos4_delay_timer.freq = clk_rate; register_current_timer_delay(&exynos4_delay_timer); |
f1a4c1f33 clocksource: exyn... |
231 |
#endif |
8bf13a434 clocksource: exyn... |
232 |
|
30d8bead5 ARM: EXYNOS4: Imp... |
233 234 235 |
if (clocksource_register_hz(&mct_frc, clk_rate)) panic("%s: can't register clocksource ", mct_frc.name); |
93bfb7697 clocksource: exyn... |
236 |
|
3252a646a clocksource: exyn... |
237 |
sched_clock_register(exynos4_read_sched_clock, 32, clk_rate); |
5e558ebd3 clocksource/drive... |
238 239 |
return 0; |
30d8bead5 ARM: EXYNOS4: Imp... |
240 241 242 243 244 |
} static void exynos4_mct_comp0_stop(void) { unsigned int tcon; |
fdb06f66d clocksource: exyn... |
245 |
tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); |
30d8bead5 ARM: EXYNOS4: Imp... |
246 247 248 249 250 |
tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC); exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON); exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB); } |
79e436d3a clockevents/drive... |
251 |
static void exynos4_mct_comp0_start(bool periodic, unsigned long cycles) |
30d8bead5 ARM: EXYNOS4: Imp... |
252 253 254 |
{ unsigned int tcon; cycle_t comp_cycle; |
fdb06f66d clocksource: exyn... |
255 |
tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); |
30d8bead5 ARM: EXYNOS4: Imp... |
256 |
|
79e436d3a clockevents/drive... |
257 |
if (periodic) { |
30d8bead5 ARM: EXYNOS4: Imp... |
258 259 260 |
tcon |= MCT_G_TCON_COMP0_AUTO_INC; exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR); } |
3252a646a clocksource: exyn... |
261 |
comp_cycle = exynos4_read_count_64() + cycles; |
30d8bead5 ARM: EXYNOS4: Imp... |
262 263 264 265 266 267 268 269 270 271 272 273 |
exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L); exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U); exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB); tcon |= MCT_G_TCON_COMP0_ENABLE; exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON); } static int exynos4_comp_set_next_event(unsigned long cycles, struct clock_event_device *evt) { |
79e436d3a clockevents/drive... |
274 |
exynos4_mct_comp0_start(false, cycles); |
30d8bead5 ARM: EXYNOS4: Imp... |
275 276 277 |
return 0; } |
79e436d3a clockevents/drive... |
278 |
static int mct_set_state_shutdown(struct clock_event_device *evt) |
30d8bead5 ARM: EXYNOS4: Imp... |
279 280 |
{ exynos4_mct_comp0_stop(); |
79e436d3a clockevents/drive... |
281 282 |
return 0; } |
30d8bead5 ARM: EXYNOS4: Imp... |
283 |
|
79e436d3a clockevents/drive... |
284 285 286 287 288 289 290 291 292 |
static int mct_set_state_periodic(struct clock_event_device *evt) { unsigned long cycles_per_jiffy; cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); exynos4_mct_comp0_stop(); exynos4_mct_comp0_start(true, cycles_per_jiffy); return 0; |
30d8bead5 ARM: EXYNOS4: Imp... |
293 294 295 |
} static struct clock_event_device mct_comp_device = { |
79e436d3a clockevents/drive... |
296 297 298 299 300 301 302 303 |
.name = "mct-comp", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .rating = 250, .set_next_event = exynos4_comp_set_next_event, .set_state_periodic = mct_set_state_periodic, .set_state_shutdown = mct_set_state_shutdown, .set_state_oneshot = mct_set_state_shutdown, |
07f101d33 clockevents/drive... |
304 |
.set_state_oneshot_stopped = mct_set_state_shutdown, |
79e436d3a clockevents/drive... |
305 |
.tick_resume = mct_set_state_shutdown, |
30d8bead5 ARM: EXYNOS4: Imp... |
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
}; static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id) { struct clock_event_device *evt = dev_id; exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT); evt->event_handler(evt); return IRQ_HANDLED; } static struct irqaction mct_comp_event_irq = { .name = "mct_comp_irq", .flags = IRQF_TIMER | IRQF_IRQPOLL, .handler = exynos4_mct_comp_isr, .dev_id = &mct_comp_device, }; |
5e558ebd3 clocksource/drive... |
325 |
static int exynos4_clockevent_init(void) |
30d8bead5 ARM: EXYNOS4: Imp... |
326 |
{ |
30d8bead5 ARM: EXYNOS4: Imp... |
327 |
mct_comp_device.cpumask = cpumask_of(0); |
838a2ae80 ARM: use clockeve... |
328 329 |
clockevents_config_and_register(&mct_comp_device, clk_rate, 0xf, 0xffffffff); |
c371dc60a ARM: EXYNOS: prep... |
330 |
setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq); |
5e558ebd3 clocksource/drive... |
331 332 |
return 0; |
30d8bead5 ARM: EXYNOS4: Imp... |
333 |
} |
991a6c7d9 ARM: EXYNOS: Fix ... |
334 |
static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick); |
30d8bead5 ARM: EXYNOS4: Imp... |
335 336 337 338 339 |
/* Clock event handling */ static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt) { unsigned long tmp; unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START; |
a1ba7a7a9 ARM: EXYNOS: add ... |
340 |
unsigned long offset = mevt->base + MCT_L_TCON_OFFSET; |
30d8bead5 ARM: EXYNOS4: Imp... |
341 |
|
fdb06f66d clocksource: exyn... |
342 |
tmp = readl_relaxed(reg_base + offset); |
30d8bead5 ARM: EXYNOS4: Imp... |
343 344 |
if (tmp & mask) { tmp &= ~mask; |
a1ba7a7a9 ARM: EXYNOS: add ... |
345 |
exynos4_mct_write(tmp, offset); |
30d8bead5 ARM: EXYNOS4: Imp... |
346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
} } static void exynos4_mct_tick_start(unsigned long cycles, struct mct_clock_event_device *mevt) { unsigned long tmp; exynos4_mct_tick_stop(mevt); tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */ /* update interrupt count buffer */ exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET); |
25985edce Fix common misspe... |
360 |
/* enable MCT tick interrupt */ |
30d8bead5 ARM: EXYNOS4: Imp... |
361 |
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET); |
fdb06f66d clocksource: exyn... |
362 |
tmp = readl_relaxed(reg_base + mevt->base + MCT_L_TCON_OFFSET); |
30d8bead5 ARM: EXYNOS4: Imp... |
363 364 365 366 367 368 369 370 |
tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START | MCT_L_TCON_INTERVAL_MODE; exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET); } static int exynos4_tick_set_next_event(unsigned long cycles, struct clock_event_device *evt) { |
31f798740 clocksource/drive... |
371 |
struct mct_clock_event_device *mevt; |
30d8bead5 ARM: EXYNOS4: Imp... |
372 |
|
31f798740 clocksource/drive... |
373 |
mevt = container_of(evt, struct mct_clock_event_device, evt); |
30d8bead5 ARM: EXYNOS4: Imp... |
374 |
exynos4_mct_tick_start(cycles, mevt); |
30d8bead5 ARM: EXYNOS4: Imp... |
375 376 |
return 0; } |
79e436d3a clockevents/drive... |
377 378 |
static int set_state_shutdown(struct clock_event_device *evt) { |
31f798740 clocksource/drive... |
379 380 381 382 |
struct mct_clock_event_device *mevt; mevt = container_of(evt, struct mct_clock_event_device, evt); exynos4_mct_tick_stop(mevt); |
79e436d3a clockevents/drive... |
383 384 385 386 |
return 0; } static int set_state_periodic(struct clock_event_device *evt) |
30d8bead5 ARM: EXYNOS4: Imp... |
387 |
{ |
31f798740 clocksource/drive... |
388 |
struct mct_clock_event_device *mevt; |
4d2e4d7f2 ARM: EXYNOS: fix ... |
389 |
unsigned long cycles_per_jiffy; |
30d8bead5 ARM: EXYNOS4: Imp... |
390 |
|
31f798740 clocksource/drive... |
391 |
mevt = container_of(evt, struct mct_clock_event_device, evt); |
79e436d3a clockevents/drive... |
392 393 |
cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); |
30d8bead5 ARM: EXYNOS4: Imp... |
394 |
exynos4_mct_tick_stop(mevt); |
79e436d3a clockevents/drive... |
395 396 |
exynos4_mct_tick_start(cycles_per_jiffy, mevt); return 0; |
30d8bead5 ARM: EXYNOS4: Imp... |
397 |
} |
37285674f clocksource/drive... |
398 |
static void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) |
30d8bead5 ARM: EXYNOS4: Imp... |
399 |
{ |
30d8bead5 ARM: EXYNOS4: Imp... |
400 401 402 403 404 |
/* * This is for supporting oneshot mode. * Mct would generate interrupt periodically * without explicit stopping. */ |
79e436d3a clockevents/drive... |
405 |
if (!clockevent_state_periodic(&mevt->evt)) |
30d8bead5 ARM: EXYNOS4: Imp... |
406 407 408 |
exynos4_mct_tick_stop(mevt); /* Clear the MCT tick interrupt */ |
37285674f clocksource/drive... |
409 |
if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) |
3a0622811 ARM: EXYNOS4: Add... |
410 |
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); |
3a0622811 ARM: EXYNOS4: Add... |
411 412 413 414 415 |
} static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) { struct mct_clock_event_device *mevt = dev_id; |
ee98d27df ARM: EXYNOS4: Div... |
416 |
struct clock_event_device *evt = &mevt->evt; |
3a0622811 ARM: EXYNOS4: Add... |
417 418 |
exynos4_mct_tick_clear(mevt); |
30d8bead5 ARM: EXYNOS4: Imp... |
419 420 421 422 423 |
evt->event_handler(evt); return IRQ_HANDLED; } |
d11b3a60f clocksource/exyno... |
424 |
static int exynos4_mct_starting_cpu(unsigned int cpu) |
30d8bead5 ARM: EXYNOS4: Imp... |
425 |
{ |
d11b3a60f clocksource/exyno... |
426 427 |
struct mct_clock_event_device *mevt = per_cpu_ptr(&percpu_mct_tick, cpu); |
479a93298 clockevents/drive... |
428 |
struct clock_event_device *evt = &mevt->evt; |
30d8bead5 ARM: EXYNOS4: Imp... |
429 |
|
e700e41d9 ARM: EXYNOS4: con... |
430 |
mevt->base = EXYNOS4_MCT_L_BASE(cpu); |
09e15176d clocksource: exyn... |
431 |
snprintf(mevt->name, sizeof(mevt->name), "mct_tick%d", cpu); |
30d8bead5 ARM: EXYNOS4: Imp... |
432 |
|
e700e41d9 ARM: EXYNOS4: con... |
433 |
evt->name = mevt->name; |
30d8bead5 ARM: EXYNOS4: Imp... |
434 435 |
evt->cpumask = cpumask_of(cpu); evt->set_next_event = exynos4_tick_set_next_event; |
79e436d3a clockevents/drive... |
436 437 438 |
evt->set_state_periodic = set_state_periodic; evt->set_state_shutdown = set_state_shutdown; evt->set_state_oneshot = set_state_shutdown; |
07f101d33 clockevents/drive... |
439 |
evt->set_state_oneshot_stopped = set_state_shutdown; |
79e436d3a clockevents/drive... |
440 |
evt->tick_resume = set_state_shutdown; |
30d8bead5 ARM: EXYNOS4: Imp... |
441 442 |
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; evt->rating = 450; |
30d8bead5 ARM: EXYNOS4: Imp... |
443 |
|
4d2e4d7f2 ARM: EXYNOS: fix ... |
444 |
exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); |
30d8bead5 ARM: EXYNOS4: Imp... |
445 |
|
3a0622811 ARM: EXYNOS4: Add... |
446 |
if (mct_int_type == MCT_INT_SPI) { |
56a94f139 clocksource: exyn... |
447 448 |
if (evt->irq == -1) |
7114cd749 clocksource: exyn... |
449 |
return -EIO; |
56a94f139 clocksource: exyn... |
450 451 452 |
irq_force_affinity(evt->irq, cpumask_of(cpu)); enable_irq(evt->irq); |
30d8bead5 ARM: EXYNOS4: Imp... |
453 |
} else { |
c371dc60a ARM: EXYNOS: prep... |
454 |
enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0); |
30d8bead5 ARM: EXYNOS4: Imp... |
455 |
} |
8db6e5104 clocksource: Exyn... |
456 457 |
clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1), 0xf, 0x7fffffff); |
4d487d7ed ARM: EXYNOS4: Fix... |
458 459 |
return 0; |
30d8bead5 ARM: EXYNOS4: Imp... |
460 |
} |
d11b3a60f clocksource/exyno... |
461 |
static int exynos4_mct_dying_cpu(unsigned int cpu) |
30d8bead5 ARM: EXYNOS4: Imp... |
462 |
{ |
d11b3a60f clocksource/exyno... |
463 464 |
struct mct_clock_event_device *mevt = per_cpu_ptr(&percpu_mct_tick, cpu); |
479a93298 clockevents/drive... |
465 |
struct clock_event_device *evt = &mevt->evt; |
79e436d3a clockevents/drive... |
466 |
evt->set_state_shutdown(evt); |
56a94f139 clocksource: exyn... |
467 468 469 |
if (mct_int_type == MCT_INT_SPI) { if (evt->irq != -1) disable_irq_nosync(evt->irq); |
c49b31b58 clocksource/exyno... |
470 |
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); |
56a94f139 clocksource: exyn... |
471 |
} else { |
c371dc60a ARM: EXYNOS: prep... |
472 |
disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); |
56a94f139 clocksource: exyn... |
473 |
} |
d11b3a60f clocksource/exyno... |
474 |
return 0; |
30d8bead5 ARM: EXYNOS4: Imp... |
475 |
} |
a8cb6041d ARM: local timers... |
476 |
|
5e558ebd3 clocksource/drive... |
477 |
static int __init exynos4_timer_resources(struct device_node *np, void __iomem *base) |
30d8bead5 ARM: EXYNOS4: Imp... |
478 |
{ |
56a94f139 clocksource: exyn... |
479 |
int err, cpu; |
ca9048ec3 clocksource: mct:... |
480 |
struct clk *mct_clk, *tick_clk; |
30d8bead5 ARM: EXYNOS4: Imp... |
481 |
|
415ac2e24 clocksource: mct:... |
482 483 484 485 486 487 |
tick_clk = np ? of_clk_get_by_name(np, "fin_pll") : clk_get(NULL, "fin_pll"); if (IS_ERR(tick_clk)) panic("%s: unable to determine tick clock rate ", __func__); clk_rate = clk_get_rate(tick_clk); |
e700e41d9 ARM: EXYNOS4: con... |
488 |
|
ca9048ec3 clocksource: mct:... |
489 490 491 492 493 |
mct_clk = np ? of_clk_get_by_name(np, "mct") : clk_get(NULL, "mct"); if (IS_ERR(mct_clk)) panic("%s: unable to retrieve mct clock instance ", __func__); clk_prepare_enable(mct_clk); |
e700e41d9 ARM: EXYNOS4: con... |
494 |
|
228e3023e Merge tag 'mct-ex... |
495 |
reg_base = base; |
36ba5d527 ARM: EXYNOS: add ... |
496 497 498 |
if (!reg_base) panic("%s: unable to ioremap mct address space ", __func__); |
a1ba7a7a9 ARM: EXYNOS: add ... |
499 |
|
e700e41d9 ARM: EXYNOS4: con... |
500 |
if (mct_int_type == MCT_INT_PPI) { |
e700e41d9 ARM: EXYNOS4: con... |
501 |
|
c371dc60a ARM: EXYNOS: prep... |
502 |
err = request_percpu_irq(mct_irqs[MCT_L0_IRQ], |
e700e41d9 ARM: EXYNOS4: con... |
503 504 505 506 |
exynos4_mct_tick_isr, "MCT", &percpu_mct_tick); WARN(err, "MCT: can't request IRQ %d (%d) ", |
c371dc60a ARM: EXYNOS: prep... |
507 |
mct_irqs[MCT_L0_IRQ], err); |
5df718d84 clocksource: exyn... |
508 |
} else { |
56a94f139 clocksource: exyn... |
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 |
for_each_possible_cpu(cpu) { int mct_irq = mct_irqs[MCT_L0_IRQ + cpu]; struct mct_clock_event_device *pcpu_mevt = per_cpu_ptr(&percpu_mct_tick, cpu); pcpu_mevt->evt.irq = -1; irq_set_status_flags(mct_irq, IRQ_NOAUTOEN); if (request_irq(mct_irq, exynos4_mct_tick_isr, IRQF_TIMER | IRQF_NOBALANCING, pcpu_mevt->name, pcpu_mevt)) { pr_err("exynos-mct: cannot register IRQ (cpu%d) ", cpu); continue; } pcpu_mevt->evt.irq = mct_irq; } |
e700e41d9 ARM: EXYNOS4: con... |
529 |
} |
a8cb6041d ARM: local timers... |
530 |
|
d11b3a60f clocksource/exyno... |
531 532 533 534 535 |
/* Install hotplug callbacks which configure the timer on this CPU */ err = cpuhp_setup_state(CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING, "AP_EXYNOS4_MCT_TIMER_STARTING", exynos4_mct_starting_cpu, exynos4_mct_dying_cpu); |
ee98d27df ARM: EXYNOS4: Div... |
536 537 |
if (err) goto out_irq; |
5e558ebd3 clocksource/drive... |
538 |
return 0; |
ee98d27df ARM: EXYNOS4: Div... |
539 540 541 |
out_irq: free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick); |
5e558ebd3 clocksource/drive... |
542 |
return err; |
30d8bead5 ARM: EXYNOS4: Imp... |
543 |
} |
5e558ebd3 clocksource/drive... |
544 |
static int __init mct_init_dt(struct device_node *np, unsigned int int_type) |
228e3023e Merge tag 'mct-ex... |
545 546 |
{ u32 nr_irqs, i; |
5e558ebd3 clocksource/drive... |
547 |
int ret; |
228e3023e Merge tag 'mct-ex... |
548 549 550 551 552 553 554 555 556 557 558 |
mct_int_type = int_type; /* This driver uses only one global timer interrupt */ mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ); /* * Find out the number of local irqs specified. The local * timer irqs are specified after the four global timer * irqs are specified. */ |
f4636d0ad clocksource: exyn... |
559 |
#ifdef CONFIG_OF |
228e3023e Merge tag 'mct-ex... |
560 |
nr_irqs = of_irq_count(np); |
f4636d0ad clocksource: exyn... |
561 562 563 |
#else nr_irqs = 0; #endif |
228e3023e Merge tag 'mct-ex... |
564 565 |
for (i = MCT_L0_IRQ; i < nr_irqs; i++) mct_irqs[i] = irq_of_parse_and_map(np, i); |
5e558ebd3 clocksource/drive... |
566 567 568 569 570 571 572 573 574 |
ret = exynos4_timer_resources(np, of_iomap(np, 0)); if (ret) return ret; ret = exynos4_clocksource_init(); if (ret) return ret; return exynos4_clockevent_init(); |
30d8bead5 ARM: EXYNOS4: Imp... |
575 |
} |
228e3023e Merge tag 'mct-ex... |
576 |
|
5e558ebd3 clocksource/drive... |
577 |
static int __init mct_init_spi(struct device_node *np) |
228e3023e Merge tag 'mct-ex... |
578 579 580 |
{ return mct_init_dt(np, MCT_INT_SPI); } |
5e558ebd3 clocksource/drive... |
581 |
static int __init mct_init_ppi(struct device_node *np) |
228e3023e Merge tag 'mct-ex... |
582 583 584 |
{ return mct_init_dt(np, MCT_INT_PPI); } |
177cf6e52 clocksources: Swi... |
585 586 |
CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi); CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi); |