162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MediaTek SoCs CPUX General Purpose Timer handling
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Based on timer-mediatek.c:
662306a36Sopenharmony_ci * Copyright (C) 2014 Matthias Brugger <matthias.bgg@gmail.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2022 Collabora Ltd.
962306a36Sopenharmony_ci *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/clockchips.h>
1562306a36Sopenharmony_ci#include <linux/clocksource.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/irqreturn.h>
1862306a36Sopenharmony_ci#include <linux/sched_clock.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include "timer-of.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define TIMER_SYNC_TICKS        3
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* cpux mcusys wrapper */
2562306a36Sopenharmony_ci#define CPUX_CON_REG		0x0
2662306a36Sopenharmony_ci#define CPUX_IDX_REG		0x4
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* cpux */
2962306a36Sopenharmony_ci#define CPUX_IDX_GLOBAL_CTRL	0x0
3062306a36Sopenharmony_ci #define CPUX_ENABLE		BIT(0)
3162306a36Sopenharmony_ci #define CPUX_CLK_DIV_MASK	GENMASK(10, 8)
3262306a36Sopenharmony_ci #define CPUX_CLK_DIV1		BIT(8)
3362306a36Sopenharmony_ci #define CPUX_CLK_DIV2		BIT(9)
3462306a36Sopenharmony_ci #define CPUX_CLK_DIV4		BIT(10)
3562306a36Sopenharmony_ci#define CPUX_IDX_GLOBAL_IRQ	0x30
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic u32 mtk_cpux_readl(u32 reg_idx, struct timer_of *to)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG);
4062306a36Sopenharmony_ci	return readl(timer_of_base(to) + CPUX_CON_REG);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic void mtk_cpux_writel(u32 val, u32 reg_idx, struct timer_of *to)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	writel(reg_idx, timer_of_base(to) + CPUX_IDX_REG);
4662306a36Sopenharmony_ci	writel(val, timer_of_base(to) + CPUX_CON_REG);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void mtk_cpux_set_irq(struct timer_of *to, bool enable)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	const unsigned long *irq_mask = cpumask_bits(cpu_possible_mask);
5262306a36Sopenharmony_ci	u32 val;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	val = mtk_cpux_readl(CPUX_IDX_GLOBAL_IRQ, to);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (enable)
5762306a36Sopenharmony_ci		val |= *irq_mask;
5862306a36Sopenharmony_ci	else
5962306a36Sopenharmony_ci		val &= ~(*irq_mask);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	mtk_cpux_writel(val, CPUX_IDX_GLOBAL_IRQ, to);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int mtk_cpux_clkevt_shutdown(struct clock_event_device *clkevt)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	/* Clear any irq */
6762306a36Sopenharmony_ci	mtk_cpux_set_irq(to_timer_of(clkevt), false);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/*
7062306a36Sopenharmony_ci	 * Disabling CPUXGPT timer will crash the platform, especially
7162306a36Sopenharmony_ci	 * if Trusted Firmware is using it (usually, for sleep states),
7262306a36Sopenharmony_ci	 * so we only mask the IRQ and call it a day.
7362306a36Sopenharmony_ci	 */
7462306a36Sopenharmony_ci	return 0;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int mtk_cpux_clkevt_resume(struct clock_event_device *clkevt)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	mtk_cpux_set_irq(to_timer_of(clkevt), true);
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct timer_of to = {
8462306a36Sopenharmony_ci	/*
8562306a36Sopenharmony_ci	 * There are per-cpu interrupts for the CPUX General Purpose Timer
8662306a36Sopenharmony_ci	 * but since this timer feeds the AArch64 System Timer we can rely
8762306a36Sopenharmony_ci	 * on the CPU timer PPIs as well, so we don't declare TIMER_OF_IRQ.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	.flags = TIMER_OF_BASE | TIMER_OF_CLOCK,
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	.clkevt = {
9262306a36Sopenharmony_ci		.name = "mtk-cpuxgpt",
9362306a36Sopenharmony_ci		.cpumask = cpu_possible_mask,
9462306a36Sopenharmony_ci		.rating = 10,
9562306a36Sopenharmony_ci		.set_state_shutdown = mtk_cpux_clkevt_shutdown,
9662306a36Sopenharmony_ci		.tick_resume = mtk_cpux_clkevt_resume,
9762306a36Sopenharmony_ci	},
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int __init mtk_cpux_init(struct device_node *node)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	u32 freq, val;
10362306a36Sopenharmony_ci	int ret;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* If this fails, bad things are about to happen... */
10662306a36Sopenharmony_ci	ret = timer_of_init(node, &to);
10762306a36Sopenharmony_ci	if (ret) {
10862306a36Sopenharmony_ci		WARN(1, "Cannot start CPUX timers.\n");
10962306a36Sopenharmony_ci		return ret;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/*
11362306a36Sopenharmony_ci	 * Check if we're given a clock with the right frequency for this
11462306a36Sopenharmony_ci	 * timer, otherwise warn but keep going with the setup anyway, as
11562306a36Sopenharmony_ci	 * that makes it possible to still boot the kernel, even though
11662306a36Sopenharmony_ci	 * it may not work correctly (random lockups, etc).
11762306a36Sopenharmony_ci	 * The reason behind this is that having an early UART may not be
11862306a36Sopenharmony_ci	 * possible for everyone and this gives a chance to retrieve kmsg
11962306a36Sopenharmony_ci	 * for eventual debugging even on consumer devices.
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	freq = timer_of_rate(&to);
12262306a36Sopenharmony_ci	if (freq > 13000000)
12362306a36Sopenharmony_ci		WARN(1, "Requested unsupported timer frequency %u\n", freq);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Clock input is 26MHz, set DIV2 to achieve 13MHz clock */
12662306a36Sopenharmony_ci	val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to);
12762306a36Sopenharmony_ci	val &= ~CPUX_CLK_DIV_MASK;
12862306a36Sopenharmony_ci	val |= CPUX_CLK_DIV2;
12962306a36Sopenharmony_ci	mtk_cpux_writel(val, CPUX_IDX_GLOBAL_CTRL, &to);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Enable all CPUXGPT timers */
13262306a36Sopenharmony_ci	val = mtk_cpux_readl(CPUX_IDX_GLOBAL_CTRL, &to);
13362306a36Sopenharmony_ci	mtk_cpux_writel(val | CPUX_ENABLE, CPUX_IDX_GLOBAL_CTRL, &to);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
13662306a36Sopenharmony_ci					TIMER_SYNC_TICKS, 0xffffffff);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ciTIMER_OF_DECLARE(mtk_mt6795, "mediatek,mt6795-systimer", mtk_cpux_init);
141