[PATCH 2/2] counter: ti-dmtimer-cap : capture driver support for OMAP DM timer

Gokul Praveen posted 2 patches 3 weeks, 2 days ago
[PATCH 2/2] counter: ti-dmtimer-cap : capture driver support for OMAP DM timer
Posted by Gokul Praveen 3 weeks, 2 days ago
Enable capture driver support for OMAP DM timer hardware.

DM timer hardware supports capture feature.It can be used
to timestamp events (falling/rising edges) detected on input signal.

The timer IO pin can also be configured in PWM mode but this
driver supports simple independent capture functionality.

It is assumed that the capture signal is active else both duty cycle
and period returned by driver will not be valid.

Signed-off-by: Gokul Praveen <g-praveen@ti.com>
---
BOOT LOGS:
https://gist.github.com/GokulPraveen2001/581fa07b2e93f30dea9f6188a97b944b

TIMER CAPTURE DTSO:

&{/} {

       main_cap10: dmtimer-main-cap-10 {
               compatible = "ti,omap-dmtimer-cap";
               ti,timers = <&main_timer10>;
               pinctrl-0 = <&maindmtimer1_pins_default>,<&main_timer10_ctrl_config>;
               pinctrl-names = "default";
       };

};

&main_timer10{
       status = "okay";
};

/*MAIN_TIMERIO1*/
&main_pmx0 {
    maindmtimer1_pins_default: maindmtimer1-default-pins {
        pinctrl-single,pins = <
            J784S4_IOPAD(0x0ec, PIN_INPUT, 0) /* (AN37) TIMER_IO1:Input mode for Capture*/
        >;
    };

};

/*Sets in CAP mode using MAIN TIMER IOX(X=1 here) CTRL MMR REGISTERS*/
&main_timerio_input {
       main_timer10_ctrl_config: main-timer10-ctrl-config {
               pinctrl-single,pins = <
                       J784S4_IOPAD(0x28,0,0x1)>; /*  MAIN_TIMER_10 will use MAIN_TIMERIO1 pin(maindmtimer1-default-pins) for capture*/
       };
};
---
 drivers/counter/Kconfig          |  13 +
 drivers/counter/Makefile         |   1 +
 drivers/counter/ti-dmtimer-cap.c | 455 +++++++++++++++++++++++++++++++
 3 files changed, 469 insertions(+)
 create mode 100644 drivers/counter/ti-dmtimer-cap.c

diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index d30d22dfe577..bec07cf15779 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -121,6 +121,19 @@ config STM32_TIMER_CNT
 	  To compile this driver as a module, choose M here: the
 	  module will be called stm32-timer-cnt.
 
+config TI_DMTIMER_CAPTURE
+	tristate "OMAP Dual-Mode Timer Capture support"
+	depends on OMAP_DM_TIMER || COMPILE_TEST
+	help
+	  Select this option to use the Texas Instruments OMAP DM Timer
+	  driver in capture mode.
+
+	  It can be used to timestamp events (falling/rising edges) detected
+	  on Timer input signal.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called ti-dmtimer-cap.
+
 config TI_ECAP_CAPTURE
 	tristate "TI eCAP capture driver"
 	depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index fa3c1d08f706..7c2d226fe984 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_FTM_QUADDEC)	+= ftm-quaddec.o
 obj-$(CONFIG_MICROCHIP_TCB_CAPTURE)	+= microchip-tcb-capture.o
 obj-$(CONFIG_INTEL_QEP)		+= intel-qep.o
 obj-$(CONFIG_TI_ECAP_CAPTURE)	+= ti-ecap-capture.o
+obj-$(CONFIG_TI_DMTIMER_CAPTURE)	+=ti-dmtimer-cap.o
diff --git a/drivers/counter/ti-dmtimer-cap.c b/drivers/counter/ti-dmtimer-cap.c
new file mode 100644
index 000000000000..dedfb15faa10
--- /dev/null
+++ b/drivers/counter/ti-dmtimer-cap.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * DM timer Capture Driver
+ */
+
+#include <clocksource/timer-ti-dm.h>
+#include <linux/atomic.h>
+#include <linux/counter.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/dmtimer-omap.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+
+#define TIMER_DRV_NAME "CAP_OMAP_DMTIMER"
+/* Timer signals */
+#define TIMER_CLOCK_SIG 0
+#define TIMER_INPUT_SIG 1
+
+/**
+ * struct cap_omap_dmtimer_counter - Structure representing a cap counter
+ *				  corresponding to omap dm timer.
+ * @counter:		Capture counter
+ * @enabled:		Tracks the enable status of omap dm timer.
+ * @mutex:			Mutex to protect cap apply state
+ * @dm_timer:		Pointer to omap dm timer.
+ * @pdata:			Pointer to omap dm timer ops.
+ * @dm_timer_pdev:	Pointer to omap dm timer platform device
+ */
+struct cap_omap_dmtimer_counter {
+	struct counter_device counter;
+	bool enabled;
+	/* Mutex to protect cap apply state */
+	struct mutex mutex;
+	struct omap_dm_timer *dm_timer;
+	const struct omap_dm_timer_ops *pdata;
+	struct platform_device *dm_timer_pdev;
+};
+/**
+ * cap_omap_dmtimer_start() - Start the cap omap dm timer in capture mode
+ * @omap:	Pointer to cap omap dm timer counter
+ */
+static void cap_omap_dmtimer_start(struct cap_omap_dmtimer_counter *omap)
+{
+	u32 ret;
+	struct device *dev = &omap->dm_timer_pdev->dev;
+
+	ret = omap->pdata->start(omap->dm_timer);
+	if (ret)
+		dev_err(dev, "%d: Failed to start timer.\n", ret);
+}
+
+/**
+ * cap_omap_dmtimer_is_enabled() -  Detect if the timer capture is enabled.
+ * @omap:	Pointer to cap omap dm timer counter
+ *
+ * Return true if capture is enabled else false.
+ */
+static bool cap_omap_dmtimer_is_enabled(struct cap_omap_dmtimer_counter *omap)
+{
+	u32 status;
+
+	status = omap->pdata->get_cap_status(omap->dm_timer);
+
+	return !!(status & OMAP_TIMER_CTRL_ST);
+}
+
+static int cap_omap_dmtimer_clk_get_freq(struct counter_device *counter,
+				 struct counter_signal *signal, u64 *freq)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+	struct clk *fclk;
+
+	fclk = omap->pdata->get_fclk(omap->dm_timer);
+	if (!fclk) {
+		dev_err(counter->parent, "invalid dmtimer fclk\n");
+		return -EINVAL;
+	}
+
+	*freq = clk_get_rate(fclk);
+	if (!(*freq)) {
+		dev_err(counter->parent, "invalid dmtimer fclk rate\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+/**
+ * cap_omap_dmtimer_apply() - Changes the state of the cap omap dm timer counter.
+ * @counter:Pointer to capture counter.
+ *
+ * Return 0 if successfully changed the state else appropriate error.
+ */
+static int cap_omap_dmtimer_apply(struct counter_device *counter)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+	struct device *dev = &omap->dm_timer_pdev->dev;
+	int ret = 0;
+
+	/* Ensure that the timer is in stop mode so that the configs can be changed. */
+	if (cap_omap_dmtimer_is_enabled(omap)) {
+		ret = omap->pdata->stop(omap->dm_timer);
+		if (ret)
+			dev_err(dev, "%d: Failed to stop timer.\n", ret);
+	}
+
+	ret = omap->pdata->set_cap(omap->dm_timer, true, true);
+	if (ret) {
+		dev_err(dev, "%d: Failed to set timer capture configuration.\n", ret);
+		return ret;
+	}
+
+	cap_omap_dmtimer_start(omap);
+
+	return ret;
+}
+
+static int cap_omap_dmtimer_capture(struct counter_device *counter,
+					struct counter_count *count, u64 *duty_cycle)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+	*duty_cycle = 0;
+
+	if (!omap->enabled) {
+		dev_err(counter->parent, "Timer is disabled.\n");
+		omap->pdata->stop(omap->dm_timer);
+		return 0;
+	}
+
+	*duty_cycle = omap->pdata->read_cap(omap->dm_timer, false);
+
+	*duty_cycle = *duty_cycle > 0 ? *duty_cycle : 0;
+
+	return *duty_cycle;
+}
+
+static int cap_omap_dmtimer_period(struct counter_device *counter,
+					struct counter_signal *signal, u64 *freq)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+	u64 clk_freq = 0;
+	u64 period = 0;
+	*freq = 0;
+
+	if (!omap->enabled) {
+		dev_err(counter->parent, "Timer is disabled.\n");
+		omap->pdata->stop(omap->dm_timer);
+		return 0;
+	}
+
+	period = omap->pdata->read_cap(omap->dm_timer, true);
+	cap_omap_dmtimer_clk_get_freq(counter, signal, &clk_freq);
+
+	if (period > 0)
+		*freq = clk_freq/period;
+
+	return *freq+1;
+}
+static int cap_omap_dmtimer_enable_read(struct counter_device *counter,
+				struct counter_count *count, u8 *enable)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+
+	*enable = omap->enabled;
+
+	return 0;
+}
+
+static int cap_omap_dmtimer_count_read(struct counter_device *counter,
+			       struct counter_count *count, u64 *val)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+
+	*val = omap->pdata->read_counter(omap->dm_timer);
+
+	return 0;
+}
+
+static int cap_omap_dmtimer_count_write(struct counter_device *counter,
+				struct counter_count *count, u64 val)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+
+	if (val > U32_MAX)
+		return -ERANGE;
+
+	omap->pdata->write_counter(omap->dm_timer, val);
+
+	return 0;
+}
+
+static int cap_omap_dmtimer_enable_write(struct counter_device *counter,
+				 struct counter_count *count, u8 enable)
+{
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+
+	if (enable == omap->enabled)
+		goto out;
+
+	if (enable)
+		cap_omap_dmtimer_apply(counter);
+	else
+		omap->pdata->stop(omap->dm_timer);
+
+	omap->enabled = enable;
+out:
+	return 0;
+}
+
+static int cap_omap_dmtimer_function_read(struct counter_device *counter,
+				  struct counter_count *count,
+				  enum counter_function *function)
+{
+	*function = COUNTER_FUNCTION_INCREASE;
+
+	return 0;
+}
+
+static int cap_omap_dmtimer_action_read(struct counter_device *counter,
+				struct counter_count *count,
+				struct counter_synapse *synapse,
+				enum counter_synapse_action *action)
+{
+	*action = (synapse->signal->id == TIMER_CLOCK_SIG) ?
+		   COUNTER_SYNAPSE_ACTION_RISING_EDGE :
+		   COUNTER_SYNAPSE_ACTION_NONE;
+
+	return 0;
+}
+
+static const struct counter_ops cap_omap_dmtimer_ops = {
+	.count_read = cap_omap_dmtimer_count_read,
+	.count_write = cap_omap_dmtimer_count_write,
+	.function_read = cap_omap_dmtimer_function_read,
+	.action_read = cap_omap_dmtimer_action_read,
+};
+
+static const enum counter_function cap_omap_dmtimer_functions[] = {
+	COUNTER_FUNCTION_INCREASE,
+};
+
+static struct counter_comp cap_omap_dmtimer_clock_ext[] = {
+	COUNTER_COMP_SIGNAL_U64("frequency", cap_omap_dmtimer_clk_get_freq, NULL),
+};
+
+static struct counter_signal cap_omap_dmtimer_signals[] = {
+	{
+		.id = TIMER_CLOCK_SIG,
+		.name = "Clock Signal",
+		.ext = cap_omap_dmtimer_clock_ext,
+		.num_ext = ARRAY_SIZE(cap_omap_dmtimer_clock_ext),
+	},
+	{
+		.id = TIMER_INPUT_SIG,
+		.name = "Input Signal",
+	},
+};
+
+/* Counter will increase at rising edges of a clock */
+static const enum counter_synapse_action cap_omap_dmtimer_clock_actions[] = {
+	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
+};
+
+/* No trigger here */
+static const enum counter_synapse_action cap_omap_dmtimer_input_actions[] = {
+	COUNTER_SYNAPSE_ACTION_NONE,
+};
+
+static struct counter_synapse cap_omap_dmtimer_synapses[] = {
+	{
+		.actions_list = cap_omap_dmtimer_clock_actions,
+		.num_actions = ARRAY_SIZE(cap_omap_dmtimer_clock_actions),
+		.signal = &cap_omap_dmtimer_signals[TIMER_CLOCK_SIG],
+	},
+	{
+		.actions_list = cap_omap_dmtimer_input_actions,
+		.num_actions = ARRAY_SIZE(cap_omap_dmtimer_input_actions),
+		.signal = &cap_omap_dmtimer_signals[TIMER_INPUT_SIG],
+	},
+};
+
+static struct counter_comp cap_omap_dmtimer_count_ext[] = {
+	COUNTER_COMP_CAPTURE(cap_omap_dmtimer_capture, NULL),
+	COUNTER_COMP_ENABLE(cap_omap_dmtimer_enable_read, cap_omap_dmtimer_enable_write),
+	COUNTER_COMP_FREQUENCY(cap_omap_dmtimer_period),
+};
+
+static struct counter_count cap_omap_dmtimer_counts[] = {
+	{
+		.name = "Timestamp Counter",
+		.functions_list = cap_omap_dmtimer_functions,
+		.num_functions = ARRAY_SIZE(cap_omap_dmtimer_functions),
+		.synapses = cap_omap_dmtimer_synapses,
+		.num_synapses = ARRAY_SIZE(cap_omap_dmtimer_synapses),
+		.ext = cap_omap_dmtimer_count_ext,
+		.num_ext = ARRAY_SIZE(cap_omap_dmtimer_count_ext),
+	},
+};
+
+static int cap_omap_dmtimer_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct dmtimer_platform_data *timer_pdata;
+	const struct omap_dm_timer_ops *pdata;
+	struct platform_device *timer_pdev;
+	struct omap_dm_timer *dm_timer;
+	struct device_node *timer;
+	struct cap_omap_dmtimer_counter *omap;
+	struct counter_device *counter_dev;
+	int ret = 0;
+
+	timer = of_parse_phandle(np, "ti,timers", 0);
+	if (!timer) {
+		dev_err(&pdev->dev, "Unable to find Timer node\n");
+		return -ENODEV;
+	}
+
+	timer_pdev = of_find_device_by_node(timer);
+	if (!timer_pdev) {
+		dev_err(&pdev->dev, "Unable to find Timer pdev\n");
+		ret = -ENODEV;
+		goto err_find_timer_pdev;
+	}
+	timer_pdata = dev_get_platdata(&timer_pdev->dev);
+	if (!timer_pdata) {
+		dev_dbg(&pdev->dev,
+			"dmtimer pdata structure NULL, deferring probe\n");
+		ret = -EPROBE_DEFER;
+		dev_err_probe(&pdev->dev, ret, "Probe deferred\n");
+		goto err_platdata;
+	}
+
+	pdata = timer_pdata->timer_ops;
+
+	if (!pdata || !pdata->request_by_node ||
+	    !pdata->free ||
+	    !pdata->enable ||
+	    !pdata->disable ||
+	    !pdata->get_fclk ||
+	    !pdata->start ||
+	    !pdata->stop ||
+	    !pdata->set_load ||
+	    !pdata->set_match ||
+	    !pdata->set_cap ||
+	    !pdata->get_cap_status ||
+		!pdata->read_cap ||
+	    !pdata->set_prescaler ||
+		!pdata->write_counter) {
+		dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
+		ret = -EINVAL;
+		goto err_platdata;
+	}
+
+	dm_timer = pdata->request_by_node(timer);
+	if (!dm_timer) {
+		ret = -EPROBE_DEFER;
+		goto err_request_timer;
+	}
+
+    /* struct cap_omap_dmtimer_counter *omap */
+	counter_dev = devm_counter_alloc(dev, sizeof(*omap));
+	if (!counter_dev) {
+		dev_err(&pdev->dev, "Unable to allocate dmtimercounter\n");
+		ret = -ENOMEM;
+		goto err_alloc_omap;
+	}
+	omap = counter_priv(counter_dev);
+
+	counter_dev->name = TIMER_DRV_NAME;
+	counter_dev->parent = dev;
+	counter_dev->ops = &cap_omap_dmtimer_ops;
+	counter_dev->signals = cap_omap_dmtimer_signals;
+	counter_dev->num_signals = ARRAY_SIZE(cap_omap_dmtimer_signals);
+	counter_dev->counts = cap_omap_dmtimer_counts;
+	counter_dev->num_counts = ARRAY_SIZE(cap_omap_dmtimer_counts);
+	mutex_init(&omap->mutex);
+	omap->pdata = pdata;
+	omap->dm_timer = dm_timer;
+	omap->dm_timer_pdev = timer_pdev;
+
+	if (pm_runtime_active(&omap->dm_timer_pdev->dev))
+		omap->pdata->stop(omap->dm_timer);
+
+	of_node_put(timer);
+
+	platform_set_drvdata(pdev, counter_dev);
+
+	ret = devm_counter_add(dev, counter_dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to add counter\n");
+
+	return 0;
+
+err_alloc_omap:
+	pdata->free(dm_timer);
+err_request_timer:
+
+err_platdata:
+	put_device(&timer_pdev->dev);
+err_find_timer_pdev:
+
+	of_node_put(timer);
+
+	return ret;
+}
+
+static void cap_omap_dmtimer_remove(struct platform_device *pdev)
+{
+	struct counter_device *counter = platform_get_drvdata(pdev);
+	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
+
+	counter_unregister(counter);
+
+	if (pm_runtime_active(&omap->dm_timer_pdev->dev))
+		omap->pdata->stop(omap->dm_timer);
+
+	omap->pdata->free(omap->dm_timer);
+
+	put_device(&omap->dm_timer_pdev->dev);
+
+	mutex_destroy(&omap->mutex);
+
+}
+
+static const struct of_device_id cap_omap_dmtimer_of_match[] = {
+	{.compatible = "ti,omap-dmtimer-cap"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, cap_omap_dmtimer_of_match);
+
+static struct platform_driver cap_omap_dmtimer_driver = {
+	.driver = {
+		.name = "omap-dmtimer-cap",
+		.of_match_table = cap_omap_dmtimer_of_match,
+	},
+	.probe = cap_omap_dmtimer_probe,
+	.remove = cap_omap_dmtimer_remove,
+};
+module_platform_driver(cap_omap_dmtimer_driver);
+
+MODULE_AUTHOR("Gokul Praveen <g-praveen@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OMAP CAP Driver using Dual-mode Timers");
+MODULE_IMPORT_NS("COUNTER");
-- 
2.34.1
Re: [PATCH 2/2] counter: ti-dmtimer-cap : capture driver support for OMAP DM timer
Posted by kernel test robot 3 weeks, 1 day ago
Hi Gokul,

kernel test robot noticed the following build errors:

[auto build test ERROR on robh/for-next]
[also build test ERROR on linus/master v6.17-rc5 next-20250909]
[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/Gokul-Praveen/dt-bindings-counter-Add-new-ti-omap-dmtimer-cap-compatible/20250909-160651
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
patch link:    https://lore.kernel.org/r/20250909080042.36127-3-g-praveen%40ti.com
patch subject: [PATCH 2/2] counter: ti-dmtimer-cap : capture driver support for OMAP DM timer
config: m68k-allyesconfig (https://download.01.org/0day-ci/archive/20250910/202509101412.ze6xyOUu-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/20250910/202509101412.ze6xyOUu-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/202509101412.ze6xyOUu-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/counter/ti-dmtimer-cap.c: In function 'cap_omap_dmtimer_is_enabled':
>> drivers/counter/ti-dmtimer-cap.c:73:31: error: 'const struct omap_dm_timer_ops' has no member named 'get_cap_status'; did you mean 'get_pwm_status'?
      73 |         status = omap->pdata->get_cap_status(omap->dm_timer);
         |                               ^~~~~~~~~~~~~~
         |                               get_pwm_status
   drivers/counter/ti-dmtimer-cap.c: In function 'cap_omap_dmtimer_apply':
>> drivers/counter/ti-dmtimer-cap.c:117:26: error: 'const struct omap_dm_timer_ops' has no member named 'set_cap'
     117 |         ret = omap->pdata->set_cap(omap->dm_timer, true, true);
         |                          ^~
   drivers/counter/ti-dmtimer-cap.c: In function 'cap_omap_dmtimer_capture':
>> drivers/counter/ti-dmtimer-cap.c:140:34: error: 'const struct omap_dm_timer_ops' has no member named 'read_cap'
     140 |         *duty_cycle = omap->pdata->read_cap(omap->dm_timer, false);
         |                                  ^~
   drivers/counter/ti-dmtimer-cap.c: In function 'cap_omap_dmtimer_period':
   drivers/counter/ti-dmtimer-cap.c:161:29: error: 'const struct omap_dm_timer_ops' has no member named 'read_cap'
     161 |         period = omap->pdata->read_cap(omap->dm_timer, true);
         |                             ^~
   drivers/counter/ti-dmtimer-cap.c: In function 'cap_omap_dmtimer_probe':
   drivers/counter/ti-dmtimer-cap.c:355:19: error: 'const struct omap_dm_timer_ops' has no member named 'set_cap'
     355 |             !pdata->set_cap ||
         |                   ^~
   drivers/counter/ti-dmtimer-cap.c:356:21: error: 'const struct omap_dm_timer_ops' has no member named 'get_cap_status'; did you mean 'get_pwm_status'?
     356 |             !pdata->get_cap_status ||
         |                     ^~~~~~~~~~~~~~
         |                     get_pwm_status
   drivers/counter/ti-dmtimer-cap.c:357:23: error: 'const struct omap_dm_timer_ops' has no member named 'read_cap'
     357 |                 !pdata->read_cap ||
         |                       ^~


vim +73 drivers/counter/ti-dmtimer-cap.c

    62	
    63	/**
    64	 * cap_omap_dmtimer_is_enabled() -  Detect if the timer capture is enabled.
    65	 * @omap:	Pointer to cap omap dm timer counter
    66	 *
    67	 * Return true if capture is enabled else false.
    68	 */
    69	static bool cap_omap_dmtimer_is_enabled(struct cap_omap_dmtimer_counter *omap)
    70	{
    71		u32 status;
    72	
  > 73		status = omap->pdata->get_cap_status(omap->dm_timer);
    74	
    75		return !!(status & OMAP_TIMER_CTRL_ST);
    76	}
    77	
    78	static int cap_omap_dmtimer_clk_get_freq(struct counter_device *counter,
    79					 struct counter_signal *signal, u64 *freq)
    80	{
    81		struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
    82		struct clk *fclk;
    83	
    84		fclk = omap->pdata->get_fclk(omap->dm_timer);
    85		if (!fclk) {
    86			dev_err(counter->parent, "invalid dmtimer fclk\n");
    87			return -EINVAL;
    88		}
    89	
    90		*freq = clk_get_rate(fclk);
    91		if (!(*freq)) {
    92			dev_err(counter->parent, "invalid dmtimer fclk rate\n");
    93			return -EINVAL;
    94		}
    95	
    96		return 0;
    97	}
    98	/**
    99	 * cap_omap_dmtimer_apply() - Changes the state of the cap omap dm timer counter.
   100	 * @counter:Pointer to capture counter.
   101	 *
   102	 * Return 0 if successfully changed the state else appropriate error.
   103	 */
   104	static int cap_omap_dmtimer_apply(struct counter_device *counter)
   105	{
   106		struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
   107		struct device *dev = &omap->dm_timer_pdev->dev;
   108		int ret = 0;
   109	
   110		/* Ensure that the timer is in stop mode so that the configs can be changed. */
   111		if (cap_omap_dmtimer_is_enabled(omap)) {
   112			ret = omap->pdata->stop(omap->dm_timer);
   113			if (ret)
   114				dev_err(dev, "%d: Failed to stop timer.\n", ret);
   115		}
   116	
 > 117		ret = omap->pdata->set_cap(omap->dm_timer, true, true);
   118		if (ret) {
   119			dev_err(dev, "%d: Failed to set timer capture configuration.\n", ret);
   120			return ret;
   121		}
   122	
   123		cap_omap_dmtimer_start(omap);
   124	
   125		return ret;
   126	}
   127	
   128	static int cap_omap_dmtimer_capture(struct counter_device *counter,
   129						struct counter_count *count, u64 *duty_cycle)
   130	{
   131		struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
   132		*duty_cycle = 0;
   133	
   134		if (!omap->enabled) {
   135			dev_err(counter->parent, "Timer is disabled.\n");
   136			omap->pdata->stop(omap->dm_timer);
   137			return 0;
   138		}
   139	
 > 140		*duty_cycle = omap->pdata->read_cap(omap->dm_timer, false);
   141	
   142		*duty_cycle = *duty_cycle > 0 ? *duty_cycle : 0;
   143	
   144		return *duty_cycle;
   145	}
   146	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH 2/2] counter: ti-dmtimer-cap : capture driver support for OMAP DM timer
Posted by Andrew Davis 3 weeks, 2 days ago
On 9/9/25 3:00 AM, Gokul Praveen wrote:
> Enable capture driver support for OMAP DM timer hardware.
> 
> DM timer hardware supports capture feature.It can be used
> to timestamp events (falling/rising edges) detected on input signal.
> 
> The timer IO pin can also be configured in PWM mode but this
> driver supports simple independent capture functionality.
> 
> It is assumed that the capture signal is active else both duty cycle
> and period returned by driver will not be valid.
> 
> Signed-off-by: Gokul Praveen <g-praveen@ti.com>
> ---
> BOOT LOGS:
> https://gist.github.com/GokulPraveen2001/581fa07b2e93f30dea9f6188a97b944b
> 
> TIMER CAPTURE DTSO:
> 
> &{/} {
> 
>         main_cap10: dmtimer-main-cap-10 {
>                 compatible = "ti,omap-dmtimer-cap";
>                 ti,timers = <&main_timer10>;
>                 pinctrl-0 = <&maindmtimer1_pins_default>,<&main_timer10_ctrl_config>;
>                 pinctrl-names = "default";
>         };
> 
> };
> 
> &main_timer10{
>         status = "okay";
> };
> 
> /*MAIN_TIMERIO1*/
> &main_pmx0 {
>      maindmtimer1_pins_default: maindmtimer1-default-pins {
>          pinctrl-single,pins = <
>              J784S4_IOPAD(0x0ec, PIN_INPUT, 0) /* (AN37) TIMER_IO1:Input mode for Capture*/
>          >;
>      };
> 
> };
> 
> /*Sets in CAP mode using MAIN TIMER IOX(X=1 here) CTRL MMR REGISTERS*/
> &main_timerio_input {
>         main_timer10_ctrl_config: main-timer10-ctrl-config {
>                 pinctrl-single,pins = <
>                         J784S4_IOPAD(0x28,0,0x1)>; /*  MAIN_TIMER_10 will use MAIN_TIMERIO1 pin(maindmtimer1-default-pins) for capture*/
>         };
> };
> ---
>   drivers/counter/Kconfig          |  13 +
>   drivers/counter/Makefile         |   1 +
>   drivers/counter/ti-dmtimer-cap.c | 455 +++++++++++++++++++++++++++++++
>   3 files changed, 469 insertions(+)
>   create mode 100644 drivers/counter/ti-dmtimer-cap.c
> 
> diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
> index d30d22dfe577..bec07cf15779 100644
> --- a/drivers/counter/Kconfig
> +++ b/drivers/counter/Kconfig
> @@ -121,6 +121,19 @@ config STM32_TIMER_CNT
>   	  To compile this driver as a module, choose M here: the
>   	  module will be called stm32-timer-cnt.
>   
> +config TI_DMTIMER_CAPTURE
> +	tristate "OMAP Dual-Mode Timer Capture support"
> +	depends on OMAP_DM_TIMER || COMPILE_TEST
> +	help
> +	  Select this option to use the Texas Instruments OMAP DM Timer
> +	  driver in capture mode.
> +
> +	  It can be used to timestamp events (falling/rising edges) detected
> +	  on Timer input signal.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called ti-dmtimer-cap.
> +
>   config TI_ECAP_CAPTURE
>   	tristate "TI eCAP capture driver"
>   	depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
> diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
> index fa3c1d08f706..7c2d226fe984 100644
> --- a/drivers/counter/Makefile
> +++ b/drivers/counter/Makefile
> @@ -17,3 +17,4 @@ obj-$(CONFIG_FTM_QUADDEC)	+= ftm-quaddec.o
>   obj-$(CONFIG_MICROCHIP_TCB_CAPTURE)	+= microchip-tcb-capture.o
>   obj-$(CONFIG_INTEL_QEP)		+= intel-qep.o
>   obj-$(CONFIG_TI_ECAP_CAPTURE)	+= ti-ecap-capture.o
> +obj-$(CONFIG_TI_DMTIMER_CAPTURE)	+=ti-dmtimer-cap.o
> diff --git a/drivers/counter/ti-dmtimer-cap.c b/drivers/counter/ti-dmtimer-cap.c
> new file mode 100644
> index 000000000000..dedfb15faa10
> --- /dev/null
> +++ b/drivers/counter/ti-dmtimer-cap.c
> @@ -0,0 +1,455 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * DM timer Capture Driver

Add proper copyright.

> + */
> +
> +#include <clocksource/timer-ti-dm.h>
> +#include <linux/atomic.h>
> +#include <linux/counter.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_data/dmtimer-omap.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/time.h>
> +
> +#define TIMER_DRV_NAME "CAP_OMAP_DMTIMER"
> +/* Timer signals */
> +#define TIMER_CLOCK_SIG 0
> +#define TIMER_INPUT_SIG 1
> +
> +/**
> + * struct cap_omap_dmtimer_counter - Structure representing a cap counter
> + *				  corresponding to omap dm timer.
> + * @counter:		Capture counter
> + * @enabled:		Tracks the enable status of omap dm timer.
> + * @mutex:			Mutex to protect cap apply state
> + * @dm_timer:		Pointer to omap dm timer.
> + * @pdata:			Pointer to omap dm timer ops.
> + * @dm_timer_pdev:	Pointer to omap dm timer platform device
> + */
> +struct cap_omap_dmtimer_counter {
> +	struct counter_device counter;
> +	bool enabled;
> +	/* Mutex to protect cap apply state */
> +	struct mutex mutex;
> +	struct omap_dm_timer *dm_timer;
> +	const struct omap_dm_timer_ops *pdata;
> +	struct platform_device *dm_timer_pdev;
> +};

Random missing newlines everywhere.

> +/**
> + * cap_omap_dmtimer_start() - Start the cap omap dm timer in capture mode
> + * @omap:	Pointer to cap omap dm timer counter
> + */
> +static void cap_omap_dmtimer_start(struct cap_omap_dmtimer_counter *omap)
> +{
> +	u32 ret;
> +	struct device *dev = &omap->dm_timer_pdev->dev;
> +
> +	ret = omap->pdata->start(omap->dm_timer);
> +	if (ret)
> +		dev_err(dev, "%d: Failed to start timer.\n", ret);
> +}
> +
> +/**
> + * cap_omap_dmtimer_is_enabled() -  Detect if the timer capture is enabled.
> + * @omap:	Pointer to cap omap dm timer counter
> + *
> + * Return true if capture is enabled else false.
> + */
> +static bool cap_omap_dmtimer_is_enabled(struct cap_omap_dmtimer_counter *omap)
> +{
> +	u32 status;
> +
> +	status = omap->pdata->get_cap_status(omap->dm_timer);
> +
> +	return !!(status & OMAP_TIMER_CTRL_ST);
> +}
> +
> +static int cap_omap_dmtimer_clk_get_freq(struct counter_device *counter,
> +				 struct counter_signal *signal, u64 *freq)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +	struct clk *fclk;
> +
> +	fclk = omap->pdata->get_fclk(omap->dm_timer);
> +	if (!fclk) {
> +		dev_err(counter->parent, "invalid dmtimer fclk\n");
> +		return -EINVAL;
> +	}
> +
> +	*freq = clk_get_rate(fclk);
> +	if (!(*freq)) {
> +		dev_err(counter->parent, "invalid dmtimer fclk rate\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +/**
> + * cap_omap_dmtimer_apply() - Changes the state of the cap omap dm timer counter.
> + * @counter:Pointer to capture counter.
> + *
> + * Return 0 if successfully changed the state else appropriate error.
> + */
> +static int cap_omap_dmtimer_apply(struct counter_device *counter)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +	struct device *dev = &omap->dm_timer_pdev->dev;
> +	int ret = 0;
> +
> +	/* Ensure that the timer is in stop mode so that the configs can be changed. */
> +	if (cap_omap_dmtimer_is_enabled(omap)) {
> +		ret = omap->pdata->stop(omap->dm_timer);
> +		if (ret)
> +			dev_err(dev, "%d: Failed to stop timer.\n", ret);
> +	}
> +
> +	ret = omap->pdata->set_cap(omap->dm_timer, true, true);
> +	if (ret) {
> +		dev_err(dev, "%d: Failed to set timer capture configuration.\n", ret);
> +		return ret;
> +	}
> +
> +	cap_omap_dmtimer_start(omap);
> +
> +	return ret;
> +}
> +
> +static int cap_omap_dmtimer_capture(struct counter_device *counter,
> +					struct counter_count *count, u64 *duty_cycle)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +	*duty_cycle = 0;
> +
> +	if (!omap->enabled) {
> +		dev_err(counter->parent, "Timer is disabled.\n");
> +		omap->pdata->stop(omap->dm_timer);
> +		return 0;
> +	}
> +
> +	*duty_cycle = omap->pdata->read_cap(omap->dm_timer, false);
> +
> +	*duty_cycle = *duty_cycle > 0 ? *duty_cycle : 0;
> +
> +	return *duty_cycle;
> +}
> +
> +static int cap_omap_dmtimer_period(struct counter_device *counter,
> +					struct counter_signal *signal, u64 *freq)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +	u64 clk_freq = 0;
> +	u64 period = 0;
> +	*freq = 0;
> +
> +	if (!omap->enabled) {
> +		dev_err(counter->parent, "Timer is disabled.\n");
> +		omap->pdata->stop(omap->dm_timer);
> +		return 0;
> +	}
> +
> +	period = omap->pdata->read_cap(omap->dm_timer, true);
> +	cap_omap_dmtimer_clk_get_freq(counter, signal, &clk_freq);
> +
> +	if (period > 0)
> +		*freq = clk_freq/period;
> +
> +	return *freq+1;
> +}
> +static int cap_omap_dmtimer_enable_read(struct counter_device *counter,
> +				struct counter_count *count, u8 *enable)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +
> +	*enable = omap->enabled;
> +
> +	return 0;
> +}
> +
> +static int cap_omap_dmtimer_count_read(struct counter_device *counter,
> +			       struct counter_count *count, u64 *val)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +
> +	*val = omap->pdata->read_counter(omap->dm_timer);
> +
> +	return 0;
> +}
> +
> +static int cap_omap_dmtimer_count_write(struct counter_device *counter,
> +				struct counter_count *count, u64 val)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +
> +	if (val > U32_MAX)
> +		return -ERANGE;
> +
> +	omap->pdata->write_counter(omap->dm_timer, val);
> +
> +	return 0;
> +}
> +
> +static int cap_omap_dmtimer_enable_write(struct counter_device *counter,
> +				 struct counter_count *count, u8 enable)
> +{
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +
> +	if (enable == omap->enabled)
> +		goto out;

Return 0. Seems very odd code style quirks like this and well everywhere in this patch..

> +
> +	if (enable)
> +		cap_omap_dmtimer_apply(counter);
> +	else
> +		omap->pdata->stop(omap->dm_timer);
> +
> +	omap->enabled = enable;
> +out:
> +	return 0;
> +}
> +
> +static int cap_omap_dmtimer_function_read(struct counter_device *counter,
> +				  struct counter_count *count,
> +				  enum counter_function *function)
> +{
> +	*function = COUNTER_FUNCTION_INCREASE;
> +
> +	return 0;
> +}
> +
> +static int cap_omap_dmtimer_action_read(struct counter_device *counter,
> +				struct counter_count *count,
> +				struct counter_synapse *synapse,
> +				enum counter_synapse_action *action)
> +{
> +	*action = (synapse->signal->id == TIMER_CLOCK_SIG) ?
> +		   COUNTER_SYNAPSE_ACTION_RISING_EDGE :
> +		   COUNTER_SYNAPSE_ACTION_NONE;
> +
> +	return 0;
> +}
> +
> +static const struct counter_ops cap_omap_dmtimer_ops = {
> +	.count_read = cap_omap_dmtimer_count_read,
> +	.count_write = cap_omap_dmtimer_count_write,
> +	.function_read = cap_omap_dmtimer_function_read,
> +	.action_read = cap_omap_dmtimer_action_read,
> +};
> +
> +static const enum counter_function cap_omap_dmtimer_functions[] = {
> +	COUNTER_FUNCTION_INCREASE,
> +};
> +
> +static struct counter_comp cap_omap_dmtimer_clock_ext[] = {
> +	COUNTER_COMP_SIGNAL_U64("frequency", cap_omap_dmtimer_clk_get_freq, NULL),
> +};
> +
> +static struct counter_signal cap_omap_dmtimer_signals[] = {
> +	{
> +		.id = TIMER_CLOCK_SIG,
> +		.name = "Clock Signal",
> +		.ext = cap_omap_dmtimer_clock_ext,
> +		.num_ext = ARRAY_SIZE(cap_omap_dmtimer_clock_ext),
> +	},
> +	{
> +		.id = TIMER_INPUT_SIG,
> +		.name = "Input Signal",
> +	},
> +};
> +
> +/* Counter will increase at rising edges of a clock */
> +static const enum counter_synapse_action cap_omap_dmtimer_clock_actions[] = {
> +	COUNTER_SYNAPSE_ACTION_RISING_EDGE,
> +};
> +
> +/* No trigger here */
> +static const enum counter_synapse_action cap_omap_dmtimer_input_actions[] = {
> +	COUNTER_SYNAPSE_ACTION_NONE,
> +};
> +
> +static struct counter_synapse cap_omap_dmtimer_synapses[] = {
> +	{
> +		.actions_list = cap_omap_dmtimer_clock_actions,
> +		.num_actions = ARRAY_SIZE(cap_omap_dmtimer_clock_actions),
> +		.signal = &cap_omap_dmtimer_signals[TIMER_CLOCK_SIG],
> +	},
> +	{
> +		.actions_list = cap_omap_dmtimer_input_actions,
> +		.num_actions = ARRAY_SIZE(cap_omap_dmtimer_input_actions),
> +		.signal = &cap_omap_dmtimer_signals[TIMER_INPUT_SIG],
> +	},
> +};
> +
> +static struct counter_comp cap_omap_dmtimer_count_ext[] = {
> +	COUNTER_COMP_CAPTURE(cap_omap_dmtimer_capture, NULL),
> +	COUNTER_COMP_ENABLE(cap_omap_dmtimer_enable_read, cap_omap_dmtimer_enable_write),
> +	COUNTER_COMP_FREQUENCY(cap_omap_dmtimer_period),
> +};
> +
> +static struct counter_count cap_omap_dmtimer_counts[] = {
> +	{
> +		.name = "Timestamp Counter",
> +		.functions_list = cap_omap_dmtimer_functions,
> +		.num_functions = ARRAY_SIZE(cap_omap_dmtimer_functions),
> +		.synapses = cap_omap_dmtimer_synapses,
> +		.num_synapses = ARRAY_SIZE(cap_omap_dmtimer_synapses),
> +		.ext = cap_omap_dmtimer_count_ext,
> +		.num_ext = ARRAY_SIZE(cap_omap_dmtimer_count_ext),
> +	},
> +};
> +
> +static int cap_omap_dmtimer_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device *dev = &pdev->dev;
> +	struct dmtimer_platform_data *timer_pdata;
> +	const struct omap_dm_timer_ops *pdata;
> +	struct platform_device *timer_pdev;
> +	struct omap_dm_timer *dm_timer;
> +	struct device_node *timer;
> +	struct cap_omap_dmtimer_counter *omap;
> +	struct counter_device *counter_dev;
> +	int ret = 0;
> +
> +	timer = of_parse_phandle(np, "ti,timers", 0);
> +	if (!timer) {
> +		dev_err(&pdev->dev, "Unable to find Timer node\n");
> +		return -ENODEV;
> +	}
> +
> +	timer_pdev = of_find_device_by_node(timer);
> +	if (!timer_pdev) {
> +		dev_err(&pdev->dev, "Unable to find Timer pdev\n");
> +		ret = -ENODEV;
> +		goto err_find_timer_pdev;
> +	}
> +	timer_pdata = dev_get_platdata(&timer_pdev->dev);
> +	if (!timer_pdata) {
> +		dev_dbg(&pdev->dev,
> +			"dmtimer pdata structure NULL, deferring probe\n");
> +		ret = -EPROBE_DEFER;
> +		dev_err_probe(&pdev->dev, ret, "Probe deferred\n");
> +		goto err_platdata;
> +	}
> +
> +	pdata = timer_pdata->timer_ops;
> +
> +	if (!pdata || !pdata->request_by_node ||
> +	    !pdata->free ||
> +	    !pdata->enable ||
> +	    !pdata->disable ||
> +	    !pdata->get_fclk ||
> +	    !pdata->start ||
> +	    !pdata->stop ||
> +	    !pdata->set_load ||
> +	    !pdata->set_match ||
> +	    !pdata->set_cap ||
> +	    !pdata->get_cap_status ||
> +		!pdata->read_cap ||

tab vs space issues in several spots, fix your editor.

> +	    !pdata->set_prescaler ||
> +		!pdata->write_counter) {
> +		dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
> +		ret = -EINVAL;
> +		goto err_platdata;
> +	}
> +
> +	dm_timer = pdata->request_by_node(timer);
> +	if (!dm_timer) {
> +		ret = -EPROBE_DEFER;
> +		goto err_request_timer;
> +	}
> +
> +    /* struct cap_omap_dmtimer_counter *omap */
> +	counter_dev = devm_counter_alloc(dev, sizeof(*omap));
> +	if (!counter_dev) {
> +		dev_err(&pdev->dev, "Unable to allocate dmtimercounter\n");
> +		ret = -ENOMEM;
> +		goto err_alloc_omap;
> +	}
> +	omap = counter_priv(counter_dev);
> +
> +	counter_dev->name = TIMER_DRV_NAME;
> +	counter_dev->parent = dev;
> +	counter_dev->ops = &cap_omap_dmtimer_ops;
> +	counter_dev->signals = cap_omap_dmtimer_signals;
> +	counter_dev->num_signals = ARRAY_SIZE(cap_omap_dmtimer_signals);
> +	counter_dev->counts = cap_omap_dmtimer_counts;
> +	counter_dev->num_counts = ARRAY_SIZE(cap_omap_dmtimer_counts);
> +	mutex_init(&omap->mutex);
> +	omap->pdata = pdata;
> +	omap->dm_timer = dm_timer;
> +	omap->dm_timer_pdev = timer_pdev;
> +
> +	if (pm_runtime_active(&omap->dm_timer_pdev->dev))
> +		omap->pdata->stop(omap->dm_timer);
> +
> +	of_node_put(timer);

Why free down here, move this up right after its last use.

> +
> +	platform_set_drvdata(pdev, counter_dev);
> +
> +	ret = devm_counter_add(dev, counter_dev);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "failed to add counter\n");
> +
> +	return 0;
> +
> +err_alloc_omap:
> +	pdata->free(dm_timer);
> +err_request_timer:
> +

Unused label?

Andrew

> +err_platdata:
> +	put_device(&timer_pdev->dev);
> +err_find_timer_pdev:
> +
> +	of_node_put(timer);
> +
> +	return ret;
> +}
> +
> +static void cap_omap_dmtimer_remove(struct platform_device *pdev)
> +{
> +	struct counter_device *counter = platform_get_drvdata(pdev);
> +	struct cap_omap_dmtimer_counter *omap = counter_priv(counter);
> +
> +	counter_unregister(counter);
> +
> +	if (pm_runtime_active(&omap->dm_timer_pdev->dev))
> +		omap->pdata->stop(omap->dm_timer);
> +
> +	omap->pdata->free(omap->dm_timer);
> +
> +	put_device(&omap->dm_timer_pdev->dev);
> +
> +	mutex_destroy(&omap->mutex);
> +
> +}
> +
> +static const struct of_device_id cap_omap_dmtimer_of_match[] = {
> +	{.compatible = "ti,omap-dmtimer-cap"},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, cap_omap_dmtimer_of_match);
> +
> +static struct platform_driver cap_omap_dmtimer_driver = {
> +	.driver = {
> +		.name = "omap-dmtimer-cap",
> +		.of_match_table = cap_omap_dmtimer_of_match,
> +	},
> +	.probe = cap_omap_dmtimer_probe,
> +	.remove = cap_omap_dmtimer_remove,
> +};
> +module_platform_driver(cap_omap_dmtimer_driver);
> +
> +MODULE_AUTHOR("Gokul Praveen <g-praveen@ti.com>");
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("OMAP CAP Driver using Dual-mode Timers");
> +MODULE_IMPORT_NS("COUNTER");