Re: [PATCH v7 2/2] pwm: Add Loongson PWM controller support

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

 



Hi Uwe:

Thanks for your detailed reply.

On Sat, Nov 23, 2024 at 1:19 AM Uwe Kleine-König <ukleinek@xxxxxxxxxx> wrote:
>
> Hello,
>
> I now finally comne around to review your patch. Thanks for your
> patience.
>
> On Tue, Oct 22, 2024 at 05:04:15PM +0800, Binbin Zhou wrote:
> > diff --git a/drivers/pwm/pwm-loongson.c b/drivers/pwm/pwm-loongson.c
> > new file mode 100644
> > index 000000000000..4c9b14efadc3
> > --- /dev/null
> > +++ b/drivers/pwm/pwm-loongson.c
> > @@ -0,0 +1,288 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Copyright (C) 2017-2024 Loongson Technology Corporation Limited.
> > + *
> > + * Loongson PWM driver
> > + *
> > + * For Loongson's PWM IP block documentation please refer Chapter 11 of
> > + * Reference Manual: https://loongson.github.io/LoongArch-Documentation/Loongson-7A1000-usermanual-EN.pdf
> > + *
> > + * Author: Juxin Gao <gaojuxin@xxxxxxxxxxx>
> > + * Further cleanup and restructuring by:
> > + *         Binbin Zhou <zhoubinbin@xxxxxxxxxxx>
> > + *
> > + * Limitations:
> > + * - The buffer register value should be written before the CTRL register.
>
> This isn't an interesting point for the high level description. I'd hope
> the driver cares for this implementation detail.
>
> > + * - When disabled the output is driven to 0 independent of the configured
> > + *   polarity.
> > + */
> > +
> > +#include <linux/acpi.h>
> > +#include <linux/clk.h>
> > +#include <linux/device.h>
> > +#include <linux/init.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pwm.h>
> > +#include <linux/units.h>
> > +
> > +/* Loongson PWM registers */
> > +#define LOONGSON_PWM_REG_DUTY                0x4 /* Low Pulse Buffer Register */
> > +#define LOONGSON_PWM_REG_PERIOD              0x8 /* Pulse Period Buffer Register */
> > +#define LOONGSON_PWM_REG_CTRL                0xc /* Control Register */
> > +
> > +/* Control register bits */
> > +#define LOONGSON_PWM_CTRL_EN         BIT(0)  /* Counter Enable Bit */
> > +#define LOONGSON_PWM_CTRL_OE         BIT(3)  /* Pulse Output Enable Control Bit, Valid Low */
> > +#define LOONGSON_PWM_CTRL_SINGLE     BIT(4)  /* Single Pulse Control Bit */
> > +#define LOONGSON_PWM_CTRL_INTE               BIT(5)  /* Interrupt Enable Bit */
> > +#define LOONGSON_PWM_CTRL_INT                BIT(6)  /* Interrupt Bit */
> > +#define LOONGSON_PWM_CTRL_RST                BIT(7)  /* Counter Reset Bit */
> > +#define LOONGSON_PWM_CTRL_CAPTE              BIT(8)  /* Measurement Pulse Enable Bit */
> > +#define LOONGSON_PWM_CTRL_INVERT     BIT(9)  /* Output flip-flop Enable Bit */
> > +#define LOONGSON_PWM_CTRL_DZONE              BIT(10) /* Anti-dead Zone Enable Bit */
>
> Most of these are unused. And you only ever access the CTRL register
> using read-modify-write. So I guess the behaviour of the hardware
> depends on how the bootloader (or boot rom) initialized these bits. I
> would prefer if you could this more deterministic.

Emm, I would explicitly initialize the CTRL register value to 0 in probe().
>
> > +#define LOONGSON_PWM_FREQ_STD                (50 * HZ_PER_KHZ)
>
> Maybe it's just me, but I think the HZ_PER_KHZ doesn't add readability
> and I would have just done:
>
>         /* default input clk frequency for the ACPI case */
>         #define LOONGSON_PWM_FREQ_DEFAULT       50000 /* Hz */

OK....
>
> > [...]
> > +static int pwm_loongson_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > +                           const struct pwm_state *state)
> > +{
> > +     int ret;
> > +     u64 period, duty_cycle;
> > +     bool enabled = pwm->state.enabled;
> > +
> > +     if (!state->enabled) {
> > +             if (enabled)
> > +                     pwm_loongson_disable(chip, pwm);
> > +             return 0;
> > +     }
> > +
> > +     ret = pwm_loongson_set_polarity(chip, pwm, state->polarity);
> > +     if (ret)
> > +             return ret;
>
> Is setting the polarity shadowed in hardware or does it take effect
> immediately? If the latter please mention that the output might glitch
> on reconfiguration in the Limitations section above.. Another
> "opportunity" to glitch is in pwm_loongson_config() above when
> LOONGSON_PWM_REG_DUTY is written but LOONGSON_PWM_REG_PERIOD isn't yet.

The setting of the polarity is shadowed in hardware, maybe we don't
need to worry.
>
> > +     period = min(state->period, NANOHZ_PER_HZ);
> > +     duty_cycle = min(state->duty_cycle, NANOHZ_PER_HZ);
>
> period and duty_cycle are measured in nanoseconds. So NSEC_PER_SEC is
> more natural to them than NANOHZ_PER_HZ.

OK...
>
> > +     ret = pwm_loongson_config(chip, pwm, duty_cycle, period);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (!enabled && state->enabled)
> > +             ret = pwm_loongson_enable(chip, pwm);
> > +
> > +     return ret;
> > +}
> > [...]
> > +static int pwm_loongson_probe(struct platform_device *pdev)
> > +{
> > +     int ret;
> > +     struct pwm_chip *chip;
> > +     struct pwm_loongson_ddata *ddata;
> > +     struct device *dev = &pdev->dev;
> > +
> > +     chip = devm_pwmchip_alloc(dev, 1, sizeof(*ddata));
> > +     if (IS_ERR(chip))
> > +             return PTR_ERR(chip);
> > +     ddata = to_pwm_loongson_ddata(chip);
> > +
> > +     ddata->base = devm_platform_ioremap_resource(pdev, 0);
> > +     if (IS_ERR(ddata->base))
> > +             return PTR_ERR(ddata->base);
> > +
> > +     if (!has_acpi_companion(dev)) {
> > +             ddata->clk = devm_clk_get_enabled(dev, NULL);
> > +             if (IS_ERR(ddata->clk))
> > +                     return dev_err_probe(dev, PTR_ERR(ddata->clk),
> > +                                          "failed to get pwm clock\n");
> > +             ddata->clk_rate = clk_get_rate(ddata->clk);
>
> I guess you rely on the clockrate to not change. So please add a call to
> devm_clk_rate_exclusive_get().
>
> > +     } else {
> > +             ddata->clk_rate = LOONGSON_PWM_FREQ_STD;
>
> I thought that clk_get() also works for devices described by ACPI?
>
> Maybe something like this gives more flexibility:
>
>         ddata->clk = devm_clk_get_optional_enabled(dev, NULL);
>         if (IS_ERR(ddata->clk))
>                 return dev_err_probe(...);
>
>         if (ddata->clk) {
>                 ret = devm_clk_rate_exclusive_get(...);
>                 if (ret)
>                         return ret;
>
>                 ddata->clk_rate = clk_get_rate(ddata->clk);
>         } else {
>                 ddata->clk_rate = LOONGSON_PWM_FREQ_STD;
>         }
>
> and it's conceptually easier given that it doesn't have to care about
> the device being described in ACPI or dt.
>
> Just a suggestion.

OK, I will try to do it.
>
> > +     }
> > +
> > +     chip->ops = &pwm_loongson_ops;
> > +     chip->atomic = true;
> > +     dev_set_drvdata(dev, chip);
> > +
> > +     ret = devm_pwmchip_add(dev, chip);
> > +     if (ret < 0)
> > +             return dev_err_probe(dev, ret, "failed to add PWM chip\n");
> > +
> > +     return 0;
> > +}
--
Thanks.
Binbin





[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