162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* linux/arch/arm/mach-exynos4/mct.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd.
562306a36Sopenharmony_ci *		http://www.samsung.com
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Exynos4 MCT(Multi-Core Timer) support
862306a36Sopenharmony_ci*/
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/irq.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/clk.h>
1462306a36Sopenharmony_ci#include <linux/clockchips.h>
1562306a36Sopenharmony_ci#include <linux/cpu.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/percpu.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/of_irq.h>
2062306a36Sopenharmony_ci#include <linux/of_address.h>
2162306a36Sopenharmony_ci#include <linux/clocksource.h>
2262306a36Sopenharmony_ci#include <linux/sched_clock.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define EXYNOS4_MCTREG(x)		(x)
2562306a36Sopenharmony_ci#define EXYNOS4_MCT_G_CNT_L		EXYNOS4_MCTREG(0x100)
2662306a36Sopenharmony_ci#define EXYNOS4_MCT_G_CNT_U		EXYNOS4_MCTREG(0x104)
2762306a36Sopenharmony_ci#define EXYNOS4_MCT_G_CNT_WSTAT		EXYNOS4_MCTREG(0x110)
2862306a36Sopenharmony_ci#define EXYNOS4_MCT_G_COMP0_L		EXYNOS4_MCTREG(0x200)
2962306a36Sopenharmony_ci#define EXYNOS4_MCT_G_COMP0_U		EXYNOS4_MCTREG(0x204)
3062306a36Sopenharmony_ci#define EXYNOS4_MCT_G_COMP0_ADD_INCR	EXYNOS4_MCTREG(0x208)
3162306a36Sopenharmony_ci#define EXYNOS4_MCT_G_TCON		EXYNOS4_MCTREG(0x240)
3262306a36Sopenharmony_ci#define EXYNOS4_MCT_G_INT_CSTAT		EXYNOS4_MCTREG(0x244)
3362306a36Sopenharmony_ci#define EXYNOS4_MCT_G_INT_ENB		EXYNOS4_MCTREG(0x248)
3462306a36Sopenharmony_ci#define EXYNOS4_MCT_G_WSTAT		EXYNOS4_MCTREG(0x24C)
3562306a36Sopenharmony_ci#define _EXYNOS4_MCT_L_BASE		EXYNOS4_MCTREG(0x300)
3662306a36Sopenharmony_ci#define EXYNOS4_MCT_L_BASE(x)		(_EXYNOS4_MCT_L_BASE + (0x100 * (x)))
3762306a36Sopenharmony_ci#define EXYNOS4_MCT_L_MASK		(0xffffff00)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define MCT_L_TCNTB_OFFSET		(0x00)
4062306a36Sopenharmony_ci#define MCT_L_ICNTB_OFFSET		(0x08)
4162306a36Sopenharmony_ci#define MCT_L_TCON_OFFSET		(0x20)
4262306a36Sopenharmony_ci#define MCT_L_INT_CSTAT_OFFSET		(0x30)
4362306a36Sopenharmony_ci#define MCT_L_INT_ENB_OFFSET		(0x34)
4462306a36Sopenharmony_ci#define MCT_L_WSTAT_OFFSET		(0x40)
4562306a36Sopenharmony_ci#define MCT_G_TCON_START		(1 << 8)
4662306a36Sopenharmony_ci#define MCT_G_TCON_COMP0_AUTO_INC	(1 << 1)
4762306a36Sopenharmony_ci#define MCT_G_TCON_COMP0_ENABLE		(1 << 0)
4862306a36Sopenharmony_ci#define MCT_L_TCON_INTERVAL_MODE	(1 << 2)
4962306a36Sopenharmony_ci#define MCT_L_TCON_INT_START		(1 << 1)
5062306a36Sopenharmony_ci#define MCT_L_TCON_TIMER_START		(1 << 0)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define TICK_BASE_CNT	1
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef CONFIG_ARM
5562306a36Sopenharmony_ci/* Use values higher than ARM arch timer. See 6282edb72bed. */
5662306a36Sopenharmony_ci#define MCT_CLKSOURCE_RATING		450
5762306a36Sopenharmony_ci#define MCT_CLKEVENTS_RATING		500
5862306a36Sopenharmony_ci#else
5962306a36Sopenharmony_ci#define MCT_CLKSOURCE_RATING		350
6062306a36Sopenharmony_ci#define MCT_CLKEVENTS_RATING		350
6162306a36Sopenharmony_ci#endif
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* There are four Global timers starting with 0 offset */
6462306a36Sopenharmony_ci#define MCT_G0_IRQ	0
6562306a36Sopenharmony_ci/* Local timers count starts after global timer count */
6662306a36Sopenharmony_ci#define MCT_L0_IRQ	4
6762306a36Sopenharmony_ci/* Max number of IRQ as per DT binding document */
6862306a36Sopenharmony_ci#define MCT_NR_IRQS	20
6962306a36Sopenharmony_ci/* Max number of local timers */
7062306a36Sopenharmony_ci#define MCT_NR_LOCAL	(MCT_NR_IRQS - MCT_L0_IRQ)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cienum {
7362306a36Sopenharmony_ci	MCT_INT_SPI,
7462306a36Sopenharmony_ci	MCT_INT_PPI
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void __iomem *reg_base;
7862306a36Sopenharmony_cistatic unsigned long clk_rate;
7962306a36Sopenharmony_cistatic unsigned int mct_int_type;
8062306a36Sopenharmony_cistatic int mct_irqs[MCT_NR_IRQS];
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistruct mct_clock_event_device {
8362306a36Sopenharmony_ci	struct clock_event_device evt;
8462306a36Sopenharmony_ci	unsigned long base;
8562306a36Sopenharmony_ci	/**
8662306a36Sopenharmony_ci	 *  The length of the name must be adjusted if number of
8762306a36Sopenharmony_ci	 *  local timer interrupts grow over two digits
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	char name[11];
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void exynos4_mct_write(unsigned int value, unsigned long offset)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	unsigned long stat_addr;
9562306a36Sopenharmony_ci	u32 mask;
9662306a36Sopenharmony_ci	u32 i;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	writel_relaxed(value, reg_base + offset);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
10162306a36Sopenharmony_ci		stat_addr = (offset & EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
10262306a36Sopenharmony_ci		switch (offset & ~EXYNOS4_MCT_L_MASK) {
10362306a36Sopenharmony_ci		case MCT_L_TCON_OFFSET:
10462306a36Sopenharmony_ci			mask = 1 << 3;		/* L_TCON write status */
10562306a36Sopenharmony_ci			break;
10662306a36Sopenharmony_ci		case MCT_L_ICNTB_OFFSET:
10762306a36Sopenharmony_ci			mask = 1 << 1;		/* L_ICNTB write status */
10862306a36Sopenharmony_ci			break;
10962306a36Sopenharmony_ci		case MCT_L_TCNTB_OFFSET:
11062306a36Sopenharmony_ci			mask = 1 << 0;		/* L_TCNTB write status */
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		default:
11362306a36Sopenharmony_ci			return;
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci	} else {
11662306a36Sopenharmony_ci		switch (offset) {
11762306a36Sopenharmony_ci		case EXYNOS4_MCT_G_TCON:
11862306a36Sopenharmony_ci			stat_addr = EXYNOS4_MCT_G_WSTAT;
11962306a36Sopenharmony_ci			mask = 1 << 16;		/* G_TCON write status */
12062306a36Sopenharmony_ci			break;
12162306a36Sopenharmony_ci		case EXYNOS4_MCT_G_COMP0_L:
12262306a36Sopenharmony_ci			stat_addr = EXYNOS4_MCT_G_WSTAT;
12362306a36Sopenharmony_ci			mask = 1 << 0;		/* G_COMP0_L write status */
12462306a36Sopenharmony_ci			break;
12562306a36Sopenharmony_ci		case EXYNOS4_MCT_G_COMP0_U:
12662306a36Sopenharmony_ci			stat_addr = EXYNOS4_MCT_G_WSTAT;
12762306a36Sopenharmony_ci			mask = 1 << 1;		/* G_COMP0_U write status */
12862306a36Sopenharmony_ci			break;
12962306a36Sopenharmony_ci		case EXYNOS4_MCT_G_COMP0_ADD_INCR:
13062306a36Sopenharmony_ci			stat_addr = EXYNOS4_MCT_G_WSTAT;
13162306a36Sopenharmony_ci			mask = 1 << 2;		/* G_COMP0_ADD_INCR w status */
13262306a36Sopenharmony_ci			break;
13362306a36Sopenharmony_ci		case EXYNOS4_MCT_G_CNT_L:
13462306a36Sopenharmony_ci			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
13562306a36Sopenharmony_ci			mask = 1 << 0;		/* G_CNT_L write status */
13662306a36Sopenharmony_ci			break;
13762306a36Sopenharmony_ci		case EXYNOS4_MCT_G_CNT_U:
13862306a36Sopenharmony_ci			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
13962306a36Sopenharmony_ci			mask = 1 << 1;		/* G_CNT_U write status */
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci		default:
14262306a36Sopenharmony_ci			return;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* Wait maximum 1 ms until written values are applied */
14762306a36Sopenharmony_ci	for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
14862306a36Sopenharmony_ci		if (readl_relaxed(reg_base + stat_addr) & mask) {
14962306a36Sopenharmony_ci			writel_relaxed(mask, reg_base + stat_addr);
15062306a36Sopenharmony_ci			return;
15162306a36Sopenharmony_ci		}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* Clocksource handling */
15762306a36Sopenharmony_cistatic void exynos4_mct_frc_start(void)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	u32 reg;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	reg = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
16262306a36Sopenharmony_ci	reg |= MCT_G_TCON_START;
16362306a36Sopenharmony_ci	exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/**
16762306a36Sopenharmony_ci * exynos4_read_count_64 - Read all 64-bits of the global counter
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * This will read all 64-bits of the global counter taking care to make sure
17062306a36Sopenharmony_ci * that the upper and lower half match.  Note that reading the MCT can be quite
17162306a36Sopenharmony_ci * slow (hundreds of nanoseconds) so you should use the 32-bit (lower half
17262306a36Sopenharmony_ci * only) version when possible.
17362306a36Sopenharmony_ci *
17462306a36Sopenharmony_ci * Returns the number of cycles in the global counter.
17562306a36Sopenharmony_ci */
17662306a36Sopenharmony_cistatic u64 exynos4_read_count_64(void)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	unsigned int lo, hi;
17962306a36Sopenharmony_ci	u32 hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	do {
18262306a36Sopenharmony_ci		hi = hi2;
18362306a36Sopenharmony_ci		lo = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L);
18462306a36Sopenharmony_ci		hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U);
18562306a36Sopenharmony_ci	} while (hi != hi2);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return ((u64)hi << 32) | lo;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/**
19162306a36Sopenharmony_ci * exynos4_read_count_32 - Read the lower 32-bits of the global counter
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci * This will read just the lower 32-bits of the global counter.  This is marked
19462306a36Sopenharmony_ci * as notrace so it can be used by the scheduler clock.
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * Returns the number of cycles in the global counter (lower 32 bits).
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_cistatic u32 notrace exynos4_read_count_32(void)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	return readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic u64 exynos4_frc_read(struct clocksource *cs)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	return exynos4_read_count_32();
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void exynos4_frc_resume(struct clocksource *cs)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	exynos4_mct_frc_start();
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic struct clocksource mct_frc = {
21462306a36Sopenharmony_ci	.name		= "mct-frc",
21562306a36Sopenharmony_ci	.rating		= MCT_CLKSOURCE_RATING,
21662306a36Sopenharmony_ci	.read		= exynos4_frc_read,
21762306a36Sopenharmony_ci	.mask		= CLOCKSOURCE_MASK(32),
21862306a36Sopenharmony_ci	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
21962306a36Sopenharmony_ci	.resume		= exynos4_frc_resume,
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic u64 notrace exynos4_read_sched_clock(void)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	return exynos4_read_count_32();
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci#if defined(CONFIG_ARM)
22862306a36Sopenharmony_cistatic struct delay_timer exynos4_delay_timer;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic cycles_t exynos4_read_current_timer(void)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	BUILD_BUG_ON_MSG(sizeof(cycles_t) != sizeof(u32),
23362306a36Sopenharmony_ci			 "cycles_t needs to move to 32-bit for ARM64 usage");
23462306a36Sopenharmony_ci	return exynos4_read_count_32();
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci#endif
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int __init exynos4_clocksource_init(bool frc_shared)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	/*
24162306a36Sopenharmony_ci	 * When the frc is shared, the main processer should have already
24262306a36Sopenharmony_ci	 * turned it on and we shouldn't be writing to TCON.
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	if (frc_shared)
24562306a36Sopenharmony_ci		mct_frc.resume = NULL;
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		exynos4_mct_frc_start();
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci#if defined(CONFIG_ARM)
25062306a36Sopenharmony_ci	exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer;
25162306a36Sopenharmony_ci	exynos4_delay_timer.freq = clk_rate;
25262306a36Sopenharmony_ci	register_current_timer_delay(&exynos4_delay_timer);
25362306a36Sopenharmony_ci#endif
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (clocksource_register_hz(&mct_frc, clk_rate))
25662306a36Sopenharmony_ci		panic("%s: can't register clocksource\n", mct_frc.name);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	sched_clock_register(exynos4_read_sched_clock, 32, clk_rate);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void exynos4_mct_comp0_stop(void)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	unsigned int tcon;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
26862306a36Sopenharmony_ci	tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
27162306a36Sopenharmony_ci	exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void exynos4_mct_comp0_start(bool periodic, unsigned long cycles)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	unsigned int tcon;
27762306a36Sopenharmony_ci	u64 comp_cycle;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (periodic) {
28262306a36Sopenharmony_ci		tcon |= MCT_G_TCON_COMP0_AUTO_INC;
28362306a36Sopenharmony_ci		exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	comp_cycle = exynos4_read_count_64() + cycles;
28762306a36Sopenharmony_ci	exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
28862306a36Sopenharmony_ci	exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	tcon |= MCT_G_TCON_COMP0_ENABLE;
29362306a36Sopenharmony_ci	exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int exynos4_comp_set_next_event(unsigned long cycles,
29762306a36Sopenharmony_ci				       struct clock_event_device *evt)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	exynos4_mct_comp0_start(false, cycles);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int mct_set_state_shutdown(struct clock_event_device *evt)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	exynos4_mct_comp0_stop();
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic int mct_set_state_periodic(struct clock_event_device *evt)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	unsigned long cycles_per_jiffy;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult)
31562306a36Sopenharmony_ci			    >> evt->shift);
31662306a36Sopenharmony_ci	exynos4_mct_comp0_stop();
31762306a36Sopenharmony_ci	exynos4_mct_comp0_start(true, cycles_per_jiffy);
31862306a36Sopenharmony_ci	return 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic struct clock_event_device mct_comp_device = {
32262306a36Sopenharmony_ci	.name			= "mct-comp",
32362306a36Sopenharmony_ci	.features		= CLOCK_EVT_FEAT_PERIODIC |
32462306a36Sopenharmony_ci				  CLOCK_EVT_FEAT_ONESHOT,
32562306a36Sopenharmony_ci	.rating			= 250,
32662306a36Sopenharmony_ci	.set_next_event		= exynos4_comp_set_next_event,
32762306a36Sopenharmony_ci	.set_state_periodic	= mct_set_state_periodic,
32862306a36Sopenharmony_ci	.set_state_shutdown	= mct_set_state_shutdown,
32962306a36Sopenharmony_ci	.set_state_oneshot	= mct_set_state_shutdown,
33062306a36Sopenharmony_ci	.set_state_oneshot_stopped = mct_set_state_shutdown,
33162306a36Sopenharmony_ci	.tick_resume		= mct_set_state_shutdown,
33262306a36Sopenharmony_ci};
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct clock_event_device *evt = dev_id;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	evt->event_handler(evt);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return IRQ_HANDLED;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int exynos4_clockevent_init(void)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	mct_comp_device.cpumask = cpumask_of(0);
34862306a36Sopenharmony_ci	clockevents_config_and_register(&mct_comp_device, clk_rate,
34962306a36Sopenharmony_ci					0xf, 0xffffffff);
35062306a36Sopenharmony_ci	if (request_irq(mct_irqs[MCT_G0_IRQ], exynos4_mct_comp_isr,
35162306a36Sopenharmony_ci			IRQF_TIMER | IRQF_IRQPOLL, "mct_comp_irq",
35262306a36Sopenharmony_ci			&mct_comp_device))
35362306a36Sopenharmony_ci		pr_err("%s: request_irq() failed\n", "mct_comp_irq");
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci/* Clock event handling */
36162306a36Sopenharmony_cistatic void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	unsigned long tmp;
36462306a36Sopenharmony_ci	unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
36562306a36Sopenharmony_ci	unsigned long offset = mevt->base + MCT_L_TCON_OFFSET;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	tmp = readl_relaxed(reg_base + offset);
36862306a36Sopenharmony_ci	if (tmp & mask) {
36962306a36Sopenharmony_ci		tmp &= ~mask;
37062306a36Sopenharmony_ci		exynos4_mct_write(tmp, offset);
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void exynos4_mct_tick_start(unsigned long cycles,
37562306a36Sopenharmony_ci				   struct mct_clock_event_device *mevt)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	unsigned long tmp;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	exynos4_mct_tick_stop(mevt);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	tmp = (1 << 31) | cycles;	/* MCT_L_UPDATE_ICNTB */
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* update interrupt count buffer */
38462306a36Sopenharmony_ci	exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* enable MCT tick interrupt */
38762306a36Sopenharmony_ci	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	tmp = readl_relaxed(reg_base + mevt->base + MCT_L_TCON_OFFSET);
39062306a36Sopenharmony_ci	tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
39162306a36Sopenharmony_ci	       MCT_L_TCON_INTERVAL_MODE;
39262306a36Sopenharmony_ci	exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	/* Clear the MCT tick interrupt */
39862306a36Sopenharmony_ci	if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1)
39962306a36Sopenharmony_ci		exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int exynos4_tick_set_next_event(unsigned long cycles,
40362306a36Sopenharmony_ci				       struct clock_event_device *evt)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct mct_clock_event_device *mevt;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	mevt = container_of(evt, struct mct_clock_event_device, evt);
40862306a36Sopenharmony_ci	exynos4_mct_tick_start(cycles, mevt);
40962306a36Sopenharmony_ci	return 0;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic int set_state_shutdown(struct clock_event_device *evt)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct mct_clock_event_device *mevt;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	mevt = container_of(evt, struct mct_clock_event_device, evt);
41762306a36Sopenharmony_ci	exynos4_mct_tick_stop(mevt);
41862306a36Sopenharmony_ci	exynos4_mct_tick_clear(mevt);
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int set_state_periodic(struct clock_event_device *evt)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct mct_clock_event_device *mevt;
42562306a36Sopenharmony_ci	unsigned long cycles_per_jiffy;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	mevt = container_of(evt, struct mct_clock_event_device, evt);
42862306a36Sopenharmony_ci	cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult)
42962306a36Sopenharmony_ci			    >> evt->shift);
43062306a36Sopenharmony_ci	exynos4_mct_tick_stop(mevt);
43162306a36Sopenharmony_ci	exynos4_mct_tick_start(cycles_per_jiffy, mevt);
43262306a36Sopenharmony_ci	return 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct mct_clock_event_device *mevt = dev_id;
43862306a36Sopenharmony_ci	struct clock_event_device *evt = &mevt->evt;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * This is for supporting oneshot mode.
44262306a36Sopenharmony_ci	 * Mct would generate interrupt periodically
44362306a36Sopenharmony_ci	 * without explicit stopping.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	if (!clockevent_state_periodic(&mevt->evt))
44662306a36Sopenharmony_ci		exynos4_mct_tick_stop(mevt);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	exynos4_mct_tick_clear(mevt);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	evt->event_handler(evt);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return IRQ_HANDLED;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int exynos4_mct_starting_cpu(unsigned int cpu)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct mct_clock_event_device *mevt =
45862306a36Sopenharmony_ci		per_cpu_ptr(&percpu_mct_tick, cpu);
45962306a36Sopenharmony_ci	struct clock_event_device *evt = &mevt->evt;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	snprintf(mevt->name, sizeof(mevt->name), "mct_tick%d", cpu);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	evt->name = mevt->name;
46462306a36Sopenharmony_ci	evt->cpumask = cpumask_of(cpu);
46562306a36Sopenharmony_ci	evt->set_next_event = exynos4_tick_set_next_event;
46662306a36Sopenharmony_ci	evt->set_state_periodic = set_state_periodic;
46762306a36Sopenharmony_ci	evt->set_state_shutdown = set_state_shutdown;
46862306a36Sopenharmony_ci	evt->set_state_oneshot = set_state_shutdown;
46962306a36Sopenharmony_ci	evt->set_state_oneshot_stopped = set_state_shutdown;
47062306a36Sopenharmony_ci	evt->tick_resume = set_state_shutdown;
47162306a36Sopenharmony_ci	evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
47262306a36Sopenharmony_ci			CLOCK_EVT_FEAT_PERCPU;
47362306a36Sopenharmony_ci	evt->rating = MCT_CLKEVENTS_RATING;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (mct_int_type == MCT_INT_SPI) {
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		if (evt->irq == -1)
48062306a36Sopenharmony_ci			return -EIO;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		irq_force_affinity(evt->irq, cpumask_of(cpu));
48362306a36Sopenharmony_ci		enable_irq(evt->irq);
48462306a36Sopenharmony_ci	} else {
48562306a36Sopenharmony_ci		enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci	clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
48862306a36Sopenharmony_ci					0xf, 0x7fffffff);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return 0;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int exynos4_mct_dying_cpu(unsigned int cpu)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct mct_clock_event_device *mevt =
49662306a36Sopenharmony_ci		per_cpu_ptr(&percpu_mct_tick, cpu);
49762306a36Sopenharmony_ci	struct clock_event_device *evt = &mevt->evt;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	evt->set_state_shutdown(evt);
50062306a36Sopenharmony_ci	if (mct_int_type == MCT_INT_SPI) {
50162306a36Sopenharmony_ci		if (evt->irq != -1)
50262306a36Sopenharmony_ci			disable_irq_nosync(evt->irq);
50362306a36Sopenharmony_ci		exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
50462306a36Sopenharmony_ci	} else {
50562306a36Sopenharmony_ci		disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci	return 0;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int __init exynos4_timer_resources(struct device_node *np)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct clk *mct_clk, *tick_clk;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	reg_base = of_iomap(np, 0);
51562306a36Sopenharmony_ci	if (!reg_base)
51662306a36Sopenharmony_ci		panic("%s: unable to ioremap mct address space\n", __func__);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	tick_clk = of_clk_get_by_name(np, "fin_pll");
51962306a36Sopenharmony_ci	if (IS_ERR(tick_clk))
52062306a36Sopenharmony_ci		panic("%s: unable to determine tick clock rate\n", __func__);
52162306a36Sopenharmony_ci	clk_rate = clk_get_rate(tick_clk);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	mct_clk = of_clk_get_by_name(np, "mct");
52462306a36Sopenharmony_ci	if (IS_ERR(mct_clk))
52562306a36Sopenharmony_ci		panic("%s: unable to retrieve mct clock instance\n", __func__);
52662306a36Sopenharmony_ci	clk_prepare_enable(mct_clk);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/**
53262306a36Sopenharmony_ci * exynos4_timer_interrupts - initialize MCT interrupts
53362306a36Sopenharmony_ci * @np: device node for MCT
53462306a36Sopenharmony_ci * @int_type: interrupt type, MCT_INT_PPI or MCT_INT_SPI
53562306a36Sopenharmony_ci * @local_idx: array mapping CPU numbers to local timer indices
53662306a36Sopenharmony_ci * @nr_local: size of @local_idx array
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_cistatic int __init exynos4_timer_interrupts(struct device_node *np,
53962306a36Sopenharmony_ci					   unsigned int int_type,
54062306a36Sopenharmony_ci					   const u32 *local_idx,
54162306a36Sopenharmony_ci					   size_t nr_local)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	int nr_irqs, i, err, cpu;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	mct_int_type = int_type;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* This driver uses only one global timer interrupt */
54862306a36Sopenharmony_ci	mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/*
55162306a36Sopenharmony_ci	 * Find out the number of local irqs specified. The local
55262306a36Sopenharmony_ci	 * timer irqs are specified after the four global timer
55362306a36Sopenharmony_ci	 * irqs are specified.
55462306a36Sopenharmony_ci	 */
55562306a36Sopenharmony_ci	nr_irqs = of_irq_count(np);
55662306a36Sopenharmony_ci	if (nr_irqs > ARRAY_SIZE(mct_irqs)) {
55762306a36Sopenharmony_ci		pr_err("exynos-mct: too many (%d) interrupts configured in DT\n",
55862306a36Sopenharmony_ci			nr_irqs);
55962306a36Sopenharmony_ci		nr_irqs = ARRAY_SIZE(mct_irqs);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	for (i = MCT_L0_IRQ; i < nr_irqs; i++)
56262306a36Sopenharmony_ci		mct_irqs[i] = irq_of_parse_and_map(np, i);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (mct_int_type == MCT_INT_PPI) {
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
56762306a36Sopenharmony_ci					 exynos4_mct_tick_isr, "MCT",
56862306a36Sopenharmony_ci					 &percpu_mct_tick);
56962306a36Sopenharmony_ci		WARN(err, "MCT: can't request IRQ %d (%d)\n",
57062306a36Sopenharmony_ci		     mct_irqs[MCT_L0_IRQ], err);
57162306a36Sopenharmony_ci	} else {
57262306a36Sopenharmony_ci		for_each_possible_cpu(cpu) {
57362306a36Sopenharmony_ci			int mct_irq;
57462306a36Sopenharmony_ci			unsigned int irq_idx;
57562306a36Sopenharmony_ci			struct mct_clock_event_device *pcpu_mevt =
57662306a36Sopenharmony_ci				per_cpu_ptr(&percpu_mct_tick, cpu);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci			if (cpu >= nr_local) {
57962306a36Sopenharmony_ci				err = -EINVAL;
58062306a36Sopenharmony_ci				goto out_irq;
58162306a36Sopenharmony_ci			}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci			irq_idx = MCT_L0_IRQ + local_idx[cpu];
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci			pcpu_mevt->evt.irq = -1;
58662306a36Sopenharmony_ci			if (irq_idx >= ARRAY_SIZE(mct_irqs))
58762306a36Sopenharmony_ci				break;
58862306a36Sopenharmony_ci			mct_irq = mct_irqs[irq_idx];
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci			irq_set_status_flags(mct_irq, IRQ_NOAUTOEN);
59162306a36Sopenharmony_ci			if (request_irq(mct_irq,
59262306a36Sopenharmony_ci					exynos4_mct_tick_isr,
59362306a36Sopenharmony_ci					IRQF_TIMER | IRQF_NOBALANCING,
59462306a36Sopenharmony_ci					pcpu_mevt->name, pcpu_mevt)) {
59562306a36Sopenharmony_ci				pr_err("exynos-mct: cannot register IRQ (cpu%d)\n",
59662306a36Sopenharmony_ci									cpu);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci				continue;
59962306a36Sopenharmony_ci			}
60062306a36Sopenharmony_ci			pcpu_mevt->evt.irq = mct_irq;
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
60562306a36Sopenharmony_ci		struct mct_clock_event_device *mevt = per_cpu_ptr(&percpu_mct_tick, cpu);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		if (cpu >= nr_local) {
60862306a36Sopenharmony_ci			err = -EINVAL;
60962306a36Sopenharmony_ci			goto out_irq;
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		mevt->base = EXYNOS4_MCT_L_BASE(local_idx[cpu]);
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* Install hotplug callbacks which configure the timer on this CPU */
61662306a36Sopenharmony_ci	err = cpuhp_setup_state(CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING,
61762306a36Sopenharmony_ci				"clockevents/exynos4/mct_timer:starting",
61862306a36Sopenharmony_ci				exynos4_mct_starting_cpu,
61962306a36Sopenharmony_ci				exynos4_mct_dying_cpu);
62062306a36Sopenharmony_ci	if (err)
62162306a36Sopenharmony_ci		goto out_irq;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	return 0;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ciout_irq:
62662306a36Sopenharmony_ci	if (mct_int_type == MCT_INT_PPI) {
62762306a36Sopenharmony_ci		free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick);
62862306a36Sopenharmony_ci	} else {
62962306a36Sopenharmony_ci		for_each_possible_cpu(cpu) {
63062306a36Sopenharmony_ci			struct mct_clock_event_device *pcpu_mevt =
63162306a36Sopenharmony_ci				per_cpu_ptr(&percpu_mct_tick, cpu);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci			if (pcpu_mevt->evt.irq != -1) {
63462306a36Sopenharmony_ci				free_irq(pcpu_mevt->evt.irq, pcpu_mevt);
63562306a36Sopenharmony_ci				pcpu_mevt->evt.irq = -1;
63662306a36Sopenharmony_ci			}
63762306a36Sopenharmony_ci		}
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci	return err;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int __init mct_init_dt(struct device_node *np, unsigned int int_type)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	bool frc_shared = of_property_read_bool(np, "samsung,frc-shared");
64562306a36Sopenharmony_ci	u32 local_idx[MCT_NR_LOCAL] = {0};
64662306a36Sopenharmony_ci	int nr_local;
64762306a36Sopenharmony_ci	int ret;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	nr_local = of_property_count_u32_elems(np, "samsung,local-timers");
65062306a36Sopenharmony_ci	if (nr_local == 0)
65162306a36Sopenharmony_ci		return -EINVAL;
65262306a36Sopenharmony_ci	if (nr_local > 0) {
65362306a36Sopenharmony_ci		if (nr_local > ARRAY_SIZE(local_idx))
65462306a36Sopenharmony_ci			return -EINVAL;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		ret = of_property_read_u32_array(np, "samsung,local-timers",
65762306a36Sopenharmony_ci						 local_idx, nr_local);
65862306a36Sopenharmony_ci		if (ret)
65962306a36Sopenharmony_ci			return ret;
66062306a36Sopenharmony_ci	} else {
66162306a36Sopenharmony_ci		int i;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		nr_local = ARRAY_SIZE(local_idx);
66462306a36Sopenharmony_ci		for (i = 0; i < nr_local; i++)
66562306a36Sopenharmony_ci			local_idx[i] = i;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	ret = exynos4_timer_resources(np);
66962306a36Sopenharmony_ci	if (ret)
67062306a36Sopenharmony_ci		return ret;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	ret = exynos4_timer_interrupts(np, int_type, local_idx, nr_local);
67362306a36Sopenharmony_ci	if (ret)
67462306a36Sopenharmony_ci		return ret;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	ret = exynos4_clocksource_init(frc_shared);
67762306a36Sopenharmony_ci	if (ret)
67862306a36Sopenharmony_ci		return ret;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/*
68162306a36Sopenharmony_ci	 * When the FRC is shared with a main processor, this secondary
68262306a36Sopenharmony_ci	 * processor cannot use the global comparator.
68362306a36Sopenharmony_ci	 */
68462306a36Sopenharmony_ci	if (frc_shared)
68562306a36Sopenharmony_ci		return 0;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	return exynos4_clockevent_init();
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int __init mct_init_spi(struct device_node *np)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	return mct_init_dt(np, MCT_INT_SPI);
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int __init mct_init_ppi(struct device_node *np)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	return mct_init_dt(np, MCT_INT_PPI);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ciTIMER_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
70162306a36Sopenharmony_ciTIMER_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
702