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 31 static void __iomem *systimer_base; 32 33 static 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 46 static 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 57 static 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 68 static 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 81 static 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 99 static 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 115 static 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 135 static 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 150 TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init); 151