qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH RFC v8 07/12] hw/timer: RX62N internal timer mod


From: Alex Bennée
Subject: Re: [Qemu-devel] [PATCH RFC v8 07/12] hw/timer: RX62N internal timer modules
Date: Fri, 03 May 2019 16:20:47 +0100
User-agent: mu4e 1.3.1; emacs 26.1

Yoshinori Sato <address@hidden> writes:

> renesas_tmr: 8bit timer modules.
> renesas_cmt: 16bit compare match timer modules.
> This part use many renesas's CPU.
> Hardware manual.
> https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf
>
> Signed-off-by: Yoshinori Sato <address@hidden>
> ---
>  include/hw/timer/renesas_cmt.h |  33 +++
>  include/hw/timer/renesas_tmr.h |  46 +++++
>  hw/timer/renesas_cmt.c         | 277 +++++++++++++++++++++++++
>  hw/timer/renesas_tmr.c         | 458 
> +++++++++++++++++++++++++++++++++++++++++
>  hw/timer/Kconfig               |   6 +
>  hw/timer/Makefile.objs         |   3 +
>  6 files changed, 823 insertions(+)
>  create mode 100644 include/hw/timer/renesas_cmt.h
>  create mode 100644 include/hw/timer/renesas_tmr.h
>  create mode 100644 hw/timer/renesas_cmt.c
>  create mode 100644 hw/timer/renesas_tmr.c
>
> diff --git a/include/hw/timer/renesas_cmt.h b/include/hw/timer/renesas_cmt.h
> new file mode 100644
> index 0000000000..7e393d7ad3
> --- /dev/null
> +++ b/include/hw/timer/renesas_cmt.h
> @@ -0,0 +1,33 @@
> +/*
> + * Renesas Compare-match timer Object
> + *
> + * Copyright (c) 2019 Yoshinori Sato
> + *
> + * This code is licensed under the GPL version 2 or later.
> + *
> + */
> +
> +#ifndef HW_RENESAS_CMT_H
> +#define HW_RENESAS_CMT_H
> +
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RENESAS_CMT "renesas-cmt"
> +#define RCMT(obj) OBJECT_CHECK(RCMTState, (obj), TYPE_RENESAS_CMT)
> +
> +typedef struct RCMTState {
> +    SysBusDevice parent_obj;
> +
> +    uint64_t input_freq;
> +    MemoryRegion memory;
> +
> +    uint16_t cmstr;
> +    uint16_t cmcr[2];
> +    uint16_t cmcnt[2];
> +    uint16_t cmcor[2];
> +    int64_t tick[2];
> +    qemu_irq cmi[2];
> +    QEMUTimer *timer[2];
> +} RCMTState;
> +
> +#endif
> diff --git a/include/hw/timer/renesas_tmr.h b/include/hw/timer/renesas_tmr.h
> new file mode 100644
> index 0000000000..718d9dc4ff
> --- /dev/null
> +++ b/include/hw/timer/renesas_tmr.h
> @@ -0,0 +1,46 @@
> +/*
> + * Renesas 8bit timer Object
> + *
> + * Copyright (c) 2018 Yoshinori Sato
> + *
> + * This code is licensed under the GPL version 2 or later.
> + *
> + */
> +
> +#ifndef HW_RENESAS_TMR_H
> +#define HW_RENESAS_TMR_H
> +
> +#include "hw/sysbus.h"
> +
> +#define TYPE_RENESAS_TMR "renesas-tmr"
> +#define RTMR(obj) OBJECT_CHECK(RTMRState, (obj), TYPE_RENESAS_TMR)
> +
> +enum timer_event {cmia = 0,
> +                  cmib = 1,
> +                  ovi = 2,
> +                  none = 3,
> +                  TMR_NR_EVENTS = 4};
> +enum {CH = 2};
> +typedef struct RTMRState {
> +    SysBusDevice parent_obj;
> +
> +    uint64_t input_freq;
> +    MemoryRegion memory;
> +
> +    uint8_t tcnt[CH];
> +    uint8_t tcora[CH];
> +    uint8_t tcorb[CH];
> +    uint8_t tcr[CH];
> +    uint8_t tccr[CH];
> +    uint8_t tcor[CH];
> +    uint8_t tcsr[CH];
> +    int64_t tick;
> +    int64_t div_round[CH];
> +    enum timer_event next[CH];
> +    qemu_irq cmia[CH];
> +    qemu_irq cmib[CH];
> +    qemu_irq ovi[CH];
> +    QEMUTimer *timer[CH];
> +} RTMRState;
> +
> +#endif
> diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c
> new file mode 100644
> index 0000000000..b82250dbc2
> --- /dev/null
> +++ b/hw/timer/renesas_cmt.c
> @@ -0,0 +1,277 @@
> +/*
> + * Renesas 16bit Compare-match timer
> + *
> + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
> + * (Rev.1.40 R01UH0033EJ0140)
> + *
> + * Copyright (c) 2019 Yoshinori Sato
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along 
> with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu-common.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +#include "qemu/timer.h"
> +#include "cpu.h"
> +#include "hw/hw.h"
> +#include "hw/sysbus.h"
> +#include "hw/registerfields.h"
> +#include "hw/timer/renesas_cmt.h"
> +#include "qemu/error-report.h"
> +
> +/*
> + *  +0 CMSTR - common control
> + *  +2 CMCR  - ch0
> + *  +4 CMCNT - ch0
> + *  +6 CMCOR - ch0
> + *  +8 CMCR  - ch1
> + * +10 CMCNT - ch1
> + * +12 CMCOR - ch1
> + * If we think that the address of CH 0 has an offset of +2,
> + * we can treat it with the same address as CH 1, so define it like that.
> + */
> +REG16(CMSTR, 0)
> +  FIELD(CMSTR, STR0, 0, 1)
> +  FIELD(CMSTR, STR1, 1, 1)
> +  FIELD(CMSTR, STR,  0, 2)
> +/* This addeess is channel offset */
> +REG16(CMCR, 0)
> +  FIELD(CMCR, CKS, 0, 2)
> +  FIELD(CMCR, CMIE, 6, 1)
> +REG16(CMCNT, 2)
> +REG16(CMCOR, 4)
> +
> +static void update_events(RCMTState *cmt, int ch)
> +{
> +    int64_t next_time;
> +
> +    if ((cmt->cmstr & (1 << ch)) == 0) {
> +        /* count disable, so not happened next event. */
> +        return ;
> +    }
> +    next_time = cmt->cmcor[ch] - cmt->cmcnt[ch];
> +    next_time *= NANOSECONDS_PER_SECOND;
> +    next_time /= cmt->input_freq;
> +    /*
> +     * CKS -> div rate
> +     *  0 -> 8 (1 << 3)
> +     *  1 -> 32 (1 << 5)
> +     *  2 -> 128 (1 << 7)
> +     *  3 -> 512 (1 << 9)
> +     */
> +    next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
> +    next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    timer_mod(cmt->timer[ch], next_time);
> +}
> +
> +static int64_t read_cmcnt(RCMTState *cmt, int ch)
> +{
> +    int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +
> +    if (cmt->cmstr & (1 << ch)) {
> +        delta = (now - cmt->tick[ch]);
> +        delta /= NANOSECONDS_PER_SECOND;
> +        delta /= cmt->input_freq;
> +        delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
> +        cmt->tick[ch] = now;
> +        return cmt->cmcnt[ch] + delta;
> +    } else {
> +        return cmt->cmcnt[ch];
> +    }
> +}
> +
> +static uint64_t cmt_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    hwaddr offset = addr & 0x0f;
> +    RCMTState *cmt = opaque;
> +    int ch = offset / 0x08;
> +    uint64_t ret;
> +
> +    if (offset == A_CMSTR) {
> +        ret = 0;
> +        ret = FIELD_DP16(ret, CMSTR, STR,
> +                         FIELD_EX16(cmt->cmstr, CMSTR, STR));
> +        return ret;
> +    } else {
> +        offset &= 0x07;
> +        if (ch == 0) {
> +            offset -= 0x02;
> +        }
> +        switch (offset) {
> +        case A_CMCR:
> +            ret = 0;
> +            ret = FIELD_DP16(ret, CMCR, CKS,
> +                             FIELD_EX16(cmt->cmstr, CMCR, CKS));
> +            ret = FIELD_DP16(ret, CMCR, CMIE,
> +                             FIELD_EX16(cmt->cmstr, CMCR, CMIE));
> +            return ret;
> +        case A_CMCNT:
> +            return read_cmcnt(cmt, ch);
> +        case A_CMCOR:
> +            return cmt->cmcor[ch];
> +        }
> +    }
> +    qemu_log_mask(LOG_UNIMP,
> +                  "renesas_cmt: Register %08lx not implemented\n",
> +                  offset);
> +    return 0xffffffffffffffffUL;
> +}
> +
> +static void start_stop(RCMTState *cmt, int ch, int st)
> +{
> +    if (st) {
> +        update_events(cmt, ch);
> +    } else {
> +        timer_del(cmt->timer[ch]);
> +    }
> +}
> +
> +static void cmt_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    hwaddr offset = addr & 0x0f;
> +    RCMTState *cmt = opaque;
> +    int ch = offset / 0x08;
> +
> +    if (offset == A_CMSTR) {
> +        cmt->cmstr = FIELD_EX16(val, CMSTR, STR);
> +        start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0));
> +        start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1));
> +    } else {
> +        offset &= 0x07;
> +        if (ch == 0) {
> +            offset -= 0x02;
> +        }
> +        switch (offset) {
> +        case A_CMCR:
> +            cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS,
> +                                       FIELD_EX16(val, CMCR, CKS));
> +            cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE,
> +                                       FIELD_EX16(val, CMCR, CMIE));
> +            break;
> +        case 2:
> +            cmt->cmcnt[ch] = val;
> +            break;
> +        case 4:
> +            cmt->cmcor[ch] = val;
> +            break;
> +        default:
> +            qemu_log_mask(LOG_UNIMP,
> +                          "renesas_cmt: Register %08lx not implemented\n",
> +                          offset);
> +            return;
> +        }
> +        if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) {
> +            update_events(cmt, ch);
> +        }
> +    }
> +}
> +
> +static const MemoryRegionOps cmt_ops = {
> +    .write = cmt_write,
> +    .read  = cmt_read,
> +    .endianness = DEVICE_NATIVE_ENDIAN,
> +    .impl = {
> +        .min_access_size = 2,
> +        .max_access_size = 2,
> +    },
> +};
> +
> +static void timer_events(RCMTState *cmt, int ch)
> +{
> +    cmt->cmcnt[ch] = 0;
> +    cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    update_events(cmt, ch);
> +    if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) {
> +        qemu_irq_pulse(cmt->cmi[ch]);
> +    }
> +}
> +
> +static void timer_event0(void *opaque)
> +{
> +    RCMTState *cmt = opaque;
> +
> +    timer_events(cmt, 0);
> +}
> +
> +static void timer_event1(void *opaque)
> +{
> +    RCMTState *cmt = opaque;
> +
> +    timer_events(cmt, 1);
> +}

I guess there is enough shared state RCMTState that you couldn't have an
array of channel structures and just pass that when you setup the
timers:

> +static void rcmt_init(Object *obj)
> +{
<snip>
> +    cmt->timer[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_event0, cmt);
> +    cmt->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_event1,
> cmt);

here?

Anyway:

Reviewed-by: Alex Bennée <address@hidden>

--
Alex Bennée



reply via email to

[Prev in Thread] Current Thread [Next in Thread]