| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- // 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);
|