diff mbox series

[v2,1/4] clock: Add ClockEvent parameter to callbacks

Message ID 20210209132040.5091-2-peter.maydell@linaro.org
State New
Headers show
Series New APIs for the Clock framework | expand

Commit Message

Peter Maydell Feb. 9, 2021, 1:20 p.m. UTC
The Clock framework allows users to specify a callback which is
called after the clock's period has been updated.  Some users need to
also have a callback which is called before the clock period is
updated.

As the first step in adding support for notifying Clock users on
pre-update events, add an argument to the ClockCallback to specify
what event is being notified, and add an argument to the various
functions for registering a callback to specify which events are
of interest to that callback.

Note that the documentation update renders correct the previously
incorrect claim in 'Adding a new clock' that callbacks "will be
explained in a following section".

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
v1->v2: (suggested by Luc) instead of making callback functions check
whether 'event' is one they are interested in, specify mask of
interesting events at callback registration time.
---
 docs/devel/clocks.rst            | 52 +++++++++++++++++++++++++++-----
 include/hw/clock.h               | 21 +++++++++++--
 include/hw/qdev-clock.h          | 17 ++++++++---
 hw/adc/npcm7xx_adc.c             |  2 +-
 hw/arm/armsse.c                  |  9 +++---
 hw/char/cadence_uart.c           |  4 +--
 hw/char/ibex_uart.c              |  4 +--
 hw/char/pl011.c                  |  5 +--
 hw/core/clock.c                  | 20 +++++++++---
 hw/core/qdev-clock.c             |  8 +++--
 hw/mips/cps.c                    |  2 +-
 hw/misc/bcm2835_cprman.c         | 23 ++++++++------
 hw/misc/npcm7xx_clk.c            | 26 +++++++++++++---
 hw/misc/npcm7xx_pwm.c            |  2 +-
 hw/misc/zynq_slcr.c              |  5 +--
 hw/timer/cmsdk-apb-dualtimer.c   |  5 +--
 hw/timer/cmsdk-apb-timer.c       |  4 +--
 hw/timer/npcm7xx_timer.c         |  2 +-
 hw/watchdog/cmsdk-apb-watchdog.c |  5 +--
 target/mips/cpu.c                |  2 +-
 20 files changed, 160 insertions(+), 58 deletions(-)

Comments

Hao Wu Feb. 10, 2021, 8:53 p.m. UTC | #1
On Tue, Feb 9, 2021 at 5:24 AM Peter Maydell <peter.maydell@linaro.org>
wrote:

> The Clock framework allows users to specify a callback which is
> called after the clock's period has been updated.  Some users need to
> also have a callback which is called before the clock period is
> updated.
>
> As the first step in adding support for notifying Clock users on
> pre-update events, add an argument to the ClockCallback to specify
> what event is being notified, and add an argument to the various
> functions for registering a callback to specify which events are
> of interest to that callback.
>
> Note that the documentation update renders correct the previously
> incorrect claim in 'Adding a new clock' that callbacks "will be
> explained in a following section".
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> v1->v2: (suggested by Luc) instead of making callback functions check
> whether 'event' is one they are interested in, specify mask of
> interesting events at callback registration time.
> ---
>  docs/devel/clocks.rst            | 52 +++++++++++++++++++++++++++-----
>  include/hw/clock.h               | 21 +++++++++++--
>  include/hw/qdev-clock.h          | 17 ++++++++---
>  hw/adc/npcm7xx_adc.c             |  2 +-
>  hw/arm/armsse.c                  |  9 +++---
>  hw/char/cadence_uart.c           |  4 +--
>  hw/char/ibex_uart.c              |  4 +--
>  hw/char/pl011.c                  |  5 +--
>  hw/core/clock.c                  | 20 +++++++++---
>  hw/core/qdev-clock.c             |  8 +++--
>  hw/mips/cps.c                    |  2 +-
>  hw/misc/bcm2835_cprman.c         | 23 ++++++++------
>  hw/misc/npcm7xx_clk.c            | 26 +++++++++++++---
>  hw/misc/npcm7xx_pwm.c            |  2 +-
>  hw/misc/zynq_slcr.c              |  5 +--
>  hw/timer/cmsdk-apb-dualtimer.c   |  5 +--
>  hw/timer/cmsdk-apb-timer.c       |  4 +--
>  hw/timer/npcm7xx_timer.c         |  2 +-
>  hw/watchdog/cmsdk-apb-watchdog.c |  5 +--
>  target/mips/cpu.c                |  2 +-
>  20 files changed, 160 insertions(+), 58 deletions(-)
>
> diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
> index c54bbb82409..cd344e3fe5d 100644
> --- a/docs/devel/clocks.rst
> +++ b/docs/devel/clocks.rst
> @@ -80,11 +80,12 @@ Adding clocks to a device must be done during the init
> method of the Device
>  instance.
>
>  To add an input clock to a device, the function ``qdev_init_clock_in()``
> -must be used.  It takes the name, a callback and an opaque parameter
> -for the callback (this will be explained in a following section).
> +must be used.  It takes the name, a callback, an opaque parameter
> +for the callback and a mask of events when the callback should be
> +called (this will be explained in a following section).
>  Output is simpler; only the name is required. Typically::
>
> -    qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev);
> +    qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev,
> ClockUpdate);
>      qdev_init_clock_out(DEVICE(dev), "clk_out");
>
>  Both functions return the created Clock pointer, which should be saved in
> the
> @@ -113,7 +114,7 @@ output.
>       * callback for the input clock (see "Callback on input clock
>       * change" section below for more information).
>       */
> -    static void clk_in_callback(void *opaque);
> +    static void clk_in_callback(void *opaque, ClockEvent event);
>
>      /*
>       * static array describing clocks:
> @@ -124,7 +125,7 @@ output.
>       *   the clk_out field of a MyDeviceState structure.
>       */
>      static const ClockPortInitArray mydev_clocks = {
> -        QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback),
> +        QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback,
> ClockUpdate),
>          QDEV_CLOCK_OUT(MyDeviceState, clk_out),
>          QDEV_CLOCK_END
>      };
> @@ -153,6 +154,40 @@ nothing else to do. This value will be propagated to
> other clocks when
>  connecting the clocks together and devices will fetch the right value
> during
>  the first reset.
>
> +Clock callbacks
> +---------------
> +
> +You can give a clock a callback function in several ways:
> +
> + * by passing it as an argument to ``qdev_init_clock_in()``
> + * as an argument to the ``QDEV_CLOCK_IN()`` macro initializing an
> +   array to be passed to ``qdev_init_clocks()``
> + * by directly calling the ``clock_set_callback()`` function
> +
> +The callback function must be of this type:
> +
> +.. code-block:: c
> +
> +   typedef void ClockCallback(void *opaque, ClockEvent event);
> +
> +The ``opaque`` argument is the pointer passed to ``qdev_init_clock_in()``
> +or ``clock_set_callback()``; for ``qdev_init_clocks()`` it is the
> +``dev`` device pointer.
> +
> +The ``event`` argument specifies why the callback has been called.
> +When you register the callback you specify a mask of ClockEvent values
> +that you are interested in. The callback will only be called for those
> +events.
> +
> +The events currently supported are:
> +
> + * ``ClockUpdate`` : called after the input clock's period has changed
> +
> +Note that a clock only has one callback: it is not possible to register
> +different functions for different events. You must register a single
> +callback which listens for all of the events you are interested in,
> +and use the ``event`` argument to identify which event has happened.
> +
>  Retrieving clocks from a device
>  -------------------------------
>
> @@ -231,7 +266,7 @@ object during device instance init. For example:
>  .. code-block:: c
>
>      clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback,
> -                             dev);
> +                             dev, ClockUpdate);
>      /* set initial value to 10ns / 100MHz */
>      clock_set_ns(clk, 10);
>
> @@ -267,11 +302,12 @@ next lowest integer. This implies some inaccuracy
> due to the rounding,
>  so be cautious about using it in calculations.
>
>  It is also possible to register a callback on clock frequency changes.
> -Here is an example:
> +Here is an example, which assumes that ``clock_callback`` has been
> +specified as the callback for the ``ClockUpdate`` event:
>
>  .. code-block:: c
>
> -    void clock_callback(void *opaque) {
> +    void clock_callback(void *opaque, ClockEvent event) {
>          MyDeviceState *s = (MyDeviceState *) opaque;
>          /*
>           * 'opaque' is the argument passed to qdev_init_clock_in();
> diff --git a/include/hw/clock.h b/include/hw/clock.h
> index e5f45e2626d..5c73b4e7ae9 100644
> --- a/include/hw/clock.h
> +++ b/include/hw/clock.h
> @@ -22,7 +22,17 @@
>  #define TYPE_CLOCK "clock"
>  OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
>
> -typedef void ClockCallback(void *opaque);
> +/*
> + * Argument to ClockCallback functions indicating why the callback
> + * has been called. A mask of these values logically ORed together
> + * is used to specify which events are interesting when the callback
> + * is registered, so these values must all be different bit values.
> + */
> +typedef enum ClockEvent {
> +    ClockUpdate = 1, /* Clock period has just updated */
> +} ClockEvent;
> +
> +typedef void ClockCallback(void *opaque, ClockEvent event);
>
>  /*
>   * clock store a value representing the clock's period in 2^-32ns unit.
> @@ -50,6 +60,7 @@ typedef void ClockCallback(void *opaque);
>   * @canonical_path: clock path string cache (used for trace purpose)
>   * @callback: called when clock changes
>   * @callback_opaque: argument for @callback
> + * @callback_events: mask of events when callback should be called
>   * @source: source (or parent in clock tree) of the clock
>   * @children: list of clocks connected to this one (it is their source)
>   * @sibling: structure used to form a clock list
> @@ -67,6 +78,7 @@ struct Clock {
>      char *canonical_path;
>      ClockCallback *callback;
>      void *callback_opaque;
> +    int callback_events;
>
>      /* Clocks are organized in a clock tree */
>      Clock *source;
> @@ -114,10 +126,15 @@ Clock *clock_new(Object *parent, const char *name);
>   * @clk: the clock to register the callback into
>   * @cb: the callback function
>   * @opaque: the argument to the callback
> + * @events: the events the callback should be called for
> + *          (logical OR of ClockEvent enum values)
>   *
>   * Register a callback called on every clock update.
> + * Note that a clock has only one callback: you cannot register
> + * different callback functions for different events.
>   */
> -void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque);
> +void clock_set_callback(Clock *clk, ClockCallback *cb,
> +                        void *opaque, int events);
>
>  /**
>   * clock_clear_callback:
> diff --git a/include/hw/qdev-clock.h b/include/hw/qdev-clock.h
> index 64ca4d266f2..348ec363525 100644
> --- a/include/hw/qdev-clock.h
> +++ b/include/hw/qdev-clock.h
> @@ -22,6 +22,8 @@
>   * @name: the name of the clock (can't be NULL).
>   * @callback: optional callback to be called on update or NULL.
>   * @opaque: argument for the callback
> + * @events: the events the callback should be called for
> + *          (logical OR of ClockEvent enum values)
>   * @returns: a pointer to the newly added clock
>   *
>   * Add an input clock to device @dev as a clock named @name.
> @@ -29,7 +31,8 @@
>   * The callback will be called with @opaque as opaque parameter.
>   */
>  Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
> -                          ClockCallback *callback, void *opaque);
> +                          ClockCallback *callback, void *opaque,
> +                          int events);
>
>  /**
>   * qdev_init_clock_out:
> @@ -105,6 +108,7 @@ void qdev_finalize_clocklist(DeviceState *dev);
>   * @output: indicates whether the clock is input or output
>   * @callback: for inputs, optional callback to be called on clock's update
>   * with device as opaque
> + * @callback_events: mask of ClockEvent values for when callback is called
>   * @offset: optional offset to store the ClockIn or ClockOut pointer in
> device
>   * state structure (0 means unused)
>   */
> @@ -112,6 +116,7 @@ struct ClockPortInitElem {
>      const char *name;
>      bool is_output;
>      ClockCallback *callback;
> +    int callback_events;
>      size_t offset;
>  };
>
> @@ -119,10 +124,11 @@ struct ClockPortInitElem {
>      (offsetof(devstate, field) + \
>       type_check(Clock *, typeof_field(devstate, field)))
>
> -#define QDEV_CLOCK(out_not_in, devstate, field, cb) { \
> +#define QDEV_CLOCK(out_not_in, devstate, field, cb, cbevents) {  \
>      .name = (stringify(field)), \
>      .is_output = out_not_in, \
>      .callback = cb, \
> +    .callback_events = cbevents, \
>      .offset = clock_offset_value(devstate, field), \
>  }
>
> @@ -133,14 +139,15 @@ struct ClockPortInitElem {
>   * @field: a field in @_devstate (must be Clock*)
>   * @callback: (for input only) callback (or NULL) to be called with the
> device
>   * state as argument
> + * @cbevents: (for input only) ClockEvent mask for when callback is called
>   *
>   * The name of the clock will be derived from @field
>   */
> -#define QDEV_CLOCK_IN(devstate, field, callback) \
> -    QDEV_CLOCK(false, devstate, field, callback)
> +#define QDEV_CLOCK_IN(devstate, field, callback, cbevents)       \
> +    QDEV_CLOCK(false, devstate, field, callback, cbevents)
>
>  #define QDEV_CLOCK_OUT(devstate, field) \
> -    QDEV_CLOCK(true, devstate, field, NULL)
> +    QDEV_CLOCK(true, devstate, field, NULL, 0)
>
>  #define QDEV_CLOCK_END { .name = NULL }
>
> diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
> index 870a6d50c27..573f4876dc6 100644
> --- a/hw/adc/npcm7xx_adc.c
> +++ b/hw/adc/npcm7xx_adc.c
> @@ -238,7 +238,7 @@ static void npcm7xx_adc_init(Object *obj)
>      memory_region_init_io(&s->iomem, obj, &npcm7xx_adc_ops, s,
>                            TYPE_NPCM7XX_ADC, 4 * KiB);
>      sysbus_init_mmio(sbd, &s->iomem);
> -    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
> +    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL,
> ClockUpdate);
>
Since there's no callback here should it be
 s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
?

>
>      for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) {
>          object_property_add_uint32_ptr(obj, "adci[*]",
> diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c
> index 26e1a8c95b6..fa155b72022 100644
> --- a/hw/arm/armsse.c
> +++ b/hw/arm/armsse.c
> @@ -230,9 +230,10 @@ static void armsse_forward_sec_resp_cfg(ARMSSE *s)
>      qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
>  }
>
> -static void armsse_mainclk_update(void *opaque)
> +static void armsse_mainclk_update(void *opaque, ClockEvent event)
>  {
>      ARMSSE *s = ARM_SSE(opaque);
> +
>      /*
>       * Set system_clock_scale from our Clock input; this is what
>       * controls the tick rate of the CPU SysTick timer.
> @@ -251,8 +252,8 @@ static void armsse_init(Object *obj)
>      assert(info->num_cpus <= SSE_MAX_CPUS);
>
>      s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK",
> -                                    armsse_mainclk_update, s);
> -    s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL);
> +                                    armsse_mainclk_update, s,
> ClockUpdate);
> +    s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL, 0);
>
>      memory_region_init(&s->container, obj, "armsse-container",
> UINT64_MAX);
>
> @@ -1120,7 +1121,7 @@ static void armsse_realize(DeviceState *dev, Error
> **errp)
>      sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
>
>      /* Set initial system_clock_scale from MAINCLK */
> -    armsse_mainclk_update(s);
> +    armsse_mainclk_update(s, ClockUpdate);
>  }
>
>  static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index c603e14012a..ceb677bc5a8 100644
> --- a/hw/char/cadence_uart.c
> +++ b/hw/char/cadence_uart.c
> @@ -519,7 +519,7 @@ static void cadence_uart_realize(DeviceState *dev,
> Error **errp)
>                               uart_event, NULL, s, NULL, true);
>  }
>
> -static void cadence_uart_refclk_update(void *opaque)
> +static void cadence_uart_refclk_update(void *opaque, ClockEvent event)
>  {
>      CadenceUARTState *s = opaque;
>
> @@ -537,7 +537,7 @@ static void cadence_uart_init(Object *obj)
>      sysbus_init_irq(sbd, &s->irq);
>
>      s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
> -            cadence_uart_refclk_update, s);
> +                                   cadence_uart_refclk_update, s,
> ClockUpdate);
>      /* initialize the frequency in case the clock remains unconnected */
>      clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
>
> diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c
> index 89f1182c9bf..edcaa30aded 100644
> --- a/hw/char/ibex_uart.c
> +++ b/hw/char/ibex_uart.c
> @@ -396,7 +396,7 @@ static void ibex_uart_write(void *opaque, hwaddr addr,
>      }
>  }
>
> -static void ibex_uart_clk_update(void *opaque)
> +static void ibex_uart_clk_update(void *opaque, ClockEvent event)
>  {
>      IbexUartState *s = opaque;
>
> @@ -466,7 +466,7 @@ static void ibex_uart_init(Object *obj)
>      IbexUartState *s = IBEX_UART(obj);
>
>      s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock",
> -                                  ibex_uart_clk_update, s);
> +                                  ibex_uart_clk_update, s, ClockUpdate);
>      clock_set_hz(s->f_clk, IBEX_UART_CLOCK);
>
>      sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);
> diff --git a/hw/char/pl011.c b/hw/char/pl011.c
> index ea4a4e52356..c5621a195ff 100644
> --- a/hw/char/pl011.c
> +++ b/hw/char/pl011.c
> @@ -309,7 +309,7 @@ static void pl011_event(void *opaque, QEMUChrEvent
> event)
>          pl011_put_fifo(opaque, 0x400);
>  }
>
> -static void pl011_clock_update(void *opaque)
> +static void pl011_clock_update(void *opaque, ClockEvent event)
>  {
>      PL011State *s = PL011(opaque);
>
> @@ -378,7 +378,8 @@ static void pl011_init(Object *obj)
>          sysbus_init_irq(sbd, &s->irq[i]);
>      }
>
> -    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update,
> s);
> +    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s,
> +                                ClockUpdate);
>
>      s->read_trigger = 1;
>      s->ifl = 0x12;
> diff --git a/hw/core/clock.c b/hw/core/clock.c
> index 76b5f468b6e..71dc1f4de65 100644
> --- a/hw/core/clock.c
> +++ b/hw/core/clock.c
> @@ -39,15 +39,16 @@ Clock *clock_new(Object *parent, const char *name)
>      return clk;
>  }
>
> -void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque)
> +void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, int
> events)
>  {
>      clk->callback = cb;
>      clk->callback_opaque = opaque;
> +    clk->callback_events = events;
>  }
>
>  void clock_clear_callback(Clock *clk)
>  {
> -    clock_set_callback(clk, NULL, NULL);
> +    clock_set_callback(clk, NULL, NULL, 0);
>  }
>
>  bool clock_set(Clock *clk, uint64_t period)
> @@ -62,6 +63,17 @@ bool clock_set(Clock *clk, uint64_t period)
>      return true;
>  }
>
> +static void clock_call_callback(Clock *clk, ClockEvent event)
> +{
> +    /*
> +     * Call the Clock's callback for this event, if it has one and
> +     * is interested in this event.
> +     */
> +    if (clk->callback && (clk->callback_events & event)) {
> +        clk->callback(clk->callback_opaque, event);
> +    }
> +}
> +
>  static void clock_propagate_period(Clock *clk, bool call_callbacks)
>  {
>      Clock *child;
> @@ -72,8 +84,8 @@ static void clock_propagate_period(Clock *clk, bool
> call_callbacks)
>              trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
>                                 CLOCK_PERIOD_TO_HZ(clk->period),
>                                 call_callbacks);
> -            if (call_callbacks && child->callback) {
> -                child->callback(child->callback_opaque);
> +            if (call_callbacks) {
> +                clock_call_callback(child, ClockUpdate);
>              }
>              clock_propagate_period(child, call_callbacks);
>          }
> diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
> index eb05f2a13ca..9c55ddc23ee 100644
> --- a/hw/core/qdev-clock.c
> +++ b/hw/core/qdev-clock.c
> @@ -111,7 +111,8 @@ Clock *qdev_init_clock_out(DeviceState *dev, const
> char *name)
>  }
>
>  Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
> -                            ClockCallback *callback, void *opaque)
> +                          ClockCallback *callback, void *opaque,
> +                          int events)
>  {
>      NamedClockList *ncl;
>
> @@ -120,7 +121,7 @@ Clock *qdev_init_clock_in(DeviceState *dev, const char
> *name,
>      ncl = qdev_init_clocklist(dev, name, false, NULL);
>
>      if (callback) {
> -        clock_set_callback(ncl->clock, callback, opaque);
> +        clock_set_callback(ncl->clock, callback, opaque, events);
>      }
>      return ncl->clock;
>  }
> @@ -137,7 +138,8 @@ void qdev_init_clocks(DeviceState *dev, const
> ClockPortInitArray clocks)
>          if (elem->is_output) {
>              *clkp = qdev_init_clock_out(dev, elem->name);
>          } else {
> -            *clkp = qdev_init_clock_in(dev, elem->name, elem->callback,
> dev);
> +            *clkp = qdev_init_clock_in(dev, elem->name, elem->callback,
> dev,
> +                                       elem->callback_events);
>          }
>      }
>  }
> diff --git a/hw/mips/cps.c b/hw/mips/cps.c
> index 7a0d289efaf..2b436700ce6 100644
> --- a/hw/mips/cps.c
> +++ b/hw/mips/cps.c
> @@ -39,7 +39,7 @@ static void mips_cps_init(Object *obj)
>      SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
>      MIPSCPSState *s = MIPS_CPS(obj);
>
> -    s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL);
> +    s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL, 0);
>      /*
>       * Cover entire address space as there do not seem to be any
>       * constraints for the base address of CPC and GIC.
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index 7e415a017c9..75e6c574d46 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -107,7 +107,7 @@ static void pll_update(CprmanPllState *pll)
>      clock_update_hz(pll->out, freq);
>  }
>
> -static void pll_xosc_update(void *opaque)
> +static void pll_xosc_update(void *opaque, ClockEvent event)
>  {
>      pll_update(CPRMAN_PLL(opaque));
>  }
> @@ -116,7 +116,8 @@ static void pll_init(Object *obj)
>  {
>      CprmanPllState *s = CPRMAN_PLL(obj);
>
> -    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in",
> pll_xosc_update, s);
> +    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update,
> +                                    s, ClockUpdate);
>      s->out = qdev_init_clock_out(DEVICE(s), "out");
>  }
>
> @@ -209,7 +210,7 @@ static void pll_update_all_channels(BCM2835CprmanState
> *s,
>      }
>  }
>
> -static void pll_channel_pll_in_update(void *opaque)
> +static void pll_channel_pll_in_update(void *opaque, ClockEvent event)
>  {
>      pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
>  }
> @@ -219,7 +220,8 @@ static void pll_channel_init(Object *obj)
>      CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj);
>
>      s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
> -                                   pll_channel_pll_in_update, s);
> +                                   pll_channel_pll_in_update, s,
> +                                   ClockUpdate);
>      s->out = qdev_init_clock_out(DEVICE(s), "out");
>  }
>
> @@ -303,7 +305,7 @@ static void clock_mux_update(CprmanClockMuxState *mux)
>      clock_update_hz(mux->out, freq);
>  }
>
> -static void clock_mux_src_update(void *opaque)
> +static void clock_mux_src_update(void *opaque, ClockEvent event)
>  {
>      CprmanClockMuxState **backref = opaque;
>      CprmanClockMuxState *s = *backref;
> @@ -335,7 +337,8 @@ static void clock_mux_init(Object *obj)
>          s->backref[i] = s;
>          s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
>                                          clock_mux_src_update,
> -                                        &s->backref[i]);
> +                                        &s->backref[i],
> +                                        ClockUpdate);
>          g_free(name);
>      }
>
> @@ -380,7 +383,7 @@ static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState
> *s)
>      clock_update(s->out, clock_get(src));
>  }
>
> -static void dsi0hsck_mux_in_update(void *opaque)
> +static void dsi0hsck_mux_in_update(void *opaque, ClockEvent event)
>  {
>      dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque));
>  }
> @@ -390,8 +393,10 @@ static void dsi0hsck_mux_init(Object *obj)
>      CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj);
>      DeviceState *dev = DEVICE(obj);
>
> -    s->plla_in = qdev_init_clock_in(dev, "plla-in",
> dsi0hsck_mux_in_update, s);
> -    s->plld_in = qdev_init_clock_in(dev, "plld-in",
> dsi0hsck_mux_in_update, s);
> +    s->plla_in = qdev_init_clock_in(dev, "plla-in",
> dsi0hsck_mux_in_update,
> +                                    s, ClockUpdate);
> +    s->plld_in = qdev_init_clock_in(dev, "plld-in",
> dsi0hsck_mux_in_update,
> +                                    s, ClockUpdate);
>      s->out = qdev_init_clock_out(DEVICE(s), "out");
>  }
>
> diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
> index 0bcae9ce957..a1ee67dc9a1 100644
> --- a/hw/misc/npcm7xx_clk.c
> +++ b/hw/misc/npcm7xx_clk.c
> @@ -586,15 +586,26 @@ static const DividerInitInfo
> divider_init_info_list[] = {
>      },
>  };
>
> +static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event)
> +{
> +    npcm7xx_clk_update_pll(opaque);
> +}
> +
>  static void npcm7xx_clk_pll_init(Object *obj)
>  {
>      NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj);
>
>      pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in",
> -            npcm7xx_clk_update_pll, pll);
> +                                       npcm7xx_clk_update_pll_cb, pll,
> +                                       ClockUpdate);
>      pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out");
>  }
>
> +static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event)
> +{
> +    npcm7xx_clk_update_sel(opaque);
> +}
> +
>  static void npcm7xx_clk_sel_init(Object *obj)
>  {
>      int i;
> @@ -603,16 +614,23 @@ static void npcm7xx_clk_sel_init(Object *obj)
>      for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) {
>          sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel),
>                  g_strdup_printf("clock-in[%d]", i),
> -                npcm7xx_clk_update_sel, sel);
> +                npcm7xx_clk_update_sel_cb, sel, ClockUpdate);
>      }
>      sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out");
>  }
> +
> +static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event)
> +{
> +    npcm7xx_clk_update_divider(opaque);
> +}
> +
>  static void npcm7xx_clk_divider_init(Object *obj)
>  {
>      NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj);
>
>      div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in",
> -            npcm7xx_clk_update_divider, div);
> +                                       npcm7xx_clk_update_divider_cb,
> +                                       div, ClockUpdate);
>      div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out");
>  }
>
> @@ -875,7 +893,7 @@ static void
> npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
>  {
>      int i;
>
> -    s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL);
> +    s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0);
>
>      /* First pass: init all converter modules */
>      QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) !=
> NPCM7XX_CLOCK_NR_PLLS);
> diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c
> index dabcb6c0f95..ce192bb2741 100644
> --- a/hw/misc/npcm7xx_pwm.c
> +++ b/hw/misc/npcm7xx_pwm.c
> @@ -493,7 +493,7 @@ static void npcm7xx_pwm_init(Object *obj)
>      memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s,
>                            TYPE_NPCM7XX_PWM, 4 * KiB);
>      sysbus_init_mmio(sbd, &s->iomem);
> -    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
> +    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
>
>      for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
>          object_property_add_uint32_ptr(obj, "freq[*]",
> diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c
> index 66504a9d3ab..c66d7db177d 100644
> --- a/hw/misc/zynq_slcr.c
> +++ b/hw/misc/zynq_slcr.c
> @@ -307,9 +307,10 @@ static void zynq_slcr_propagate_clocks(ZynqSLCRState
> *s)
>      clock_propagate(s->uart1_ref_clk);
>  }
>
> -static void zynq_slcr_ps_clk_callback(void *opaque)
> +static void zynq_slcr_ps_clk_callback(void *opaque, ClockEvent event)
>  {
>      ZynqSLCRState *s = (ZynqSLCRState *) opaque;
> +
>      zynq_slcr_compute_clocks(s);
>      zynq_slcr_propagate_clocks(s);
>  }
> @@ -576,7 +577,7 @@ static const MemoryRegionOps slcr_ops = {
>  };
>
>  static const ClockPortInitArray zynq_slcr_clocks = {
> -    QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback),
> +    QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback,
> ClockUpdate),
>      QDEV_CLOCK_OUT(ZynqSLCRState, uart0_ref_clk),
>      QDEV_CLOCK_OUT(ZynqSLCRState, uart1_ref_clk),
>      QDEV_CLOCK_END
> diff --git a/hw/timer/cmsdk-apb-dualtimer.c
> b/hw/timer/cmsdk-apb-dualtimer.c
> index ef49f5852d3..d4a509c798e 100644
> --- a/hw/timer/cmsdk-apb-dualtimer.c
> +++ b/hw/timer/cmsdk-apb-dualtimer.c
> @@ -449,7 +449,7 @@ static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
>      s->timeritop = 0;
>  }
>
> -static void cmsdk_apb_dualtimer_clk_update(void *opaque)
> +static void cmsdk_apb_dualtimer_clk_update(void *opaque, ClockEvent event)
>  {
>      CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
>      int i;
> @@ -478,7 +478,8 @@ static void cmsdk_apb_dualtimer_init(Object *obj)
>          sysbus_init_irq(sbd, &s->timermod[i].timerint);
>      }
>      s->timclk = qdev_init_clock_in(DEVICE(s), "TIMCLK",
> -                                   cmsdk_apb_dualtimer_clk_update, s);
> +                                   cmsdk_apb_dualtimer_clk_update, s,
> +                                   ClockUpdate);
>  }
>
>  static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c
> index ee51ce3369c..68aa1a76360 100644
> --- a/hw/timer/cmsdk-apb-timer.c
> +++ b/hw/timer/cmsdk-apb-timer.c
> @@ -204,7 +204,7 @@ static void cmsdk_apb_timer_reset(DeviceState *dev)
>      ptimer_transaction_commit(s->timer);
>  }
>
> -static void cmsdk_apb_timer_clk_update(void *opaque)
> +static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
>  {
>      CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
>
> @@ -223,7 +223,7 @@ static void cmsdk_apb_timer_init(Object *obj)
>      sysbus_init_mmio(sbd, &s->iomem);
>      sysbus_init_irq(sbd, &s->timerint);
>      s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
> -                                 cmsdk_apb_timer_clk_update, s);
> +                                 cmsdk_apb_timer_clk_update, s,
> ClockUpdate);
>  }
>
>  static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
> index 36e2c07db26..4efdf135b82 100644
> --- a/hw/timer/npcm7xx_timer.c
> +++ b/hw/timer/npcm7xx_timer.c
> @@ -627,7 +627,7 @@ static void npcm7xx_timer_init(Object *obj)
>      sysbus_init_mmio(sbd, &s->iomem);
>      qdev_init_gpio_out_named(dev, &w->reset_signal,
>              NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
> -    s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
> +    s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL, 0);
>  }
>
>  static const VMStateDescription vmstate_npcm7xx_base_timer = {
> diff --git a/hw/watchdog/cmsdk-apb-watchdog.c
> b/hw/watchdog/cmsdk-apb-watchdog.c
> index 302f1711738..5a2cd46eb76 100644
> --- a/hw/watchdog/cmsdk-apb-watchdog.c
> +++ b/hw/watchdog/cmsdk-apb-watchdog.c
> @@ -310,7 +310,7 @@ static void cmsdk_apb_watchdog_reset(DeviceState *dev)
>      ptimer_transaction_commit(s->timer);
>  }
>
> -static void cmsdk_apb_watchdog_clk_update(void *opaque)
> +static void cmsdk_apb_watchdog_clk_update(void *opaque, ClockEvent event)
>  {
>      CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
>
> @@ -329,7 +329,8 @@ static void cmsdk_apb_watchdog_init(Object *obj)
>      sysbus_init_mmio(sbd, &s->iomem);
>      sysbus_init_irq(sbd, &s->wdogint);
>      s->wdogclk = qdev_init_clock_in(DEVICE(s), "WDOGCLK",
> -                                    cmsdk_apb_watchdog_clk_update, s);
> +                                    cmsdk_apb_watchdog_clk_update, s,
> +                                    ClockUpdate);
>
>      s->is_luminary = false;
>      s->id = cmsdk_apb_watchdog_id;
> diff --git a/target/mips/cpu.c b/target/mips/cpu.c
> index ad163ead625..2f3d9d2ce2c 100644
> --- a/target/mips/cpu.c
> +++ b/target/mips/cpu.c
> @@ -636,7 +636,7 @@ static void mips_cpu_initfn(Object *obj)
>      MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj);
>
>      cpu_set_cpustate_pointers(cpu);
> -    cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu);
> +    cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu, 0);
>      env->cpu_model = mcc->cpu_def;
>  }
>
> --
> 2.20.1
>
>
>
Peter Maydell Feb. 10, 2021, 10:19 p.m. UTC | #2
On Wed, 10 Feb 2021 at 20:53, Hao Wu <wuhaotsh@google.com> wrote:
>
>
>
> On Tue, Feb 9, 2021 at 5:24 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>> diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
>> index 870a6d50c27..573f4876dc6 100644
>> --- a/hw/adc/npcm7xx_adc.c
>> +++ b/hw/adc/npcm7xx_adc.c
>> @@ -238,7 +238,7 @@ static void npcm7xx_adc_init(Object *obj)
>>      memory_region_init_io(&s->iomem, obj, &npcm7xx_adc_ops, s,
>>                            TYPE_NPCM7XX_ADC, 4 * KiB);
>>      sysbus_init_mmio(sbd, &s->iomem);
>> -    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
>> +    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, ClockUpdate);
>
> Since there's no callback here should it be
>  s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
> ?

Yes; thanks for the catch. (The function ignores the events argument
if there's no callback function specified, but 0 makes more sense.)

-- PMM
Luc Michel Feb. 11, 2021, 10:14 a.m. UTC | #3
On 13:20 Tue 09 Feb     , Peter Maydell wrote:
> The Clock framework allows users to specify a callback which is
> called after the clock's period has been updated.  Some users need to
> also have a callback which is called before the clock period is
> updated.
> 
> As the first step in adding support for notifying Clock users on
> pre-update events, add an argument to the ClockCallback to specify
> what event is being notified, and add an argument to the various
> functions for registering a callback to specify which events are
> of interest to that callback.
> 
> Note that the documentation update renders correct the previously
> incorrect claim in 'Adding a new clock' that callbacks "will be
> explained in a following section".
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

With Hao's comment addressed:

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

> ---
> v1->v2: (suggested by Luc) instead of making callback functions check
> whether 'event' is one they are interested in, specify mask of
> interesting events at callback registration time.
> ---
>  docs/devel/clocks.rst            | 52 +++++++++++++++++++++++++++-----
>  include/hw/clock.h               | 21 +++++++++++--
>  include/hw/qdev-clock.h          | 17 ++++++++---
>  hw/adc/npcm7xx_adc.c             |  2 +-
>  hw/arm/armsse.c                  |  9 +++---
>  hw/char/cadence_uart.c           |  4 +--
>  hw/char/ibex_uart.c              |  4 +--
>  hw/char/pl011.c                  |  5 +--
>  hw/core/clock.c                  | 20 +++++++++---
>  hw/core/qdev-clock.c             |  8 +++--
>  hw/mips/cps.c                    |  2 +-
>  hw/misc/bcm2835_cprman.c         | 23 ++++++++------
>  hw/misc/npcm7xx_clk.c            | 26 +++++++++++++---
>  hw/misc/npcm7xx_pwm.c            |  2 +-
>  hw/misc/zynq_slcr.c              |  5 +--
>  hw/timer/cmsdk-apb-dualtimer.c   |  5 +--
>  hw/timer/cmsdk-apb-timer.c       |  4 +--
>  hw/timer/npcm7xx_timer.c         |  2 +-
>  hw/watchdog/cmsdk-apb-watchdog.c |  5 +--
>  target/mips/cpu.c                |  2 +-
>  20 files changed, 160 insertions(+), 58 deletions(-)
> 
> diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
> index c54bbb82409..cd344e3fe5d 100644
> --- a/docs/devel/clocks.rst
> +++ b/docs/devel/clocks.rst
> @@ -80,11 +80,12 @@ Adding clocks to a device must be done during the init method of the Device
>  instance.
>  
>  To add an input clock to a device, the function ``qdev_init_clock_in()``
> -must be used.  It takes the name, a callback and an opaque parameter
> -for the callback (this will be explained in a following section).
> +must be used.  It takes the name, a callback, an opaque parameter
> +for the callback and a mask of events when the callback should be
> +called (this will be explained in a following section).
>  Output is simpler; only the name is required. Typically::
>  
> -    qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev);
> +    qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev, ClockUpdate);
>      qdev_init_clock_out(DEVICE(dev), "clk_out");
>  
>  Both functions return the created Clock pointer, which should be saved in the
> @@ -113,7 +114,7 @@ output.
>       * callback for the input clock (see "Callback on input clock
>       * change" section below for more information).
>       */
> -    static void clk_in_callback(void *opaque);
> +    static void clk_in_callback(void *opaque, ClockEvent event);
>  
>      /*
>       * static array describing clocks:
> @@ -124,7 +125,7 @@ output.
>       *   the clk_out field of a MyDeviceState structure.
>       */
>      static const ClockPortInitArray mydev_clocks = {
> -        QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback),
> +        QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback, ClockUpdate),
>          QDEV_CLOCK_OUT(MyDeviceState, clk_out),
>          QDEV_CLOCK_END
>      };
> @@ -153,6 +154,40 @@ nothing else to do. This value will be propagated to other clocks when
>  connecting the clocks together and devices will fetch the right value during
>  the first reset.
>  
> +Clock callbacks
> +---------------
> +
> +You can give a clock a callback function in several ways:
> +
> + * by passing it as an argument to ``qdev_init_clock_in()``
> + * as an argument to the ``QDEV_CLOCK_IN()`` macro initializing an
> +   array to be passed to ``qdev_init_clocks()``
> + * by directly calling the ``clock_set_callback()`` function
> +
> +The callback function must be of this type:
> +
> +.. code-block:: c
> +
> +   typedef void ClockCallback(void *opaque, ClockEvent event);
> +
> +The ``opaque`` argument is the pointer passed to ``qdev_init_clock_in()``
> +or ``clock_set_callback()``; for ``qdev_init_clocks()`` it is the
> +``dev`` device pointer.
> +
> +The ``event`` argument specifies why the callback has been called.
> +When you register the callback you specify a mask of ClockEvent values
> +that you are interested in. The callback will only be called for those
> +events.
> +
> +The events currently supported are:
> +
> + * ``ClockUpdate`` : called after the input clock's period has changed
> +
> +Note that a clock only has one callback: it is not possible to register
> +different functions for different events. You must register a single
> +callback which listens for all of the events you are interested in,
> +and use the ``event`` argument to identify which event has happened.
> +
>  Retrieving clocks from a device
>  -------------------------------
>  
> @@ -231,7 +266,7 @@ object during device instance init. For example:
>  .. code-block:: c
>  
>      clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback,
> -                             dev);
> +                             dev, ClockUpdate);
>      /* set initial value to 10ns / 100MHz */
>      clock_set_ns(clk, 10);
>  
> @@ -267,11 +302,12 @@ next lowest integer. This implies some inaccuracy due to the rounding,
>  so be cautious about using it in calculations.
>  
>  It is also possible to register a callback on clock frequency changes.
> -Here is an example:
> +Here is an example, which assumes that ``clock_callback`` has been
> +specified as the callback for the ``ClockUpdate`` event:
>  
>  .. code-block:: c
>  
> -    void clock_callback(void *opaque) {
> +    void clock_callback(void *opaque, ClockEvent event) {
>          MyDeviceState *s = (MyDeviceState *) opaque;
>          /*
>           * 'opaque' is the argument passed to qdev_init_clock_in();
> diff --git a/include/hw/clock.h b/include/hw/clock.h
> index e5f45e2626d..5c73b4e7ae9 100644
> --- a/include/hw/clock.h
> +++ b/include/hw/clock.h
> @@ -22,7 +22,17 @@
>  #define TYPE_CLOCK "clock"
>  OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
>  
> -typedef void ClockCallback(void *opaque);
> +/*
> + * Argument to ClockCallback functions indicating why the callback
> + * has been called. A mask of these values logically ORed together
> + * is used to specify which events are interesting when the callback
> + * is registered, so these values must all be different bit values.
> + */
> +typedef enum ClockEvent {
> +    ClockUpdate = 1, /* Clock period has just updated */
> +} ClockEvent;
> +
> +typedef void ClockCallback(void *opaque, ClockEvent event);
>  
>  /*
>   * clock store a value representing the clock's period in 2^-32ns unit.
> @@ -50,6 +60,7 @@ typedef void ClockCallback(void *opaque);
>   * @canonical_path: clock path string cache (used for trace purpose)
>   * @callback: called when clock changes
>   * @callback_opaque: argument for @callback
> + * @callback_events: mask of events when callback should be called
>   * @source: source (or parent in clock tree) of the clock
>   * @children: list of clocks connected to this one (it is their source)
>   * @sibling: structure used to form a clock list
> @@ -67,6 +78,7 @@ struct Clock {
>      char *canonical_path;
>      ClockCallback *callback;
>      void *callback_opaque;
> +    int callback_events;
>  
>      /* Clocks are organized in a clock tree */
>      Clock *source;
> @@ -114,10 +126,15 @@ Clock *clock_new(Object *parent, const char *name);
>   * @clk: the clock to register the callback into
>   * @cb: the callback function
>   * @opaque: the argument to the callback
> + * @events: the events the callback should be called for
> + *          (logical OR of ClockEvent enum values)
>   *
>   * Register a callback called on every clock update.
> + * Note that a clock has only one callback: you cannot register
> + * different callback functions for different events.
>   */
> -void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque);
> +void clock_set_callback(Clock *clk, ClockCallback *cb,
> +                        void *opaque, int events);
>  
>  /**
>   * clock_clear_callback:
> diff --git a/include/hw/qdev-clock.h b/include/hw/qdev-clock.h
> index 64ca4d266f2..348ec363525 100644
> --- a/include/hw/qdev-clock.h
> +++ b/include/hw/qdev-clock.h
> @@ -22,6 +22,8 @@
>   * @name: the name of the clock (can't be NULL).
>   * @callback: optional callback to be called on update or NULL.
>   * @opaque: argument for the callback
> + * @events: the events the callback should be called for
> + *          (logical OR of ClockEvent enum values)
>   * @returns: a pointer to the newly added clock
>   *
>   * Add an input clock to device @dev as a clock named @name.
> @@ -29,7 +31,8 @@
>   * The callback will be called with @opaque as opaque parameter.
>   */
>  Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
> -                          ClockCallback *callback, void *opaque);
> +                          ClockCallback *callback, void *opaque,
> +                          int events);
>  
>  /**
>   * qdev_init_clock_out:
> @@ -105,6 +108,7 @@ void qdev_finalize_clocklist(DeviceState *dev);
>   * @output: indicates whether the clock is input or output
>   * @callback: for inputs, optional callback to be called on clock's update
>   * with device as opaque
> + * @callback_events: mask of ClockEvent values for when callback is called
>   * @offset: optional offset to store the ClockIn or ClockOut pointer in device
>   * state structure (0 means unused)
>   */
> @@ -112,6 +116,7 @@ struct ClockPortInitElem {
>      const char *name;
>      bool is_output;
>      ClockCallback *callback;
> +    int callback_events;
>      size_t offset;
>  };
>  
> @@ -119,10 +124,11 @@ struct ClockPortInitElem {
>      (offsetof(devstate, field) + \
>       type_check(Clock *, typeof_field(devstate, field)))
>  
> -#define QDEV_CLOCK(out_not_in, devstate, field, cb) { \
> +#define QDEV_CLOCK(out_not_in, devstate, field, cb, cbevents) {  \
>      .name = (stringify(field)), \
>      .is_output = out_not_in, \
>      .callback = cb, \
> +    .callback_events = cbevents, \
>      .offset = clock_offset_value(devstate, field), \
>  }
>  
> @@ -133,14 +139,15 @@ struct ClockPortInitElem {
>   * @field: a field in @_devstate (must be Clock*)
>   * @callback: (for input only) callback (or NULL) to be called with the device
>   * state as argument
> + * @cbevents: (for input only) ClockEvent mask for when callback is called
>   *
>   * The name of the clock will be derived from @field
>   */
> -#define QDEV_CLOCK_IN(devstate, field, callback) \
> -    QDEV_CLOCK(false, devstate, field, callback)
> +#define QDEV_CLOCK_IN(devstate, field, callback, cbevents)       \
> +    QDEV_CLOCK(false, devstate, field, callback, cbevents)
>  
>  #define QDEV_CLOCK_OUT(devstate, field) \
> -    QDEV_CLOCK(true, devstate, field, NULL)
> +    QDEV_CLOCK(true, devstate, field, NULL, 0)
>  
>  #define QDEV_CLOCK_END { .name = NULL }
>  
> diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
> index 870a6d50c27..573f4876dc6 100644
> --- a/hw/adc/npcm7xx_adc.c
> +++ b/hw/adc/npcm7xx_adc.c
> @@ -238,7 +238,7 @@ static void npcm7xx_adc_init(Object *obj)
>      memory_region_init_io(&s->iomem, obj, &npcm7xx_adc_ops, s,
>                            TYPE_NPCM7XX_ADC, 4 * KiB);
>      sysbus_init_mmio(sbd, &s->iomem);
> -    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
> +    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, ClockUpdate);
>  
>      for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) {
>          object_property_add_uint32_ptr(obj, "adci[*]",
> diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c
> index 26e1a8c95b6..fa155b72022 100644
> --- a/hw/arm/armsse.c
> +++ b/hw/arm/armsse.c
> @@ -230,9 +230,10 @@ static void armsse_forward_sec_resp_cfg(ARMSSE *s)
>      qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
>  }
>  
> -static void armsse_mainclk_update(void *opaque)
> +static void armsse_mainclk_update(void *opaque, ClockEvent event)
>  {
>      ARMSSE *s = ARM_SSE(opaque);
> +
>      /*
>       * Set system_clock_scale from our Clock input; this is what
>       * controls the tick rate of the CPU SysTick timer.
> @@ -251,8 +252,8 @@ static void armsse_init(Object *obj)
>      assert(info->num_cpus <= SSE_MAX_CPUS);
>  
>      s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK",
> -                                    armsse_mainclk_update, s);
> -    s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL);
> +                                    armsse_mainclk_update, s, ClockUpdate);
> +    s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL, 0);
>  
>      memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX);
>  
> @@ -1120,7 +1121,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
>      sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
>  
>      /* Set initial system_clock_scale from MAINCLK */
> -    armsse_mainclk_update(s);
> +    armsse_mainclk_update(s, ClockUpdate);
>  }
>  
>  static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
> diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
> index c603e14012a..ceb677bc5a8 100644
> --- a/hw/char/cadence_uart.c
> +++ b/hw/char/cadence_uart.c
> @@ -519,7 +519,7 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
>                               uart_event, NULL, s, NULL, true);
>  }
>  
> -static void cadence_uart_refclk_update(void *opaque)
> +static void cadence_uart_refclk_update(void *opaque, ClockEvent event)
>  {
>      CadenceUARTState *s = opaque;
>  
> @@ -537,7 +537,7 @@ static void cadence_uart_init(Object *obj)
>      sysbus_init_irq(sbd, &s->irq);
>  
>      s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
> -            cadence_uart_refclk_update, s);
> +                                   cadence_uart_refclk_update, s, ClockUpdate);
>      /* initialize the frequency in case the clock remains unconnected */
>      clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
>  
> diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c
> index 89f1182c9bf..edcaa30aded 100644
> --- a/hw/char/ibex_uart.c
> +++ b/hw/char/ibex_uart.c
> @@ -396,7 +396,7 @@ static void ibex_uart_write(void *opaque, hwaddr addr,
>      }
>  }
>  
> -static void ibex_uart_clk_update(void *opaque)
> +static void ibex_uart_clk_update(void *opaque, ClockEvent event)
>  {
>      IbexUartState *s = opaque;
>  
> @@ -466,7 +466,7 @@ static void ibex_uart_init(Object *obj)
>      IbexUartState *s = IBEX_UART(obj);
>  
>      s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock",
> -                                  ibex_uart_clk_update, s);
> +                                  ibex_uart_clk_update, s, ClockUpdate);
>      clock_set_hz(s->f_clk, IBEX_UART_CLOCK);
>  
>      sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);
> diff --git a/hw/char/pl011.c b/hw/char/pl011.c
> index ea4a4e52356..c5621a195ff 100644
> --- a/hw/char/pl011.c
> +++ b/hw/char/pl011.c
> @@ -309,7 +309,7 @@ static void pl011_event(void *opaque, QEMUChrEvent event)
>          pl011_put_fifo(opaque, 0x400);
>  }
>  
> -static void pl011_clock_update(void *opaque)
> +static void pl011_clock_update(void *opaque, ClockEvent event)
>  {
>      PL011State *s = PL011(opaque);
>  
> @@ -378,7 +378,8 @@ static void pl011_init(Object *obj)
>          sysbus_init_irq(sbd, &s->irq[i]);
>      }
>  
> -    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s);
> +    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s,
> +                                ClockUpdate);
>  
>      s->read_trigger = 1;
>      s->ifl = 0x12;
> diff --git a/hw/core/clock.c b/hw/core/clock.c
> index 76b5f468b6e..71dc1f4de65 100644
> --- a/hw/core/clock.c
> +++ b/hw/core/clock.c
> @@ -39,15 +39,16 @@ Clock *clock_new(Object *parent, const char *name)
>      return clk;
>  }
>  
> -void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque)
> +void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, int events)
>  {
>      clk->callback = cb;
>      clk->callback_opaque = opaque;
> +    clk->callback_events = events;
>  }
>  
>  void clock_clear_callback(Clock *clk)
>  {
> -    clock_set_callback(clk, NULL, NULL);
> +    clock_set_callback(clk, NULL, NULL, 0);
>  }
>  
>  bool clock_set(Clock *clk, uint64_t period)
> @@ -62,6 +63,17 @@ bool clock_set(Clock *clk, uint64_t period)
>      return true;
>  }
>  
> +static void clock_call_callback(Clock *clk, ClockEvent event)
> +{
> +    /*
> +     * Call the Clock's callback for this event, if it has one and
> +     * is interested in this event.
> +     */
> +    if (clk->callback && (clk->callback_events & event)) {
> +        clk->callback(clk->callback_opaque, event);
> +    }
> +}
> +
>  static void clock_propagate_period(Clock *clk, bool call_callbacks)
>  {
>      Clock *child;
> @@ -72,8 +84,8 @@ static void clock_propagate_period(Clock *clk, bool call_callbacks)
>              trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
>                                 CLOCK_PERIOD_TO_HZ(clk->period),
>                                 call_callbacks);
> -            if (call_callbacks && child->callback) {
> -                child->callback(child->callback_opaque);
> +            if (call_callbacks) {
> +                clock_call_callback(child, ClockUpdate);
>              }
>              clock_propagate_period(child, call_callbacks);
>          }
> diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
> index eb05f2a13ca..9c55ddc23ee 100644
> --- a/hw/core/qdev-clock.c
> +++ b/hw/core/qdev-clock.c
> @@ -111,7 +111,8 @@ Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
>  }
>  
>  Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
> -                            ClockCallback *callback, void *opaque)
> +                          ClockCallback *callback, void *opaque,
> +                          int events)
>  {
>      NamedClockList *ncl;
>  
> @@ -120,7 +121,7 @@ Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
>      ncl = qdev_init_clocklist(dev, name, false, NULL);
>  
>      if (callback) {
> -        clock_set_callback(ncl->clock, callback, opaque);
> +        clock_set_callback(ncl->clock, callback, opaque, events);
>      }
>      return ncl->clock;
>  }
> @@ -137,7 +138,8 @@ void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
>          if (elem->is_output) {
>              *clkp = qdev_init_clock_out(dev, elem->name);
>          } else {
> -            *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev);
> +            *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev,
> +                                       elem->callback_events);
>          }
>      }
>  }
> diff --git a/hw/mips/cps.c b/hw/mips/cps.c
> index 7a0d289efaf..2b436700ce6 100644
> --- a/hw/mips/cps.c
> +++ b/hw/mips/cps.c
> @@ -39,7 +39,7 @@ static void mips_cps_init(Object *obj)
>      SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
>      MIPSCPSState *s = MIPS_CPS(obj);
>  
> -    s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL);
> +    s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL, 0);
>      /*
>       * Cover entire address space as there do not seem to be any
>       * constraints for the base address of CPC and GIC.
> diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
> index 7e415a017c9..75e6c574d46 100644
> --- a/hw/misc/bcm2835_cprman.c
> +++ b/hw/misc/bcm2835_cprman.c
> @@ -107,7 +107,7 @@ static void pll_update(CprmanPllState *pll)
>      clock_update_hz(pll->out, freq);
>  }
>  
> -static void pll_xosc_update(void *opaque)
> +static void pll_xosc_update(void *opaque, ClockEvent event)
>  {
>      pll_update(CPRMAN_PLL(opaque));
>  }
> @@ -116,7 +116,8 @@ static void pll_init(Object *obj)
>  {
>      CprmanPllState *s = CPRMAN_PLL(obj);
>  
> -    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s);
> +    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update,
> +                                    s, ClockUpdate);
>      s->out = qdev_init_clock_out(DEVICE(s), "out");
>  }
>  
> @@ -209,7 +210,7 @@ static void pll_update_all_channels(BCM2835CprmanState *s,
>      }
>  }
>  
> -static void pll_channel_pll_in_update(void *opaque)
> +static void pll_channel_pll_in_update(void *opaque, ClockEvent event)
>  {
>      pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
>  }
> @@ -219,7 +220,8 @@ static void pll_channel_init(Object *obj)
>      CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj);
>  
>      s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
> -                                   pll_channel_pll_in_update, s);
> +                                   pll_channel_pll_in_update, s,
> +                                   ClockUpdate);
>      s->out = qdev_init_clock_out(DEVICE(s), "out");
>  }
>  
> @@ -303,7 +305,7 @@ static void clock_mux_update(CprmanClockMuxState *mux)
>      clock_update_hz(mux->out, freq);
>  }
>  
> -static void clock_mux_src_update(void *opaque)
> +static void clock_mux_src_update(void *opaque, ClockEvent event)
>  {
>      CprmanClockMuxState **backref = opaque;
>      CprmanClockMuxState *s = *backref;
> @@ -335,7 +337,8 @@ static void clock_mux_init(Object *obj)
>          s->backref[i] = s;
>          s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
>                                          clock_mux_src_update,
> -                                        &s->backref[i]);
> +                                        &s->backref[i],
> +                                        ClockUpdate);
>          g_free(name);
>      }
>  
> @@ -380,7 +383,7 @@ static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState *s)
>      clock_update(s->out, clock_get(src));
>  }
>  
> -static void dsi0hsck_mux_in_update(void *opaque)
> +static void dsi0hsck_mux_in_update(void *opaque, ClockEvent event)
>  {
>      dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque));
>  }
> @@ -390,8 +393,10 @@ static void dsi0hsck_mux_init(Object *obj)
>      CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj);
>      DeviceState *dev = DEVICE(obj);
>  
> -    s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update, s);
> -    s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update, s);
> +    s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update,
> +                                    s, ClockUpdate);
> +    s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update,
> +                                    s, ClockUpdate);
>      s->out = qdev_init_clock_out(DEVICE(s), "out");
>  }
>  
> diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
> index 0bcae9ce957..a1ee67dc9a1 100644
> --- a/hw/misc/npcm7xx_clk.c
> +++ b/hw/misc/npcm7xx_clk.c
> @@ -586,15 +586,26 @@ static const DividerInitInfo divider_init_info_list[] = {
>      },
>  };
>  
> +static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event)
> +{
> +    npcm7xx_clk_update_pll(opaque);
> +}
> +
>  static void npcm7xx_clk_pll_init(Object *obj)
>  {
>      NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj);
>  
>      pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in",
> -            npcm7xx_clk_update_pll, pll);
> +                                       npcm7xx_clk_update_pll_cb, pll,
> +                                       ClockUpdate);
>      pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out");
>  }
>  
> +static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event)
> +{
> +    npcm7xx_clk_update_sel(opaque);
> +}
> +
>  static void npcm7xx_clk_sel_init(Object *obj)
>  {
>      int i;
> @@ -603,16 +614,23 @@ static void npcm7xx_clk_sel_init(Object *obj)
>      for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) {
>          sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel),
>                  g_strdup_printf("clock-in[%d]", i),
> -                npcm7xx_clk_update_sel, sel);
> +                npcm7xx_clk_update_sel_cb, sel, ClockUpdate);
>      }
>      sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out");
>  }
> +
> +static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event)
> +{
> +    npcm7xx_clk_update_divider(opaque);
> +}
> +
>  static void npcm7xx_clk_divider_init(Object *obj)
>  {
>      NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj);
>  
>      div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in",
> -            npcm7xx_clk_update_divider, div);
> +                                       npcm7xx_clk_update_divider_cb,
> +                                       div, ClockUpdate);
>      div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out");
>  }
>  
> @@ -875,7 +893,7 @@ static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
>  {
>      int i;
>  
> -    s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL);
> +    s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0);
>  
>      /* First pass: init all converter modules */
>      QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS);
> diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c
> index dabcb6c0f95..ce192bb2741 100644
> --- a/hw/misc/npcm7xx_pwm.c
> +++ b/hw/misc/npcm7xx_pwm.c
> @@ -493,7 +493,7 @@ static void npcm7xx_pwm_init(Object *obj)
>      memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s,
>                            TYPE_NPCM7XX_PWM, 4 * KiB);
>      sysbus_init_mmio(sbd, &s->iomem);
> -    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
> +    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
>  
>      for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
>          object_property_add_uint32_ptr(obj, "freq[*]",
> diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c
> index 66504a9d3ab..c66d7db177d 100644
> --- a/hw/misc/zynq_slcr.c
> +++ b/hw/misc/zynq_slcr.c
> @@ -307,9 +307,10 @@ static void zynq_slcr_propagate_clocks(ZynqSLCRState *s)
>      clock_propagate(s->uart1_ref_clk);
>  }
>  
> -static void zynq_slcr_ps_clk_callback(void *opaque)
> +static void zynq_slcr_ps_clk_callback(void *opaque, ClockEvent event)
>  {
>      ZynqSLCRState *s = (ZynqSLCRState *) opaque;
> +
>      zynq_slcr_compute_clocks(s);
>      zynq_slcr_propagate_clocks(s);
>  }
> @@ -576,7 +577,7 @@ static const MemoryRegionOps slcr_ops = {
>  };
>  
>  static const ClockPortInitArray zynq_slcr_clocks = {
> -    QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback),
> +    QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback, ClockUpdate),
>      QDEV_CLOCK_OUT(ZynqSLCRState, uart0_ref_clk),
>      QDEV_CLOCK_OUT(ZynqSLCRState, uart1_ref_clk),
>      QDEV_CLOCK_END
> diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c
> index ef49f5852d3..d4a509c798e 100644
> --- a/hw/timer/cmsdk-apb-dualtimer.c
> +++ b/hw/timer/cmsdk-apb-dualtimer.c
> @@ -449,7 +449,7 @@ static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
>      s->timeritop = 0;
>  }
>  
> -static void cmsdk_apb_dualtimer_clk_update(void *opaque)
> +static void cmsdk_apb_dualtimer_clk_update(void *opaque, ClockEvent event)
>  {
>      CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
>      int i;
> @@ -478,7 +478,8 @@ static void cmsdk_apb_dualtimer_init(Object *obj)
>          sysbus_init_irq(sbd, &s->timermod[i].timerint);
>      }
>      s->timclk = qdev_init_clock_in(DEVICE(s), "TIMCLK",
> -                                   cmsdk_apb_dualtimer_clk_update, s);
> +                                   cmsdk_apb_dualtimer_clk_update, s,
> +                                   ClockUpdate);
>  }
>  
>  static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c
> index ee51ce3369c..68aa1a76360 100644
> --- a/hw/timer/cmsdk-apb-timer.c
> +++ b/hw/timer/cmsdk-apb-timer.c
> @@ -204,7 +204,7 @@ static void cmsdk_apb_timer_reset(DeviceState *dev)
>      ptimer_transaction_commit(s->timer);
>  }
>  
> -static void cmsdk_apb_timer_clk_update(void *opaque)
> +static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
>  {
>      CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
>  
> @@ -223,7 +223,7 @@ static void cmsdk_apb_timer_init(Object *obj)
>      sysbus_init_mmio(sbd, &s->iomem);
>      sysbus_init_irq(sbd, &s->timerint);
>      s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
> -                                 cmsdk_apb_timer_clk_update, s);
> +                                 cmsdk_apb_timer_clk_update, s, ClockUpdate);
>  }
>  
>  static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
> index 36e2c07db26..4efdf135b82 100644
> --- a/hw/timer/npcm7xx_timer.c
> +++ b/hw/timer/npcm7xx_timer.c
> @@ -627,7 +627,7 @@ static void npcm7xx_timer_init(Object *obj)
>      sysbus_init_mmio(sbd, &s->iomem);
>      qdev_init_gpio_out_named(dev, &w->reset_signal,
>              NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
> -    s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
> +    s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL, 0);
>  }
>  
>  static const VMStateDescription vmstate_npcm7xx_base_timer = {
> diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
> index 302f1711738..5a2cd46eb76 100644
> --- a/hw/watchdog/cmsdk-apb-watchdog.c
> +++ b/hw/watchdog/cmsdk-apb-watchdog.c
> @@ -310,7 +310,7 @@ static void cmsdk_apb_watchdog_reset(DeviceState *dev)
>      ptimer_transaction_commit(s->timer);
>  }
>  
> -static void cmsdk_apb_watchdog_clk_update(void *opaque)
> +static void cmsdk_apb_watchdog_clk_update(void *opaque, ClockEvent event)
>  {
>      CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
>  
> @@ -329,7 +329,8 @@ static void cmsdk_apb_watchdog_init(Object *obj)
>      sysbus_init_mmio(sbd, &s->iomem);
>      sysbus_init_irq(sbd, &s->wdogint);
>      s->wdogclk = qdev_init_clock_in(DEVICE(s), "WDOGCLK",
> -                                    cmsdk_apb_watchdog_clk_update, s);
> +                                    cmsdk_apb_watchdog_clk_update, s,
> +                                    ClockUpdate);
>  
>      s->is_luminary = false;
>      s->id = cmsdk_apb_watchdog_id;
> diff --git a/target/mips/cpu.c b/target/mips/cpu.c
> index ad163ead625..2f3d9d2ce2c 100644
> --- a/target/mips/cpu.c
> +++ b/target/mips/cpu.c
> @@ -636,7 +636,7 @@ static void mips_cpu_initfn(Object *obj)
>      MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj);
>  
>      cpu_set_cpustate_pointers(cpu);
> -    cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu);
> +    cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu, 0);
>      env->cpu_model = mcc->cpu_def;
>  }
>  
> -- 
> 2.20.1
> 

--
Philippe Mathieu-Daudé Feb. 11, 2021, 3:29 p.m. UTC | #4
Hi Peter,

On 2/9/21 2:20 PM, Peter Maydell wrote:
> The Clock framework allows users to specify a callback which is
> called after the clock's period has been updated.  Some users need to
> also have a callback which is called before the clock period is
> updated.
> 
> As the first step in adding support for notifying Clock users on
> pre-update events, add an argument to the ClockCallback to specify
> what event is being notified, and add an argument to the various
> functions for registering a callback to specify which events are
> of interest to that callback.
> 
> Note that the documentation update renders correct the previously
> incorrect claim in 'Adding a new clock' that callbacks "will be
> explained in a following section".
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> v1->v2: (suggested by Luc) instead of making callback functions check
> whether 'event' is one they are interested in, specify mask of
> interesting events at callback registration time.
> ---
>  docs/devel/clocks.rst            | 52 +++++++++++++++++++++++++++-----
>  include/hw/clock.h               | 21 +++++++++++--
>  include/hw/qdev-clock.h          | 17 ++++++++---
>  hw/adc/npcm7xx_adc.c             |  2 +-
>  hw/arm/armsse.c                  |  9 +++---
>  hw/char/cadence_uart.c           |  4 +--
>  hw/char/ibex_uart.c              |  4 +--
>  hw/char/pl011.c                  |  5 +--
>  hw/core/clock.c                  | 20 +++++++++---
>  hw/core/qdev-clock.c             |  8 +++--
>  hw/mips/cps.c                    |  2 +-
>  hw/misc/bcm2835_cprman.c         | 23 ++++++++------
>  hw/misc/npcm7xx_clk.c            | 26 +++++++++++++---
>  hw/misc/npcm7xx_pwm.c            |  2 +-
>  hw/misc/zynq_slcr.c              |  5 +--
>  hw/timer/cmsdk-apb-dualtimer.c   |  5 +--
>  hw/timer/cmsdk-apb-timer.c       |  4 +--
>  hw/timer/npcm7xx_timer.c         |  2 +-
>  hw/watchdog/cmsdk-apb-watchdog.c |  5 +--
>  target/mips/cpu.c                |  2 +-
>  20 files changed, 160 insertions(+), 58 deletions(-)

> diff --git a/include/hw/clock.h b/include/hw/clock.h
> index e5f45e2626d..5c73b4e7ae9 100644
> --- a/include/hw/clock.h
> +++ b/include/hw/clock.h
> @@ -22,7 +22,17 @@
>  #define TYPE_CLOCK "clock"
>  OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
>  
> -typedef void ClockCallback(void *opaque);
> +/*
> + * Argument to ClockCallback functions indicating why the callback
> + * has been called. A mask of these values logically ORed together
> + * is used to specify which events are interesting when the callback
> + * is registered, so these values must all be different bit values.
> + */
> +typedef enum ClockEvent {
> +    ClockUpdate = 1, /* Clock period has just updated */

Just wondering loudly (QEMU doesn't have enum naming conventions),
maybe rename  ClockUpdate -> ClockUpdateEvent to clarify.

> +} ClockEvent;
> +
> +typedef void ClockCallback(void *opaque, ClockEvent event);
>  
>  /*
>   * clock store a value representing the clock's period in 2^-32ns unit.
> @@ -50,6 +60,7 @@ typedef void ClockCallback(void *opaque);
>   * @canonical_path: clock path string cache (used for trace purpose)
>   * @callback: called when clock changes
>   * @callback_opaque: argument for @callback
> + * @callback_events: mask of events when callback should be called
>   * @source: source (or parent in clock tree) of the clock
>   * @children: list of clocks connected to this one (it is their source)
>   * @sibling: structure used to form a clock list
> @@ -67,6 +78,7 @@ struct Clock {
>      char *canonical_path;
>      ClockCallback *callback;
>      void *callback_opaque;
> +    int callback_events;

Isn't "unsigned" recommended for bit mask?

Otherwise (minor Hao Wu's NULL remark):
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
diff mbox series

Patch

diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
index c54bbb82409..cd344e3fe5d 100644
--- a/docs/devel/clocks.rst
+++ b/docs/devel/clocks.rst
@@ -80,11 +80,12 @@  Adding clocks to a device must be done during the init method of the Device
 instance.
 
 To add an input clock to a device, the function ``qdev_init_clock_in()``
-must be used.  It takes the name, a callback and an opaque parameter
-for the callback (this will be explained in a following section).
+must be used.  It takes the name, a callback, an opaque parameter
+for the callback and a mask of events when the callback should be
+called (this will be explained in a following section).
 Output is simpler; only the name is required. Typically::
 
-    qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev);
+    qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev, ClockUpdate);
     qdev_init_clock_out(DEVICE(dev), "clk_out");
 
 Both functions return the created Clock pointer, which should be saved in the
@@ -113,7 +114,7 @@  output.
      * callback for the input clock (see "Callback on input clock
      * change" section below for more information).
      */
-    static void clk_in_callback(void *opaque);
+    static void clk_in_callback(void *opaque, ClockEvent event);
 
     /*
      * static array describing clocks:
@@ -124,7 +125,7 @@  output.
      *   the clk_out field of a MyDeviceState structure.
      */
     static const ClockPortInitArray mydev_clocks = {
-        QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback),
+        QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback, ClockUpdate),
         QDEV_CLOCK_OUT(MyDeviceState, clk_out),
         QDEV_CLOCK_END
     };
@@ -153,6 +154,40 @@  nothing else to do. This value will be propagated to other clocks when
 connecting the clocks together and devices will fetch the right value during
 the first reset.
 
+Clock callbacks
+---------------
+
+You can give a clock a callback function in several ways:
+
+ * by passing it as an argument to ``qdev_init_clock_in()``
+ * as an argument to the ``QDEV_CLOCK_IN()`` macro initializing an
+   array to be passed to ``qdev_init_clocks()``
+ * by directly calling the ``clock_set_callback()`` function
+
+The callback function must be of this type:
+
+.. code-block:: c
+
+   typedef void ClockCallback(void *opaque, ClockEvent event);
+
+The ``opaque`` argument is the pointer passed to ``qdev_init_clock_in()``
+or ``clock_set_callback()``; for ``qdev_init_clocks()`` it is the
+``dev`` device pointer.
+
+The ``event`` argument specifies why the callback has been called.
+When you register the callback you specify a mask of ClockEvent values
+that you are interested in. The callback will only be called for those
+events.
+
+The events currently supported are:
+
+ * ``ClockUpdate`` : called after the input clock's period has changed
+
+Note that a clock only has one callback: it is not possible to register
+different functions for different events. You must register a single
+callback which listens for all of the events you are interested in,
+and use the ``event`` argument to identify which event has happened.
+
 Retrieving clocks from a device
 -------------------------------
 
@@ -231,7 +266,7 @@  object during device instance init. For example:
 .. code-block:: c
 
     clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback,
-                             dev);
+                             dev, ClockUpdate);
     /* set initial value to 10ns / 100MHz */
     clock_set_ns(clk, 10);
 
@@ -267,11 +302,12 @@  next lowest integer. This implies some inaccuracy due to the rounding,
 so be cautious about using it in calculations.
 
 It is also possible to register a callback on clock frequency changes.
-Here is an example:
+Here is an example, which assumes that ``clock_callback`` has been
+specified as the callback for the ``ClockUpdate`` event:
 
 .. code-block:: c
 
-    void clock_callback(void *opaque) {
+    void clock_callback(void *opaque, ClockEvent event) {
         MyDeviceState *s = (MyDeviceState *) opaque;
         /*
          * 'opaque' is the argument passed to qdev_init_clock_in();
diff --git a/include/hw/clock.h b/include/hw/clock.h
index e5f45e2626d..5c73b4e7ae9 100644
--- a/include/hw/clock.h
+++ b/include/hw/clock.h
@@ -22,7 +22,17 @@ 
 #define TYPE_CLOCK "clock"
 OBJECT_DECLARE_SIMPLE_TYPE(Clock, CLOCK)
 
-typedef void ClockCallback(void *opaque);
+/*
+ * Argument to ClockCallback functions indicating why the callback
+ * has been called. A mask of these values logically ORed together
+ * is used to specify which events are interesting when the callback
+ * is registered, so these values must all be different bit values.
+ */
+typedef enum ClockEvent {
+    ClockUpdate = 1, /* Clock period has just updated */
+} ClockEvent;
+
+typedef void ClockCallback(void *opaque, ClockEvent event);
 
 /*
  * clock store a value representing the clock's period in 2^-32ns unit.
@@ -50,6 +60,7 @@  typedef void ClockCallback(void *opaque);
  * @canonical_path: clock path string cache (used for trace purpose)
  * @callback: called when clock changes
  * @callback_opaque: argument for @callback
+ * @callback_events: mask of events when callback should be called
  * @source: source (or parent in clock tree) of the clock
  * @children: list of clocks connected to this one (it is their source)
  * @sibling: structure used to form a clock list
@@ -67,6 +78,7 @@  struct Clock {
     char *canonical_path;
     ClockCallback *callback;
     void *callback_opaque;
+    int callback_events;
 
     /* Clocks are organized in a clock tree */
     Clock *source;
@@ -114,10 +126,15 @@  Clock *clock_new(Object *parent, const char *name);
  * @clk: the clock to register the callback into
  * @cb: the callback function
  * @opaque: the argument to the callback
+ * @events: the events the callback should be called for
+ *          (logical OR of ClockEvent enum values)
  *
  * Register a callback called on every clock update.
+ * Note that a clock has only one callback: you cannot register
+ * different callback functions for different events.
  */
-void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque);
+void clock_set_callback(Clock *clk, ClockCallback *cb,
+                        void *opaque, int events);
 
 /**
  * clock_clear_callback:
diff --git a/include/hw/qdev-clock.h b/include/hw/qdev-clock.h
index 64ca4d266f2..348ec363525 100644
--- a/include/hw/qdev-clock.h
+++ b/include/hw/qdev-clock.h
@@ -22,6 +22,8 @@ 
  * @name: the name of the clock (can't be NULL).
  * @callback: optional callback to be called on update or NULL.
  * @opaque: argument for the callback
+ * @events: the events the callback should be called for
+ *          (logical OR of ClockEvent enum values)
  * @returns: a pointer to the newly added clock
  *
  * Add an input clock to device @dev as a clock named @name.
@@ -29,7 +31,8 @@ 
  * The callback will be called with @opaque as opaque parameter.
  */
 Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
-                          ClockCallback *callback, void *opaque);
+                          ClockCallback *callback, void *opaque,
+                          int events);
 
 /**
  * qdev_init_clock_out:
@@ -105,6 +108,7 @@  void qdev_finalize_clocklist(DeviceState *dev);
  * @output: indicates whether the clock is input or output
  * @callback: for inputs, optional callback to be called on clock's update
  * with device as opaque
+ * @callback_events: mask of ClockEvent values for when callback is called
  * @offset: optional offset to store the ClockIn or ClockOut pointer in device
  * state structure (0 means unused)
  */
@@ -112,6 +116,7 @@  struct ClockPortInitElem {
     const char *name;
     bool is_output;
     ClockCallback *callback;
+    int callback_events;
     size_t offset;
 };
 
@@ -119,10 +124,11 @@  struct ClockPortInitElem {
     (offsetof(devstate, field) + \
      type_check(Clock *, typeof_field(devstate, field)))
 
-#define QDEV_CLOCK(out_not_in, devstate, field, cb) { \
+#define QDEV_CLOCK(out_not_in, devstate, field, cb, cbevents) {  \
     .name = (stringify(field)), \
     .is_output = out_not_in, \
     .callback = cb, \
+    .callback_events = cbevents, \
     .offset = clock_offset_value(devstate, field), \
 }
 
@@ -133,14 +139,15 @@  struct ClockPortInitElem {
  * @field: a field in @_devstate (must be Clock*)
  * @callback: (for input only) callback (or NULL) to be called with the device
  * state as argument
+ * @cbevents: (for input only) ClockEvent mask for when callback is called
  *
  * The name of the clock will be derived from @field
  */
-#define QDEV_CLOCK_IN(devstate, field, callback) \
-    QDEV_CLOCK(false, devstate, field, callback)
+#define QDEV_CLOCK_IN(devstate, field, callback, cbevents)       \
+    QDEV_CLOCK(false, devstate, field, callback, cbevents)
 
 #define QDEV_CLOCK_OUT(devstate, field) \
-    QDEV_CLOCK(true, devstate, field, NULL)
+    QDEV_CLOCK(true, devstate, field, NULL, 0)
 
 #define QDEV_CLOCK_END { .name = NULL }
 
diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
index 870a6d50c27..573f4876dc6 100644
--- a/hw/adc/npcm7xx_adc.c
+++ b/hw/adc/npcm7xx_adc.c
@@ -238,7 +238,7 @@  static void npcm7xx_adc_init(Object *obj)
     memory_region_init_io(&s->iomem, obj, &npcm7xx_adc_ops, s,
                           TYPE_NPCM7XX_ADC, 4 * KiB);
     sysbus_init_mmio(sbd, &s->iomem);
-    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
+    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, ClockUpdate);
 
     for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) {
         object_property_add_uint32_ptr(obj, "adci[*]",
diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c
index 26e1a8c95b6..fa155b72022 100644
--- a/hw/arm/armsse.c
+++ b/hw/arm/armsse.c
@@ -230,9 +230,10 @@  static void armsse_forward_sec_resp_cfg(ARMSSE *s)
     qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
 }
 
-static void armsse_mainclk_update(void *opaque)
+static void armsse_mainclk_update(void *opaque, ClockEvent event)
 {
     ARMSSE *s = ARM_SSE(opaque);
+
     /*
      * Set system_clock_scale from our Clock input; this is what
      * controls the tick rate of the CPU SysTick timer.
@@ -251,8 +252,8 @@  static void armsse_init(Object *obj)
     assert(info->num_cpus <= SSE_MAX_CPUS);
 
     s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK",
-                                    armsse_mainclk_update, s);
-    s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL);
+                                    armsse_mainclk_update, s, ClockUpdate);
+    s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL, 0);
 
     memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX);
 
@@ -1120,7 +1121,7 @@  static void armsse_realize(DeviceState *dev, Error **errp)
     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
 
     /* Set initial system_clock_scale from MAINCLK */
-    armsse_mainclk_update(s);
+    armsse_mainclk_update(s, ClockUpdate);
 }
 
 static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index c603e14012a..ceb677bc5a8 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -519,7 +519,7 @@  static void cadence_uart_realize(DeviceState *dev, Error **errp)
                              uart_event, NULL, s, NULL, true);
 }
 
-static void cadence_uart_refclk_update(void *opaque)
+static void cadence_uart_refclk_update(void *opaque, ClockEvent event)
 {
     CadenceUARTState *s = opaque;
 
@@ -537,7 +537,7 @@  static void cadence_uart_init(Object *obj)
     sysbus_init_irq(sbd, &s->irq);
 
     s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
-            cadence_uart_refclk_update, s);
+                                   cadence_uart_refclk_update, s, ClockUpdate);
     /* initialize the frequency in case the clock remains unconnected */
     clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
 
diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c
index 89f1182c9bf..edcaa30aded 100644
--- a/hw/char/ibex_uart.c
+++ b/hw/char/ibex_uart.c
@@ -396,7 +396,7 @@  static void ibex_uart_write(void *opaque, hwaddr addr,
     }
 }
 
-static void ibex_uart_clk_update(void *opaque)
+static void ibex_uart_clk_update(void *opaque, ClockEvent event)
 {
     IbexUartState *s = opaque;
 
@@ -466,7 +466,7 @@  static void ibex_uart_init(Object *obj)
     IbexUartState *s = IBEX_UART(obj);
 
     s->f_clk = qdev_init_clock_in(DEVICE(obj), "f_clock",
-                                  ibex_uart_clk_update, s);
+                                  ibex_uart_clk_update, s, ClockUpdate);
     clock_set_hz(s->f_clk, IBEX_UART_CLOCK);
 
     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->tx_watermark);
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
index ea4a4e52356..c5621a195ff 100644
--- a/hw/char/pl011.c
+++ b/hw/char/pl011.c
@@ -309,7 +309,7 @@  static void pl011_event(void *opaque, QEMUChrEvent event)
         pl011_put_fifo(opaque, 0x400);
 }
 
-static void pl011_clock_update(void *opaque)
+static void pl011_clock_update(void *opaque, ClockEvent event)
 {
     PL011State *s = PL011(opaque);
 
@@ -378,7 +378,8 @@  static void pl011_init(Object *obj)
         sysbus_init_irq(sbd, &s->irq[i]);
     }
 
-    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s);
+    s->clk = qdev_init_clock_in(DEVICE(obj), "clk", pl011_clock_update, s,
+                                ClockUpdate);
 
     s->read_trigger = 1;
     s->ifl = 0x12;
diff --git a/hw/core/clock.c b/hw/core/clock.c
index 76b5f468b6e..71dc1f4de65 100644
--- a/hw/core/clock.c
+++ b/hw/core/clock.c
@@ -39,15 +39,16 @@  Clock *clock_new(Object *parent, const char *name)
     return clk;
 }
 
-void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque)
+void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, int events)
 {
     clk->callback = cb;
     clk->callback_opaque = opaque;
+    clk->callback_events = events;
 }
 
 void clock_clear_callback(Clock *clk)
 {
-    clock_set_callback(clk, NULL, NULL);
+    clock_set_callback(clk, NULL, NULL, 0);
 }
 
 bool clock_set(Clock *clk, uint64_t period)
@@ -62,6 +63,17 @@  bool clock_set(Clock *clk, uint64_t period)
     return true;
 }
 
+static void clock_call_callback(Clock *clk, ClockEvent event)
+{
+    /*
+     * Call the Clock's callback for this event, if it has one and
+     * is interested in this event.
+     */
+    if (clk->callback && (clk->callback_events & event)) {
+        clk->callback(clk->callback_opaque, event);
+    }
+}
+
 static void clock_propagate_period(Clock *clk, bool call_callbacks)
 {
     Clock *child;
@@ -72,8 +84,8 @@  static void clock_propagate_period(Clock *clk, bool call_callbacks)
             trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
                                CLOCK_PERIOD_TO_HZ(clk->period),
                                call_callbacks);
-            if (call_callbacks && child->callback) {
-                child->callback(child->callback_opaque);
+            if (call_callbacks) {
+                clock_call_callback(child, ClockUpdate);
             }
             clock_propagate_period(child, call_callbacks);
         }
diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
index eb05f2a13ca..9c55ddc23ee 100644
--- a/hw/core/qdev-clock.c
+++ b/hw/core/qdev-clock.c
@@ -111,7 +111,8 @@  Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
 }
 
 Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
-                            ClockCallback *callback, void *opaque)
+                          ClockCallback *callback, void *opaque,
+                          int events)
 {
     NamedClockList *ncl;
 
@@ -120,7 +121,7 @@  Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
     ncl = qdev_init_clocklist(dev, name, false, NULL);
 
     if (callback) {
-        clock_set_callback(ncl->clock, callback, opaque);
+        clock_set_callback(ncl->clock, callback, opaque, events);
     }
     return ncl->clock;
 }
@@ -137,7 +138,8 @@  void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
         if (elem->is_output) {
             *clkp = qdev_init_clock_out(dev, elem->name);
         } else {
-            *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev);
+            *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev,
+                                       elem->callback_events);
         }
     }
 }
diff --git a/hw/mips/cps.c b/hw/mips/cps.c
index 7a0d289efaf..2b436700ce6 100644
--- a/hw/mips/cps.c
+++ b/hw/mips/cps.c
@@ -39,7 +39,7 @@  static void mips_cps_init(Object *obj)
     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
     MIPSCPSState *s = MIPS_CPS(obj);
 
-    s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL);
+    s->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, NULL, 0);
     /*
      * Cover entire address space as there do not seem to be any
      * constraints for the base address of CPC and GIC.
diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c
index 7e415a017c9..75e6c574d46 100644
--- a/hw/misc/bcm2835_cprman.c
+++ b/hw/misc/bcm2835_cprman.c
@@ -107,7 +107,7 @@  static void pll_update(CprmanPllState *pll)
     clock_update_hz(pll->out, freq);
 }
 
-static void pll_xosc_update(void *opaque)
+static void pll_xosc_update(void *opaque, ClockEvent event)
 {
     pll_update(CPRMAN_PLL(opaque));
 }
@@ -116,7 +116,8 @@  static void pll_init(Object *obj)
 {
     CprmanPllState *s = CPRMAN_PLL(obj);
 
-    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update, s);
+    s->xosc_in = qdev_init_clock_in(DEVICE(s), "xosc-in", pll_xosc_update,
+                                    s, ClockUpdate);
     s->out = qdev_init_clock_out(DEVICE(s), "out");
 }
 
@@ -209,7 +210,7 @@  static void pll_update_all_channels(BCM2835CprmanState *s,
     }
 }
 
-static void pll_channel_pll_in_update(void *opaque)
+static void pll_channel_pll_in_update(void *opaque, ClockEvent event)
 {
     pll_channel_update(CPRMAN_PLL_CHANNEL(opaque));
 }
@@ -219,7 +220,8 @@  static void pll_channel_init(Object *obj)
     CprmanPllChannelState *s = CPRMAN_PLL_CHANNEL(obj);
 
     s->pll_in = qdev_init_clock_in(DEVICE(s), "pll-in",
-                                   pll_channel_pll_in_update, s);
+                                   pll_channel_pll_in_update, s,
+                                   ClockUpdate);
     s->out = qdev_init_clock_out(DEVICE(s), "out");
 }
 
@@ -303,7 +305,7 @@  static void clock_mux_update(CprmanClockMuxState *mux)
     clock_update_hz(mux->out, freq);
 }
 
-static void clock_mux_src_update(void *opaque)
+static void clock_mux_src_update(void *opaque, ClockEvent event)
 {
     CprmanClockMuxState **backref = opaque;
     CprmanClockMuxState *s = *backref;
@@ -335,7 +337,8 @@  static void clock_mux_init(Object *obj)
         s->backref[i] = s;
         s->srcs[i] = qdev_init_clock_in(DEVICE(s), name,
                                         clock_mux_src_update,
-                                        &s->backref[i]);
+                                        &s->backref[i],
+                                        ClockUpdate);
         g_free(name);
     }
 
@@ -380,7 +383,7 @@  static void dsi0hsck_mux_update(CprmanDsi0HsckMuxState *s)
     clock_update(s->out, clock_get(src));
 }
 
-static void dsi0hsck_mux_in_update(void *opaque)
+static void dsi0hsck_mux_in_update(void *opaque, ClockEvent event)
 {
     dsi0hsck_mux_update(CPRMAN_DSI0HSCK_MUX(opaque));
 }
@@ -390,8 +393,10 @@  static void dsi0hsck_mux_init(Object *obj)
     CprmanDsi0HsckMuxState *s = CPRMAN_DSI0HSCK_MUX(obj);
     DeviceState *dev = DEVICE(obj);
 
-    s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update, s);
-    s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update, s);
+    s->plla_in = qdev_init_clock_in(dev, "plla-in", dsi0hsck_mux_in_update,
+                                    s, ClockUpdate);
+    s->plld_in = qdev_init_clock_in(dev, "plld-in", dsi0hsck_mux_in_update,
+                                    s, ClockUpdate);
     s->out = qdev_init_clock_out(DEVICE(s), "out");
 }
 
diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 0bcae9ce957..a1ee67dc9a1 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -586,15 +586,26 @@  static const DividerInitInfo divider_init_info_list[] = {
     },
 };
 
+static void npcm7xx_clk_update_pll_cb(void *opaque, ClockEvent event)
+{
+    npcm7xx_clk_update_pll(opaque);
+}
+
 static void npcm7xx_clk_pll_init(Object *obj)
 {
     NPCM7xxClockPLLState *pll = NPCM7XX_CLOCK_PLL(obj);
 
     pll->clock_in = qdev_init_clock_in(DEVICE(pll), "clock-in",
-            npcm7xx_clk_update_pll, pll);
+                                       npcm7xx_clk_update_pll_cb, pll,
+                                       ClockUpdate);
     pll->clock_out = qdev_init_clock_out(DEVICE(pll), "clock-out");
 }
 
+static void npcm7xx_clk_update_sel_cb(void *opaque, ClockEvent event)
+{
+    npcm7xx_clk_update_sel(opaque);
+}
+
 static void npcm7xx_clk_sel_init(Object *obj)
 {
     int i;
@@ -603,16 +614,23 @@  static void npcm7xx_clk_sel_init(Object *obj)
     for (i = 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) {
         sel->clock_in[i] = qdev_init_clock_in(DEVICE(sel),
                 g_strdup_printf("clock-in[%d]", i),
-                npcm7xx_clk_update_sel, sel);
+                npcm7xx_clk_update_sel_cb, sel, ClockUpdate);
     }
     sel->clock_out = qdev_init_clock_out(DEVICE(sel), "clock-out");
 }
+
+static void npcm7xx_clk_update_divider_cb(void *opaque, ClockEvent event)
+{
+    npcm7xx_clk_update_divider(opaque);
+}
+
 static void npcm7xx_clk_divider_init(Object *obj)
 {
     NPCM7xxClockDividerState *div = NPCM7XX_CLOCK_DIVIDER(obj);
 
     div->clock_in = qdev_init_clock_in(DEVICE(div), "clock-in",
-            npcm7xx_clk_update_divider, div);
+                                       npcm7xx_clk_update_divider_cb,
+                                       div, ClockUpdate);
     div->clock_out = qdev_init_clock_out(DEVICE(div), "clock-out");
 }
 
@@ -875,7 +893,7 @@  static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s)
 {
     int i;
 
-    s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL);
+    s->clkref = qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL, 0);
 
     /* First pass: init all converter modules */
     QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) != NPCM7XX_CLOCK_NR_PLLS);
diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c
index dabcb6c0f95..ce192bb2741 100644
--- a/hw/misc/npcm7xx_pwm.c
+++ b/hw/misc/npcm7xx_pwm.c
@@ -493,7 +493,7 @@  static void npcm7xx_pwm_init(Object *obj)
     memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s,
                           TYPE_NPCM7XX_PWM, 4 * KiB);
     sysbus_init_mmio(sbd, &s->iomem);
-    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
+    s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL, 0);
 
     for (i = 0; i < NPCM7XX_PWM_PER_MODULE; ++i) {
         object_property_add_uint32_ptr(obj, "freq[*]",
diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c
index 66504a9d3ab..c66d7db177d 100644
--- a/hw/misc/zynq_slcr.c
+++ b/hw/misc/zynq_slcr.c
@@ -307,9 +307,10 @@  static void zynq_slcr_propagate_clocks(ZynqSLCRState *s)
     clock_propagate(s->uart1_ref_clk);
 }
 
-static void zynq_slcr_ps_clk_callback(void *opaque)
+static void zynq_slcr_ps_clk_callback(void *opaque, ClockEvent event)
 {
     ZynqSLCRState *s = (ZynqSLCRState *) opaque;
+
     zynq_slcr_compute_clocks(s);
     zynq_slcr_propagate_clocks(s);
 }
@@ -576,7 +577,7 @@  static const MemoryRegionOps slcr_ops = {
 };
 
 static const ClockPortInitArray zynq_slcr_clocks = {
-    QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback),
+    QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback, ClockUpdate),
     QDEV_CLOCK_OUT(ZynqSLCRState, uart0_ref_clk),
     QDEV_CLOCK_OUT(ZynqSLCRState, uart1_ref_clk),
     QDEV_CLOCK_END
diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c
index ef49f5852d3..d4a509c798e 100644
--- a/hw/timer/cmsdk-apb-dualtimer.c
+++ b/hw/timer/cmsdk-apb-dualtimer.c
@@ -449,7 +449,7 @@  static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
     s->timeritop = 0;
 }
 
-static void cmsdk_apb_dualtimer_clk_update(void *opaque)
+static void cmsdk_apb_dualtimer_clk_update(void *opaque, ClockEvent event)
 {
     CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
     int i;
@@ -478,7 +478,8 @@  static void cmsdk_apb_dualtimer_init(Object *obj)
         sysbus_init_irq(sbd, &s->timermod[i].timerint);
     }
     s->timclk = qdev_init_clock_in(DEVICE(s), "TIMCLK",
-                                   cmsdk_apb_dualtimer_clk_update, s);
+                                   cmsdk_apb_dualtimer_clk_update, s,
+                                   ClockUpdate);
 }
 
 static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)
diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c
index ee51ce3369c..68aa1a76360 100644
--- a/hw/timer/cmsdk-apb-timer.c
+++ b/hw/timer/cmsdk-apb-timer.c
@@ -204,7 +204,7 @@  static void cmsdk_apb_timer_reset(DeviceState *dev)
     ptimer_transaction_commit(s->timer);
 }
 
-static void cmsdk_apb_timer_clk_update(void *opaque)
+static void cmsdk_apb_timer_clk_update(void *opaque, ClockEvent event)
 {
     CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
 
@@ -223,7 +223,7 @@  static void cmsdk_apb_timer_init(Object *obj)
     sysbus_init_mmio(sbd, &s->iomem);
     sysbus_init_irq(sbd, &s->timerint);
     s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
-                                 cmsdk_apb_timer_clk_update, s);
+                                 cmsdk_apb_timer_clk_update, s, ClockUpdate);
 }
 
 static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
index 36e2c07db26..4efdf135b82 100644
--- a/hw/timer/npcm7xx_timer.c
+++ b/hw/timer/npcm7xx_timer.c
@@ -627,7 +627,7 @@  static void npcm7xx_timer_init(Object *obj)
     sysbus_init_mmio(sbd, &s->iomem);
     qdev_init_gpio_out_named(dev, &w->reset_signal,
             NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
-    s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
+    s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL, 0);
 }
 
 static const VMStateDescription vmstate_npcm7xx_base_timer = {
diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
index 302f1711738..5a2cd46eb76 100644
--- a/hw/watchdog/cmsdk-apb-watchdog.c
+++ b/hw/watchdog/cmsdk-apb-watchdog.c
@@ -310,7 +310,7 @@  static void cmsdk_apb_watchdog_reset(DeviceState *dev)
     ptimer_transaction_commit(s->timer);
 }
 
-static void cmsdk_apb_watchdog_clk_update(void *opaque)
+static void cmsdk_apb_watchdog_clk_update(void *opaque, ClockEvent event)
 {
     CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
 
@@ -329,7 +329,8 @@  static void cmsdk_apb_watchdog_init(Object *obj)
     sysbus_init_mmio(sbd, &s->iomem);
     sysbus_init_irq(sbd, &s->wdogint);
     s->wdogclk = qdev_init_clock_in(DEVICE(s), "WDOGCLK",
-                                    cmsdk_apb_watchdog_clk_update, s);
+                                    cmsdk_apb_watchdog_clk_update, s,
+                                    ClockUpdate);
 
     s->is_luminary = false;
     s->id = cmsdk_apb_watchdog_id;
diff --git a/target/mips/cpu.c b/target/mips/cpu.c
index ad163ead625..2f3d9d2ce2c 100644
--- a/target/mips/cpu.c
+++ b/target/mips/cpu.c
@@ -636,7 +636,7 @@  static void mips_cpu_initfn(Object *obj)
     MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(obj);
 
     cpu_set_cpustate_pointers(cpu);
-    cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu);
+    cpu->clock = qdev_init_clock_in(DEVICE(obj), "clk-in", NULL, cpu, 0);
     env->cpu_model = mcc->cpu_def;
 }