Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
1
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
2edb36c
Documentation
arch
alpha
arm
boot
common
configs
crypto
include
kernel
lib
mach-at91
mach-bcm2835
mach-clps711x
mach-cns3xxx
mach-davinci
mach-dove
mach-ebsa110
mach-ep93xx
mach-exynos
include
Kconfig
Makefile
Makefile.boot
clock-exynos4.c
clock-exynos4.h
clock-exynos4210.c
clock-exynos4212.c
clock-exynos5.c
common.c
common.h
cpuidle.c
dev-ahci.c
dev-audio.c
dev-drm.c
dev-dwmci.c
dev-ohci.c
dev-sysmmu.c
dev-uart.c
dma.c
headsmp.S
hotplug.c
mach-armlex4210.c
mach-exynos4-dt.c
mach-exynos5-dt.c
mach-nuri.c
mach-origen.c
mach-smdk4x12.c
mach-smdkv310.c
mach-universal_c210.c
mct.c
platsmp.c
pm.c
pm_domains.c
pmu.c
setup-fimc.c
setup-fimd0.c
setup-i2c0.c
setup-i2c1.c
setup-i2c2.c
setup-i2c3.c
setup-i2c4.c
setup-i2c5.c
setup-i2c6.c
setup-i2c7.c
setup-keypad.c
setup-sdhci-gpio.c
setup-spi.c
setup-usb-phy.c
mach-footbridge
mach-gemini
mach-h720x
mach-highbank
mach-imx
mach-integrator
mach-iop13xx
mach-iop32x
mach-iop33x
mach-ixp4xx
mach-kirkwood
mach-ks8695
mach-l7200
mach-lpc32xx
mach-mmp
mach-msm
mach-mv78xx0
mach-mvebu
mach-mxs
mach-netx
mach-nomadik
mach-omap1
mach-omap2
mach-orion5x
mach-picoxcell
mach-prima2
mach-pxa
mach-realview
mach-rpc
mach-s3c2410
mach-s3c2412
mach-s3c2440
mach-s3c24xx
mach-s3c64xx
mach-s5p64x0
mach-s5pc100
mach-s5pv210
mach-sa1100
mach-shark
mach-shmobile
mach-socfpga
mach-spear13xx
mach-spear3xx
mach-spear6xx
mach-tegra
mach-u300
mach-ux500
mach-versatile
mach-vexpress
mach-vt8500
mach-w90x900
mach-zynq
mm
net
nwfpe
oprofile
plat-iop
plat-mxc
plat-nomadik
plat-omap
plat-orion
plat-pxa
plat-s3c24xx
plat-samsung
plat-spear
plat-versatile
tools
vfp
xen
Kconfig
Kconfig-nommu
Kconfig.debug
Makefile
arm64
avr32
blackfin
c6x
cris
frv
h8300
hexagon
ia64
m32r
m68k
microblaze
mips
mn10300
openrisc
parisc
powerpc
s390
score
sh
sparc
tile
um
unicore32
x86
xtensa
.gitignore
Kconfig
block
crypto
drivers
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
arch
/
arm
/
mach-exynos
/
mct.c
Copy path
Blame
Blame
Latest commit
Kukjin Kim
ARM: EXYNOS: add support for EXYNOS5440 SoC
Nov 22, 2012
2edb36c
·
Nov 22, 2012
History
History
500 lines (409 loc) · 12.2 KB
Breadcrumbs
linux
/
arch
/
arm
/
mach-exynos
/
mct.c
Top
File metadata and controls
Code
Blame
500 lines (409 loc) · 12.2 KB
Raw
/* 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> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/percpu.h> #include <linux/of.h> #include <asm/arch_timer.h> #include <asm/hardware/gic.h> #include <asm/localtimer.h> #include <plat/cpu.h> #include <mach/map.h> #include <mach/irqs.h> #include <mach/regs-mct.h> #include <asm/mach/time.h> #define TICK_BASE_CNT 1 enum { MCT_INT_SPI, MCT_INT_PPI }; static unsigned long clk_rate; static unsigned int mct_int_type; struct mct_clock_event_device { struct clock_event_device *evt; void __iomem *base; char name[10]; }; static void exynos4_mct_write(unsigned int value, void *addr) { void __iomem *stat_addr; u32 mask; u32 i; __raw_writel(value, addr); if (likely(addr >= EXYNOS4_MCT_L_BASE(0))) { u32 base = (u32) addr & EXYNOS4_MCT_L_MASK; switch ((u32) addr & ~EXYNOS4_MCT_L_MASK) { case (u32) MCT_L_TCON_OFFSET: stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET; mask = 1 << 3; /* L_TCON write status */ break; case (u32) MCT_L_ICNTB_OFFSET: stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET; mask = 1 << 1; /* L_ICNTB write status */ break; case (u32) MCT_L_TCNTB_OFFSET: stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET; mask = 1 << 0; /* L_TCNTB write status */ break; default: return; } } else { switch ((u32) addr) { case (u32) EXYNOS4_MCT_G_TCON: stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 16; /* G_TCON write status */ break; case (u32) EXYNOS4_MCT_G_COMP0_L: stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 0; /* G_COMP0_L write status */ break; case (u32) EXYNOS4_MCT_G_COMP0_U: stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 1; /* G_COMP0_U write status */ break; case (u32) EXYNOS4_MCT_G_COMP0_ADD_INCR: stat_addr = EXYNOS4_MCT_G_WSTAT; mask = 1 << 2; /* G_COMP0_ADD_INCR w status */ break; case (u32) EXYNOS4_MCT_G_CNT_L: stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; mask = 1 << 0; /* G_CNT_L write status */ break; case (u32) EXYNOS4_MCT_G_CNT_U: stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; mask = 1 << 1; /* G_CNT_U write status */ break; default: return; } } /* Wait maximum 1 ms until written values are applied */ for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++) if (__raw_readl(stat_addr) & mask) { __raw_writel(mask, stat_addr); return; } panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr); } /* Clocksource handling */ static void exynos4_mct_frc_start(u32 hi, u32 lo) { u32 reg; exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L); exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U); reg = __raw_readl(EXYNOS4_MCT_G_TCON); reg |= MCT_G_TCON_START; exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON); } static cycle_t exynos4_frc_read(struct clocksource *cs) { unsigned int lo, hi; u32 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U); do { hi = hi2; lo = __raw_readl(EXYNOS4_MCT_G_CNT_L); hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U); } while (hi != hi2); return ((cycle_t)hi << 32) | lo; } static void exynos4_frc_resume(struct clocksource *cs) { exynos4_mct_frc_start(0, 0); } struct clocksource mct_frc = { .name = "mct-frc", .rating = 400, .read = exynos4_frc_read, .mask = CLOCKSOURCE_MASK(64), .flags = CLOCK_SOURCE_IS_CONTINUOUS, .resume = exynos4_frc_resume, }; static void __init exynos4_clocksource_init(void) { exynos4_mct_frc_start(0, 0); if (clocksource_register_hz(&mct_frc, clk_rate)) panic("%s: can't register clocksource\n", mct_frc.name); } static void exynos4_mct_comp0_stop(void) { unsigned int tcon; tcon = __raw_readl(EXYNOS4_MCT_G_TCON); 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); } static void exynos4_mct_comp0_start(enum clock_event_mode mode, unsigned long cycles) { unsigned int tcon; cycle_t comp_cycle; tcon = __raw_readl(EXYNOS4_MCT_G_TCON); if (mode == CLOCK_EVT_MODE_PERIODIC) { tcon |= MCT_G_TCON_COMP0_AUTO_INC; exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR); } comp_cycle = exynos4_frc_read(&mct_frc) + cycles; 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) { exynos4_mct_comp0_start(evt->mode, cycles); return 0; } static void exynos4_comp_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { unsigned long cycles_per_jiffy; exynos4_mct_comp0_stop(); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: cycles_per_jiffy = (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); exynos4_mct_comp0_start(mode, cycles_per_jiffy); break; case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_RESUME: break; } } static struct clock_event_device mct_comp_device = { .name = "mct-comp", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .rating = 250, .set_next_event = exynos4_comp_set_next_event, .set_mode = exynos4_comp_set_mode, }; 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, }; static void exynos4_clockevent_init(void) { clockevents_calc_mult_shift(&mct_comp_device, clk_rate, 5); mct_comp_device.max_delta_ns = clockevent_delta2ns(0xffffffff, &mct_comp_device); mct_comp_device.min_delta_ns = clockevent_delta2ns(0xf, &mct_comp_device); mct_comp_device.cpumask = cpumask_of(0); clockevents_register_device(&mct_comp_device); if (soc_is_exynos5250()) setup_irq(EXYNOS5_IRQ_MCT_G0, &mct_comp_event_irq); else setup_irq(EXYNOS4_IRQ_MCT_G0, &mct_comp_event_irq); } #ifdef CONFIG_LOCAL_TIMERS static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick); /* 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; void __iomem *addr = mevt->base + MCT_L_TCON_OFFSET; tmp = __raw_readl(addr); if (tmp & mask) { tmp &= ~mask; exynos4_mct_write(tmp, addr); } } 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); /* enable MCT tick interrupt */ exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET); tmp = __raw_readl(mevt->base + MCT_L_TCON_OFFSET); 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) { struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); exynos4_mct_tick_start(cycles, mevt); return 0; } static inline void exynos4_tick_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick); unsigned long cycles_per_jiffy; exynos4_mct_tick_stop(mevt); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: cycles_per_jiffy = (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift); exynos4_mct_tick_start(cycles_per_jiffy, mevt); break; case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: case CLOCK_EVT_MODE_RESUME: break; } } static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) { struct clock_event_device *evt = mevt->evt; /* * This is for supporting oneshot mode. * Mct would generate interrupt periodically * without explicit stopping. */ if (evt->mode != CLOCK_EVT_MODE_PERIODIC) exynos4_mct_tick_stop(mevt); /* Clear the MCT tick interrupt */ if (__raw_readl(mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) { exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); return 1; } else { return 0; } } static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) { struct mct_clock_event_device *mevt = dev_id; struct clock_event_device *evt = mevt->evt; exynos4_mct_tick_clear(mevt); evt->event_handler(evt); return IRQ_HANDLED; } static struct irqaction mct_tick0_event_irq = { .name = "mct_tick0_irq", .flags = IRQF_TIMER | IRQF_NOBALANCING, .handler = exynos4_mct_tick_isr, }; static struct irqaction mct_tick1_event_irq = { .name = "mct_tick1_irq", .flags = IRQF_TIMER | IRQF_NOBALANCING, .handler = exynos4_mct_tick_isr, }; static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt) { struct mct_clock_event_device *mevt; unsigned int cpu = smp_processor_id(); int mct_lx_irq; mevt = this_cpu_ptr(&percpu_mct_tick); mevt->evt = evt; mevt->base = EXYNOS4_MCT_L_BASE(cpu); sprintf(mevt->name, "mct_tick%d", cpu); evt->name = mevt->name; evt->cpumask = cpumask_of(cpu); evt->set_next_event = exynos4_tick_set_next_event; evt->set_mode = exynos4_tick_set_mode; evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; evt->rating = 450; clockevents_calc_mult_shift(evt, clk_rate / (TICK_BASE_CNT + 1), 5); evt->max_delta_ns = clockevent_delta2ns(0x7fffffff, evt); evt->min_delta_ns = clockevent_delta2ns(0xf, evt); clockevents_register_device(evt); exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); if (mct_int_type == MCT_INT_SPI) { if (cpu == 0) { mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L0 : EXYNOS5_IRQ_MCT_L0; mct_tick0_event_irq.dev_id = mevt; evt->irq = mct_lx_irq; setup_irq(mct_lx_irq, &mct_tick0_event_irq); } else { mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L1 : EXYNOS5_IRQ_MCT_L1; mct_tick1_event_irq.dev_id = mevt; evt->irq = mct_lx_irq; setup_irq(mct_lx_irq, &mct_tick1_event_irq); irq_set_affinity(mct_lx_irq, cpumask_of(1)); } } else { enable_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER, 0); } return 0; } static void exynos4_local_timer_stop(struct clock_event_device *evt) { unsigned int cpu = smp_processor_id(); evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); if (mct_int_type == MCT_INT_SPI) if (cpu == 0) remove_irq(evt->irq, &mct_tick0_event_irq); else remove_irq(evt->irq, &mct_tick1_event_irq); else disable_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER); } static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = { .setup = exynos4_local_timer_setup, .stop = exynos4_local_timer_stop, }; #endif /* CONFIG_LOCAL_TIMERS */ static void __init exynos4_timer_resources(void) { struct clk *mct_clk; mct_clk = clk_get(NULL, "xtal"); clk_rate = clk_get_rate(mct_clk); #ifdef CONFIG_LOCAL_TIMERS if (mct_int_type == MCT_INT_PPI) { int err; err = request_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER, exynos4_mct_tick_isr, "MCT", &percpu_mct_tick); WARN(err, "MCT: can't request IRQ %d (%d)\n", EXYNOS_IRQ_MCT_LOCALTIMER, err); } local_timer_register(&exynos4_mct_tick_ops); #endif /* CONFIG_LOCAL_TIMERS */ } static void __init exynos_timer_init(void) { if (soc_is_exynos5440()) { arch_timer_of_register(); return; } if ((soc_is_exynos4210()) || (soc_is_exynos5250())) mct_int_type = MCT_INT_SPI; else mct_int_type = MCT_INT_PPI; exynos4_timer_resources(); exynos4_clocksource_init(); exynos4_clockevent_init(); } struct sys_timer exynos4_timer = { .init = exynos_timer_init, };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
You can’t perform that action at this time.