All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christopher Harvey <charvey@matrox.com>
To: qemu-devel@nongnu.org
Cc: paul@codesourcery.com
Subject: [Qemu-devel] [PATCH] Implement the global timer present in ARM MPCore chips.
Date: Wed, 6 Jul 2011 16:04:35 -0400	[thread overview]
Message-ID: <20110706200435.GA2168@harvey-pc.matrox.com> (raw)

I just rebased this from a REALLY old version and made some
changes. Before I go ahead and clean it up and properly test I wanted
to run it by this list to make sure it wont get rejected after all
that work. 

You can read about the hardware here:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BEJHAGEE.html

Reminder: 
There are probably bugs in this code, do not commit :P

I will be happy to answer any questions. 

Signed-off-by: Christopher Harvey <charvey@matrox.com>
---
 hw/mpcore.c |  188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 177 insertions(+), 11 deletions(-)

diff --git a/hw/mpcore.c b/hw/mpcore.c
index 379065a..a66cb0c 100644
--- a/hw/mpcore.c
+++ b/hw/mpcore.c
@@ -20,6 +20,12 @@ gic_get_current_cpu(void)
 
 #include "arm_gic.c"
 
+/* Global timer data */
+static uint64_t global_timer_count = 0;
+static uint64_t global_timer_this_inc = 0;
+  /* Only for prescale and enable bits */
+static uint32_t global_timer_control = 0;
+
 /* MPCore private memory region.  */
 
 typedef struct {
@@ -34,11 +40,30 @@ typedef struct {
     int id; /* Encodes both timer/watchdog and CPU.  */
 } mpcore_timer_state;
 
+typedef struct {
+    uint64_t compare;
+    uint32_t inc;
+    uint32_t status;
+    /* Only used for Auto-inc, IRQ enable and comp-enable (banked bits) */
+    /* Prescaler and enable bits are in the global control variable. */
+    uint32_t control; 
+
+    /* 
+     We actually need one of these for each core even for the global timer
+     because we don't simulate every tick, just the interrupt intervals, 
+     which can be different for each core.
+    */
+    QEMUTimer *timer;
+    int id;  /* Encodes only CPU id. */
+    struct mpcore_priv_state *mpcore;
+} mpcore_global_timer_state;
+
 typedef struct mpcore_priv_state {
     gic_state gic;
     uint32_t scu_control;
     int iomemtype;
-    mpcore_timer_state timer[8];
+    mpcore_timer_state timer[2*NCPU];
+    mpcore_global_timer_state g_timers[NCPU];
     uint32_t num_cpu;
 } mpcore_priv_state;
 
@@ -46,16 +71,41 @@ typedef struct mpcore_priv_state {
 
 static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
 {
-    if (s->status & ~s->old_status) {
+    if (s->status & ~s->old_status) { /* status == 1 && old_status == 0 */
         gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
     }
     s->old_status = s->status;
 }
 
 /* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
-static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
+static inline uint32_t mpcore_timer_scale(uint32_t control)
+{   
+    /* (Prescaler + 1) * 10 */
+    return (((control >> 8) & 0xff) + 1) * 10;
+}
+
+static uint64_t get_instantaneous_count(void) 
+{
+    uint64_t ret;
+    ret = qemu_get_clock_ns(vm_clock) - global_timer_count;
+    ret /= mpcore_timer_scale(global_timer_control);
+    ret += global_timer_count;
+    return ret;
+}
+
+static void set_next_global_tick(mpcore_global_timer_state *s) 
 {
-    return (((s->control >> 8) & 0xff) + 1) * 10;
+    int64_t to_wait;
+    if(s->compare < get_instantaneous_count())
+        return; /* Cortex-A9 r2p0 and later fire even when count >= compare, 
+		   not only on equality. Worth simulating? */
+
+    if(global_timer_control & 1) { /* Only set next tick if enabled. */
+       to_wait = qemu_get_clock_ns(vm_clock); 
+       to_wait += (s->compare - global_timer_count) * mpcore_timer_scale(global_timer_control);
+       global_timer_this_inc = s->compare - global_timer_count;
+       qemu_mod_timer(s->timer, to_wait);
+    }
 }
 
 static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
@@ -64,10 +114,27 @@ static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
         return;
     if (restart)
         s->tick = qemu_get_clock_ns(vm_clock);
-    s->tick += (int64_t)s->count * mpcore_timer_scale(s);
+    s->tick += (int64_t)s->count * mpcore_timer_scale(s->control);
     qemu_mod_timer(s->timer, s->tick);
 }
 
+static void mpcore_global_timer_tick(void *opaque) 
+{
+    mpcore_global_timer_state *s = (mpcore_global_timer_state *)opaque;
+    global_timer_count += global_timer_this_inc;
+    if(s->control & 0xB)/*test for auto-inc bit, comp and timer enable bits*/
+        s->compare += s->inc;
+
+    if(s->control & 7) { /* test for IRQ enable bit, compare enable and,
+			    timer enable */
+        if(!s->status) {
+            s->status = 1;
+            gic_set_pending_private(&s->mpcore->gic, s->id >> 1, 27);
+	}
+    }
+    set_next_global_tick(s);
+}
+
 static void mpcore_timer_tick(void *opaque)
 {
     mpcore_timer_state *s = (mpcore_timer_state *)opaque;
@@ -93,7 +160,7 @@ static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
             return 0;
         /* Slow and ugly, but hopefully won't happen too often.  */
         val = s->tick - qemu_get_clock_ns(vm_clock);
-        val /= mpcore_timer_scale(s);
+        val /= mpcore_timer_scale(s->control);
         if (val < 0)
             val = 0;
         return val;
@@ -106,6 +173,39 @@ static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
     }
 }
 
+static uint32_t mpcore_global_timer_read(mpcore_global_timer_state *s, int offset) 
+{
+    uint32_t ret;
+    offset &= 0xff; 
+    switch(offset) {
+    case 0:    /* Counter lower bits */
+    case 0x04: /* Counter high bits */
+
+        break;
+    case 0x08: /* Control */
+        ret = (s->control              & 0xE)     |    /* Bits [3:1]  */
+	      (global_timer_control    & 0xFF01);      /* Bits [15:8] and 0 */
+	break;
+    case 0x0C: /* Status */
+        ret = s->status & 1;
+	break;
+    case 0x10: /* Compare lower */
+        ret = s->compare & 0xffffffff;
+	break;
+    case 0x14: /* Compare upper  */
+        ret = s->compare >> 32;
+	break;
+    case 0x18: /* Auto increment amount */
+        ret = s->inc;
+	break;
+    default:
+        hw_error("mpcore_priv_read: Bad offset %x\n", (int)offset);
+	break;
+    }
+    return ret;
+}
+
+
 static void mpcore_timer_write(mpcore_timer_state *s, int offset,
                                uint32_t value)
 {
@@ -116,7 +216,7 @@ static void mpcore_timer_write(mpcore_timer_state *s, int offset,
         /* Fall through.  */
     case 4: /* Counter.  */
         if ((s->control & 1) && s->count) {
-            /* Cancel the previous timer.  */
+            /* Cancel the previous timer. */
             qemu_del_timer(s->timer);
         }
         s->count = value;
@@ -133,13 +233,49 @@ static void mpcore_timer_write(mpcore_timer_state *s, int offset,
             mpcore_timer_reload(s, 1);
         }
         break;
-    case 12: /* Interrupt status.  */
+    case 12: /* Interrupt status. */
         s->status &= ~value;
         mpcore_timer_update_irq(s);
         break;
     }
 }
 
+static void mpcore_global_timer_write(mpcore_global_timer_state *s, int offset,
+				      uint32_t value) 
+{
+    offset &= 0xff;
+    switch (offset) {
+    case 0:     /* lower 32 bits. */
+    case 0x04:  /* upper 32 bits. */
+        /* TODO: implement counter modify */
+        break;
+    case 0x08:
+        /* Control */
+        s->control = value & 0xE;
+	global_timer_control = value & 0xFF01;
+	set_next_global_tick(s);
+	break;
+    case 0x0C:
+        if (value & 1)
+	    s->status = 0;
+	break;
+    case 0x10:
+        /* Compare registers */
+        s->compare &= 0xffffffff00000000;
+	s->compare |= value;
+	set_next_global_tick(s);
+	break;
+    case 0x14:
+        s->compare = (s->compare & 0xffffffff ) |
+	             ((uint64_t)value << 32);
+	set_next_global_tick(s);
+	break;
+    case 0x18:
+        s->inc = value;
+	break;
+    }
+}
+
 static void mpcore_timer_init(mpcore_priv_state *mpcore,
                               mpcore_timer_state *s, int id)
 {
@@ -148,6 +284,18 @@ static void mpcore_timer_init(mpcore_priv_state *mpcore,
     s->timer = qemu_new_timer_ns(vm_clock, mpcore_timer_tick, s);
 }
 
+static void mpcore_global_timer_init(mpcore_priv_state *mpcore,
+				     mpcore_global_timer_state *s, int id) 
+{
+    s->status = 0; /* 0 is the correct power-on/reset value. */
+    s->inc = 0;
+    s->compare = 0;
+    s->control = 0;
+    s->id = id;
+    s->mpcore = mpcore;
+    s->timer = qemu_new_timer_ns(vm_clock, mpcore_global_timer_tick, s);
+}
+
 
 /* Per-CPU private memory mapped IO.  */
 
@@ -171,7 +319,7 @@ static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
         default:
             goto bad_reg;
         }
-    } else if (offset < 0x600) {
+    } else if (offset < 0x200) {
         /* Interrupt controller.  */
         if (offset < 0x200) {
             id = gic_get_current_cpu();
@@ -182,6 +330,13 @@ static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
             }
         }
         return gic_cpu_read(&s->gic, id, offset & 0xff);
+    } else if (offset < 0x300) {
+      /* Global timer */
+      id = gic_get_current_cpu();
+      return mpcore_global_timer_read(&s->g_timers[id], offset);
+    } else if (offset < 0x600) {
+      /* Non-existent */
+      goto bad_reg;
     } else if (offset < 0xb00) {
         /* Timers.  */
         if (offset < 0x700) {
@@ -203,7 +358,7 @@ bad_reg:
 }
 
 static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
-                          uint32_t value)
+			      uint32_t value)
 {
     mpcore_priv_state *s = (mpcore_priv_state *)opaque;
     int id;
@@ -220,7 +375,7 @@ static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
         default:
             goto bad_reg;
         }
-    } else if (offset < 0x600) {
+    } else if (offset < 0x200) {
         /* Interrupt controller.  */
         if (offset < 0x200) {
             id = gic_get_current_cpu();
@@ -230,6 +385,13 @@ static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
         if (id < s->num_cpu) {
             gic_cpu_write(&s->gic, id, offset & 0xff, value);
         }
+    } else if (offset < 0x300) {
+        /* Global timer */
+        id = gic_get_current_cpu();
+        mpcore_global_timer_write(&s->g_timers[id], offset, value);
+    } else if (offset < 0x600) {
+        /* Non-existent */
+        goto bad_reg;
     } else if (offset < 0xb00) {
         /* Timers.  */
         if (offset < 0x700) {
@@ -282,5 +444,9 @@ static int mpcore_priv_init(SysBusDevice *dev)
     for (i = 0; i < s->num_cpu * 2; i++) {
         mpcore_timer_init(s, &s->timer[i], i);
     }
+    /* Assuming that num_cpu is 4 or less */
+    for (i = 0; i < s->num_cpu; i++) {
+        mpcore_global_timer_init(s, &s->g_timers[i], i);
+    }
     return 0;
 }
-- 
1.7.3.4

             reply	other threads:[~2011-07-06 20:04 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-06 20:04 Christopher Harvey [this message]
2011-07-06 21:58 ` [Qemu-devel] [PATCH] Implement the global timer present in ARM MPCore chips Peter Maydell
2011-07-07 17:15   ` Christopher Harvey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110706200435.GA2168@harvey-pc.matrox.com \
    --to=charvey@matrox.com \
    --cc=paul@codesourcery.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.