18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale FlexTimer Module (FTM) timer driver. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 108c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 168c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/fsl/ftm.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define FTM_SC_CLK(c) ((c) << FTM_SC_CLK_MASK_SHIFT) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct ftm_clock_device { 238c2ecf20Sopenharmony_ci void __iomem *clksrc_base; 248c2ecf20Sopenharmony_ci void __iomem *clkevt_base; 258c2ecf20Sopenharmony_ci unsigned long periodic_cyc; 268c2ecf20Sopenharmony_ci unsigned long ps; 278c2ecf20Sopenharmony_ci bool big_endian; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct ftm_clock_device *priv; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic inline u32 ftm_readl(void __iomem *addr) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci if (priv->big_endian) 358c2ecf20Sopenharmony_ci return ioread32be(addr); 368c2ecf20Sopenharmony_ci else 378c2ecf20Sopenharmony_ci return ioread32(addr); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline void ftm_writel(u32 val, void __iomem *addr) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if (priv->big_endian) 438c2ecf20Sopenharmony_ci iowrite32be(val, addr); 448c2ecf20Sopenharmony_ci else 458c2ecf20Sopenharmony_ci iowrite32(val, addr); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic inline void ftm_counter_enable(void __iomem *base) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci u32 val; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* select and enable counter clock source */ 538c2ecf20Sopenharmony_ci val = ftm_readl(base + FTM_SC); 548c2ecf20Sopenharmony_ci val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); 558c2ecf20Sopenharmony_ci val |= priv->ps | FTM_SC_CLK(1); 568c2ecf20Sopenharmony_ci ftm_writel(val, base + FTM_SC); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline void ftm_counter_disable(void __iomem *base) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci u32 val; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* disable counter clock source */ 648c2ecf20Sopenharmony_ci val = ftm_readl(base + FTM_SC); 658c2ecf20Sopenharmony_ci val &= ~(FTM_SC_PS_MASK | FTM_SC_CLK_MASK); 668c2ecf20Sopenharmony_ci ftm_writel(val, base + FTM_SC); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline void ftm_irq_acknowledge(void __iomem *base) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 val; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci val = ftm_readl(base + FTM_SC); 748c2ecf20Sopenharmony_ci val &= ~FTM_SC_TOF; 758c2ecf20Sopenharmony_ci ftm_writel(val, base + FTM_SC); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic inline void ftm_irq_enable(void __iomem *base) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci u32 val; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci val = ftm_readl(base + FTM_SC); 838c2ecf20Sopenharmony_ci val |= FTM_SC_TOIE; 848c2ecf20Sopenharmony_ci ftm_writel(val, base + FTM_SC); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline void ftm_irq_disable(void __iomem *base) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci u32 val; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci val = ftm_readl(base + FTM_SC); 928c2ecf20Sopenharmony_ci val &= ~FTM_SC_TOIE; 938c2ecf20Sopenharmony_ci ftm_writel(val, base + FTM_SC); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline void ftm_reset_counter(void __iomem *base) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * The CNT register contains the FTM counter value. 1008c2ecf20Sopenharmony_ci * Reset clears the CNT register. Writing any value to COUNT 1018c2ecf20Sopenharmony_ci * updates the counter with its initial value, CNTIN. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci ftm_writel(0x00, base + FTM_CNT); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic u64 notrace ftm_read_sched_clock(void) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci return ftm_readl(priv->clksrc_base + FTM_CNT); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int ftm_set_next_event(unsigned long delta, 1128c2ecf20Sopenharmony_ci struct clock_event_device *unused) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * The CNNIN and MOD are all double buffer registers, writing 1168c2ecf20Sopenharmony_ci * to the MOD register latches the value into a buffer. The MOD 1178c2ecf20Sopenharmony_ci * register is updated with the value of its write buffer with 1188c2ecf20Sopenharmony_ci * the following scenario: 1198c2ecf20Sopenharmony_ci * a, the counter source clock is diabled. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_ci ftm_counter_disable(priv->clkevt_base); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Force the value of CNTIN to be loaded into the FTM counter */ 1248c2ecf20Sopenharmony_ci ftm_reset_counter(priv->clkevt_base); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* 1278c2ecf20Sopenharmony_ci * The counter increments until the value of MOD is reached, 1288c2ecf20Sopenharmony_ci * at which point the counter is reloaded with the value of CNTIN. 1298c2ecf20Sopenharmony_ci * The TOF (the overflow flag) bit is set when the FTM counter 1308c2ecf20Sopenharmony_ci * changes from MOD to CNTIN. So we should using the delta - 1. 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci ftm_writel(delta - 1, priv->clkevt_base + FTM_MOD); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ftm_counter_enable(priv->clkevt_base); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ftm_irq_enable(priv->clkevt_base); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int ftm_set_oneshot(struct clock_event_device *evt) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci ftm_counter_disable(priv->clkevt_base); 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int ftm_set_periodic(struct clock_event_device *evt) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci ftm_set_next_event(priv->periodic_cyc, evt); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic irqreturn_t ftm_evt_interrupt(int irq, void *dev_id) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ftm_irq_acknowledge(priv->clkevt_base); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (likely(clockevent_state_oneshot(evt))) { 1608c2ecf20Sopenharmony_ci ftm_irq_disable(priv->clkevt_base); 1618c2ecf20Sopenharmony_ci ftm_counter_disable(priv->clkevt_base); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci evt->event_handler(evt); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct clock_event_device ftm_clockevent = { 1708c2ecf20Sopenharmony_ci .name = "Freescale ftm timer", 1718c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 1728c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 1738c2ecf20Sopenharmony_ci .set_state_periodic = ftm_set_periodic, 1748c2ecf20Sopenharmony_ci .set_state_oneshot = ftm_set_oneshot, 1758c2ecf20Sopenharmony_ci .set_next_event = ftm_set_next_event, 1768c2ecf20Sopenharmony_ci .rating = 300, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int __init ftm_clockevent_init(unsigned long freq, int irq) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci int err; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ftm_writel(0x00, priv->clkevt_base + FTM_CNTIN); 1848c2ecf20Sopenharmony_ci ftm_writel(~0u, priv->clkevt_base + FTM_MOD); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ftm_reset_counter(priv->clkevt_base); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 1898c2ecf20Sopenharmony_ci "Freescale ftm timer", &ftm_clockevent); 1908c2ecf20Sopenharmony_ci if (err) { 1918c2ecf20Sopenharmony_ci pr_err("ftm: setup irq failed: %d\n", err); 1928c2ecf20Sopenharmony_ci return err; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ftm_clockevent.cpumask = cpumask_of(0); 1968c2ecf20Sopenharmony_ci ftm_clockevent.irq = irq; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci clockevents_config_and_register(&ftm_clockevent, 1998c2ecf20Sopenharmony_ci freq / (1 << priv->ps), 2008c2ecf20Sopenharmony_ci 1, 0xffff); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ftm_counter_enable(priv->clkevt_base); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int __init ftm_clocksource_init(unsigned long freq) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int err; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ftm_writel(0x00, priv->clksrc_base + FTM_CNTIN); 2128c2ecf20Sopenharmony_ci ftm_writel(~0u, priv->clksrc_base + FTM_MOD); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ftm_reset_counter(priv->clksrc_base); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci sched_clock_register(ftm_read_sched_clock, 16, freq / (1 << priv->ps)); 2178c2ecf20Sopenharmony_ci err = clocksource_mmio_init(priv->clksrc_base + FTM_CNT, "fsl-ftm", 2188c2ecf20Sopenharmony_ci freq / (1 << priv->ps), 300, 16, 2198c2ecf20Sopenharmony_ci clocksource_mmio_readl_up); 2208c2ecf20Sopenharmony_ci if (err) { 2218c2ecf20Sopenharmony_ci pr_err("ftm: init clock source mmio failed: %d\n", err); 2228c2ecf20Sopenharmony_ci return err; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ftm_counter_enable(priv->clksrc_base); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int __init __ftm_clk_init(struct device_node *np, char *cnt_name, 2318c2ecf20Sopenharmony_ci char *ftm_name) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct clk *clk; 2348c2ecf20Sopenharmony_ci int err; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci clk = of_clk_get_by_name(np, cnt_name); 2378c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 2388c2ecf20Sopenharmony_ci pr_err("ftm: Cannot get \"%s\": %ld\n", cnt_name, PTR_ERR(clk)); 2398c2ecf20Sopenharmony_ci return PTR_ERR(clk); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci err = clk_prepare_enable(clk); 2428c2ecf20Sopenharmony_ci if (err) { 2438c2ecf20Sopenharmony_ci pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", 2448c2ecf20Sopenharmony_ci cnt_name, err); 2458c2ecf20Sopenharmony_ci return err; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci clk = of_clk_get_by_name(np, ftm_name); 2498c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 2508c2ecf20Sopenharmony_ci pr_err("ftm: Cannot get \"%s\": %ld\n", ftm_name, PTR_ERR(clk)); 2518c2ecf20Sopenharmony_ci return PTR_ERR(clk); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci err = clk_prepare_enable(clk); 2548c2ecf20Sopenharmony_ci if (err) 2558c2ecf20Sopenharmony_ci pr_err("ftm: clock failed to prepare+enable \"%s\": %d\n", 2568c2ecf20Sopenharmony_ci ftm_name, err); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return clk_get_rate(clk); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic unsigned long __init ftm_clk_init(struct device_node *np) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci long freq; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci freq = __ftm_clk_init(np, "ftm-evt-counter-en", "ftm-evt"); 2668c2ecf20Sopenharmony_ci if (freq <= 0) 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci freq = __ftm_clk_init(np, "ftm-src-counter-en", "ftm-src"); 2708c2ecf20Sopenharmony_ci if (freq <= 0) 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return freq; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int __init ftm_calc_closest_round_cyc(unsigned long freq) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci priv->ps = 0; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* The counter register is only using the lower 16 bits, and 2818c2ecf20Sopenharmony_ci * if the 'freq' value is to big here, then the periodic_cyc 2828c2ecf20Sopenharmony_ci * may exceed 0xFFFF. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ci do { 2858c2ecf20Sopenharmony_ci priv->periodic_cyc = DIV_ROUND_CLOSEST(freq, 2868c2ecf20Sopenharmony_ci HZ * (1 << priv->ps++)); 2878c2ecf20Sopenharmony_ci } while (priv->periodic_cyc > 0xFFFF); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (priv->ps > FTM_PS_MAX) { 2908c2ecf20Sopenharmony_ci pr_err("ftm: the prescaler is %lu > %d\n", 2918c2ecf20Sopenharmony_ci priv->ps, FTM_PS_MAX); 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int __init ftm_timer_init(struct device_node *np) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci unsigned long freq; 3018c2ecf20Sopenharmony_ci int ret, irq; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 3048c2ecf20Sopenharmony_ci if (!priv) 3058c2ecf20Sopenharmony_ci return -ENOMEM; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret = -ENXIO; 3088c2ecf20Sopenharmony_ci priv->clkevt_base = of_iomap(np, 0); 3098c2ecf20Sopenharmony_ci if (!priv->clkevt_base) { 3108c2ecf20Sopenharmony_ci pr_err("ftm: unable to map event timer registers\n"); 3118c2ecf20Sopenharmony_ci goto err_clkevt; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci priv->clksrc_base = of_iomap(np, 1); 3158c2ecf20Sopenharmony_ci if (!priv->clksrc_base) { 3168c2ecf20Sopenharmony_ci pr_err("ftm: unable to map source timer registers\n"); 3178c2ecf20Sopenharmony_ci goto err_clksrc; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = -EINVAL; 3218c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 3228c2ecf20Sopenharmony_ci if (irq <= 0) { 3238c2ecf20Sopenharmony_ci pr_err("ftm: unable to get IRQ from DT, %d\n", irq); 3248c2ecf20Sopenharmony_ci goto err; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci priv->big_endian = of_property_read_bool(np, "big-endian"); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci freq = ftm_clk_init(np); 3308c2ecf20Sopenharmony_ci if (!freq) 3318c2ecf20Sopenharmony_ci goto err; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ret = ftm_calc_closest_round_cyc(freq); 3348c2ecf20Sopenharmony_ci if (ret) 3358c2ecf20Sopenharmony_ci goto err; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = ftm_clocksource_init(freq); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci goto err; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = ftm_clockevent_init(freq, irq); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci goto err; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cierr: 3488c2ecf20Sopenharmony_ci iounmap(priv->clksrc_base); 3498c2ecf20Sopenharmony_cierr_clksrc: 3508c2ecf20Sopenharmony_ci iounmap(priv->clkevt_base); 3518c2ecf20Sopenharmony_cierr_clkevt: 3528c2ecf20Sopenharmony_ci kfree(priv); 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init); 356