+
/*
* PIT timer module.
*/
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index
487c8525996724fbf9c6e9726dabb478d86513b9..3cb3237ef788170f1c2d2bf5db3d47adbeca5664 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -763,4 +763,13 @@ config RALINK_TIMER
Enables support for system tick counter present on
Ralink SoCs RT3352 and MT7620.
+config COLDFIRE_DMA_TIMERS
+ bool "Coldfire DMA timer clocksource"
+ depends on M5441x
+ select GENERIC_SCHED_CLOCK
+ help
+ Enables support for the Coldfire M5441x DMA timers as
+ a clocksource and a clockevent. It supports oneshot and
+ high resolution.
+
endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index
43ef16a4efa6a33bf69ca05a8d66d682826841c9..3a740cd5d52a6f5e68c4e8a0831a2f3306f56941 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -92,3 +92,4 @@ obj-$(CONFIG_GXP_TIMER) += timer-gxp.o
obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o
obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o
+obj-$(CONFIG_COLDFIRE_DMA_TIMERS) += mcf_dma_timer.o
diff --git a/drivers/clocksource/mcf_dma_timer.c b/drivers/
clocksource/mcf_dma_timer.c
new file mode 100644
index
0000000000000000000000000000000000000000..f5cb0f92fcecffcd51a6e73ca38e5a8d8bade0a9
--- /dev/null
+++ b/drivers/clocksource/mcf_dma_timer.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mcf_dma_timer.c -- Freescale ColdFire DMA Timer.
+ *
+ * Copyright (C) 2024, Jean-Michel Hautbois
<jeanmichel.hautbois@xxxxxxxxxx>
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sched/clock.h>
+#include <linux/sched_clock.h>
+
+#include <asm/machdep.h>
+#include <asm/coldfire.h>
+#include <asm/mcfpit.h>
+#include <asm/mcfsim.h>
+
+struct dmatmr_regs {
+ u16 *dtmr;
+ u8 *dtxmr;
+ u8 *dter;
+ u32 *dtrr;
+ u32 *dtcr;
+ u32 *dtcn;
+};
+
+struct dmatmr_priv {
+ void __iomem *base;
+ struct clk *clk;
+ struct platform_device *pdev;
+ unsigned long rate;
+ raw_spinlock_t lock;
+ struct clock_event_device ced;
+ struct clocksource cs;
+ int id;
+ struct dmatmr_regs regs;
+};
+
+static u64 sched_dtim_clk_val;
+static void __iomem *dmatmr_sched_clk_counter;
+
+static struct dmatmr_priv *ced_to_priv(struct clock_event_device *ced)
+{
+ return container_of(ced, struct dmatmr_priv, ced);
+}
+
+static void sys_dtim_init(struct dmatmr_priv *priv)
+{
+ __raw_writel((priv->rate / HZ) - 1, priv->regs.dtrr);
+ __raw_writew(BIT(4) | BIT(1) | BIT(0), priv->regs.dtmr);
+ __raw_writeb(0, priv->regs.dtxmr);
+ __raw_writeb(1, priv->regs.dter);
+
+ dev_info(&priv->pdev->dev, "Initialized for sched_clock at %d
hz", HZ);
+}
+
+static inline u64 notrace read_dtcn(void)
+{
+ return __raw_readl(dmatmr_sched_clk_counter);
+}
+
+static u64 notrace sys_dtim_read(void)
+{
+ return sched_dtim_clk_val + read_dtcn();
+}
+
+static u64 cfv4_read_dtimvalue(struct clocksource *cs)
+{
+ return sys_dtim_read();
+}
+
+static int cfv4_set_next_event(unsigned long delta,
+ struct clock_event_device *dev)
+{
+ struct dmatmr_priv *priv = ced_to_priv(dev);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&priv->lock, flags);
+ /* read timer value */
+ sched_dtim_clk_val += read_dtcn();
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* reset timer with delta cycle */
+ __raw_writew(0, priv->regs.dtmr);
+ __raw_writel(delta, priv->regs.dtrr);
+ __raw_writew(BIT(4) | BIT(1) | BIT(0), priv->regs.dtmr);
+
+ return 0;
+}
+
+static int cfv4_set_oneshot(struct clock_event_device *dev)
+{
+ struct dmatmr_priv *priv = ced_to_priv(dev);
+ unsigned long flags;
+
+ /* read timer value */
+ raw_spin_lock_irqsave(&priv->lock, flags);
+ sched_dtim_clk_val += read_dtcn();
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+
+ __raw_writew(0, priv->regs.dtmr);
+ return 0;
+}
+
+static irqreturn_t coldfire_dtim_clk_irq(int irq, void *dev)
+{
+ struct dmatmr_priv *priv = dev;
+ unsigned long flags;
+
+ /* acknowledge the IRQ */
+ __raw_writeb(BIT(0) | BIT(1), priv->regs.dter);
+
+ /* read timer value */
+ raw_spin_lock_irqsave(&priv->lock, flags);
+ sched_dtim_clk_val += read_dtcn();
+ raw_spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* restart counter */
+ __raw_writel(0, dmatmr_sched_clk_counter);
+
+ priv->ced.event_handler(&priv->ced);
+
+ return IRQ_HANDLED;
+}
+
+static void mcf_dma_register_clocksource(struct dmatmr_priv *priv)
+{
+ struct clocksource *cs = &priv->cs;
+
+ cs->name = dev_name(&priv->pdev->dev);
+ cs->rating = 250;
+ cs->mask = CLOCKSOURCE_MASK(30);
+ cs->read = cfv4_read_dtimvalue;
+ cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
+
+ dev_info(&priv->pdev->dev, "registering clocksource\n");
+
+ clocksource_register_hz(cs, priv->rate);
+}
+
+static void mcf_dma_register_clockevent(struct dmatmr_priv *priv)
+{
+ struct clock_event_device *ced = &priv->ced;
+
+ ced->name = dev_name(&priv->pdev->dev);
+ ced->features = CLOCK_EVT_FEAT_ONESHOT;
+ ced->rating = 250;
+ ced->shift = 20;
+ ced->cpumask = cpumask_of(smp_processor_id());
+ ced->set_state_oneshot = cfv4_set_oneshot;
+ ced->set_next_event = cfv4_set_next_event;
+
+ dev_info(&priv->pdev->dev, "registering clockevent\n");
+
+ clockevents_config_and_register(ced, priv->rate, 2, 0xffffffff);
+}
+
+static void mcf_dma_init_registers(struct dmatmr_priv *priv)
+{
+ struct dmatmr_regs *regs = &priv->regs;
+
+ regs->dtmr = priv->base + 0x0;
+ regs->dtxmr = priv->base + 0x2;
+ regs->dter = priv->base + 0x3;
+ regs->dtrr = priv->base + 0x4;
+ regs->dtcr = priv->base + 0x8;
+ regs->dtcn = priv->base + 0xc;