qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined


From: Fabien Chouteau
Subject: Re: [Qemu-devel] [PATCH 1/6] [RFC] Emulation of GRLIB GPTimer as defined in GRLIB IP Core User's Manual.
Date: Thu, 09 Dec 2010 11:04:58 +0100
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.12) Gecko/20101027 Thunderbird/3.1.6

On 12/08/2010 11:51 PM, Edgar E. Iglesias wrote:
On Mon, Dec 06, 2010 at 10:26:02AM +0100, Fabien Chouteau wrote:

Signed-off-by: Fabien Chouteau<address@hidden>
---
  hw/grlib_gptimer.c |  448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 448 insertions(+), 0 deletions(-)

diff --git a/hw/grlib_gptimer.c b/hw/grlib_gptimer.c
new file mode 100644
index 0000000..41edbe4
--- /dev/null
+++ b/hw/grlib_gptimer.c
@@ -0,0 +1,448 @@
+/*
+ * QEMU GRLIB GPTimer Emulator
+ *
+ * Copyright (c) 2010 AdaCore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+
+#include "grlib.h"
+
+/* #define DEBUG_TIMER */
+
+#ifdef DEBUG_TIMER
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("GPTIMER: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define UNIT_REG_SIZE    16     /* Size of memory mapped regs for the unit */
+#define GPTIMER_REG_SIZE 16     /* Size of memory mapped regs for a GPTimer */
+
+#define GPTIMER_MAX_TIMERS 8
+
+/* GPTimer Config register fields */
+#define GPTIMER_ENABLE      (1<<  0)
+#define GPTIMER_RESTART     (1<<  1)
+#define GPTIMER_LOAD        (1<<  2)
+#define GPTIMER_INT_ENABLE  (1<<  3)
+#define GPTIMER_INT_PENDING (1<<  4)
+#define GPTIMER_CHAIN       (1<<  5) /* Not supported */
+#define GPTIMER_DEBUG_HALT  (1<<  6) /* Not supported */
+
+/* Memory mapped register offsets */
+#define SCALER_OFFSET         0x00
+#define SCALER_RELOAD_OFFSET  0x04
+#define CONFIG_OFFSET         0x08
+#define COUNTER_OFFSET        0x00
+#define COUNTER_RELOAD_OFFSET 0x04
+#define TIMER_BASE            0x10
+
+typedef struct GPTimer     GPTimer;
+typedef struct GPTimerUnit GPTimerUnit;
+
+struct GPTimer
+{
+    QEMUBH *bh;
+    struct ptimer_state *ptimer;
+
+    qemu_irq     irq;
+    int          id;
+    GPTimerUnit *unit;
+
+    /* registers */
+    uint32_t counter;
+    uint32_t reload;
+    uint32_t config;
+};
+
+struct GPTimerUnit
+{
+    SysBusDevice  busdev;
+
+    uint32_t nr_timers;         /* Number of timers available */
+    uint32_t freq_hz;           /* System frequency */
+    uint32_t irq_line;          /* Base irq line */
+
+    GPTimer *timers;
+
+    /* registers */
+    uint32_t scaler;
+    uint32_t reload;
+    uint32_t config;
+};
+
+DeviceState *grlib_gptimer_create(target_phys_addr_t  base,
+                                  uint32_t            nr_timers,
+                                  uint32_t            freq,
+                                  qemu_irq           *cpu_irqs,
+                                  int                 base_irq)
+{
+    DeviceState *dev;
+    int i;
+
+    dev = qdev_create(NULL, "grlib,gptimer");
+    qdev_prop_set_uint32(dev, "nr-timers", nr_timers);
+    qdev_prop_set_uint32(dev, "frequency", freq);
+    qdev_prop_set_uint32(dev, "irq-line", base_irq);
+
+    if (qdev_init(dev)) {
+        return NULL;
+    }
+
+    sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+
+    for (i = 0; i<  nr_timers; i++)
+        sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]);
+
+    return dev;
+}
+
+static void grlib_gptimer_enable(GPTimer *timer)
+{
+    assert(timer != NULL);
+
+    DPRINTF("%s id:%d\n", __func__, timer->id);
+
+    ptimer_stop(timer->ptimer);
+
+    if (!(timer->config&  GPTIMER_ENABLE)) {
+        /* Timer disabled */
+        DPRINTF("%s id:%d Timer disabled (config 0x%x)\n", __func__,
+                timer->id, timer->config);
+        return;
+    }
+
+    /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at
+       underflow. Set count + 1 to simulate the GPTimer behavior. */
+
+    DPRINTF("%s id:%d set count 0x%x and run\n",
+            __func__,
+            timer->id,
+            timer->counter + 1);
+
+    ptimer_set_count(timer->ptimer, timer->counter + 1);
+    ptimer_run(timer->ptimer, 1);
+}
+
+static void grlib_gptimer_restart(GPTimer *timer)
+{
+    assert(timer != NULL);
+
+    DPRINTF("%s id:%d reload val: 0x%x\n", __func__, timer->id, timer->reload);
+
+    timer->counter = timer->reload;
+    grlib_gptimer_enable(timer);
+}
+
+static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler)
+{
+    int i = 0;
+    uint32_t value = 0;
+
+    assert(unit != NULL);
+
+
+    if (scaler>  0) {
+        value = unit->freq_hz / (scaler + 1);
+    } else {
+        value = unit->freq_hz;
+    }
+
+    DPRINTF("%s scaler:%d freq:0x%x\n", __func__, scaler, value);
+
+    for (i = 0; i<  unit->nr_timers; i++) {
+        ptimer_set_freq(unit->timers[i].ptimer, value);
+    }
+}
+
+static void grlib_gptimer_hit(void *opaque)
+{
+    GPTimer *timer = opaque;
+    assert(timer != NULL);
+
+    DPRINTF("%s id:%d\n", __func__, timer->id);
+
+    /* Timer expired */
+
+    if (timer->config&  GPTIMER_INT_ENABLE) {
+        /* Set the pending bit (only unset by write in the config register) */
+        timer->config&= GPTIMER_INT_PENDING;

Do you mean |= GPTIMER_INT_PENDING here?

Yep, thanks.

+        qemu_set_irq(timer->irq, 1);

Hmm, this interrupt logic doesn't seem right. You are never clearing
the timer->irq line?

Once you've got the INT_PENDING bit logic right (more on that later),
I suspect you simply want to connect the irq state to the interrupt
pending bit in the config reg. e.g:

qemu_set_irq(timer->irq, !!(timer->config&  GPTIMER_INT_PENDING));

You'll need to update the timer->irq state after every piece of logic
that potentially changes the int-pending bit.


GrLib peripherals just throw interrupts and never expect any kind of
acknowledgment, that's why the interrupt pending bit has to be cleared by
software. IRQs are acknowledged by the CPU at the beginning of the trap
process.

The interrupt pending bit is only useful when all the timers use the same IRQ line and you want to know which one expired. For instance, our software never
read or clear it at all.

writing a zero should keep the IP bit untouched, e.g:

                    unit->timers[id].config&= GPTIMER_INT_PENDING;
                    unit->timers[id].config&= (~value)&  GPTIMER_INT_PENDING;
                    unit->timers[id].config |= value&  (~(GPTIMER_INT_PENDING | 
... other magic bits...));


Right, I've missed the "write 1 to clear" part, but I don't understand your code.
I prefer something clearer, like:

if (value & GPTIMER_INT_PENDING) {
    /* clear pending bit */
    value &= ~GPTIMER_INT_PENDING;
} else {
    /* keep pending bit */
    value |= unit->timers[id].config & GPTIMER_INT_PENDING;
}

--
Fabien Chouteau




reply via email to

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