summaryrefslogtreecommitdiffstats
path: root/hw/core
diff options
context:
space:
mode:
Diffstat (limited to 'hw/core')
-rw-r--r--hw/core/Makefile.objs1
-rw-r--r--hw/core/register.c160
2 files changed, 161 insertions, 0 deletions
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 82a9ef84f8..cfd4840397 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -15,4 +15,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 0000000000..033b03c438
--- /dev/null
+++ b/hw/core/register.c
@@ -0,0 +1,160 @@
+/*
+ * Register Definition API
+ *
+ * Copyright (c) 2016 Xilinx Inc.
+ * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
+ *
+ * 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.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#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, uint64_t re, 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 & re));
+
+ /* Mask based on the read enable size */
+ ret &= re;
+
+ 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);
+}