162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright 2016 Freescale Semiconductor, Inc.
462306a36Sopenharmony_ci// Copyright 2017 NXP
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/clockchips.h>
862306a36Sopenharmony_ci#include <linux/clocksource.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/sched_clock.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "timer-of.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define TPM_PARAM			0x4
1662306a36Sopenharmony_ci#define TPM_PARAM_WIDTH_SHIFT		16
1762306a36Sopenharmony_ci#define TPM_PARAM_WIDTH_MASK		(0xff << 16)
1862306a36Sopenharmony_ci#define TPM_SC				0x10
1962306a36Sopenharmony_ci#define TPM_SC_CMOD_INC_PER_CNT		(0x1 << 3)
2062306a36Sopenharmony_ci#define TPM_SC_CMOD_DIV_DEFAULT		0x3
2162306a36Sopenharmony_ci#define TPM_SC_CMOD_DIV_MAX		0x7
2262306a36Sopenharmony_ci#define TPM_SC_TOF_MASK			(0x1 << 7)
2362306a36Sopenharmony_ci#define TPM_CNT				0x14
2462306a36Sopenharmony_ci#define TPM_MOD				0x18
2562306a36Sopenharmony_ci#define TPM_STATUS			0x1c
2662306a36Sopenharmony_ci#define TPM_STATUS_CH0F			BIT(0)
2762306a36Sopenharmony_ci#define TPM_C0SC			0x20
2862306a36Sopenharmony_ci#define TPM_C0SC_CHIE			BIT(6)
2962306a36Sopenharmony_ci#define TPM_C0SC_MODE_SHIFT		2
3062306a36Sopenharmony_ci#define TPM_C0SC_MODE_MASK		0x3c
3162306a36Sopenharmony_ci#define TPM_C0SC_MODE_SW_COMPARE	0x4
3262306a36Sopenharmony_ci#define TPM_C0SC_CHF_MASK		(0x1 << 7)
3362306a36Sopenharmony_ci#define TPM_C0V				0x24
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int counter_width __ro_after_init;
3662306a36Sopenharmony_cistatic void __iomem *timer_base __ro_after_init;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline void tpm_timer_disable(void)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	unsigned int val;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* channel disable */
4362306a36Sopenharmony_ci	val = readl(timer_base + TPM_C0SC);
4462306a36Sopenharmony_ci	val &= ~(TPM_C0SC_MODE_MASK | TPM_C0SC_CHIE);
4562306a36Sopenharmony_ci	writel(val, timer_base + TPM_C0SC);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline void tpm_timer_enable(void)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	unsigned int val;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* channel enabled in sw compare mode */
5362306a36Sopenharmony_ci	val = readl(timer_base + TPM_C0SC);
5462306a36Sopenharmony_ci	val |= (TPM_C0SC_MODE_SW_COMPARE << TPM_C0SC_MODE_SHIFT) |
5562306a36Sopenharmony_ci	       TPM_C0SC_CHIE;
5662306a36Sopenharmony_ci	writel(val, timer_base + TPM_C0SC);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic inline void tpm_irq_acknowledge(void)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	writel(TPM_STATUS_CH0F, timer_base + TPM_STATUS);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic inline unsigned long tpm_read_counter(void)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	return readl(timer_base + TPM_CNT);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#if defined(CONFIG_ARM)
7062306a36Sopenharmony_cistatic struct delay_timer tpm_delay_timer;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic unsigned long tpm_read_current_timer(void)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	return tpm_read_counter();
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic u64 notrace tpm_read_sched_clock(void)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	return tpm_read_counter();
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci#endif
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int tpm_set_next_event(unsigned long delta,
8462306a36Sopenharmony_ci				struct clock_event_device *evt)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	unsigned long next, now;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	next = tpm_read_counter();
8962306a36Sopenharmony_ci	next += delta;
9062306a36Sopenharmony_ci	writel(next, timer_base + TPM_C0V);
9162306a36Sopenharmony_ci	now = tpm_read_counter();
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/*
9462306a36Sopenharmony_ci	 * NOTE: We observed in a very small probability, the bus fabric
9562306a36Sopenharmony_ci	 * contention between GPU and A7 may results a few cycles delay
9662306a36Sopenharmony_ci	 * of writing CNT registers which may cause the min_delta event got
9762306a36Sopenharmony_ci	 * missed, so we need add a ETIME check here in case it happened.
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci	return (int)(next - now) <= 0 ? -ETIME : 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int tpm_set_state_oneshot(struct clock_event_device *evt)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	tpm_timer_enable();
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int tpm_set_state_shutdown(struct clock_event_device *evt)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	tpm_timer_disable();
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic irqreturn_t tpm_timer_interrupt(int irq, void *dev_id)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct clock_event_device *evt = dev_id;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	tpm_irq_acknowledge();
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	evt->event_handler(evt);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return IRQ_HANDLED;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic struct timer_of to_tpm = {
12862306a36Sopenharmony_ci	.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
12962306a36Sopenharmony_ci	.clkevt = {
13062306a36Sopenharmony_ci		.name			= "i.MX TPM Timer",
13162306a36Sopenharmony_ci		.rating			= 200,
13262306a36Sopenharmony_ci		.features		= CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ,
13362306a36Sopenharmony_ci		.set_state_shutdown	= tpm_set_state_shutdown,
13462306a36Sopenharmony_ci		.set_state_oneshot	= tpm_set_state_oneshot,
13562306a36Sopenharmony_ci		.set_next_event		= tpm_set_next_event,
13662306a36Sopenharmony_ci		.cpumask		= cpu_possible_mask,
13762306a36Sopenharmony_ci	},
13862306a36Sopenharmony_ci	.of_irq = {
13962306a36Sopenharmony_ci		.handler		= tpm_timer_interrupt,
14062306a36Sopenharmony_ci		.flags			= IRQF_TIMER,
14162306a36Sopenharmony_ci	},
14262306a36Sopenharmony_ci	.of_clk = {
14362306a36Sopenharmony_ci		.name = "per",
14462306a36Sopenharmony_ci	},
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int __init tpm_clocksource_init(void)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci#if defined(CONFIG_ARM)
15062306a36Sopenharmony_ci	tpm_delay_timer.read_current_timer = &tpm_read_current_timer;
15162306a36Sopenharmony_ci	tpm_delay_timer.freq = timer_of_rate(&to_tpm) >> 3;
15262306a36Sopenharmony_ci	register_current_timer_delay(&tpm_delay_timer);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	sched_clock_register(tpm_read_sched_clock, counter_width,
15562306a36Sopenharmony_ci			     timer_of_rate(&to_tpm) >> 3);
15662306a36Sopenharmony_ci#endif
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return clocksource_mmio_init(timer_base + TPM_CNT,
15962306a36Sopenharmony_ci				     "imx-tpm",
16062306a36Sopenharmony_ci				     timer_of_rate(&to_tpm) >> 3,
16162306a36Sopenharmony_ci				     to_tpm.clkevt.rating,
16262306a36Sopenharmony_ci				     counter_width,
16362306a36Sopenharmony_ci				     clocksource_mmio_readl_up);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void __init tpm_clockevent_init(void)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	clockevents_config_and_register(&to_tpm.clkevt,
16962306a36Sopenharmony_ci					timer_of_rate(&to_tpm) >> 3,
17062306a36Sopenharmony_ci					300,
17162306a36Sopenharmony_ci					GENMASK(counter_width - 1,
17262306a36Sopenharmony_ci					1));
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int __init tpm_timer_init(struct device_node *np)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct clk *ipg;
17862306a36Sopenharmony_ci	int ret;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ipg = of_clk_get_by_name(np, "ipg");
18162306a36Sopenharmony_ci	if (IS_ERR(ipg)) {
18262306a36Sopenharmony_ci		pr_err("tpm: failed to get ipg clk\n");
18362306a36Sopenharmony_ci		return -ENODEV;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	/* enable clk before accessing registers */
18662306a36Sopenharmony_ci	ret = clk_prepare_enable(ipg);
18762306a36Sopenharmony_ci	if (ret) {
18862306a36Sopenharmony_ci		pr_err("tpm: ipg clock enable failed (%d)\n", ret);
18962306a36Sopenharmony_ci		clk_put(ipg);
19062306a36Sopenharmony_ci		return ret;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	ret = timer_of_init(np, &to_tpm);
19462306a36Sopenharmony_ci	if (ret)
19562306a36Sopenharmony_ci		return ret;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	timer_base = timer_of_base(&to_tpm);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	counter_width = (readl(timer_base + TPM_PARAM)
20062306a36Sopenharmony_ci		& TPM_PARAM_WIDTH_MASK) >> TPM_PARAM_WIDTH_SHIFT;
20162306a36Sopenharmony_ci	/* use rating 200 for 32-bit counter and 150 for 16-bit counter */
20262306a36Sopenharmony_ci	to_tpm.clkevt.rating = counter_width == 0x20 ? 200 : 150;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/*
20562306a36Sopenharmony_ci	 * Initialize tpm module to a known state
20662306a36Sopenharmony_ci	 * 1) Counter disabled
20762306a36Sopenharmony_ci	 * 2) TPM counter operates in up counting mode
20862306a36Sopenharmony_ci	 * 3) Timer Overflow Interrupt disabled
20962306a36Sopenharmony_ci	 * 4) Channel0 disabled
21062306a36Sopenharmony_ci	 * 5) DMA transfers disabled
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci	/* make sure counter is disabled */
21362306a36Sopenharmony_ci	writel(0, timer_base + TPM_SC);
21462306a36Sopenharmony_ci	/* TOF is W1C */
21562306a36Sopenharmony_ci	writel(TPM_SC_TOF_MASK, timer_base + TPM_SC);
21662306a36Sopenharmony_ci	writel(0, timer_base + TPM_CNT);
21762306a36Sopenharmony_ci	/* CHF is W1C */
21862306a36Sopenharmony_ci	writel(TPM_C0SC_CHF_MASK, timer_base + TPM_C0SC);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/*
22162306a36Sopenharmony_ci	 * increase per cnt,
22262306a36Sopenharmony_ci	 * div 8 for 32-bit counter and div 128 for 16-bit counter
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	writel(TPM_SC_CMOD_INC_PER_CNT |
22562306a36Sopenharmony_ci		(counter_width == 0x20 ?
22662306a36Sopenharmony_ci		TPM_SC_CMOD_DIV_DEFAULT : TPM_SC_CMOD_DIV_MAX),
22762306a36Sopenharmony_ci		timer_base + TPM_SC);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* set MOD register to maximum for free running mode */
23062306a36Sopenharmony_ci	writel(GENMASK(counter_width - 1, 0), timer_base + TPM_MOD);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	tpm_clockevent_init();
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return tpm_clocksource_init();
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ciTIMER_OF_DECLARE(imx7ulp, "fsl,imx7ulp-tpm", tpm_timer_init);
237