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