mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v5 0/3] clocksource/drivers/timer-ti-dm: Add clocksource and clockevent support
@ 2026-05-08 13:39 Markus Schneider-Pargmann (TI)
  2026-05-08 13:39 ` [PATCH v5 1/3] clocksource/drivers/timer-ti-dm: Fix property name in comment Markus Schneider-Pargmann (TI)
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Markus Schneider-Pargmann (TI) @ 2026-05-08 13:39 UTC (permalink / raw)
  To: Thomas Gleixner, Daniel Lezcano
  Cc: Vishal Mahaveer, Kevin Hilman, Dhruva Gole, Sebin Francis,
	Kendall Willis, Akashdeep Kaur, linux-kernel,
	Markus Schneider-Pargmann (TI)

Hi,

this series adds support for both clocksource and clockevent to the TI
Dual-Mode Timer driver. This can be used as a time source for low power
modes in which the CPUs are temporarily not running.

The driver looks for two always on timers in the DT to be used for
clocksource and clockevent. These are then handled as reserved and can
not be used for normal use.

This series is based on v7.1-rc1.

Best
Markus

Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
---
Changes in v5:
- Rebased to v7.1-rc1
- Remove complicated pre-detection of always on timers. Instead use them
  as they are probed.
- Link to v4: https://lore.kernel.org/r/20260309-topic-ti-dm-clkevt-v6-16-v4-0-bb8d2a04c45e@baylibre.com

Changes in v4:
- Rebased onto v7.0
- Link to v3: https://lore.kernel.org/r/20260204-topic-ti-dm-clkevt-v6-16-v3-0-83e65d01f4ae@baylibre.com

Changes in v3:
- Use devm_add_action_or_reset() to cleanup clocksource registration
- Use RESOURCE_SIZE_MAX instead of -1 if no timers suitable for
  clocksource or clockevent were found
- Link to v2: https://lore.kernel.org/r/20251216-topic-ti-dm-clkevt-v6-16-v2-0-bfd7dd085c19@baylibre.com

Changes in v2:
- Rebased to v6.19-rc1
- Previous compile issues because of the __init section of
  sched_clock_register() was solved upstream by removing it from the
  __init section in
  https://lore.kernel.org/r/20250602151853.1942521-8-daniel.lezcano@linaro.org
- Link to v1: https://lore.kernel.org/r/20250623-topic-ti-dm-clkevt-v6-16-v1-0-b00086761ee1@baylibre.com

---
Markus Schneider-Pargmann (TI) (3):
      clocksource/drivers/timer-ti-dm: Fix property name in comment
      clocksource/drivers/timer-ti-dm: Add clocksource support
      clocksource/drivers/timer-ti-dm: Add clockevent support

 drivers/clocksource/timer-ti-dm-systimer.c |   2 +-
 drivers/clocksource/timer-ti-dm.c          | 217 +++++++++++++++++++++++++++++
 2 files changed, 218 insertions(+), 1 deletion(-)
---
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20250623-topic-ti-dm-clkevt-v6-16-b5e6599b7c01

Best regards,
-- 
Markus Schneider-Pargmann (TI) <msp@baylibre.com>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v5 1/3] clocksource/drivers/timer-ti-dm: Fix property name in comment
  2026-05-08 13:39 [PATCH v5 0/3] clocksource/drivers/timer-ti-dm: Add clocksource and clockevent support Markus Schneider-Pargmann (TI)
@ 2026-05-08 13:39 ` Markus Schneider-Pargmann (TI)
  2026-06-13 14:33   ` [tip: timers/clocksource] " tip-bot2 for Markus Schneider-Pargmann (TI)
  2026-05-08 13:39 ` [PATCH v5 2/3] clocksource/drivers/timer-ti-dm: Add clocksource support Markus Schneider-Pargmann (TI)
  2026-05-08 13:39 ` [PATCH v5 3/3] clocksource/drivers/timer-ti-dm: Add clockevent support Markus Schneider-Pargmann (TI)
  2 siblings, 1 reply; 7+ messages in thread
From: Markus Schneider-Pargmann (TI) @ 2026-05-08 13:39 UTC (permalink / raw)
  To: Thomas Gleixner, Daniel Lezcano
  Cc: Vishal Mahaveer, Kevin Hilman, Dhruva Gole, Sebin Francis,
	Kendall Willis, Akashdeep Kaur, linux-kernel,
	Markus Schneider-Pargmann (TI)

ti,always-on property doesn't exist. ti,timer-alwon is meant here. Fix
this minor bug in the comment.

Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
---
 drivers/clocksource/timer-ti-dm-systimer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clocksource/timer-ti-dm-systimer.c b/drivers/clocksource/timer-ti-dm-systimer.c
index eb0dfe4b9b7caf31c21dd065a69f38641cb0c053..3804c1234522499bb43cf174afc017cedb79ea38 100644
--- a/drivers/clocksource/timer-ti-dm-systimer.c
+++ b/drivers/clocksource/timer-ti-dm-systimer.c
@@ -226,7 +226,7 @@ static bool __init dmtimer_is_preferred(struct device_node *np)
  * Some omap3 boards with unreliable oscillator must not use the counter_32k
  * or dmtimer1 with 32 KiHz source. Additionally, the boards with unreliable
  * oscillator should really set counter_32k as disabled, and delete dmtimer1
- * ti,always-on property, but let's not count on it. For these quirky cases,
+ * ti,timer-alwon property, but let's not count on it. For these quirky cases,
  * we prefer using the always-on secure dmtimer12 with the internal 32 KiHz
  * clock as the clocksource, and any available dmtimer as clockevent.
  *

-- 
2.53.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v5 2/3] clocksource/drivers/timer-ti-dm: Add clocksource support
  2026-05-08 13:39 [PATCH v5 0/3] clocksource/drivers/timer-ti-dm: Add clocksource and clockevent support Markus Schneider-Pargmann (TI)
  2026-05-08 13:39 ` [PATCH v5 1/3] clocksource/drivers/timer-ti-dm: Fix property name in comment Markus Schneider-Pargmann (TI)
@ 2026-05-08 13:39 ` Markus Schneider-Pargmann (TI)
  2026-06-13 14:33   ` [tip: timers/clocksource] " tip-bot2 for Markus Schneider-Pargmann (TI)
  2026-05-08 13:39 ` [PATCH v5 3/3] clocksource/drivers/timer-ti-dm: Add clockevent support Markus Schneider-Pargmann (TI)
  2 siblings, 1 reply; 7+ messages in thread
From: Markus Schneider-Pargmann (TI) @ 2026-05-08 13:39 UTC (permalink / raw)
  To: Thomas Gleixner, Daniel Lezcano
  Cc: Vishal Mahaveer, Kevin Hilman, Dhruva Gole, Sebin Francis,
	Kendall Willis, Akashdeep Kaur, linux-kernel,
	Markus Schneider-Pargmann (TI)

Add support for using the TI Dual-Mode Timer as a clocksource. The
driver automatically picks the first timer that is marked as always-on
on with the "ti,timer-alwon" property to be the clocksource.

The timer can then be used for CPU independent time keeping.

Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
---
 drivers/clocksource/timer-ti-dm.c | 103 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
index 793e7cdcb1b16b58db3a81668e3c8144efc7baaf..98cc343087689d8ce208e2d105ce5ed2c6722dd6 100644
--- a/drivers/clocksource/timer-ti-dm.c
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -20,6 +20,7 @@
 
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/clocksource.h>
 #include <linux/cpu_pm.h>
 #include <linux/module.h>
 #include <linux/io.h>
@@ -29,6 +30,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/dmtimer-omap.h>
+#include <linux/sched_clock.h>
 
 #include <clocksource/timer-ti-dm.h>
 #include <linux/delay.h>
@@ -148,6 +150,14 @@ static u32 omap_reserved_systimers;
 static LIST_HEAD(omap_timer_list);
 static DEFINE_SPINLOCK(dm_timer_lock);
 
+struct dmtimer_clocksource {
+	struct clocksource dev;
+	struct dmtimer *timer;
+	unsigned int loadval;
+};
+
+static void __iomem *omap_dm_timer_sched_clock_counter;
+
 enum {
 	REQUEST_ANY = 0,
 	REQUEST_BY_ID,
@@ -1185,6 +1195,92 @@ static const struct dev_pm_ops omap_dm_timer_pm_ops = {
 
 static const struct of_device_id omap_timer_match[];
 
+static struct dmtimer_clocksource *omap_dm_timer_to_clocksource(struct clocksource *cs)
+{
+	return container_of(cs, struct dmtimer_clocksource, dev);
+}
+
+static u64 omap_dm_timer_read_cycles(struct clocksource *cs)
+{
+	struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs);
+	struct dmtimer *timer = clksrc->timer;
+
+	return (u64)__omap_dm_timer_read_counter(timer);
+}
+
+static u64 notrace omap_dm_timer_read_sched_clock(void)
+{
+	/* Posted mode is not active here, so we can read directly */
+	return readl_relaxed(omap_dm_timer_sched_clock_counter);
+}
+
+static void omap_dm_timer_clocksource_suspend(struct clocksource *cs)
+{
+	struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs);
+	struct dmtimer *timer = clksrc->timer;
+
+	clksrc->loadval = __omap_dm_timer_read_counter(timer);
+	__omap_dm_timer_stop(timer);
+}
+
+static void omap_dm_timer_clocksource_resume(struct clocksource *cs)
+{
+	struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs);
+	struct dmtimer *timer = clksrc->timer;
+
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clksrc->loadval);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR);
+}
+
+static void omap_dm_timer_clocksource_unregister(void *data)
+{
+	struct clocksource *cs = data;
+
+	clocksource_unregister(cs);
+}
+
+static int omap_dm_timer_setup_clocksource(struct dmtimer *timer)
+{
+	struct device *dev = &timer->pdev->dev;
+	struct dmtimer_clocksource *clksrc;
+	int err;
+
+	__omap_dm_timer_init_regs(timer);
+
+	timer->reserved = 1;
+
+	clksrc = devm_kzalloc(dev, sizeof(*clksrc), GFP_KERNEL);
+	if (!clksrc)
+		return -ENOMEM;
+
+	clksrc->timer = timer;
+
+	clksrc->dev.name = "omap_dm_timer";
+	clksrc->dev.rating = 300;
+	clksrc->dev.read = omap_dm_timer_read_cycles;
+	clksrc->dev.mask = CLOCKSOURCE_MASK(32);
+	clksrc->dev.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+	clksrc->dev.suspend = omap_dm_timer_clocksource_suspend;
+	clksrc->dev.resume = omap_dm_timer_clocksource_resume;
+
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0);
+	dmtimer_write(timer, OMAP_TIMER_LOAD_REG, 0);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR);
+
+	omap_dm_timer_sched_clock_counter = timer->func_base + _OMAP_TIMER_COUNTER_OFFSET;
+	sched_clock_register(omap_dm_timer_read_sched_clock, 32, timer->fclk_rate);
+
+	err = clocksource_register_hz(&clksrc->dev, timer->fclk_rate);
+	if (err)
+		return dev_err_probe(dev, err, "Could not register as clocksource\n");
+
+	err = devm_add_action_or_reset(dev, omap_dm_timer_clocksource_unregister, &clksrc->dev);
+	if (err)
+		return dev_err_probe(dev, err, "Could not register clocksource_unregister action\n");
+
+	return 0;
+}
+
 /**
  * omap_dm_timer_probe - probe function called for every registered device
  * @pdev:	pointer to current timer platform device
@@ -1272,6 +1368,13 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
 
 	timer->pdev = pdev;
 
+	if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk) &&
+	    !omap_dm_timer_sched_clock_counter) {
+		ret = omap_dm_timer_setup_clocksource(timer);
+		if (ret)
+			return ret;
+	}
+
 	pm_runtime_enable(dev);
 
 	if (!timer->reserved) {

-- 
2.53.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v5 3/3] clocksource/drivers/timer-ti-dm: Add clockevent support
  2026-05-08 13:39 [PATCH v5 0/3] clocksource/drivers/timer-ti-dm: Add clocksource and clockevent support Markus Schneider-Pargmann (TI)
  2026-05-08 13:39 ` [PATCH v5 1/3] clocksource/drivers/timer-ti-dm: Fix property name in comment Markus Schneider-Pargmann (TI)
  2026-05-08 13:39 ` [PATCH v5 2/3] clocksource/drivers/timer-ti-dm: Add clocksource support Markus Schneider-Pargmann (TI)
@ 2026-05-08 13:39 ` Markus Schneider-Pargmann (TI)
  2026-06-13 14:33   ` [tip: timers/clocksource] " tip-bot2 for Markus Schneider-Pargmann (TI)
  2 siblings, 1 reply; 7+ messages in thread
From: Markus Schneider-Pargmann (TI) @ 2026-05-08 13:39 UTC (permalink / raw)
  To: Thomas Gleixner, Daniel Lezcano
  Cc: Vishal Mahaveer, Kevin Hilman, Dhruva Gole, Sebin Francis,
	Kendall Willis, Akashdeep Kaur, linux-kernel,
	Markus Schneider-Pargmann (TI)

Add support for using the TI Dual-Mode Timer for clockevents. The second
always on device with the "ti,timer-alwon" property is selected to be
used for clockevents. The first one is used as clocksource.

This allows clockevents to be setup independently of the CPU.

Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
---
 drivers/clocksource/timer-ti-dm.c | 124 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 5 deletions(-)

diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
index 98cc343087689d8ce208e2d105ce5ed2c6722dd6..bd06afb7d5226ac185ff2d149863d00bedd0f609 100644
--- a/drivers/clocksource/timer-ti-dm.c
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -21,8 +21,10 @@
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clocksource.h>
+#include <linux/clockchips.h>
 #include <linux/cpu_pm.h>
 #include <linux/module.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/device.h>
 #include <linux/err.h>
@@ -156,6 +158,13 @@ struct dmtimer_clocksource {
 	unsigned int loadval;
 };
 
+struct omap_dm_timer_clockevent {
+	struct clock_event_device dev;
+	struct dmtimer *timer;
+	u32 period;
+};
+
+static bool omap_dm_timer_clockevent_setup;
 static void __iomem *omap_dm_timer_sched_clock_counter;
 
 enum {
@@ -1281,6 +1290,106 @@ static int omap_dm_timer_setup_clocksource(struct dmtimer *timer)
 	return 0;
 }
 
+static struct omap_dm_timer_clockevent *to_dm_timer_clockevent(struct clock_event_device *evt)
+{
+	return container_of(evt, struct omap_dm_timer_clockevent, dev);
+}
+
+static int omap_dm_timer_evt_set_next_event(unsigned long cycles,
+					    struct clock_event_device *evt)
+{
+	struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt);
+	struct dmtimer *timer = clkevt->timer;
+
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0xffffffff - cycles);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST);
+
+	return 0;
+}
+
+static int omap_dm_timer_evt_shutdown(struct clock_event_device *evt)
+{
+	struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt);
+	struct dmtimer *timer = clkevt->timer;
+
+	__omap_dm_timer_stop(timer);
+
+	return 0;
+}
+
+static int omap_dm_timer_evt_set_periodic(struct clock_event_device *evt)
+{
+	struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt);
+	struct dmtimer *timer = clkevt->timer;
+
+	omap_dm_timer_evt_shutdown(evt);
+
+	omap_dm_timer_set_load(&timer->cookie, clkevt->period);
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clkevt->period);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG,
+		      OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST);
+
+	return 0;
+}
+
+static irqreturn_t omap_dm_timer_evt_interrupt(int irq, void *dev_id)
+{
+	struct omap_dm_timer_clockevent *clkevt = dev_id;
+	struct dmtimer *timer = clkevt->timer;
+
+	__omap_dm_timer_write_status(timer, OMAP_TIMER_INT_OVERFLOW);
+
+	clkevt->dev.event_handler(&clkevt->dev);
+
+	return IRQ_HANDLED;
+}
+
+static int omap_dm_timer_setup_clockevent(struct dmtimer *timer)
+{
+	struct device *dev = &timer->pdev->dev;
+	struct omap_dm_timer_clockevent *clkevt;
+	int ret;
+
+	clkevt = devm_kzalloc(dev, sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		return -ENOMEM;
+
+	timer->reserved = 1;
+	clkevt->timer = timer;
+
+	clkevt->dev.name = "omap_dm_timer";
+	clkevt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	clkevt->dev.rating = 300;
+	clkevt->dev.set_next_event = omap_dm_timer_evt_set_next_event;
+	clkevt->dev.set_state_shutdown = omap_dm_timer_evt_shutdown;
+	clkevt->dev.set_state_periodic = omap_dm_timer_evt_set_periodic;
+	clkevt->dev.set_state_oneshot = omap_dm_timer_evt_shutdown;
+	clkevt->dev.set_state_oneshot_stopped = omap_dm_timer_evt_shutdown;
+	clkevt->dev.tick_resume = omap_dm_timer_evt_shutdown;
+	clkevt->dev.cpumask = cpu_possible_mask;
+	clkevt->period = 0xffffffff - DIV_ROUND_CLOSEST(timer->fclk_rate, HZ);
+
+	__omap_dm_timer_init_regs(timer);
+	__omap_dm_timer_stop(timer);
+	__omap_dm_timer_enable_posted(timer);
+
+	ret = devm_request_irq(dev, timer->irq, omap_dm_timer_evt_interrupt,
+			       IRQF_TIMER, "omap_dm_timer_clockevent", clkevt);
+	if (ret) {
+		dev_err(dev, "Failed to request interrupt: %d\n", ret);
+		return ret;
+	}
+
+	__omap_dm_timer_int_enable(timer, OMAP_TIMER_INT_OVERFLOW);
+
+	clockevents_config_and_register(&clkevt->dev, timer->fclk_rate,
+					3,
+					0xffffffff);
+
+	omap_dm_timer_clockevent_setup = true;
+	return 0;
+}
+
 /**
  * omap_dm_timer_probe - probe function called for every registered device
  * @pdev:	pointer to current timer platform device
@@ -1368,11 +1477,16 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
 
 	timer->pdev = pdev;
 
-	if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk) &&
-	    !omap_dm_timer_sched_clock_counter) {
-		ret = omap_dm_timer_setup_clocksource(timer);
-		if (ret)
-			return ret;
+	if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk)) {
+		if (!omap_dm_timer_sched_clock_counter) {
+			ret = omap_dm_timer_setup_clocksource(timer);
+			if (ret)
+				return ret;
+		} else if (!omap_dm_timer_clockevent_setup) {
+			ret = omap_dm_timer_setup_clockevent(timer);
+			if (ret)
+				return ret;
+		}
 	}
 
 	pm_runtime_enable(dev);

-- 
2.53.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tip: timers/clocksource] clocksource/drivers/timer-ti-dm: Add clockevent support
  2026-05-08 13:39 ` [PATCH v5 3/3] clocksource/drivers/timer-ti-dm: Add clockevent support Markus Schneider-Pargmann (TI)
@ 2026-06-13 14:33   ` tip-bot2 for Markus Schneider-Pargmann (TI)
  0 siblings, 0 replies; 7+ messages in thread
From: tip-bot2 for Markus Schneider-Pargmann (TI) @ 2026-06-13 14:33 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Markus Schneider-Pargmann (TI), Daniel Lezcano, x86, linux-kernel

The following commit has been merged into the timers/clocksource branch of tip:

Commit-ID:     e393cca0388c2fe6f67d4658b2e05f57e244285b
Gitweb:        https://git.kernel.org/tip/e393cca0388c2fe6f67d4658b2e05f57e244285b
Author:        Markus Schneider-Pargmann (TI) <msp@baylibre.com>
AuthorDate:    Fri, 08 May 2026 15:39:29 +02:00
Committer:     Daniel Lezcano <daniel.lezcano@kernel.org>
CommitterDate: Wed, 03 Jun 2026 10:06:02 +02:00

clocksource/drivers/timer-ti-dm: Add clockevent support

Add support for using the TI Dual-Mode Timer for clockevents. The second
always on device with the "ti,timer-alwon" property is selected to be
used for clockevents. The first one is used as clocksource.

This allows clockevents to be setup independently of the CPU.

Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org>
Link: https://patch.msgid.link/20260508-topic-ti-dm-clkevt-v6-16-v5-3-61d546a0aff9@baylibre.com
---
 drivers/clocksource/timer-ti-dm.c | 124 +++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 5 deletions(-)

diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
index 98cc343..bd06afb 100644
--- a/drivers/clocksource/timer-ti-dm.c
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -21,8 +21,10 @@
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clocksource.h>
+#include <linux/clockchips.h>
 #include <linux/cpu_pm.h>
 #include <linux/module.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/device.h>
 #include <linux/err.h>
@@ -156,6 +158,13 @@ struct dmtimer_clocksource {
 	unsigned int loadval;
 };
 
+struct omap_dm_timer_clockevent {
+	struct clock_event_device dev;
+	struct dmtimer *timer;
+	u32 period;
+};
+
+static bool omap_dm_timer_clockevent_setup;
 static void __iomem *omap_dm_timer_sched_clock_counter;
 
 enum {
@@ -1281,6 +1290,106 @@ static int omap_dm_timer_setup_clocksource(struct dmtimer *timer)
 	return 0;
 }
 
+static struct omap_dm_timer_clockevent *to_dm_timer_clockevent(struct clock_event_device *evt)
+{
+	return container_of(evt, struct omap_dm_timer_clockevent, dev);
+}
+
+static int omap_dm_timer_evt_set_next_event(unsigned long cycles,
+					    struct clock_event_device *evt)
+{
+	struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt);
+	struct dmtimer *timer = clkevt->timer;
+
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0xffffffff - cycles);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST);
+
+	return 0;
+}
+
+static int omap_dm_timer_evt_shutdown(struct clock_event_device *evt)
+{
+	struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt);
+	struct dmtimer *timer = clkevt->timer;
+
+	__omap_dm_timer_stop(timer);
+
+	return 0;
+}
+
+static int omap_dm_timer_evt_set_periodic(struct clock_event_device *evt)
+{
+	struct omap_dm_timer_clockevent *clkevt = to_dm_timer_clockevent(evt);
+	struct dmtimer *timer = clkevt->timer;
+
+	omap_dm_timer_evt_shutdown(evt);
+
+	omap_dm_timer_set_load(&timer->cookie, clkevt->period);
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clkevt->period);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG,
+		      OMAP_TIMER_CTRL_AR | OMAP_TIMER_CTRL_ST);
+
+	return 0;
+}
+
+static irqreturn_t omap_dm_timer_evt_interrupt(int irq, void *dev_id)
+{
+	struct omap_dm_timer_clockevent *clkevt = dev_id;
+	struct dmtimer *timer = clkevt->timer;
+
+	__omap_dm_timer_write_status(timer, OMAP_TIMER_INT_OVERFLOW);
+
+	clkevt->dev.event_handler(&clkevt->dev);
+
+	return IRQ_HANDLED;
+}
+
+static int omap_dm_timer_setup_clockevent(struct dmtimer *timer)
+{
+	struct device *dev = &timer->pdev->dev;
+	struct omap_dm_timer_clockevent *clkevt;
+	int ret;
+
+	clkevt = devm_kzalloc(dev, sizeof(*clkevt), GFP_KERNEL);
+	if (!clkevt)
+		return -ENOMEM;
+
+	timer->reserved = 1;
+	clkevt->timer = timer;
+
+	clkevt->dev.name = "omap_dm_timer";
+	clkevt->dev.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	clkevt->dev.rating = 300;
+	clkevt->dev.set_next_event = omap_dm_timer_evt_set_next_event;
+	clkevt->dev.set_state_shutdown = omap_dm_timer_evt_shutdown;
+	clkevt->dev.set_state_periodic = omap_dm_timer_evt_set_periodic;
+	clkevt->dev.set_state_oneshot = omap_dm_timer_evt_shutdown;
+	clkevt->dev.set_state_oneshot_stopped = omap_dm_timer_evt_shutdown;
+	clkevt->dev.tick_resume = omap_dm_timer_evt_shutdown;
+	clkevt->dev.cpumask = cpu_possible_mask;
+	clkevt->period = 0xffffffff - DIV_ROUND_CLOSEST(timer->fclk_rate, HZ);
+
+	__omap_dm_timer_init_regs(timer);
+	__omap_dm_timer_stop(timer);
+	__omap_dm_timer_enable_posted(timer);
+
+	ret = devm_request_irq(dev, timer->irq, omap_dm_timer_evt_interrupt,
+			       IRQF_TIMER, "omap_dm_timer_clockevent", clkevt);
+	if (ret) {
+		dev_err(dev, "Failed to request interrupt: %d\n", ret);
+		return ret;
+	}
+
+	__omap_dm_timer_int_enable(timer, OMAP_TIMER_INT_OVERFLOW);
+
+	clockevents_config_and_register(&clkevt->dev, timer->fclk_rate,
+					3,
+					0xffffffff);
+
+	omap_dm_timer_clockevent_setup = true;
+	return 0;
+}
+
 /**
  * omap_dm_timer_probe - probe function called for every registered device
  * @pdev:	pointer to current timer platform device
@@ -1368,11 +1477,16 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
 
 	timer->pdev = pdev;
 
-	if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk) &&
-	    !omap_dm_timer_sched_clock_counter) {
-		ret = omap_dm_timer_setup_clocksource(timer);
-		if (ret)
-			return ret;
+	if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk)) {
+		if (!omap_dm_timer_sched_clock_counter) {
+			ret = omap_dm_timer_setup_clocksource(timer);
+			if (ret)
+				return ret;
+		} else if (!omap_dm_timer_clockevent_setup) {
+			ret = omap_dm_timer_setup_clockevent(timer);
+			if (ret)
+				return ret;
+		}
 	}
 
 	pm_runtime_enable(dev);

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tip: timers/clocksource] clocksource/drivers/timer-ti-dm: Add clocksource support
  2026-05-08 13:39 ` [PATCH v5 2/3] clocksource/drivers/timer-ti-dm: Add clocksource support Markus Schneider-Pargmann (TI)
@ 2026-06-13 14:33   ` tip-bot2 for Markus Schneider-Pargmann (TI)
  0 siblings, 0 replies; 7+ messages in thread
From: tip-bot2 for Markus Schneider-Pargmann (TI) @ 2026-06-13 14:33 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Markus Schneider-Pargmann (TI), Daniel Lezcano, x86, linux-kernel

The following commit has been merged into the timers/clocksource branch of tip:

Commit-ID:     b8eeeca5545659c5d67264b151784e74b18c8254
Gitweb:        https://git.kernel.org/tip/b8eeeca5545659c5d67264b151784e74b18c8254
Author:        Markus Schneider-Pargmann (TI) <msp@baylibre.com>
AuthorDate:    Fri, 08 May 2026 15:39:28 +02:00
Committer:     Daniel Lezcano <daniel.lezcano@kernel.org>
CommitterDate: Wed, 03 Jun 2026 10:05:47 +02:00

clocksource/drivers/timer-ti-dm: Add clocksource support

Add support for using the TI Dual-Mode Timer as a clocksource. The
driver automatically picks the first timer that is marked as always-on
on with the "ti,timer-alwon" property to be the clocksource.

The timer can then be used for CPU independent time keeping.

Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org>
Link: https://patch.msgid.link/20260508-topic-ti-dm-clkevt-v6-16-v5-2-61d546a0aff9@baylibre.com
---
 drivers/clocksource/timer-ti-dm.c | 103 +++++++++++++++++++++++++++++-
 1 file changed, 103 insertions(+)

diff --git a/drivers/clocksource/timer-ti-dm.c b/drivers/clocksource/timer-ti-dm.c
index 793e7cd..98cc343 100644
--- a/drivers/clocksource/timer-ti-dm.c
+++ b/drivers/clocksource/timer-ti-dm.c
@@ -20,6 +20,7 @@
 
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/clocksource.h>
 #include <linux/cpu_pm.h>
 #include <linux/module.h>
 #include <linux/io.h>
@@ -29,6 +30,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/dmtimer-omap.h>
+#include <linux/sched_clock.h>
 
 #include <clocksource/timer-ti-dm.h>
 #include <linux/delay.h>
@@ -148,6 +150,14 @@ static u32 omap_reserved_systimers;
 static LIST_HEAD(omap_timer_list);
 static DEFINE_SPINLOCK(dm_timer_lock);
 
+struct dmtimer_clocksource {
+	struct clocksource dev;
+	struct dmtimer *timer;
+	unsigned int loadval;
+};
+
+static void __iomem *omap_dm_timer_sched_clock_counter;
+
 enum {
 	REQUEST_ANY = 0,
 	REQUEST_BY_ID,
@@ -1185,6 +1195,92 @@ static const struct dev_pm_ops omap_dm_timer_pm_ops = {
 
 static const struct of_device_id omap_timer_match[];
 
+static struct dmtimer_clocksource *omap_dm_timer_to_clocksource(struct clocksource *cs)
+{
+	return container_of(cs, struct dmtimer_clocksource, dev);
+}
+
+static u64 omap_dm_timer_read_cycles(struct clocksource *cs)
+{
+	struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs);
+	struct dmtimer *timer = clksrc->timer;
+
+	return (u64)__omap_dm_timer_read_counter(timer);
+}
+
+static u64 notrace omap_dm_timer_read_sched_clock(void)
+{
+	/* Posted mode is not active here, so we can read directly */
+	return readl_relaxed(omap_dm_timer_sched_clock_counter);
+}
+
+static void omap_dm_timer_clocksource_suspend(struct clocksource *cs)
+{
+	struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs);
+	struct dmtimer *timer = clksrc->timer;
+
+	clksrc->loadval = __omap_dm_timer_read_counter(timer);
+	__omap_dm_timer_stop(timer);
+}
+
+static void omap_dm_timer_clocksource_resume(struct clocksource *cs)
+{
+	struct dmtimer_clocksource *clksrc = omap_dm_timer_to_clocksource(cs);
+	struct dmtimer *timer = clksrc->timer;
+
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, clksrc->loadval);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR);
+}
+
+static void omap_dm_timer_clocksource_unregister(void *data)
+{
+	struct clocksource *cs = data;
+
+	clocksource_unregister(cs);
+}
+
+static int omap_dm_timer_setup_clocksource(struct dmtimer *timer)
+{
+	struct device *dev = &timer->pdev->dev;
+	struct dmtimer_clocksource *clksrc;
+	int err;
+
+	__omap_dm_timer_init_regs(timer);
+
+	timer->reserved = 1;
+
+	clksrc = devm_kzalloc(dev, sizeof(*clksrc), GFP_KERNEL);
+	if (!clksrc)
+		return -ENOMEM;
+
+	clksrc->timer = timer;
+
+	clksrc->dev.name = "omap_dm_timer";
+	clksrc->dev.rating = 300;
+	clksrc->dev.read = omap_dm_timer_read_cycles;
+	clksrc->dev.mask = CLOCKSOURCE_MASK(32);
+	clksrc->dev.flags = CLOCK_SOURCE_IS_CONTINUOUS;
+	clksrc->dev.suspend = omap_dm_timer_clocksource_suspend;
+	clksrc->dev.resume = omap_dm_timer_clocksource_resume;
+
+	dmtimer_write(timer, OMAP_TIMER_COUNTER_REG, 0);
+	dmtimer_write(timer, OMAP_TIMER_LOAD_REG, 0);
+	dmtimer_write(timer, OMAP_TIMER_CTRL_REG, OMAP_TIMER_CTRL_ST | OMAP_TIMER_CTRL_AR);
+
+	omap_dm_timer_sched_clock_counter = timer->func_base + _OMAP_TIMER_COUNTER_OFFSET;
+	sched_clock_register(omap_dm_timer_read_sched_clock, 32, timer->fclk_rate);
+
+	err = clocksource_register_hz(&clksrc->dev, timer->fclk_rate);
+	if (err)
+		return dev_err_probe(dev, err, "Could not register as clocksource\n");
+
+	err = devm_add_action_or_reset(dev, omap_dm_timer_clocksource_unregister, &clksrc->dev);
+	if (err)
+		return dev_err_probe(dev, err, "Could not register clocksource_unregister action\n");
+
+	return 0;
+}
+
 /**
  * omap_dm_timer_probe - probe function called for every registered device
  * @pdev:	pointer to current timer platform device
@@ -1272,6 +1368,13 @@ static int omap_dm_timer_probe(struct platform_device *pdev)
 
 	timer->pdev = pdev;
 
+	if (timer->capability & OMAP_TIMER_ALWON && !IS_ERR_OR_NULL(timer->fclk) &&
+	    !omap_dm_timer_sched_clock_counter) {
+		ret = omap_dm_timer_setup_clocksource(timer);
+		if (ret)
+			return ret;
+	}
+
 	pm_runtime_enable(dev);
 
 	if (!timer->reserved) {

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [tip: timers/clocksource] clocksource/drivers/timer-ti-dm: Fix property name in comment
  2026-05-08 13:39 ` [PATCH v5 1/3] clocksource/drivers/timer-ti-dm: Fix property name in comment Markus Schneider-Pargmann (TI)
@ 2026-06-13 14:33   ` tip-bot2 for Markus Schneider-Pargmann (TI)
  0 siblings, 0 replies; 7+ messages in thread
From: tip-bot2 for Markus Schneider-Pargmann (TI) @ 2026-06-13 14:33 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Markus Schneider-Pargmann (TI),
	Daniel Lezcano, Kevin Hilman, Dhruva Gole, x86, linux-kernel

The following commit has been merged into the timers/clocksource branch of tip:

Commit-ID:     8ee4e6dc8b960122b4c8504978d20774634b6633
Gitweb:        https://git.kernel.org/tip/8ee4e6dc8b960122b4c8504978d20774634b6633
Author:        Markus Schneider-Pargmann (TI) <msp@baylibre.com>
AuthorDate:    Fri, 08 May 2026 15:39:27 +02:00
Committer:     Daniel Lezcano <daniel.lezcano@kernel.org>
CommitterDate: Wed, 03 Jun 2026 10:05:29 +02:00

clocksource/drivers/timer-ti-dm: Fix property name in comment

ti,always-on property doesn't exist. ti,timer-alwon is meant here. Fix
this minor bug in the comment.

Signed-off-by: Markus Schneider-Pargmann (TI) <msp@baylibre.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org>
Reviewed-by: Kevin Hilman <khilman@baylibre.com>
Reviewed-by: Dhruva Gole <d-gole@ti.com>
Link: https://patch.msgid.link/20260508-topic-ti-dm-clkevt-v6-16-v5-1-61d546a0aff9@baylibre.com
---
 drivers/clocksource/timer-ti-dm-systimer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clocksource/timer-ti-dm-systimer.c b/drivers/clocksource/timer-ti-dm-systimer.c
index eb0dfe4..3804c12 100644
--- a/drivers/clocksource/timer-ti-dm-systimer.c
+++ b/drivers/clocksource/timer-ti-dm-systimer.c
@@ -226,7 +226,7 @@ static bool __init dmtimer_is_preferred(struct device_node *np)
  * Some omap3 boards with unreliable oscillator must not use the counter_32k
  * or dmtimer1 with 32 KiHz source. Additionally, the boards with unreliable
  * oscillator should really set counter_32k as disabled, and delete dmtimer1
- * ti,always-on property, but let's not count on it. For these quirky cases,
+ * ti,timer-alwon property, but let's not count on it. For these quirky cases,
  * we prefer using the always-on secure dmtimer12 with the internal 32 KiHz
  * clock as the clocksource, and any available dmtimer as clockevent.
  *

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-06-13 14:33 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-08 13:39 [PATCH v5 0/3] clocksource/drivers/timer-ti-dm: Add clocksource and clockevent support Markus Schneider-Pargmann (TI)
2026-05-08 13:39 ` [PATCH v5 1/3] clocksource/drivers/timer-ti-dm: Fix property name in comment Markus Schneider-Pargmann (TI)
2026-06-13 14:33   ` [tip: timers/clocksource] " tip-bot2 for Markus Schneider-Pargmann (TI)
2026-05-08 13:39 ` [PATCH v5 2/3] clocksource/drivers/timer-ti-dm: Add clocksource support Markus Schneider-Pargmann (TI)
2026-06-13 14:33   ` [tip: timers/clocksource] " tip-bot2 for Markus Schneider-Pargmann (TI)
2026-05-08 13:39 ` [PATCH v5 3/3] clocksource/drivers/timer-ti-dm: Add clockevent support Markus Schneider-Pargmann (TI)
2026-06-13 14:33   ` [tip: timers/clocksource] " tip-bot2 for Markus Schneider-Pargmann (TI)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®