[PATCH v4 1/5] pwm: add the Berlin pwm controller driver
Sebastian Hesselbarth
sebastian.hesselbarth at gmail.com
Tue Aug 18 04:53:27 PDT 2015
On 08/18/2015 01:38 PM, Antoine Tenart wrote:
> Add a PWM controller driver for the Marvell Berlin SoCs. This PWM
> controller has 4 channels.
>
> Signed-off-by: Antoine Tenart <antoine.tenart at free-electrons.com>
> Acked-by: Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
> ---
[...]
> 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 at free-electrons.com>
> + *
> + * 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);
The reason why I usually tend to _not_ use _relaxed() in low-performance
setup code is that you'll have to think about reordering issues when
using _relaxed ones.
The question here is: Is it _guaranteed_ that above writel_relaxed()
will be issued _before_ actually releasing the spin_lock?
> + 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);
Please use the same read-modify-write sequence as below for disable.
> + 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);
You _have_ to first call pwmchip_remove() and then
clk_disable_unprepare().
Sebastian
> +}
> +
> +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 at free-electrons.com>");
> +MODULE_DESCRIPTION("Marvell Berlin PWM driver");
> +MODULE_LICENSE("GPL v2");
>
More information about the linux-arm-kernel
mailing list