Re: [PATCH v4 3/8] Add an internal PLL Clock object

2024-02-12 Thread Arnaud Minier
Hello Alistair,

Yes, I think we should bail out if pll_set_vco_multiplier receives an invalid 
value to respect the hardware defined bounds.
I actually intended to add a return there but I missed it. It will be added in 
the next version.

Thanks,
Arnaud Minier


- Mail original -
De: "Alistair Francis" 
À: "Arnaud Minier" 
Cc: "qemu-devel" , "Laurent Vivier" 
, "Alistair Francis" , "Inès 
Varhol" , "Peter Maydell" 
, "Paolo Bonzini" , "Thomas 
Huth" , qemu-...@nongnu.org
Envoyé: Jeudi 1 Février 2024 01:18:22
Objet: Re: [PATCH v4 3/8] Add an internal PLL Clock object

On Wed, Jan 31, 2024 at 2:09 AM Arnaud Minier
 wrote:
>
> This object represents the PLLs and their channels. The PLLs allow for a
> more fine-grained control of the clocks frequency.
>
> Wasn't sure about how to handle the reset and the migration so used the
> same appproach as the BCM2835 CPRMAN.
>
> Signed-off-by: Arnaud Minier 
> Signed-off-by: Inès Varhol 
> ---
>  hw/misc/stm32l4x5_rcc.c   | 175 ++
>  hw/misc/trace-events  |   5 +
>  include/hw/misc/stm32l4x5_rcc.h   |  40 +
>  include/hw/misc/stm32l4x5_rcc_internals.h |  22 +++
>  4 files changed, 242 insertions(+)
>
> diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
> index ed10832f88..fb0233c3e9 100644
> --- a/hw/misc/stm32l4x5_rcc.c
> +++ b/hw/misc/stm32l4x5_rcc.c
> @@ -162,6 +162,156 @@ static void clock_mux_set_source(RccClockMuxState *mux, 
> RccClockMuxSource src)
>  clock_mux_update(mux);
>  }
>
> +static void pll_update(RccPllState *pll)
> +{
> +uint64_t vco_freq, old_channel_freq, channel_freq;
> +int i;
> +
> +/* The common PLLM factor is handled by the PLL mux */
> +vco_freq = muldiv64(clock_get_hz(pll->in), pll->vco_multiplier, 1);
> +
> +for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
> +if (!pll->channel_exists[i]) {
> +continue;
> +}
> +
> +old_channel_freq = clock_get_hz(pll->channels[i]);
> +if (!pll->enabled ||
> +!pll->channel_enabled[i] ||
> +!pll->channel_divider[i]) {
> +channel_freq = 0;
> +} else {
> +channel_freq = muldiv64(vco_freq,
> +1,
> +pll->channel_divider[i]);
> +}
> +
> +/* No change, early continue to avoid log spam and useless 
> propagation */
> +if (old_channel_freq == channel_freq) {
> +continue;
> +}
> +
> +clock_update_hz(pll->channels[i], channel_freq);
> +trace_stm32l4x5_rcc_pll_update(pll->id, i, vco_freq,
> +old_channel_freq, channel_freq);
> +}
> +}
> +
> +static void pll_src_update(void *opaque, ClockEvent event)
> +{
> +RccPllState *s = opaque;
> +pll_update(s);
> +}
> +
> +static void pll_init(Object *obj)
> +{
> +RccPllState *s = RCC_PLL(obj);
> +size_t i;
> +
> +s->in = qdev_init_clock_in(DEVICE(s), "in",
> +   pll_src_update, s, ClockUpdate);
> +
> +const char *names[] = {
> +"out-p", "out-q", "out-r",
> +};
> +
> +for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
> +s->channels[i] = qdev_init_clock_out(DEVICE(s), names[i]);
> +}
> +}
> +
> +static void pll_reset_hold(Object *obj)
> +{ }
> +
> +static const VMStateDescription pll_vmstate = {
> +.name = TYPE_RCC_PLL,
> +.version_id = 1,
> +.minimum_version_id = 1,
> +.fields = (VMStateField[]) {
> +VMSTATE_UINT32(id, RccPllState),
> +VMSTATE_CLOCK(in, RccPllState),
> +VMSTATE_ARRAY_CLOCK(channels, RccPllState,
> +RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_BOOL(enabled, RccPllState),
> +VMSTATE_UINT32(vco_multiplier, RccPllState),
> +VMSTATE_BOOL_ARRAY(channel_enabled, RccPllState, 
> RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_BOOL_ARRAY(channel_exists, RccPllState, 
> RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_UINT32_ARRAY(channel_divider, RccPllState, 
> RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_END_OF_LIST()
> +}
> +};
> +
> +static void pll_class_init(ObjectClass *klass, void *data)
> +{
> +DeviceClass *dc = DEVICE_CLASS(klass);
> +ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> +rc->phases.hold = pll_reset_hold;
> +dc->vmsd = _vmstate;
> +}
> +
> +static void pll_set_vco_multiplier(RccPllState *pll, uint32_

Re: [PATCH v4 3/8] Add an internal PLL Clock object

2024-01-31 Thread Alistair Francis
On Wed, Jan 31, 2024 at 2:09 AM Arnaud Minier
 wrote:
>
> This object represents the PLLs and their channels. The PLLs allow for a
> more fine-grained control of the clocks frequency.
>
> Wasn't sure about how to handle the reset and the migration so used the
> same appproach as the BCM2835 CPRMAN.
>
> Signed-off-by: Arnaud Minier 
> Signed-off-by: Inès Varhol 
> ---
>  hw/misc/stm32l4x5_rcc.c   | 175 ++
>  hw/misc/trace-events  |   5 +
>  include/hw/misc/stm32l4x5_rcc.h   |  40 +
>  include/hw/misc/stm32l4x5_rcc_internals.h |  22 +++
>  4 files changed, 242 insertions(+)
>
> diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
> index ed10832f88..fb0233c3e9 100644
> --- a/hw/misc/stm32l4x5_rcc.c
> +++ b/hw/misc/stm32l4x5_rcc.c
> @@ -162,6 +162,156 @@ static void clock_mux_set_source(RccClockMuxState *mux, 
> RccClockMuxSource src)
>  clock_mux_update(mux);
>  }
>
> +static void pll_update(RccPllState *pll)
> +{
> +uint64_t vco_freq, old_channel_freq, channel_freq;
> +int i;
> +
> +/* The common PLLM factor is handled by the PLL mux */
> +vco_freq = muldiv64(clock_get_hz(pll->in), pll->vco_multiplier, 1);
> +
> +for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
> +if (!pll->channel_exists[i]) {
> +continue;
> +}
> +
> +old_channel_freq = clock_get_hz(pll->channels[i]);
> +if (!pll->enabled ||
> +!pll->channel_enabled[i] ||
> +!pll->channel_divider[i]) {
> +channel_freq = 0;
> +} else {
> +channel_freq = muldiv64(vco_freq,
> +1,
> +pll->channel_divider[i]);
> +}
> +
> +/* No change, early continue to avoid log spam and useless 
> propagation */
> +if (old_channel_freq == channel_freq) {
> +continue;
> +}
> +
> +clock_update_hz(pll->channels[i], channel_freq);
> +trace_stm32l4x5_rcc_pll_update(pll->id, i, vco_freq,
> +old_channel_freq, channel_freq);
> +}
> +}
> +
> +static void pll_src_update(void *opaque, ClockEvent event)
> +{
> +RccPllState *s = opaque;
> +pll_update(s);
> +}
> +
> +static void pll_init(Object *obj)
> +{
> +RccPllState *s = RCC_PLL(obj);
> +size_t i;
> +
> +s->in = qdev_init_clock_in(DEVICE(s), "in",
> +   pll_src_update, s, ClockUpdate);
> +
> +const char *names[] = {
> +"out-p", "out-q", "out-r",
> +};
> +
> +for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
> +s->channels[i] = qdev_init_clock_out(DEVICE(s), names[i]);
> +}
> +}
> +
> +static void pll_reset_hold(Object *obj)
> +{ }
> +
> +static const VMStateDescription pll_vmstate = {
> +.name = TYPE_RCC_PLL,
> +.version_id = 1,
> +.minimum_version_id = 1,
> +.fields = (VMStateField[]) {
> +VMSTATE_UINT32(id, RccPllState),
> +VMSTATE_CLOCK(in, RccPllState),
> +VMSTATE_ARRAY_CLOCK(channels, RccPllState,
> +RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_BOOL(enabled, RccPllState),
> +VMSTATE_UINT32(vco_multiplier, RccPllState),
> +VMSTATE_BOOL_ARRAY(channel_enabled, RccPllState, 
> RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_BOOL_ARRAY(channel_exists, RccPllState, 
> RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_UINT32_ARRAY(channel_divider, RccPllState, 
> RCC_NUM_CHANNEL_PLL_OUT),
> +VMSTATE_END_OF_LIST()
> +}
> +};
> +
> +static void pll_class_init(ObjectClass *klass, void *data)
> +{
> +DeviceClass *dc = DEVICE_CLASS(klass);
> +ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> +rc->phases.hold = pll_reset_hold;
> +dc->vmsd = _vmstate;
> +}
> +
> +static void pll_set_vco_multiplier(RccPllState *pll, uint32_t vco_multiplier)
> +{
> +if (pll->vco_multiplier == vco_multiplier) {
> +return;
> +}
> +
> +if (vco_multiplier < 8 || vco_multiplier > 86) {
> +qemu_log_mask(LOG_GUEST_ERROR,
> +"%s: VCO multiplier is out of bound (%u) for PLL %u\n",
> +__func__, vco_multiplier, pll->id);

Should we bail out with an invalid value?

Alistair

> +}
> +
> +trace_stm32l4x5_rcc_pll_set_vco_multiplier(pll->id,
> +pll->vco_multiplier, vco_multiplier);
> +
> +pll->vco_multiplier = vco_multiplier;
> +pll_update(pll);
> +}
> +
> +static void pll_set_enable(RccPllState *pll, bool enabled)
> +{
> +if (pll->enabled == enabled) {
> +return;
> +}
> +
> +pll->enabled = enabled;
> +pll_update(pll);
> +}
> +
> +static void pll_set_channel_enable(RccPllState *pll,
> +   PllCommonChannels channel,
> +   bool enabled)
> +{
> +if (pll->channel_enabled[channel] == enabled) {
> +return;
> +}
> +
> +if (enabled) {
> +

[PATCH v4 3/8] Add an internal PLL Clock object

2024-01-30 Thread Arnaud Minier
This object represents the PLLs and their channels. The PLLs allow for a
more fine-grained control of the clocks frequency.

Wasn't sure about how to handle the reset and the migration so used the
same appproach as the BCM2835 CPRMAN.

Signed-off-by: Arnaud Minier 
Signed-off-by: Inès Varhol 
---
 hw/misc/stm32l4x5_rcc.c   | 175 ++
 hw/misc/trace-events  |   5 +
 include/hw/misc/stm32l4x5_rcc.h   |  40 +
 include/hw/misc/stm32l4x5_rcc_internals.h |  22 +++
 4 files changed, 242 insertions(+)

diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c
index ed10832f88..fb0233c3e9 100644
--- a/hw/misc/stm32l4x5_rcc.c
+++ b/hw/misc/stm32l4x5_rcc.c
@@ -162,6 +162,156 @@ static void clock_mux_set_source(RccClockMuxState *mux, 
RccClockMuxSource src)
 clock_mux_update(mux);
 }
 
+static void pll_update(RccPllState *pll)
+{
+uint64_t vco_freq, old_channel_freq, channel_freq;
+int i;
+
+/* The common PLLM factor is handled by the PLL mux */
+vco_freq = muldiv64(clock_get_hz(pll->in), pll->vco_multiplier, 1);
+
+for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
+if (!pll->channel_exists[i]) {
+continue;
+}
+
+old_channel_freq = clock_get_hz(pll->channels[i]);
+if (!pll->enabled ||
+!pll->channel_enabled[i] ||
+!pll->channel_divider[i]) {
+channel_freq = 0;
+} else {
+channel_freq = muldiv64(vco_freq,
+1,
+pll->channel_divider[i]);
+}
+
+/* No change, early continue to avoid log spam and useless propagation 
*/
+if (old_channel_freq == channel_freq) {
+continue;
+}
+
+clock_update_hz(pll->channels[i], channel_freq);
+trace_stm32l4x5_rcc_pll_update(pll->id, i, vco_freq,
+old_channel_freq, channel_freq);
+}
+}
+
+static void pll_src_update(void *opaque, ClockEvent event)
+{
+RccPllState *s = opaque;
+pll_update(s);
+}
+
+static void pll_init(Object *obj)
+{
+RccPllState *s = RCC_PLL(obj);
+size_t i;
+
+s->in = qdev_init_clock_in(DEVICE(s), "in",
+   pll_src_update, s, ClockUpdate);
+
+const char *names[] = {
+"out-p", "out-q", "out-r",
+};
+
+for (i = 0; i < RCC_NUM_CHANNEL_PLL_OUT; i++) {
+s->channels[i] = qdev_init_clock_out(DEVICE(s), names[i]);
+}
+}
+
+static void pll_reset_hold(Object *obj)
+{ }
+
+static const VMStateDescription pll_vmstate = {
+.name = TYPE_RCC_PLL,
+.version_id = 1,
+.minimum_version_id = 1,
+.fields = (VMStateField[]) {
+VMSTATE_UINT32(id, RccPllState),
+VMSTATE_CLOCK(in, RccPllState),
+VMSTATE_ARRAY_CLOCK(channels, RccPllState,
+RCC_NUM_CHANNEL_PLL_OUT),
+VMSTATE_BOOL(enabled, RccPllState),
+VMSTATE_UINT32(vco_multiplier, RccPllState),
+VMSTATE_BOOL_ARRAY(channel_enabled, RccPllState, 
RCC_NUM_CHANNEL_PLL_OUT),
+VMSTATE_BOOL_ARRAY(channel_exists, RccPllState, 
RCC_NUM_CHANNEL_PLL_OUT),
+VMSTATE_UINT32_ARRAY(channel_divider, RccPllState, 
RCC_NUM_CHANNEL_PLL_OUT),
+VMSTATE_END_OF_LIST()
+}
+};
+
+static void pll_class_init(ObjectClass *klass, void *data)
+{
+DeviceClass *dc = DEVICE_CLASS(klass);
+ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+rc->phases.hold = pll_reset_hold;
+dc->vmsd = _vmstate;
+}
+
+static void pll_set_vco_multiplier(RccPllState *pll, uint32_t vco_multiplier)
+{
+if (pll->vco_multiplier == vco_multiplier) {
+return;
+}
+
+if (vco_multiplier < 8 || vco_multiplier > 86) {
+qemu_log_mask(LOG_GUEST_ERROR,
+"%s: VCO multiplier is out of bound (%u) for PLL %u\n",
+__func__, vco_multiplier, pll->id);
+}
+
+trace_stm32l4x5_rcc_pll_set_vco_multiplier(pll->id,
+pll->vco_multiplier, vco_multiplier);
+
+pll->vco_multiplier = vco_multiplier;
+pll_update(pll);
+}
+
+static void pll_set_enable(RccPllState *pll, bool enabled)
+{
+if (pll->enabled == enabled) {
+return;
+}
+
+pll->enabled = enabled;
+pll_update(pll);
+}
+
+static void pll_set_channel_enable(RccPllState *pll,
+   PllCommonChannels channel,
+   bool enabled)
+{
+if (pll->channel_enabled[channel] == enabled) {
+return;
+}
+
+if (enabled) {
+trace_stm32l4x5_rcc_pll_channel_enable(pll->id, channel);
+} else {
+trace_stm32l4x5_rcc_pll_channel_disable(pll->id, channel);
+}
+
+pll->channel_enabled[channel] = enabled;
+pll_update(pll);
+}
+
+static void pll_set_channel_divider(RccPllState *pll,
+PllCommonChannels channel,
+uint32_t divider)
+{
+if (pll->channel_divider[channel] == divider) {