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

Andrew Jeffery andrew at aj.id.au
Wed May 25 12:13:57 AEST 2016


On Mon, 2016-05-23 at 17:00 +0930, Joel Stanley wrote:
> 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.

A double win!

> 
> > 
> > 
> > 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 
> >  #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?).

I'll look into it.

> 
> > 
> > +
> > +    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.

Hah!

> 
> Perhaps make ticks the uint32_t:
> 
>  uint32_t ticks = (uint32_t)floor( ... );

I'll try clean up the casts.

> 
> > 
> > +}
> > +
> > +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.

Fair point. I'll add some words.

> 
> > 
> > +
> > +    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?

Because reload_ns wasn't relevant for the loop above, so to make it's
useful lifetime explicit I scoped it to where I needed it.

> 
> > 
> > +
> > +    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.

Okay, I'll look at restructuring it.

Thanks for the review.

Andrew

> 
> > 
> > +
> > +    if (interrupt) {
> >          t->level = !t->level;
> >          qemu_set_irq(t->irq, t->level);
> >      }
> > +
> > +    timer_mod(&t->timer, calculate_next(t));
> >  }
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part
URL: <http://lists.ozlabs.org/pipermail/openbmc/attachments/20160525/705d2daf/attachment.sig>


More information about the openbmc mailing list