/*
- * QEMU ETRAX System Emulator
+ * QEMU ETRAX Timers
*
* Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
*
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include <stdio.h>
-#include <sys/time.h>
-#include "hw.h"
+#include "sysbus.h"
+#include "sysemu.h"
#include "qemu-timer.h"
#define D(x)
-void etrax_ack_irq(CPUState *env, uint32_t mask);
-
-#define R_TIME 0xb001e038
-#define RW_TMR0_DIV 0xb001e000
-#define R_TMR0_DATA 0xb001e004
-#define RW_TMR0_CTRL 0xb001e008
-#define RW_TMR1_DIV 0xb001e010
-#define R_TMR1_DATA 0xb001e014
-#define RW_TMR1_CTRL 0xb001e018
-
-#define RW_INTR_MASK 0xb001e048
-#define RW_ACK_INTR 0xb001e04c
-#define R_INTR 0xb001e050
-#define R_MASKED_INTR 0xb001e054
-
-
-uint32_t rw_intr_mask;
-uint32_t rw_ack_intr;
-uint32_t r_intr;
-
-struct fs_timer_t {
- QEMUBH *bh;
- unsigned int limit;
- int scale;
- ptimer_state *ptimer;
- CPUState *env;
- qemu_irq *irq;
- uint32_t mask;
- struct timeval last;
+#define RW_TMR0_DIV 0x00
+#define R_TMR0_DATA 0x04
+#define RW_TMR0_CTRL 0x08
+#define RW_TMR1_DIV 0x10
+#define R_TMR1_DATA 0x14
+#define RW_TMR1_CTRL 0x18
+#define R_TIME 0x38
+#define RW_WD_CTRL 0x40
+#define R_WD_STAT 0x44
+#define RW_INTR_MASK 0x48
+#define RW_ACK_INTR 0x4c
+#define R_INTR 0x50
+#define R_MASKED_INTR 0x54
+
+struct etrax_timer {
+ SysBusDevice busdev;
+ qemu_irq irq;
+ qemu_irq nmi;
+
+ QEMUBH *bh_t0;
+ QEMUBH *bh_t1;
+ QEMUBH *bh_wd;
+ ptimer_state *ptimer_t0;
+ ptimer_state *ptimer_t1;
+ ptimer_state *ptimer_wd;
+
+ int wd_hits;
+
+ /* Control registers. */
+ uint32_t rw_tmr0_div;
+ uint32_t r_tmr0_data;
+ uint32_t rw_tmr0_ctrl;
+
+ uint32_t rw_tmr1_div;
+ uint32_t r_tmr1_data;
+ uint32_t rw_tmr1_ctrl;
+
+ uint32_t rw_wd_ctrl;
+
+ uint32_t rw_intr_mask;
+ uint32_t rw_ack_intr;
+ uint32_t r_intr;
+ uint32_t r_masked_intr;
};
-static struct fs_timer_t timer[2];
-
-static inline int timer_index(target_phys_addr_t addr)
+static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
{
- int t = 0;
- if (addr >= 0xb005e000)
- t = 1;
- return t;
+ struct etrax_timer *t = opaque;
+ uint32_t r = 0;
+
+ switch (addr) {
+ case R_TMR0_DATA:
+ r = ptimer_get_count(t->ptimer_t0);
+ break;
+ case R_TMR1_DATA:
+ r = ptimer_get_count(t->ptimer_t1);
+ break;
+ case R_TIME:
+ r = qemu_get_clock_ns(vm_clock) / 10;
+ break;
+ case RW_INTR_MASK:
+ r = t->rw_intr_mask;
+ break;
+ case R_MASKED_INTR:
+ r = t->r_intr & t->rw_intr_mask;
+ break;
+ default:
+ D(printf ("%s %x\n", __func__, addr));
+ break;
+ }
+ return r;
}
-/* diff two timevals. Return a single int in us. */
-int diff_timeval_us(struct timeval *a, struct timeval *b)
+static void update_ctrl(struct etrax_timer *t, int tnum)
{
- int diff;
-
- /* assume these values are signed. */
- diff = (a->tv_sec - b->tv_sec) * 1000 * 1000;
- diff += (a->tv_usec - b->tv_usec);
- return diff;
+ unsigned int op;
+ unsigned int freq;
+ unsigned int freq_hz;
+ unsigned int div;
+ uint32_t ctrl;
+
+ ptimer_state *timer;
+
+ if (tnum == 0) {
+ ctrl = t->rw_tmr0_ctrl;
+ div = t->rw_tmr0_div;
+ timer = t->ptimer_t0;
+ } else {
+ ctrl = t->rw_tmr1_ctrl;
+ div = t->rw_tmr1_div;
+ timer = t->ptimer_t1;
+ }
+
+
+ op = ctrl & 3;
+ freq = ctrl >> 2;
+ freq_hz = 32000000;
+
+ switch (freq)
+ {
+ case 0:
+ case 1:
+ D(printf ("extern or disabled timer clock?\n"));
+ break;
+ case 4: freq_hz = 29493000; break;
+ case 5: freq_hz = 32000000; break;
+ case 6: freq_hz = 32768000; break;
+ case 7: freq_hz = 100000000; break;
+ default:
+ abort();
+ break;
+ }
+
+ D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
+ ptimer_set_freq(timer, freq_hz);
+ ptimer_set_limit(timer, div, 0);
+
+ switch (op)
+ {
+ case 0:
+ /* Load. */
+ ptimer_set_limit(timer, div, 1);
+ break;
+ case 1:
+ /* Hold. */
+ ptimer_stop(timer);
+ break;
+ case 2:
+ /* Run. */
+ ptimer_run(timer, 0);
+ break;
+ default:
+ abort();
+ break;
+ }
}
-static uint32_t timer_readb (void *opaque, target_phys_addr_t addr)
+static void timer_update_irq(struct etrax_timer *t)
{
- CPUState *env;
- uint32_t r = 0;
+ t->r_intr &= ~(t->rw_ack_intr);
+ t->r_masked_intr = t->r_intr & t->rw_intr_mask;
- env = opaque;
- D(printf ("%s %x pc=%x\n", __func__, addr, env->pc));
- return r;
+ D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr));
+ qemu_set_irq(t->irq, !!t->r_masked_intr);
}
-static uint32_t timer_readw (void *opaque, target_phys_addr_t addr)
-{
- CPUState *env;
- uint32_t r = 0;
- env = opaque;
- D(printf ("%s %x pc=%x\n", __func__, addr, env->pc));
- return r;
-}
-
-static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
+static void timer0_hit(void *opaque)
{
- CPUState *env = opaque;
- uint32_t r = 0;
- int t = timer_index(addr);
-
- switch (addr) {
- case R_TMR0_DATA:
- break;
- case R_TMR1_DATA:
- D(printf ("R_TMR1_DATA\n"));
- break;
- case R_TIME:
- {
- struct timeval now;
- gettimeofday(&now, NULL);
- if (!(timer[t].last.tv_sec == 0
- && timer[t].last.tv_usec == 0)) {
- r = diff_timeval_us(&now, &timer[t].last);
- r *= 1000; /* convert to ns. */
- r++; /* make sure we increase for each call. */
- }
- timer[t].last = now;
- break;
- }
-
- case RW_INTR_MASK:
- r = rw_intr_mask;
- break;
- case R_MASKED_INTR:
- r = r_intr & rw_intr_mask;
- break;
- default:
- printf ("%s %x p=%x\n", __func__, addr, env->pc);
- break;
- }
- return r;
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 1;
+ timer_update_irq(t);
}
-static void
-timer_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
-{
- CPUState *env;
- env = opaque;
- D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc));
-}
-static void
-timer_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+static void timer1_hit(void *opaque)
{
- CPUState *env;
- env = opaque;
- D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc));
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 2;
+ timer_update_irq(t);
}
-static void write_ctrl(struct fs_timer_t *t, uint32_t v)
+static void watchdog_hit(void *opaque)
{
- int op;
- int freq;
- int freq_hz;
-
- op = v & 3;
- freq = v >> 2;
- freq_hz = 32000000;
-
- switch (freq)
- {
- case 0:
- case 1:
- printf ("extern or disabled timer clock?\n");
- break;
- case 4: freq_hz = 29493000; break;
- case 5: freq_hz = 32000000; break;
- case 6: freq_hz = 32768000; break;
- case 7: freq_hz = 100000000; break;
- default:
- abort();
- break;
- }
-
- printf ("freq_hz=%d limit=%d\n", freq_hz, t->limit);
- t->scale = 0;
- if (t->limit > 2048)
- {
- t->scale = 2048;
- ptimer_set_period(t->ptimer, freq_hz / t->scale);
- }
-
- printf ("op=%d\n", op);
- switch (op)
- {
- case 0:
- printf ("limit=%d %d\n", t->limit, t->limit/t->scale);
- ptimer_set_limit(t->ptimer, t->limit / t->scale, 1);
- break;
- case 1:
- ptimer_stop(t->ptimer);
- break;
- case 2:
- ptimer_run(t->ptimer, 0);
- break;
- default:
- abort();
- break;
- }
+ struct etrax_timer *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);
+ }
+ else
+ qemu_system_reset_request();
+
+ t->wd_hits++;
}
-static void timer_ack_irq(struct fs_timer_t *t)
+static inline void timer_watchdog_update(struct etrax_timer *t, uint32_t value)
{
- if (!(r_intr & t->mask & rw_intr_mask)) {
- qemu_irq_lower(t->irq[0]);
- etrax_ack_irq(t->env, 1 << 0x1b);
- }
+ unsigned int wd_en = t->rw_wd_ctrl & (1 << 8);
+ unsigned int wd_key = t->rw_wd_ctrl >> 9;
+ unsigned int wd_cnt = t->rw_wd_ctrl & 511;
+ unsigned int new_key = value >> 9 & ((1 << 7) - 1);
+ unsigned int new_cmd = (value >> 8) & 1;
+
+ /* If the watchdog is enabled, they written key must match the
+ complement of the previous. */
+ wd_key = ~wd_key & ((1 << 7) - 1);
+
+ if (wd_en && wd_key != new_key)
+ return;
+
+ 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);
+
+ t->wd_hits = 0;
+
+ ptimer_set_freq(t->ptimer_wd, 760);
+ if (wd_cnt == 0)
+ wd_cnt = 256;
+ ptimer_set_count(t->ptimer_wd, wd_cnt);
+ if (new_cmd)
+ ptimer_run(t->ptimer_wd, 1);
+ else
+ ptimer_stop(t->ptimer_wd);
+
+ t->rw_wd_ctrl = value;
}
static void
timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
{
- CPUState *env = opaque;
- int t = timer_index(addr);
-
- D(printf ("%s %x %x pc=%x\n",
- __func__, addr, value, env->pc));
- switch (addr)
- {
- case RW_TMR0_DIV:
- D(printf ("RW_TMR0_DIV=%x\n", value));
- timer[t].limit = value;
- break;
- case RW_TMR0_CTRL:
- D(printf ("RW_TMR0_CTRL=%x\n", value));
- write_ctrl(&timer[t], value);
- break;
- case RW_TMR1_DIV:
- D(printf ("RW_TMR1_DIV=%x\n", value));
- break;
- case RW_TMR1_CTRL:
- D(printf ("RW_TMR1_CTRL=%x\n", value));
- break;
- case RW_INTR_MASK:
- D(printf ("RW_INTR_MASK=%x\n", value));
- rw_intr_mask = value;
- break;
- case RW_ACK_INTR:
- r_intr &= ~value;
- timer_ack_irq(&timer[t]);
- break;
- default:
- printf ("%s %x %x pc=%x\n",
- __func__, addr, value, env->pc);
- break;
- }
+ struct etrax_timer *t = opaque;
+
+ switch (addr)
+ {
+ case RW_TMR0_DIV:
+ t->rw_tmr0_div = value;
+ break;
+ case RW_TMR0_CTRL:
+ D(printf ("RW_TMR0_CTRL=%x\n", value));
+ t->rw_tmr0_ctrl = value;
+ update_ctrl(t, 0);
+ break;
+ case RW_TMR1_DIV:
+ t->rw_tmr1_div = value;
+ break;
+ case RW_TMR1_CTRL:
+ D(printf ("RW_TMR1_CTRL=%x\n", value));
+ t->rw_tmr1_ctrl = value;
+ update_ctrl(t, 1);
+ break;
+ case RW_INTR_MASK:
+ D(printf ("RW_INTR_MASK=%x\n", value));
+ t->rw_intr_mask = value;
+ timer_update_irq(t);
+ break;
+ case RW_WD_CTRL:
+ timer_watchdog_update(t, value);
+ break;
+ case RW_ACK_INTR:
+ t->rw_ack_intr = value;
+ timer_update_irq(t);
+ t->rw_ack_intr = 0;
+ break;
+ default:
+ printf ("%s " TARGET_FMT_plx " %x\n",
+ __func__, addr, value);
+ break;
+ }
}
-static CPUReadMemoryFunc *timer_read[] = {
- &timer_readb,
- &timer_readw,
+static CPUReadMemoryFunc * const timer_read[] = {
+ NULL, NULL,
&timer_readl,
};
-static CPUWriteMemoryFunc *timer_write[] = {
- &timer_writeb,
- &timer_writew,
+static CPUWriteMemoryFunc * const timer_write[] = {
+ NULL, NULL,
&timer_writel,
};
-static void timer_irq(void *opaque)
+static void etraxfs_timer_reset(void *opaque)
{
- struct fs_timer_t *t = opaque;
- r_intr |= t->mask;
- if (t->mask & rw_intr_mask) {
- qemu_irq_raise(t->irq[0]);
- }
+ struct etrax_timer *t = opaque;
+
+ ptimer_stop(t->ptimer_t0);
+ ptimer_stop(t->ptimer_t1);
+ ptimer_stop(t->ptimer_wd);
+ t->rw_wd_ctrl = 0;
+ t->r_intr = 0;
+ t->rw_intr_mask = 0;
+ qemu_irq_lower(t->irq);
}
-void etraxfs_timer_init(CPUState *env, qemu_irq *irqs)
+static int etraxfs_timer_init(SysBusDevice *dev)
{
- int timer_regs;
-
- timer[0].bh = qemu_bh_new(timer_irq, &timer[0]);
- timer[0].ptimer = ptimer_init(timer[0].bh);
- timer[0].irq = irqs + 0x1b;
- timer[0].mask = 1;
- timer[0].env = env;
-
- timer[1].bh = qemu_bh_new(timer_irq, &timer[1]);
- timer[1].ptimer = ptimer_init(timer[1].bh);
- timer[1].irq = irqs + 0x1b;
- timer[1].mask = 1;
- timer[1].env = env;
-
- timer_regs = cpu_register_io_memory(0, timer_read, timer_write, env);
- cpu_register_physical_memory (0xb001e000, 0x5c, timer_regs);
- cpu_register_physical_memory (0xb005e000, 0x5c, timer_regs);
+ struct etrax_timer *t = FROM_SYSBUS(typeof (*t), dev);
+ int timer_regs;
+
+ t->bh_t0 = qemu_bh_new(timer0_hit, t);
+ t->bh_t1 = qemu_bh_new(timer1_hit, t);
+ t->bh_wd = qemu_bh_new(watchdog_hit, t);
+ t->ptimer_t0 = ptimer_init(t->bh_t0);
+ t->ptimer_t1 = ptimer_init(t->bh_t1);
+ t->ptimer_wd = ptimer_init(t->bh_wd);
+
+ sysbus_init_irq(dev, &t->irq);
+ sysbus_init_irq(dev, &t->nmi);
+
+ timer_regs = cpu_register_io_memory(timer_read, timer_write, t,
+ DEVICE_NATIVE_ENDIAN);
+ sysbus_init_mmio(dev, 0x5c, timer_regs);
+
+ qemu_register_reset(etraxfs_timer_reset, t);
+ return 0;
+}
+
+static void etraxfs_timer_register(void)
+{
+ sysbus_register_dev("etraxfs,timer", sizeof (struct etrax_timer),
+ etraxfs_timer_init);
}
+
+device_init(etraxfs_timer_register)