at master 3.3 kB view raw
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);