Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
3.3 kB · 150 lines
C
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151// 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);