// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2025 Realtek Semiconductor Corp. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/irqflags.h>#include <linux/interrupt.h>#include "timer-of.h" #define ENBL 1#define DSBL 0 #define SYSTIMER_RATE 1000000#define SYSTIMER_MIN_DELTA 0x64#define SYSTIMER_MAX_DELTA ULONG_MAX /* SYSTIMER Register Offset (RTK Internal Use) */#define TS_LW_OFST 0x0#define TS_HW_OFST 0x4#define TS_CMP_VAL_LW_OFST 0x8#define TS_CMP_VAL_HW_OFST 0xC#define TS_CMP_CTRL_OFST 0x10#define TS_CMP_STAT_OFST 0x14 /* SYSTIMER CMP CTRL REG Mask */#define TS_CMP_EN_MASK 0x1#define TS_WR_EN0_MASK 0x2 static void __iomem *systimer_base; static u64 rtk_ts64_read(void){ u32 low, high; u64 ts; /* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */ low = readl(systimer_base + TS_LW_OFST); high = readl(systimer_base + TS_HW_OFST); ts = ((u64)high << 32) | low; return ts;} static void rtk_cmp_value_write(u64 value){ u32 high, low; low = value & 0xFFFFFFFF; high = value >> 32; writel(high, systimer_base + TS_CMP_VAL_HW_OFST); writel(low, systimer_base + TS_CMP_VAL_LW_OFST);} static inline void rtk_cmp_en_write(bool cmp_en){ u32 val; val = TS_WR_EN0_MASK; if (cmp_en == ENBL) val |= TS_CMP_EN_MASK; writel(val, systimer_base + TS_CMP_CTRL_OFST);} static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt){ u64 cmp_val; rtk_cmp_en_write(DSBL); cmp_val = rtk_ts64_read(); /* Set CMP value to current timestamp plus delta_us */ rtk_cmp_value_write(cmp_val + cycles); rtk_cmp_en_write(ENBL); return 0;} static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id){ struct clock_event_device *clkevt = dev_id; void __iomem *reg_base; u32 val; /* Disable TS CMP Match */ rtk_cmp_en_write(DSBL); /* Clear TS CMP INTR */ reg_base = systimer_base + TS_CMP_STAT_OFST; val = readl(reg_base) & TS_CMP_EN_MASK; writel(val | TS_CMP_EN_MASK, reg_base); clkevt->event_handler(clkevt); return IRQ_HANDLED;} static int rtk_syst_shutdown(struct clock_event_device *clkevt){ void __iomem *reg_base; u64 cmp_val = 0; /* Disable TS CMP Match */ rtk_cmp_en_write(DSBL); /* Set compare value to 0 */ rtk_cmp_value_write(cmp_val); /* Clear TS CMP INTR */ reg_base = systimer_base + TS_CMP_STAT_OFST; writel(TS_CMP_EN_MASK, reg_base); return 0;} static struct timer_of rtk_timer_to = { .flags = TIMER_OF_IRQ | TIMER_OF_BASE, .clkevt = { .name = "rtk-clkevt", .rating = 300, .cpumask = cpu_possible_mask, .features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_ONESHOT, .set_next_event = rtk_syst_clkevt_next_event, .set_state_oneshot = rtk_syst_shutdown, .set_state_shutdown = rtk_syst_shutdown, }, .of_irq = { .flags = IRQF_TIMER | IRQF_IRQPOLL, .handler = rtk_ts_match_intr_handler, },}; static int __init rtk_systimer_init(struct device_node *node){ int ret; ret = timer_of_init(node, &rtk_timer_to); if (ret) return ret; systimer_base = timer_of_base(&rtk_timer_to); clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE, SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA); return 0;} TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025 Realtek Semiconductor Corp.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/irqflags.h>
#include <linux/interrupt.h>
#include "timer-of.h"
#define ENBL 1
#define DSBL 0
#define SYSTIMER_RATE 1000000
#define SYSTIMER_MIN_DELTA 0x64
#define SYSTIMER_MAX_DELTA ULONG_MAX
/* SYSTIMER Register Offset (RTK Internal Use) */
#define TS_LW_OFST 0x0
#define TS_HW_OFST 0x4
#define TS_CMP_VAL_LW_OFST 0x8
#define TS_CMP_VAL_HW_OFST 0xC
#define TS_CMP_CTRL_OFST 0x10
#define TS_CMP_STAT_OFST 0x14
/* SYSTIMER CMP CTRL REG Mask */
#define TS_CMP_EN_MASK 0x1
#define TS_WR_EN0_MASK 0x2
static void __iomem *systimer_base;
static u64 rtk_ts64_read(void)
{
u32 low, high;
u64 ts;
/* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
low = readl(systimer_base + TS_LW_OFST);
high = readl(systimer_base + TS_HW_OFST);
ts = ((u64)high << 32) | low;
return ts;
}
static void rtk_cmp_value_write(u64 value)
{
u32 high, low;
low = value & 0xFFFFFFFF;
high = value >> 32;
writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
}
static inline void rtk_cmp_en_write(bool cmp_en)
{
u32 val;
val = TS_WR_EN0_MASK;
if (cmp_en == ENBL)
val |= TS_CMP_EN_MASK;
writel(val, systimer_base + TS_CMP_CTRL_OFST);
}
static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt)
{
u64 cmp_val;
rtk_cmp_en_write(DSBL);
cmp_val = rtk_ts64_read();
/* Set CMP value to current timestamp plus delta_us */
rtk_cmp_value_write(cmp_val + cycles);
rtk_cmp_en_write(ENBL);
return 0;
}
static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
{
struct clock_event_device *clkevt = dev_id;
void __iomem *reg_base;
u32 val;
/* Disable TS CMP Match */
rtk_cmp_en_write(DSBL);
/* Clear TS CMP INTR */
reg_base = systimer_base + TS_CMP_STAT_OFST;
val = readl(reg_base) & TS_CMP_EN_MASK;
writel(val | TS_CMP_EN_MASK, reg_base);
clkevt->event_handler(clkevt);
return IRQ_HANDLED;
}
static int rtk_syst_shutdown(struct clock_event_device *clkevt)
{
void __iomem *reg_base;
u64 cmp_val = 0;
/* Disable TS CMP Match */
rtk_cmp_en_write(DSBL);
/* Set compare value to 0 */
rtk_cmp_value_write(cmp_val);
/* Clear TS CMP INTR */
reg_base = systimer_base + TS_CMP_STAT_OFST;
writel(TS_CMP_EN_MASK, reg_base);
return 0;
}
static struct timer_of rtk_timer_to = {
.flags = TIMER_OF_IRQ | TIMER_OF_BASE,
.clkevt = {
.name = "rtk-clkevt",
.rating = 300,
.cpumask = cpu_possible_mask,
.features = CLOCK_EVT_FEAT_DYNIRQ |
CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = rtk_syst_clkevt_next_event,
.set_state_oneshot = rtk_syst_shutdown,
.set_state_shutdown = rtk_syst_shutdown,
},
.of_irq = {
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = rtk_ts_match_intr_handler,
},
};
static int __init rtk_systimer_init(struct device_node *node)
{
int ret;
ret = timer_of_init(node, &rtk_timer_to);
if (ret)
return ret;
systimer_base = timer_of_base(&rtk_timer_to);
clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE,
SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
return 0;
}
TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);