18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Google, Inc. 58c2ecf20Sopenharmony_ci * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 98c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 108c2ecf20Sopenharmony_ci#include <linux/cpu.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_address.h> 178c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 188c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/delay.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define TIMER_MATCH_VAL 0x0000 238c2ecf20Sopenharmony_ci#define TIMER_COUNT_VAL 0x0004 248c2ecf20Sopenharmony_ci#define TIMER_ENABLE 0x0008 258c2ecf20Sopenharmony_ci#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) 268c2ecf20Sopenharmony_ci#define TIMER_ENABLE_EN BIT(0) 278c2ecf20Sopenharmony_ci#define TIMER_CLEAR 0x000C 288c2ecf20Sopenharmony_ci#define DGT_CLK_CTL 0x10 298c2ecf20Sopenharmony_ci#define DGT_CLK_CTL_DIV_4 0x3 308c2ecf20Sopenharmony_ci#define TIMER_STS_GPT0_CLR_PEND BIT(10) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define GPT_HZ 32768 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void __iomem *event_base; 358c2ecf20Sopenharmony_cistatic void __iomem *sts_base; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic irqreturn_t msm_timer_interrupt(int irq, void *dev_id) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 408c2ecf20Sopenharmony_ci /* Stop the timer tick */ 418c2ecf20Sopenharmony_ci if (clockevent_state_oneshot(evt)) { 428c2ecf20Sopenharmony_ci u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); 438c2ecf20Sopenharmony_ci ctrl &= ~TIMER_ENABLE_EN; 448c2ecf20Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_ENABLE); 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci evt->event_handler(evt); 478c2ecf20Sopenharmony_ci return IRQ_HANDLED; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int msm_timer_set_next_event(unsigned long cycles, 518c2ecf20Sopenharmony_ci struct clock_event_device *evt) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ctrl &= ~TIMER_ENABLE_EN; 568c2ecf20Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_ENABLE); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_CLEAR); 598c2ecf20Sopenharmony_ci writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (sts_base) 628c2ecf20Sopenharmony_ci while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND) 638c2ecf20Sopenharmony_ci cpu_relax(); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int msm_timer_shutdown(struct clock_event_device *evt) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 ctrl; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci ctrl = readl_relaxed(event_base + TIMER_ENABLE); 748c2ecf20Sopenharmony_ci ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); 758c2ecf20Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_ENABLE); 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic struct clock_event_device __percpu *msm_evt; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void __iomem *source_base; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic notrace u64 msm_read_timer_count(struct clocksource *cs) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return readl_relaxed(source_base + TIMER_COUNT_VAL); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct clocksource msm_clocksource = { 898c2ecf20Sopenharmony_ci .name = "dg_timer", 908c2ecf20Sopenharmony_ci .rating = 300, 918c2ecf20Sopenharmony_ci .read = msm_read_timer_count, 928c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 938c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int msm_timer_irq; 978c2ecf20Sopenharmony_cistatic int msm_timer_has_ppi; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int msm_local_timer_starting_cpu(unsigned int cpu) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu); 1028c2ecf20Sopenharmony_ci int err; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci evt->irq = msm_timer_irq; 1058c2ecf20Sopenharmony_ci evt->name = "msm_timer"; 1068c2ecf20Sopenharmony_ci evt->features = CLOCK_EVT_FEAT_ONESHOT; 1078c2ecf20Sopenharmony_ci evt->rating = 200; 1088c2ecf20Sopenharmony_ci evt->set_state_shutdown = msm_timer_shutdown; 1098c2ecf20Sopenharmony_ci evt->set_state_oneshot = msm_timer_shutdown; 1108c2ecf20Sopenharmony_ci evt->tick_resume = msm_timer_shutdown; 1118c2ecf20Sopenharmony_ci evt->set_next_event = msm_timer_set_next_event; 1128c2ecf20Sopenharmony_ci evt->cpumask = cpumask_of(cpu); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (msm_timer_has_ppi) { 1178c2ecf20Sopenharmony_ci enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); 1188c2ecf20Sopenharmony_ci } else { 1198c2ecf20Sopenharmony_ci err = request_irq(evt->irq, msm_timer_interrupt, 1208c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_NOBALANCING | 1218c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING, "gp_timer", evt); 1228c2ecf20Sopenharmony_ci if (err) 1238c2ecf20Sopenharmony_ci pr_err("request_irq failed\n"); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int msm_local_timer_dying_cpu(unsigned int cpu) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci evt->set_state_shutdown(evt); 1348c2ecf20Sopenharmony_ci disable_percpu_irq(evt->irq); 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic u64 notrace msm_sched_clock_read(void) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci return msm_clocksource.read(&msm_clocksource); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic unsigned long msm_read_current_timer(void) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci return msm_clocksource.read(&msm_clocksource); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct delay_timer msm_delay_timer = { 1498c2ecf20Sopenharmony_ci .read_current_timer = msm_read_current_timer, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, 1538c2ecf20Sopenharmony_ci bool percpu) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct clocksource *cs = &msm_clocksource; 1568c2ecf20Sopenharmony_ci int res = 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci msm_timer_irq = irq; 1598c2ecf20Sopenharmony_ci msm_timer_has_ppi = percpu; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci msm_evt = alloc_percpu(struct clock_event_device); 1628c2ecf20Sopenharmony_ci if (!msm_evt) { 1638c2ecf20Sopenharmony_ci pr_err("memory allocation failed for clockevents\n"); 1648c2ecf20Sopenharmony_ci goto err; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (percpu) 1688c2ecf20Sopenharmony_ci res = request_percpu_irq(irq, msm_timer_interrupt, 1698c2ecf20Sopenharmony_ci "gp_timer", msm_evt); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (res) { 1728c2ecf20Sopenharmony_ci pr_err("request_percpu_irq failed\n"); 1738c2ecf20Sopenharmony_ci } else { 1748c2ecf20Sopenharmony_ci /* Install and invoke hotplug callbacks */ 1758c2ecf20Sopenharmony_ci res = cpuhp_setup_state(CPUHP_AP_QCOM_TIMER_STARTING, 1768c2ecf20Sopenharmony_ci "clockevents/qcom/timer:starting", 1778c2ecf20Sopenharmony_ci msm_local_timer_starting_cpu, 1788c2ecf20Sopenharmony_ci msm_local_timer_dying_cpu); 1798c2ecf20Sopenharmony_ci if (res) { 1808c2ecf20Sopenharmony_ci free_percpu_irq(irq, msm_evt); 1818c2ecf20Sopenharmony_ci goto err; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cierr: 1868c2ecf20Sopenharmony_ci writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); 1878c2ecf20Sopenharmony_ci res = clocksource_register_hz(cs, dgt_hz); 1888c2ecf20Sopenharmony_ci if (res) 1898c2ecf20Sopenharmony_ci pr_err("clocksource_register failed\n"); 1908c2ecf20Sopenharmony_ci sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); 1918c2ecf20Sopenharmony_ci msm_delay_timer.freq = dgt_hz; 1928c2ecf20Sopenharmony_ci register_current_timer_delay(&msm_delay_timer); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return res; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int __init msm_dt_timer_init(struct device_node *np) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci u32 freq; 2008c2ecf20Sopenharmony_ci int irq, ret; 2018c2ecf20Sopenharmony_ci struct resource res; 2028c2ecf20Sopenharmony_ci u32 percpu_offset; 2038c2ecf20Sopenharmony_ci void __iomem *base; 2048c2ecf20Sopenharmony_ci void __iomem *cpu0_base; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 2078c2ecf20Sopenharmony_ci if (!base) { 2088c2ecf20Sopenharmony_ci pr_err("Failed to map event base\n"); 2098c2ecf20Sopenharmony_ci return -ENXIO; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* We use GPT0 for the clockevent */ 2138c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 1); 2148c2ecf20Sopenharmony_ci if (irq <= 0) { 2158c2ecf20Sopenharmony_ci pr_err("Can't get irq\n"); 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* We use CPU0's DGT for the clocksource */ 2208c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) 2218c2ecf20Sopenharmony_ci percpu_offset = 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 2248c2ecf20Sopenharmony_ci if (ret) { 2258c2ecf20Sopenharmony_ci pr_err("Failed to parse DGT resource\n"); 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); 2308c2ecf20Sopenharmony_ci if (!cpu0_base) { 2318c2ecf20Sopenharmony_ci pr_err("Failed to map source base\n"); 2328c2ecf20Sopenharmony_ci return -EINVAL; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "clock-frequency", &freq)) { 2368c2ecf20Sopenharmony_ci pr_err("Unknown frequency\n"); 2378c2ecf20Sopenharmony_ci return -EINVAL; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci event_base = base + 0x4; 2418c2ecf20Sopenharmony_ci sts_base = base + 0x88; 2428c2ecf20Sopenharmony_ci source_base = cpu0_base + 0x24; 2438c2ecf20Sopenharmony_ci freq /= 4; 2448c2ecf20Sopenharmony_ci writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return msm_timer_init(freq, 32, irq, !!percpu_offset); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); 2498c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); 250