18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Allwinner SoCs hstimer driver. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013 Maxime Ripard 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 98c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 108c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 158c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/irq.h> 198c2ecf20Sopenharmony_ci#include <linux/irqreturn.h> 208c2ecf20Sopenharmony_ci#include <linux/reset.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/of_address.h> 248c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define TIMER_IRQ_EN_REG 0x00 278c2ecf20Sopenharmony_ci#define TIMER_IRQ_EN(val) BIT(val) 288c2ecf20Sopenharmony_ci#define TIMER_IRQ_ST_REG 0x04 298c2ecf20Sopenharmony_ci#define TIMER_CTL_REG(val) (0x20 * (val) + 0x10) 308c2ecf20Sopenharmony_ci#define TIMER_CTL_ENABLE BIT(0) 318c2ecf20Sopenharmony_ci#define TIMER_CTL_RELOAD BIT(1) 328c2ecf20Sopenharmony_ci#define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) 338c2ecf20Sopenharmony_ci#define TIMER_CTL_ONESHOT BIT(7) 348c2ecf20Sopenharmony_ci#define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14) 358c2ecf20Sopenharmony_ci#define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18) 368c2ecf20Sopenharmony_ci#define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c) 378c2ecf20Sopenharmony_ci#define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define TIMER_SYNC_TICKS 3 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct sun5i_timer { 428c2ecf20Sopenharmony_ci void __iomem *base; 438c2ecf20Sopenharmony_ci struct clk *clk; 448c2ecf20Sopenharmony_ci struct notifier_block clk_rate_cb; 458c2ecf20Sopenharmony_ci u32 ticks_per_jiffy; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define to_sun5i_timer(x) \ 498c2ecf20Sopenharmony_ci container_of(x, struct sun5i_timer, clk_rate_cb) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct sun5i_timer_clksrc { 528c2ecf20Sopenharmony_ci struct sun5i_timer timer; 538c2ecf20Sopenharmony_ci struct clocksource clksrc; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define to_sun5i_timer_clksrc(x) \ 578c2ecf20Sopenharmony_ci container_of(x, struct sun5i_timer_clksrc, clksrc) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct sun5i_timer_clkevt { 608c2ecf20Sopenharmony_ci struct sun5i_timer timer; 618c2ecf20Sopenharmony_ci struct clock_event_device clkevt; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define to_sun5i_timer_clkevt(x) \ 658c2ecf20Sopenharmony_ci container_of(x, struct sun5i_timer_clkevt, clkevt) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * When we disable a timer, we need to wait at least for 2 cycles of 698c2ecf20Sopenharmony_ci * the timer source clock. We will use for that the clocksource timer 708c2ecf20Sopenharmony_ci * that is already setup and runs at the same frequency than the other 718c2ecf20Sopenharmony_ci * timers, and we never will be disabled. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic void sun5i_clkevt_sync(struct sun5i_timer_clkevt *ce) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci u32 old = readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1)); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci while ((old - readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) 788c2ecf20Sopenharmony_ci cpu_relax(); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void sun5i_clkevt_time_stop(struct sun5i_timer_clkevt *ce, u8 timer) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer)); 848c2ecf20Sopenharmony_ci writel(val & ~TIMER_CTL_ENABLE, ce->timer.base + TIMER_CTL_REG(timer)); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci sun5i_clkevt_sync(ce); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void sun5i_clkevt_time_setup(struct sun5i_timer_clkevt *ce, u8 timer, u32 delay) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci writel(delay, ce->timer.base + TIMER_INTVAL_LO_REG(timer)); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, bool periodic) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer)); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (periodic) 998c2ecf20Sopenharmony_ci val &= ~TIMER_CTL_ONESHOT; 1008c2ecf20Sopenharmony_ci else 1018c2ecf20Sopenharmony_ci val |= TIMER_CTL_ONESHOT; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, 1048c2ecf20Sopenharmony_ci ce->timer.base + TIMER_CTL_REG(timer)); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 1208c2ecf20Sopenharmony_ci sun5i_clkevt_time_start(ce, 0, false); 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 1298c2ecf20Sopenharmony_ci sun5i_clkevt_time_setup(ce, 0, ce->timer.ticks_per_jiffy); 1308c2ecf20Sopenharmony_ci sun5i_clkevt_time_start(ce, 0, true); 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int sun5i_clkevt_next_event(unsigned long evt, 1358c2ecf20Sopenharmony_ci struct clock_event_device *clkevt) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci sun5i_clkevt_time_stop(ce, 0); 1408c2ecf20Sopenharmony_ci sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS); 1418c2ecf20Sopenharmony_ci sun5i_clkevt_time_start(ce, 0, false); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG); 1518c2ecf20Sopenharmony_ci ce->clkevt.event_handler(&ce->clkevt); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic u64 sun5i_clksrc_read(struct clocksource *clksrc) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct sun5i_timer_clksrc *cs = to_sun5i_timer_clksrc(clksrc); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1)); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int sun5i_rate_cb_clksrc(struct notifier_block *nb, 1648c2ecf20Sopenharmony_ci unsigned long event, void *data) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct clk_notifier_data *ndata = data; 1678c2ecf20Sopenharmony_ci struct sun5i_timer *timer = to_sun5i_timer(nb); 1688c2ecf20Sopenharmony_ci struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci switch (event) { 1718c2ecf20Sopenharmony_ci case PRE_RATE_CHANGE: 1728c2ecf20Sopenharmony_ci clocksource_unregister(&cs->clksrc); 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci case POST_RATE_CHANGE: 1768c2ecf20Sopenharmony_ci clocksource_register_hz(&cs->clksrc, ndata->new_rate); 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci default: 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return NOTIFY_DONE; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int __init sun5i_setup_clocksource(struct device_node *node, 1878c2ecf20Sopenharmony_ci void __iomem *base, 1888c2ecf20Sopenharmony_ci struct clk *clk, int irq) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct sun5i_timer_clksrc *cs; 1918c2ecf20Sopenharmony_ci unsigned long rate; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci cs = kzalloc(sizeof(*cs), GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (!cs) 1968c2ecf20Sopenharmony_ci return -ENOMEM; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 1998c2ecf20Sopenharmony_ci if (ret) { 2008c2ecf20Sopenharmony_ci pr_err("Couldn't enable parent clock\n"); 2018c2ecf20Sopenharmony_ci goto err_free; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 2058c2ecf20Sopenharmony_ci if (!rate) { 2068c2ecf20Sopenharmony_ci pr_err("Couldn't get parent clock rate\n"); 2078c2ecf20Sopenharmony_ci ret = -EINVAL; 2088c2ecf20Sopenharmony_ci goto err_disable_clk; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci cs->timer.base = base; 2128c2ecf20Sopenharmony_ci cs->timer.clk = clk; 2138c2ecf20Sopenharmony_ci cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc; 2148c2ecf20Sopenharmony_ci cs->timer.clk_rate_cb.next = NULL; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb); 2178c2ecf20Sopenharmony_ci if (ret) { 2188c2ecf20Sopenharmony_ci pr_err("Unable to register clock notifier.\n"); 2198c2ecf20Sopenharmony_ci goto err_disable_clk; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci writel(~0, base + TIMER_INTVAL_LO_REG(1)); 2238c2ecf20Sopenharmony_ci writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, 2248c2ecf20Sopenharmony_ci base + TIMER_CTL_REG(1)); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci cs->clksrc.name = node->name; 2278c2ecf20Sopenharmony_ci cs->clksrc.rating = 340; 2288c2ecf20Sopenharmony_ci cs->clksrc.read = sun5i_clksrc_read; 2298c2ecf20Sopenharmony_ci cs->clksrc.mask = CLOCKSOURCE_MASK(32); 2308c2ecf20Sopenharmony_ci cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = clocksource_register_hz(&cs->clksrc, rate); 2338c2ecf20Sopenharmony_ci if (ret) { 2348c2ecf20Sopenharmony_ci pr_err("Couldn't register clock source.\n"); 2358c2ecf20Sopenharmony_ci goto err_remove_notifier; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cierr_remove_notifier: 2418c2ecf20Sopenharmony_ci clk_notifier_unregister(clk, &cs->timer.clk_rate_cb); 2428c2ecf20Sopenharmony_cierr_disable_clk: 2438c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 2448c2ecf20Sopenharmony_cierr_free: 2458c2ecf20Sopenharmony_ci kfree(cs); 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int sun5i_rate_cb_clkevt(struct notifier_block *nb, 2508c2ecf20Sopenharmony_ci unsigned long event, void *data) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct clk_notifier_data *ndata = data; 2538c2ecf20Sopenharmony_ci struct sun5i_timer *timer = to_sun5i_timer(nb); 2548c2ecf20Sopenharmony_ci struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (event == POST_RATE_CHANGE) { 2578c2ecf20Sopenharmony_ci clockevents_update_freq(&ce->clkevt, ndata->new_rate); 2588c2ecf20Sopenharmony_ci ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base, 2658c2ecf20Sopenharmony_ci struct clk *clk, int irq) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct sun5i_timer_clkevt *ce; 2688c2ecf20Sopenharmony_ci unsigned long rate; 2698c2ecf20Sopenharmony_ci int ret; 2708c2ecf20Sopenharmony_ci u32 val; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ce = kzalloc(sizeof(*ce), GFP_KERNEL); 2738c2ecf20Sopenharmony_ci if (!ce) 2748c2ecf20Sopenharmony_ci return -ENOMEM; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 2778c2ecf20Sopenharmony_ci if (ret) { 2788c2ecf20Sopenharmony_ci pr_err("Couldn't enable parent clock\n"); 2798c2ecf20Sopenharmony_ci goto err_free; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 2838c2ecf20Sopenharmony_ci if (!rate) { 2848c2ecf20Sopenharmony_ci pr_err("Couldn't get parent clock rate\n"); 2858c2ecf20Sopenharmony_ci ret = -EINVAL; 2868c2ecf20Sopenharmony_ci goto err_disable_clk; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ce->timer.base = base; 2908c2ecf20Sopenharmony_ci ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); 2918c2ecf20Sopenharmony_ci ce->timer.clk = clk; 2928c2ecf20Sopenharmony_ci ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt; 2938c2ecf20Sopenharmony_ci ce->timer.clk_rate_cb.next = NULL; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb); 2968c2ecf20Sopenharmony_ci if (ret) { 2978c2ecf20Sopenharmony_ci pr_err("Unable to register clock notifier.\n"); 2988c2ecf20Sopenharmony_ci goto err_disable_clk; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ce->clkevt.name = node->name; 3028c2ecf20Sopenharmony_ci ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 3038c2ecf20Sopenharmony_ci ce->clkevt.set_next_event = sun5i_clkevt_next_event; 3048c2ecf20Sopenharmony_ci ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown; 3058c2ecf20Sopenharmony_ci ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic; 3068c2ecf20Sopenharmony_ci ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot; 3078c2ecf20Sopenharmony_ci ce->clkevt.tick_resume = sun5i_clkevt_shutdown; 3088c2ecf20Sopenharmony_ci ce->clkevt.rating = 340; 3098c2ecf20Sopenharmony_ci ce->clkevt.irq = irq; 3108c2ecf20Sopenharmony_ci ce->clkevt.cpumask = cpu_possible_mask; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Enable timer0 interrupt */ 3138c2ecf20Sopenharmony_ci val = readl(base + TIMER_IRQ_EN_REG); 3148c2ecf20Sopenharmony_ci writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci clockevents_config_and_register(&ce->clkevt, rate, 3178c2ecf20Sopenharmony_ci TIMER_SYNC_TICKS, 0xffffffff); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 3208c2ecf20Sopenharmony_ci "sun5i_timer0", ce); 3218c2ecf20Sopenharmony_ci if (ret) { 3228c2ecf20Sopenharmony_ci pr_err("Unable to register interrupt\n"); 3238c2ecf20Sopenharmony_ci goto err_remove_notifier; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cierr_remove_notifier: 3298c2ecf20Sopenharmony_ci clk_notifier_unregister(clk, &ce->timer.clk_rate_cb); 3308c2ecf20Sopenharmony_cierr_disable_clk: 3318c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 3328c2ecf20Sopenharmony_cierr_free: 3338c2ecf20Sopenharmony_ci kfree(ce); 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int __init sun5i_timer_init(struct device_node *node) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct reset_control *rstc; 3408c2ecf20Sopenharmony_ci void __iomem *timer_base; 3418c2ecf20Sopenharmony_ci struct clk *clk; 3428c2ecf20Sopenharmony_ci int irq, ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci timer_base = of_io_request_and_map(node, 0, of_node_full_name(node)); 3458c2ecf20Sopenharmony_ci if (IS_ERR(timer_base)) { 3468c2ecf20Sopenharmony_ci pr_err("Can't map registers\n"); 3478c2ecf20Sopenharmony_ci return PTR_ERR(timer_base); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 3518c2ecf20Sopenharmony_ci if (irq <= 0) { 3528c2ecf20Sopenharmony_ci pr_err("Can't parse IRQ\n"); 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci clk = of_clk_get(node, 0); 3578c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 3588c2ecf20Sopenharmony_ci pr_err("Can't get timer clock\n"); 3598c2ecf20Sopenharmony_ci return PTR_ERR(clk); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci rstc = of_reset_control_get(node, NULL); 3638c2ecf20Sopenharmony_ci if (!IS_ERR(rstc)) 3648c2ecf20Sopenharmony_ci reset_control_deassert(rstc); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = sun5i_setup_clocksource(node, timer_base, clk, irq); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return sun5i_setup_clockevent(node, timer_base, clk, irq); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer", 3738c2ecf20Sopenharmony_ci sun5i_timer_init); 3748c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer", 3758c2ecf20Sopenharmony_ci sun5i_timer_init); 376