18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Integrator/AP timer driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2000-2003 Deep Blue Solutions Ltd 58c2ecf20Sopenharmony_ci * Copyright (c) 2014, Linaro Limited 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 108c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 138c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "timer-sp.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic void __iomem * sched_clk_base; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic u64 notrace integrator_read_sched_clock(void) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci return -readl(sched_clk_base + TIMER_VALUE); 248c2ecf20Sopenharmony_ci} 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int __init integrator_clocksource_init(unsigned long inrate, 278c2ecf20Sopenharmony_ci void __iomem *base) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC; 308c2ecf20Sopenharmony_ci unsigned long rate = inrate; 318c2ecf20Sopenharmony_ci int ret; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (rate >= 1500000) { 348c2ecf20Sopenharmony_ci rate /= 16; 358c2ecf20Sopenharmony_ci ctrl |= TIMER_CTRL_DIV16; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci writel(0xffff, base + TIMER_LOAD); 398c2ecf20Sopenharmony_ci writel(ctrl, base + TIMER_CTRL); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci ret = clocksource_mmio_init(base + TIMER_VALUE, "timer2", 428c2ecf20Sopenharmony_ci rate, 200, 16, clocksource_mmio_readl_down); 438c2ecf20Sopenharmony_ci if (ret) 448c2ecf20Sopenharmony_ci return ret; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci sched_clk_base = base; 478c2ecf20Sopenharmony_ci sched_clock_register(integrator_read_sched_clock, 16, rate); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return 0; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic unsigned long timer_reload; 538c2ecf20Sopenharmony_cistatic void __iomem * clkevt_base; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * IRQ handler for the timer 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic irqreturn_t integrator_timer_interrupt(int irq, void *dev_id) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* clear the interrupt */ 638c2ecf20Sopenharmony_ci writel(1, clkevt_base + TIMER_INTCLR); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci evt->event_handler(evt); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int clkevt_shutdown(struct clock_event_device *evt) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Disable timer */ 758c2ecf20Sopenharmony_ci writel(ctrl, clkevt_base + TIMER_CTRL); 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int clkevt_set_oneshot(struct clock_event_device *evt) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci u32 ctrl = readl(clkevt_base + TIMER_CTRL) & 828c2ecf20Sopenharmony_ci ~(TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* Leave the timer disabled, .set_next_event will enable it */ 858c2ecf20Sopenharmony_ci writel(ctrl, clkevt_base + TIMER_CTRL); 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int clkevt_set_periodic(struct clock_event_device *evt) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Disable timer */ 948c2ecf20Sopenharmony_ci writel(ctrl, clkevt_base + TIMER_CTRL); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Enable the timer and start the periodic tick */ 978c2ecf20Sopenharmony_ci writel(timer_reload, clkevt_base + TIMER_LOAD); 988c2ecf20Sopenharmony_ci ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; 998c2ecf20Sopenharmony_ci writel(ctrl, clkevt_base + TIMER_CTRL); 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); 1088c2ecf20Sopenharmony_ci writel(next, clkevt_base + TIMER_LOAD); 1098c2ecf20Sopenharmony_ci writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic struct clock_event_device integrator_clockevent = { 1158c2ecf20Sopenharmony_ci .name = "timer1", 1168c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 1178c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 1188c2ecf20Sopenharmony_ci .set_state_shutdown = clkevt_shutdown, 1198c2ecf20Sopenharmony_ci .set_state_periodic = clkevt_set_periodic, 1208c2ecf20Sopenharmony_ci .set_state_oneshot = clkevt_set_oneshot, 1218c2ecf20Sopenharmony_ci .tick_resume = clkevt_shutdown, 1228c2ecf20Sopenharmony_ci .set_next_event = clkevt_set_next_event, 1238c2ecf20Sopenharmony_ci .rating = 300, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int integrator_clockevent_init(unsigned long inrate, 1278c2ecf20Sopenharmony_ci void __iomem *base, int irq) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci unsigned long rate = inrate; 1308c2ecf20Sopenharmony_ci unsigned int ctrl = 0; 1318c2ecf20Sopenharmony_ci int ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci clkevt_base = base; 1348c2ecf20Sopenharmony_ci /* Calculate and program a divisor */ 1358c2ecf20Sopenharmony_ci if (rate > 0x100000 * HZ) { 1368c2ecf20Sopenharmony_ci rate /= 256; 1378c2ecf20Sopenharmony_ci ctrl |= TIMER_CTRL_DIV256; 1388c2ecf20Sopenharmony_ci } else if (rate > 0x10000 * HZ) { 1398c2ecf20Sopenharmony_ci rate /= 16; 1408c2ecf20Sopenharmony_ci ctrl |= TIMER_CTRL_DIV16; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci timer_reload = rate / HZ; 1438c2ecf20Sopenharmony_ci writel(ctrl, clkevt_base + TIMER_CTRL); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ret = request_irq(irq, integrator_timer_interrupt, 1468c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "timer", 1478c2ecf20Sopenharmony_ci &integrator_clockevent); 1488c2ecf20Sopenharmony_ci if (ret) 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci clockevents_config_and_register(&integrator_clockevent, 1528c2ecf20Sopenharmony_ci rate, 1538c2ecf20Sopenharmony_ci 1, 1548c2ecf20Sopenharmony_ci 0xffffU); 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int __init integrator_ap_timer_init_of(struct device_node *node) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci const char *path; 1618c2ecf20Sopenharmony_ci void __iomem *base; 1628c2ecf20Sopenharmony_ci int err; 1638c2ecf20Sopenharmony_ci int irq; 1648c2ecf20Sopenharmony_ci struct clk *clk; 1658c2ecf20Sopenharmony_ci unsigned long rate; 1668c2ecf20Sopenharmony_ci struct device_node *alias_node; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci base = of_io_request_and_map(node, 0, "integrator-timer"); 1698c2ecf20Sopenharmony_ci if (IS_ERR(base)) 1708c2ecf20Sopenharmony_ci return PTR_ERR(base); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci clk = of_clk_get(node, 0); 1738c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1748c2ecf20Sopenharmony_ci pr_err("No clock for %pOFn\n", node); 1758c2ecf20Sopenharmony_ci return PTR_ERR(clk); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci clk_prepare_enable(clk); 1788c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 1798c2ecf20Sopenharmony_ci writel(0, base + TIMER_CTRL); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci err = of_property_read_string(of_aliases, 1828c2ecf20Sopenharmony_ci "arm,timer-primary", &path); 1838c2ecf20Sopenharmony_ci if (err) { 1848c2ecf20Sopenharmony_ci pr_warn("Failed to read property\n"); 1858c2ecf20Sopenharmony_ci return err; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci alias_node = of_find_node_by_path(path); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * The pointer is used as an identifier not as a pointer, we 1928c2ecf20Sopenharmony_ci * can drop the refcount on the of__node immediately after 1938c2ecf20Sopenharmony_ci * getting it. 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci of_node_put(alias_node); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (node == alias_node) 1988c2ecf20Sopenharmony_ci /* The primary timer lacks IRQ, use as clocksource */ 1998c2ecf20Sopenharmony_ci return integrator_clocksource_init(rate, base); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci err = of_property_read_string(of_aliases, 2028c2ecf20Sopenharmony_ci "arm,timer-secondary", &path); 2038c2ecf20Sopenharmony_ci if (err) { 2048c2ecf20Sopenharmony_ci pr_warn("Failed to read property\n"); 2058c2ecf20Sopenharmony_ci return err; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci alias_node = of_find_node_by_path(path); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci of_node_put(alias_node); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (node == alias_node) { 2138c2ecf20Sopenharmony_ci /* The secondary timer will drive the clock event */ 2148c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 2158c2ecf20Sopenharmony_ci return integrator_clockevent_init(rate, base, irq); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci pr_info("Timer @%p unused\n", base); 2198c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer", 2258c2ecf20Sopenharmony_ci integrator_ap_timer_init_of); 226