timer-realtek.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2025 Realtek Semiconductor Corp.
  4. */
  5. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  6. #include <linux/irqflags.h>
  7. #include <linux/interrupt.h>
  8. #include "timer-of.h"
  9. #define ENBL 1
  10. #define DSBL 0
  11. #define SYSTIMER_RATE 1000000
  12. #define SYSTIMER_MIN_DELTA 0x64
  13. #define SYSTIMER_MAX_DELTA ULONG_MAX
  14. /* SYSTIMER Register Offset (RTK Internal Use) */
  15. #define TS_LW_OFST 0x0
  16. #define TS_HW_OFST 0x4
  17. #define TS_CMP_VAL_LW_OFST 0x8
  18. #define TS_CMP_VAL_HW_OFST 0xC
  19. #define TS_CMP_CTRL_OFST 0x10
  20. #define TS_CMP_STAT_OFST 0x14
  21. /* SYSTIMER CMP CTRL REG Mask */
  22. #define TS_CMP_EN_MASK 0x1
  23. #define TS_WR_EN0_MASK 0x2
  24. static void __iomem *systimer_base;
  25. static u64 rtk_ts64_read(void)
  26. {
  27. u32 low, high;
  28. u64 ts;
  29. /* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
  30. low = readl(systimer_base + TS_LW_OFST);
  31. high = readl(systimer_base + TS_HW_OFST);
  32. ts = ((u64)high << 32) | low;
  33. return ts;
  34. }
  35. static void rtk_cmp_value_write(u64 value)
  36. {
  37. u32 high, low;
  38. low = value & 0xFFFFFFFF;
  39. high = value >> 32;
  40. writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
  41. writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
  42. }
  43. static inline void rtk_cmp_en_write(bool cmp_en)
  44. {
  45. u32 val;
  46. val = TS_WR_EN0_MASK;
  47. if (cmp_en == ENBL)
  48. val |= TS_CMP_EN_MASK;
  49. writel(val, systimer_base + TS_CMP_CTRL_OFST);
  50. }
  51. static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt)
  52. {
  53. u64 cmp_val;
  54. rtk_cmp_en_write(DSBL);
  55. cmp_val = rtk_ts64_read();
  56. /* Set CMP value to current timestamp plus delta_us */
  57. rtk_cmp_value_write(cmp_val + cycles);
  58. rtk_cmp_en_write(ENBL);
  59. return 0;
  60. }
  61. static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
  62. {
  63. struct clock_event_device *clkevt = dev_id;
  64. void __iomem *reg_base;
  65. u32 val;
  66. /* Disable TS CMP Match */
  67. rtk_cmp_en_write(DSBL);
  68. /* Clear TS CMP INTR */
  69. reg_base = systimer_base + TS_CMP_STAT_OFST;
  70. val = readl(reg_base) & TS_CMP_EN_MASK;
  71. writel(val | TS_CMP_EN_MASK, reg_base);
  72. clkevt->event_handler(clkevt);
  73. return IRQ_HANDLED;
  74. }
  75. static int rtk_syst_shutdown(struct clock_event_device *clkevt)
  76. {
  77. void __iomem *reg_base;
  78. u64 cmp_val = 0;
  79. /* Disable TS CMP Match */
  80. rtk_cmp_en_write(DSBL);
  81. /* Set compare value to 0 */
  82. rtk_cmp_value_write(cmp_val);
  83. /* Clear TS CMP INTR */
  84. reg_base = systimer_base + TS_CMP_STAT_OFST;
  85. writel(TS_CMP_EN_MASK, reg_base);
  86. return 0;
  87. }
  88. static struct timer_of rtk_timer_to = {
  89. .flags = TIMER_OF_IRQ | TIMER_OF_BASE,
  90. .clkevt = {
  91. .name = "rtk-clkevt",
  92. .rating = 300,
  93. .cpumask = cpu_possible_mask,
  94. .features = CLOCK_EVT_FEAT_DYNIRQ |
  95. CLOCK_EVT_FEAT_ONESHOT,
  96. .set_next_event = rtk_syst_clkevt_next_event,
  97. .set_state_oneshot = rtk_syst_shutdown,
  98. .set_state_shutdown = rtk_syst_shutdown,
  99. },
  100. .of_irq = {
  101. .flags = IRQF_TIMER | IRQF_IRQPOLL,
  102. .handler = rtk_ts_match_intr_handler,
  103. },
  104. };
  105. static int __init rtk_systimer_init(struct device_node *node)
  106. {
  107. int ret;
  108. ret = timer_of_init(node, &rtk_timer_to);
  109. if (ret)
  110. return ret;
  111. systimer_base = timer_of_base(&rtk_timer_to);
  112. clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE,
  113. SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
  114. return 0;
  115. }
  116. TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);