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