* 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"
+#include "ptimer.h"
#define D(x)
-#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_WD_CTRL 0xb001e040
-#define RW_INTR_MASK 0xb001e048
-#define RW_ACK_INTR 0xb001e04c
-#define R_INTR 0xb001e050
-#define R_MASKED_INTR 0xb001e054
-
-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;
-
- uint32_t rw_intr_mask;
- uint32_t rw_ack_intr;
- uint32_t r_intr;
-};
+#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;
+ MemoryRegion mmio;
+ 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;
-static struct fs_timer_t timer[2];
+ uint32_t rw_wd_ctrl;
-static inline int timer_index(target_phys_addr_t addr)
+ uint32_t rw_intr_mask;
+ uint32_t rw_ack_intr;
+ uint32_t r_intr;
+ uint32_t r_masked_intr;
+};
+
+static uint64_t
+timer_read(void *opaque, target_phys_addr_t addr, unsigned int size)
{
- 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;
+ 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;
+ }
- /* assume these values are signed. */
- diff = (a->tv_sec - b->tv_sec) * 1000 * 1000;
- diff += (a->tv_usec - b->tv_usec);
- return diff;
+
+ 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)
+
+static void timer0_hit(void *opaque)
{
- CPUState *env;
- uint32_t r = 0;
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 1;
+ timer_update_irq(t);
+}
- env = opaque;
- D(printf ("%s %x pc=%x\n", __func__, addr, env->pc));
- return r;
+static void timer1_hit(void *opaque)
+{
+ struct etrax_timer *t = opaque;
+ t->r_intr |= 2;
+ timer_update_irq(t);
}
-static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
+static void watchdog_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 = timer[t].rw_intr_mask;
- break;
- case R_MASKED_INTR:
- r = timer[t].r_intr & timer[t].rw_intr_mask;
- break;
- default:
- D(printf ("%s %x p=%x\n", __func__, addr, env->pc));
- break;
- }
- return r;
+ 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_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
+static inline void timer_watchdog_update(struct etrax_timer *t, uint32_t value)
{
- CPUState *env;
- env = opaque;
- D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc));
+ 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_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
+timer_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
{
- CPUState *env;
- env = opaque;
- D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc));
+ struct etrax_timer *t = opaque;
+ uint32_t value = val64;
+
+ 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 void write_ctrl(struct fs_timer_t *t, uint32_t v)
+static const MemoryRegionOps timer_ops = {
+ .read = timer_read,
+ .write = timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void etraxfs_timer_reset(void *opaque)
{
- int op;
- int freq;
- int freq_hz;
-
- op = v & 3;
- freq = v >> 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 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);
- }
-
- switch (op)
- {
- case 0:
- D(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;
+
+ 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);
}
-static void timer_ack_irq(struct fs_timer_t *t)
+static int etraxfs_timer_init(SysBusDevice *dev)
{
- if (!(t->r_intr & t->mask & t->rw_intr_mask))
- qemu_irq_lower(t->irq[0]);
+ struct etrax_timer *t = FROM_SYSBUS(typeof (*t), dev);
+
+ 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);
+
+ memory_region_init_io(&t->mmio, &timer_ops, t, "etraxfs-timer", 0x5c);
+ sysbus_init_mmio(dev, &t->mmio);
+ qemu_register_reset(etraxfs_timer_reset, t);
+ return 0;
}
-static void
-timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
+static void etraxfs_timer_class_init(ObjectClass *klass, void *data)
{
- 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));
- timer[t].rw_intr_mask = value;
- break;
- case RW_WD_CTRL:
- D(printf ("RW_WD_CTRL=%x\n", value));
- break;
- case RW_ACK_INTR:
- timer[t].r_intr &= ~value;
- timer_ack_irq(&timer[t]);
- break;
- default:
- printf ("%s %x %x pc=%x\n",
- __func__, addr, value, env->pc);
- break;
- }
-}
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-static CPUReadMemoryFunc *timer_read[] = {
- &timer_readb,
- &timer_readw,
- &timer_readl,
-};
+ sdc->init = etraxfs_timer_init;
+}
-static CPUWriteMemoryFunc *timer_write[] = {
- &timer_writeb,
- &timer_writew,
- &timer_writel,
+static TypeInfo etraxfs_timer_info = {
+ .name = "etraxfs,timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof (struct etrax_timer),
+ .class_init = etraxfs_timer_class_init,
};
-static void timer_irq(void *opaque)
+static void etraxfs_timer_register_types(void)
{
- struct fs_timer_t *t = opaque;
- t->r_intr |= t->mask;
- if (t->mask & t->rw_intr_mask) {
- D(printf("%s raise\n", __func__));
- qemu_irq_raise(t->irq[0]);
- }
+ type_register_static(&etraxfs_timer_info);
}
-void etraxfs_timer_init(CPUState *env, qemu_irq *irqs)
-{
- 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 + 26;
- 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 + 26;
- 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);
-}
+type_init(etraxfs_timer_register_types)