Add a PWM controller driver for the Marvell Berlin SoCs. This PWM[...]
controller has 4 channels.
Signed-off-by: Antoine Tenart <antoine.tenart@xxxxxxxxxxxxxxxxxx>
Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth@xxxxxxxxx>
---
diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c[...]
new file mode 100644
index 000000000000..1bb3958b6e4f
--- /dev/null
+++ b/drivers/pwm/pwm-berlin.c
@@ -0,0 +1,218 @@
+/*
+ * Marvell Berlin PWM driver
+ *
+ * Copyright (C) 2015 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@xxxxxxxxxxxxxxxxxx>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+
+#define berlin_pwm_readl(chip, channel, offset) \
+ readl_relaxed((chip)->base + (channel) * 0x10 + offset)
+#define berlin_pwm_writel(val, chip, channel, offset) \
+ writel_relaxed(val, (chip)->base + (channel) * 0x10 + offset)
+
+/* prescaler table: {1, 4, 8, 16, 64, 256, 1024, 4096} */
+static const u32 prescaler_diff_table[] = {
+ 1, 4, 2, 2, 4, 4, 4, 4,
+};
+
+static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
+ int duty_ns, int period_ns)
+{
+ struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
+ int prescale = 0;
+ u32 val, duty, period;
+ u64 cycles;
+
+ cycles = clk_get_rate(pwm->clk);
+ cycles *= period_ns;
+ do_div(cycles, NSEC_PER_SEC);
+
+ while (cycles > BERLIN_PWM_MAX_TCNT)
+ do_div(cycles, prescaler_diff_table[++prescale]);
+
+ if (cycles > BERLIN_PWM_MAX_TCNT)
+ return -EINVAL;
+
+ period = cycles;
+ cycles *= duty_ns;
+ do_div(cycles, period_ns);
+ duty = cycles;
+
+ spin_lock(&pwm->lock);
+
+ val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
+ val &= ~BERLIN_PWM_PRESCALE_MASK;
+ val |= prescale;
+ berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL);
+
+ berlin_pwm_writel(duty, pwm, pwm_dev->hwpwm, BERLIN_PWM_DUTY);
+ berlin_pwm_writel(period, pwm, pwm_dev->hwpwm, BERLIN_PWM_TCNT);
+ spin_unlock(&pwm->lock);[...]
+
+ return 0;
+}
+static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm_dev)
+{
+ struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
+
+ spin_lock(&pwm->lock);
+ berlin_pwm_writel(BERLIN_PWM_ENABLE, pwm, pwm_dev->hwpwm,
+ BERLIN_PWM_EN);
+ spin_unlock(&pwm->lock);[...]
+
+ return 0;
+}
+
+static void berlin_pwm_disable(struct pwm_chip *chip,
+ struct pwm_device *pwm_dev)
+{
+ struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip);
+ u32 val;
+
+ spin_lock(&pwm->lock);
+
+ val = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
+ val &= ~BERLIN_PWM_ENABLE;
+ berlin_pwm_writel(val, pwm, pwm_dev->hwpwm, BERLIN_PWM_EN);
+
+ spin_unlock(&pwm->lock);
+}
+static int berlin_pwm_remove(struct platform_device *pdev)
+{
+ struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(pwm->clk);
+ return pwmchip_remove(&pwm->chip);
+}--
+
+static struct platform_driver berlin_pwm_driver = {
+ .probe = berlin_pwm_probe,
+ .remove = berlin_pwm_remove,
+ .driver = {
+ .name = "berlin-pwm",
+ .of_match_table = berlin_pwm_match,
+ },
+};
+module_platform_driver(berlin_pwm_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@xxxxxxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Marvell Berlin PWM driver");
+MODULE_LICENSE("GPL v2");