18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* linux/arch/arm/mach-exynos4/mct.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 58c2ecf20Sopenharmony_ci * http://www.samsung.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Exynos4 MCT(Multi-Core Timer) support 88c2ecf20Sopenharmony_ci*/ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/irq.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 158c2ecf20Sopenharmony_ci#include <linux/cpu.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/percpu.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 228c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define EXYNOS4_MCTREG(x) (x) 258c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100) 268c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104) 278c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110) 288c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200) 298c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204) 308c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208) 318c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240) 328c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244) 338c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248) 348c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C) 358c2ecf20Sopenharmony_ci#define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300) 368c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x)) 378c2ecf20Sopenharmony_ci#define EXYNOS4_MCT_L_MASK (0xffffff00) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MCT_L_TCNTB_OFFSET (0x00) 408c2ecf20Sopenharmony_ci#define MCT_L_ICNTB_OFFSET (0x08) 418c2ecf20Sopenharmony_ci#define MCT_L_TCON_OFFSET (0x20) 428c2ecf20Sopenharmony_ci#define MCT_L_INT_CSTAT_OFFSET (0x30) 438c2ecf20Sopenharmony_ci#define MCT_L_INT_ENB_OFFSET (0x34) 448c2ecf20Sopenharmony_ci#define MCT_L_WSTAT_OFFSET (0x40) 458c2ecf20Sopenharmony_ci#define MCT_G_TCON_START (1 << 8) 468c2ecf20Sopenharmony_ci#define MCT_G_TCON_COMP0_AUTO_INC (1 << 1) 478c2ecf20Sopenharmony_ci#define MCT_G_TCON_COMP0_ENABLE (1 << 0) 488c2ecf20Sopenharmony_ci#define MCT_L_TCON_INTERVAL_MODE (1 << 2) 498c2ecf20Sopenharmony_ci#define MCT_L_TCON_INT_START (1 << 1) 508c2ecf20Sopenharmony_ci#define MCT_L_TCON_TIMER_START (1 << 0) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define TICK_BASE_CNT 1 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cienum { 558c2ecf20Sopenharmony_ci MCT_INT_SPI, 568c2ecf20Sopenharmony_ci MCT_INT_PPI 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cienum { 608c2ecf20Sopenharmony_ci MCT_G0_IRQ, 618c2ecf20Sopenharmony_ci MCT_G1_IRQ, 628c2ecf20Sopenharmony_ci MCT_G2_IRQ, 638c2ecf20Sopenharmony_ci MCT_G3_IRQ, 648c2ecf20Sopenharmony_ci MCT_L0_IRQ, 658c2ecf20Sopenharmony_ci MCT_L1_IRQ, 668c2ecf20Sopenharmony_ci MCT_L2_IRQ, 678c2ecf20Sopenharmony_ci MCT_L3_IRQ, 688c2ecf20Sopenharmony_ci MCT_L4_IRQ, 698c2ecf20Sopenharmony_ci MCT_L5_IRQ, 708c2ecf20Sopenharmony_ci MCT_L6_IRQ, 718c2ecf20Sopenharmony_ci MCT_L7_IRQ, 728c2ecf20Sopenharmony_ci MCT_NR_IRQS, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void __iomem *reg_base; 768c2ecf20Sopenharmony_cistatic unsigned long clk_rate; 778c2ecf20Sopenharmony_cistatic unsigned int mct_int_type; 788c2ecf20Sopenharmony_cistatic int mct_irqs[MCT_NR_IRQS]; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct mct_clock_event_device { 818c2ecf20Sopenharmony_ci struct clock_event_device evt; 828c2ecf20Sopenharmony_ci unsigned long base; 838c2ecf20Sopenharmony_ci char name[10]; 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void exynos4_mct_write(unsigned int value, unsigned long offset) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned long stat_addr; 898c2ecf20Sopenharmony_ci u32 mask; 908c2ecf20Sopenharmony_ci u32 i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci writel_relaxed(value, reg_base + offset); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) { 958c2ecf20Sopenharmony_ci stat_addr = (offset & EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; 968c2ecf20Sopenharmony_ci switch (offset & ~EXYNOS4_MCT_L_MASK) { 978c2ecf20Sopenharmony_ci case MCT_L_TCON_OFFSET: 988c2ecf20Sopenharmony_ci mask = 1 << 3; /* L_TCON write status */ 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci case MCT_L_ICNTB_OFFSET: 1018c2ecf20Sopenharmony_ci mask = 1 << 1; /* L_ICNTB write status */ 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci case MCT_L_TCNTB_OFFSET: 1048c2ecf20Sopenharmony_ci mask = 1 << 0; /* L_TCNTB write status */ 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci default: 1078c2ecf20Sopenharmony_ci return; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci switch (offset) { 1118c2ecf20Sopenharmony_ci case EXYNOS4_MCT_G_TCON: 1128c2ecf20Sopenharmony_ci stat_addr = EXYNOS4_MCT_G_WSTAT; 1138c2ecf20Sopenharmony_ci mask = 1 << 16; /* G_TCON write status */ 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case EXYNOS4_MCT_G_COMP0_L: 1168c2ecf20Sopenharmony_ci stat_addr = EXYNOS4_MCT_G_WSTAT; 1178c2ecf20Sopenharmony_ci mask = 1 << 0; /* G_COMP0_L write status */ 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci case EXYNOS4_MCT_G_COMP0_U: 1208c2ecf20Sopenharmony_ci stat_addr = EXYNOS4_MCT_G_WSTAT; 1218c2ecf20Sopenharmony_ci mask = 1 << 1; /* G_COMP0_U write status */ 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case EXYNOS4_MCT_G_COMP0_ADD_INCR: 1248c2ecf20Sopenharmony_ci stat_addr = EXYNOS4_MCT_G_WSTAT; 1258c2ecf20Sopenharmony_ci mask = 1 << 2; /* G_COMP0_ADD_INCR w status */ 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case EXYNOS4_MCT_G_CNT_L: 1288c2ecf20Sopenharmony_ci stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; 1298c2ecf20Sopenharmony_ci mask = 1 << 0; /* G_CNT_L write status */ 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci case EXYNOS4_MCT_G_CNT_U: 1328c2ecf20Sopenharmony_ci stat_addr = EXYNOS4_MCT_G_CNT_WSTAT; 1338c2ecf20Sopenharmony_ci mask = 1 << 1; /* G_CNT_U write status */ 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci default: 1368c2ecf20Sopenharmony_ci return; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Wait maximum 1 ms until written values are applied */ 1418c2ecf20Sopenharmony_ci for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++) 1428c2ecf20Sopenharmony_ci if (readl_relaxed(reg_base + stat_addr) & mask) { 1438c2ecf20Sopenharmony_ci writel_relaxed(mask, reg_base + stat_addr); 1448c2ecf20Sopenharmony_ci return; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* Clocksource handling */ 1518c2ecf20Sopenharmony_cistatic void exynos4_mct_frc_start(void) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci u32 reg; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci reg = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); 1568c2ecf20Sopenharmony_ci reg |= MCT_G_TCON_START; 1578c2ecf20Sopenharmony_ci exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * exynos4_read_count_64 - Read all 64-bits of the global counter 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * This will read all 64-bits of the global counter taking care to make sure 1648c2ecf20Sopenharmony_ci * that the upper and lower half match. Note that reading the MCT can be quite 1658c2ecf20Sopenharmony_ci * slow (hundreds of nanoseconds) so you should use the 32-bit (lower half 1668c2ecf20Sopenharmony_ci * only) version when possible. 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Returns the number of cycles in the global counter. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_cistatic u64 exynos4_read_count_64(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci unsigned int lo, hi; 1738c2ecf20Sopenharmony_ci u32 hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci do { 1768c2ecf20Sopenharmony_ci hi = hi2; 1778c2ecf20Sopenharmony_ci lo = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L); 1788c2ecf20Sopenharmony_ci hi2 = readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_U); 1798c2ecf20Sopenharmony_ci } while (hi != hi2); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return ((u64)hi << 32) | lo; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/** 1858c2ecf20Sopenharmony_ci * exynos4_read_count_32 - Read the lower 32-bits of the global counter 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * This will read just the lower 32-bits of the global counter. This is marked 1888c2ecf20Sopenharmony_ci * as notrace so it can be used by the scheduler clock. 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * Returns the number of cycles in the global counter (lower 32 bits). 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistatic u32 notrace exynos4_read_count_32(void) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return readl_relaxed(reg_base + EXYNOS4_MCT_G_CNT_L); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic u64 exynos4_frc_read(struct clocksource *cs) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return exynos4_read_count_32(); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void exynos4_frc_resume(struct clocksource *cs) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci exynos4_mct_frc_start(); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic struct clocksource mct_frc = { 2088c2ecf20Sopenharmony_ci .name = "mct-frc", 2098c2ecf20Sopenharmony_ci .rating = 450, /* use value higher than ARM arch timer */ 2108c2ecf20Sopenharmony_ci .read = exynos4_frc_read, 2118c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 2128c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 2138c2ecf20Sopenharmony_ci .resume = exynos4_frc_resume, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic u64 notrace exynos4_read_sched_clock(void) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci return exynos4_read_count_32(); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) 2228c2ecf20Sopenharmony_cistatic struct delay_timer exynos4_delay_timer; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic cycles_t exynos4_read_current_timer(void) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci BUILD_BUG_ON_MSG(sizeof(cycles_t) != sizeof(u32), 2278c2ecf20Sopenharmony_ci "cycles_t needs to move to 32-bit for ARM64 usage"); 2288c2ecf20Sopenharmony_ci return exynos4_read_count_32(); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci#endif 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int __init exynos4_clocksource_init(void) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci exynos4_mct_frc_start(); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) 2378c2ecf20Sopenharmony_ci exynos4_delay_timer.read_current_timer = &exynos4_read_current_timer; 2388c2ecf20Sopenharmony_ci exynos4_delay_timer.freq = clk_rate; 2398c2ecf20Sopenharmony_ci register_current_timer_delay(&exynos4_delay_timer); 2408c2ecf20Sopenharmony_ci#endif 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (clocksource_register_hz(&mct_frc, clk_rate)) 2438c2ecf20Sopenharmony_ci panic("%s: can't register clocksource\n", mct_frc.name); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci sched_clock_register(exynos4_read_sched_clock, 32, clk_rate); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void exynos4_mct_comp0_stop(void) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci unsigned int tcon; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); 2558c2ecf20Sopenharmony_ci tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON); 2588c2ecf20Sopenharmony_ci exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void exynos4_mct_comp0_start(bool periodic, unsigned long cycles) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci unsigned int tcon; 2648c2ecf20Sopenharmony_ci u64 comp_cycle; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci tcon = readl_relaxed(reg_base + EXYNOS4_MCT_G_TCON); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (periodic) { 2698c2ecf20Sopenharmony_ci tcon |= MCT_G_TCON_COMP0_AUTO_INC; 2708c2ecf20Sopenharmony_ci exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci comp_cycle = exynos4_read_count_64() + cycles; 2748c2ecf20Sopenharmony_ci exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L); 2758c2ecf20Sopenharmony_ci exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci tcon |= MCT_G_TCON_COMP0_ENABLE; 2808c2ecf20Sopenharmony_ci exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int exynos4_comp_set_next_event(unsigned long cycles, 2848c2ecf20Sopenharmony_ci struct clock_event_device *evt) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci exynos4_mct_comp0_start(false, cycles); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int mct_set_state_shutdown(struct clock_event_device *evt) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci exynos4_mct_comp0_stop(); 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int mct_set_state_periodic(struct clock_event_device *evt) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci unsigned long cycles_per_jiffy; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult) 3028c2ecf20Sopenharmony_ci >> evt->shift); 3038c2ecf20Sopenharmony_ci exynos4_mct_comp0_stop(); 3048c2ecf20Sopenharmony_ci exynos4_mct_comp0_start(true, cycles_per_jiffy); 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic struct clock_event_device mct_comp_device = { 3098c2ecf20Sopenharmony_ci .name = "mct-comp", 3108c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 3118c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 3128c2ecf20Sopenharmony_ci .rating = 250, 3138c2ecf20Sopenharmony_ci .set_next_event = exynos4_comp_set_next_event, 3148c2ecf20Sopenharmony_ci .set_state_periodic = mct_set_state_periodic, 3158c2ecf20Sopenharmony_ci .set_state_shutdown = mct_set_state_shutdown, 3168c2ecf20Sopenharmony_ci .set_state_oneshot = mct_set_state_shutdown, 3178c2ecf20Sopenharmony_ci .set_state_oneshot_stopped = mct_set_state_shutdown, 3188c2ecf20Sopenharmony_ci .tick_resume = mct_set_state_shutdown, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci evt->event_handler(evt); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int exynos4_clockevent_init(void) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci mct_comp_device.cpumask = cpumask_of(0); 3358c2ecf20Sopenharmony_ci clockevents_config_and_register(&mct_comp_device, clk_rate, 3368c2ecf20Sopenharmony_ci 0xf, 0xffffffff); 3378c2ecf20Sopenharmony_ci if (request_irq(mct_irqs[MCT_G0_IRQ], exynos4_mct_comp_isr, 3388c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "mct_comp_irq", 3398c2ecf20Sopenharmony_ci &mct_comp_device)) 3408c2ecf20Sopenharmony_ci pr_err("%s: request_irq() failed\n", "mct_comp_irq"); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/* Clock event handling */ 3488c2ecf20Sopenharmony_cistatic void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci unsigned long tmp; 3518c2ecf20Sopenharmony_ci unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START; 3528c2ecf20Sopenharmony_ci unsigned long offset = mevt->base + MCT_L_TCON_OFFSET; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci tmp = readl_relaxed(reg_base + offset); 3558c2ecf20Sopenharmony_ci if (tmp & mask) { 3568c2ecf20Sopenharmony_ci tmp &= ~mask; 3578c2ecf20Sopenharmony_ci exynos4_mct_write(tmp, offset); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void exynos4_mct_tick_start(unsigned long cycles, 3628c2ecf20Sopenharmony_ci struct mct_clock_event_device *mevt) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci unsigned long tmp; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci exynos4_mct_tick_stop(mevt); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */ 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* update interrupt count buffer */ 3718c2ecf20Sopenharmony_ci exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* enable MCT tick interrupt */ 3748c2ecf20Sopenharmony_ci exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci tmp = readl_relaxed(reg_base + mevt->base + MCT_L_TCON_OFFSET); 3778c2ecf20Sopenharmony_ci tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START | 3788c2ecf20Sopenharmony_ci MCT_L_TCON_INTERVAL_MODE; 3798c2ecf20Sopenharmony_ci exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void exynos4_mct_tick_clear(struct mct_clock_event_device *mevt) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci /* Clear the MCT tick interrupt */ 3858c2ecf20Sopenharmony_ci if (readl_relaxed(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) 3868c2ecf20Sopenharmony_ci exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int exynos4_tick_set_next_event(unsigned long cycles, 3908c2ecf20Sopenharmony_ci struct clock_event_device *evt) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct mct_clock_event_device *mevt; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci mevt = container_of(evt, struct mct_clock_event_device, evt); 3958c2ecf20Sopenharmony_ci exynos4_mct_tick_start(cycles, mevt); 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int set_state_shutdown(struct clock_event_device *evt) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct mct_clock_event_device *mevt; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci mevt = container_of(evt, struct mct_clock_event_device, evt); 4048c2ecf20Sopenharmony_ci exynos4_mct_tick_stop(mevt); 4058c2ecf20Sopenharmony_ci exynos4_mct_tick_clear(mevt); 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic int set_state_periodic(struct clock_event_device *evt) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct mct_clock_event_device *mevt; 4128c2ecf20Sopenharmony_ci unsigned long cycles_per_jiffy; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci mevt = container_of(evt, struct mct_clock_event_device, evt); 4158c2ecf20Sopenharmony_ci cycles_per_jiffy = (((unsigned long long)NSEC_PER_SEC / HZ * evt->mult) 4168c2ecf20Sopenharmony_ci >> evt->shift); 4178c2ecf20Sopenharmony_ci exynos4_mct_tick_stop(mevt); 4188c2ecf20Sopenharmony_ci exynos4_mct_tick_start(cycles_per_jiffy, mevt); 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct mct_clock_event_device *mevt = dev_id; 4258c2ecf20Sopenharmony_ci struct clock_event_device *evt = &mevt->evt; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * This is for supporting oneshot mode. 4298c2ecf20Sopenharmony_ci * Mct would generate interrupt periodically 4308c2ecf20Sopenharmony_ci * without explicit stopping. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_ci if (!clockevent_state_periodic(&mevt->evt)) 4338c2ecf20Sopenharmony_ci exynos4_mct_tick_stop(mevt); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci exynos4_mct_tick_clear(mevt); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci evt->event_handler(evt); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int exynos4_mct_starting_cpu(unsigned int cpu) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct mct_clock_event_device *mevt = 4458c2ecf20Sopenharmony_ci per_cpu_ptr(&percpu_mct_tick, cpu); 4468c2ecf20Sopenharmony_ci struct clock_event_device *evt = &mevt->evt; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci mevt->base = EXYNOS4_MCT_L_BASE(cpu); 4498c2ecf20Sopenharmony_ci snprintf(mevt->name, sizeof(mevt->name), "mct_tick%d", cpu); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci evt->name = mevt->name; 4528c2ecf20Sopenharmony_ci evt->cpumask = cpumask_of(cpu); 4538c2ecf20Sopenharmony_ci evt->set_next_event = exynos4_tick_set_next_event; 4548c2ecf20Sopenharmony_ci evt->set_state_periodic = set_state_periodic; 4558c2ecf20Sopenharmony_ci evt->set_state_shutdown = set_state_shutdown; 4568c2ecf20Sopenharmony_ci evt->set_state_oneshot = set_state_shutdown; 4578c2ecf20Sopenharmony_ci evt->set_state_oneshot_stopped = set_state_shutdown; 4588c2ecf20Sopenharmony_ci evt->tick_resume = set_state_shutdown; 4598c2ecf20Sopenharmony_ci evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 4608c2ecf20Sopenharmony_ci evt->rating = 500; /* use value higher than ARM arch timer */ 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (mct_int_type == MCT_INT_SPI) { 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (evt->irq == -1) 4678c2ecf20Sopenharmony_ci return -EIO; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci irq_force_affinity(evt->irq, cpumask_of(cpu)); 4708c2ecf20Sopenharmony_ci enable_irq(evt->irq); 4718c2ecf20Sopenharmony_ci } else { 4728c2ecf20Sopenharmony_ci enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1), 4758c2ecf20Sopenharmony_ci 0xf, 0x7fffffff); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int exynos4_mct_dying_cpu(unsigned int cpu) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct mct_clock_event_device *mevt = 4838c2ecf20Sopenharmony_ci per_cpu_ptr(&percpu_mct_tick, cpu); 4848c2ecf20Sopenharmony_ci struct clock_event_device *evt = &mevt->evt; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci evt->set_state_shutdown(evt); 4878c2ecf20Sopenharmony_ci if (mct_int_type == MCT_INT_SPI) { 4888c2ecf20Sopenharmony_ci if (evt->irq != -1) 4898c2ecf20Sopenharmony_ci disable_irq_nosync(evt->irq); 4908c2ecf20Sopenharmony_ci exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET); 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci disable_percpu_irq(mct_irqs[MCT_L0_IRQ]); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int __init exynos4_timer_resources(struct device_node *np) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct clk *mct_clk, *tick_clk; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci reg_base = of_iomap(np, 0); 5028c2ecf20Sopenharmony_ci if (!reg_base) 5038c2ecf20Sopenharmony_ci panic("%s: unable to ioremap mct address space\n", __func__); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci tick_clk = of_clk_get_by_name(np, "fin_pll"); 5068c2ecf20Sopenharmony_ci if (IS_ERR(tick_clk)) 5078c2ecf20Sopenharmony_ci panic("%s: unable to determine tick clock rate\n", __func__); 5088c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(tick_clk); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci mct_clk = of_clk_get_by_name(np, "mct"); 5118c2ecf20Sopenharmony_ci if (IS_ERR(mct_clk)) 5128c2ecf20Sopenharmony_ci panic("%s: unable to retrieve mct clock instance\n", __func__); 5138c2ecf20Sopenharmony_ci clk_prepare_enable(mct_clk); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int __init exynos4_timer_interrupts(struct device_node *np, 5198c2ecf20Sopenharmony_ci unsigned int int_type) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci int nr_irqs, i, err, cpu; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci mct_int_type = int_type; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* This driver uses only one global timer interrupt */ 5268c2ecf20Sopenharmony_ci mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * Find out the number of local irqs specified. The local 5308c2ecf20Sopenharmony_ci * timer irqs are specified after the four global timer 5318c2ecf20Sopenharmony_ci * irqs are specified. 5328c2ecf20Sopenharmony_ci */ 5338c2ecf20Sopenharmony_ci nr_irqs = of_irq_count(np); 5348c2ecf20Sopenharmony_ci if (nr_irqs > ARRAY_SIZE(mct_irqs)) { 5358c2ecf20Sopenharmony_ci pr_err("exynos-mct: too many (%d) interrupts configured in DT\n", 5368c2ecf20Sopenharmony_ci nr_irqs); 5378c2ecf20Sopenharmony_ci nr_irqs = ARRAY_SIZE(mct_irqs); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci for (i = MCT_L0_IRQ; i < nr_irqs; i++) 5408c2ecf20Sopenharmony_ci mct_irqs[i] = irq_of_parse_and_map(np, i); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (mct_int_type == MCT_INT_PPI) { 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci err = request_percpu_irq(mct_irqs[MCT_L0_IRQ], 5458c2ecf20Sopenharmony_ci exynos4_mct_tick_isr, "MCT", 5468c2ecf20Sopenharmony_ci &percpu_mct_tick); 5478c2ecf20Sopenharmony_ci WARN(err, "MCT: can't request IRQ %d (%d)\n", 5488c2ecf20Sopenharmony_ci mct_irqs[MCT_L0_IRQ], err); 5498c2ecf20Sopenharmony_ci } else { 5508c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 5518c2ecf20Sopenharmony_ci int mct_irq; 5528c2ecf20Sopenharmony_ci struct mct_clock_event_device *pcpu_mevt = 5538c2ecf20Sopenharmony_ci per_cpu_ptr(&percpu_mct_tick, cpu); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci pcpu_mevt->evt.irq = -1; 5568c2ecf20Sopenharmony_ci if (MCT_L0_IRQ + cpu >= ARRAY_SIZE(mct_irqs)) 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci mct_irq = mct_irqs[MCT_L0_IRQ + cpu]; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci irq_set_status_flags(mct_irq, IRQ_NOAUTOEN); 5618c2ecf20Sopenharmony_ci if (request_irq(mct_irq, 5628c2ecf20Sopenharmony_ci exynos4_mct_tick_isr, 5638c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_NOBALANCING, 5648c2ecf20Sopenharmony_ci pcpu_mevt->name, pcpu_mevt)) { 5658c2ecf20Sopenharmony_ci pr_err("exynos-mct: cannot register IRQ (cpu%d)\n", 5668c2ecf20Sopenharmony_ci cpu); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci continue; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci pcpu_mevt->evt.irq = mct_irq; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Install hotplug callbacks which configure the timer on this CPU */ 5758c2ecf20Sopenharmony_ci err = cpuhp_setup_state(CPUHP_AP_EXYNOS4_MCT_TIMER_STARTING, 5768c2ecf20Sopenharmony_ci "clockevents/exynos4/mct_timer:starting", 5778c2ecf20Sopenharmony_ci exynos4_mct_starting_cpu, 5788c2ecf20Sopenharmony_ci exynos4_mct_dying_cpu); 5798c2ecf20Sopenharmony_ci if (err) 5808c2ecf20Sopenharmony_ci goto out_irq; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciout_irq: 5858c2ecf20Sopenharmony_ci if (mct_int_type == MCT_INT_PPI) { 5868c2ecf20Sopenharmony_ci free_percpu_irq(mct_irqs[MCT_L0_IRQ], &percpu_mct_tick); 5878c2ecf20Sopenharmony_ci } else { 5888c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 5898c2ecf20Sopenharmony_ci struct mct_clock_event_device *pcpu_mevt = 5908c2ecf20Sopenharmony_ci per_cpu_ptr(&percpu_mct_tick, cpu); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (pcpu_mevt->evt.irq != -1) { 5938c2ecf20Sopenharmony_ci free_irq(pcpu_mevt->evt.irq, pcpu_mevt); 5948c2ecf20Sopenharmony_ci pcpu_mevt->evt.irq = -1; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci return err; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int __init mct_init_dt(struct device_node *np, unsigned int int_type) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci int ret; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci ret = exynos4_timer_resources(np); 6068c2ecf20Sopenharmony_ci if (ret) 6078c2ecf20Sopenharmony_ci return ret; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci ret = exynos4_timer_interrupts(np, int_type); 6108c2ecf20Sopenharmony_ci if (ret) 6118c2ecf20Sopenharmony_ci return ret; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci ret = exynos4_clocksource_init(); 6148c2ecf20Sopenharmony_ci if (ret) 6158c2ecf20Sopenharmony_ci return ret; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci return exynos4_clockevent_init(); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int __init mct_init_spi(struct device_node *np) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci return mct_init_dt(np, MCT_INT_SPI); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int __init mct_init_ppi(struct device_node *np) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci return mct_init_dt(np, MCT_INT_PPI); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi); 6318c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi); 632