All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alistair Francis <alistair.francis@xilinx.com>
To: qemu-devel@nongnu.org, peter.maydell@linaro.org
Cc: alistair.francis@xilinx.com, crosthwaitepeter@gmail.com,
	edgar.iglesias@xilinx.com, edgar.iglesias@gmail.com,
	afaerber@suse.de, fred.konrad@greensocs.com,
	alex.bennee@linaro.org
Subject: [Qemu-devel] [PATCH v6 02/13] register: Add Register API
Date: Thu, 12 May 2016 15:45:56 -0700	[thread overview]
Message-ID: <8db13b5c1d4b5a9afed38f29fcb2b4b8c4f5aa57.1463093051.git.alistair.francis@xilinx.com> (raw)
In-Reply-To: <cover.1463093051.git.alistair.francis@xilinx.com>

This API provides some encapsulation of registers and factors our some
common functionality to common code. Bits of device state (usually MMIO
registers), often have all sorts of access restrictions and semantics
associated with them. This API allow you to define what those
restrictions are on a bit-by-bit basis.

Helper functions are then used to access the register which observe the
semantics defined by the RegisterAccessInfo struct.

Some features:
Bits can be marked as read_only (ro field)
Bits can be marked as write-1-clear (w1c field)
Bits can be marked as reserved (rsvd field)
Reset values can be defined (reset)
Bits can be marked clear on read (cor)
Pre and post action callbacks can be added to read and write ops
Verbose debugging info can be enabled/disabled

Useful for defining device register spaces in a data driven way. Cuts
down on a lot of the verbosity and repetition in the switch-case blocks
in the standard foo_mmio_read/write functions.

Also useful for automated generation of device models from hardware
design sources.

Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Signed-off-by: Alistair Francis <alistair.francis@xilinx.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
---
V5:
 - Convert to using only one memory region
V4:
 - Rebase
 - Remove the guest error masking
 - Simplify the unimplemented masking
 - Use the reserved value in the write calculations
 - Remove read_lite and write_lite
 - General fixes to asserts and log printing
V3:
 - Address some comments from Fred

 hw/core/Makefile.objs |   1 +
 hw/core/register.c    | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/register.h | 110 +++++++++++++++++++++++++++++++++++++
 3 files changed, 260 insertions(+)
 create mode 100644 hw/core/register.c
 create mode 100644 include/hw/register.h

diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index abb3560..bf95db5 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -14,4 +14,5 @@ common-obj-$(CONFIG_SOFTMMU) += machine.o
 common-obj-$(CONFIG_SOFTMMU) += null-machine.o
 common-obj-$(CONFIG_SOFTMMU) += loader.o
 common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
+common-obj-$(CONFIG_SOFTMMU) += register.o
 common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o
diff --git a/hw/core/register.c b/hw/core/register.c
new file mode 100644
index 0000000..5e6f621
--- /dev/null
+++ b/hw/core/register.c
@@ -0,0 +1,149 @@
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/register.h"
+#include "hw/qdev.h"
+#include "qemu/log.h"
+
+static inline void register_write_val(RegisterInfo *reg, uint64_t val)
+{
+    g_assert(reg->data);
+
+    switch (reg->data_size) {
+    case 1:
+        *(uint8_t *)reg->data = val;
+        break;
+    case 2:
+        *(uint16_t *)reg->data = val;
+        break;
+    case 4:
+        *(uint32_t *)reg->data = val;
+        break;
+    case 8:
+        *(uint64_t *)reg->data = val;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static inline uint64_t register_read_val(RegisterInfo *reg)
+{
+    switch (reg->data_size) {
+    case 1:
+        return *(uint8_t *)reg->data;
+    case 2:
+        return *(uint16_t *)reg->data;
+    case 4:
+        return *(uint32_t *)reg->data;
+    case 8:
+        return *(uint64_t *)reg->data;
+    default:
+        g_assert_not_reached();
+    }
+    return 0; /* unreachable */
+}
+
+void register_write(RegisterInfo *reg, uint64_t val, uint64_t we,
+                    const char* prefix, bool debug)
+{
+    uint64_t old_val, new_val, test, no_w_mask;
+    const RegisterAccessInfo *ac;
+
+    assert(reg);
+
+    ac = reg->access;
+
+    if (!ac || !ac->name) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to undefined device state "
+                      "(written value: %#" PRIx64 ")\n", prefix, val);
+        return;
+    }
+
+    old_val = reg->data ? register_read_val(reg) : ac->reset;
+
+    test = (old_val ^ val) & ac->rsvd;
+    if (test) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: change of value in reserved bit"
+                      "fields: %#" PRIx64 ")\n", prefix, test);
+    }
+
+    test = val & ac->unimp;
+    if (test) {
+        qemu_log_mask(LOG_UNIMP,
+                      "%s:%s writing %#" PRIx64 " to unimplemented bits:" \
+                      " %#" PRIx64 "",
+                      prefix, reg->access->name, val, ac->unimp);
+    }
+
+    /* Create the no write mask based on the read only, write to clear and
+     * reserved bit masks.
+     */
+    no_w_mask = ac->ro | ac->w1c | ac->rsvd | ~we;
+    new_val = (val & ~no_w_mask) | (old_val & no_w_mask);
+    new_val &= ~(val & ac->w1c);
+
+    if (ac->pre_write) {
+        new_val = ac->pre_write(reg, new_val);
+    }
+
+    if (debug) {
+        qemu_log("%s:%s: write of value %#" PRIx64 "\n", prefix, ac->name,
+                 new_val);
+    }
+
+    register_write_val(reg, new_val);
+
+    if (ac->post_write) {
+        ac->post_write(reg, new_val);
+    }
+}
+
+uint64_t register_read(RegisterInfo *reg, const char* prefix, bool debug)
+{
+    uint64_t ret;
+    const RegisterAccessInfo *ac;
+
+    assert(reg);
+
+    ac = reg->access;
+    if (!ac || !ac->name) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from undefined device state\n",
+                      prefix);
+        return 0;
+    }
+
+    ret = reg->data ? register_read_val(reg) : ac->reset;
+
+    register_write_val(reg, ret & ~ac->cor);
+
+    if (ac->post_read) {
+        ret = ac->post_read(reg, ret);
+    }
+
+    if (debug) {
+        qemu_log("%s:%s: read of value %#" PRIx64 "\n", prefix,
+                 ac->name, ret);
+    }
+
+    return ret;
+}
+
+void register_reset(RegisterInfo *reg)
+{
+    g_assert(reg);
+
+    if (!reg->data || !reg->access) {
+        return;
+    }
+
+    register_write_val(reg, reg->access->reset);
+}
diff --git a/include/hw/register.h b/include/hw/register.h
new file mode 100644
index 0000000..07d0616
--- /dev/null
+++ b/include/hw/register.h
@@ -0,0 +1,110 @@
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef REGISTER_H
+#define REGISTER_H
+
+#include "exec/memory.h"
+
+typedef struct RegisterInfo RegisterInfo;
+typedef struct RegisterAccessInfo RegisterAccessInfo;
+
+/**
+ * Access description for a register that is part of guest accessible device
+ * state.
+ *
+ * @name: String name of the register
+ * @ro: whether or not the bit is read-only
+ * @w1c: bits with the common write 1 to clear semantic.
+ * @reset: reset value.
+ * @cor: Bits that are clear on read
+ * @rsvd: Bits that are reserved and should not be changed
+ *
+ * @pre_write: Pre write callback. Passed the value that's to be written,
+ * immediately before the actual write. The returned value is what is written,
+ * giving the handler a chance to modify the written value.
+ * @post_write: Post write callback. Passed the written value. Most write side
+ * effects should be implemented here.
+ *
+ * @post_read: Post read callback. Passes the value that is about to be returned
+ * for a read. The return value from this function is what is ultimately read,
+ * allowing this function to modify the value before return to the client.
+ */
+
+struct RegisterAccessInfo {
+    const char *name;
+    uint64_t ro;
+    uint64_t w1c;
+    uint64_t reset;
+    uint64_t cor;
+    uint64_t rsvd;
+    uint64_t unimp;
+
+    uint64_t (*pre_write)(RegisterInfo *reg, uint64_t val);
+    void (*post_write)(RegisterInfo *reg, uint64_t val);
+
+    uint64_t (*post_read)(RegisterInfo *reg, uint64_t val);
+};
+
+/**
+ * A register that is part of guest accessible state
+ * @data: pointer to the register data. Will be cast
+ * to the relevant uint type depending on data_size.
+ * @data_size: Size of the register in bytes. Must be
+ * 1, 2, 4 or 8
+ *
+ * @access: Access description of this register
+ *
+ * @debug: Whether or not verbose debug is enabled
+ * @prefix: String prefix for log and debug messages
+ *
+ * @opaque: Opaque data for the register
+ */
+
+struct RegisterInfo {
+    /* <public> */
+    void *data;
+    int data_size;
+
+    const RegisterAccessInfo *access;
+
+    void *opaque;
+};
+
+/**
+ * write a value to a register, subject to its restrictions
+ * @reg: register to write to
+ * @val: value to write
+ * @we: write enable mask
+ * @prefix: The device prefix that should be printed before the register name
+ * @debug: Should the write operation debug information be printed?
+ */
+
+void register_write(RegisterInfo *reg, uint64_t val, uint64_t we,
+                    const char* prefix, bool debug);
+
+/**
+ * read a value from a register, subject to its restrictions
+ * @reg: register to read from
+ * @prefix: The device prefix that should be printed before the register name
+ * @debug: Should the read operation debug information be printed?
+ * returns: value read
+ */
+
+uint64_t register_read(RegisterInfo *reg, const char* prefix, bool debug);
+
+/**
+ * reset a register
+ * @reg: register to reset
+ */
+
+void register_reset(RegisterInfo *reg);
+
+#endif
-- 
2.7.4

  parent reply	other threads:[~2016-05-12 22:49 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-12 22:45 [Qemu-devel] [PATCH v6 00/13] data-driven device registers Alistair Francis
2016-05-12 22:45 ` [Qemu-devel] [PATCH v6 01/13] bitops: Add MAKE_64BIT_MASK macro Alistair Francis
2016-06-09 18:46   ` Peter Maydell
2016-06-13 20:57     ` Alistair Francis
2016-05-12 22:45 ` Alistair Francis [this message]
2016-06-09 18:55   ` [Qemu-devel] [PATCH v6 02/13] register: Add Register API Peter Maydell
2016-06-13 21:01     ` Alistair Francis
2016-05-12 22:45 ` [Qemu-devel] [PATCH v6 03/13] register: Add Memory API glue Alistair Francis
2016-06-09 13:08   ` KONRAD Frederic
2016-06-21 16:52     ` Alistair Francis
2016-06-09 19:03   ` Peter Maydell
2016-06-21  0:46     ` Alistair Francis
2016-06-21  6:48       ` Peter Maydell
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 04/13] register: Define REG and FIELD macros Alistair Francis
2016-06-10 10:52   ` Peter Maydell
2016-06-21 17:41     ` Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 05/13] register: QOMify Alistair Francis
2016-06-10 10:55   ` Peter Maydell
2016-06-21 16:49     ` Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 06/13] register: Add block initialise helper Alistair Francis
2016-06-10 11:02   ` Peter Maydell
2016-06-21 18:25     ` Alistair Francis
2016-06-21 19:45       ` Peter Maydell
2016-06-21 22:21         ` Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 07/13] dma: Add Xilinx Zynq devcfg device model Alistair Francis
2016-06-10 11:16   ` Peter Maydell
2016-06-21 18:34     ` Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 08/13] xilinx_zynq: Connect devcfg to the Zynq machine model Alistair Francis
2016-06-10 11:19   ` Peter Maydell
2016-06-21 18:36     ` Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 09/13] qdev: Define qdev_get_gpio_out Alistair Francis
2016-06-10 11:48   ` Peter Maydell
2016-06-21 19:05     ` Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 10/13] irq: Add opaque setter routine Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 11/13] register: Add GPIO API Alistair Francis
2016-06-10 11:52   ` Peter Maydell
2016-06-21 22:34     ` Alistair Francis
2016-05-12 22:46 ` [Qemu-devel] [PATCH v6 12/13] misc: Introduce ZynqMP IOU SLCR Alistair Francis
2016-06-09  0:30 ` [Qemu-devel] [PATCH v6 00/13] data-driven device registers Alistair Francis
2016-06-10 11:53   ` Peter Maydell
2016-06-13 21:11     ` Alistair Francis

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=8db13b5c1d4b5a9afed38f29fcb2b4b8c4f5aa57.1463093051.git.alistair.francis@xilinx.com \
    --to=alistair.francis@xilinx.com \
    --cc=afaerber@suse.de \
    --cc=alex.bennee@linaro.org \
    --cc=crosthwaitepeter@gmail.com \
    --cc=edgar.iglesias@gmail.com \
    --cc=edgar.iglesias@xilinx.com \
    --cc=fred.konrad@greensocs.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.