[RFC PATCH 3/7] motion: Add simple-pwm.c PWM based DC motor controller driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxx>
---
 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@xxxxxxxxxxx>
+ */
+
+#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@xxxxxxxxxxx>");
+MODULE_DESCRIPTION("Simple PWM based DC motor motion control driver");
+MODULE_LICENSE("GPL");
-- 
2.47.2





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux