diff mbox series

[2/6] pwm: add mule pwm-over-i2c driver

Message ID 20240529-buzzer_support-v1-2-fd3eb0a24442@cherry.de (mailing list archive)
State New
Headers show
Series Add Mule PWM-over-I2C support | expand

Commit Message

Farouk Bouabid May 29, 2024, 10:10 a.m. UTC
Mule is a device that can output a PWM signal based on I2C commands.

Add pwm driver for Mule PWM-over-I2C controller.

Signed-off-by: Farouk Bouabid <farouk.bouabid@cherry.de>
---
 drivers/pwm/Kconfig    |  10 +++++
 drivers/pwm/Makefile   |   1 +
 drivers/pwm/pwm-mule.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+)
diff mbox series

Patch

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 4b956d661755..eb8cfa113ec7 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -425,6 +425,16 @@  config PWM_MICROCHIP_CORE
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-microchip-core.
 
+config PWM_MULE
+	tristate "Mule PWM-over-I2C support"
+	depends on I2C && OF
+	help
+	  PWM driver for Mule PWM-over-I2C controller. Mule is a device
+	  that can output a PWM signal based on I2C commands.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-mule.
+
 config PWM_MXS
 	tristate "Freescale MXS PWM support"
 	depends on ARCH_MXS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index c5ec9e168ee7..cdd736ea3244 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -38,6 +38,7 @@  obj-$(CONFIG_PWM_MESON)		+= pwm-meson.o
 obj-$(CONFIG_PWM_MEDIATEK)	+= pwm-mediatek.o
 obj-$(CONFIG_PWM_MICROCHIP_CORE)	+= pwm-microchip-core.o
 obj-$(CONFIG_PWM_MTK_DISP)	+= pwm-mtk-disp.o
+obj-$(CONFIG_PWM_MULE)		+= pwm-mule.o
 obj-$(CONFIG_PWM_MXS)		+= pwm-mxs.o
 obj-$(CONFIG_PWM_NTXEC)		+= pwm-ntxec.o
 obj-$(CONFIG_PWM_OMAP_DMTIMER)	+= pwm-omap-dmtimer.o
diff --git a/drivers/pwm/pwm-mule.c b/drivers/pwm/pwm-mule.c
new file mode 100644
index 000000000000..e8593a48b16e
--- /dev/null
+++ b/drivers/pwm/pwm-mule.c
@@ -0,0 +1,115 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mule PWM-over-I2C controller driver
+ *
+ * Copyright (C) 2024 Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+struct mule_pwm {
+	struct mutex lock;
+	struct regmap *regmap;
+};
+
+static const struct regmap_config pwm_mule_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+};
+
+#define MULE_PWM_DCY_REG	0x0
+#define MULE_PWM_FREQ_L_REG	0x1	/* LSB register */
+#define MULE_PWM_FREQ_H_REG	0x2	/* MSB register */
+
+#define NANOSECONDS_TO_HZ(x) (1000000000UL/(x))
+
+static int pwm_mule_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			      const struct pwm_state *state)
+{
+	struct mule_pwm *priv = pwmchip_get_drvdata(chip);
+	u8 duty_cycle;
+	u64 freq;
+	int ret;
+
+	freq = NANOSECONDS_TO_HZ(state->period);
+
+	if (freq > U16_MAX) /* Frequency is 16-bit wide */ {
+		dev_err(chip->dev,
+			"Failed to set frequency: %llu Hz: out of 16-bit range\n", freq);
+		return -EINVAL;
+	}
+
+	if (state->enabled)
+		duty_cycle = pwm_get_relative_duty_cycle(state, 100);
+	else
+		duty_cycle = 0;
+
+	mutex_lock(&priv->lock);
+
+	ret = regmap_bulk_write(priv->regmap, MULE_PWM_FREQ_L_REG, &freq, 2);
+	if (ret) {
+		dev_err(chip->dev,
+			"Failed to set frequency: %llu Hz: %d\n", freq, ret);
+		goto out;
+	}
+
+	ret = regmap_write(priv->regmap, MULE_PWM_DCY_REG, duty_cycle);
+	if (ret)
+		dev_err(chip->dev,
+			"Failed to set duty cycle: %u: %d\n", duty_cycle, ret);
+
+out:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static const struct pwm_ops pwm_mule_ops = {
+	.apply = pwm_mule_apply,
+};
+
+static int pwm_mule_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct pwm_chip *chip;
+	struct mule_pwm *priv;
+
+	chip = devm_pwmchip_alloc(dev, 1, sizeof(*priv));
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);
+
+	priv = pwmchip_get_drvdata(chip);
+
+	mutex_init(&priv->lock);
+
+	priv->regmap = devm_regmap_init_i2c(client, &pwm_mule_config);
+	if (IS_ERR(priv->regmap))
+		return dev_err_probe(dev, PTR_ERR(priv->regmap),
+				     "Failed to allocate i2c register map\n");
+
+	chip->ops = &pwm_mule_ops;
+
+	return devm_pwmchip_add(dev, chip);
+}
+
+static const struct of_device_id pwm_mule_of_match[] = {
+	{ .compatible = "tsd,pwm-mule", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pwm_mule_of_match);
+
+static struct i2c_driver pwm_mule_driver = {
+	.driver = {
+		.name = "pwm-mule",
+		.of_match_table = pwm_mule_of_match,
+	},
+	.probe = pwm_mule_probe,
+};
+module_i2c_driver(pwm_mule_driver);
+
+MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>");
+MODULE_DESCRIPTION("Mule PWM driver");
+MODULE_LICENSE("GPL");