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