162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Allwinner A1X SoCs timer handling.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Maxime Ripard
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Based on code from
1062306a36Sopenharmony_ci * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
1162306a36Sopenharmony_ci * Benn Huang <benn@allwinnertech.com>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/clk.h>
1562306a36Sopenharmony_ci#include <linux/clockchips.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/irq.h>
1862306a36Sopenharmony_ci#include <linux/irqreturn.h>
1962306a36Sopenharmony_ci#include <linux/sched_clock.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/of_address.h>
2262306a36Sopenharmony_ci#include <linux/of_irq.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "timer-of.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define TIMER_IRQ_EN_REG	0x00
2762306a36Sopenharmony_ci#define TIMER_IRQ_EN(val)		BIT(val)
2862306a36Sopenharmony_ci#define TIMER_IRQ_ST_REG	0x04
2962306a36Sopenharmony_ci#define TIMER_IRQ_CLEAR(val)		BIT(val)
3062306a36Sopenharmony_ci#define TIMER_CTL_REG(val)	(0x10 * val + 0x10)
3162306a36Sopenharmony_ci#define TIMER_CTL_ENABLE		BIT(0)
3262306a36Sopenharmony_ci#define TIMER_CTL_RELOAD		BIT(1)
3362306a36Sopenharmony_ci#define TIMER_CTL_CLK_SRC(val)		(((val) & 0x3) << 2)
3462306a36Sopenharmony_ci#define TIMER_CTL_CLK_SRC_OSC24M		(1)
3562306a36Sopenharmony_ci#define TIMER_CTL_CLK_PRES(val)		(((val) & 0x7) << 4)
3662306a36Sopenharmony_ci#define TIMER_CTL_ONESHOT		BIT(7)
3762306a36Sopenharmony_ci#define TIMER_INTVAL_REG(val)	(0x10 * (val) + 0x14)
3862306a36Sopenharmony_ci#define TIMER_CNTVAL_REG(val)	(0x10 * (val) + 0x18)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define TIMER_SYNC_TICKS	3
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * When we disable a timer, we need to wait at least for 2 cycles of
4462306a36Sopenharmony_ci * the timer source clock. We will use for that the clocksource timer
4562306a36Sopenharmony_ci * that is already setup and runs at the same frequency than the other
4662306a36Sopenharmony_ci * timers, and we never will be disabled.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic void sun4i_clkevt_sync(void __iomem *base)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u32 old = readl(base + TIMER_CNTVAL_REG(1));
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	while ((old - readl(base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS)
5362306a36Sopenharmony_ci		cpu_relax();
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void sun4i_clkevt_time_stop(void __iomem *base, u8 timer)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	u32 val = readl(base + TIMER_CTL_REG(timer));
5962306a36Sopenharmony_ci	writel(val & ~TIMER_CTL_ENABLE, base + TIMER_CTL_REG(timer));
6062306a36Sopenharmony_ci	sun4i_clkevt_sync(base);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void sun4i_clkevt_time_setup(void __iomem *base, u8 timer,
6462306a36Sopenharmony_ci				    unsigned long delay)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	writel(delay, base + TIMER_INTVAL_REG(timer));
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void sun4i_clkevt_time_start(void __iomem *base, u8 timer,
7062306a36Sopenharmony_ci				    bool periodic)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	u32 val = readl(base + TIMER_CTL_REG(timer));
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (periodic)
7562306a36Sopenharmony_ci		val &= ~TIMER_CTL_ONESHOT;
7662306a36Sopenharmony_ci	else
7762306a36Sopenharmony_ci		val |= TIMER_CTL_ONESHOT;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
8062306a36Sopenharmony_ci	       base + TIMER_CTL_REG(timer));
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int sun4i_clkevt_shutdown(struct clock_event_device *evt)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct timer_of *to = to_timer_of(evt);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	sun4i_clkevt_time_stop(timer_of_base(to), 0);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int sun4i_clkevt_set_oneshot(struct clock_event_device *evt)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct timer_of *to = to_timer_of(evt);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	sun4i_clkevt_time_stop(timer_of_base(to), 0);
9762306a36Sopenharmony_ci	sun4i_clkevt_time_start(timer_of_base(to), 0, false);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int sun4i_clkevt_set_periodic(struct clock_event_device *evt)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct timer_of *to = to_timer_of(evt);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	sun4i_clkevt_time_stop(timer_of_base(to), 0);
10762306a36Sopenharmony_ci	sun4i_clkevt_time_setup(timer_of_base(to), 0, timer_of_period(to));
10862306a36Sopenharmony_ci	sun4i_clkevt_time_start(timer_of_base(to), 0, true);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int sun4i_clkevt_next_event(unsigned long evt,
11462306a36Sopenharmony_ci				   struct clock_event_device *clkevt)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct timer_of *to = to_timer_of(clkevt);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	sun4i_clkevt_time_stop(timer_of_base(to), 0);
11962306a36Sopenharmony_ci	sun4i_clkevt_time_setup(timer_of_base(to), 0, evt - TIMER_SYNC_TICKS);
12062306a36Sopenharmony_ci	sun4i_clkevt_time_start(timer_of_base(to), 0, false);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void sun4i_timer_clear_interrupt(void __iomem *base)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	writel(TIMER_IRQ_CLEAR(0), base + TIMER_IRQ_ST_REG);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct clock_event_device *evt = dev_id;
13362306a36Sopenharmony_ci	struct timer_of *to = to_timer_of(evt);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	sun4i_timer_clear_interrupt(timer_of_base(to));
13662306a36Sopenharmony_ci	evt->event_handler(evt);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return IRQ_HANDLED;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct timer_of to = {
14262306a36Sopenharmony_ci	.flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	.clkevt = {
14562306a36Sopenharmony_ci		.name = "sun4i_tick",
14662306a36Sopenharmony_ci		.rating = 350,
14762306a36Sopenharmony_ci		.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
14862306a36Sopenharmony_ci				CLOCK_EVT_FEAT_DYNIRQ,
14962306a36Sopenharmony_ci		.set_state_shutdown = sun4i_clkevt_shutdown,
15062306a36Sopenharmony_ci		.set_state_periodic = sun4i_clkevt_set_periodic,
15162306a36Sopenharmony_ci		.set_state_oneshot = sun4i_clkevt_set_oneshot,
15262306a36Sopenharmony_ci		.tick_resume = sun4i_clkevt_shutdown,
15362306a36Sopenharmony_ci		.set_next_event = sun4i_clkevt_next_event,
15462306a36Sopenharmony_ci		.cpumask = cpu_possible_mask,
15562306a36Sopenharmony_ci	},
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	.of_irq = {
15862306a36Sopenharmony_ci		.handler = sun4i_timer_interrupt,
15962306a36Sopenharmony_ci		.flags = IRQF_TIMER | IRQF_IRQPOLL,
16062306a36Sopenharmony_ci	},
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic u64 notrace sun4i_timer_sched_read(void)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	return ~readl(timer_of_base(&to) + TIMER_CNTVAL_REG(1));
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int __init sun4i_timer_init(struct device_node *node)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci	u32 val;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	ret = timer_of_init(node, &to);
17462306a36Sopenharmony_ci	if (ret)
17562306a36Sopenharmony_ci		return ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	writel(~0, timer_of_base(&to) + TIMER_INTVAL_REG(1));
17862306a36Sopenharmony_ci	writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
17962306a36Sopenharmony_ci	       TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
18062306a36Sopenharmony_ci	       timer_of_base(&to) + TIMER_CTL_REG(1));
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/*
18362306a36Sopenharmony_ci	 * sched_clock_register does not have priorities, and on sun6i and
18462306a36Sopenharmony_ci	 * later there is a better sched_clock registered by arm_arch_timer.c
18562306a36Sopenharmony_ci	 */
18662306a36Sopenharmony_ci	if (of_machine_is_compatible("allwinner,sun4i-a10") ||
18762306a36Sopenharmony_ci	    of_machine_is_compatible("allwinner,sun5i-a13") ||
18862306a36Sopenharmony_ci	    of_machine_is_compatible("allwinner,sun5i-a10s") ||
18962306a36Sopenharmony_ci	    of_machine_is_compatible("allwinner,suniv-f1c100s"))
19062306a36Sopenharmony_ci		sched_clock_register(sun4i_timer_sched_read, 32,
19162306a36Sopenharmony_ci				     timer_of_rate(&to));
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	ret = clocksource_mmio_init(timer_of_base(&to) + TIMER_CNTVAL_REG(1),
19462306a36Sopenharmony_ci				    node->name, timer_of_rate(&to), 350, 32,
19562306a36Sopenharmony_ci				    clocksource_mmio_readl_down);
19662306a36Sopenharmony_ci	if (ret) {
19762306a36Sopenharmony_ci		pr_err("Failed to register clocksource\n");
19862306a36Sopenharmony_ci		return ret;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
20262306a36Sopenharmony_ci	       timer_of_base(&to) + TIMER_CTL_REG(0));
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* Make sure timer is stopped before playing with interrupts */
20562306a36Sopenharmony_ci	sun4i_clkevt_time_stop(timer_of_base(&to), 0);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* clear timer0 interrupt */
20862306a36Sopenharmony_ci	sun4i_timer_clear_interrupt(timer_of_base(&to));
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
21162306a36Sopenharmony_ci					TIMER_SYNC_TICKS, 0xffffffff);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Enable timer0 interrupt */
21462306a36Sopenharmony_ci	val = readl(timer_of_base(&to) + TIMER_IRQ_EN_REG);
21562306a36Sopenharmony_ci	writel(val | TIMER_IRQ_EN(0), timer_of_base(&to) + TIMER_IRQ_EN_REG);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return ret;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ciTIMER_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
22062306a36Sopenharmony_ci		       sun4i_timer_init);
22162306a36Sopenharmony_ciTIMER_OF_DECLARE(sun8i_a23, "allwinner,sun8i-a23-timer",
22262306a36Sopenharmony_ci		 sun4i_timer_init);
22362306a36Sopenharmony_ciTIMER_OF_DECLARE(sun8i_v3s, "allwinner,sun8i-v3s-timer",
22462306a36Sopenharmony_ci		 sun4i_timer_init);
22562306a36Sopenharmony_ciTIMER_OF_DECLARE(suniv, "allwinner,suniv-f1c100s-timer",
22662306a36Sopenharmony_ci		       sun4i_timer_init);
227