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

From: Farouk Bouabid
Date: Wed May 29 2024 - 06:12:52 EST


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@xxxxxxxxx>
---
drivers/pwm/Kconfig | 10 +++++
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-mule.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 126 insertions(+)

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@xxxxxxxxx>");
+MODULE_DESCRIPTION("Mule PWM driver");
+MODULE_LICENSE("GPL");

--
2.34.1