mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 01:03:55 -06:00
target-arm queue:
* raspi: add model of cprman clock manager * sbsa-ref: add an SBSA generic watchdog device * arm/trace: Fix hex printing * raspi: Add models of Pi 3 model A+, Pi Zero and Pi A+ * hw/arm/smmuv3: Set the restoration priority of the vSMMUv3 explicitly * Nuvoton NPCM7xx: Add USB, RNG, GPIO and watchdog support * hw/arm: fix min_cpus for xlnx-versal-virt platform * hw/arm/highbank: Silence warnings about missing fallthrough statements * linux-user: Support Aarch64 BTI * Armv7M systick: fix corner case bugs by rewriting to use ptimer -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl+YBA4ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3rWRD/9hqjzL4d7xKcFQdQdRXsxv 7zX82arHdxg9pNvusie/tuhX0PLswQ8TPEHEBVQvngxF7y/HqLBFuZAQvFf4ou6R 9+myTXE2RuWHOYKlrr/M6p4csABXNMm7PiA3VMeKcTEh4DoamLyBz6j1X4obPiA+ tLaRw4azzYAZnHoCaF6BX+4uf4bQZoqAtAS4IodJAAbDXJStl0VUFoS34MPhgW6/ dwGF8DbQJVYRqa7xEXck4Yx7dkx13I66+iYUf9kCyoCkdyz1sIq58fbKhXQP4lqN I3e5XGBVJfeku7w/TGOpsw8OCyTng0z636iglfLVOrsj5N03fT8j72ehY7jJsN9f CgHvQ1JAX1DvA/v23oxs3WccwAOfJJsOERtf9QxyMbTR1czCeIY1LYMnkOFtyL87 6IQpwM0WF1z4lja0dmrvhKJWjqn+kVI2cDtxrprsulCHi+pcIdJMq8vJDfxjpqqe SnDXVSAn8KjBrClaJRqHfbi+5ggsTwsLpBtEToQ4AOR342XVRfEY8IfTLb1D2+6q z99BFiyJtZ6iiJq5jgGMhppN6tEuHFK7Vr6IwhGDgFTchWb6by+K3i8/VzrbWVk9 O+KEeO92dg6jVd+6FyXOPnJ3DcUXEp6EVUVrKBBUC+LTU8Lf1MCgeprjSi87UHIX xQg635uOQU3gxkqxCaE0XA== =OFlu -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20201027-1' into staging target-arm queue: * raspi: add model of cprman clock manager * sbsa-ref: add an SBSA generic watchdog device * arm/trace: Fix hex printing * raspi: Add models of Pi 3 model A+, Pi Zero and Pi A+ * hw/arm/smmuv3: Set the restoration priority of the vSMMUv3 explicitly * Nuvoton NPCM7xx: Add USB, RNG, GPIO and watchdog support * hw/arm: fix min_cpus for xlnx-versal-virt platform * hw/arm/highbank: Silence warnings about missing fallthrough statements * linux-user: Support Aarch64 BTI * Armv7M systick: fix corner case bugs by rewriting to use ptimer # gpg: Signature made Tue 27 Oct 2020 11:27:10 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20201027-1: (48 commits) hw/timer/armv7m_systick: Rewrite to use ptimers hw/core/ptimer: Support ptimer being disabled by timer callback hw/arm/sbsa-ref: add SBSA watchdog device hw/watchdog: Implement SBSA watchdog device hw/arm/bcm2835_peripherals: connect the UART clock hw/char/pl011: add a clock input hw/misc/bcm2835_cprman: add sane reset values to the registers hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer hw/misc/bcm2835_cprman: implement clock mux behaviour hw/misc/bcm2835_cprman: add a clock mux skeleton implementation hw/misc/bcm2835_cprman: implement PLL channels behaviour hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation hw/misc/bcm2835_cprman: implement PLLs behaviour hw/misc/bcm2835_cprman: add a PLL skeleton implementation hw/arm/raspi: add a skeleton implementation of the CPRMAN hw/arm/raspi: fix CPRMAN base address hw/core/clock: trace clock values in Hz instead of ns hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro arm/trace: Fix hex printing hw/arm/raspi: Add the Raspberry Pi 3 model A+ ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
802427bcda
64 changed files with 5461 additions and 279 deletions
|
@ -39,26 +39,6 @@ static inline int64_t systick_scale(SysTickState *s)
|
|||
}
|
||||
}
|
||||
|
||||
static void systick_reload(SysTickState *s, int reset)
|
||||
{
|
||||
/* The Cortex-M3 Devices Generic User Guide says that "When the
|
||||
* ENABLE bit is set to 1, the counter loads the RELOAD value from the
|
||||
* SYST RVR register and then counts down". So, we need to check the
|
||||
* ENABLE bit before reloading the value.
|
||||
*/
|
||||
trace_systick_reload();
|
||||
|
||||
if ((s->control & SYSTICK_ENABLE) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
s->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
s->tick += (s->reload + 1) * systick_scale(s);
|
||||
timer_mod(s->timer, s->tick);
|
||||
}
|
||||
|
||||
static void systick_timer_tick(void *opaque)
|
||||
{
|
||||
SysTickState *s = (SysTickState *)opaque;
|
||||
|
@ -70,10 +50,12 @@ static void systick_timer_tick(void *opaque)
|
|||
/* Tell the NVIC to pend the SysTick exception */
|
||||
qemu_irq_pulse(s->irq);
|
||||
}
|
||||
if (s->reload == 0) {
|
||||
s->control &= ~SYSTICK_ENABLE;
|
||||
} else {
|
||||
systick_reload(s, 0);
|
||||
if (ptimer_get_limit(s->ptimer) == 0) {
|
||||
/*
|
||||
* Timer expiry with SYST_RVR zero disables the timer
|
||||
* (but doesn't clear SYST_CSR.ENABLE)
|
||||
*/
|
||||
ptimer_stop(s->ptimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,30 +76,11 @@ static MemTxResult systick_read(void *opaque, hwaddr addr, uint64_t *data,
|
|||
s->control &= ~SYSTICK_COUNTFLAG;
|
||||
break;
|
||||
case 0x4: /* SysTick Reload Value. */
|
||||
val = s->reload;
|
||||
val = ptimer_get_limit(s->ptimer);
|
||||
break;
|
||||
case 0x8: /* SysTick Current Value. */
|
||||
{
|
||||
int64_t t;
|
||||
|
||||
if ((s->control & SYSTICK_ENABLE) == 0) {
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
t = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
if (t >= s->tick) {
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
val = ((s->tick - (t + 1)) / systick_scale(s)) + 1;
|
||||
/* The interrupt in triggered when the timer reaches zero.
|
||||
However the counter is not reloaded until the next clock
|
||||
tick. This is a hack to return zero during the first tick. */
|
||||
if (val > s->reload) {
|
||||
val = 0;
|
||||
}
|
||||
val = ptimer_get_count(s->ptimer);
|
||||
break;
|
||||
}
|
||||
case 0xc: /* SysTick Calibration Value. */
|
||||
val = 10000;
|
||||
break;
|
||||
|
@ -149,39 +112,50 @@ static MemTxResult systick_write(void *opaque, hwaddr addr,
|
|||
switch (addr) {
|
||||
case 0x0: /* SysTick Control and Status. */
|
||||
{
|
||||
uint32_t oldval = s->control;
|
||||
uint32_t oldval;
|
||||
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
oldval = s->control;
|
||||
s->control &= 0xfffffff8;
|
||||
s->control |= value & 7;
|
||||
|
||||
if ((oldval ^ value) & SYSTICK_ENABLE) {
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
if (value & SYSTICK_ENABLE) {
|
||||
if (s->tick) {
|
||||
s->tick += now;
|
||||
timer_mod(s->timer, s->tick);
|
||||
} else {
|
||||
systick_reload(s, 1);
|
||||
}
|
||||
/*
|
||||
* Always reload the period in case board code has
|
||||
* changed system_clock_scale. If we ever replace that
|
||||
* global with a more sensible API then we might be able
|
||||
* to set the period only when it actually changes.
|
||||
*/
|
||||
ptimer_set_period(s->ptimer, systick_scale(s));
|
||||
ptimer_run(s->ptimer, 0);
|
||||
} else {
|
||||
timer_del(s->timer);
|
||||
s->tick -= now;
|
||||
if (s->tick < 0) {
|
||||
s->tick = 0;
|
||||
}
|
||||
ptimer_stop(s->ptimer);
|
||||
}
|
||||
} else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
|
||||
/* This is a hack. Force the timer to be reloaded
|
||||
when the reference clock is changed. */
|
||||
systick_reload(s, 1);
|
||||
ptimer_set_period(s->ptimer, systick_scale(s));
|
||||
}
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
break;
|
||||
}
|
||||
case 0x4: /* SysTick Reload Value. */
|
||||
s->reload = value;
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
ptimer_set_limit(s->ptimer, value & 0xffffff, 0);
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
break;
|
||||
case 0x8: /* SysTick Current Value. Writes reload the timer. */
|
||||
systick_reload(s, 1);
|
||||
case 0x8: /* SysTick Current Value. */
|
||||
/*
|
||||
* Writing any value clears SYST_CVR to zero and clears
|
||||
* SYST_CSR.COUNTFLAG. The counter will then reload from SYST_RVR
|
||||
* on the next clock edge unless SYST_RVR is zero.
|
||||
*/
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
if (ptimer_get_limit(s->ptimer) == 0) {
|
||||
ptimer_stop(s->ptimer);
|
||||
}
|
||||
ptimer_set_count(s->ptimer, 0);
|
||||
s->control &= ~SYSTICK_COUNTFLAG;
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
|
@ -210,10 +184,13 @@ static void systick_reset(DeviceState *dev)
|
|||
*/
|
||||
assert(system_clock_scale != 0);
|
||||
|
||||
ptimer_transaction_begin(s->ptimer);
|
||||
s->control = 0;
|
||||
s->reload = 0;
|
||||
s->tick = 0;
|
||||
timer_del(s->timer);
|
||||
ptimer_stop(s->ptimer);
|
||||
ptimer_set_count(s->ptimer, 0);
|
||||
ptimer_set_limit(s->ptimer, 0, 0);
|
||||
ptimer_set_period(s->ptimer, systick_scale(s));
|
||||
ptimer_transaction_commit(s->ptimer);
|
||||
}
|
||||
|
||||
static void systick_instance_init(Object *obj)
|
||||
|
@ -229,18 +206,21 @@ static void systick_instance_init(Object *obj)
|
|||
static void systick_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
SysTickState *s = SYSTICK(dev);
|
||||
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, systick_timer_tick, s);
|
||||
s->ptimer = ptimer_init(systick_timer_tick, s,
|
||||
PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
|
||||
PTIMER_POLICY_NO_COUNTER_ROUND_DOWN |
|
||||
PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
|
||||
PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_systick = {
|
||||
.name = "armv7m_systick",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(control, SysTickState),
|
||||
VMSTATE_UINT32(reload, SysTickState),
|
||||
VMSTATE_INT64(tick, SysTickState),
|
||||
VMSTATE_TIMER_PTR(timer, SysTickState),
|
||||
VMSTATE_PTIMER(ptimer, SysTickState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
|
||||
#include "hw/irq.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/misc/npcm7xx_clk.h"
|
||||
#include "hw/timer/npcm7xx_timer.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
@ -60,6 +61,50 @@ enum NPCM7xxTimerRegisters {
|
|||
#define NPCM7XX_TCSR_PRESCALE_START 0
|
||||
#define NPCM7XX_TCSR_PRESCALE_LEN 8
|
||||
|
||||
#define NPCM7XX_WTCR_WTCLK(rv) extract32(rv, 10, 2)
|
||||
#define NPCM7XX_WTCR_FREEZE_EN BIT(9)
|
||||
#define NPCM7XX_WTCR_WTE BIT(7)
|
||||
#define NPCM7XX_WTCR_WTIE BIT(6)
|
||||
#define NPCM7XX_WTCR_WTIS(rv) extract32(rv, 4, 2)
|
||||
#define NPCM7XX_WTCR_WTIF BIT(3)
|
||||
#define NPCM7XX_WTCR_WTRF BIT(2)
|
||||
#define NPCM7XX_WTCR_WTRE BIT(1)
|
||||
#define NPCM7XX_WTCR_WTR BIT(0)
|
||||
|
||||
/*
|
||||
* The number of clock cycles between interrupt and reset in watchdog, used
|
||||
* by the software to handle the interrupt before system is reset.
|
||||
*/
|
||||
#define NPCM7XX_WATCHDOG_INTERRUPT_TO_RESET_CYCLES 1024
|
||||
|
||||
/* Start or resume the timer. */
|
||||
static void npcm7xx_timer_start(NPCM7xxBaseTimer *t)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
t->expires_ns = now + t->remaining_ns;
|
||||
timer_mod(&t->qtimer, t->expires_ns);
|
||||
}
|
||||
|
||||
/* Stop counting. Record the time remaining so we can continue later. */
|
||||
static void npcm7xx_timer_pause(NPCM7xxBaseTimer *t)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
timer_del(&t->qtimer);
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
t->remaining_ns = t->expires_ns - now;
|
||||
}
|
||||
|
||||
/* Delete the timer and reset it to default state. */
|
||||
static void npcm7xx_timer_clear(NPCM7xxBaseTimer *t)
|
||||
{
|
||||
timer_del(&t->qtimer);
|
||||
t->expires_ns = 0;
|
||||
t->remaining_ns = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the index of timer in the tc->timer array. This can be used to
|
||||
* locate the registers that belong to this timer.
|
||||
|
@ -102,6 +147,52 @@ static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
|
|||
return count;
|
||||
}
|
||||
|
||||
static uint32_t npcm7xx_watchdog_timer_prescaler(const NPCM7xxWatchdogTimer *t)
|
||||
{
|
||||
switch (NPCM7XX_WTCR_WTCLK(t->wtcr)) {
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
return 256;
|
||||
case 2:
|
||||
return 2048;
|
||||
case 3:
|
||||
return 65536;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static void npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
|
||||
int64_t cycles)
|
||||
{
|
||||
uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t);
|
||||
int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles;
|
||||
|
||||
/*
|
||||
* The reset function always clears the current timer. The caller of the
|
||||
* this needs to decide whether to start the watchdog timer based on
|
||||
* specific flag in WTCR.
|
||||
*/
|
||||
npcm7xx_timer_clear(&t->base_timer);
|
||||
|
||||
ns *= prescaler;
|
||||
t->base_timer.remaining_ns = ns;
|
||||
}
|
||||
|
||||
static void npcm7xx_watchdog_timer_reset(NPCM7xxWatchdogTimer *t)
|
||||
{
|
||||
int64_t cycles = 1;
|
||||
uint32_t s = NPCM7XX_WTCR_WTIS(t->wtcr);
|
||||
|
||||
g_assert(s <= 3);
|
||||
|
||||
cycles <<= NPCM7XX_WATCHDOG_BASETIME_SHIFT;
|
||||
cycles <<= 2 * s;
|
||||
|
||||
npcm7xx_watchdog_timer_reset_cycles(t, cycles);
|
||||
}
|
||||
|
||||
/*
|
||||
* Raise the interrupt line if there's a pending interrupt and interrupts are
|
||||
* enabled for this timer. If not, lower it.
|
||||
|
@ -116,16 +207,6 @@ static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
|
|||
trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, pending);
|
||||
}
|
||||
|
||||
/* Start or resume the timer. */
|
||||
static void npcm7xx_timer_start(NPCM7xxTimer *t)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
t->expires_ns = now + t->remaining_ns;
|
||||
timer_mod(&t->qtimer, t->expires_ns);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the counter reaches zero. Sets the interrupt flag, and either
|
||||
* restarts or disables the timer.
|
||||
|
@ -138,9 +219,9 @@ static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
|
|||
tc->tisr |= BIT(index);
|
||||
|
||||
if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
|
||||
t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
|
||||
t->base_timer.remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
|
||||
if (t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||
npcm7xx_timer_start(t);
|
||||
npcm7xx_timer_start(&t->base_timer);
|
||||
}
|
||||
} else {
|
||||
t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
|
||||
|
@ -149,18 +230,6 @@ static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
|
|||
npcm7xx_timer_check_interrupt(t);
|
||||
}
|
||||
|
||||
/* Stop counting. Record the time remaining so we can continue later. */
|
||||
static void npcm7xx_timer_pause(NPCM7xxTimer *t)
|
||||
{
|
||||
int64_t now;
|
||||
|
||||
timer_del(&t->qtimer);
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
t->remaining_ns = t->expires_ns - now;
|
||||
if (t->remaining_ns <= 0) {
|
||||
npcm7xx_timer_reached_zero(t);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Restart the timer from its initial value. If the timer was enabled and stays
|
||||
|
@ -170,10 +239,10 @@ static void npcm7xx_timer_pause(NPCM7xxTimer *t)
|
|||
*/
|
||||
static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
|
||||
{
|
||||
t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
|
||||
t->base_timer.remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
|
||||
|
||||
if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||
npcm7xx_timer_start(t);
|
||||
npcm7xx_timer_start(&t->base_timer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,10 +253,10 @@ static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
|
|||
if (t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
|
||||
return npcm7xx_timer_ns_to_count(t, t->base_timer.expires_ns - now);
|
||||
}
|
||||
|
||||
return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
|
||||
return npcm7xx_timer_ns_to_count(t, t->base_timer.remaining_ns);
|
||||
}
|
||||
|
||||
static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
|
||||
|
@ -219,9 +288,9 @@ static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
|
|||
|
||||
if (npcm7xx_tcsr_prescaler(old_tcsr) != npcm7xx_tcsr_prescaler(new_tcsr)) {
|
||||
/* Recalculate time remaining based on the current TDR value. */
|
||||
t->remaining_ns = npcm7xx_timer_count_to_ns(t, tdr);
|
||||
t->base_timer.remaining_ns = npcm7xx_timer_count_to_ns(t, tdr);
|
||||
if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
|
||||
npcm7xx_timer_start(t);
|
||||
npcm7xx_timer_start(&t->base_timer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,10 +304,13 @@ static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
|
|||
if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
|
||||
if (new_tcsr & NPCM7XX_TCSR_CEN) {
|
||||
t->tcsr |= NPCM7XX_TCSR_CACT;
|
||||
npcm7xx_timer_start(t);
|
||||
npcm7xx_timer_start(&t->base_timer);
|
||||
} else {
|
||||
t->tcsr &= ~NPCM7XX_TCSR_CACT;
|
||||
npcm7xx_timer_pause(t);
|
||||
npcm7xx_timer_pause(&t->base_timer);
|
||||
if (t->base_timer.remaining_ns <= 0) {
|
||||
npcm7xx_timer_reached_zero(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -259,9 +331,47 @@ static void npcm7xx_timer_write_tisr(NPCM7xxTimerCtrlState *s, uint32_t value)
|
|||
if (value & (1U << i)) {
|
||||
npcm7xx_timer_check_interrupt(&s->timer[i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void npcm7xx_timer_write_wtcr(NPCM7xxWatchdogTimer *t, uint32_t new_wtcr)
|
||||
{
|
||||
uint32_t old_wtcr = t->wtcr;
|
||||
|
||||
/*
|
||||
* WTIF and WTRF are cleared by writing 1. Writing 0 makes these bits
|
||||
* unchanged.
|
||||
*/
|
||||
if (new_wtcr & NPCM7XX_WTCR_WTIF) {
|
||||
new_wtcr &= ~NPCM7XX_WTCR_WTIF;
|
||||
} else if (old_wtcr & NPCM7XX_WTCR_WTIF) {
|
||||
new_wtcr |= NPCM7XX_WTCR_WTIF;
|
||||
}
|
||||
if (new_wtcr & NPCM7XX_WTCR_WTRF) {
|
||||
new_wtcr &= ~NPCM7XX_WTCR_WTRF;
|
||||
} else if (old_wtcr & NPCM7XX_WTCR_WTRF) {
|
||||
new_wtcr |= NPCM7XX_WTCR_WTRF;
|
||||
}
|
||||
|
||||
t->wtcr = new_wtcr;
|
||||
|
||||
if (new_wtcr & NPCM7XX_WTCR_WTR) {
|
||||
t->wtcr &= ~NPCM7XX_WTCR_WTR;
|
||||
npcm7xx_watchdog_timer_reset(t);
|
||||
if (new_wtcr & NPCM7XX_WTCR_WTE) {
|
||||
npcm7xx_timer_start(&t->base_timer);
|
||||
}
|
||||
} else if ((old_wtcr ^ new_wtcr) & NPCM7XX_WTCR_WTE) {
|
||||
if (new_wtcr & NPCM7XX_WTCR_WTE) {
|
||||
npcm7xx_timer_start(&t->base_timer);
|
||||
} else {
|
||||
npcm7xx_timer_pause(&t->base_timer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static hwaddr npcm7xx_tcsr_index(hwaddr reg)
|
||||
{
|
||||
switch (reg) {
|
||||
|
@ -353,7 +463,7 @@ static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
|
|||
break;
|
||||
|
||||
case NPCM7XX_TIMER_WTCR:
|
||||
value = s->wtcr;
|
||||
value = s->watchdog_timer.wtcr;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -409,8 +519,7 @@ static void npcm7xx_timer_write(void *opaque, hwaddr offset,
|
|||
return;
|
||||
|
||||
case NPCM7XX_TIMER_WTCR:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
|
||||
__func__, value);
|
||||
npcm7xx_timer_write_wtcr(&s->watchdog_timer, value);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -448,15 +557,42 @@ static void npcm7xx_timer_enter_reset(Object *obj, ResetType type)
|
|||
for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
|
||||
NPCM7xxTimer *t = &s->timer[i];
|
||||
|
||||
timer_del(&t->qtimer);
|
||||
t->expires_ns = 0;
|
||||
t->remaining_ns = 0;
|
||||
npcm7xx_timer_clear(&t->base_timer);
|
||||
t->tcsr = 0x00000005;
|
||||
t->ticr = 0x00000000;
|
||||
}
|
||||
|
||||
s->tisr = 0x00000000;
|
||||
s->wtcr = 0x00000400;
|
||||
/*
|
||||
* Set WTCLK to 1(default) and reset all flags except WTRF.
|
||||
* WTRF is not reset during a core domain reset.
|
||||
*/
|
||||
s->watchdog_timer.wtcr = 0x00000400 | (s->watchdog_timer.wtcr &
|
||||
NPCM7XX_WTCR_WTRF);
|
||||
}
|
||||
|
||||
static void npcm7xx_watchdog_timer_expired(void *opaque)
|
||||
{
|
||||
NPCM7xxWatchdogTimer *t = opaque;
|
||||
|
||||
if (t->wtcr & NPCM7XX_WTCR_WTE) {
|
||||
if (t->wtcr & NPCM7XX_WTCR_WTIF) {
|
||||
if (t->wtcr & NPCM7XX_WTCR_WTRE) {
|
||||
t->wtcr |= NPCM7XX_WTCR_WTRF;
|
||||
/* send reset signal to CLK module*/
|
||||
qemu_irq_raise(t->reset_signal);
|
||||
}
|
||||
} else {
|
||||
t->wtcr |= NPCM7XX_WTCR_WTIF;
|
||||
if (t->wtcr & NPCM7XX_WTCR_WTIE) {
|
||||
/* send interrupt */
|
||||
qemu_irq_raise(t->irq);
|
||||
}
|
||||
npcm7xx_watchdog_timer_reset_cycles(t,
|
||||
NPCM7XX_WATCHDOG_INTERRUPT_TO_RESET_CYCLES);
|
||||
npcm7xx_timer_start(&t->base_timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void npcm7xx_timer_hold_reset(Object *obj)
|
||||
|
@ -467,6 +603,7 @@ static void npcm7xx_timer_hold_reset(Object *obj)
|
|||
for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
|
||||
qemu_irq_lower(s->timer[i].irq);
|
||||
}
|
||||
qemu_irq_lower(s->watchdog_timer.irq);
|
||||
}
|
||||
|
||||
static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
|
||||
|
@ -474,43 +611,80 @@ static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
|
|||
NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
|
||||
SysBusDevice *sbd = &s->parent;
|
||||
int i;
|
||||
NPCM7xxWatchdogTimer *w;
|
||||
|
||||
for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
|
||||
NPCM7xxTimer *t = &s->timer[i];
|
||||
t->ctrl = s;
|
||||
timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t);
|
||||
timer_init_ns(&t->base_timer.qtimer, QEMU_CLOCK_VIRTUAL,
|
||||
npcm7xx_timer_expired, t);
|
||||
sysbus_init_irq(sbd, &t->irq);
|
||||
}
|
||||
|
||||
w = &s->watchdog_timer;
|
||||
w->ctrl = s;
|
||||
timer_init_ns(&w->base_timer.qtimer, QEMU_CLOCK_VIRTUAL,
|
||||
npcm7xx_watchdog_timer_expired, w);
|
||||
sysbus_init_irq(sbd, &w->irq);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s,
|
||||
TYPE_NPCM7XX_TIMER, 4 * KiB);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
qdev_init_gpio_out_named(dev, &w->reset_signal,
|
||||
NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_npcm7xx_timer = {
|
||||
.name = "npcm7xx-timer",
|
||||
static const VMStateDescription vmstate_npcm7xx_base_timer = {
|
||||
.name = "npcm7xx-base-timer",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_TIMER(qtimer, NPCM7xxTimer),
|
||||
VMSTATE_INT64(expires_ns, NPCM7xxTimer),
|
||||
VMSTATE_INT64(remaining_ns, NPCM7xxTimer),
|
||||
VMSTATE_TIMER(qtimer, NPCM7xxBaseTimer),
|
||||
VMSTATE_INT64(expires_ns, NPCM7xxBaseTimer),
|
||||
VMSTATE_INT64(remaining_ns, NPCM7xxBaseTimer),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_npcm7xx_timer = {
|
||||
.name = "npcm7xx-timer",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(base_timer, NPCM7xxTimer,
|
||||
0, vmstate_npcm7xx_base_timer,
|
||||
NPCM7xxBaseTimer),
|
||||
VMSTATE_UINT32(tcsr, NPCM7xxTimer),
|
||||
VMSTATE_UINT32(ticr, NPCM7xxTimer),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
|
||||
.name = "npcm7xx-timer-ctrl",
|
||||
static const VMStateDescription vmstate_npcm7xx_watchdog_timer = {
|
||||
.name = "npcm7xx-watchdog-timer",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(base_timer, NPCM7xxWatchdogTimer,
|
||||
0, vmstate_npcm7xx_base_timer,
|
||||
NPCM7xxBaseTimer),
|
||||
VMSTATE_UINT32(wtcr, NPCM7xxWatchdogTimer),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
|
||||
.name = "npcm7xx-timer-ctrl",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
|
||||
VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState),
|
||||
VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
|
||||
NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
|
||||
NPCM7xxTimer),
|
||||
VMSTATE_STRUCT(watchdog_timer, NPCM7xxTimerCtrlState,
|
||||
0, vmstate_npcm7xx_watchdog_timer,
|
||||
NPCM7xxWatchdogTimer),
|
||||
VMSTATE_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue