All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Peter A. G. Crosthwaite" <peter.crosthwaite@petalogix.com>
To: qemu-devel@nongnu.org, monstr@monstr.eu,
	john.williams@petalogix.com, peter.crosthwaite@petalogix.com,
	edgar.iglesias@petalogix.com, duyl@xilinx.com, linnj@xilinx.com,
	paul@codesourcery.com, peter.maydell@linaro.org,
	afaerber@suse.de
Cc: John Linn <john.linn@xilinx.com>
Subject: [Qemu-devel] [RFC PATCH v2 2/4] cadence_ttc: initial version of device model
Date: Tue,  7 Feb 2012 16:19:45 +1000	[thread overview]
Message-ID: <11ee1a59f2bffdbdf37e49911df0428635cbb36f.1328594621.git.peter.crosthwaite@petalogix.com> (raw)
In-Reply-To: <cover.1328594621.git.peter.crosthwaite@petalogix.com>
In-Reply-To: <cover.1328594621.git.peter.crosthwaite@petalogix.com>

Implemented cadence Triple Timer Counter (TCC)

Signed-off-by: Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
Signed-off-by: John Linn <john.linn@xilinx.com>
---
changes from v1
refactored event driven code
marked vmsd as unmigratable

 Makefile.target  |    1 +
 hw/cadence_ttc.c |  399 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 400 insertions(+), 0 deletions(-)
 create mode 100644 hw/cadence_ttc.c

diff --git a/Makefile.target b/Makefile.target
index 620a91d..feefafa 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -338,6 +338,7 @@ obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
 obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += cadence_uart.o
+obj-arm-y += cadence_ttc.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += arm_l2x0.o
 obj-arm-y += arm_mptimer.o
diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c
new file mode 100644
index 0000000..5074e2c
--- /dev/null
+++ b/hw/cadence_ttc.c
@@ -0,0 +1,399 @@
+/*
+ * Xilinx Zynq cadence TTC model
+ *
+ * Copyright (c) 2011 Xilinx Inc.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Written By Haibing Ma
+ *            M. Habib
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+ * 02139, USA.
+ */
+
+#include "sysbus.h"
+#include "qemu-timer.h"
+#include "ptimer.h"
+
+#ifdef CADENCE_TTC_ERR_DEBUG
+#define qemu_debug(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    fflush(stderr); \
+    } while (0);
+#else
+    #define qemu_debug(...)
+#endif
+
+#define COUNTER_INTR_IV     0x00000001
+#define COUNTER_INTR_M1     0x00000002
+#define COUNTER_INTR_M2     0x00000004
+#define COUNTER_INTR_M3     0x00000008
+#define COUNTER_INTR_OV     0x00000010
+#define COUNTER_INTR_EV     0x00000020
+
+#define COUNTER_CTRL_DIS    0x00000001
+#define COUNTER_CTRL_INT    0x00000002
+#define COUNTER_CTRL_DEC    0x00000004
+#define COUNTER_CTRL_MATCH  0x00000008
+#define COUNTER_CTRL_RST    0x00000010
+
+#define CLOCK_CTRL_PS_EN    0x00000001
+#define CLOCK_CTRL_PS_V     0x0000001e
+
+typedef struct {
+    ptimer_state *timer;
+    int freq;
+
+    uint32_t reg_clock;
+    uint32_t reg_count;
+    uint16_t reg_value;
+    uint16_t reg_interval;
+    uint16_t reg_match[3];
+    uint32_t reg_intr;
+    uint32_t reg_intr_en;
+    uint32_t reg_event_ctrl;
+    uint32_t reg_event;
+
+    uint32_t event_interval;
+    int serviced;
+
+    qemu_irq irq;
+} CadenceTimerState;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    CadenceTimerState * timer[3];
+} cadence_ttc_state;
+
+static void cadence_timer_update(CadenceTimerState *s)
+{
+    qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en));
+}
+
+static CadenceTimerState *cadence_timer_from_addr(void *opaque,
+                                        target_phys_addr_t offset)
+{
+    unsigned int index;
+    cadence_ttc_state *s = (cadence_ttc_state *)opaque;
+
+    index = (offset >> 2) % 3;
+
+    return s->timer[index];
+}
+
+static inline int is_between(int x, int a, int b)
+{
+    if (a < b) {
+        return x > a && x < b;
+    }
+    return x < a && x > b;
+}
+
+static void cadence_timer_run(CadenceTimerState *s)
+{
+    int i;
+    int32_t event_interval;
+    int32_t interval = (s->reg_count & COUNTER_CTRL_INT) ?
+            ((int32_t)s->reg_interval + 1) : 0x10000;
+    int32_t next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1 : interval;
+
+    for (i = 0; i < 3; ++i) {
+        if (is_between((int)s->reg_match[i], (int)s->reg_value, next_value)) {
+            next_value = s->reg_match[i];
+        }
+    }
+    event_interval = next_value - (int32_t)s->reg_value;
+    s->event_interval = (event_interval < 0) ? -event_interval : event_interval;
+    s->serviced = 0;
+
+    ptimer_set_limit(s->timer, (uint64_t)s->event_interval, 1);
+    ptimer_run(s->timer, 1);
+}
+
+static uint32_t cadence_counter_value(CadenceTimerState *s)
+{
+    int i;
+    int32_t interval = (s->reg_count & COUNTER_CTRL_INT) ?
+            (int)(s->reg_interval + 1) : 0x10000;
+
+    int32_t r = s->event_interval - ptimer_get_count(s->timer);
+    int32_t x = s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r);
+    int32_t x_mod = (x + interval) % interval;
+
+    if (!s->serviced) {
+        for (i = 0; i < 3; ++i) {
+            if (is_between(s->reg_match[i], s->reg_value, x) ||
+                    s->reg_match[i] == r) {
+                s->reg_intr |= (2 << i);
+            }
+        }
+        if (x_mod != x) {
+            s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
+                COUNTER_INTR_IV : COUNTER_INTR_OV;
+        }
+        s->serviced = 1;
+        cadence_timer_update(s);
+    }
+
+    return (uint32_t)x_mod;
+}
+
+static void cadence_counter_clock(CadenceTimerState *s , uint32_t value)
+{
+    int freq;
+
+    s->reg_clock = value & 0x3f;
+    if (s->reg_clock & CLOCK_CTRL_PS_EN) {
+        freq = s->freq;
+        freq >>= ((value & CLOCK_CTRL_PS_V) >> 1) + 1;
+        ptimer_set_freq(s->timer, freq);
+    }
+}
+
+static void cadence_counter_control(CadenceTimerState *s , uint32_t value)
+{
+    if (value & COUNTER_CTRL_RST) {
+        ptimer_stop(s->timer);
+        s->reg_value = 0;
+        s->event_interval = 0;
+    }
+    if ((s->reg_count ^  value) & COUNTER_CTRL_DIS) { /* start or stop */
+        if (value & COUNTER_CTRL_DIS) { /* stop */
+            ptimer_stop(s->timer);
+            s->reg_value = cadence_counter_value(s);
+        } else {
+            cadence_timer_run(s);
+        }
+    }
+    s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST;
+}
+
+static void cadence_timer_tick(void *opaque)
+{
+    CadenceTimerState *s = opaque;
+
+    s->reg_value = cadence_counter_value(s);
+    cadence_timer_run(s);
+}
+
+static uint32_t cadence_ttc_read_imp(void *opaque, target_phys_addr_t offset)
+{
+    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
+    uint32_t value;
+
+    switch (offset) {
+    case 0x00: /* clock control */
+    case 0x04:
+    case 0x08:
+        return s->reg_clock;
+
+    case 0x0c: /* counter control */
+    case 0x10:
+    case 0x14:
+        return s->reg_count;
+
+    case 0x18: /* counter value */
+    case 0x1c:
+    case 0x20:
+        return cadence_counter_value(s);
+
+    case 0x24: /* reg_interval counter */
+    case 0x28:
+    case 0x2c:
+        return s->reg_interval;
+
+    case 0x30: /* match 1 counter */
+    case 0x34:
+    case 0x38:
+        return s->reg_match[0];
+
+    case 0x3c: /* match 2 counter */
+    case 0x40:
+    case 0x44:
+        return s->reg_match[1];
+
+    case 0x48: /* match 3 counter */
+    case 0x4c:
+    case 0x50:
+        return s->reg_match[2];
+
+    case 0x54: /* interrupt register */
+    case 0x58:
+    case 0x5c:
+        /* cleared after read */
+        value = s->reg_intr;
+        s->reg_intr = 0;
+        return value;
+
+    case 0x60: /* interrupt enable */
+    case 0x64:
+    case 0x68:
+        return s->reg_intr_en;
+
+    case 0x6c:
+    case 0x70:
+    case 0x74:
+        return s->reg_event_ctrl;
+
+    case 0x78:
+    case 0x7c:
+    case 0x80:
+        return s->reg_event;
+
+    default:
+        return 0;
+    }
+}
+
+static uint64_t cadence_ttc_read(void *opaque, target_phys_addr_t offset,
+    unsigned size)
+{
+    uint32_t ret = cadence_ttc_read_imp(opaque, offset);
+
+    qemu_debug("addr: %08x data: %08x\n", offset, ret);
+    return ret;
+}
+
+static void cadence_ttc_write(void *opaque, target_phys_addr_t offset,
+        uint64_t value, unsigned size)
+{
+    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
+
+    qemu_debug("addr: %08x data %08x\n", offset, (unsigned)value);
+
+    switch (offset) {
+    case 0x00: /* clock control */
+    case 0x04:
+    case 0x08:
+        cadence_counter_clock(s, value);
+        break;
+
+    case 0x0c: /* conter control */
+    case 0x10:
+    case 0x14:
+        cadence_counter_control(s, value);
+        break;
+
+    case 0x24: /* interval register */
+    case 0x28:
+    case 0x2c:
+        s->reg_interval = value & 0xffff;
+        break;
+
+    case 0x30: /* match register */
+    case 0x34:
+    case 0x38:
+        s->reg_match[0] = value & 0xffff;
+
+    case 0x3c: /* match register */
+    case 0x40:
+    case 0x44:
+        s->reg_match[1] = value & 0xffff;
+
+    case 0x48: /* match register */
+    case 0x4c:
+    case 0x50:
+        s->reg_match[2] = value & 0xffff;
+        break;
+
+    case 0x54: /* interrupt register */
+    case 0x58:
+    case 0x5c:
+        s->reg_intr &= (~value & 0xfff);
+        break;
+
+    case 0x60: /* interrupt enable */
+    case 0x64:
+    case 0x68:
+        s->reg_intr_en = value & 0x3f;
+        break;
+
+    case 0x6c: /* event control */
+    case 0x70:
+    case 0x74:
+        s->reg_event_ctrl = value & 0x07;
+        break;
+
+    default:
+        return;
+    }
+
+    cadence_timer_update(s);
+}
+
+static const MemoryRegionOps cadence_ttc_ops = {
+    .read = cadence_ttc_read,
+    .write = cadence_ttc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static CadenceTimerState *cadence_timer_init(uint32_t freq)
+{
+    CadenceTimerState *s;
+    QEMUBH *bh;
+
+    s = (CadenceTimerState *)g_malloc0(sizeof(CadenceTimerState));
+    s->freq = freq;
+    s->reg_count = 0x21;
+
+    bh = qemu_bh_new(cadence_timer_tick, s);
+    s->timer = ptimer_init(bh);
+    ptimer_set_freq(s->timer, freq);
+
+    return s;
+}
+
+static int cadence_ttc_init(SysBusDevice *dev)
+{
+    cadence_ttc_state *s = FROM_SYSBUS(cadence_ttc_state, dev);
+    int i;
+
+    for (i = 0; i < 3; ++i) {
+        s->timer[i] = cadence_timer_init(2500000);
+        sysbus_init_irq(dev, &s->timer[i]->irq);
+    }
+
+    memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+/* FIMXE: add vmsd support */
+
+static const VMStateDescription vmstate_cadence_ttc = {
+    .name = "cadence_TTC",
+    .unmigratable = 1,
+};
+
+static void cadence_ttc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = cadence_ttc_init;
+    dc->vmsd = &vmstate_cadence_ttc;
+}
+
+static TypeInfo cadence_ttc_info = {
+    .name  = "cadence_ttc",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(cadence_ttc_state),
+    .class_init = cadence_ttc_class_init,
+};
+
+static void cadence_ttc_register(void)
+{
+    type_register_static(&cadence_ttc_info);
+}
+
+device_init(cadence_ttc_register)
-- 
1.7.3.2

  parent reply	other threads:[~2012-02-07  6:14 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-07  6:19 [Qemu-devel] [RFC PATCH v2 0/4]Zynq-7000 EPP platform model Peter A. G. Crosthwaite
2012-02-07  6:19 ` [Qemu-devel] [RFC PATCH v2 1/4] cadence_uart: initial version of device model Peter A. G. Crosthwaite
2012-02-07  6:19 ` Peter A. G. Crosthwaite [this message]
2012-02-07 11:28   ` [Qemu-devel] [RFC PATCH v2 2/4] cadence_ttc: " Paul Brook
2012-02-08  7:27     ` Peter Crosthwaite
2012-02-08 10:15       ` Paul Brook
2012-02-08 10:35         ` Peter Crosthwaite
2012-02-08 12:35           ` Paul Brook
2012-02-08 12:47             ` Peter Crosthwaite
2012-02-07  6:19 ` [Qemu-devel] [RFC PATCH v2 3/4] cadence_gem: " Peter A. G. Crosthwaite
2012-02-07  6:19 ` [Qemu-devel] [RFC PATCH v2 4/4] xilinx_zynq: machine model initial version Peter A. G. Crosthwaite
2012-02-07 11:25 ` [Qemu-devel] [RFC PATCH v2 0/4]Zynq-7000 EPP platform model Paul Brook
2012-02-08  8:24   ` Peter Crosthwaite

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=11ee1a59f2bffdbdf37e49911df0428635cbb36f.1328594621.git.peter.crosthwaite@petalogix.com \
    --to=peter.crosthwaite@petalogix.com \
    --cc=afaerber@suse.de \
    --cc=duyl@xilinx.com \
    --cc=edgar.iglesias@petalogix.com \
    --cc=john.linn@xilinx.com \
    --cc=john.williams@petalogix.com \
    --cc=linnj@xilinx.com \
    --cc=monstr@monstr.eu \
    --cc=paul@codesourcery.com \
    --cc=peter.maydell@linaro.org \
    --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.