18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright 2016 Freescale Semiconductor, Inc.
48c2ecf20Sopenharmony_ci// Copyright 2017 NXP
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk.h>
78c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
88c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "timer-of.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define TPM_PARAM			0x4
168c2ecf20Sopenharmony_ci#define TPM_PARAM_WIDTH_SHIFT		16
178c2ecf20Sopenharmony_ci#define TPM_PARAM_WIDTH_MASK		(0xff << 16)
188c2ecf20Sopenharmony_ci#define TPM_SC				0x10
198c2ecf20Sopenharmony_ci#define TPM_SC_CMOD_INC_PER_CNT		(0x1 << 3)
208c2ecf20Sopenharmony_ci#define TPM_SC_CMOD_DIV_DEFAULT		0x3
218c2ecf20Sopenharmony_ci#define TPM_SC_CMOD_DIV_MAX		0x7
228c2ecf20Sopenharmony_ci#define TPM_SC_TOF_MASK			(0x1 << 7)
238c2ecf20Sopenharmony_ci#define TPM_CNT				0x14
248c2ecf20Sopenharmony_ci#define TPM_MOD				0x18
258c2ecf20Sopenharmony_ci#define TPM_STATUS			0x1c
268c2ecf20Sopenharmony_ci#define TPM_STATUS_CH0F			BIT(0)
278c2ecf20Sopenharmony_ci#define TPM_C0SC			0x20
288c2ecf20Sopenharmony_ci#define TPM_C0SC_CHIE			BIT(6)
298c2ecf20Sopenharmony_ci#define TPM_C0SC_MODE_SHIFT		2
308c2ecf20Sopenharmony_ci#define TPM_C0SC_MODE_MASK		0x3c
318c2ecf20Sopenharmony_ci#define TPM_C0SC_MODE_SW_COMPARE	0x4
328c2ecf20Sopenharmony_ci#define TPM_C0SC_CHF_MASK		(0x1 << 7)
338c2ecf20Sopenharmony_ci#define TPM_C0V				0x24
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int counter_width;
368c2ecf20Sopenharmony_cistatic void __iomem *timer_base;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic inline void tpm_timer_disable(void)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned int val;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* channel disable */
438c2ecf20Sopenharmony_ci	val = readl(timer_base + TPM_C0SC);
448c2ecf20Sopenharmony_ci	val &= ~(TPM_C0SC_MODE_MASK | TPM_C0SC_CHIE);
458c2ecf20Sopenharmony_ci	writel(val, timer_base + TPM_C0SC);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic inline void tpm_timer_enable(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	unsigned int val;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* channel enabled in sw compare mode */
538c2ecf20Sopenharmony_ci	val = readl(timer_base + TPM_C0SC);
548c2ecf20Sopenharmony_ci	val |= (TPM_C0SC_MODE_SW_COMPARE << TPM_C0SC_MODE_SHIFT) |
558c2ecf20Sopenharmony_ci	       TPM_C0SC_CHIE;
568c2ecf20Sopenharmony_ci	writel(val, timer_base + TPM_C0SC);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic inline void tpm_irq_acknowledge(void)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	writel(TPM_STATUS_CH0F, timer_base + TPM_STATUS);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic inline unsigned long tpm_read_counter(void)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	return readl(timer_base + TPM_CNT);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM)
708c2ecf20Sopenharmony_cistatic struct delay_timer tpm_delay_timer;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic unsigned long tpm_read_current_timer(void)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	return tpm_read_counter();
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci#endif
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic u64 notrace tpm_read_sched_clock(void)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	return tpm_read_counter();
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int tpm_set_next_event(unsigned long delta,
848c2ecf20Sopenharmony_ci				struct clock_event_device *evt)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	unsigned long next, now;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	next = tpm_read_counter();
898c2ecf20Sopenharmony_ci	next += delta;
908c2ecf20Sopenharmony_ci	writel(next, timer_base + TPM_C0V);
918c2ecf20Sopenharmony_ci	now = tpm_read_counter();
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/*
948c2ecf20Sopenharmony_ci	 * NOTE: We observed in a very small probability, the bus fabric
958c2ecf20Sopenharmony_ci	 * contention between GPU and A7 may results a few cycles delay
968c2ecf20Sopenharmony_ci	 * of writing CNT registers which may cause the min_delta event got
978c2ecf20Sopenharmony_ci	 * missed, so we need add a ETIME check here in case it happened.
988c2ecf20Sopenharmony_ci	 */
998c2ecf20Sopenharmony_ci	return (int)(next - now) <= 0 ? -ETIME : 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int tpm_set_state_oneshot(struct clock_event_device *evt)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	tpm_timer_enable();
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int tpm_set_state_shutdown(struct clock_event_device *evt)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	tpm_timer_disable();
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic irqreturn_t tpm_timer_interrupt(int irq, void *dev_id)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct clock_event_device *evt = dev_id;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	tpm_irq_acknowledge();
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	evt->event_handler(evt);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic struct timer_of to_tpm = {
1288c2ecf20Sopenharmony_ci	.flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
1298c2ecf20Sopenharmony_ci	.clkevt = {
1308c2ecf20Sopenharmony_ci		.name			= "i.MX7ULP TPM Timer",
1318c2ecf20Sopenharmony_ci		.rating			= 200,
1328c2ecf20Sopenharmony_ci		.features		= CLOCK_EVT_FEAT_ONESHOT,
1338c2ecf20Sopenharmony_ci		.set_state_shutdown	= tpm_set_state_shutdown,
1348c2ecf20Sopenharmony_ci		.set_state_oneshot	= tpm_set_state_oneshot,
1358c2ecf20Sopenharmony_ci		.set_next_event		= tpm_set_next_event,
1368c2ecf20Sopenharmony_ci		.cpumask		= cpu_possible_mask,
1378c2ecf20Sopenharmony_ci	},
1388c2ecf20Sopenharmony_ci	.of_irq = {
1398c2ecf20Sopenharmony_ci		.handler		= tpm_timer_interrupt,
1408c2ecf20Sopenharmony_ci		.flags			= IRQF_TIMER | IRQF_IRQPOLL,
1418c2ecf20Sopenharmony_ci	},
1428c2ecf20Sopenharmony_ci	.of_clk = {
1438c2ecf20Sopenharmony_ci		.name = "per",
1448c2ecf20Sopenharmony_ci	},
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int __init tpm_clocksource_init(void)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM)
1508c2ecf20Sopenharmony_ci	tpm_delay_timer.read_current_timer = &tpm_read_current_timer;
1518c2ecf20Sopenharmony_ci	tpm_delay_timer.freq = timer_of_rate(&to_tpm) >> 3;
1528c2ecf20Sopenharmony_ci	register_current_timer_delay(&tpm_delay_timer);
1538c2ecf20Sopenharmony_ci#endif
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	sched_clock_register(tpm_read_sched_clock, counter_width,
1568c2ecf20Sopenharmony_ci			     timer_of_rate(&to_tpm) >> 3);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return clocksource_mmio_init(timer_base + TPM_CNT,
1598c2ecf20Sopenharmony_ci				     "imx-tpm",
1608c2ecf20Sopenharmony_ci				     timer_of_rate(&to_tpm) >> 3,
1618c2ecf20Sopenharmony_ci				     to_tpm.clkevt.rating,
1628c2ecf20Sopenharmony_ci				     counter_width,
1638c2ecf20Sopenharmony_ci				     clocksource_mmio_readl_up);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic void __init tpm_clockevent_init(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	clockevents_config_and_register(&to_tpm.clkevt,
1698c2ecf20Sopenharmony_ci					timer_of_rate(&to_tpm) >> 3,
1708c2ecf20Sopenharmony_ci					300,
1718c2ecf20Sopenharmony_ci					GENMASK(counter_width - 1,
1728c2ecf20Sopenharmony_ci					1));
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int __init tpm_timer_init(struct device_node *np)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct clk *ipg;
1788c2ecf20Sopenharmony_ci	int ret;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ipg = of_clk_get_by_name(np, "ipg");
1818c2ecf20Sopenharmony_ci	if (IS_ERR(ipg)) {
1828c2ecf20Sopenharmony_ci		pr_err("tpm: failed to get ipg clk\n");
1838c2ecf20Sopenharmony_ci		return -ENODEV;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	/* enable clk before accessing registers */
1868c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(ipg);
1878c2ecf20Sopenharmony_ci	if (ret) {
1888c2ecf20Sopenharmony_ci		pr_err("tpm: ipg clock enable failed (%d)\n", ret);
1898c2ecf20Sopenharmony_ci		clk_put(ipg);
1908c2ecf20Sopenharmony_ci		return ret;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	ret = timer_of_init(np, &to_tpm);
1948c2ecf20Sopenharmony_ci	if (ret)
1958c2ecf20Sopenharmony_ci		return ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	timer_base = timer_of_base(&to_tpm);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	counter_width = (readl(timer_base + TPM_PARAM)
2008c2ecf20Sopenharmony_ci		& TPM_PARAM_WIDTH_MASK) >> TPM_PARAM_WIDTH_SHIFT;
2018c2ecf20Sopenharmony_ci	/* use rating 200 for 32-bit counter and 150 for 16-bit counter */
2028c2ecf20Sopenharmony_ci	to_tpm.clkevt.rating = counter_width == 0x20 ? 200 : 150;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/*
2058c2ecf20Sopenharmony_ci	 * Initialize tpm module to a known state
2068c2ecf20Sopenharmony_ci	 * 1) Counter disabled
2078c2ecf20Sopenharmony_ci	 * 2) TPM counter operates in up counting mode
2088c2ecf20Sopenharmony_ci	 * 3) Timer Overflow Interrupt disabled
2098c2ecf20Sopenharmony_ci	 * 4) Channel0 disabled
2108c2ecf20Sopenharmony_ci	 * 5) DMA transfers disabled
2118c2ecf20Sopenharmony_ci	 */
2128c2ecf20Sopenharmony_ci	/* make sure counter is disabled */
2138c2ecf20Sopenharmony_ci	writel(0, timer_base + TPM_SC);
2148c2ecf20Sopenharmony_ci	/* TOF is W1C */
2158c2ecf20Sopenharmony_ci	writel(TPM_SC_TOF_MASK, timer_base + TPM_SC);
2168c2ecf20Sopenharmony_ci	writel(0, timer_base + TPM_CNT);
2178c2ecf20Sopenharmony_ci	/* CHF is W1C */
2188c2ecf20Sopenharmony_ci	writel(TPM_C0SC_CHF_MASK, timer_base + TPM_C0SC);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * increase per cnt,
2228c2ecf20Sopenharmony_ci	 * div 8 for 32-bit counter and div 128 for 16-bit counter
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	writel(TPM_SC_CMOD_INC_PER_CNT |
2258c2ecf20Sopenharmony_ci		(counter_width == 0x20 ?
2268c2ecf20Sopenharmony_ci		TPM_SC_CMOD_DIV_DEFAULT : TPM_SC_CMOD_DIV_MAX),
2278c2ecf20Sopenharmony_ci		timer_base + TPM_SC);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/* set MOD register to maximum for free running mode */
2308c2ecf20Sopenharmony_ci	writel(GENMASK(counter_width - 1, 0), timer_base + TPM_MOD);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	tpm_clockevent_init();
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return tpm_clocksource_init();
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(imx7ulp, "fsl,imx7ulp-tpm", tpm_timer_init);
237