[PATCH qemu] hw/timer: Add clock value matching support to aspeed_timer

Joel Stanley joel at jms.id.au
Mon May 23 17:30:15 AEST 2016


On Fri, May 20, 2016 at 12:34 PM, Andrew Jeffery <andrew at aj.id.au> wrote:
> Clock value matching allows Linux to boot with CONFIG_NO_HZ_IDLE=y on
> the palmetto-bmc machine. Two match registers are provided for each
> timer.

Thanks for implementing this.

We also boot much faster now.

>
> Signed-off-by: Andrew Jeffery <andrew at aj.id.au>
> ---
>  hw/timer/aspeed_timer.c         | 132 +++++++++++++++++++++++++++++-----------
>  include/hw/timer/aspeed_timer.h |   5 +-
>  2 files changed, 101 insertions(+), 36 deletions(-)
>
> diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
> index 51e8303cdaa3..e88234e7823f 100644
> --- a/hw/timer/aspeed_timer.c
> +++ b/hw/timer/aspeed_timer.c
> @@ -9,13 +9,12 @@
>   * the COPYING file in the top-level directory.
>   */
>
> +#include <math.h>
>  #include "qemu/osdep.h"
> -#include "hw/ptimer.h"
>  #include "hw/sysbus.h"
>  #include "hw/timer/aspeed_timer.h"
>  #include "qemu-common.h"
>  #include "qemu/bitops.h"
> -#include "qemu/main-loop.h"
>  #include "qemu/timer.h"
>  #include "trace.h"
>
> @@ -76,21 +75,96 @@ static inline bool timer_can_pulse(AspeedTimer *t)
>      return t->id >= TIMER_FIRST_CAP_PULSE;
>  }
>
> +static inline bool timer_external_clock(AspeedTimer *t)
> +{
> +    return timer_ctrl_status(t, op_external_clock);
> +}
> +
> +static inline double calculate_period(struct AspeedTimer *t)
> +{
> +    double rate;
> +
> +    rate = timer_external_clock(t) ? TIMER_CLOCK_EXT_HZ : TIMER_CLOCK_APB_HZ;

Would it make more sense return the rate from timer_external_clock
(perhaps get_timer_clock?).

> +
> +    return NANOSECONDS_PER_SECOND / rate;
> +}
> +
> +static inline uint32_t calculate_ticks(struct AspeedTimer *t, uint64_t now_ns)
> +{
> +    double ticks;
> +
> +    ticks = floor((now_ns - t->start) / calculate_period(t));
> +
> +    return t->reload - MIN(t->reload, (uint32_t) ticks);

Ugh, Casty McCastface.

Perhaps make ticks the uint32_t:

 uint32_t ticks = (uint32_t)floor( ... );

> +}
> +
> +static inline uint64_t calculate_time(struct AspeedTimer *t, uint32_t ticks)
> +{
> +    double delta_ns;
> +
> +    ticks = MIN(t->reload, ticks);
> +    delta_ns = floor((t->reload - ticks) * calculate_period(t));

As above.

> +
> +    return t->start + (uint64_t) delta_ns;
> +}
> +
> +static uint64_t calculate_next(struct AspeedTimer *t)
> +{
> +    uint64_t now;
> +    uint64_t next;
> +    int i;
> +    uint64_t seq[] = {
> +        MAX(t->match[0], t->match[1]),
> +        MIN(t->match[0], t->match[1]),
> +        0,
> +    };

Perhaps a comment on why you chose to do it like this? I only
understand because you explained it to me.

> +
> +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    for (i = 0; i < ARRAY_SIZE(seq); i++) {
> +        next = calculate_time(t, seq[i]);
> +        if (now < next) {
> +            return next;
> +        }
> +    }
> +
> +    {
> +        uint64_t reload_ns;
> +
> +        reload_ns = (uint64_t) floor(t->reload * calculate_period(t));
> +        t->start = now - ((now - t->start) % reload_ns);
> +    }

Why is this scoped like this?

> +
> +    return calculate_next(t);
> +}
> +
>  static void aspeed_timer_expire(void *opaque)
>  {
>      AspeedTimer *t = opaque;
> +    bool interrupt = false;
> +    uint32_t ticks;
>
> -    /* Only support interrupts on match values of zero for the moment - this is
> -     * sufficient to boot an aspeed_defconfig Linux kernel.
> -     *
> -     * TODO: matching on arbitrary values (see e.g. hw/timer/a9gtimer.c)
> -     */
> -    bool match = !(t->match[0] && t->match[1]);
> -    bool interrupt = timer_overflow_interrupt(t) || match;
> -    if (timer_enabled(t) && interrupt) {
> +    if (!timer_enabled(t)) {
> +        return;
> +    }
> +
> +    ticks = calculate_ticks(t, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
> +
> +    interrupt = !ticks && timer_overflow_interrupt(t);

I think it's clearer to say

if (!ticks && timer_overflow_interrupt)
   interrupt = true;


> +
> +    if (!interrupt) {
> +        interrupt = ticks <= MIN(t->match[0], t->match[1]);
> +    }

if (!interrupt && ticks <=  MIN(t->match[0], t->match[1]))
    interrupt = true;

> +
> +    if (!interrupt) {
> +        interrupt = ticks <= MAX(t->match[0], t->match[1]);
> +    }

As above. And the advantage of structuring it like this is these
else..else if statements can be represented as such.

> +
> +    if (interrupt) {
>          t->level = !t->level;
>          qemu_set_irq(t->irq, t->level);
>      }
> +
> +    timer_mod(&t->timer, calculate_next(t));
>  }


More information about the openbmc mailing list