devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Jander <david@protonic.nl>
To: linux-kernel@vger.kernel.org
Cc: linux-iio@vger.kernel.org, Jonathan Corbet <corbet@lwn.net>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	devicetree@vger.kernel.org, linux-doc@vger.kernel.org,
	Nuno Sa <nuno.sa@analog.com>, Jonathan Cameron <jic23@kernel.org>,
	Oleksij Rempel <o.rempel@pengutronix.de>,
	David Jander <david@protonic.nl>
Subject: [RFC PATCH 3/7] motion: Add simple-pwm.c PWM based DC motor controller driver
Date: Thu, 27 Feb 2025 17:28:19 +0100	[thread overview]
Message-ID: <20250227162823.3585810-4-david@protonic.nl> (raw)
In-Reply-To: <20250227162823.3585810-1-david@protonic.nl>

This is a simple DC-motor motion control driver that implements
 - 1 or 2 PWM channels for uni-directional or bi-directional drive
 - Trapezoidal acceleration profiles
 - Time-based movements
 - External trigger support

Tested with TI DRV8873

Signed-off-by: David Jander <david@protonic.nl>
---
 drivers/motion/Kconfig      |  13 ++-
 drivers/motion/Makefile     |   1 +
 drivers/motion/simple-pwm.c | 199 ++++++++++++++++++++++++++++++++++++
 3 files changed, 212 insertions(+), 1 deletion(-)
 create mode 100644 drivers/motion/simple-pwm.c

diff --git a/drivers/motion/Kconfig b/drivers/motion/Kconfig
index 7715301c667e..63c4bdedb12a 100644
--- a/drivers/motion/Kconfig
+++ b/drivers/motion/Kconfig
@@ -11,7 +11,6 @@ menuconfig MOTION
 
 if MOTION
 
-
 config TMC5240
 	tristate "TMC5240 stepper motor driver"
 	depends on SPI
@@ -23,6 +22,18 @@ config TMC5240
 	  To compile this driver as a module, choose M here: the
 	  module will be called tmc5240.
 
+config MOTION_SIMPLE_PWM
+	tristate "Simple PWM base DC motor driver"
+	depends on PWM
+	select MOTION_HELPERS
+	help
+	  Say Y here is you have a DC motor driver you wish to control
+	  with 1 or 2 PWM outputs. Typically this is an H-bridge or similar
+	  driver, like the TI DRV8873 for example.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called "motion-simple-pwm".
+
 config MOTION_HELPERS
 	bool
 	depends on MOTION
diff --git a/drivers/motion/Makefile b/drivers/motion/Makefile
index 4f4e31138503..6b13b527fa17 100644
--- a/drivers/motion/Makefile
+++ b/drivers/motion/Makefile
@@ -2,3 +2,4 @@
 obj-$(CONFIG_MOTION)		+= motion-core.o
 obj-$(CONFIG_MOTION_HELPERS)	+= motion-helpers.o
 obj-$(CONFIG_TMC5240)		+= tmc5240.o
+obj-$(CONFIG_MOTION_SIMPLE_PWM)	+= simple-pwm.o
diff --git a/drivers/motion/simple-pwm.c b/drivers/motion/simple-pwm.c
new file mode 100644
index 000000000000..89626c792235
--- /dev/null
+++ b/drivers/motion/simple-pwm.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Motion Control Subsystem - Simple speed proportional-PWM based motor driver
+ *
+ * Copyright (C) 2024 Protonic Holland
+ *      David Jander <david@protonic.nl>
+ */
+
+#include <asm-generic/errno-base.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/motion.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+#include <linux/pwm.h>
+#include <asm/unaligned.h>
+
+#include "motion-core.h"
+#include "motion-helpers.h"
+
+#define MOTPWM_PWM_SCALE 100000
+#define MOTPWM_TIMER_PERIOD (20 * NSEC_PER_MSEC)
+
+struct motpwm {
+	struct pwm_device *pwms[2];
+	struct motion_device mdev;
+	struct platform_device *pdev;
+	bool pwm_inverted;
+};
+
+static inline int __effective_dc(struct motpwm *mp, unsigned int dc)
+{
+	if (mp->pwm_inverted)
+		return MOTPWM_PWM_SCALE - dc;
+	return dc;
+}
+
+static inline int __motpwm_set_speed_locked(struct motpwm *mp, unsigned int dir,
+		unsigned int dc, bool enable)
+{
+	struct pwm_state ps;
+	int cidx = !dir;
+	struct pwm_device *pwm, *cpwm;
+
+	dir = !!dir;
+	pwm = mp->pwms[dir];
+	cpwm = mp->pwms[cidx];
+
+	if (cpwm) {
+		pwm_init_state(cpwm, &ps);
+		ps.duty_cycle = __effective_dc(mp, 0);
+		ps.enabled = enable;
+		pwm_apply_might_sleep(cpwm, &ps);
+	}
+	if (!pwm)
+		return -EINVAL;
+
+	pwm_init_state(pwm, &ps);
+	pwm_set_relative_duty_cycle(&ps, __effective_dc(mp, dc), MOTPWM_PWM_SCALE);
+	ps.enabled = enable;
+	pwm_apply_might_sleep(pwm, &ps);
+
+	return 0;
+}
+
+static void motpwm_startup(struct motion_device *mdev)
+{
+	dev_info(mdev->dev, "Startup\n");
+}
+
+static void motpwm_powerdown(struct motion_device *mdev)
+{
+	struct motpwm *mp = container_of(mdev, struct motpwm, mdev);
+
+	dev_info(mdev->dev, "Shutdown\n");
+	__motpwm_set_speed_locked(mp, 0, 0, false);
+}
+
+static int motpwm_check_speed(struct motion_device *mdev, unsigned int dir,
+		unsigned int speed)
+{
+	struct motpwm *mp = container_of(mdev, struct motpwm, mdev);
+
+	if (!mp->pwms[!!dir])
+		return -EINVAL;
+
+	if (speed > MOTPWM_PWM_SCALE)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void motpwm_set_speed(struct motion_device *mdev, unsigned int dir,
+		unsigned int speed)
+{
+	struct motpwm *mp = container_of(mdev, struct motpwm, mdev);
+
+	__motpwm_set_speed_locked(mp, dir, speed, true);
+}
+
+static struct motion_timed_speed_ops motpwm_mts_ops = {
+	.startup = motpwm_startup,
+	.powerdown = motpwm_powerdown,
+	.check_speed = motpwm_check_speed,
+	.set_speed = motpwm_set_speed
+};
+
+static int motpwm_probe(struct platform_device *pdev)
+{
+	struct motpwm *mp;
+	struct device *dev = &pdev->dev;
+	struct fwnode_handle *fwnode = dev_fwnode(dev);
+
+	mp = devm_kzalloc(dev, sizeof(struct motpwm), GFP_KERNEL);
+	if (!mp)
+		return -ENOMEM;
+
+	mp->pwms[0] = devm_fwnode_pwm_get(dev, fwnode, "left");
+	if (IS_ERR(mp->pwms[0])) {
+		int err = PTR_ERR(mp->pwms[0]);
+
+		if (err == -ENODEV)
+			mp->pwms[0] = NULL;
+		else
+			return err;
+	}
+	mp->pwms[1] = devm_fwnode_pwm_get(dev, fwnode, "right");
+	if (IS_ERR(mp->pwms[1])) {
+		int err = PTR_ERR(mp->pwms[1]);
+
+		if (err == -ENODEV)
+			mp->pwms[1] = NULL;
+		else
+			return err;
+	}
+	if (!mp->pwms[0] && !mp->pwms[1]) {
+		dev_err(dev, "Need at least one PWM");
+		return -ENODEV;
+	}
+
+	mp->pwm_inverted = fwnode_property_read_bool(fwnode, "motion,pwm-inverted");
+
+	mp->pdev = pdev;
+	platform_set_drvdata(pdev, mp);
+
+	mp->mdev.parent = &pdev->dev;
+	motion_timed_speed_init(&mp->mdev, &motpwm_mts_ops, MOTPWM_PWM_SCALE);
+	mp->mdev.capabilities.type = MOT_TYPE_DC_MOTOR;
+	mp->mdev.capabilities.subdiv = 1;
+	motion_fwnode_get_capabilities(&mp->mdev, fwnode);
+
+	return motion_register_device(&mp->mdev);
+}
+
+static void motpwm_shutdown(struct platform_device *pdev)
+{
+	struct motpwm *mp = platform_get_drvdata(pdev);
+
+	motion_unregister_device(&mp->mdev);
+}
+
+static int motpwm_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int motpwm_resume(struct device *dev)
+{
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(motpwm_pm, motpwm_suspend, motpwm_resume);
+
+static const struct of_device_id motpwm_of_match[] = {
+	{ .compatible = "motion-simple-pwm" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, motpwm_of_match);
+
+static struct platform_driver motpwm_driver = {
+	.probe = motpwm_probe,
+	.shutdown = motpwm_shutdown,
+	.driver = {
+		.name = "motion-simple-pwm",
+		.pm = pm_sleep_ptr(&motpwm_pm),
+		.of_match_table = motpwm_of_match,
+	},
+};
+
+module_platform_driver(motpwm_driver);
+
+MODULE_AUTHOR("David Jander <david@protonic.nl>");
+MODULE_DESCRIPTION("Simple PWM based DC motor motion control driver");
+MODULE_LICENSE("GPL");
-- 
2.47.2


  parent reply	other threads:[~2025-02-27 16:29 UTC|newest]

Thread overview: 59+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-27 16:28 [RFC PATCH 0/7] Add Linux Motion Control subsystem David Jander
2025-02-27 16:28 ` [RFC PATCH 1/7] drivers: Add motion control subsystem David Jander
2025-02-28 16:44   ` Uwe Kleine-König
2025-03-05 15:40     ` David Jander
2025-03-05 23:21       ` Uwe Kleine-König
2025-03-06  7:18         ` Greg Kroah-Hartman
2025-03-06  8:20           ` David Jander
2025-03-06  9:03             ` Greg Kroah-Hartman
2025-03-06  9:34               ` David Jander
2025-03-06 13:39                 ` Greg Kroah-Hartman
2025-03-06 14:25                   ` David Jander
2025-03-06 14:54                     ` Greg Kroah-Hartman
2025-03-06  9:25         ` David Jander
2025-03-09 17:32           ` Jonathan Cameron
2025-03-10  8:45             ` David Jander
2025-02-28 22:36   ` David Lechner
2025-03-03  8:36     ` David Jander
2025-03-03 11:01       ` Pavel Pisa
2025-03-03 16:04         ` David Jander
2025-02-27 16:28 ` [RFC PATCH 2/7] motion: Add ADI/Trinamic TMC5240 stepper motor controller David Jander
2025-02-27 16:28 ` David Jander [this message]
2025-02-27 16:28 ` [RFC PATCH 4/7] Documentation: Add Linux Motion Control documentation David Jander
2025-02-27 16:37   ` Jonathan Corbet
2025-02-28 13:02     ` David Jander
2025-02-28 14:42       ` Jonathan Corbet
2025-02-28 15:06         ` David Jander
2025-02-27 16:28 ` [RFC PATCH 5/7] dt-bindings: motion: Add common motion device properties David Jander
2025-02-28  7:06   ` Krzysztof Kozlowski
2025-02-28  7:13   ` Krzysztof Kozlowski
2025-02-27 16:28 ` [RFC PATCH 6/7] dt-bindings: motion: Add adi,tmc5240 bindings David Jander
2025-02-28  7:11   ` Krzysztof Kozlowski
2025-02-28  8:48     ` David Jander
2025-02-28  9:35       ` Krzysztof Kozlowski
2025-02-28  9:51         ` David Jander
2025-02-28 14:01           ` Krzysztof Kozlowski
2025-02-28 22:38   ` David Lechner
2025-03-03 11:22     ` David Jander
2025-03-03 12:28       ` David Lechner
2025-03-03 13:18         ` David Jander
2025-02-27 16:28 ` [RFC PATCH 7/7] dt-bindings: motion: Add motion-simple-pwm bindings David Jander
2025-02-27 17:38   ` Rob Herring (Arm)
2025-02-28  7:12   ` Krzysztof Kozlowski
2025-02-28  9:22     ` David Jander
2025-02-28  9:37       ` Krzysztof Kozlowski
2025-02-28 10:09         ` David Jander
2025-02-28 15:18           ` Uwe Kleine-König
2025-03-03 10:53             ` Maud Spierings
2025-03-03 11:40             ` David Jander
2025-03-03 14:18               ` Krzysztof Kozlowski
2025-03-03 16:09                 ` David Jander
2025-02-28 22:41   ` David Lechner
2025-03-03 12:54     ` David Jander
2025-02-28  9:34 ` [RFC PATCH 0/7] Add Linux Motion Control subsystem Pavel Pisa
2025-02-28  9:35 ` Pavel Pisa
2025-02-28 11:57   ` David Jander
2025-02-28 15:23     ` Pavel Pisa
2025-03-03 10:45       ` David Jander
2025-02-28 22:36 ` David Lechner
2025-03-03  8:28   ` David Jander

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250227162823.3585810-4-david@protonic.nl \
    --to=david@protonic.nl \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=devicetree@vger.kernel.org \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nuno.sa@analog.com \
    --cc=o.rempel@pengutronix.de \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).