Re: [PATCH v6 2/2] pwm: sifive: Add a driver for SiFive SoC PWM

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

 



On Thu, Feb 14, 2019 at 2:04 PM Uwe Kleine-König
<u.kleine-koenig@xxxxxxxxxxxxxx> wrote:
>
> Hello,
>
> On Thu, Feb 14, 2019 at 01:25:27PM +0530, Yash Shah wrote:
> > On Wed, Feb 13, 2019 at 4:05 PM Uwe Kleine-König
> > <u.kleine-koenig@xxxxxxxxxxxxxx> wrote:
> > > On Wed, Feb 13, 2019 at 02:56:18PM +0530, Yash Shah wrote:
> > > > +static int pwm_sifive_enable(struct pwm_chip *chip, struct pwm_device *dev,
> > > > +                          bool enable)
> > > > +{
> > > > +     struct pwm_sifive_ddata *pwm = pwm_sifive_chip_to_ddata(chip);
> > > > +     u32 val;
> > > > +     int ret;
> > > > +
> > > > +     if (enable) {
> > > > +             ret = clk_enable(pwm->clk);
> > > > +             if (ret) {
> > > > +                     dev_err(pwm->chip.dev, "Enable clk failed:%d\n", ret);
> > > > +                     return ret;
> > > > +             }
> > > > +     }
> > > > +
> > > > +     val = readl(pwm->regs + PWM_SIFIVE_PWMCFG);
> > > > +
> > > > +     if (enable)
> > > > +             val |= BIT(PWM_SIFIVE_PWMCFG_EN_ALWAYS);
> > > > +     else
> > > > +             val &= ~BIT(PWM_SIFIVE_PWMCFG_EN_ALWAYS);
> > > > +
> > > > +     writel(val, pwm->regs + PWM_SIFIVE_PWMCFG);
> > > > +
> > > > +     if (!enable)
> > > > +             clk_disable(pwm->clk);
> > >
> > > A disabled PWM is supposed to output an inactive signal. If the PWM runs
> > > at (near) 100% and you disable it, does it reliably give that inactive
> > > signal after completing the currently running period?
> >
> > Yes, you are right, it just freezes at that state (100%).
> > What if I set duty cycle = 0 if (!state->enabled) before disabling the PWM?
>
> Then you only need to be sure that the inactive level is already latched
> to the pwmcmpXip output (which should only need one clock cycle if I'm
> not mistaken) before disabling the clock.
>
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *dev,
> > > > +                         struct pwm_state *state)
> > > > +{
> > > > +     struct pwm_sifive_ddata *pwm = pwm_sifive_chip_to_ddata(chip);
> > > > +     unsigned int duty_cycle;
> > > > +     u32 frac, val;
> > > > +     struct pwm_state cur_state;
> > > > +     bool enabled;
> > > > +     int ret;
> > > > +
> > > > +     pwm_get_state(dev, &cur_state);
> > > > +     enabled = cur_state.enabled;
> > > > +
> > > > +     if (state->polarity != PWM_POLARITY_INVERSED)
> > > > +             return -EINVAL;
> > > > +
> > > > +     if (state->period != cur_state.period) {
> > > > +             if (pwm->user_count != 1)
> > > > +                     return -EINVAL;
> > >
> > > I think we need locking here. Consider two pwm users on two CPUs:
> > >
> > >         CPU1                                    CPU2
> > >         pwm_sifive_apply(pwm0, period=A, ...)
> > >          check user_count==1 -> good
> > >          ...                                    pwm1 = pwm_get(...)
> > >          ...                                    pwm_sifive_apply(pwm1, period=B...)
> > >          ...                                      configure based on B
> > >          pwm_sifive_update_clock()
> >
> > mutex_lock();
> >   if (pwm->user_count != 1)
> >                   return -EINVAL;
> > mutex_unlock();
> > Something like this?
>
> No, the lock needs to protect more. You must at least cover increasing
> and decreasing of user_count and you must hold the lock until the period
> update is completed.

Got your point. Will use locks at appropriate places

>
> > > Also I wonder if we should change the period if the user requested
> > > enabled=false.
> >
> > You want me to NOT update period if enabled=false, right?
>
> I don't know for sure. Given that period is shared for all four PWM
> outputs it might be sensible to change it at least in a shadow variable
> and only do it when actually needed. (But maybe we can postpone that as
> it doesn't matter for correctness of the driver.)
>
> The question here is: In the following snippet:
>
>         pwm0 = pwm_get(... the first pwm ...)
>
>         pwm_apply_state(pwm0, { .enabled = true, .period = 4000 });
>         pwm_apply_state(pwm0, { .enabled = false, .period = 8000 });
>
>         pwm1 = pwm_get(... the second pwm ...)
>         pwm_apply_state(pwm1, { .enabled = true, .period = 4000 });
>         pwm_apply_state(pwm0, { .enabled = true, .period = 8000 });
>
> Which of the two last commands should fail?
>
> > > > +             pwm->real_period = state->period;
> > > > +             pwm_sifive_update_clock(pwm, clk_get_rate(pwm->clk));
> > >
> > > If you change from
> > >
> > >         .period = A
> > >         .duty_cycle = B
> > >
> > > to
> > >
> > >         .period = C
> > >         .duty_cycle = D
> > >
> > > the output pin might see a period with
> > >
> > >         .period = C
> > >         .duty_cycle = B
> > >
> > > right? I think this is not fixable, but this needs a prominent comment.
> >
> > Good point. Is the below comment good enough?
> > /* When changing both duty cycle and period, the old duty cycle might
> > be active with new the period settings for a period */
>
> I'd add some blame on the hardware. Something like:
>
>         /*
>          * When changing both duty cycle and period, we cannot prevent in
>          * software that the output might produce a period with mixed
>          * settings (new period length and old duty cycle).
>          */
>
> I'd say it makes sense to put this information at the top of the driver
> to have it in a prominent place. Also point out the inability to provide
> 100% duty cycle and that the hardware is limited to inverted output.
> Then all limitations are summarized in a single place.

Sure, will do as you suggested.

>
> Maybe this mismatch could be made less likely by changing the order of
> the register accesses and a delay depending on pwms and old and new
> settings. But I'd say this is too much for now and can be addressed
> later when and if necessary.
>
> > > > +     }
> > > > +
> > > > +     if (!state->enabled && enabled) {
> > > > +             ret = pwm_sifive_enable(chip, dev, false);
> > > > +             if (ret)
> > > > +                     return ret;
> > > > +             enabled = false;
> > > > +     }
> > > > +
> > > > +     duty_cycle = state->duty_cycle;
> > > > +     frac = div_u64((u64)duty_cycle * (1 << PWM_SIFIVE_CMPWIDTH) +
> > > > +                    (1 << PWM_SIFIVE_CMPWIDTH) / 2, state->period);
> > > > +     /* The hardware cannot generate a 100% duty cycle */
> > >
> > > @Thierry: Do you consider this bad enough that pwm_apply_state should
> > > fail if 100% is requested?
>
> This question is still open.
>
> > > > +     frac = min(frac, (1U << PWM_SIFIVE_CMPWIDTH) - 1);
> > > > +
> > > > +     val = readl(pwm->regs + PWM_SIFIVE_PWMCFG);
> > > > +     val |= BIT(PWM_SIFIVE_PWMCFG_DEGLITCH);
> > > > +     writel(val, pwm->regs + PWM_SIFIVE_PWMCFG);
> > > > +
> > > > +     writel(frac, pwm->regs + PWM_SIFIVE_PWMCMP0 + dev->hwpwm *
> > > > +            PWM_SIFIVE_SIZE_PWMCMP);
> > > > +
> > > > +     val &= ~BIT(PWM_SIFIVE_PWMCFG_DEGLITCH);
> > >
> > > Doesn't that come too early? I thought the right thing was to keep it
> > > set all the time. With this code I think you might see a duty cycle of
> > > 50 when going from 60 to 40.
> >
> > We cannot set it all the time.
> > Setting it all the time makes every alternate period to remain high
> > (latched state).
> > As per the manual, it needs to be set when reprogramming and must be
> > cleared afterwards.
>
> I didn't find this in the manual. When looking at Figure 6 and the
> description of pwmdeglitch I have the impression that your statement is
> wrong.
>
> Setting pwmdeglitch only prevents the output from getting low during a
> period, it can only go low when pwms overflows (i.e. the start of a
> period). That's exactly what we want. Where is the misunderstanding?
>
> If however you clear pwmdeglitch after an update, consider the following
> series of events:
>
>  - Assume pwmcmpX is 0x4000 and pwms is 0x5000, so pwmcmpXip is high.
>  - You set pwmdeglitch and change pwmcmpX to 0x8000 while pwms advanced
>    only little. Then pwmcmpXip remains high.
>  - Then you clear pwmdeglitch at say pwms = 0x5020, this makes pwmcmpXip
>    fall which we should prevent.
>
> Also note that when setting pwmdeglitch while configuring pwm0---if it
> really had the behaviour you pointed out---would interfere with the
> maybe running pwm1.
>
> So I'm convinced keeping pwmdeglitch active always is the right thing to
> do.

I agree that figure 6 suggests that pwmdeglitch should remain active
through out.
But for some strange reason when I set deglitch bit, I am seeing every
alternate pwm cycle to remain high (latched) unless I clear the
deglitch bit.
I am debugging this issue however is it ok if I remove deglitch logic
from driver until this issue has been root caused?

>
> Best regards
> Uwe
>
> --
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |




[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