Re: [RFC PATCH] clocksource/drivers/sh_cmt: access registers according to spec

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

 



Hi Wolfram,

On Wed, Nov 16, 2022 at 9:21 PM Wolfram Sang
<wsa+renesas@xxxxxxxxxxxxxxxxxxxx> wrote:
> Documentation for most CMTs say that we need to wait two input clocks
> before changes propagate to the timer. This is especially relevant when
> we stop the timer to change further settings. Implement the delays
> according to the spec. To avoid unnecessary delays in atomic mode, we
> also check if the to-be-written value actually differs. CMCNT is a bit
> special because testing showed that we need to wait 3 cycles instead.
> AFAIU, this is also true for all CMTs. Also, the WRFLAG needs to be
> checked before writing. This fixes "cannot clear CMCNT" messages which
> occur often on R-Car Gen4 SoCs, but only very rarely on older SoCs for
> some reason.
>
> Fixes: 81b3b2711072 ("clocksource: sh_cmt: Add support for multiple channels per device")
> Signed-off-by: Wolfram Sang <wsa+renesas@xxxxxxxxxxxxxxxxxxxx>

Thanks for your patch!

> --- a/drivers/clocksource/sh_cmt.c
> +++ b/drivers/clocksource/sh_cmt.c
> @@ -13,6 +13,7 @@
>  #include <linux/init.h>
>  #include <linux/interrupt.h>
>  #include <linux/io.h>
> +#include <linux/iopoll.h>
>  #include <linux/ioport.h>
>  #include <linux/irq.h>
>  #include <linux/module.h>
> @@ -116,6 +117,7 @@ struct sh_cmt_device {
>         void __iomem *mapbase;
>         struct clk *clk;
>         unsigned long rate;
> +       unsigned long reg_delay;

unsigned int (everywhere)?
Yes, this is the result of a long-by-long division, but it should be
a small value anyway.

>
>         raw_spinlock_t lock; /* Protect the shared start/stop register */
>

> @@ -270,12 +284,26 @@ static inline u32 sh_cmt_read_cmcnt(struct sh_cmt_channel *ch)
>
>  static inline void sh_cmt_write_cmcnt(struct sh_cmt_channel *ch, u32 value)
>  {
> +       /* Tests showed that we need to wait 3 clocks here */
> +       unsigned long cmcnt_delay = ch->cmt->reg_delay * 3 / 2;

DIV_ROUND_UP()

> +       u32 reg;
> +
> +       if (ch->cmt->info->model > SH_CMT_16BIT)
> +               read_poll_timeout_atomic(sh_cmt_read_cmcsr, reg, !(reg & SH_CMT32_CMCSR_WRFLG),
> +                                        1, cmcnt_delay, false, ch);
> +
>         ch->cmt->info->write_count(ch->ioctrl, CMCNT, value);
> +       udelay(cmcnt_delay);
>  }

> @@ -1032,10 +1043,16 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
>         if (ret < 0)
>                 goto err_clk_unprepare;
>
> -       if (cmt->info->width == 16)
> -               cmt->rate = clk_get_rate(cmt->clk) / 512;
> -       else
> -               cmt->rate = clk_get_rate(cmt->clk) / 8;
> +       rate = clk_get_rate(cmt->clk);
> +       if (!rate) {
> +               ret = -EINVAL;
> +               goto err_clk_disable;
> +       }
> +
> +       /* We shall wait 2 input clks after register writes */
> +       if (cmt->info->model >= SH_CMT_48BIT) // FIXME: Really not needed for older ones?
> +               cmt->reg_delay = 2000000UL / rate + 1;
> +       cmt->rate = cmt->info->width == 16 ? rate / 512 : rate / 8;

cmt->rate = rate / (cmt->info->width == 16 ? 512 : 8);

>
>         /* Map the memory resource(s). */
>         ret = sh_cmt_map_memory(cmt);

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds



[Index of Archives]     [Linux Samsung SOC]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux