diff mbox series

[RFC,01/28] clk: renesas: rzg2l: Add FOUTPOSTDIV clk support

Message ID 20220112174612.10773-2-biju.das.jz@bp.renesas.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show
Series Add RZ/G2L Display support | expand

Commit Message

Biju Das Jan. 12, 2022, 5:45 p.m. UTC
PLL5 generates FOUTPOSTDIV clk and is sourced by LCDC/DSI modules.
The FOUTPOSTDIV is connected to PLL5_4 MUX. Video clock is sourced
from DSI divider which is connected to PLL5_4 MUX.

Added 2 LUT's for generating FOUTPOSTDIV, 1 for DSI mode and other
for DPI mode as it requires different parameters for generating the
video clock. The LUT supports minimal set of frequency used by
commonly used resolutions.

This patch uses the above LUT to generate the required video clock
by matching the frequency value in LUT with FOUTPOSTDIV/DSI_DIV.

Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
---
 drivers/clk/renesas/rzg2l-cpg.c | 217 ++++++++++++++++++++++++++++++++
 drivers/clk/renesas/rzg2l-cpg.h |  15 +++
 2 files changed, 232 insertions(+)

Comments

Geert Uytterhoeven Feb. 1, 2022, 11:44 a.m. UTC | #1
Hi Biju,

On Wed, Jan 12, 2022 at 6:46 PM Biju Das <biju.das.jz@bp.renesas.com> wrote:
> PLL5 generates FOUTPOSTDIV clk and is sourced by LCDC/DSI modules.
> The FOUTPOSTDIV is connected to PLL5_4 MUX. Video clock is sourced
> from DSI divider which is connected to PLL5_4 MUX.
>
> Added 2 LUT's for generating FOUTPOSTDIV, 1 for DSI mode and other
> for DPI mode as it requires different parameters for generating the
> video clock. The LUT supports minimal set of frequency used by
> commonly used resolutions.
>
> This patch uses the above LUT to generate the required video clock
> by matching the frequency value in LUT with FOUTPOSTDIV/DSI_DIV.
>
> Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>

Thanks for your patch!

> --- a/drivers/clk/renesas/rzg2l-cpg.c
> +++ b/drivers/clk/renesas/rzg2l-cpg.c
> @@ -78,6 +78,8 @@ struct sd_hw_data {
>   * @last_dt_core_clk: ID of the last Core Clock exported to DT
>   * @notifiers: Notifier chain to save/restore clock state for system resume
>   * @info: Pointer to platform data
> + * @pll5_table: Table containing a set of pll5 parameters
> + * @num_pll5_entries: Number of entries in pll5 table
>   */
>  struct rzg2l_cpg_priv {
>         struct reset_controller_dev rcdev;
> @@ -93,6 +95,9 @@ struct rzg2l_cpg_priv {
>
>         struct raw_notifier_head notifiers;
>         const struct rzg2l_cpg_info *info;
> +
> +       const struct rzg2l_pll5_param *pll5_table;
> +       unsigned int num_pll5_entries;
>  };
>
>  static void rzg2l_cpg_del_clk_provider(void *data)
> @@ -266,6 +271,203 @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core,
>         return clk_hw->clk;
>  }
>
> +struct rzg2l_pll5_param {
> +       u64     frequency;

u32 should be sufficient.

> +       u64     pl5_refdiv;

u8? ;-)

(and move down to avoid gaps)

> +       u32     pl5_intin;

u8?

> +       u32     pl5_fracin;
> +       u8      pl5_postdiv1;
> +       u8      pl5_postdiv2;
> +       u8      dsi_div_a;
> +       u8      dsi_div_b;
> +       u8      dsi_div;
> +       u8      clksrc;
> +};
> +
> +static const struct rzg2l_pll5_param dsi_mode_param[] = {
> +       {   25175000, 1,  16, 13141593, 1, 1, 2, 3, 16, 0 }, /* VGA 25.175MHz */
> +       {   25200000, 1,  16, 13421773, 1, 1, 2, 3, 16, 0 }, /* VGA 25.200MHz */
> +       {   27000000, 1,  18,        0, 1, 1, 2, 3, 16, 0 }, /* 480p/576p 27.000MHz */
> +       {   27027000, 1,  18,   301990, 1, 1, 2, 3, 16, 0 }, /* 480p 27.027MHz */
> +       {   29605000, 1,  19, 12359216, 1, 1, 2, 3, 16, 0 }, /* WVGA 29.605MHz */
> +       {   40000000, 2,  80,        0, 2, 1, 1, 2,  6, 0 }, /* SVGA 40.00MHz */
> +       {   65000000, 1,  43,  5592405, 1, 1, 2, 3, 16, 0 }, /* XGA 65.00MHz */
> +       {   71000000, 2,  71,        0, 1, 1, 1, 2,  6, 0 }, /* WXGA 1280x800 71.0MHz */
> +       {   74176000, 1,  49,  7560932, 1, 1, 2, 3, 16, 0 }, /* 720p 74.176MHz */
> +       {   74250000, 1,  49,  8388608, 1, 1, 2, 3, 16, 0 }, /* 720p 74.25MHz */
> +       {   85500000, 2,  85,  8388608, 1, 1, 1, 2,  6, 0 }, /* FWXGA 1360x768 85.5MHz */
> +       {   88750000, 2,  88, 12582912, 1, 1, 1, 2,  6, 1 }, /* WXGA+ 1440x900 88.75MHz */
> +       {  108000000, 2, 108,        0, 1, 1, 1, 2,  6, 1 }, /* SXGA 108MHz */
> +       {  148500000, 2, 148,  8388608, 1, 1, 1, 2,  6, 1 }, /* 1080p 148.5MHz */
> +       { 3000000000, 1, 125,        0, 1, 1, 0, 0,  0, 0 }, /* 3000 MHz */

3000000000U, as this is larger than 1^31.
Why do you need the 3 GHz entry? There are no such high video modes.

Do you need .dsi_div?
.dsi_div = (1 << .dsi_div_a) * (.dsi_div_b + 1)

Personally, I don't like tables, as calculations are more flexible.
I'd expect the formula to be:

    .frequency = extal / .pl5_refdiv *
                 ((.pl5_intin << 24 + .pl5_fracin) >> 24) /
                 (.pl5_postdiv1 * .pl5_postdiv * .dsi_div)

(with extal = 24 MHz)

However, that gives the wrong results for .pl5_refdiv = 2:
  - For such entries in the above table, it calculates a frequency
    that's twice the value in the table,
  - For such entries in the below table, it calculates a frequency
    that's half the value in the table.

Note that some entries (esp. the first one) from the table below give
a clock rate closer to the expected one the corresponding entries
from the table above.

> +};
> +
> +static const struct rzg2l_pll5_param dpi_mode_param[] = {
> +       {   25175000, 1, 102, 13386820, 7, 7, 1, 0, 2, 0 }, /* VGA 25.175MHz */
> +       {   25200000, 1,  73,  8388608, 7, 5, 1, 0, 2, 0 }, /* VGA 25.200MHz */
> +       {   27000000, 1,  78, 12582912, 7, 5, 1, 0, 2, 0 }, /* 480p/576p 27.000MHz */
> +       {   27027000, 1, 110,  6043992, 7, 7, 1, 0, 2, 0 }, /* 480p 27.027MHz */
> +       {   29605000, 1,  88, 13673431, 6, 6, 1, 0, 2, 0 }, /* WVGA 29.605MHz */
> +       {   40000000, 1,  70,        0, 7, 3, 1, 0, 2, 0 }, /* SVGA 40.00MHz */
> +       {   65000000, 1,  81,  4194304, 5, 3, 1, 0, 2, 0 }, /* XGA 65.00MHz */
> +       {   71000000, 2,  71,        0, 6, 2, 1, 0, 2, 0 }, /* WXGA 1280x800 71.0MHz */
> +       {   74176000, 1,  74,  2952790, 6, 2, 1, 0, 2, 0 }, /* 720p 74.176MHz */
> +       {   74250000, 1,  86, 10485760, 7, 2, 1, 0, 2, 0 }, /* 720p 74.25MHz */
> +       {   85500000, 1,  83,  8388608, 6, 2, 1, 0, 2, 0 }, /* WXGA 1280x800 83.5MHz   */

83500000

> +       { 3000000000, 1, 125,        0, 1, 1, 0, 0, 0, 0 }, /* 3000 MHz */
> +};
> +
> +static int rzg2l_cpg_sipll5_get_index(unsigned long rate,
> +                                     const struct rzg2l_pll5_param *pll5tab,
> +                                     unsigned int n)

Perhaps just pass priv instead of pll5tab and n?

> +{
> +       unsigned int  i;
> +
> +       for (i = 0; i < n; i++) {
> +               if (pll5tab[i].frequency  == rate / pll5tab[i].dsi_div)
> +                       break;
> +       }
> +
> +       if (i == n)
> +               i--;
> +
> +       return i;
> +}
> +
> +struct sipll5 {
> +       struct clk_hw hw;
> +       unsigned int conf;
> +       unsigned long rate;
> +       struct rzg2l_cpg_priv *priv;
> +};
> +
> +#define to_sipll5(_hw) container_of(_hw, struct sipll5, hw)
> +
> +static unsigned long rzg2l_cpg_sipll5_get_rate(unsigned long rate,
> +                                              const struct rzg2l_pll5_param *pll5tab,
> +                                              unsigned int n)

This function has only a single caller, so perhaps it's better to
inline it manually?

> +{
> +       int index = rzg2l_cpg_sipll5_get_index(rate, pll5tab, n);
> +
> +       return pll5tab[index].frequency * pll5tab[index].dsi_div;
> +}
> +
> +static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw,
> +                                                 unsigned long parent_rate)
> +{
> +       struct sipll5 *sipll5 = to_sipll5(hw);
> +
> +       return sipll5->rate;
> +}
> +
> +static long rzg2l_cpg_sipll5_round_rate(struct clk_hw *hw,
> +                                       unsigned long rate,
> +                                       unsigned long *parent_rate)
> +{
> +       struct sipll5 *sipll5 = to_sipll5(hw);
> +       struct rzg2l_cpg_priv *priv = sipll5->priv;
> +       const struct rzg2l_pll5_param *pll5tab = priv->pll5_table;
> +
> +       sipll5->rate = rzg2l_cpg_sipll5_get_rate(rate, pll5tab, priv->num_pll5_entries);
> +       return sipll5->rate;
> +}
> +
> +static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
> +                                    unsigned long rate,
> +                                    unsigned long parent_rate)
> +{
> +       struct sipll5 *sipll5 = to_sipll5(hw);
> +       struct rzg2l_cpg_priv *priv = sipll5->priv;
> +       const struct rzg2l_pll5_param *pll5tab = priv->pll5_table;
> +       u8 id = rzg2l_cpg_sipll5_get_index(rate, pll5tab, priv->num_pll5_entries);
> +       int ret;
> +       u32 val;
> +
> +       /* Put PLL5 into standby mode */
> +       writel(0x00050000, priv->base + CPG_SIPLL5_STBY);

Please add defines for magic numbers

> +       ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
> +                                !(val & CPG_SIPLL5_MON_PLL5_LOCK), 100, 250000);
> +       if (ret) {
> +               dev_err(priv->dev, "failed to release pll5 lock");
> +               return ret;
> +       }
> +
> +       /* Output clock setting 1 */
> +       writel(0x01110000 |

Magic number

> +              (pll5tab[id].pl5_postdiv1 << 0) |
> +              (pll5tab[id].pl5_postdiv2 << 4) |
> +              (pll5tab[id].pl5_refdiv << 8),
> +              priv->base + CPG_SIPLL5_CLK1);
> +       /* Output clock setting, SSCG modulation value setting 3 */
> +       writel((0 << 0) | (pll5tab[id].pl5_fracin << 8), priv->base + CPG_SIPLL5_CLK3);
> +       /* Output clock setting 4 */
> +       writel(0x000000ff | (pll5tab[id].pl5_intin << 16), priv->base + CPG_SIPLL5_CLK4);

magic number (and more below)

> +
> +       /* SSCG modulation value setting 5 */
> +       writel((0x16 << 0), priv->base + CPG_SIPLL5_CLK5);

> +       /* PLL normal mode setting */
> +       writel(0x00050001, priv->base + CPG_SIPLL5_STBY);
> +
> +       /* PLL normal mode transition, output clock stability check */
> +       ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
> +                                (val & CPG_SIPLL5_MON_PLL5_LOCK), 100, 250000);
> +       if (ret) {
> +               dev_err(priv->dev, "failed to lock pll5");
> +               return ret;
> +       }
> +
> +       return 0;
> +}

> @@ -918,6 +1123,18 @@ static int __init rzg2l_cpg_probe(struct platform_device *pdev)
>         priv->num_resets = info->num_resets;
>         priv->last_dt_core_clk = info->last_dt_core_clk;
>
> +       priv->pll5_table = dpi_mode_param;
> +       priv->num_pll5_entries = ARRAY_SIZE(dpi_mode_param);
> +       /* Fix me: Selection of the table needs to be overridden by either
> +        * 1) a property in DTS or
> +        * 2) Detecting DSI/DPI mode from dts or
> +        * 3) DSI/DPI mode runtime detection
> +        */
> +       if (info->pll5_lcdc_dsi_mode) {
> +               priv->pll5_table = dsi_mode_param;
> +               priv->num_pll5_entries = ARRAY_SIZE(dsi_mode_param);
> +       }
> +

I'd expect that to be detected at runtime, or derived from what clock
rate(s) the display driver asks for?

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

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
Biju Das March 18, 2022, 10:21 a.m. UTC | #2
Hi Geert,

Thanks for the feedback.

> Subject: Re: [RFC 01/28] clk: renesas: rzg2l: Add FOUTPOSTDIV clk support
> 
> Hi Biju,
> 
> On Wed, Jan 12, 2022 at 6:46 PM Biju Das <biju.das.jz@bp.renesas.com>
> wrote:
> > PLL5 generates FOUTPOSTDIV clk and is sourced by LCDC/DSI modules.
> > The FOUTPOSTDIV is connected to PLL5_4 MUX. Video clock is sourced
> > from DSI divider which is connected to PLL5_4 MUX.
> >
> > Added 2 LUT's for generating FOUTPOSTDIV, 1 for DSI mode and other for
> > DPI mode as it requires different parameters for generating the video
> > clock. The LUT supports minimal set of frequency used by commonly used
> > resolutions.
> >
> > This patch uses the above LUT to generate the required video clock by
> > matching the frequency value in LUT with FOUTPOSTDIV/DSI_DIV.
> >
> > Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
> 
> Thanks for your patch!
> 
> > --- a/drivers/clk/renesas/rzg2l-cpg.c
> > +++ b/drivers/clk/renesas/rzg2l-cpg.c
> > @@ -78,6 +78,8 @@ struct sd_hw_data {
> >   * @last_dt_core_clk: ID of the last Core Clock exported to DT
> >   * @notifiers: Notifier chain to save/restore clock state for system
> resume
> >   * @info: Pointer to platform data
> > + * @pll5_table: Table containing a set of pll5 parameters
> > + * @num_pll5_entries: Number of entries in pll5 table
> >   */
> >  struct rzg2l_cpg_priv {
> >         struct reset_controller_dev rcdev; @@ -93,6 +95,9 @@ struct
> > rzg2l_cpg_priv {
> >
> >         struct raw_notifier_head notifiers;
> >         const struct rzg2l_cpg_info *info;
> > +
> > +       const struct rzg2l_pll5_param *pll5_table;
> > +       unsigned int num_pll5_entries;
> >  };
> >
> >  static void rzg2l_cpg_del_clk_provider(void *data) @@ -266,6 +271,203
> > @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core,
> >         return clk_hw->clk;
> >  }
> >
> > +struct rzg2l_pll5_param {
> > +       u64     frequency;
> 
> u32 should be sufficient.

OK.

> 
> > +       u64     pl5_refdiv;
> 
> u8? ;-)
> 
> (and move down to avoid gaps)
> 
> > +       u32     pl5_intin;
> 
> u8?
> 
> > +       u32     pl5_fracin;
> > +       u8      pl5_postdiv1;
> > +       u8      pl5_postdiv2;
> > +       u8      dsi_div_a;
> > +       u8      dsi_div_b;
> > +       u8      dsi_div;
> > +       u8      clksrc;
> > +};
> > +
> > +static const struct rzg2l_pll5_param dsi_mode_param[] = {
> > +       {   25175000, 1,  16, 13141593, 1, 1, 2, 3, 16, 0 }, /* VGA
> 25.175MHz */
> > +       {   25200000, 1,  16, 13421773, 1, 1, 2, 3, 16, 0 }, /* VGA
> 25.200MHz */
> > +       {   27000000, 1,  18,        0, 1, 1, 2, 3, 16, 0 }, /*
> 480p/576p 27.000MHz */
> > +       {   27027000, 1,  18,   301990, 1, 1, 2, 3, 16, 0 }, /* 480p
> 27.027MHz */
> > +       {   29605000, 1,  19, 12359216, 1, 1, 2, 3, 16, 0 }, /* WVGA
> 29.605MHz */
> > +       {   40000000, 2,  80,        0, 2, 1, 1, 2,  6, 0 }, /* SVGA
> 40.00MHz */
> > +       {   65000000, 1,  43,  5592405, 1, 1, 2, 3, 16, 0 }, /* XGA
> 65.00MHz */
> > +       {   71000000, 2,  71,        0, 1, 1, 1, 2,  6, 0 }, /* WXGA
> 1280x800 71.0MHz */
> > +       {   74176000, 1,  49,  7560932, 1, 1, 2, 3, 16, 0 }, /* 720p
> 74.176MHz */
> > +       {   74250000, 1,  49,  8388608, 1, 1, 2, 3, 16, 0 }, /* 720p
> 74.25MHz */
> > +       {   85500000, 2,  85,  8388608, 1, 1, 1, 2,  6, 0 }, /* FWXGA
> 1360x768 85.5MHz */
> > +       {   88750000, 2,  88, 12582912, 1, 1, 1, 2,  6, 1 }, /* WXGA+
> 1440x900 88.75MHz */
> > +       {  108000000, 2, 108,        0, 1, 1, 1, 2,  6, 1 }, /* SXGA
> 108MHz */
> > +       {  148500000, 2, 148,  8388608, 1, 1, 1, 2,  6, 1 }, /* 1080p
> 148.5MHz */
> > +       { 3000000000, 1, 125,        0, 1, 1, 0, 0,  0, 0 }, /* 3000 MHz
> */
> 
> 3000000000U, as this is larger than 1^31.
> Why do you need the 3 GHz entry? There are no such high video modes.
> 
> Do you need .dsi_div?
> .dsi_div = (1 << .dsi_div_a) * (.dsi_div_b + 1)

OK, Will change it.

> 
> Personally, I don't like tables, as calculations are more flexible.
> I'd expect the formula to be:

Agreed, Will use the generic equation and remove the LUT for next version.

> 
>     .frequency = extal / .pl5_refdiv *
>                  ((.pl5_intin << 24 + .pl5_fracin) >> 24) /
>                  (.pl5_postdiv1 * .pl5_postdiv * .dsi_div)
> 
> (with extal = 24 MHz)
> 
> However, that gives the wrong results for .pl5_refdiv = 2:
>   - For such entries in the above table, it calculates a frequency
>     that's twice the value in the table,
>   - For such entries in the below table, it calculates a frequency
>     that's half the value in the table.

> 
> Note that some entries (esp. the first one) from the table below give a
> clock rate closer to the expected one the corresponding entries from the
> table above.
> 
> > +};
> > +
> > +static const struct rzg2l_pll5_param dpi_mode_param[] = {
> > +       {   25175000, 1, 102, 13386820, 7, 7, 1, 0, 2, 0 }, /* VGA
> 25.175MHz */
> > +       {   25200000, 1,  73,  8388608, 7, 5, 1, 0, 2, 0 }, /* VGA
> 25.200MHz */
> > +       {   27000000, 1,  78, 12582912, 7, 5, 1, 0, 2, 0 }, /* 480p/576p
> 27.000MHz */
> > +       {   27027000, 1, 110,  6043992, 7, 7, 1, 0, 2, 0 }, /* 480p
> 27.027MHz */
> > +       {   29605000, 1,  88, 13673431, 6, 6, 1, 0, 2, 0 }, /* WVGA
> 29.605MHz */
> > +       {   40000000, 1,  70,        0, 7, 3, 1, 0, 2, 0 }, /* SVGA
> 40.00MHz */
> > +       {   65000000, 1,  81,  4194304, 5, 3, 1, 0, 2, 0 }, /* XGA
> 65.00MHz */
> > +       {   71000000, 2,  71,        0, 6, 2, 1, 0, 2, 0 }, /* WXGA
> 1280x800 71.0MHz */
> > +       {   74176000, 1,  74,  2952790, 6, 2, 1, 0, 2, 0 }, /* 720p
> 74.176MHz */
> > +       {   74250000, 1,  86, 10485760, 7, 2, 1, 0, 2, 0 }, /* 720p
> 74.25MHz */
> > +       {   85500000, 1,  83,  8388608, 6, 2, 1, 0, 2, 0 }, /* WXGA
> 1280x800 83.5MHz   */
> 
> 83500000
> 
> > +       { 3000000000, 1, 125,        0, 1, 1, 0, 0, 0, 0 }, /* 3000 MHz
> */
> > +};
> > +
> > +static int rzg2l_cpg_sipll5_get_index(unsigned long rate,
> > +                                     const struct rzg2l_pll5_param
> *pll5tab,
> > +                                     unsigned int n)
> 
> Perhaps just pass priv instead of pll5tab and n?
> 
> > +{
> > +       unsigned int  i;
> > +
> > +       for (i = 0; i < n; i++) {
> > +               if (pll5tab[i].frequency  == rate / pll5tab[i].dsi_div)
> > +                       break;
> > +       }
> > +
> > +       if (i == n)
> > +               i--;
> > +
> > +       return i;
> > +}
> > +
> > +struct sipll5 {
> > +       struct clk_hw hw;
> > +       unsigned int conf;
> > +       unsigned long rate;
> > +       struct rzg2l_cpg_priv *priv;
> > +};
> > +
> > +#define to_sipll5(_hw) container_of(_hw, struct sipll5, hw)
> > +
> > +static unsigned long rzg2l_cpg_sipll5_get_rate(unsigned long rate,
> > +                                              const struct
> rzg2l_pll5_param *pll5tab,
> > +                                              unsigned int n)
> 
> This function has only a single caller, so perhaps it's better to inline
> it manually?
> 
> > +{
> > +       int index = rzg2l_cpg_sipll5_get_index(rate, pll5tab, n);
> > +
> > +       return pll5tab[index].frequency * pll5tab[index].dsi_div; }
> > +
> > +static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw,
> > +                                                 unsigned long
> > +parent_rate) {
> > +       struct sipll5 *sipll5 = to_sipll5(hw);
> > +
> > +       return sipll5->rate;
> > +}
> > +
> > +static long rzg2l_cpg_sipll5_round_rate(struct clk_hw *hw,
> > +                                       unsigned long rate,
> > +                                       unsigned long *parent_rate) {
> > +       struct sipll5 *sipll5 = to_sipll5(hw);
> > +       struct rzg2l_cpg_priv *priv = sipll5->priv;
> > +       const struct rzg2l_pll5_param *pll5tab = priv->pll5_table;
> > +
> > +       sipll5->rate = rzg2l_cpg_sipll5_get_rate(rate, pll5tab, priv-
> >num_pll5_entries);
> > +       return sipll5->rate;
> > +}
> > +
> > +static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
> > +                                    unsigned long rate,
> > +                                    unsigned long parent_rate) {
> > +       struct sipll5 *sipll5 = to_sipll5(hw);
> > +       struct rzg2l_cpg_priv *priv = sipll5->priv;
> > +       const struct rzg2l_pll5_param *pll5tab = priv->pll5_table;
> > +       u8 id = rzg2l_cpg_sipll5_get_index(rate, pll5tab, priv-
> >num_pll5_entries);
> > +       int ret;
> > +       u32 val;
> > +
> > +       /* Put PLL5 into standby mode */
> > +       writel(0x00050000, priv->base + CPG_SIPLL5_STBY);
> 
> Please add defines for magic numbers

OK.

> 
> > +       ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
> > +                                !(val & CPG_SIPLL5_MON_PLL5_LOCK), 100,
> 250000);
> > +       if (ret) {
> > +               dev_err(priv->dev, "failed to release pll5 lock");
> > +               return ret;
> > +       }
> > +
> > +       /* Output clock setting 1 */
> > +       writel(0x01110000 |
> 
> Magic number
OK.
> 
> > +              (pll5tab[id].pl5_postdiv1 << 0) |
> > +              (pll5tab[id].pl5_postdiv2 << 4) |
> > +              (pll5tab[id].pl5_refdiv << 8),
> > +              priv->base + CPG_SIPLL5_CLK1);
> > +       /* Output clock setting, SSCG modulation value setting 3 */
> > +       writel((0 << 0) | (pll5tab[id].pl5_fracin << 8), priv->base +
> CPG_SIPLL5_CLK3);
> > +       /* Output clock setting 4 */
> > +       writel(0x000000ff | (pll5tab[id].pl5_intin << 16), priv->base
> > + + CPG_SIPLL5_CLK4);
> 
> magic number (and more below)

OK.

> 
> > +
> > +       /* SSCG modulation value setting 5 */
> > +       writel((0x16 << 0), priv->base + CPG_SIPLL5_CLK5);
> 
> > +       /* PLL normal mode setting */
> > +       writel(0x00050001, priv->base + CPG_SIPLL5_STBY);
> > +
> > +       /* PLL normal mode transition, output clock stability check */
> > +       ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
> > +                                (val & CPG_SIPLL5_MON_PLL5_LOCK), 100,
> 250000);
> > +       if (ret) {
> > +               dev_err(priv->dev, "failed to lock pll5");
> > +               return ret;
> > +       }
> > +
> > +       return 0;
> > +}
> 
> > @@ -918,6 +1123,18 @@ static int __init rzg2l_cpg_probe(struct
> platform_device *pdev)
> >         priv->num_resets = info->num_resets;
> >         priv->last_dt_core_clk = info->last_dt_core_clk;
> >
> > +       priv->pll5_table = dpi_mode_param;
> > +       priv->num_pll5_entries = ARRAY_SIZE(dpi_mode_param);
> > +       /* Fix me: Selection of the table needs to be overridden by
> either
> > +        * 1) a property in DTS or
> > +        * 2) Detecting DSI/DPI mode from dts or
> > +        * 3) DSI/DPI mode runtime detection
> > +        */
> > +       if (info->pll5_lcdc_dsi_mode) {
> > +               priv->pll5_table = dsi_mode_param;
> > +               priv->num_pll5_entries = ARRAY_SIZE(dsi_mode_param);
> > +       }
> > +
> 
> I'd expect that to be detected at runtime, or derived from what clock
> rate(s) the display driver asks for?

OK. Will Add DSI support for now and when we add RZ/G2UL support
At that time will add runtime check for detecting DSI and DPI.

Cheers,
Biju
diff mbox series

Patch

diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c
index edd0abe34a37..c6f609c9fa6d 100644
--- a/drivers/clk/renesas/rzg2l-cpg.c
+++ b/drivers/clk/renesas/rzg2l-cpg.c
@@ -78,6 +78,8 @@  struct sd_hw_data {
  * @last_dt_core_clk: ID of the last Core Clock exported to DT
  * @notifiers: Notifier chain to save/restore clock state for system resume
  * @info: Pointer to platform data
+ * @pll5_table: Table containing a set of pll5 parameters
+ * @num_pll5_entries: Number of entries in pll5 table
  */
 struct rzg2l_cpg_priv {
 	struct reset_controller_dev rcdev;
@@ -93,6 +95,9 @@  struct rzg2l_cpg_priv {
 
 	struct raw_notifier_head notifiers;
 	const struct rzg2l_cpg_info *info;
+
+	const struct rzg2l_pll5_param *pll5_table;
+	unsigned int num_pll5_entries;
 };
 
 static void rzg2l_cpg_del_clk_provider(void *data)
@@ -266,6 +271,203 @@  rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core,
 	return clk_hw->clk;
 }
 
+struct rzg2l_pll5_param {
+	u64	frequency;
+	u64	pl5_refdiv;
+	u32	pl5_intin;
+	u32	pl5_fracin;
+	u8	pl5_postdiv1;
+	u8	pl5_postdiv2;
+	u8	dsi_div_a;
+	u8	dsi_div_b;
+	u8	dsi_div;
+	u8	clksrc;
+};
+
+static const struct rzg2l_pll5_param dsi_mode_param[] = {
+	{   25175000, 1,  16, 13141593, 1, 1, 2, 3, 16, 0 }, /* VGA 25.175MHz */
+	{   25200000, 1,  16, 13421773, 1, 1, 2, 3, 16, 0 }, /* VGA 25.200MHz */
+	{   27000000, 1,  18,        0, 1, 1, 2, 3, 16, 0 }, /* 480p/576p 27.000MHz */
+	{   27027000, 1,  18,   301990, 1, 1, 2, 3, 16, 0 }, /* 480p 27.027MHz */
+	{   29605000, 1,  19, 12359216, 1, 1, 2, 3, 16, 0 }, /* WVGA 29.605MHz */
+	{   40000000, 2,  80,        0, 2, 1, 1, 2,  6, 0 }, /* SVGA 40.00MHz */
+	{   65000000, 1,  43,  5592405, 1, 1, 2, 3, 16, 0 }, /* XGA 65.00MHz */
+	{   71000000, 2,  71,        0, 1, 1, 1, 2,  6, 0 }, /* WXGA 1280x800 71.0MHz */
+	{   74176000, 1,  49,  7560932, 1, 1, 2, 3, 16, 0 }, /* 720p 74.176MHz */
+	{   74250000, 1,  49,  8388608, 1, 1, 2, 3, 16, 0 }, /* 720p 74.25MHz */
+	{   85500000, 2,  85,  8388608, 1, 1, 1, 2,  6, 0 }, /* FWXGA 1360x768 85.5MHz */
+	{   88750000, 2,  88, 12582912, 1, 1, 1, 2,  6, 1 }, /* WXGA+ 1440x900 88.75MHz */
+	{  108000000, 2, 108,        0, 1, 1, 1, 2,  6, 1 }, /* SXGA 108MHz */
+	{  148500000, 2, 148,  8388608, 1, 1, 1, 2,  6, 1 }, /* 1080p 148.5MHz */
+	{ 3000000000, 1, 125,        0, 1, 1, 0, 0,  0, 0 }, /* 3000 MHz */
+};
+
+static const struct rzg2l_pll5_param dpi_mode_param[] = {
+	{   25175000, 1, 102, 13386820, 7, 7, 1, 0, 2, 0 }, /* VGA 25.175MHz */
+	{   25200000, 1,  73,  8388608, 7, 5, 1, 0, 2, 0 }, /* VGA 25.200MHz */
+	{   27000000, 1,  78, 12582912, 7, 5, 1, 0, 2, 0 }, /* 480p/576p 27.000MHz */
+	{   27027000, 1, 110,  6043992, 7, 7, 1, 0, 2, 0 }, /* 480p 27.027MHz */
+	{   29605000, 1,  88, 13673431, 6, 6, 1, 0, 2, 0 }, /* WVGA 29.605MHz */
+	{   40000000, 1,  70,        0, 7, 3, 1, 0, 2, 0 }, /* SVGA 40.00MHz */
+	{   65000000, 1,  81,  4194304, 5, 3, 1, 0, 2, 0 }, /* XGA 65.00MHz */
+	{   71000000, 2,  71,        0, 6, 2, 1, 0, 2, 0 }, /* WXGA 1280x800 71.0MHz */
+	{   74176000, 1,  74,  2952790, 6, 2, 1, 0, 2, 0 }, /* 720p 74.176MHz */
+	{   74250000, 1,  86, 10485760, 7, 2, 1, 0, 2, 0 }, /* 720p 74.25MHz */
+	{   85500000, 1,  83,  8388608, 6, 2, 1, 0, 2, 0 }, /* WXGA 1280x800 83.5MHz   */
+	{ 3000000000, 1, 125,        0, 1, 1, 0, 0, 0, 0 }, /* 3000 MHz */
+};
+
+static int rzg2l_cpg_sipll5_get_index(unsigned long rate,
+				      const struct rzg2l_pll5_param *pll5tab,
+				      unsigned int n)
+{
+	unsigned int  i;
+
+	for (i = 0; i < n; i++) {
+		if (pll5tab[i].frequency  == rate / pll5tab[i].dsi_div)
+			break;
+	}
+
+	if (i == n)
+		i--;
+
+	return i;
+}
+
+struct sipll5 {
+	struct clk_hw hw;
+	unsigned int conf;
+	unsigned long rate;
+	struct rzg2l_cpg_priv *priv;
+};
+
+#define to_sipll5(_hw)	container_of(_hw, struct sipll5, hw)
+
+static unsigned long rzg2l_cpg_sipll5_get_rate(unsigned long rate,
+					       const struct rzg2l_pll5_param *pll5tab,
+					       unsigned int n)
+{
+	int index = rzg2l_cpg_sipll5_get_index(rate, pll5tab, n);
+
+	return pll5tab[index].frequency * pll5tab[index].dsi_div;
+}
+
+static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw,
+						  unsigned long parent_rate)
+{
+	struct sipll5 *sipll5 = to_sipll5(hw);
+
+	return sipll5->rate;
+}
+
+static long rzg2l_cpg_sipll5_round_rate(struct clk_hw *hw,
+					unsigned long rate,
+					unsigned long *parent_rate)
+{
+	struct sipll5 *sipll5 = to_sipll5(hw);
+	struct rzg2l_cpg_priv *priv = sipll5->priv;
+	const struct rzg2l_pll5_param *pll5tab = priv->pll5_table;
+
+	sipll5->rate = rzg2l_cpg_sipll5_get_rate(rate, pll5tab, priv->num_pll5_entries);
+	return sipll5->rate;
+}
+
+static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
+				     unsigned long rate,
+				     unsigned long parent_rate)
+{
+	struct sipll5 *sipll5 = to_sipll5(hw);
+	struct rzg2l_cpg_priv *priv = sipll5->priv;
+	const struct rzg2l_pll5_param *pll5tab = priv->pll5_table;
+	u8 id = rzg2l_cpg_sipll5_get_index(rate, pll5tab, priv->num_pll5_entries);
+	int ret;
+	u32 val;
+
+	/* Put PLL5 into standby mode */
+	writel(0x00050000, priv->base + CPG_SIPLL5_STBY);
+	ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
+				 !(val & CPG_SIPLL5_MON_PLL5_LOCK), 100, 250000);
+	if (ret) {
+		dev_err(priv->dev, "failed to release pll5 lock");
+		return ret;
+	}
+
+	/* Output clock setting 1 */
+	writel(0x01110000 |
+	       (pll5tab[id].pl5_postdiv1 << 0) |
+	       (pll5tab[id].pl5_postdiv2 << 4) |
+	       (pll5tab[id].pl5_refdiv << 8),
+	       priv->base + CPG_SIPLL5_CLK1);
+	/* Output clock setting, SSCG modulation value setting 3 */
+	writel((0 << 0) | (pll5tab[id].pl5_fracin << 8), priv->base + CPG_SIPLL5_CLK3);
+	/* Output clock setting 4 */
+	writel(0x000000ff | (pll5tab[id].pl5_intin << 16), priv->base + CPG_SIPLL5_CLK4);
+
+	/* SSCG modulation value setting 5 */
+	writel((0x16 << 0), priv->base + CPG_SIPLL5_CLK5);
+	/* PLL normal mode setting */
+	writel(0x00050001, priv->base + CPG_SIPLL5_STBY);
+
+	/* PLL normal mode transition, output clock stability check */
+	ret = readl_poll_timeout(priv->base + CPG_SIPLL5_MON, val,
+				 (val & CPG_SIPLL5_MON_PLL5_LOCK), 100, 250000);
+	if (ret) {
+		dev_err(priv->dev, "failed to lock pll5");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct clk_ops rzg2l_cpg_sipll5_ops = {
+	.recalc_rate = rzg2l_cpg_sipll5_recalc_rate,
+	.round_rate = rzg2l_cpg_sipll5_round_rate,
+	.set_rate = rzg2l_cpg_sipll5_set_rate,
+};
+
+static struct clk * __init
+rzg2l_cpg_sipll5_register(const struct cpg_core_clk *core,
+			  struct clk **clks,
+			  struct rzg2l_cpg_priv *priv)
+{
+	const struct clk *parent;
+	struct clk_init_data init;
+	const char *parent_name;
+	struct sipll5 *sipll5;
+	struct clk_hw *clk_hw;
+	int ret;
+
+	parent = clks[core->parent & 0xffff];
+	if (IS_ERR(parent))
+		return ERR_CAST(parent);
+
+	sipll5 = devm_kzalloc(priv->dev, sizeof(*sipll5), GFP_KERNEL);
+	if (!sipll5)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = core->name;
+	parent_name = __clk_get_name(parent);
+	init.ops = &rzg2l_cpg_sipll5_ops;
+	init.flags = 0;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	sipll5->hw.init = &init;
+	sipll5->conf = core->conf;
+	sipll5->priv = priv;
+
+	writel(0x00050001, priv->base + CPG_SIPLL5_STBY);
+
+	clk_hw = &sipll5->hw;
+	clk_hw->init = &init;
+
+	ret = devm_clk_hw_register(priv->dev, clk_hw);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return clk_hw->clk;
+}
+
 struct pll_clk {
 	struct clk_hw hw;
 	unsigned int conf;
@@ -420,6 +622,9 @@  rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core,
 		clk = rzg2l_cpg_pll_clk_register(core, priv->clks,
 						 priv->base, priv);
 		break;
+	case CLK_TYPE_SIPLL5:
+		clk = rzg2l_cpg_sipll5_register(core, priv->clks, priv);
+		break;
 	case CLK_TYPE_DIV:
 		clk = rzg2l_cpg_div_clk_register(core, priv->clks,
 						 priv->base, priv);
@@ -918,6 +1123,18 @@  static int __init rzg2l_cpg_probe(struct platform_device *pdev)
 	priv->num_resets = info->num_resets;
 	priv->last_dt_core_clk = info->last_dt_core_clk;
 
+	priv->pll5_table = dpi_mode_param;
+	priv->num_pll5_entries = ARRAY_SIZE(dpi_mode_param);
+	/* Fix me: Selection of the table needs to be overridden by either
+	 * 1) a property in DTS or
+	 * 2) Detecting DSI/DPI mode from dts or
+	 * 3) DSI/DPI mode runtime detection
+	 */
+	if (info->pll5_lcdc_dsi_mode) {
+		priv->pll5_table = dsi_mode_param;
+		priv->num_pll5_entries = ARRAY_SIZE(dsi_mode_param);
+	}
+
 	for (i = 0; i < nclks; i++)
 		clks[i] = ERR_PTR(-ENOENT);
 
diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h
index 5729d102034b..75b3a3bbbb60 100644
--- a/drivers/clk/renesas/rzg2l-cpg.h
+++ b/drivers/clk/renesas/rzg2l-cpg.h
@@ -9,6 +9,12 @@ 
 #ifndef __RENESAS_RZG2L_CPG_H__
 #define __RENESAS_RZG2L_CPG_H__
 
+#define CPG_SIPLL5_STBY		(0x140)
+#define CPG_SIPLL5_CLK1		(0x144)
+#define CPG_SIPLL5_CLK3		(0x14C)
+#define CPG_SIPLL5_CLK4		(0x150)
+#define CPG_SIPLL5_CLK5		(0x154)
+#define CPG_SIPLL5_MON		(0x15C)
 #define CPG_PL1_DDIV		(0x200)
 #define CPG_PL2_DDIV		(0x204)
 #define CPG_PL3A_DDIV		(0x208)
@@ -19,6 +25,8 @@ 
 #define CPG_PL6_SSEL		(0x414)
 #define CPG_PL6_ETH_SSEL	(0x418)
 
+#define CPG_SIPLL5_MON_PLL5_LOCK	BIT(4)
+
 #define CPG_CLKSTATUS_SELSDHI0_STS	BIT(28)
 #define CPG_CLKSTATUS_SELSDHI1_STS	BIT(29)
 
@@ -86,6 +94,9 @@  enum clk_types {
 
 	/* Clock with SD clock source selector */
 	CLK_TYPE_SD_MUX,
+
+	/* Clock for SIPLL5 */
+	CLK_TYPE_SIPLL5,
 };
 
 #define DEF_TYPE(_name, _id, _type...) \
@@ -109,6 +120,8 @@  enum clk_types {
 #define DEF_SD_MUX(_name, _id, _conf, _parent_names, _num_parents) \
 	DEF_TYPE(_name, _id, CLK_TYPE_SD_MUX, .conf = _conf, \
 		 .parent_names = _parent_names, .num_parents = _num_parents)
+#define DEF_PLL5_FOUTPOSTDIV(_name, _id, _parent) \
+	DEF_TYPE(_name, _id, CLK_TYPE_SIPLL5, .parent = _parent)
 
 /**
  * struct rzg2l_mod_clk - Module Clocks definitions
@@ -200,6 +213,8 @@  struct rzg2l_cpg_info {
 	/* Critical Module Clocks that should not be disabled */
 	const unsigned int *crit_mod_clks;
 	unsigned int num_crit_mod_clks;
+
+	bool pll5_lcdc_dsi_mode;
 };
 
 extern const struct rzg2l_cpg_info r9a07g044_cpg_info;