162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007 Google, Inc. 562306a36Sopenharmony_ci * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clocksource.h> 962306a36Sopenharmony_ci#include <linux/clockchips.h> 1062306a36Sopenharmony_ci#include <linux/cpu.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/irq.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/of_irq.h> 1862306a36Sopenharmony_ci#include <linux/sched_clock.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/delay.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define TIMER_MATCH_VAL 0x0000 2362306a36Sopenharmony_ci#define TIMER_COUNT_VAL 0x0004 2462306a36Sopenharmony_ci#define TIMER_ENABLE 0x0008 2562306a36Sopenharmony_ci#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1) 2662306a36Sopenharmony_ci#define TIMER_ENABLE_EN BIT(0) 2762306a36Sopenharmony_ci#define TIMER_CLEAR 0x000C 2862306a36Sopenharmony_ci#define DGT_CLK_CTL 0x10 2962306a36Sopenharmony_ci#define DGT_CLK_CTL_DIV_4 0x3 3062306a36Sopenharmony_ci#define TIMER_STS_GPT0_CLR_PEND BIT(10) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define GPT_HZ 32768 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic void __iomem *event_base; 3562306a36Sopenharmony_cistatic void __iomem *sts_base; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic irqreturn_t msm_timer_interrupt(int irq, void *dev_id) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct clock_event_device *evt = dev_id; 4062306a36Sopenharmony_ci /* Stop the timer tick */ 4162306a36Sopenharmony_ci if (clockevent_state_oneshot(evt)) { 4262306a36Sopenharmony_ci u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); 4362306a36Sopenharmony_ci ctrl &= ~TIMER_ENABLE_EN; 4462306a36Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_ENABLE); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci evt->event_handler(evt); 4762306a36Sopenharmony_ci return IRQ_HANDLED; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int msm_timer_set_next_event(unsigned long cycles, 5162306a36Sopenharmony_ci struct clock_event_device *evt) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ctrl &= ~TIMER_ENABLE_EN; 5662306a36Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_ENABLE); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_CLEAR); 5962306a36Sopenharmony_ci writel_relaxed(cycles, event_base + TIMER_MATCH_VAL); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (sts_base) 6262306a36Sopenharmony_ci while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND) 6362306a36Sopenharmony_ci cpu_relax(); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE); 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int msm_timer_shutdown(struct clock_event_device *evt) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci u32 ctrl; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ctrl = readl_relaxed(event_base + TIMER_ENABLE); 7462306a36Sopenharmony_ci ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN); 7562306a36Sopenharmony_ci writel_relaxed(ctrl, event_base + TIMER_ENABLE); 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct clock_event_device __percpu *msm_evt; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void __iomem *source_base; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic notrace u64 msm_read_timer_count(struct clocksource *cs) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci return readl_relaxed(source_base + TIMER_COUNT_VAL); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic struct clocksource msm_clocksource = { 8962306a36Sopenharmony_ci .name = "dg_timer", 9062306a36Sopenharmony_ci .rating = 300, 9162306a36Sopenharmony_ci .read = msm_read_timer_count, 9262306a36Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 9362306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int msm_timer_irq; 9762306a36Sopenharmony_cistatic int msm_timer_has_ppi; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int msm_local_timer_starting_cpu(unsigned int cpu) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu); 10262306a36Sopenharmony_ci int err; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci evt->irq = msm_timer_irq; 10562306a36Sopenharmony_ci evt->name = "msm_timer"; 10662306a36Sopenharmony_ci evt->features = CLOCK_EVT_FEAT_ONESHOT; 10762306a36Sopenharmony_ci evt->rating = 200; 10862306a36Sopenharmony_ci evt->set_state_shutdown = msm_timer_shutdown; 10962306a36Sopenharmony_ci evt->set_state_oneshot = msm_timer_shutdown; 11062306a36Sopenharmony_ci evt->tick_resume = msm_timer_shutdown; 11162306a36Sopenharmony_ci evt->set_next_event = msm_timer_set_next_event; 11262306a36Sopenharmony_ci evt->cpumask = cpumask_of(cpu); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (msm_timer_has_ppi) { 11762306a36Sopenharmony_ci enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING); 11862306a36Sopenharmony_ci } else { 11962306a36Sopenharmony_ci err = request_irq(evt->irq, msm_timer_interrupt, 12062306a36Sopenharmony_ci IRQF_TIMER | IRQF_NOBALANCING | 12162306a36Sopenharmony_ci IRQF_TRIGGER_RISING, "gp_timer", evt); 12262306a36Sopenharmony_ci if (err) 12362306a36Sopenharmony_ci pr_err("request_irq failed\n"); 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int msm_local_timer_dying_cpu(unsigned int cpu) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct clock_event_device *evt = per_cpu_ptr(msm_evt, cpu); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci evt->set_state_shutdown(evt); 13462306a36Sopenharmony_ci disable_percpu_irq(evt->irq); 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic u64 notrace msm_sched_clock_read(void) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci return msm_clocksource.read(&msm_clocksource); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic unsigned long msm_read_current_timer(void) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci return msm_clocksource.read(&msm_clocksource); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct delay_timer msm_delay_timer = { 14962306a36Sopenharmony_ci .read_current_timer = msm_read_current_timer, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq, 15362306a36Sopenharmony_ci bool percpu) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct clocksource *cs = &msm_clocksource; 15662306a36Sopenharmony_ci int res = 0; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci msm_timer_irq = irq; 15962306a36Sopenharmony_ci msm_timer_has_ppi = percpu; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci msm_evt = alloc_percpu(struct clock_event_device); 16262306a36Sopenharmony_ci if (!msm_evt) { 16362306a36Sopenharmony_ci pr_err("memory allocation failed for clockevents\n"); 16462306a36Sopenharmony_ci goto err; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (percpu) 16862306a36Sopenharmony_ci res = request_percpu_irq(irq, msm_timer_interrupt, 16962306a36Sopenharmony_ci "gp_timer", msm_evt); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (res) { 17262306a36Sopenharmony_ci pr_err("request_percpu_irq failed\n"); 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci /* Install and invoke hotplug callbacks */ 17562306a36Sopenharmony_ci res = cpuhp_setup_state(CPUHP_AP_QCOM_TIMER_STARTING, 17662306a36Sopenharmony_ci "clockevents/qcom/timer:starting", 17762306a36Sopenharmony_ci msm_local_timer_starting_cpu, 17862306a36Sopenharmony_ci msm_local_timer_dying_cpu); 17962306a36Sopenharmony_ci if (res) { 18062306a36Sopenharmony_ci free_percpu_irq(irq, msm_evt); 18162306a36Sopenharmony_ci goto err; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cierr: 18662306a36Sopenharmony_ci writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE); 18762306a36Sopenharmony_ci res = clocksource_register_hz(cs, dgt_hz); 18862306a36Sopenharmony_ci if (res) 18962306a36Sopenharmony_ci pr_err("clocksource_register failed\n"); 19062306a36Sopenharmony_ci sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz); 19162306a36Sopenharmony_ci msm_delay_timer.freq = dgt_hz; 19262306a36Sopenharmony_ci register_current_timer_delay(&msm_delay_timer); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return res; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int __init msm_dt_timer_init(struct device_node *np) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci u32 freq; 20062306a36Sopenharmony_ci int irq, ret; 20162306a36Sopenharmony_ci struct resource res; 20262306a36Sopenharmony_ci u32 percpu_offset; 20362306a36Sopenharmony_ci void __iomem *base; 20462306a36Sopenharmony_ci void __iomem *cpu0_base; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci base = of_iomap(np, 0); 20762306a36Sopenharmony_ci if (!base) { 20862306a36Sopenharmony_ci pr_err("Failed to map event base\n"); 20962306a36Sopenharmony_ci return -ENXIO; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* We use GPT0 for the clockevent */ 21362306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, 1); 21462306a36Sopenharmony_ci if (irq <= 0) { 21562306a36Sopenharmony_ci pr_err("Can't get irq\n"); 21662306a36Sopenharmony_ci return -EINVAL; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* We use CPU0's DGT for the clocksource */ 22062306a36Sopenharmony_ci if (of_property_read_u32(np, "cpu-offset", &percpu_offset)) 22162306a36Sopenharmony_ci percpu_offset = 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 22462306a36Sopenharmony_ci if (ret) { 22562306a36Sopenharmony_ci pr_err("Failed to parse DGT resource\n"); 22662306a36Sopenharmony_ci return ret; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res)); 23062306a36Sopenharmony_ci if (!cpu0_base) { 23162306a36Sopenharmony_ci pr_err("Failed to map source base\n"); 23262306a36Sopenharmony_ci return -EINVAL; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (of_property_read_u32(np, "clock-frequency", &freq)) { 23662306a36Sopenharmony_ci pr_err("Unknown frequency\n"); 23762306a36Sopenharmony_ci return -EINVAL; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci event_base = base + 0x4; 24162306a36Sopenharmony_ci sts_base = base + 0x88; 24262306a36Sopenharmony_ci source_base = cpu0_base + 0x24; 24362306a36Sopenharmony_ci freq /= 4; 24462306a36Sopenharmony_ci writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return msm_timer_init(freq, 32, irq, !!percpu_offset); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ciTIMER_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init); 24962306a36Sopenharmony_ciTIMER_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init); 250