#include "hw.h"
#include "sysemu.h"
#include "qemu-timer.h"
+#include "etraxfs.h"
#define D(x)
struct fs_timer_t {
CPUState *env;
qemu_irq *irq;
- target_phys_addr_t base;
+ qemu_irq *nmi;
QEMUBH *bh_t0;
QEMUBH *bh_t1;
ptimer_state *ptimer_wd;
struct timeval last;
+ int wd_hits;
+
/* Control registers. */
uint32_t rw_tmr0_div;
uint32_t r_tmr0_data;
uint32_t r_masked_intr;
};
-static uint32_t timer_rinvalid (void *opaque, target_phys_addr_t addr)
-{
- struct fs_timer_t *t = opaque;
- CPUState *env = t->env;
- cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n",
- addr, env->pc);
- return 0;
-}
-
static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
{
struct fs_timer_t *t = opaque;
- D(CPUState *env = t->env);
uint32_t r = 0;
- /* Make addr relative to this instances base. */
- addr -= t->base;
switch (addr) {
case R_TMR0_DATA:
+ r = ptimer_get_count(t->ptimer_t0);
break;
case R_TMR1_DATA:
- D(printf ("R_TMR1_DATA\n"));
+ r = ptimer_get_count(t->ptimer_t1);
break;
case R_TIME:
- r = qemu_get_clock(vm_clock) * 10;
+ r = qemu_get_clock(vm_clock) / 10;
break;
case RW_INTR_MASK:
r = t->rw_intr_mask;
r = t->r_intr & t->rw_intr_mask;
break;
default:
- D(printf ("%s %x p=%x\n", __func__, addr, env->pc));
+ D(printf ("%s %x\n", __func__, addr));
break;
}
return r;
}
-static void
-timer_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value)
-{
- struct fs_timer_t *t = opaque;
- CPUState *env = t->env;
- cpu_abort(env, "Unsupported short access. reg=%x pc=%x.\n",
- addr, env->pc);
-}
-
#define TIMER_SLOWDOWN 1
static void update_ctrl(struct fs_timer_t *t, int tnum)
{
unsigned int freq_hz;
unsigned int div;
uint32_t ctrl;
+
ptimer_state *timer;
if (tnum == 0) {
case 4: freq_hz = 29493000; break;
case 5: freq_hz = 32000000; break;
case 6: freq_hz = 32768000; break;
- case 7: freq_hz = 100001000; break;
+ case 7: freq_hz = 100000000; break;
default:
abort();
break;
D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
div = div * TIMER_SLOWDOWN;
- div >>= 15;
- freq_hz >>= 15;
+ div /= 1000;
+ freq_hz /= 1000;
ptimer_set_freq(timer, freq_hz);
ptimer_set_limit(timer, div, 0);
static void watchdog_hit(void *opaque)
{
- qemu_system_reset_request();
+ struct fs_timer_t *t = opaque;
+ if (t->wd_hits == 0) {
+ /* real hw gives a single tick before reseting but we are
+ a bit friendlier to compensate for our slower execution. */
+ ptimer_set_count(t->ptimer_wd, 10);
+ ptimer_run(t->ptimer_wd, 1);
+ qemu_irq_raise(t->nmi[0]);
+ }
+ else
+ qemu_system_reset_request();
+
+ t->wd_hits++;
}
static inline void timer_watchdog_update(struct fs_timer_t *t, uint32_t value)
D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n",
wd_en, new_key, wd_key, new_cmd, wd_cnt));
+ if (t->wd_hits)
+ qemu_irq_lower(t->nmi[0]);
+
+ t->wd_hits = 0;
+
ptimer_set_freq(t->ptimer_wd, 760);
if (wd_cnt == 0)
wd_cnt = 256;
timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
{
struct fs_timer_t *t = opaque;
- CPUState *env = t->env;
- /* Make addr relative to this instances base. */
- addr -= t->base;
switch (addr)
{
case RW_TMR0_DIV:
t->rw_ack_intr = 0;
break;
default:
- printf ("%s %x %x pc=%x\n",
- __func__, addr, value, env->pc);
+ printf ("%s " TARGET_FMT_plx " %x\n",
+ __func__, addr, value);
break;
}
}
static CPUReadMemoryFunc *timer_read[] = {
- &timer_rinvalid,
- &timer_rinvalid,
+ NULL, NULL,
&timer_readl,
};
static CPUWriteMemoryFunc *timer_write[] = {
- &timer_winvalid,
- &timer_winvalid,
+ NULL, NULL,
&timer_writel,
};
qemu_irq_lower(t->irq[0]);
}
-void etraxfs_timer_init(CPUState *env, qemu_irq *irqs,
+void etraxfs_timer_init(CPUState *env, qemu_irq *irqs, qemu_irq *nmi,
target_phys_addr_t base)
{
static struct fs_timer_t *t;
int timer_regs;
t = qemu_mallocz(sizeof *t);
- if (!t)
- return;
t->bh_t0 = qemu_bh_new(timer0_hit, t);
t->bh_t1 = qemu_bh_new(timer1_hit, t);
t->ptimer_t1 = ptimer_init(t->bh_t1);
t->ptimer_wd = ptimer_init(t->bh_wd);
t->irq = irqs;
+ t->nmi = nmi;
t->env = env;
- t->base = base;
timer_regs = cpu_register_io_memory(0, timer_read, timer_write, t);
cpu_register_physical_memory (base, 0x5c, timer_regs);