18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Rockchip timer support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) Daniel Lezcano <daniel.lezcano@linaro.org> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_address.h> 158c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define TIMER_NAME "rk_timer" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define TIMER_LOAD_COUNT0 0x00 208c2ecf20Sopenharmony_ci#define TIMER_LOAD_COUNT1 0x04 218c2ecf20Sopenharmony_ci#define TIMER_CURRENT_VALUE0 0x08 228c2ecf20Sopenharmony_ci#define TIMER_CURRENT_VALUE1 0x0C 238c2ecf20Sopenharmony_ci#define TIMER_CONTROL_REG3288 0x10 248c2ecf20Sopenharmony_ci#define TIMER_CONTROL_REG3399 0x1c 258c2ecf20Sopenharmony_ci#define TIMER_INT_STATUS 0x18 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define TIMER_DISABLE 0x0 288c2ecf20Sopenharmony_ci#define TIMER_ENABLE 0x1 298c2ecf20Sopenharmony_ci#define TIMER_MODE_FREE_RUNNING (0 << 1) 308c2ecf20Sopenharmony_ci#define TIMER_MODE_USER_DEFINED_COUNT (1 << 1) 318c2ecf20Sopenharmony_ci#define TIMER_INT_UNMASK (1 << 2) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct rk_timer { 348c2ecf20Sopenharmony_ci void __iomem *base; 358c2ecf20Sopenharmony_ci void __iomem *ctrl; 368c2ecf20Sopenharmony_ci struct clk *clk; 378c2ecf20Sopenharmony_ci struct clk *pclk; 388c2ecf20Sopenharmony_ci u32 freq; 398c2ecf20Sopenharmony_ci int irq; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct rk_clkevt { 438c2ecf20Sopenharmony_ci struct clock_event_device ce; 448c2ecf20Sopenharmony_ci struct rk_timer timer; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct rk_clkevt *rk_clkevt; 488c2ecf20Sopenharmony_cistatic struct rk_timer *rk_clksrc; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline struct rk_timer *rk_timer(struct clock_event_device *ce) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return &container_of(ce, struct rk_clkevt, ce)->timer; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline void rk_timer_disable(struct rk_timer *timer) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci writel_relaxed(TIMER_DISABLE, timer->ctrl); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline void rk_timer_enable(struct rk_timer *timer, u32 flags) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci writel_relaxed(TIMER_ENABLE | flags, timer->ctrl); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void rk_timer_update_counter(unsigned long cycles, 668c2ecf20Sopenharmony_ci struct rk_timer *timer) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0); 698c2ecf20Sopenharmony_ci writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void rk_timer_interrupt_clear(struct rk_timer *timer) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci writel_relaxed(1, timer->base + TIMER_INT_STATUS); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic inline int rk_timer_set_next_event(unsigned long cycles, 788c2ecf20Sopenharmony_ci struct clock_event_device *ce) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct rk_timer *timer = rk_timer(ce); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci rk_timer_disable(timer); 838c2ecf20Sopenharmony_ci rk_timer_update_counter(cycles, timer); 848c2ecf20Sopenharmony_ci rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT | 858c2ecf20Sopenharmony_ci TIMER_INT_UNMASK); 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int rk_timer_shutdown(struct clock_event_device *ce) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct rk_timer *timer = rk_timer(ce); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci rk_timer_disable(timer); 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int rk_timer_set_periodic(struct clock_event_device *ce) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct rk_timer *timer = rk_timer(ce); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci rk_timer_disable(timer); 1028c2ecf20Sopenharmony_ci rk_timer_update_counter(timer->freq / HZ - 1, timer); 1038c2ecf20Sopenharmony_ci rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK); 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic irqreturn_t rk_timer_interrupt(int irq, void *dev_id) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct clock_event_device *ce = dev_id; 1108c2ecf20Sopenharmony_ci struct rk_timer *timer = rk_timer(ce); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci rk_timer_interrupt_clear(timer); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (clockevent_state_oneshot(ce)) 1158c2ecf20Sopenharmony_ci rk_timer_disable(timer); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ce->event_handler(ce); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic u64 notrace rk_timer_sched_read(void) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int __init 1288c2ecf20Sopenharmony_cirk_timer_probe(struct rk_timer *timer, struct device_node *np) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct clk *timer_clk; 1318c2ecf20Sopenharmony_ci struct clk *pclk; 1328c2ecf20Sopenharmony_ci int ret = -EINVAL, irq; 1338c2ecf20Sopenharmony_ci u32 ctrl_reg = TIMER_CONTROL_REG3288; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci timer->base = of_iomap(np, 0); 1368c2ecf20Sopenharmony_ci if (!timer->base) { 1378c2ecf20Sopenharmony_ci pr_err("Failed to get base address for '%s'\n", TIMER_NAME); 1388c2ecf20Sopenharmony_ci return -ENXIO; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "rockchip,rk3399-timer")) 1428c2ecf20Sopenharmony_ci ctrl_reg = TIMER_CONTROL_REG3399; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci timer->ctrl = timer->base + ctrl_reg; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pclk = of_clk_get_by_name(np, "pclk"); 1478c2ecf20Sopenharmony_ci if (IS_ERR(pclk)) { 1488c2ecf20Sopenharmony_ci ret = PTR_ERR(pclk); 1498c2ecf20Sopenharmony_ci pr_err("Failed to get pclk for '%s'\n", TIMER_NAME); 1508c2ecf20Sopenharmony_ci goto out_unmap; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pclk); 1548c2ecf20Sopenharmony_ci if (ret) { 1558c2ecf20Sopenharmony_ci pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME); 1568c2ecf20Sopenharmony_ci goto out_unmap; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci timer->pclk = pclk; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci timer_clk = of_clk_get_by_name(np, "timer"); 1618c2ecf20Sopenharmony_ci if (IS_ERR(timer_clk)) { 1628c2ecf20Sopenharmony_ci ret = PTR_ERR(timer_clk); 1638c2ecf20Sopenharmony_ci pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME); 1648c2ecf20Sopenharmony_ci goto out_timer_clk; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ret = clk_prepare_enable(timer_clk); 1688c2ecf20Sopenharmony_ci if (ret) { 1698c2ecf20Sopenharmony_ci pr_err("Failed to enable timer clock\n"); 1708c2ecf20Sopenharmony_ci goto out_timer_clk; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci timer->clk = timer_clk; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci timer->freq = clk_get_rate(timer_clk); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 1778c2ecf20Sopenharmony_ci if (!irq) { 1788c2ecf20Sopenharmony_ci ret = -EINVAL; 1798c2ecf20Sopenharmony_ci pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME); 1808c2ecf20Sopenharmony_ci goto out_irq; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci timer->irq = irq; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci rk_timer_interrupt_clear(timer); 1858c2ecf20Sopenharmony_ci rk_timer_disable(timer); 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciout_irq: 1898c2ecf20Sopenharmony_ci clk_disable_unprepare(timer_clk); 1908c2ecf20Sopenharmony_ciout_timer_clk: 1918c2ecf20Sopenharmony_ci clk_disable_unprepare(pclk); 1928c2ecf20Sopenharmony_ciout_unmap: 1938c2ecf20Sopenharmony_ci iounmap(timer->base); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void __init rk_timer_cleanup(struct rk_timer *timer) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci clk_disable_unprepare(timer->clk); 2018c2ecf20Sopenharmony_ci clk_disable_unprepare(timer->pclk); 2028c2ecf20Sopenharmony_ci iounmap(timer->base); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int __init rk_clkevt_init(struct device_node *np) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct clock_event_device *ce; 2088c2ecf20Sopenharmony_ci int ret = -EINVAL; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci rk_clkevt = kzalloc(sizeof(struct rk_clkevt), GFP_KERNEL); 2118c2ecf20Sopenharmony_ci if (!rk_clkevt) { 2128c2ecf20Sopenharmony_ci ret = -ENOMEM; 2138c2ecf20Sopenharmony_ci goto out; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = rk_timer_probe(&rk_clkevt->timer, np); 2178c2ecf20Sopenharmony_ci if (ret) 2188c2ecf20Sopenharmony_ci goto out_probe; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ce = &rk_clkevt->ce; 2218c2ecf20Sopenharmony_ci ce->name = TIMER_NAME; 2228c2ecf20Sopenharmony_ci ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | 2238c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_DYNIRQ; 2248c2ecf20Sopenharmony_ci ce->set_next_event = rk_timer_set_next_event; 2258c2ecf20Sopenharmony_ci ce->set_state_shutdown = rk_timer_shutdown; 2268c2ecf20Sopenharmony_ci ce->set_state_periodic = rk_timer_set_periodic; 2278c2ecf20Sopenharmony_ci ce->irq = rk_clkevt->timer.irq; 2288c2ecf20Sopenharmony_ci ce->cpumask = cpu_possible_mask; 2298c2ecf20Sopenharmony_ci ce->rating = 250; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ret = request_irq(rk_clkevt->timer.irq, rk_timer_interrupt, IRQF_TIMER, 2328c2ecf20Sopenharmony_ci TIMER_NAME, ce); 2338c2ecf20Sopenharmony_ci if (ret) { 2348c2ecf20Sopenharmony_ci pr_err("Failed to initialize '%s': %d\n", 2358c2ecf20Sopenharmony_ci TIMER_NAME, ret); 2368c2ecf20Sopenharmony_ci goto out_irq; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci clockevents_config_and_register(&rk_clkevt->ce, 2408c2ecf20Sopenharmony_ci rk_clkevt->timer.freq, 1, UINT_MAX); 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciout_irq: 2448c2ecf20Sopenharmony_ci rk_timer_cleanup(&rk_clkevt->timer); 2458c2ecf20Sopenharmony_ciout_probe: 2468c2ecf20Sopenharmony_ci kfree(rk_clkevt); 2478c2ecf20Sopenharmony_ciout: 2488c2ecf20Sopenharmony_ci /* Leave rk_clkevt not NULL to prevent future init */ 2498c2ecf20Sopenharmony_ci rk_clkevt = ERR_PTR(ret); 2508c2ecf20Sopenharmony_ci return ret; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int __init rk_clksrc_init(struct device_node *np) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci int ret = -EINVAL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci rk_clksrc = kzalloc(sizeof(struct rk_timer), GFP_KERNEL); 2588c2ecf20Sopenharmony_ci if (!rk_clksrc) { 2598c2ecf20Sopenharmony_ci ret = -ENOMEM; 2608c2ecf20Sopenharmony_ci goto out; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = rk_timer_probe(rk_clksrc, np); 2648c2ecf20Sopenharmony_ci if (ret) 2658c2ecf20Sopenharmony_ci goto out_probe; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci rk_timer_update_counter(UINT_MAX, rk_clksrc); 2688c2ecf20Sopenharmony_ci rk_timer_enable(rk_clksrc, 0); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci ret = clocksource_mmio_init(rk_clksrc->base + TIMER_CURRENT_VALUE0, 2718c2ecf20Sopenharmony_ci TIMER_NAME, rk_clksrc->freq, 250, 32, 2728c2ecf20Sopenharmony_ci clocksource_mmio_readl_down); 2738c2ecf20Sopenharmony_ci if (ret) { 2748c2ecf20Sopenharmony_ci pr_err("Failed to register clocksource\n"); 2758c2ecf20Sopenharmony_ci goto out_clocksource; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci sched_clock_register(rk_timer_sched_read, 32, rk_clksrc->freq); 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ciout_clocksource: 2828c2ecf20Sopenharmony_ci rk_timer_cleanup(rk_clksrc); 2838c2ecf20Sopenharmony_ciout_probe: 2848c2ecf20Sopenharmony_ci kfree(rk_clksrc); 2858c2ecf20Sopenharmony_ciout: 2868c2ecf20Sopenharmony_ci /* Leave rk_clksrc not NULL to prevent future init */ 2878c2ecf20Sopenharmony_ci rk_clksrc = ERR_PTR(ret); 2888c2ecf20Sopenharmony_ci return ret; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int __init rk_timer_init(struct device_node *np) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci if (!rk_clkevt) 2948c2ecf20Sopenharmony_ci return rk_clkevt_init(np); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!rk_clksrc) 2978c2ecf20Sopenharmony_ci return rk_clksrc_init(np); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci pr_err("Too many timer definitions for '%s'\n", TIMER_NAME); 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", rk_timer_init); 3048c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", rk_timer_init); 305