* [PATCH 0/2] clocksource: Support ARM SSE(Subsystems for Embedded)
@ 2025-08-21 15:24 Jisheng Zhang
2025-08-21 15:24 ` [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer Jisheng Zhang
2025-08-21 15:24 ` [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver Jisheng Zhang
0 siblings, 2 replies; 9+ messages in thread
From: Jisheng Zhang @ 2025-08-21 15:24 UTC (permalink / raw)
To: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel, devicetree
Here is the ARM SSE(Subsystems for Embedded) timer doc URL:
https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
Although the IP is mostly seen on MCU SoC platforms, but nothing
prevent it from being integrated into linux capable SoC platforms.
The IP core may have a system counter to generate timestamp value,
a system timer to raise an interrupt when a period has elapsed, and
a System Watchdog to detect errant system behaviour then reset the
system if a period elapses without ping.
The differences between this IP and arm mmio arch timer include not
limit to:
1. The system can add the timer frames as many as it want, I.E no
up to 8 timer frames limitation at all.
2. The IP supports watchdog.
3. physical timer only.
4. The system counter can be exposed to so can be under the control of
linux.
Patch1 adds the dt-binding doc for it.
Patch2 introduces a clocksource+watchdog driver for the IP.
Jisheng Zhang (2):
dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer
clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver
.../bindings/timer/arm,sse_timer.yaml | 90 +++
drivers/clocksource/Kconfig | 7 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-sse.c | 540 ++++++++++++++++++
4 files changed, 638 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
create mode 100644 drivers/clocksource/timer-sse.c
--
2.50.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer
2025-08-21 15:24 [PATCH 0/2] clocksource: Support ARM SSE(Subsystems for Embedded) Jisheng Zhang
@ 2025-08-21 15:24 ` Jisheng Zhang
2025-08-21 15:55 ` Jisheng Zhang
` (2 more replies)
2025-08-21 15:24 ` [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver Jisheng Zhang
1 sibling, 3 replies; 9+ messages in thread
From: Jisheng Zhang @ 2025-08-21 15:24 UTC (permalink / raw)
To: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel, devicetree
Add binding doc for the ARM SSE(Subsystems for Embedded) timer. Here
is the document URL:
https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
Although the IP is mostly seen on MCU SoC platforms, but nothing
prevent it from being integrated into linux capable SoC platforms.
The IP core may have a system counter to generate timestamp value,
a system timer to raise an interrupt when a period has elapsed, and
a System Watchdog to detect errant system behaviour then reset the
system if a period elapses without ping.
Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
---
.../bindings/timer/arm,sse_timer.yaml | 90 +++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
diff --git a/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml b/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
new file mode 100644
index 000000000000..37a79f9052d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/timer/arm,sse_timer.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM SSE(Subsystems for Embedded) system timer
+
+maintainers:
+ - Jisheng Zhang <jszhang@kernel.org>
+
+description: |+
+ ARM SSE(Subsystems for Embedded) system timer core may have a system counter
+ to generate timestamp value, a system timer to raise an interrupt when a
+ period has elapsed, and a System Watchdog to detect errant system behaviour
+ then reset the system if a period elapses without ping.
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - arm,sse-timer
+
+ reg:
+ maxItems: 1
+ description: The system counter control frame base
+
+ clocks:
+ maxItems: 1
+
+ '#address-cells':
+ enum: [1, 2]
+
+ '#size-cells':
+ const: 1
+
+ ranges: true
+
+patternProperties:
+ '^frame@[0-9a-f]+$':
+ type: object
+ additionalProperties: false
+ description: A timer node has some frame sub-nodes, each frame can be timer frame or watchdog frame. Each frame has the following properties.
+ properties:
+ interrupts:
+ minItems: 1
+ items:
+ - description: timer irq
+
+ reg:
+ minItems: 1
+ items:
+ - description: 1st view base address
+ - description: 2nd optional view base address if this is a watchdog frame
+
+ required:
+ - interrupts
+ - reg
+
+required:
+ - compatible
+ - reg
+ - '#address-cells'
+ - '#size-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ timer@f7f3e000 {
+ compatible = "arm,sse-timer";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ reg = <0xf7f3e000 0x2000>;
+ clocks = <&core_clk>;
+
+ frame@f7f20000 {
+ reg = <0xf7f20000 0x1000>;
+ interrupts = <0 26 0x8>;
+ };
+
+ frame@f7f30000 {
+ interrupts = <0 15 0x8>;
+ reg = <0xf7f32000 0x1000>,
+ <0xf7f33000 0x1000>;
+ };
+ };
+
+...
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver
2025-08-21 15:24 [PATCH 0/2] clocksource: Support ARM SSE(Subsystems for Embedded) Jisheng Zhang
2025-08-21 15:24 ` [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer Jisheng Zhang
@ 2025-08-21 15:24 ` Jisheng Zhang
2025-08-21 15:52 ` Jisheng Zhang
2025-08-22 9:43 ` kernel test robot
1 sibling, 2 replies; 9+ messages in thread
From: Jisheng Zhang @ 2025-08-21 15:24 UTC (permalink / raw)
To: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-kernel, devicetree
Here is the ARM SSE(Subsystems for Embedded) timer doc URL:
https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
Although the IP is mostly seen on MCU SoC platforms, but nothing
prevent it from being integrated into linux capable SoC platforms.
The IP core may have a system counter to generate timestamp value,
a system timer to raise an interrupt when a period has elapsed, and
a System Watchdog to detect errant system behaviour then reset the
system if a period elapses without ping.
The differences between this IP and arm mmio arch timer include not
limit to:
1. The system can add the timer frames as many as it want, I.E no
up to 8 timer frames limitation at all.
2. The IP supports watchdog.
3. physical timer only.
4. The system counter can be exposed to so can be under the control of
linux.
Introduce a clocksource+watchdog driver for the ARM SSE timer IP.
Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
---
drivers/clocksource/Kconfig | 7 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-sse.c | 540 ++++++++++++++++++++++++++++++++
3 files changed, 548 insertions(+)
create mode 100644 drivers/clocksource/timer-sse.c
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 645f517a1ac2..bfcbd7a1a498 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -152,6 +152,13 @@ config REALTEK_OTTO_TIMER
RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series
such as RTL9301, RTL9302 or RTL9303.
+config SSE_TIMER
+ bool "ARM SSE(Subsystems for Embedded) system timer driver"
+ depends on COMMON_CLK
+ default n
+ help
+ Enables support for the ARM SSE(Subsystems for Embedded) system timer driver.
+
config SUN4I_TIMER
bool "Sun4i timer driver" if COMPILE_TEST
depends on HAS_IOMEM
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 205bf3b0a8f3..dd688807694d 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_BCM2835_TIMER) += bcm2835_timer.o
obj-$(CONFIG_CLPS711X_TIMER) += clps711x-timer.o
obj-$(CONFIG_MXS_TIMER) += mxs_timer.o
obj-$(CONFIG_CLKSRC_PXA) += timer-pxa.o
+obj-$(CONFIG_SSE_TIMER) += timer-sse.o
obj-$(CONFIG_SUN4I_TIMER) += timer-sun4i.o
obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o
obj-$(CONFIG_MESON6_TIMER) += timer-meson6.o
diff --git a/drivers/clocksource/timer-sse.c b/drivers/clocksource/timer-sse.c
new file mode 100644
index 000000000000..cf7d0bf07c2e
--- /dev/null
+++ b/drivers/clocksource/timer-sse.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Synaptics Incorporated
+ *
+ * Author: Jisheng Zhang <jszhang@kernel.org>
+ *
+ * Some code are borrow from
+ * linux/drivers/clocksource/arm_arch_timer.c
+ * Copyright (C) 2011 ARM Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/irq.h>
+#include <linux/irqreturn.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/watchdog.h>
+
+#define CNTCR 0x00
+#define CNTCV 0x08
+
+#define CNTPCT 0x00
+#define CNTP_CVAL 0x20
+#define CNTP_CTL 0x2c
+#define CNTP_CTL_ENABLE BIT(0)
+#define CNTP_CTL_IMASK BIT(1)
+#define CNTP_CTL_ISTATUS BIT(2)
+#define CNTP_AIVAL_RELOAD 0x48
+#define CNTP_AIVAL_CTL 0x4c
+#define CNTP_AIVAL_CTL_EN BIT(0)
+#define CNTP_AIVAL_CTL_CLR BIT(1)
+#define CNTP_CFG 0x50
+#define CNTP_CFG_AIVAL_MASK GENMASK(3, 0)
+#define CNTP_CFG_AIVAL_IMPL 1
+
+#define WCS 0x00
+#define WCS_EN BIT(0)
+#define WCS_WS0 BIT(1)
+#define WCS_WS1 BIT(2)
+#define WOR 0x08
+#define WCV 0x10
+
+#define WRR 0x00
+
+#define SSE_WDT_DEFAULT_SECONDS 30
+
+enum sse_timer_mode {
+ SSE_TIMER_NORMAL,
+ SSE_TIMER_AUTOINC,
+};
+
+struct sse_timer_frame {
+ void __iomem *base;
+ struct clocksource cs;
+ struct clock_event_device ce;
+ u32 ticks_per_jiffy;
+ int irq;
+ enum sse_timer_mode mode;
+};
+
+struct sse_wdt_frame {
+ struct watchdog_device wdd;
+ void __iomem *ctrl_base;
+ void __iomem *refr_base;
+ unsigned long freq;
+ int irq;
+};
+
+struct sse_timer {
+ void __iomem *cntctrl_base;
+ struct clk *clk;
+ struct sse_timer_frame *timers;
+ struct sse_wdt_frame *wdts;
+ int timer_num;
+ int wdt_num;
+};
+
+#define cs_to_sse_timer_frame(x) \
+ container_of(x, struct sse_timer_frame, cs)
+#define ce_to_sse_timer_frame(x) \
+ container_of(x, struct sse_timer_frame, ce)
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0444);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static __always_inline u64 sse_get_cntpct(struct sse_timer_frame *f)
+{
+#ifndef CONFIG_64BIT
+ u32 cnt_lo, cnt_hi, tmp_hi;
+
+ do {
+ cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(f->base + CNTPCT + 4));
+ cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(f->base + CNTPCT));
+ tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(f->base + CNTPCT + 4));
+ } while (cnt_hi != tmp_hi);
+
+ return ((u64) cnt_hi << 32) | cnt_lo;
+#else
+ return readq_relaxed(f->base + CNTPCT);
+#endif
+
+}
+
+static int sse_ce_shutdown(struct clock_event_device *ce)
+{
+ struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
+ u32 val;
+
+ val = readl_relaxed(f->base + CNTP_CTL);
+ val &= ~CNTP_CTL_ENABLE;
+ writel_relaxed(val, f->base + CNTP_CTL);
+ return 0;
+}
+
+static int sse_ce_set_oneshot(struct clock_event_device *ce)
+{
+ struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
+ u32 val;
+
+ f->mode = SSE_TIMER_NORMAL;
+
+ val = readl_relaxed(f->base + CNTP_CTL);
+ if (val & CNTP_CTL_ENABLE) {
+ val &= ~CNTP_CTL_ENABLE;
+ writel_relaxed(val, f->base + CNTP_CTL);
+ }
+
+ writel_relaxed(0, f->base + CNTP_AIVAL_CTL);
+
+ return 0;
+}
+
+static int sse_ce_set_periodic(struct clock_event_device *ce)
+{
+ struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
+ u32 val;
+
+ f->mode = SSE_TIMER_AUTOINC;
+
+ val = readl_relaxed(f->base + CNTP_CTL);
+ if (val & CNTP_CTL_ENABLE) {
+ val &= ~CNTP_CTL_ENABLE;
+ writel_relaxed(val, f->base + CNTP_CTL);
+ }
+
+ val |= CNTP_CTL_ENABLE;
+ val &= ~CNTP_CTL_IMASK;
+
+ writel_relaxed(f->ticks_per_jiffy, f->base + CNTP_AIVAL_RELOAD);
+ writel_relaxed(CNTP_AIVAL_CTL_EN, f->base + CNTP_AIVAL_CTL);
+ writel_relaxed(val, f->base + CNTP_CTL);
+
+ return 0;
+}
+
+static int sse_ce_next_event(unsigned long evt, struct clock_event_device *ce)
+{
+ struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
+ u32 val;
+ u64 cnt;
+
+ val = readl_relaxed(f->base + CNTP_CTL);
+ if (val & CNTP_CTL_ENABLE) {
+ val &= ~CNTP_CTL_ENABLE;
+ writel_relaxed(val, f->base + CNTP_CTL);
+ }
+
+ val |= CNTP_CTL_ENABLE;
+ val &= ~CNTP_CTL_IMASK;
+
+ cnt = sse_get_cntpct(f);
+ writeq_relaxed(evt + cnt, f->base + CNTP_CVAL);
+ writel_relaxed(val, f->base + CNTP_CTL);
+
+ return 0;
+}
+
+static irqreturn_t sse_timer_interrupt(int irq, void *dev_id)
+{
+ struct sse_timer_frame *f = dev_id;
+ u32 val;
+
+ val = readl_relaxed(f->base + CNTP_CTL);
+ if (!(val & CNTP_CTL_ISTATUS))
+ return IRQ_NONE;
+
+ if (f->mode == SSE_TIMER_AUTOINC) {
+ val = readl_relaxed(f->base + CNTP_AIVAL_CTL);
+ val &= ~CNTP_AIVAL_CTL_CLR;
+ writel_relaxed(val, f->base + CNTP_AIVAL_CTL);
+ } else {
+ val |= CNTP_CTL_IMASK;
+ writel_relaxed(val, f->base + CNTP_CTL);
+ }
+ f->ce.event_handler(&f->ce);
+
+ return IRQ_HANDLED;
+}
+
+static int sse_setup_clockevent(struct device *dev, struct sse_timer_frame *f,
+ unsigned long rate)
+{
+ int ret;
+ u32 val = readl_relaxed(f->base + CNTP_CFG);
+
+ f->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
+
+ f->ce.name = "sse-timer";
+ f->ce.rating = 300;
+ f->ce.irq = f->irq;
+ f->ce.cpumask = cpu_possible_mask;
+ f->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
+ f->ce.set_state_shutdown = sse_ce_shutdown;
+ f->ce.set_next_event = sse_ce_next_event;
+ f->ce.set_state_oneshot_stopped = sse_ce_shutdown;
+ if (FIELD_GET(CNTP_CFG_AIVAL_MASK, val) == CNTP_CFG_AIVAL_IMPL) {
+ f->ce.set_state_periodic = sse_ce_set_periodic;
+ f->ce.set_state_oneshot = sse_ce_set_oneshot;
+ }
+
+ clockevents_config_and_register(&f->ce, rate, 0xf, ULONG_MAX);
+
+ ret = devm_request_irq(dev, f->irq, sse_timer_interrupt,
+ IRQF_TIMER | IRQF_NOBALANCING,
+ f->ce.name, f);
+ if (ret) {
+ dev_err(dev, "Unable to register interrupt\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static noinstr u64 cs_sse_get_cntpct(struct clocksource *cs)
+{
+ struct sse_timer_frame *f = cs_to_sse_timer_frame(cs);
+
+ return sse_get_cntpct(f);
+}
+
+static int sse_setup_clocksource(struct device *dev, struct sse_timer_frame *f,
+ unsigned long rate)
+{
+ int ret;
+
+ f->cs.name = "sse-timer";
+ f->cs.rating = 300;
+ f->cs.read = cs_sse_get_cntpct;
+ f->cs.mask = CLOCKSOURCE_MASK(64);
+ f->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+ ret = clocksource_register_hz(&f->cs, rate);
+ if (ret) {
+ dev_err(dev, "Couldn't register clock source.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t sse_wdt_interrupt(int irq, void *dev_id)
+{
+ struct sse_wdt_frame *f = dev_id;
+ u32 val;
+
+ val = readl_relaxed(f->ctrl_base + WCS);
+ if (val & WCS_WS0)
+ watchdog_notify_pretimeout(&f->wdd);
+
+ return IRQ_HANDLED;
+}
+
+static int sse_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
+{
+ struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
+
+ wdd->timeout = timeout;
+
+ /* Pretimeout is 1/2 of timeout */
+ wdd->pretimeout = timeout / 2;
+
+ writel_relaxed(((u64)f->freq / 2) * timeout, f->ctrl_base + WOR);
+
+ return 0;
+}
+
+static int sse_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int timeout)
+{
+ struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
+
+ if (!timeout)
+ return -EINVAL;
+
+ /* pretimeout should not exceed 1/2 of max_timeout */
+ if (timeout * 2 <= f->wdd.max_timeout)
+ return sse_wdt_set_timeout(wdd, timeout * 2);
+
+ return -EINVAL;
+}
+
+static int sse_wdt_ping(struct watchdog_device *wdd)
+{
+ struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(0, f->refr_base + WRR);
+
+ return 0;
+}
+
+static int sse_wdt_start(struct watchdog_device *wdd)
+{
+ struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(WCS_EN, f->ctrl_base + WCS);
+
+ return 0;
+}
+
+static int sse_wdt_stop(struct watchdog_device *wdd)
+{
+ struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
+
+ writel_relaxed(0, f->ctrl_base + WCS);
+
+ return 0;
+}
+
+static const struct watchdog_info sse_wdt_info = {
+ .identity = "ARM SSE Watchdog",
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_PRETIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE |
+ WDIOF_CARDRESET,
+};
+
+static const struct watchdog_ops sse_wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = sse_wdt_start,
+ .stop = sse_wdt_stop,
+ .ping = sse_wdt_ping,
+ .set_timeout = sse_wdt_set_timeout,
+ .set_pretimeout = sse_wdt_set_pretimeout,
+};
+
+static int sse_setup_wdt(struct device *dev, struct sse_wdt_frame *f, unsigned long rate)
+{
+ u32 val;
+ int ret;
+ unsigned int max_pretimeout, timeout = SSE_WDT_DEFAULT_SECONDS;
+
+ f->freq = rate;
+ f->wdd.info = &sse_wdt_info;
+ f->wdd.ops = &sse_wdt_ops;
+ f->wdd.parent = dev;
+
+ max_pretimeout = U32_MAX / f->freq;
+ /* Maximum timeout is twice the pretimeout */
+ f->wdd.max_timeout = max_pretimeout * 2;
+ f->wdd.max_hw_heartbeat_ms = max_pretimeout * 1000;
+ /* Minimum first timeout (pretimeout) is 1, so min_timeout as 2 */
+ f->wdd.min_timeout = 2;
+
+ val = readl_relaxed(f->ctrl_base + WCS);
+ if (val & WCS_WS1) {
+ dev_warn(dev, "System reset by WDT.\n");
+ f->wdd.bootstatus |= WDIOF_CARDRESET;
+ }
+
+ if (val & WCS_EN) {
+ timeout = readl_relaxed(f->ctrl_base + WOR) / 2 / f->freq;
+ set_bit(WDOG_HW_RUNNING, &f->wdd.status);
+ }
+
+ watchdog_set_drvdata(&f->wdd, f);
+ watchdog_set_nowayout(&f->wdd, nowayout);
+ watchdog_init_timeout(&f->wdd, timeout, dev);
+ watchdog_stop_on_reboot(&f->wdd);
+ watchdog_stop_on_unregister(&f->wdd);
+
+ ret = devm_watchdog_register_device(dev, &f->wdd);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, f->irq, sse_wdt_interrupt, 0, "sse-wdt", f);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to register interrupt handler\n");
+
+ return 0;
+}
+
+static int sse_timer_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct sse_timer *sse;
+ unsigned long rate;
+ int i, j, ret;
+
+ sse = devm_kzalloc(&pdev->dev, sizeof(*sse), GFP_KERNEL);
+ if (!sse)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, sse);
+
+ sse->cntctrl_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(sse->cntctrl_base)) {
+ dev_err(&pdev->dev, "Can't map registers\n");
+ return PTR_ERR(sse->cntctrl_base);
+ }
+
+ sse->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(sse->clk)) {
+ dev_err(&pdev->dev, "Can't get timer clock\n");
+ return PTR_ERR(sse->clk);
+ }
+
+ rate = clk_get_rate(sse->clk);
+ if (!rate) {
+ dev_err(&pdev->dev, "Couldn't get parent clock rate\n");
+ return -EINVAL;
+ }
+
+ for_each_available_child_of_node_scoped(np, frame_node) {
+ const __be32 *addr = of_get_address(frame_node, 1, NULL, NULL);
+
+ if (!addr)
+ sse->timer_num++;
+ else
+ sse->wdt_num++;
+ }
+
+ sse->timers = devm_kcalloc(&pdev->dev, sse->timer_num, sizeof(*sse->timers), GFP_KERNEL);
+ if (!sse->timers)
+ return -ENOMEM;
+ sse->wdts = devm_kcalloc(&pdev->dev, sse->wdt_num, sizeof(*sse->wdts), GFP_KERNEL);
+ if (!sse->wdts)
+ return -ENOMEM;
+
+ writel_relaxed(0, sse->cntctrl_base + CNTCR);
+ writeq_relaxed(0, sse->cntctrl_base + CNTCV);
+ writel_relaxed(1, sse->cntctrl_base + CNTCR);
+
+ i = j = 0;
+ for_each_available_child_of_node_scoped(np, frame_node) {
+ struct resource res;
+ void __iomem *base;
+ const __be32 *addr;
+
+ ret = of_address_to_resource(frame_node, 0, &res);
+ if (ret < 0)
+ return ret;
+ base = devm_ioremap_resource(&pdev->dev, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ addr = of_get_address(frame_node, 1, NULL, NULL);
+ if (!addr) {
+ sse->timers[i].base = base;
+
+ ret = of_irq_get(frame_node, 0);
+ if (ret < 0)
+ return ret;
+ sse->timers[i].irq = ret;
+ i++;
+ } else {
+ sse->wdts[j].ctrl_base = base;
+
+ ret = of_address_to_resource(frame_node, 1, &res);
+ if (ret < 0)
+ return ret;
+ sse->wdts[j].refr_base = devm_ioremap_resource(&pdev->dev, &res);
+ if (IS_ERR(sse->wdts[j].refr_base))
+ return PTR_ERR(sse->wdts[j].refr_base);
+
+ ret = of_irq_get(frame_node, 0);
+ if (ret < 0)
+ return ret;
+ sse->wdts[j].irq = ret;
+ j++;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_WATCHDOG)) {
+ for (i = 0; i < sse->wdt_num; i++) {
+ ret = sse_setup_wdt(&pdev->dev, &sse->wdts[i], rate);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for (i = 0; i < sse->timer_num; i++) {
+ ret = sse_setup_clocksource(&pdev->dev, &sse->timers[i], rate);
+ if (ret)
+ goto err_unreg_clocksource;
+ }
+
+ if (sse->timer_num > 0) {
+ ret = sse_setup_clockevent(&pdev->dev, &sse->timers[0], rate);
+ if (ret)
+ goto err_unreg_clocksource;
+ }
+
+ return 0;
+
+err_unreg_clocksource:
+ for (j = 0; j < i; j++)
+ clocksource_unregister(&sse->timers[j].cs);
+ return ret;
+}
+
+static const struct of_device_id sse_timer_of_match[] = {
+ { .compatible = "arm,sse-timer" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sse_timer_of_match);
+
+static struct platform_driver sse_timer_driver = {
+ .probe = sse_timer_probe,
+ .driver = {
+ .name = "sse-timer",
+ .of_match_table = sse_timer_of_match,
+ },
+};
+module_platform_driver(sse_timer_driver);
+
+MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
+MODULE_DESCRIPTION("ARM SSE system timer driver");
+MODULE_LICENSE("GPL");
--
2.50.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver
2025-08-21 15:24 ` [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver Jisheng Zhang
@ 2025-08-21 15:52 ` Jisheng Zhang
2025-08-22 9:43 ` kernel test robot
1 sibling, 0 replies; 9+ messages in thread
From: Jisheng Zhang @ 2025-08-21 15:52 UTC (permalink / raw)
To: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
linux-arm-kernel, linux-watchdog@vger.kernel.org Conor Dooley
Cc: linux-kernel, devicetree
On Thu, Aug 21, 2025 at 11:24:29PM +0800, Jisheng Zhang wrote:
> Here is the ARM SSE(Subsystems for Embedded) timer doc URL:
> https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
>
> Although the IP is mostly seen on MCU SoC platforms, but nothing
> prevent it from being integrated into linux capable SoC platforms.
>
> The IP core may have a system counter to generate timestamp value,
> a system timer to raise an interrupt when a period has elapsed, and
> a System Watchdog to detect errant system behaviour then reset the
> system if a period elapses without ping.
>
> The differences between this IP and arm mmio arch timer include not
> limit to:
> 1. The system can add the timer frames as many as it want, I.E no
> up to 8 timer frames limitation at all.
> 2. The IP supports watchdog.
> 3. physical timer only.
> 4. The system counter can be exposed to so can be under the control of
> linux.
>
> Introduce a clocksource+watchdog driver for the ARM SSE timer IP.
+ arm and wdt maillist
>
> Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
> ---
> drivers/clocksource/Kconfig | 7 +
> drivers/clocksource/Makefile | 1 +
> drivers/clocksource/timer-sse.c | 540 ++++++++++++++++++++++++++++++++
> 3 files changed, 548 insertions(+)
> create mode 100644 drivers/clocksource/timer-sse.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 645f517a1ac2..bfcbd7a1a498 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -152,6 +152,13 @@ config REALTEK_OTTO_TIMER
> RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series
> such as RTL9301, RTL9302 or RTL9303.
>
> +config SSE_TIMER
> + bool "ARM SSE(Subsystems for Embedded) system timer driver"
> + depends on COMMON_CLK
> + default n
> + help
> + Enables support for the ARM SSE(Subsystems for Embedded) system timer driver.
> +
> config SUN4I_TIMER
> bool "Sun4i timer driver" if COMPILE_TEST
> depends on HAS_IOMEM
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 205bf3b0a8f3..dd688807694d 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -33,6 +33,7 @@ obj-$(CONFIG_BCM2835_TIMER) += bcm2835_timer.o
> obj-$(CONFIG_CLPS711X_TIMER) += clps711x-timer.o
> obj-$(CONFIG_MXS_TIMER) += mxs_timer.o
> obj-$(CONFIG_CLKSRC_PXA) += timer-pxa.o
> +obj-$(CONFIG_SSE_TIMER) += timer-sse.o
> obj-$(CONFIG_SUN4I_TIMER) += timer-sun4i.o
> obj-$(CONFIG_SUN5I_HSTIMER) += timer-sun5i.o
> obj-$(CONFIG_MESON6_TIMER) += timer-meson6.o
> diff --git a/drivers/clocksource/timer-sse.c b/drivers/clocksource/timer-sse.c
> new file mode 100644
> index 000000000000..cf7d0bf07c2e
> --- /dev/null
> +++ b/drivers/clocksource/timer-sse.c
> @@ -0,0 +1,540 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2025 Synaptics Incorporated
> + *
> + * Author: Jisheng Zhang <jszhang@kernel.org>
> + *
> + * Some code are borrow from
> + * linux/drivers/clocksource/arm_arch_timer.c
> + * Copyright (C) 2011 ARM Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/clocksource.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/irq.h>
> +#include <linux/irqreturn.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +#include <linux/watchdog.h>
> +
> +#define CNTCR 0x00
> +#define CNTCV 0x08
> +
> +#define CNTPCT 0x00
> +#define CNTP_CVAL 0x20
> +#define CNTP_CTL 0x2c
> +#define CNTP_CTL_ENABLE BIT(0)
> +#define CNTP_CTL_IMASK BIT(1)
> +#define CNTP_CTL_ISTATUS BIT(2)
> +#define CNTP_AIVAL_RELOAD 0x48
> +#define CNTP_AIVAL_CTL 0x4c
> +#define CNTP_AIVAL_CTL_EN BIT(0)
> +#define CNTP_AIVAL_CTL_CLR BIT(1)
> +#define CNTP_CFG 0x50
> +#define CNTP_CFG_AIVAL_MASK GENMASK(3, 0)
> +#define CNTP_CFG_AIVAL_IMPL 1
> +
> +#define WCS 0x00
> +#define WCS_EN BIT(0)
> +#define WCS_WS0 BIT(1)
> +#define WCS_WS1 BIT(2)
> +#define WOR 0x08
> +#define WCV 0x10
> +
> +#define WRR 0x00
> +
> +#define SSE_WDT_DEFAULT_SECONDS 30
> +
> +enum sse_timer_mode {
> + SSE_TIMER_NORMAL,
> + SSE_TIMER_AUTOINC,
> +};
> +
> +struct sse_timer_frame {
> + void __iomem *base;
> + struct clocksource cs;
> + struct clock_event_device ce;
> + u32 ticks_per_jiffy;
> + int irq;
> + enum sse_timer_mode mode;
> +};
> +
> +struct sse_wdt_frame {
> + struct watchdog_device wdd;
> + void __iomem *ctrl_base;
> + void __iomem *refr_base;
> + unsigned long freq;
> + int irq;
> +};
> +
> +struct sse_timer {
> + void __iomem *cntctrl_base;
> + struct clk *clk;
> + struct sse_timer_frame *timers;
> + struct sse_wdt_frame *wdts;
> + int timer_num;
> + int wdt_num;
> +};
> +
> +#define cs_to_sse_timer_frame(x) \
> + container_of(x, struct sse_timer_frame, cs)
> +#define ce_to_sse_timer_frame(x) \
> + container_of(x, struct sse_timer_frame, ce)
> +
> +static bool nowayout = WATCHDOG_NOWAYOUT;
> +module_param(nowayout, bool, 0444);
> +MODULE_PARM_DESC(nowayout,
> + "Watchdog cannot be stopped once started (default="
> + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> +
> +static __always_inline u64 sse_get_cntpct(struct sse_timer_frame *f)
> +{
> +#ifndef CONFIG_64BIT
> + u32 cnt_lo, cnt_hi, tmp_hi;
> +
> + do {
> + cnt_hi = __le32_to_cpu((__le32 __force)__raw_readl(f->base + CNTPCT + 4));
> + cnt_lo = __le32_to_cpu((__le32 __force)__raw_readl(f->base + CNTPCT));
> + tmp_hi = __le32_to_cpu((__le32 __force)__raw_readl(f->base + CNTPCT + 4));
> + } while (cnt_hi != tmp_hi);
> +
> + return ((u64) cnt_hi << 32) | cnt_lo;
> +#else
> + return readq_relaxed(f->base + CNTPCT);
> +#endif
> +
> +}
> +
> +static int sse_ce_shutdown(struct clock_event_device *ce)
> +{
> + struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
> + u32 val;
> +
> + val = readl_relaxed(f->base + CNTP_CTL);
> + val &= ~CNTP_CTL_ENABLE;
> + writel_relaxed(val, f->base + CNTP_CTL);
> + return 0;
> +}
> +
> +static int sse_ce_set_oneshot(struct clock_event_device *ce)
> +{
> + struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
> + u32 val;
> +
> + f->mode = SSE_TIMER_NORMAL;
> +
> + val = readl_relaxed(f->base + CNTP_CTL);
> + if (val & CNTP_CTL_ENABLE) {
> + val &= ~CNTP_CTL_ENABLE;
> + writel_relaxed(val, f->base + CNTP_CTL);
> + }
> +
> + writel_relaxed(0, f->base + CNTP_AIVAL_CTL);
> +
> + return 0;
> +}
> +
> +static int sse_ce_set_periodic(struct clock_event_device *ce)
> +{
> + struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
> + u32 val;
> +
> + f->mode = SSE_TIMER_AUTOINC;
> +
> + val = readl_relaxed(f->base + CNTP_CTL);
> + if (val & CNTP_CTL_ENABLE) {
> + val &= ~CNTP_CTL_ENABLE;
> + writel_relaxed(val, f->base + CNTP_CTL);
> + }
> +
> + val |= CNTP_CTL_ENABLE;
> + val &= ~CNTP_CTL_IMASK;
> +
> + writel_relaxed(f->ticks_per_jiffy, f->base + CNTP_AIVAL_RELOAD);
> + writel_relaxed(CNTP_AIVAL_CTL_EN, f->base + CNTP_AIVAL_CTL);
> + writel_relaxed(val, f->base + CNTP_CTL);
> +
> + return 0;
> +}
> +
> +static int sse_ce_next_event(unsigned long evt, struct clock_event_device *ce)
> +{
> + struct sse_timer_frame *f = ce_to_sse_timer_frame(ce);
> + u32 val;
> + u64 cnt;
> +
> + val = readl_relaxed(f->base + CNTP_CTL);
> + if (val & CNTP_CTL_ENABLE) {
> + val &= ~CNTP_CTL_ENABLE;
> + writel_relaxed(val, f->base + CNTP_CTL);
> + }
> +
> + val |= CNTP_CTL_ENABLE;
> + val &= ~CNTP_CTL_IMASK;
> +
> + cnt = sse_get_cntpct(f);
> + writeq_relaxed(evt + cnt, f->base + CNTP_CVAL);
> + writel_relaxed(val, f->base + CNTP_CTL);
> +
> + return 0;
> +}
> +
> +static irqreturn_t sse_timer_interrupt(int irq, void *dev_id)
> +{
> + struct sse_timer_frame *f = dev_id;
> + u32 val;
> +
> + val = readl_relaxed(f->base + CNTP_CTL);
> + if (!(val & CNTP_CTL_ISTATUS))
> + return IRQ_NONE;
> +
> + if (f->mode == SSE_TIMER_AUTOINC) {
> + val = readl_relaxed(f->base + CNTP_AIVAL_CTL);
> + val &= ~CNTP_AIVAL_CTL_CLR;
> + writel_relaxed(val, f->base + CNTP_AIVAL_CTL);
> + } else {
> + val |= CNTP_CTL_IMASK;
> + writel_relaxed(val, f->base + CNTP_CTL);
> + }
> + f->ce.event_handler(&f->ce);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int sse_setup_clockevent(struct device *dev, struct sse_timer_frame *f,
> + unsigned long rate)
> +{
> + int ret;
> + u32 val = readl_relaxed(f->base + CNTP_CFG);
> +
> + f->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
> +
> + f->ce.name = "sse-timer";
> + f->ce.rating = 300;
> + f->ce.irq = f->irq;
> + f->ce.cpumask = cpu_possible_mask;
> + f->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
> + f->ce.set_state_shutdown = sse_ce_shutdown;
> + f->ce.set_next_event = sse_ce_next_event;
> + f->ce.set_state_oneshot_stopped = sse_ce_shutdown;
> + if (FIELD_GET(CNTP_CFG_AIVAL_MASK, val) == CNTP_CFG_AIVAL_IMPL) {
> + f->ce.set_state_periodic = sse_ce_set_periodic;
> + f->ce.set_state_oneshot = sse_ce_set_oneshot;
> + }
> +
> + clockevents_config_and_register(&f->ce, rate, 0xf, ULONG_MAX);
> +
> + ret = devm_request_irq(dev, f->irq, sse_timer_interrupt,
> + IRQF_TIMER | IRQF_NOBALANCING,
> + f->ce.name, f);
> + if (ret) {
> + dev_err(dev, "Unable to register interrupt\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static noinstr u64 cs_sse_get_cntpct(struct clocksource *cs)
> +{
> + struct sse_timer_frame *f = cs_to_sse_timer_frame(cs);
> +
> + return sse_get_cntpct(f);
> +}
> +
> +static int sse_setup_clocksource(struct device *dev, struct sse_timer_frame *f,
> + unsigned long rate)
> +{
> + int ret;
> +
> + f->cs.name = "sse-timer";
> + f->cs.rating = 300;
> + f->cs.read = cs_sse_get_cntpct;
> + f->cs.mask = CLOCKSOURCE_MASK(64);
> + f->cs.flags = CLOCK_SOURCE_IS_CONTINUOUS;
> +
> + ret = clocksource_register_hz(&f->cs, rate);
> + if (ret) {
> + dev_err(dev, "Couldn't register clock source.\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static irqreturn_t sse_wdt_interrupt(int irq, void *dev_id)
> +{
> + struct sse_wdt_frame *f = dev_id;
> + u32 val;
> +
> + val = readl_relaxed(f->ctrl_base + WCS);
> + if (val & WCS_WS0)
> + watchdog_notify_pretimeout(&f->wdd);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int sse_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
> +{
> + struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
> +
> + wdd->timeout = timeout;
> +
> + /* Pretimeout is 1/2 of timeout */
> + wdd->pretimeout = timeout / 2;
> +
> + writel_relaxed(((u64)f->freq / 2) * timeout, f->ctrl_base + WOR);
> +
> + return 0;
> +}
> +
> +static int sse_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int timeout)
> +{
> + struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
> +
> + if (!timeout)
> + return -EINVAL;
> +
> + /* pretimeout should not exceed 1/2 of max_timeout */
> + if (timeout * 2 <= f->wdd.max_timeout)
> + return sse_wdt_set_timeout(wdd, timeout * 2);
> +
> + return -EINVAL;
> +}
> +
> +static int sse_wdt_ping(struct watchdog_device *wdd)
> +{
> + struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
> +
> + writel_relaxed(0, f->refr_base + WRR);
> +
> + return 0;
> +}
> +
> +static int sse_wdt_start(struct watchdog_device *wdd)
> +{
> + struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
> +
> + writel_relaxed(WCS_EN, f->ctrl_base + WCS);
> +
> + return 0;
> +}
> +
> +static int sse_wdt_stop(struct watchdog_device *wdd)
> +{
> + struct sse_wdt_frame *f = watchdog_get_drvdata(wdd);
> +
> + writel_relaxed(0, f->ctrl_base + WCS);
> +
> + return 0;
> +}
> +
> +static const struct watchdog_info sse_wdt_info = {
> + .identity = "ARM SSE Watchdog",
> + .options = WDIOF_SETTIMEOUT |
> + WDIOF_PRETIMEOUT |
> + WDIOF_KEEPALIVEPING |
> + WDIOF_MAGICCLOSE |
> + WDIOF_CARDRESET,
> +};
> +
> +static const struct watchdog_ops sse_wdt_ops = {
> + .owner = THIS_MODULE,
> + .start = sse_wdt_start,
> + .stop = sse_wdt_stop,
> + .ping = sse_wdt_ping,
> + .set_timeout = sse_wdt_set_timeout,
> + .set_pretimeout = sse_wdt_set_pretimeout,
> +};
> +
> +static int sse_setup_wdt(struct device *dev, struct sse_wdt_frame *f, unsigned long rate)
> +{
> + u32 val;
> + int ret;
> + unsigned int max_pretimeout, timeout = SSE_WDT_DEFAULT_SECONDS;
> +
> + f->freq = rate;
> + f->wdd.info = &sse_wdt_info;
> + f->wdd.ops = &sse_wdt_ops;
> + f->wdd.parent = dev;
> +
> + max_pretimeout = U32_MAX / f->freq;
> + /* Maximum timeout is twice the pretimeout */
> + f->wdd.max_timeout = max_pretimeout * 2;
> + f->wdd.max_hw_heartbeat_ms = max_pretimeout * 1000;
> + /* Minimum first timeout (pretimeout) is 1, so min_timeout as 2 */
> + f->wdd.min_timeout = 2;
> +
> + val = readl_relaxed(f->ctrl_base + WCS);
> + if (val & WCS_WS1) {
> + dev_warn(dev, "System reset by WDT.\n");
> + f->wdd.bootstatus |= WDIOF_CARDRESET;
> + }
> +
> + if (val & WCS_EN) {
> + timeout = readl_relaxed(f->ctrl_base + WOR) / 2 / f->freq;
> + set_bit(WDOG_HW_RUNNING, &f->wdd.status);
> + }
> +
> + watchdog_set_drvdata(&f->wdd, f);
> + watchdog_set_nowayout(&f->wdd, nowayout);
> + watchdog_init_timeout(&f->wdd, timeout, dev);
> + watchdog_stop_on_reboot(&f->wdd);
> + watchdog_stop_on_unregister(&f->wdd);
> +
> + ret = devm_watchdog_register_device(dev, &f->wdd);
> + if (ret)
> + return ret;
> +
> + ret = devm_request_irq(dev, f->irq, sse_wdt_interrupt, 0, "sse-wdt", f);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Failed to register interrupt handler\n");
> +
> + return 0;
> +}
> +
> +static int sse_timer_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + struct sse_timer *sse;
> + unsigned long rate;
> + int i, j, ret;
> +
> + sse = devm_kzalloc(&pdev->dev, sizeof(*sse), GFP_KERNEL);
> + if (!sse)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, sse);
> +
> + sse->cntctrl_base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(sse->cntctrl_base)) {
> + dev_err(&pdev->dev, "Can't map registers\n");
> + return PTR_ERR(sse->cntctrl_base);
> + }
> +
> + sse->clk = devm_clk_get_enabled(&pdev->dev, NULL);
> + if (IS_ERR(sse->clk)) {
> + dev_err(&pdev->dev, "Can't get timer clock\n");
> + return PTR_ERR(sse->clk);
> + }
> +
> + rate = clk_get_rate(sse->clk);
> + if (!rate) {
> + dev_err(&pdev->dev, "Couldn't get parent clock rate\n");
> + return -EINVAL;
> + }
> +
> + for_each_available_child_of_node_scoped(np, frame_node) {
> + const __be32 *addr = of_get_address(frame_node, 1, NULL, NULL);
> +
> + if (!addr)
> + sse->timer_num++;
> + else
> + sse->wdt_num++;
> + }
> +
> + sse->timers = devm_kcalloc(&pdev->dev, sse->timer_num, sizeof(*sse->timers), GFP_KERNEL);
> + if (!sse->timers)
> + return -ENOMEM;
> + sse->wdts = devm_kcalloc(&pdev->dev, sse->wdt_num, sizeof(*sse->wdts), GFP_KERNEL);
> + if (!sse->wdts)
> + return -ENOMEM;
> +
> + writel_relaxed(0, sse->cntctrl_base + CNTCR);
> + writeq_relaxed(0, sse->cntctrl_base + CNTCV);
> + writel_relaxed(1, sse->cntctrl_base + CNTCR);
> +
> + i = j = 0;
> + for_each_available_child_of_node_scoped(np, frame_node) {
> + struct resource res;
> + void __iomem *base;
> + const __be32 *addr;
> +
> + ret = of_address_to_resource(frame_node, 0, &res);
> + if (ret < 0)
> + return ret;
> + base = devm_ioremap_resource(&pdev->dev, &res);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + addr = of_get_address(frame_node, 1, NULL, NULL);
> + if (!addr) {
> + sse->timers[i].base = base;
> +
> + ret = of_irq_get(frame_node, 0);
> + if (ret < 0)
> + return ret;
> + sse->timers[i].irq = ret;
> + i++;
> + } else {
> + sse->wdts[j].ctrl_base = base;
> +
> + ret = of_address_to_resource(frame_node, 1, &res);
> + if (ret < 0)
> + return ret;
> + sse->wdts[j].refr_base = devm_ioremap_resource(&pdev->dev, &res);
> + if (IS_ERR(sse->wdts[j].refr_base))
> + return PTR_ERR(sse->wdts[j].refr_base);
> +
> + ret = of_irq_get(frame_node, 0);
> + if (ret < 0)
> + return ret;
> + sse->wdts[j].irq = ret;
> + j++;
> + }
> + }
> +
> + if (IS_ENABLED(CONFIG_WATCHDOG)) {
> + for (i = 0; i < sse->wdt_num; i++) {
> + ret = sse_setup_wdt(&pdev->dev, &sse->wdts[i], rate);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + for (i = 0; i < sse->timer_num; i++) {
> + ret = sse_setup_clocksource(&pdev->dev, &sse->timers[i], rate);
> + if (ret)
> + goto err_unreg_clocksource;
> + }
> +
> + if (sse->timer_num > 0) {
> + ret = sse_setup_clockevent(&pdev->dev, &sse->timers[0], rate);
> + if (ret)
> + goto err_unreg_clocksource;
> + }
> +
> + return 0;
> +
> +err_unreg_clocksource:
> + for (j = 0; j < i; j++)
> + clocksource_unregister(&sse->timers[j].cs);
> + return ret;
> +}
> +
> +static const struct of_device_id sse_timer_of_match[] = {
> + { .compatible = "arm,sse-timer" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, sse_timer_of_match);
> +
> +static struct platform_driver sse_timer_driver = {
> + .probe = sse_timer_probe,
> + .driver = {
> + .name = "sse-timer",
> + .of_match_table = sse_timer_of_match,
> + },
> +};
> +module_platform_driver(sse_timer_driver);
> +
> +MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
> +MODULE_DESCRIPTION("ARM SSE system timer driver");
> +MODULE_LICENSE("GPL");
> --
> 2.50.1
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer
2025-08-21 15:24 ` [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer Jisheng Zhang
@ 2025-08-21 15:55 ` Jisheng Zhang
2025-08-21 22:39 ` Rob Herring (Arm)
2025-08-22 7:21 ` Krzysztof Kozlowski
2 siblings, 0 replies; 9+ messages in thread
From: Jisheng Zhang @ 2025-08-21 15:55 UTC (permalink / raw)
To: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-watchdog, linux-arm-kernel
Cc: linux-kernel, devicetree
On Thu, Aug 21, 2025 at 11:24:28PM +0800, Jisheng Zhang wrote:
> Add binding doc for the ARM SSE(Subsystems for Embedded) timer. Here
> is the document URL:
> https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
>
> Although the IP is mostly seen on MCU SoC platforms, but nothing
> prevent it from being integrated into linux capable SoC platforms.
>
> The IP core may have a system counter to generate timestamp value,
> a system timer to raise an interrupt when a period has elapsed, and
> a System Watchdog to detect errant system behaviour then reset the
> system if a period elapses without ping.
>
> Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
> ---
> .../bindings/timer/arm,sse_timer.yaml | 90 +++++++++++++++++++
> 1 file changed, 90 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
>
> diff --git a/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml b/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
> new file mode 100644
> index 000000000000..37a79f9052d0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
> @@ -0,0 +1,90 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/timer/arm,sse_timer.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ARM SSE(Subsystems for Embedded) system timer
> +
> +maintainers:
> + - Jisheng Zhang <jszhang@kernel.org>
> +
> +description: |+
> + ARM SSE(Subsystems for Embedded) system timer core may have a system counter
> + to generate timestamp value, a system timer to raise an interrupt when a
> + period has elapsed, and a System Watchdog to detect errant system behaviour
> + then reset the system if a period elapses without ping.
> +
> +properties:
> + compatible:
> + items:
> + - enum:
> + - arm,sse-timer
> +
> + reg:
> + maxItems: 1
> + description: The system counter control frame base
> +
> + clocks:
> + maxItems: 1
> +
> + '#address-cells':
> + enum: [1, 2]
> +
> + '#size-cells':
> + const: 1
> +
> + ranges: true
> +
> +patternProperties:
> + '^frame@[0-9a-f]+$':
> + type: object
> + additionalProperties: false
> + description: A timer node has some frame sub-nodes, each frame can be timer frame or watchdog frame. Each frame has the following properties.
> + properties:
> + interrupts:
> + minItems: 1
> + items:
> + - description: timer irq
> +
> + reg:
> + minItems: 1
> + items:
> + - description: 1st view base address
> + - description: 2nd optional view base address if this is a watchdog frame
> +
> + required:
> + - interrupts
> + - reg
> +
> +required:
> + - compatible
> + - reg
> + - '#address-cells'
> + - '#size-cells'
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + timer@f7f3e000 {
> + compatible = "arm,sse-timer";
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> + reg = <0xf7f3e000 0x2000>;
> + clocks = <&core_clk>;
> +
> + frame@f7f20000 {
> + reg = <0xf7f20000 0x1000>;
> + interrupts = <0 26 0x8>;
> + };
> +
> + frame@f7f30000 {
> + interrupts = <0 15 0x8>;
> + reg = <0xf7f32000 0x1000>,
> + <0xf7f33000 0x1000>;
> + };
> + };
> +
> +...
> --
> 2.50.1
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer
2025-08-21 15:24 ` [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer Jisheng Zhang
2025-08-21 15:55 ` Jisheng Zhang
@ 2025-08-21 22:39 ` Rob Herring (Arm)
2025-08-22 0:41 ` Jisheng Zhang
2025-08-22 7:21 ` Krzysztof Kozlowski
2 siblings, 1 reply; 9+ messages in thread
From: Rob Herring (Arm) @ 2025-08-21 22:39 UTC (permalink / raw)
To: Jisheng Zhang
Cc: Daniel Lezcano, linux-kernel, Thomas Gleixner, devicetree,
Krzysztof Kozlowski, Conor Dooley
On Thu, 21 Aug 2025 23:24:28 +0800, Jisheng Zhang wrote:
> Add binding doc for the ARM SSE(Subsystems for Embedded) timer. Here
> is the document URL:
> https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
>
> Although the IP is mostly seen on MCU SoC platforms, but nothing
> prevent it from being integrated into linux capable SoC platforms.
>
> The IP core may have a system counter to generate timestamp value,
> a system timer to raise an interrupt when a period has elapsed, and
> a System Watchdog to detect errant system behaviour then reset the
> system if a period elapses without ping.
>
> Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
> ---
> .../bindings/timer/arm,sse_timer.yaml | 90 +++++++++++++++++++
> 1 file changed, 90 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
./Documentation/devicetree/bindings/timer/arm,sse_timer.yaml:43:111: [warning] line too long (145 > 110 characters) (line-length)
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml: patternProperties:^frame@[0-9a-f]+$:properties:interrupts: 'oneOf' conditional failed, one must be fixed:
[{'description': 'timer irq'}] is too short
False schema does not allow 1
hint: "minItems" is only needed if less than the "items" list length
from schema $id: http://devicetree.org/meta-schemas/items.yaml#
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250821152429.26995-2-jszhang@kernel.org
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer
2025-08-21 22:39 ` Rob Herring (Arm)
@ 2025-08-22 0:41 ` Jisheng Zhang
0 siblings, 0 replies; 9+ messages in thread
From: Jisheng Zhang @ 2025-08-22 0:41 UTC (permalink / raw)
To: Rob Herring (Arm)
Cc: Daniel Lezcano, linux-kernel, Thomas Gleixner, devicetree,
linux-watchdog,
linux-arm-kernel@lists.infradead.org Krzysztof Kozlowski,
Conor Dooley
On Thu, Aug 21, 2025 at 05:39:48PM -0500, Rob Herring (Arm) wrote:
>
> On Thu, 21 Aug 2025 23:24:28 +0800, Jisheng Zhang wrote:
> > Add binding doc for the ARM SSE(Subsystems for Embedded) timer. Here
> > is the document URL:
> > https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
> >
> > Although the IP is mostly seen on MCU SoC platforms, but nothing
> > prevent it from being integrated into linux capable SoC platforms.
> >
> > The IP core may have a system counter to generate timestamp value,
> > a system timer to raise an interrupt when a period has elapsed, and
> > a System Watchdog to detect errant system behaviour then reset the
> > system if a period elapses without ping.
> >
> > Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
> > ---
> > .../bindings/timer/arm,sse_timer.yaml | 90 +++++++++++++++++++
> > 1 file changed, 90 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
> >
>
> My bot found errors running 'make dt_binding_check' on your patch:
Oops, thanks for the hint. I would like to collect feedbacks to the
implementation itself, then fix the dt errors/warnings in v2.
Thanks
>
> yamllint warnings/errors:
> ./Documentation/devicetree/bindings/timer/arm,sse_timer.yaml:43:111: [warning] line too long (145 > 110 characters) (line-length)
>
> dtschema/dtc warnings/errors:
> /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml: patternProperties:^frame@[0-9a-f]+$:properties:interrupts: 'oneOf' conditional failed, one must be fixed:
> [{'description': 'timer irq'}] is too short
> False schema does not allow 1
> hint: "minItems" is only needed if less than the "items" list length
> from schema $id: http://devicetree.org/meta-schemas/items.yaml#
>
> doc reference errors (make refcheckdocs):
>
> See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/20250821152429.26995-2-jszhang@kernel.org
>
> The base for the series is generally the latest rc1. A different dependency
> should be noted in *this* patch.
>
> If you already ran 'make dt_binding_check' and didn't see the above
> error(s), then make sure 'yamllint' is installed and dt-schema is up to
> date:
>
> pip3 install dtschema --upgrade
>
> Please check and re-submit after running the above command yourself. Note
> that DT_SCHEMA_FILES can be set to your schema file to speed up checking
> your schema. However, it must be unset to test all examples with your schema.
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer
2025-08-21 15:24 ` [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer Jisheng Zhang
2025-08-21 15:55 ` Jisheng Zhang
2025-08-21 22:39 ` Rob Herring (Arm)
@ 2025-08-22 7:21 ` Krzysztof Kozlowski
2 siblings, 0 replies; 9+ messages in thread
From: Krzysztof Kozlowski @ 2025-08-22 7:21 UTC (permalink / raw)
To: Jisheng Zhang
Cc: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-kernel, devicetree
On Thu, Aug 21, 2025 at 11:24:28PM +0800, Jisheng Zhang wrote:
> Add binding doc for the ARM SSE(Subsystems for Embedded) timer. Here
> is the document URL:
> https://developer.arm.com/documentation/107610/0000/System-timer-components?lang=en
>
> Although the IP is mostly seen on MCU SoC platforms, but nothing
> prevent it from being integrated into linux capable SoC platforms.
But is there such integration?
>
> The IP core may have a system counter to generate timestamp value,
> a system timer to raise an interrupt when a period has elapsed, and
> a System Watchdog to detect errant system behaviour then reset the
> system if a period elapses without ping.
>
> Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
> ---
> .../bindings/timer/arm,sse_timer.yaml | 90 +++++++++++++++++++
> 1 file changed, 90 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
>
> diff --git a/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml b/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
> new file mode 100644
> index 000000000000..37a79f9052d0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/arm,sse_timer.yaml
> @@ -0,0 +1,90 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/timer/arm,sse_timer.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: ARM SSE(Subsystems for Embedded) system timer
> +
> +maintainers:
> + - Jisheng Zhang <jszhang@kernel.org>
> +
> +description: |+
Drop |+
> + ARM SSE(Subsystems for Embedded) system timer core may have a system counter
> + to generate timestamp value, a system timer to raise an interrupt when a
> + period has elapsed, and a System Watchdog to detect errant system behaviour
> + then reset the system if a period elapses without ping.
> +
> +properties:
> + compatible:
> + items:
> + - enum:
Drop these two, just const.
> + - arm,sse-timer
> +
> + reg:
> + maxItems: 1
> + description: The system counter control frame base
> +
> + clocks:
> + maxItems: 1
> +
> + '#address-cells':
> + enum: [1, 2]
> +
> + '#size-cells':
> + const: 1
> +
> + ranges: true
> +
> +patternProperties:
> + '^frame@[0-9a-f]+$':
> + type: object
> + additionalProperties: false
> + description: A timer node has some frame sub-nodes, each frame can be timer frame or watchdog frame. Each frame has the following properties.
Please follow Linux coding style.
> + properties:
> + interrupts:
> + minItems: 1
Drop
> + items:
> + - description: timer irq
Drop, instead maxItems: 1
> +
> + reg:
Keep order as in every other binding (and DTS coding style).
> + minItems: 1
> + items:
> + - description: 1st view base address
> + - description: 2nd optional view base address if this is a watchdog frame
> +
> + required:
> + - interrupts
> + - reg
Keep DTS coding style order.
> +
> +required:
> + - compatible
> + - reg
> + - '#address-cells'
> + - '#size-cells'
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + timer@f7f3e000 {
> + compatible = "arm,sse-timer";
> + #address-cells = <1>;
Keep order as in DTS coding style.
> + #size-cells = <1>;
> + ranges;
> + reg = <0xf7f3e000 0x2000>;
> + clocks = <&core_clk>;
> +
> + frame@f7f20000 {
> + reg = <0xf7f20000 0x1000>;
> + interrupts = <0 26 0x8>;
Use proper defines
> + };
> +
> + frame@f7f30000 {
> + interrupts = <0 15 0x8>;
> + reg = <0xf7f32000 0x1000>,
> + <0xf7f33000 0x1000>;
> + };
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver
2025-08-21 15:24 ` [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver Jisheng Zhang
2025-08-21 15:52 ` Jisheng Zhang
@ 2025-08-22 9:43 ` kernel test robot
1 sibling, 0 replies; 9+ messages in thread
From: kernel test robot @ 2025-08-22 9:43 UTC (permalink / raw)
To: Jisheng Zhang, Daniel Lezcano, Thomas Gleixner, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: oe-kbuild-all, linux-kernel, devicetree
Hi Jisheng,
kernel test robot noticed the following build errors:
[auto build test ERROR on tip/timers/core]
[also build test ERROR on robh/for-next linus/master v6.17-rc2 next-20250822]
[cannot apply to daniel-lezcano/clockevents/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jisheng-Zhang/dt-bindings-timer-Add-ARM-SSE-Subsystems-for-Embedded-timer/20250822-000122
base: tip/timers/core
patch link: https://lore.kernel.org/r/20250821152429.26995-3-jszhang%40kernel.org
patch subject: [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20250822/202508221736.qO3Ji6Dw-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250822/202508221736.qO3Ji6Dw-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508221736.qO3Ji6Dw-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/clocksource/timer-sse.c: In function 'sse_setup_clockevent':
>> drivers/clocksource/timer-sse.c:228:13: error: implicit declaration of function 'FIELD_GET' [-Wimplicit-function-declaration]
228 | if (FIELD_GET(CNTP_CFG_AIVAL_MASK, val) == CNTP_CFG_AIVAL_IMPL) {
| ^~~~~~~~~
vim +/FIELD_GET +228 drivers/clocksource/timer-sse.c
211
212 static int sse_setup_clockevent(struct device *dev, struct sse_timer_frame *f,
213 unsigned long rate)
214 {
215 int ret;
216 u32 val = readl_relaxed(f->base + CNTP_CFG);
217
218 f->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
219
220 f->ce.name = "sse-timer";
221 f->ce.rating = 300;
222 f->ce.irq = f->irq;
223 f->ce.cpumask = cpu_possible_mask;
224 f->ce.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ;
225 f->ce.set_state_shutdown = sse_ce_shutdown;
226 f->ce.set_next_event = sse_ce_next_event;
227 f->ce.set_state_oneshot_stopped = sse_ce_shutdown;
> 228 if (FIELD_GET(CNTP_CFG_AIVAL_MASK, val) == CNTP_CFG_AIVAL_IMPL) {
229 f->ce.set_state_periodic = sse_ce_set_periodic;
230 f->ce.set_state_oneshot = sse_ce_set_oneshot;
231 }
232
233 clockevents_config_and_register(&f->ce, rate, 0xf, ULONG_MAX);
234
235 ret = devm_request_irq(dev, f->irq, sse_timer_interrupt,
236 IRQF_TIMER | IRQF_NOBALANCING,
237 f->ce.name, f);
238 if (ret) {
239 dev_err(dev, "Unable to register interrupt\n");
240 return ret;
241 }
242
243 return 0;
244 }
245
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-08-22 9:44 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-21 15:24 [PATCH 0/2] clocksource: Support ARM SSE(Subsystems for Embedded) Jisheng Zhang
2025-08-21 15:24 ` [PATCH 1/2] dt-bindings: timer: Add ARM SSE(Subsystems for Embedded) timer Jisheng Zhang
2025-08-21 15:55 ` Jisheng Zhang
2025-08-21 22:39 ` Rob Herring (Arm)
2025-08-22 0:41 ` Jisheng Zhang
2025-08-22 7:21 ` Krzysztof Kozlowski
2025-08-21 15:24 ` [PATCH 2/2] clocksource/drivers: Add ARM SSE(Subsystems for Embedded) Timer driver Jisheng Zhang
2025-08-21 15:52 ` Jisheng Zhang
2025-08-22 9:43 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).