18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 ARM Limited 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Vladimir Murzin <vladimir.murzin@arm.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 128c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/irq.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 208c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define TIMER_CTRL 0x0 248c2ecf20Sopenharmony_ci#define TIMER_CTRL_ENABLE BIT(0) 258c2ecf20Sopenharmony_ci#define TIMER_CTRL_IE BIT(3) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define TIMER_VALUE 0x4 288c2ecf20Sopenharmony_ci#define TIMER_RELOAD 0x8 298c2ecf20Sopenharmony_ci#define TIMER_INT 0xc 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct clockevent_mps2 { 328c2ecf20Sopenharmony_ci void __iomem *reg; 338c2ecf20Sopenharmony_ci u32 clock_count_per_tick; 348c2ecf20Sopenharmony_ci struct clock_event_device clkevt; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void __iomem *sched_clock_base; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic u64 notrace mps2_sched_read(void) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci return ~readl_relaxed(sched_clock_base + TIMER_VALUE); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return container_of(c, struct clockevent_mps2, clkevt); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci writel_relaxed(val, to_mps2_clkevt(c)->reg + offset); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int mps2_timer_shutdown(struct clock_event_device *ce) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci clockevent_mps2_writel(0, ce, TIMER_RELOAD); 578c2ecf20Sopenharmony_ci clockevent_mps2_writel(0, ce, TIMER_CTRL); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci clockevent_mps2_writel(next, ce, TIMER_VALUE); 658c2ecf20Sopenharmony_ci clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int mps2_timer_set_periodic(struct clock_event_device *ce) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD); 758c2ecf20Sopenharmony_ci clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE); 768c2ecf20Sopenharmony_ci clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic irqreturn_t mps2_timer_interrupt(int irq, void *dev_id) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct clockevent_mps2 *ce = dev_id; 848c2ecf20Sopenharmony_ci u32 status = readl_relaxed(ce->reg + TIMER_INT); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!status) { 878c2ecf20Sopenharmony_ci pr_warn("spurious interrupt\n"); 888c2ecf20Sopenharmony_ci return IRQ_NONE; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci writel_relaxed(1, ce->reg + TIMER_INT); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ce->clkevt.event_handler(&ce->clkevt); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int __init mps2_clockevent_init(struct device_node *np) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci void __iomem *base; 1018c2ecf20Sopenharmony_ci struct clk *clk = NULL; 1028c2ecf20Sopenharmony_ci struct clockevent_mps2 *ce; 1038c2ecf20Sopenharmony_ci u32 rate; 1048c2ecf20Sopenharmony_ci int irq, ret; 1058c2ecf20Sopenharmony_ci const char *name = "mps2-clkevt"; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "clock-frequency", &rate); 1088c2ecf20Sopenharmony_ci if (ret) { 1098c2ecf20Sopenharmony_ci clk = of_clk_get(np, 0); 1108c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1118c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 1128c2ecf20Sopenharmony_ci pr_err("failed to get clock for clockevent: %d\n", ret); 1138c2ecf20Sopenharmony_ci goto out; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 1178c2ecf20Sopenharmony_ci if (ret) { 1188c2ecf20Sopenharmony_ci pr_err("failed to enable clock for clockevent: %d\n", ret); 1198c2ecf20Sopenharmony_ci goto out_clk_put; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 1268c2ecf20Sopenharmony_ci if (!base) { 1278c2ecf20Sopenharmony_ci ret = -EADDRNOTAVAIL; 1288c2ecf20Sopenharmony_ci pr_err("failed to map register for clockevent: %d\n", ret); 1298c2ecf20Sopenharmony_ci goto out_clk_disable; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 1338c2ecf20Sopenharmony_ci if (!irq) { 1348c2ecf20Sopenharmony_ci ret = -ENOENT; 1358c2ecf20Sopenharmony_ci pr_err("failed to get irq for clockevent: %d\n", ret); 1368c2ecf20Sopenharmony_ci goto out_iounmap; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ce = kzalloc(sizeof(*ce), GFP_KERNEL); 1408c2ecf20Sopenharmony_ci if (!ce) { 1418c2ecf20Sopenharmony_ci ret = -ENOMEM; 1428c2ecf20Sopenharmony_ci goto out_iounmap; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ce->reg = base; 1468c2ecf20Sopenharmony_ci ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ); 1478c2ecf20Sopenharmony_ci ce->clkevt.irq = irq; 1488c2ecf20Sopenharmony_ci ce->clkevt.name = name; 1498c2ecf20Sopenharmony_ci ce->clkevt.rating = 200; 1508c2ecf20Sopenharmony_ci ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; 1518c2ecf20Sopenharmony_ci ce->clkevt.cpumask = cpu_possible_mask; 1528c2ecf20Sopenharmony_ci ce->clkevt.set_state_shutdown = mps2_timer_shutdown; 1538c2ecf20Sopenharmony_ci ce->clkevt.set_state_periodic = mps2_timer_set_periodic; 1548c2ecf20Sopenharmony_ci ce->clkevt.set_state_oneshot = mps2_timer_shutdown; 1558c2ecf20Sopenharmony_ci ce->clkevt.set_next_event = mps2_timer_set_next_event; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Ensure timer is disabled */ 1588c2ecf20Sopenharmony_ci writel_relaxed(0, base + TIMER_CTRL); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce); 1618c2ecf20Sopenharmony_ci if (ret) { 1628c2ecf20Sopenharmony_ci pr_err("failed to request irq for clockevent: %d\n", ret); 1638c2ecf20Sopenharmony_ci goto out_kfree; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciout_kfree: 1718c2ecf20Sopenharmony_ci kfree(ce); 1728c2ecf20Sopenharmony_ciout_iounmap: 1738c2ecf20Sopenharmony_ci iounmap(base); 1748c2ecf20Sopenharmony_ciout_clk_disable: 1758c2ecf20Sopenharmony_ci /* clk_{disable, unprepare, put}() can handle NULL as a parameter */ 1768c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 1778c2ecf20Sopenharmony_ciout_clk_put: 1788c2ecf20Sopenharmony_ci clk_put(clk); 1798c2ecf20Sopenharmony_ciout: 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int __init mps2_clocksource_init(struct device_node *np) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci void __iomem *base; 1868c2ecf20Sopenharmony_ci struct clk *clk = NULL; 1878c2ecf20Sopenharmony_ci u32 rate; 1888c2ecf20Sopenharmony_ci int ret; 1898c2ecf20Sopenharmony_ci const char *name = "mps2-clksrc"; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "clock-frequency", &rate); 1928c2ecf20Sopenharmony_ci if (ret) { 1938c2ecf20Sopenharmony_ci clk = of_clk_get(np, 0); 1948c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1958c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 1968c2ecf20Sopenharmony_ci pr_err("failed to get clock for clocksource: %d\n", ret); 1978c2ecf20Sopenharmony_ci goto out; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 2018c2ecf20Sopenharmony_ci if (ret) { 2028c2ecf20Sopenharmony_ci pr_err("failed to enable clock for clocksource: %d\n", ret); 2038c2ecf20Sopenharmony_ci goto out_clk_put; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 2108c2ecf20Sopenharmony_ci if (!base) { 2118c2ecf20Sopenharmony_ci ret = -EADDRNOTAVAIL; 2128c2ecf20Sopenharmony_ci pr_err("failed to map register for clocksource: %d\n", ret); 2138c2ecf20Sopenharmony_ci goto out_clk_disable; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Ensure timer is disabled */ 2178c2ecf20Sopenharmony_ci writel_relaxed(0, base + TIMER_CTRL); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* ... and set it up as free-running clocksource */ 2208c2ecf20Sopenharmony_ci writel_relaxed(0xffffffff, base + TIMER_VALUE); 2218c2ecf20Sopenharmony_ci writel_relaxed(0xffffffff, base + TIMER_RELOAD); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci writel_relaxed(TIMER_CTRL_ENABLE, base + TIMER_CTRL); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = clocksource_mmio_init(base + TIMER_VALUE, name, 2268c2ecf20Sopenharmony_ci rate, 200, 32, 2278c2ecf20Sopenharmony_ci clocksource_mmio_readl_down); 2288c2ecf20Sopenharmony_ci if (ret) { 2298c2ecf20Sopenharmony_ci pr_err("failed to init clocksource: %d\n", ret); 2308c2ecf20Sopenharmony_ci goto out_iounmap; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci sched_clock_base = base; 2348c2ecf20Sopenharmony_ci sched_clock_register(mps2_sched_read, 32, rate); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciout_iounmap: 2398c2ecf20Sopenharmony_ci iounmap(base); 2408c2ecf20Sopenharmony_ciout_clk_disable: 2418c2ecf20Sopenharmony_ci /* clk_{disable, unprepare, put}() can handle NULL as a parameter */ 2428c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 2438c2ecf20Sopenharmony_ciout_clk_put: 2448c2ecf20Sopenharmony_ci clk_put(clk); 2458c2ecf20Sopenharmony_ciout: 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int __init mps2_timer_init(struct device_node *np) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci static int has_clocksource, has_clockevent; 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!has_clocksource) { 2558c2ecf20Sopenharmony_ci ret = mps2_clocksource_init(np); 2568c2ecf20Sopenharmony_ci if (!ret) { 2578c2ecf20Sopenharmony_ci has_clocksource = 1; 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (!has_clockevent) { 2638c2ecf20Sopenharmony_ci ret = mps2_clockevent_init(np); 2648c2ecf20Sopenharmony_ci if (!ret) { 2658c2ecf20Sopenharmony_ci has_clockevent = 1; 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init); 274