mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
hw/timer: RX62N 8-Bit timer (TMR)
renesas_tmr: 8bit 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 <ysato@users.sourceforge.jp> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20200224141923.82118-16-ysato@users.sourceforge.jp> [PMD: Split from CMT, filled VMStateField for migration] Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
This commit is contained in:
parent
e78597cc45
commit
7adca78eda
@ -1972,8 +1972,10 @@ M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Magnus Damm <magnus.damm@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/char/sh_serial.c
|
||||
F: hw/timer/renesas_tmr.c
|
||||
F: hw/timer/sh_timer.c
|
||||
F: include/hw/sh4/sh.h
|
||||
F: include/hw/timer/renesas_tmr.h
|
||||
|
||||
Renesas RX peripherals
|
||||
M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
|
@ -35,3 +35,6 @@ config CMSDK_APB_TIMER
|
||||
config CMSDK_APB_DUALTIMER
|
||||
bool
|
||||
select PTIMER
|
||||
|
||||
config RENESAS_TMR
|
||||
bool
|
||||
|
@ -23,6 +23,7 @@ common-obj-$(CONFIG_OMAP) += omap_gptimer.o
|
||||
common-obj-$(CONFIG_OMAP) += omap_synctimer.o
|
||||
common-obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
|
||||
common-obj-$(CONFIG_SH4) += sh_timer.o
|
||||
common-obj-$(CONFIG_RENESAS_TMR) += renesas_tmr.o
|
||||
common-obj-$(CONFIG_DIGIC) += digic-timer.o
|
||||
common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
|
||||
|
||||
|
477
hw/timer/renesas_tmr.c
Normal file
477
hw/timer/renesas_tmr.c
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Renesas 8bit timer
|
||||
*
|
||||
* Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
|
||||
* (Rev.1.40 R01UH0033EJ0140)
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* 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/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/timer/renesas_tmr.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
REG8(TCR, 0)
|
||||
FIELD(TCR, CCLR, 3, 2)
|
||||
FIELD(TCR, OVIE, 5, 1)
|
||||
FIELD(TCR, CMIEA, 6, 1)
|
||||
FIELD(TCR, CMIEB, 7, 1)
|
||||
REG8(TCSR, 2)
|
||||
FIELD(TCSR, OSA, 0, 2)
|
||||
FIELD(TCSR, OSB, 2, 2)
|
||||
FIELD(TCSR, ADTE, 4, 2)
|
||||
REG8(TCORA, 4)
|
||||
REG8(TCORB, 6)
|
||||
REG8(TCNT, 8)
|
||||
REG8(TCCR, 10)
|
||||
FIELD(TCCR, CKS, 0, 3)
|
||||
FIELD(TCCR, CSS, 3, 2)
|
||||
FIELD(TCCR, TMRIS, 7, 1)
|
||||
|
||||
#define INTERNAL 0x01
|
||||
#define CASCADING 0x03
|
||||
#define CCLR_A 0x01
|
||||
#define CCLR_B 0x02
|
||||
|
||||
static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
|
||||
|
||||
static uint8_t concat_reg(uint8_t *reg)
|
||||
{
|
||||
return (reg[0] << 8) | reg[1];
|
||||
}
|
||||
|
||||
static void update_events(RTMRState *tmr, int ch)
|
||||
{
|
||||
uint16_t diff[TMR_NR_EVENTS], min;
|
||||
int64_t next_time;
|
||||
int i, event;
|
||||
|
||||
if (tmr->tccr[ch] == 0) {
|
||||
return ;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
|
||||
/* external clock mode */
|
||||
/* event not happened */
|
||||
return ;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) {
|
||||
/* cascading mode */
|
||||
if (ch == 1) {
|
||||
tmr->next[ch] = none;
|
||||
return ;
|
||||
}
|
||||
diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
|
||||
diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
|
||||
diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
|
||||
} else {
|
||||
/* separate mode */
|
||||
diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
|
||||
diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
|
||||
diff[ovi] = 0x100 - tmr->tcnt[ch];
|
||||
}
|
||||
/* Search for the most recently occurring event. */
|
||||
for (event = 0, min = diff[0], i = 1; i < none; i++) {
|
||||
if (min > diff[i]) {
|
||||
event = i;
|
||||
min = diff[i];
|
||||
}
|
||||
}
|
||||
tmr->next[ch] = event;
|
||||
next_time = diff[event];
|
||||
next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
|
||||
next_time *= NANOSECONDS_PER_SECOND;
|
||||
next_time /= tmr->input_freq;
|
||||
next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
timer_mod(&tmr->timer[ch], next_time);
|
||||
}
|
||||
|
||||
static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
|
||||
{
|
||||
int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
|
||||
int et;
|
||||
|
||||
tmr->div_round[ch] += delta;
|
||||
if (divrate > 0) {
|
||||
et = tmr->div_round[ch] / divrate;
|
||||
tmr->div_round[ch] %= divrate;
|
||||
} else {
|
||||
/* disble clock. so no update */
|
||||
et = 0;
|
||||
}
|
||||
return et;
|
||||
}
|
||||
|
||||
static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
|
||||
{
|
||||
int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int elapsed, ovf = 0;
|
||||
uint16_t tcnt[2];
|
||||
uint32_t ret;
|
||||
|
||||
delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
|
||||
if (delta > 0) {
|
||||
tmr->tick = now;
|
||||
|
||||
if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) {
|
||||
/* timer1 count update */
|
||||
elapsed = elapsed_time(tmr, 1, delta);
|
||||
if (elapsed >= 0x100) {
|
||||
ovf = elapsed >> 8;
|
||||
}
|
||||
tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
|
||||
}
|
||||
switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
|
||||
case INTERNAL:
|
||||
elapsed = elapsed_time(tmr, 0, delta);
|
||||
tcnt[0] = tmr->tcnt[0] + elapsed;
|
||||
break;
|
||||
case CASCADING:
|
||||
if (ovf > 0) {
|
||||
tcnt[0] = tmr->tcnt[0] + ovf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tcnt[0] = tmr->tcnt[0];
|
||||
tcnt[1] = tmr->tcnt[1];
|
||||
}
|
||||
if (size == 1) {
|
||||
return tcnt[ch];
|
||||
} else {
|
||||
ret = 0;
|
||||
ret = deposit32(ret, 0, 8, tcnt[1]);
|
||||
ret = deposit32(ret, 8, 8, tcnt[0]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t read_tccr(uint8_t r)
|
||||
{
|
||||
uint8_t tccr = 0;
|
||||
tccr = FIELD_DP8(tccr, TCCR, TMRIS,
|
||||
FIELD_EX8(r, TCCR, TMRIS));
|
||||
tccr = FIELD_DP8(tccr, TCCR, CSS,
|
||||
FIELD_EX8(r, TCCR, CSS));
|
||||
tccr = FIELD_DP8(tccr, TCCR, CKS,
|
||||
FIELD_EX8(r, TCCR, CKS));
|
||||
return tccr;
|
||||
}
|
||||
|
||||
static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
int ch = addr & 1;
|
||||
uint64_t ret;
|
||||
|
||||
if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
|
||||
HWADDR_PRIX "\n",
|
||||
addr);
|
||||
return UINT64_MAX;
|
||||
}
|
||||
switch (addr & 0x0e) {
|
||||
case A_TCR:
|
||||
ret = 0;
|
||||
ret = FIELD_DP8(ret, TCR, CCLR,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
|
||||
ret = FIELD_DP8(ret, TCR, OVIE,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
|
||||
ret = FIELD_DP8(ret, TCR, CMIEA,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
|
||||
ret = FIELD_DP8(ret, TCR, CMIEB,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
|
||||
return ret;
|
||||
case A_TCSR:
|
||||
ret = 0;
|
||||
ret = FIELD_DP8(ret, TCSR, OSA,
|
||||
FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
|
||||
ret = FIELD_DP8(ret, TCSR, OSB,
|
||||
FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
|
||||
switch (ch) {
|
||||
case 0:
|
||||
ret = FIELD_DP8(ret, TCSR, ADTE,
|
||||
FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
|
||||
break;
|
||||
case 1: /* CH1 ADTE unimplement always 1 */
|
||||
ret = FIELD_DP8(ret, TCSR, ADTE, 1);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
case A_TCORA:
|
||||
if (size == 1) {
|
||||
return tmr->tcora[ch];
|
||||
} else if (ch == 0) {
|
||||
return concat_reg(tmr->tcora);
|
||||
}
|
||||
case A_TCORB:
|
||||
if (size == 1) {
|
||||
return tmr->tcorb[ch];
|
||||
} else {
|
||||
return concat_reg(tmr->tcorb);
|
||||
}
|
||||
case A_TCNT:
|
||||
return read_tcnt(tmr, size, ch);
|
||||
case A_TCCR:
|
||||
if (size == 1) {
|
||||
return read_tccr(tmr->tccr[ch]);
|
||||
} else {
|
||||
return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
|
||||
}
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
|
||||
" not implemented\n",
|
||||
addr);
|
||||
break;
|
||||
}
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
|
||||
uint8_t *reg, uint64_t val)
|
||||
{
|
||||
if (size == 1) {
|
||||
reg[ch] = val;
|
||||
update_events(tmr, ch);
|
||||
} else {
|
||||
reg[0] = extract32(val, 8, 8);
|
||||
reg[1] = extract32(val, 0, 8);
|
||||
update_events(tmr, 0);
|
||||
update_events(tmr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
int ch = addr & 1;
|
||||
|
||||
if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
|
||||
addr);
|
||||
return;
|
||||
}
|
||||
switch (addr & 0x0e) {
|
||||
case A_TCR:
|
||||
tmr->tcr[ch] = val;
|
||||
break;
|
||||
case A_TCSR:
|
||||
tmr->tcsr[ch] = val;
|
||||
break;
|
||||
case A_TCORA:
|
||||
tmr_write_count(tmr, ch, size, tmr->tcora, val);
|
||||
break;
|
||||
case A_TCORB:
|
||||
tmr_write_count(tmr, ch, size, tmr->tcorb, val);
|
||||
break;
|
||||
case A_TCNT:
|
||||
tmr_write_count(tmr, ch, size, tmr->tcnt, val);
|
||||
break;
|
||||
case A_TCCR:
|
||||
tmr_write_count(tmr, ch, size, tmr->tccr, val);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
|
||||
" not implemented\n",
|
||||
addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps tmr_ops = {
|
||||
.write = tmr_write,
|
||||
.read = tmr_read,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static void timer_events(RTMRState *tmr, int ch);
|
||||
|
||||
static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
|
||||
uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
|
||||
{
|
||||
uint16_t ret = tcnt;
|
||||
|
||||
switch (tmr->next[ch]) {
|
||||
case none:
|
||||
break;
|
||||
case cmia:
|
||||
if (tcnt >= tcora) {
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
|
||||
ret = tcnt - tcora;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
|
||||
qemu_irq_pulse(tmr->cmia[ch]);
|
||||
}
|
||||
if (sz == 8 && ch == 0 &&
|
||||
FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) {
|
||||
tmr->tcnt[1]++;
|
||||
timer_events(tmr, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case cmib:
|
||||
if (tcnt >= tcorb) {
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
|
||||
ret = tcnt - tcorb;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
|
||||
qemu_irq_pulse(tmr->cmib[ch]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ovi:
|
||||
if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
|
||||
qemu_irq_pulse(tmr->ovi[ch]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void timer_events(RTMRState *tmr, int ch)
|
||||
{
|
||||
uint16_t tcnt;
|
||||
|
||||
tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
|
||||
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) {
|
||||
tmr->tcnt[ch] = issue_event(tmr, ch, 8,
|
||||
tmr->tcnt[ch],
|
||||
tmr->tcora[ch],
|
||||
tmr->tcorb[ch]) & 0xff;
|
||||
} else {
|
||||
if (ch == 1) {
|
||||
return ;
|
||||
}
|
||||
tcnt = issue_event(tmr, ch, 16,
|
||||
concat_reg(tmr->tcnt),
|
||||
concat_reg(tmr->tcora),
|
||||
concat_reg(tmr->tcorb));
|
||||
tmr->tcnt[0] = (tcnt >> 8) & 0xff;
|
||||
tmr->tcnt[1] = tcnt & 0xff;
|
||||
}
|
||||
update_events(tmr, ch);
|
||||
}
|
||||
|
||||
static void timer_event0(void *opaque)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
|
||||
timer_events(tmr, 0);
|
||||
}
|
||||
|
||||
static void timer_event1(void *opaque)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
|
||||
timer_events(tmr, 1);
|
||||
}
|
||||
|
||||
static void rtmr_reset(DeviceState *dev)
|
||||
{
|
||||
RTMRState *tmr = RTMR(dev);
|
||||
tmr->tcr[0] = tmr->tcr[1] = 0x00;
|
||||
tmr->tcsr[0] = 0x00;
|
||||
tmr->tcsr[1] = 0x10;
|
||||
tmr->tcnt[0] = tmr->tcnt[1] = 0x00;
|
||||
tmr->tcora[0] = tmr->tcora[1] = 0xff;
|
||||
tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
|
||||
tmr->tccr[0] = tmr->tccr[1] = 0x00;
|
||||
tmr->next[0] = tmr->next[1] = none;
|
||||
tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
static void rtmr_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *d = SYS_BUS_DEVICE(obj);
|
||||
RTMRState *tmr = RTMR(obj);
|
||||
int i;
|
||||
|
||||
memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
|
||||
tmr, "renesas-tmr", 0x10);
|
||||
sysbus_init_mmio(d, &tmr->memory);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
|
||||
sysbus_init_irq(d, &tmr->cmia[i]);
|
||||
sysbus_init_irq(d, &tmr->cmib[i]);
|
||||
sysbus_init_irq(d, &tmr->ovi[i]);
|
||||
}
|
||||
timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
|
||||
timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rtmr = {
|
||||
.name = "rx-tmr",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(tick, RTMRState),
|
||||
VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
|
||||
VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
|
||||
VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property rtmr_properties[] = {
|
||||
DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void rtmr_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_rtmr;
|
||||
dc->reset = rtmr_reset;
|
||||
device_class_set_props(dc, rtmr_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo rtmr_info = {
|
||||
.name = TYPE_RENESAS_TMR,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RTMRState),
|
||||
.instance_init = rtmr_init,
|
||||
.class_init = rtmr_class_init,
|
||||
};
|
||||
|
||||
static void rtmr_register_types(void)
|
||||
{
|
||||
type_register_static(&rtmr_info);
|
||||
}
|
||||
|
||||
type_init(rtmr_register_types)
|
55
include/hw/timer/renesas_tmr.h
Normal file
55
include/hw/timer/renesas_tmr.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Renesas 8bit timer Object
|
||||
*
|
||||
* Copyright (c) 2018 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HW_TIMER_RENESAS_TMR_H
|
||||
#define HW_TIMER_RENESAS_TMR_H
|
||||
|
||||
#include "qemu/timer.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 {
|
||||
TMR_CH = 2,
|
||||
TMR_NR_IRQ = 3 * TMR_CH
|
||||
};
|
||||
|
||||
typedef struct RTMRState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
uint64_t input_freq;
|
||||
MemoryRegion memory;
|
||||
|
||||
int64_t tick;
|
||||
uint8_t tcnt[TMR_CH];
|
||||
uint8_t tcora[TMR_CH];
|
||||
uint8_t tcorb[TMR_CH];
|
||||
uint8_t tcr[TMR_CH];
|
||||
uint8_t tccr[TMR_CH];
|
||||
uint8_t tcor[TMR_CH];
|
||||
uint8_t tcsr[TMR_CH];
|
||||
int64_t div_round[TMR_CH];
|
||||
uint8_t next[TMR_CH];
|
||||
qemu_irq cmia[TMR_CH];
|
||||
qemu_irq cmib[TMR_CH];
|
||||
qemu_irq ovi[TMR_CH];
|
||||
QEMUTimer timer[TMR_CH];
|
||||
} RTMRState;
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user