diff mbox series

[v2,11/15] hw/misc/bcm2835_cprman: implement clock mux behaviour

Message ID 20201005195612.1999165-12-luc@lmichel.fr
State Superseded
Headers show
Series raspi: add the bcm2835 cprman clock manager | expand

Commit Message

Luc Michel Oct. 5, 2020, 7:56 p.m. UTC
A clock mux can be configured to select one of its 10 sources through
the CM_CTL register. It also embeds yet another clock divider, composed
of an integer part and a fractional part. The number of bits of each
part is mux dependent.

Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 hw/misc/bcm2835_cprman.c | 44 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

Comments

Philippe Mathieu-Daudé Oct. 6, 2020, 9:04 a.m. UTC | #1
On 10/5/20 9:56 PM, Luc Michel wrote:
> A clock mux can be configured to select one of its 10 sources through

> the CM_CTL register. It also embeds yet another clock divider, composed

> of an integer part and a fractional part. The number of bits of each

> part is mux dependent.

> 

> Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> Signed-off-by: Luc Michel <luc@lmichel.fr>

> ---

>  hw/misc/bcm2835_cprman.c | 44 +++++++++++++++++++++++++++++++++++++++-

>  1 file changed, 43 insertions(+), 1 deletion(-)

> 

> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c

> index a470ce2026..7d59423367 100644

> --- a/hw/misc/bcm2835_cprman.c

> +++ b/hw/misc/bcm2835_cprman.c

> @@ -229,19 +229,61 @@ static const TypeInfo cprman_pll_channel_info = {

>  };

>  

>  

>  /* clock mux */

>  

> +static bool clock_mux_is_enabled(CprmanClockMuxState *mux)

> +{

> +    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);

> +}

> +

>  static void clock_mux_update(CprmanClockMuxState *mux)

>  {

> -    clock_update(mux->out, 0);

> +    uint64_t freq;

> +    uint32_t div, src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);

> +    bool enabled = clock_mux_is_enabled(mux);

> +

> +    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);

> +

> +    if (!enabled) {

> +        clock_update(mux->out, 0);

> +        return;

> +    }

> +

> +    freq = clock_get_hz(mux->srcs[src]);

> +

> +    if (mux->int_bits == 0 && mux->frac_bits == 0) {

> +        clock_update_hz(mux->out, freq);

> +        return;

> +    }

> +

> +    /*

> +     * The divider has an integer and a fractional part. The size of each part

> +     * varies with the muxes (int_bits and frac_bits). Both parts are

> +     * concatenated, with the integer part always starting at bit 12.

> +     */

> +    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);

> +    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;


I understand the description as:

                   0
                  [     12-bit    ][     12-bit    ][   reserved...   ]
 CM_CLOCKx_DIV    [      FRAC     ][      INT      ][                 ]
                  [         <frac>][<int>          ][                 ]
                            ^^^^^^^^^^^^^

What about:

       div = extract32(mux->reg_cm[1],
                       R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits,
                       mux->frac_bits, mux->int_bits);

Also consider adding the register visual representation if it is
correct.

> +

> +    if (!div) {

> +        clock_update(mux->out, 0);

> +    }

> +

> +    freq = muldiv64(freq, 1 << mux->frac_bits, div);

> +

> +    clock_update_hz(mux->out, freq);

>  }

>  

>  static void clock_mux_src_update(void *opaque)

>  {

>      CprmanClockMuxState **backref = opaque;

>      CprmanClockMuxState *s = *backref;

> +    CprmanClockMuxSource src = backref - s->backref;

> +

> +    if (FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC) != src) {

> +        return;

> +    }

>  

>      clock_mux_update(s);

>  }

>  

>  static void clock_mux_init(Object *obj)

>
Luc Michel Oct. 10, 2020, 1:09 p.m. UTC | #2
On 11:04 Tue 06 Oct     , Philippe Mathieu-Daudé wrote:
> On 10/5/20 9:56 PM, Luc Michel wrote:

> > A clock mux can be configured to select one of its 10 sources through

> > the CM_CTL register. It also embeds yet another clock divider, composed

> > of an integer part and a fractional part. The number of bits of each

> > part is mux dependent.

> > 

> > Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> > Signed-off-by: Luc Michel <luc@lmichel.fr>

> > ---

> >  hw/misc/bcm2835_cprman.c | 44 +++++++++++++++++++++++++++++++++++++++-

> >  1 file changed, 43 insertions(+), 1 deletion(-)

> > 

> > diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c

> > index a470ce2026..7d59423367 100644

> > --- a/hw/misc/bcm2835_cprman.c

> > +++ b/hw/misc/bcm2835_cprman.c

> > @@ -229,19 +229,61 @@ static const TypeInfo cprman_pll_channel_info = {

> >  };

> >  

> >  

> >  /* clock mux */

> >  

> > +static bool clock_mux_is_enabled(CprmanClockMuxState *mux)

> > +{

> > +    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);

> > +}

> > +

> >  static void clock_mux_update(CprmanClockMuxState *mux)

> >  {

> > -    clock_update(mux->out, 0);

> > +    uint64_t freq;

> > +    uint32_t div, src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);

> > +    bool enabled = clock_mux_is_enabled(mux);

> > +

> > +    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);

> > +

> > +    if (!enabled) {

> > +        clock_update(mux->out, 0);

> > +        return;

> > +    }

> > +

> > +    freq = clock_get_hz(mux->srcs[src]);

> > +

> > +    if (mux->int_bits == 0 && mux->frac_bits == 0) {

> > +        clock_update_hz(mux->out, freq);

> > +        return;

> > +    }

> > +

> > +    /*

> > +     * The divider has an integer and a fractional part. The size of each part

> > +     * varies with the muxes (int_bits and frac_bits). Both parts are

> > +     * concatenated, with the integer part always starting at bit 12.

> > +     */

> > +    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);

> > +    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;

> 

> I understand the description as:

> 

>                    0

>                   [     12-bit    ][     12-bit    ][   reserved...   ]

>  CM_CLOCKx_DIV    [      FRAC     ][      INT      ][                 ]

>                   [         <frac>][<int>          ][                 ]

>                             ^^^^^^^^^^^^^

Yes, this is correct :)

> 

> What about:

> 

>        div = extract32(mux->reg_cm[1],

>                        R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits,

>                        mux->frac_bits, mux->int_bits);

Yes good idea, with s/,/+/ here -------^

> 

> Also consider adding the register visual representation if it is

> correct.

Sure, I'll add a visual representation for more clarity.

Thanks!

-- 
Luc

> 

> > +

> > +    if (!div) {

> > +        clock_update(mux->out, 0);

> > +    }

> > +

> > +    freq = muldiv64(freq, 1 << mux->frac_bits, div);

> > +

> > +    clock_update_hz(mux->out, freq);

> >  }

> >  

> >  static void clock_mux_src_update(void *opaque)

> >  {

> >      CprmanClockMuxState **backref = opaque;

> >      CprmanClockMuxState *s = *backref;

> > +    CprmanClockMuxSource src = backref - s->backref;

> > +

> > +    if (FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC) != src) {

> > +        return;

> > +    }

> >  

> >      clock_mux_update(s);

> >  }

> >  

> >  static void clock_mux_init(Object *obj)

> > 


--
diff mbox series

Patch

diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index a470ce2026..7d59423367 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -229,19 +229,61 @@  static const TypeInfo cprman_pll_channel_info = {
 };
 
 
 /* clock mux */
 
+static bool clock_mux_is_enabled(CprmanClockMuxState *mux)
+{
+    return FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, ENABLE);
+}
+
 static void clock_mux_update(CprmanClockMuxState *mux)
 {
-    clock_update(mux->out, 0);
+    uint64_t freq;
+    uint32_t div, src = FIELD_EX32(*mux->reg_cm, CM_CLOCKx_CTL, SRC);
+    bool enabled = clock_mux_is_enabled(mux);
+
+    *mux->reg_cm = FIELD_DP32(*mux->reg_cm, CM_CLOCKx_CTL, BUSY, enabled);
+
+    if (!enabled) {
+        clock_update(mux->out, 0);
+        return;
+    }
+
+    freq = clock_get_hz(mux->srcs[src]);
+
+    if (mux->int_bits == 0 && mux->frac_bits == 0) {
+        clock_update_hz(mux->out, freq);
+        return;
+    }
+
+    /*
+     * The divider has an integer and a fractional part. The size of each part
+     * varies with the muxes (int_bits and frac_bits). Both parts are
+     * concatenated, with the integer part always starting at bit 12.
+     */
+    div = mux->reg_cm[1] >> (R_CM_CLOCKx_DIV_FRAC_LENGTH - mux->frac_bits);
+    div &= (1 << (mux->int_bits + mux->frac_bits)) - 1;
+
+    if (!div) {
+        clock_update(mux->out, 0);
+    }
+
+    freq = muldiv64(freq, 1 << mux->frac_bits, div);
+
+    clock_update_hz(mux->out, freq);
 }
 
 static void clock_mux_src_update(void *opaque)
 {
     CprmanClockMuxState **backref = opaque;
     CprmanClockMuxState *s = *backref;
+    CprmanClockMuxSource src = backref - s->backref;
+
+    if (FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC) != src) {
+        return;
+    }
 
     clock_mux_update(s);
 }
 
 static void clock_mux_init(Object *obj)