File Index
Files and directories
Sign in
FILE

timer-realtek.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clocksource/timer-realtek.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (c) 2025 Realtek Semiconductor Corp.
  4 */
  5
  6#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
  7
  8#include <linux/irqflags.h>
  9#include <linux/interrupt.h>
 10#include "timer-of.h"
 11
 12#define ENBL 1
 13#define DSBL 0
 14
 15#define SYSTIMER_RATE 1000000
 16#define SYSTIMER_MIN_DELTA 0x64
 17#define SYSTIMER_MAX_DELTA ULONG_MAX
 18
 19/* SYSTIMER Register Offset (RTK Internal Use) */
 20#define TS_LW_OFST 0x0
 21#define TS_HW_OFST 0x4
 22#define TS_CMP_VAL_LW_OFST 0x8
 23#define TS_CMP_VAL_HW_OFST 0xC
 24#define TS_CMP_CTRL_OFST 0x10
 25#define TS_CMP_STAT_OFST 0x14
 26
 27/* SYSTIMER CMP CTRL REG Mask */
 28#define TS_CMP_EN_MASK 0x1
 29#define TS_WR_EN0_MASK 0x2
 30
 31static void __iomem *systimer_base;
 32
 33static u64 rtk_ts64_read(void)
 34{
 35	u32 low, high;
 36	u64 ts;
 37
 38	/* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
 39	low = readl(systimer_base + TS_LW_OFST);
 40	high = readl(systimer_base + TS_HW_OFST);
 41	ts = ((u64)high << 32) | low;
 42
 43	return ts;
 44}
 45
 46static void rtk_cmp_value_write(u64 value)
 47{
 48	u32 high, low;
 49
 50	low = value & 0xFFFFFFFF;
 51	high = value >> 32;
 52
 53	writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
 54	writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
 55}
 56
 57static inline void rtk_cmp_en_write(bool cmp_en)
 58{
 59	u32 val;
 60
 61	val = TS_WR_EN0_MASK;
 62	if (cmp_en == ENBL)
 63		val |= TS_CMP_EN_MASK;
 64
 65	writel(val, systimer_base + TS_CMP_CTRL_OFST);
 66}
 67
 68static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt)
 69{
 70	u64 cmp_val;
 71
 72	rtk_cmp_en_write(DSBL);
 73	cmp_val = rtk_ts64_read();
 74
 75	/* Set CMP value to current timestamp plus delta_us */
 76	rtk_cmp_value_write(cmp_val + cycles);
 77	rtk_cmp_en_write(ENBL);
 78	return 0;
 79}
 80
 81static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
 82{
 83	struct clock_event_device *clkevt = dev_id;
 84	void __iomem *reg_base;
 85	u32 val;
 86
 87	/* Disable TS CMP Match */
 88	rtk_cmp_en_write(DSBL);
 89
 90	/* Clear TS CMP INTR */
 91	reg_base = systimer_base + TS_CMP_STAT_OFST;
 92	val = readl(reg_base) & TS_CMP_EN_MASK;
 93	writel(val | TS_CMP_EN_MASK, reg_base);
 94	clkevt->event_handler(clkevt);
 95
 96	return IRQ_HANDLED;
 97}
 98
 99static int rtk_syst_shutdown(struct clock_event_device *clkevt)
100{
101	void __iomem *reg_base;
102	u64 cmp_val = 0;
103
104	/* Disable TS CMP Match */
105	rtk_cmp_en_write(DSBL);
106	/* Set compare value to 0 */
107	rtk_cmp_value_write(cmp_val);
108
109	/* Clear TS CMP INTR */
110	reg_base = systimer_base + TS_CMP_STAT_OFST;
111	writel(TS_CMP_EN_MASK, reg_base);
112	return 0;
113}
114
115static struct timer_of rtk_timer_to = {
116	.flags = TIMER_OF_IRQ | TIMER_OF_BASE,
117
118	.clkevt = {
119		.name			= "rtk-clkevt",
120		.rating			= 300,
121		.cpumask		= cpu_possible_mask,
122		.features		= CLOCK_EVT_FEAT_DYNIRQ |
123					  CLOCK_EVT_FEAT_ONESHOT,
124		.set_next_event		= rtk_syst_clkevt_next_event,
125		.set_state_oneshot	= rtk_syst_shutdown,
126		.set_state_shutdown	= rtk_syst_shutdown,
127	},
128
129	.of_irq = {
130		.flags			= IRQF_TIMER | IRQF_IRQPOLL,
131		.handler		= rtk_ts_match_intr_handler,
132	},
133};
134
135static int __init rtk_systimer_init(struct device_node *node)
136{
137	int ret;
138
139	ret = timer_of_init(node, &rtk_timer_to);
140	if (ret)
141		return ret;
142
143	systimer_base = timer_of_base(&rtk_timer_to);
144	clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE,
145					SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
146
147	return 0;
148}
149
150TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);
~/ Read-only file preview.