qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v2 11/17] Openrisc: add a timer


From: Jia Liu
Subject: [Qemu-devel] [PATCH v2 11/17] Openrisc: add a timer
Date: Sun, 27 May 2012 13:32:53 +0800

add a timer for openrisc.

Signed-off-by: Jia Liu <address@hidden>
---
 Makefile.target       |    1 +
 cpu-exec.c            |    4 ++
 hw/openrisc_timer.c   |  153 +++++++++++++++++++++++++++++++++++++++++++++++++
 target-openrisc/cpu.h |   20 ++++++-
 4 files changed, 177 insertions(+), 1 deletion(-)
 create mode 100644 hw/openrisc_timer.c

diff --git a/Makefile.target b/Makefile.target
index f2dfa2a..ca03b03 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -392,6 +392,7 @@ obj-xtensa-y += core-dc233c.o
 obj-xtensa-y += core-fsf.o
 
 obj-openrisc-y += openrisc_pic.o
+obj-openrisc-y += openrisc_timer.o
 
 main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
diff --git a/cpu-exec.c b/cpu-exec.c
index ee9afd7..845b2ae 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -382,6 +382,10 @@ int cpu_exec(CPUArchState *env)
                             && (env->sr & SR_IEE)) {
                             idx = EXCP_INT;
                         }
+                        if ((interrupt_request & CPU_INTERRUPT_TIMER)
+                            && (env->sr & SR_TEE)) {
+                            idx = EXCP_TICK;
+                        }
                         if (idx >= 0) {
                             env->exception_index = idx;
                             do_interrupt(env);
diff --git a/hw/openrisc_timer.c b/hw/openrisc_timer.c
new file mode 100644
index 0000000..9f7c094
--- /dev/null
+++ b/hw/openrisc_timer.c
@@ -0,0 +1,153 @@
+/*
+ *  QEMU openrisc timer support
+ *
+ *  Copyright (c) 2011-2012 Jia Liu <address@hidden>
+ *                          Zhizhou Zhang <address@hidden>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "openrisc_cpudev.h"
+#include "qemu-timer.h"
+
+#define TIMER_FREQ    (20 * 1000 * 1000)    /* 20MHz */
+
+/* The time when ttcr changes */
+static uint64_t last_clk;
+static int is_counting;
+
+/* Timer Mode */
+enum {
+    TIMER_NONE = (0<<30),
+    TIMER_INTR = (1<<30),
+    TIMER_SHOT = (2<<30),
+    TIMER_CONT = (3<<30),
+};
+
+static void count_update(CPUOpenriscState *env)
+{
+    uint64_t now, next;
+    uint32_t wait;
+
+    now = qemu_get_clock_ns(vm_clock);
+    if (!is_counting) {
+        qemu_del_timer(env->timer);
+        last_clk = now;
+        return;
+    }
+
+    env->ttcr += (uint32_t)muldiv64(now - last_clk, TIMER_FREQ,
+                                    get_ticks_per_sec());
+    last_clk = now;
+
+    if ((env->ttmr & TTMR_TP) <= (env->ttcr & TTMR_TP)) {
+        wait = TTMR_TP - (env->ttcr & TTMR_TP) + 1;
+        wait += env->ttmr & TTMR_TP;
+    } else {
+        wait = (env->ttmr & TTMR_TP) - (env->ttcr & TTMR_TP);
+    }
+
+    next = now + muldiv64(wait, get_ticks_per_sec(), TIMER_FREQ);
+    qemu_mod_timer(env->timer, next);
+}
+
+static void count_start(CPUOpenriscState *env)
+{
+    is_counting = 1;
+    count_update(env);
+}
+
+static void count_stop(CPUOpenriscState *env)
+{
+    is_counting = 0;
+    count_update(env);
+}
+
+uint32_t cpu_openrisc_get_count(CPUOpenriscState *env)
+{
+    count_update(env);
+    return env->ttcr;
+}
+
+void cpu_openrisc_store_count(CPUOpenriscState *env, uint32_t count)
+{
+    /* Store new count register */
+    env->ttcr = count;
+    if (env->ttmr & TIMER_NONE) {
+        return;
+    }
+    count_start(env);
+}
+
+void cpu_openrisc_store_compare(CPUOpenriscState *env, uint32_t value)
+{
+    int ip = env->ttmr & TTMR_IP;
+
+    if (value & TTMR_IP) { /* Keep IP bit */
+        env->ttmr = (value & ~TTMR_IP) + ip;
+    } else {               /* Clear IP bit */
+        env->ttmr = value & ~TTMR_IP;
+        env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
+    }
+    count_update(env);
+
+    switch (env->ttmr & TTMR_M) {
+    case TIMER_NONE:
+        count_stop(env);
+        break;
+    case TIMER_INTR:
+        count_start(env);
+        break;
+    case TIMER_SHOT:
+        count_start(env);
+        break;
+    case TIMER_CONT:
+        count_start(env);
+        break;
+    }
+}
+
+static void openrisc_timer_cb(void *opaque)
+{
+    CPUOpenriscState *env = opaque;
+
+    if ((env->ttmr & TTMR_IE) &&
+         qemu_timer_expired(env->timer, qemu_get_clock_ns(vm_clock))) {
+        env->ttmr |= TTMR_IP;
+        env->interrupt_request |= CPU_INTERRUPT_TIMER;
+    }
+
+    switch (env->ttmr & TTMR_M) {
+    case TIMER_NONE:
+        break;
+    case TIMER_INTR:
+        env->ttcr = 0;
+        count_start(env);
+        break;
+    case TIMER_SHOT:
+        count_stop(env);
+        break;
+    case TIMER_CONT:
+        count_start(env);
+        break;
+    }
+}
+
+void cpu_openrisc_clock_init(CPUOpenriscState *env)
+{
+    env->timer = qemu_new_timer_ns(vm_clock, &openrisc_timer_cb, env);
+    env->ttmr = 0;
+    env->ttcr = 0;
+}
diff --git a/target-openrisc/cpu.h b/target-openrisc/cpu.h
index 7417dcc..c3212b0 100644
--- a/target-openrisc/cpu.h
+++ b/target-openrisc/cpu.h
@@ -139,6 +139,14 @@ enum {
     SR_SCE = (1<<17),
 };
 
+/* TTMR bit */
+enum {
+    TTMR_TP = (0xfffffff),
+    TTMR_IP = (1<<28),
+    TTMR_IE = (1<<29),
+    TTMR_M  = (3<<30),
+};
+
 enum {
     DTLB_WAYS = 1,
     DTLB_SIZE = 64,
@@ -199,6 +207,10 @@ struct CPUOpenriscState {
                             target_phys_addr_t *physical, int *prot,
                             target_ulong address, int rw);
 
+    struct QEMUTimer *timer;
+    uint32_t ttmr;          /* Timer tick mode register */
+    uint32_t ttcr;          /* Timer tick count register */
+
     uint32_t picmr;         /* Interrupt mask register */
     uint32_t picsr;         /* Interrupt contrl register*/
 #endif
@@ -276,6 +288,10 @@ int cpu_openrisc_handle_mmu_fault(CPUOpenriscState *env, 
target_ulong address,
 
 void openrisc_reset(CPUOpenriscState *env);
 #if !defined(CONFIG_USER_ONLY)
+void cpu_openrisc_store_count(CPUOpenriscState *env, target_ulong count);
+void cpu_openrisc_store_compare(CPUOpenriscState *env, target_ulong value);
+uint32_t cpu_openrisc_get_count(CPUOpenriscState *env);
+
 void cpu_openrisc_pic_reset(CPUOpenriscState *env);
 void cpu_openrisc_store_picsr(CPUOpenriscState *env, uint32_t value);
 void cpu_openrisc_store_picmr(CPUOpenriscState *env, uint32_t value);
@@ -318,9 +334,11 @@ static inline int cpu_mmu_index(CPUOpenriscState *env)
     return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX;
 }
 
+#define CPU_INTERRUPT_TIMER   CPU_INTERRUPT_TGT_INT_0
 static inline bool cpu_has_work(CPUOpenriscState *env)
 {
-    return 1;
+    return env->interrupt_request & (CPU_INTERRUPT_HARD |
+                                     CPU_INTERRUPT_TIMER);
 }
 
 #include "exec-all.h"
-- 
1.7.9.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]