18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/clocksource/timer-oxnas-rps.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Oxford Semiconductor Ltd 68c2ecf20Sopenharmony_ci * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com> 78c2ecf20Sopenharmony_ci * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 218c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* TIMER1 used as tick 248c2ecf20Sopenharmony_ci * TIMER2 used as clocksource 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Registers definitions */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define TIMER_LOAD_REG 0x0 308c2ecf20Sopenharmony_ci#define TIMER_CURR_REG 0x4 318c2ecf20Sopenharmony_ci#define TIMER_CTRL_REG 0x8 328c2ecf20Sopenharmony_ci#define TIMER_CLRINT_REG 0xC 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define TIMER_BITS 24 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define TIMER_MAX_VAL (BIT(TIMER_BITS) - 1) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define TIMER_PERIODIC BIT(6) 398c2ecf20Sopenharmony_ci#define TIMER_ENABLE BIT(7) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define TIMER_DIV1 (0) 428c2ecf20Sopenharmony_ci#define TIMER_DIV16 (1 << 2) 438c2ecf20Sopenharmony_ci#define TIMER_DIV256 (2 << 2) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define TIMER1_REG_OFFSET 0 468c2ecf20Sopenharmony_ci#define TIMER2_REG_OFFSET 0x20 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* Clockevent & Clocksource data */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct oxnas_rps_timer { 518c2ecf20Sopenharmony_ci struct clock_event_device clkevent; 528c2ecf20Sopenharmony_ci void __iomem *clksrc_base; 538c2ecf20Sopenharmony_ci void __iomem *clkevt_base; 548c2ecf20Sopenharmony_ci unsigned long timer_period; 558c2ecf20Sopenharmony_ci unsigned int timer_prescaler; 568c2ecf20Sopenharmony_ci struct clk *clk; 578c2ecf20Sopenharmony_ci int irq; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic irqreturn_t oxnas_rps_timer_irq(int irq, void *dev_id) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct oxnas_rps_timer *rps = dev_id; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci writel_relaxed(0, rps->clkevt_base + TIMER_CLRINT_REG); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci rps->clkevent.event_handler(&rps->clkevent); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return IRQ_HANDLED; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void oxnas_rps_timer_config(struct oxnas_rps_timer *rps, 728c2ecf20Sopenharmony_ci unsigned long period, 738c2ecf20Sopenharmony_ci unsigned int periodic) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci uint32_t cfg = rps->timer_prescaler; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (period) 788c2ecf20Sopenharmony_ci cfg |= TIMER_ENABLE; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (periodic) 818c2ecf20Sopenharmony_ci cfg |= TIMER_PERIODIC; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci writel_relaxed(period, rps->clkevt_base + TIMER_LOAD_REG); 848c2ecf20Sopenharmony_ci writel_relaxed(cfg, rps->clkevt_base + TIMER_CTRL_REG); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int oxnas_rps_timer_shutdown(struct clock_event_device *evt) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct oxnas_rps_timer *rps = 908c2ecf20Sopenharmony_ci container_of(evt, struct oxnas_rps_timer, clkevent); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci oxnas_rps_timer_config(rps, 0, 0); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic int oxnas_rps_timer_set_periodic(struct clock_event_device *evt) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct oxnas_rps_timer *rps = 1008c2ecf20Sopenharmony_ci container_of(evt, struct oxnas_rps_timer, clkevent); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci oxnas_rps_timer_config(rps, rps->timer_period, 1); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int oxnas_rps_timer_set_oneshot(struct clock_event_device *evt) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct oxnas_rps_timer *rps = 1108c2ecf20Sopenharmony_ci container_of(evt, struct oxnas_rps_timer, clkevent); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci oxnas_rps_timer_config(rps, rps->timer_period, 0); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int oxnas_rps_timer_next_event(unsigned long delta, 1188c2ecf20Sopenharmony_ci struct clock_event_device *evt) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct oxnas_rps_timer *rps = 1218c2ecf20Sopenharmony_ci container_of(evt, struct oxnas_rps_timer, clkevent); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci oxnas_rps_timer_config(rps, delta, 0); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int __init oxnas_rps_clockevent_init(struct oxnas_rps_timer *rps) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci ulong clk_rate = clk_get_rate(rps->clk); 1318c2ecf20Sopenharmony_ci ulong timer_rate; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* Start with prescaler 1 */ 1348c2ecf20Sopenharmony_ci rps->timer_prescaler = TIMER_DIV1; 1358c2ecf20Sopenharmony_ci rps->timer_period = DIV_ROUND_UP(clk_rate, HZ); 1368c2ecf20Sopenharmony_ci timer_rate = clk_rate; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (rps->timer_period > TIMER_MAX_VAL) { 1398c2ecf20Sopenharmony_ci rps->timer_prescaler = TIMER_DIV16; 1408c2ecf20Sopenharmony_ci timer_rate = clk_rate / 16; 1418c2ecf20Sopenharmony_ci rps->timer_period = DIV_ROUND_UP(timer_rate, HZ); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci if (rps->timer_period > TIMER_MAX_VAL) { 1448c2ecf20Sopenharmony_ci rps->timer_prescaler = TIMER_DIV256; 1458c2ecf20Sopenharmony_ci timer_rate = clk_rate / 256; 1468c2ecf20Sopenharmony_ci rps->timer_period = DIV_ROUND_UP(timer_rate, HZ); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci rps->clkevent.name = "oxnas-rps"; 1508c2ecf20Sopenharmony_ci rps->clkevent.features = CLOCK_EVT_FEAT_PERIODIC | 1518c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT | 1528c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_DYNIRQ; 1538c2ecf20Sopenharmony_ci rps->clkevent.tick_resume = oxnas_rps_timer_shutdown; 1548c2ecf20Sopenharmony_ci rps->clkevent.set_state_shutdown = oxnas_rps_timer_shutdown; 1558c2ecf20Sopenharmony_ci rps->clkevent.set_state_periodic = oxnas_rps_timer_set_periodic; 1568c2ecf20Sopenharmony_ci rps->clkevent.set_state_oneshot = oxnas_rps_timer_set_oneshot; 1578c2ecf20Sopenharmony_ci rps->clkevent.set_next_event = oxnas_rps_timer_next_event; 1588c2ecf20Sopenharmony_ci rps->clkevent.rating = 200; 1598c2ecf20Sopenharmony_ci rps->clkevent.cpumask = cpu_possible_mask; 1608c2ecf20Sopenharmony_ci rps->clkevent.irq = rps->irq; 1618c2ecf20Sopenharmony_ci clockevents_config_and_register(&rps->clkevent, 1628c2ecf20Sopenharmony_ci timer_rate, 1638c2ecf20Sopenharmony_ci 1, 1648c2ecf20Sopenharmony_ci TIMER_MAX_VAL); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci pr_info("Registered clock event rate %luHz prescaler %x period %lu\n", 1678c2ecf20Sopenharmony_ci clk_rate, 1688c2ecf20Sopenharmony_ci rps->timer_prescaler, 1698c2ecf20Sopenharmony_ci rps->timer_period); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Clocksource */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void __iomem *timer_sched_base; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic u64 notrace oxnas_rps_read_sched_clock(void) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci return ~readl_relaxed(timer_sched_base); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int __init oxnas_rps_clocksource_init(struct oxnas_rps_timer *rps) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci ulong clk_rate = clk_get_rate(rps->clk); 1868c2ecf20Sopenharmony_ci int ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* use prescale 16 */ 1898c2ecf20Sopenharmony_ci clk_rate = clk_rate / 16; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci writel_relaxed(TIMER_MAX_VAL, rps->clksrc_base + TIMER_LOAD_REG); 1928c2ecf20Sopenharmony_ci writel_relaxed(TIMER_PERIODIC | TIMER_ENABLE | TIMER_DIV16, 1938c2ecf20Sopenharmony_ci rps->clksrc_base + TIMER_CTRL_REG); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci timer_sched_base = rps->clksrc_base + TIMER_CURR_REG; 1968c2ecf20Sopenharmony_ci sched_clock_register(oxnas_rps_read_sched_clock, 1978c2ecf20Sopenharmony_ci TIMER_BITS, clk_rate); 1988c2ecf20Sopenharmony_ci ret = clocksource_mmio_init(timer_sched_base, 1998c2ecf20Sopenharmony_ci "oxnas_rps_clocksource_timer", 2008c2ecf20Sopenharmony_ci clk_rate, 250, TIMER_BITS, 2018c2ecf20Sopenharmony_ci clocksource_mmio_readl_down); 2028c2ecf20Sopenharmony_ci if (WARN_ON(ret)) { 2038c2ecf20Sopenharmony_ci pr_err("can't register clocksource\n"); 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci pr_info("Registered clocksource rate %luHz\n", clk_rate); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int __init oxnas_rps_timer_init(struct device_node *np) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct oxnas_rps_timer *rps; 2158c2ecf20Sopenharmony_ci void __iomem *base; 2168c2ecf20Sopenharmony_ci int ret; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci rps = kzalloc(sizeof(*rps), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!rps) 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci rps->clk = of_clk_get(np, 0); 2238c2ecf20Sopenharmony_ci if (IS_ERR(rps->clk)) { 2248c2ecf20Sopenharmony_ci ret = PTR_ERR(rps->clk); 2258c2ecf20Sopenharmony_ci goto err_alloc; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ret = clk_prepare_enable(rps->clk); 2298c2ecf20Sopenharmony_ci if (ret) 2308c2ecf20Sopenharmony_ci goto err_clk; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 2338c2ecf20Sopenharmony_ci if (!base) { 2348c2ecf20Sopenharmony_ci ret = -ENXIO; 2358c2ecf20Sopenharmony_ci goto err_clk_prepare; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rps->irq = irq_of_parse_and_map(np, 0); 2398c2ecf20Sopenharmony_ci if (!rps->irq) { 2408c2ecf20Sopenharmony_ci ret = -EINVAL; 2418c2ecf20Sopenharmony_ci goto err_iomap; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci rps->clkevt_base = base + TIMER1_REG_OFFSET; 2458c2ecf20Sopenharmony_ci rps->clksrc_base = base + TIMER2_REG_OFFSET; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Disable timers */ 2488c2ecf20Sopenharmony_ci writel_relaxed(0, rps->clkevt_base + TIMER_CTRL_REG); 2498c2ecf20Sopenharmony_ci writel_relaxed(0, rps->clksrc_base + TIMER_CTRL_REG); 2508c2ecf20Sopenharmony_ci writel_relaxed(0, rps->clkevt_base + TIMER_LOAD_REG); 2518c2ecf20Sopenharmony_ci writel_relaxed(0, rps->clksrc_base + TIMER_LOAD_REG); 2528c2ecf20Sopenharmony_ci writel_relaxed(0, rps->clkevt_base + TIMER_CLRINT_REG); 2538c2ecf20Sopenharmony_ci writel_relaxed(0, rps->clksrc_base + TIMER_CLRINT_REG); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = request_irq(rps->irq, oxnas_rps_timer_irq, 2568c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, 2578c2ecf20Sopenharmony_ci "rps-timer", rps); 2588c2ecf20Sopenharmony_ci if (ret) 2598c2ecf20Sopenharmony_ci goto err_iomap; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci ret = oxnas_rps_clocksource_init(rps); 2628c2ecf20Sopenharmony_ci if (ret) 2638c2ecf20Sopenharmony_ci goto err_irqreq; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = oxnas_rps_clockevent_init(rps); 2668c2ecf20Sopenharmony_ci if (ret) 2678c2ecf20Sopenharmony_ci goto err_irqreq; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cierr_irqreq: 2728c2ecf20Sopenharmony_ci free_irq(rps->irq, rps); 2738c2ecf20Sopenharmony_cierr_iomap: 2748c2ecf20Sopenharmony_ci iounmap(base); 2758c2ecf20Sopenharmony_cierr_clk_prepare: 2768c2ecf20Sopenharmony_ci clk_disable_unprepare(rps->clk); 2778c2ecf20Sopenharmony_cierr_clk: 2788c2ecf20Sopenharmony_ci clk_put(rps->clk); 2798c2ecf20Sopenharmony_cierr_alloc: 2808c2ecf20Sopenharmony_ci kfree(rps); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(ox810se_rps, 2868c2ecf20Sopenharmony_ci "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init); 2878c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(ox820_rps, 2888c2ecf20Sopenharmony_ci "oxsemi,ox820-rps-timer", oxnas_rps_timer_init); 289