diff mbox series

[10/14] hw/misc/bcm2835_cprman: implement clock mux behaviour

Message ID 20200925101731.2159827-11-luc@lmichel.fr
State New
Headers show
Series raspi: add the bcm2835 cprman clock manager | expand

Commit Message

Luc Michel Sept. 25, 2020, 10:17 a.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 fractionnal part. The number of bits of each
part is mux dependant.

Signed-off-by: Luc Michel <luc@lmichel.fr>
---
 hw/misc/bcm2835_cprman.c | 43 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)

Comments

Philippe Mathieu-Daudé Sept. 26, 2020, 9:40 p.m. UTC | #1
On 9/25/20 12:17 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 fractionnal part. The number of bits of each


Typo "fractional".

> part is mux dependant.


Typo "dependent"?

> 

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

> ---

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

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

> 

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

> index 8df2db0fd9..75bc11939b 100644

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

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

> @@ -229,19 +229,60 @@ 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, div;

> +    uint32_t 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;

> +

> +    freq = (freq << 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;

> +    uint32_t current_src;

> +

> +    current_src = FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC);

> +

> +    if (current_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 8df2db0fd9..75bc11939b 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -229,19 +229,60 @@  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, div;
+    uint32_t 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;
+
+    freq = (freq << 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;
+    uint32_t current_src;
+
+    current_src = FIELD_EX32(*s->reg_cm, CM_CLOCKx_CTL, SRC);
+
+    if (current_src != src) {
+        return;
+    }
 
     clock_mux_update(s);
 }
 
 static void clock_mux_init(Object *obj)