summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/timer-realtek.c
diff options
context:
space:
mode:
authorDanilo Krummrich <dakr@kernel.org>2026-08-13 18:52:04 +0200
committerDanilo Krummrich <dakr@kernel.org>2026-08-17 01:41:36 +0200
commitf146c7bc85a51f95c05e0f8173d484eb301ece78 (patch)
tree07296ae54ed6551492b0c609dc0f5463f61f89ca /drivers/clocksource/timer-realtek.c
downloadlinux-stable-f146c7bc85a51f95c05e0f8173d484eb301ece78.tar.gz
linux-stable-f146c7bc85a51f95c05e0f8173d484eb301ece78.zip
PCI: Add pci_irq_type() to query the allocated interrupt typegrafted
Add a helper that returns PCI_IRQ_MSIX, PCI_IRQ_MSI, or PCI_IRQ_INTX based on the interrupt type the PCI core selected after pci_alloc_irq_vectors(). Several drivers already open-code this check against pdev->msix_enabled and pdev->msi_enabled, or even open code this helper [1]. A common helper avoids the duplication and keeps drivers from accessing the bitfield directly (see also [2]). Acked-by: Bjorn Helgaas <bhelgaas@google.com> Tested-by: John Hubbard <jhubbard@nvidia.com> Link: https://elixir.bootlin.com/linux/v7.1/source/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c#L196 [1] Inspired-by: John Hubbard <jhubbard@nvidia.com> Link: https://lore.kernel.org/all/DKKG2QM3YJYB.Z2H2B2UXJ75N@kernel.org/ [2] Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260813165234.620555-5-dakr@kernel.org [ Add missing pci_irq_type() stub for CONFIG_PCI=n. ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Diffstat (limited to 'drivers/clocksource/timer-realtek.c')
-rw-r--r--drivers/clocksource/timer-realtek.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/drivers/clocksource/timer-realtek.c b/drivers/clocksource/timer-realtek.c
new file mode 100644
index 000000000..4f0439de9
--- /dev/null
+++ b/drivers/clocksource/timer-realtek.c
@@ -0,0 +1,150 @@
+// 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);