18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Marvell Orion SoC timer handling.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
78c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * Timer 0 is used as free-running clocksource, while timer 1 is
118c2ecf20Sopenharmony_ci * used as clock_event_device.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/bitops.h>
168c2ecf20Sopenharmony_ci#include <linux/clk.h>
178c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/of_address.h>
218c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
228c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
238c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define TIMER_CTRL		0x00
268c2ecf20Sopenharmony_ci#define  TIMER0_EN		BIT(0)
278c2ecf20Sopenharmony_ci#define  TIMER0_RELOAD_EN	BIT(1)
288c2ecf20Sopenharmony_ci#define  TIMER1_EN		BIT(2)
298c2ecf20Sopenharmony_ci#define  TIMER1_RELOAD_EN	BIT(3)
308c2ecf20Sopenharmony_ci#define TIMER0_RELOAD		0x10
318c2ecf20Sopenharmony_ci#define TIMER0_VAL		0x14
328c2ecf20Sopenharmony_ci#define TIMER1_RELOAD		0x18
338c2ecf20Sopenharmony_ci#define TIMER1_VAL		0x1c
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define ORION_ONESHOT_MIN	1
368c2ecf20Sopenharmony_ci#define ORION_ONESHOT_MAX	0xfffffffe
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void __iomem *timer_base;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic unsigned long notrace orion_read_timer(void)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return ~readl(timer_base + TIMER0_VAL);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic struct delay_timer orion_delay_timer = {
468c2ecf20Sopenharmony_ci	.read_current_timer = orion_read_timer,
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic void orion_delay_timer_init(unsigned long rate)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	orion_delay_timer.freq = rate;
528c2ecf20Sopenharmony_ci	register_current_timer_delay(&orion_delay_timer);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * Free-running clocksource handling.
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_cistatic u64 notrace orion_read_sched_clock(void)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	return ~readl(timer_base + TIMER0_VAL);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * Clockevent handling.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic u32 ticks_per_jiffy;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int orion_clkevt_next_event(unsigned long delta,
698c2ecf20Sopenharmony_ci				   struct clock_event_device *dev)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	/* setup and enable one-shot timer */
728c2ecf20Sopenharmony_ci	writel(delta, timer_base + TIMER1_VAL);
738c2ecf20Sopenharmony_ci	atomic_io_modify(timer_base + TIMER_CTRL,
748c2ecf20Sopenharmony_ci		TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int orion_clkevt_shutdown(struct clock_event_device *dev)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	/* disable timer */
828c2ecf20Sopenharmony_ci	atomic_io_modify(timer_base + TIMER_CTRL,
838c2ecf20Sopenharmony_ci			 TIMER1_RELOAD_EN | TIMER1_EN, 0);
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int orion_clkevt_set_periodic(struct clock_event_device *dev)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	/* setup and enable periodic timer at 1/HZ intervals */
908c2ecf20Sopenharmony_ci	writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD);
918c2ecf20Sopenharmony_ci	writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL);
928c2ecf20Sopenharmony_ci	atomic_io_modify(timer_base + TIMER_CTRL,
938c2ecf20Sopenharmony_ci			 TIMER1_RELOAD_EN | TIMER1_EN,
948c2ecf20Sopenharmony_ci			 TIMER1_RELOAD_EN | TIMER1_EN);
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic struct clock_event_device orion_clkevt = {
998c2ecf20Sopenharmony_ci	.name			= "orion_event",
1008c2ecf20Sopenharmony_ci	.features		= CLOCK_EVT_FEAT_ONESHOT |
1018c2ecf20Sopenharmony_ci				  CLOCK_EVT_FEAT_PERIODIC,
1028c2ecf20Sopenharmony_ci	.shift			= 32,
1038c2ecf20Sopenharmony_ci	.rating			= 300,
1048c2ecf20Sopenharmony_ci	.set_next_event		= orion_clkevt_next_event,
1058c2ecf20Sopenharmony_ci	.set_state_shutdown	= orion_clkevt_shutdown,
1068c2ecf20Sopenharmony_ci	.set_state_periodic	= orion_clkevt_set_periodic,
1078c2ecf20Sopenharmony_ci	.set_state_oneshot	= orion_clkevt_shutdown,
1088c2ecf20Sopenharmony_ci	.tick_resume		= orion_clkevt_shutdown,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	orion_clkevt.event_handler(&orion_clkevt);
1148c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int __init orion_timer_init(struct device_node *np)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	unsigned long rate;
1208c2ecf20Sopenharmony_ci	struct clk *clk;
1218c2ecf20Sopenharmony_ci	int irq, ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* timer registers are shared with watchdog timer */
1248c2ecf20Sopenharmony_ci	timer_base = of_iomap(np, 0);
1258c2ecf20Sopenharmony_ci	if (!timer_base) {
1268c2ecf20Sopenharmony_ci		pr_err("%pOFn: unable to map resource\n", np);
1278c2ecf20Sopenharmony_ci		return -ENXIO;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	clk = of_clk_get(np, 0);
1318c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1328c2ecf20Sopenharmony_ci		pr_err("%pOFn: unable to get clk\n", np);
1338c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(clk);
1378c2ecf20Sopenharmony_ci	if (ret) {
1388c2ecf20Sopenharmony_ci		pr_err("Failed to prepare clock\n");
1398c2ecf20Sopenharmony_ci		return ret;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* we are only interested in timer1 irq */
1438c2ecf20Sopenharmony_ci	irq = irq_of_parse_and_map(np, 1);
1448c2ecf20Sopenharmony_ci	if (irq <= 0) {
1458c2ecf20Sopenharmony_ci		pr_err("%pOFn: unable to parse timer1 irq\n", np);
1468c2ecf20Sopenharmony_ci		ret = -EINVAL;
1478c2ecf20Sopenharmony_ci		goto out_unprep_clk;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	rate = clk_get_rate(clk);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* setup timer0 as free-running clocksource */
1538c2ecf20Sopenharmony_ci	writel(~0, timer_base + TIMER0_VAL);
1548c2ecf20Sopenharmony_ci	writel(~0, timer_base + TIMER0_RELOAD);
1558c2ecf20Sopenharmony_ci	atomic_io_modify(timer_base + TIMER_CTRL,
1568c2ecf20Sopenharmony_ci		TIMER0_RELOAD_EN | TIMER0_EN,
1578c2ecf20Sopenharmony_ci		TIMER0_RELOAD_EN | TIMER0_EN);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	ret = clocksource_mmio_init(timer_base + TIMER0_VAL,
1608c2ecf20Sopenharmony_ci				    "orion_clocksource", rate, 300, 32,
1618c2ecf20Sopenharmony_ci				    clocksource_mmio_readl_down);
1628c2ecf20Sopenharmony_ci	if (ret) {
1638c2ecf20Sopenharmony_ci		pr_err("Failed to initialize mmio timer\n");
1648c2ecf20Sopenharmony_ci		goto out_unprep_clk;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	sched_clock_register(orion_read_sched_clock, 32, rate);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* setup timer1 as clockevent timer */
1708c2ecf20Sopenharmony_ci	ret = request_irq(irq, orion_clkevt_irq_handler, IRQF_TIMER,
1718c2ecf20Sopenharmony_ci			  "orion_event", NULL);
1728c2ecf20Sopenharmony_ci	if (ret) {
1738c2ecf20Sopenharmony_ci		pr_err("%pOFn: unable to setup irq\n", np);
1748c2ecf20Sopenharmony_ci		goto out_unprep_clk;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ;
1788c2ecf20Sopenharmony_ci	orion_clkevt.cpumask = cpumask_of(0);
1798c2ecf20Sopenharmony_ci	orion_clkevt.irq = irq;
1808c2ecf20Sopenharmony_ci	clockevents_config_and_register(&orion_clkevt, rate,
1818c2ecf20Sopenharmony_ci					ORION_ONESHOT_MIN, ORION_ONESHOT_MAX);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	orion_delay_timer_init(rate);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciout_unprep_clk:
1898c2ecf20Sopenharmony_ci	clk_disable_unprepare(clk);
1908c2ecf20Sopenharmony_ci	return ret;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
193