qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Alistair Francis <alistair.francis@xilinx.com>
To: qemu-devel@nongnu.org, peter.maydell@linaro.org,
	fred.konrad@greensocs.com
Cc: edgar.iglesias@xilinx.com, edgar.iglesias@gmail.com,
	crosthwaitepeter@gmail.com, afaerber@suse.de,
	alistair.francis@xilinx.com
Subject: [Qemu-devel] [PATCH v3 14/16] register: Add GPIO API
Date: Fri, 29 Jan 2016 17:01:15 -0800	[thread overview]
Message-ID: <3fceee1de45a06a01119003f253c78ca42a17910.1454115217.git.alistair.francis@xilinx.com> (raw)
In-Reply-To: <cover.1454115217.git.alistair.francis@xilinx.com>

From: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

Add GPIO functionality to the register API. This allows association
and automatic connection of GPIOs to bits in registers. GPIO inputs
will attach to handlers that automatically set read-only bits in
registers. GPIO outputs will be updated to reflect their field value
when their respective registers are written (or reset). Supports
active low GPIOs.

This is particularly effective for implementing system level
controllers, where heterogenous collections of control signals are
placed is a SoC specific peripheral then propagated all over the
system.

Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
[ EI Changes:
  * register: Add a polarity field to GPIO connections
              Makes it possible to directly connect active low signals
              to generic interrupt pins.
]
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Signed-off-by: Alistair Francis <alistair.francis@xilinx.com>
---

 hw/core/register.c    | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/register.h | 26 ++++++++++++++
 2 files changed, 120 insertions(+)

diff --git a/hw/core/register.c b/hw/core/register.c
index 4d7dd95..f461cd7 100644
--- a/hw/core/register.c
+++ b/hw/core/register.c
@@ -138,6 +138,8 @@ void register_write(RegisterInfo *reg, uint64_t val, uint64_t we)
 
 register_write_fast:
     register_write_val(reg, new_val);
+    register_refresh_gpios(reg, old_val);
+
     if (ac->post_write) {
         ac->post_write(reg, new_val);
     }
@@ -180,18 +182,85 @@ uint64_t register_read(RegisterInfo *reg)
 void register_reset(RegisterInfo *reg)
 {
     assert(reg);
+    uint64_t old_val;
 
     if (!reg->data || !reg->access) {
         return;
     }
 
+    old_val = register_read_val(reg);
+
     register_write_val(reg, reg->access->reset);
+    register_refresh_gpios(reg, old_val);
+}
+
+void register_refresh_gpios(RegisterInfo *reg, uint64_t old_value)
+{
+    const RegisterAccessInfo *ac;
+    const RegisterGPIOMapping *gpio;
+
+    ac = reg->access;
+    for (gpio = ac->gpios; gpio && gpio->name; gpio++) {
+        int i;
+
+        if (gpio->input) {
+            continue;
+        }
+
+        for (i = 0; i < gpio->num; ++i) {
+            uint64_t gpio_value, gpio_value_old;
+
+            qemu_irq gpo = qdev_get_gpio_out_named(DEVICE(reg), gpio->name, i);
+            gpio_value_old = extract64(old_value,
+                                       gpio->bit_pos + i * gpio->width,
+                                       gpio->width) ^ gpio->polarity;
+            gpio_value = extract64(register_read_val(reg),
+                                   gpio->bit_pos + i * gpio->width,
+                                   gpio->width) ^ gpio->polarity;
+            if (!(gpio_value_old ^ gpio_value)) {
+                continue;
+            }
+            if (reg->debug && gpo) {
+                qemu_log("refreshing gpio out %s to %" PRIx64 "\n",
+                         gpio->name, gpio_value);
+            }
+            qemu_set_irq(gpo, gpio_value);
+        }
+    }
+}
+
+typedef struct DeviceNamedGPIOHandlerOpaque {
+    DeviceState *dev;
+    const char *name;
+} DeviceNamedGPIOHandlerOpaque;
+
+static void register_gpio_handler(void *opaque, int n, int level)
+{
+    DeviceNamedGPIOHandlerOpaque *gho = opaque;
+    RegisterInfo *reg = REGISTER(gho->dev);
+
+    const RegisterAccessInfo *ac;
+    const RegisterGPIOMapping *gpio;
+
+    ac = reg->access;
+    for (gpio = ac->gpios; gpio && gpio->name; gpio++) {
+        if (gpio->input && !strcmp(gho->name, gpio->name)) {
+            register_write_val(reg, deposit64(register_read_val(reg),
+                                              gpio->bit_pos + n * gpio->width,
+                                              gpio->width,
+                                              level ^ gpio->polarity));
+            return;
+        }
+    }
+
+    abort();
 }
 
 void register_init(RegisterInfo *reg)
 {
     assert(reg);
     const RegisterAccessInfo *ac;
+    const RegisterGPIOMapping *gpio;
 
     if (!reg->data || !reg->access) {
         return;
@@ -200,6 +269,30 @@ void register_init(RegisterInfo *reg)
     object_initialize((void *)reg, sizeof(*reg), TYPE_REGISTER);
 
     ac = reg->access;
+    for (gpio = ac->gpios; gpio && gpio->name; gpio++) {
+        if (!gpio->num) {
+            ((RegisterGPIOMapping *)gpio)->num = 1;
+        }
+        if (!gpio->width) {
+            ((RegisterGPIOMapping *)gpio)->width = 1;
+        }
+        if (gpio->input) {
+            DeviceNamedGPIOHandlerOpaque gho = {
+                .name = gpio->name,
+                .dev = DEVICE(reg),
+            };
+            qemu_irq irq;
+
+            qdev_init_gpio_in_named(DEVICE(reg), register_gpio_handler,
+                                    gpio->name, gpio->num);
+            irq = qdev_get_gpio_in_named(DEVICE(reg), gpio->name, gpio->num);
+            qemu_irq_set_opaque(irq, g_memdup(&gho, sizeof(gho)));
+        } else {
+            qemu_irq *gpos = g_new0(qemu_irq, gpio->num);
+
+            qdev_init_gpio_out_named(DEVICE(reg), gpos, gpio->name, gpio->num);
+        }
+    }
 
     /* if there are no debug msgs and no RMW requirement, mark for fast write */
     reg->write_lite = reg->debug || ac->ro || ac->w1c || ac->pre_write ||
@@ -279,6 +372,7 @@ void register_init_block32(DeviceState *owner, const RegisterAccessInfo *rae,
             .opaque = owner,
         };
         register_init(r);
+        qdev_pass_all_gpios(DEVICE(r), owner);
 
         memory_region_init_io(&r->mem, OBJECT(owner), ops, r, r->access->name,
                               sizeof(uint32_t));
diff --git a/include/hw/register.h b/include/hw/register.h
index 30dedbf..9a8a94b 100644
--- a/include/hw/register.h
+++ b/include/hw/register.h
@@ -13,6 +13,7 @@
 
 #include "hw/qdev-core.h"
 #include "exec/memory.h"
+#include "hw/irq.h"
 
 typedef struct RegisterInfo RegisterInfo;
 typedef struct RegisterAccessInfo RegisterAccessInfo;
@@ -28,6 +29,18 @@ typedef struct RegisterAccessError {
     const char *reason;
 } RegisterAccessError;
 
+#define REG_GPIO_POL_HIGH 0
+#define REG_GPIO_POL_LOW  1
+
+typedef struct RegisterGPIOMapping {
+    const char *name;
+    uint8_t bit_pos;
+    bool input;
+    bool polarity;
+    uint8_t num;
+    uint8_t width;
+} RegisterGPIOMapping;
+
 /**
  * Access description for a register that is part of guest accessible device
  * state.
@@ -78,6 +91,8 @@ struct RegisterAccessInfo {
 
     uint64_t (*post_read)(RegisterInfo *reg, uint64_t val);
 
+    const RegisterGPIOMapping *gpios;
+
     struct {
         hwaddr addr;
     } decode;
@@ -157,6 +172,17 @@ void register_reset(RegisterInfo *reg);
 void register_init(RegisterInfo *reg);
 
 /**
+ * Refresh GPIO outputs based on diff between old value register current value.
+ * GPIOs are refreshed for fields where the old value differs to the current
+ * value.
+ *
+ * @reg: Register to refresh GPIO outs
+ * @old_value: previous value of register
+ */
+
+void register_refresh_gpios(RegisterInfo *reg, uint64_t old_value);
+
+/**
  * Memory API MMIO write handler that will write to a Register API register.
  *  _be for big endian variant and _le for little endian.
  * @opaque: RegisterInfo to write to
-- 
2.5.0

  parent reply	other threads:[~2016-01-30  1:04 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-30  1:00 [Qemu-devel] [PATCH v3 00/16] data-driven device registers Alistair Francis
2016-01-30  1:00 ` [Qemu-devel] [PATCH v3 01/16] memory: Allow subregions to not be printed by info mtree Alistair Francis
2016-01-30  1:00 ` [Qemu-devel] [PATCH v3 02/16] register: Add Register API Alistair Francis
2016-02-09 16:06   ` Alex Bennée
2016-02-09 19:35     ` Alistair Francis
2016-02-09 22:29       ` Peter Crosthwaite
2016-01-30  1:00 ` [Qemu-devel] [PATCH v3 03/16] register: Add Memory API glue Alistair Francis
2016-01-30  1:00 ` [Qemu-devel] [PATCH v3 04/16] register: Add support for decoding information Alistair Francis
2016-01-30  1:00 ` [Qemu-devel] [PATCH v3 05/16] register: Define REG and FIELD macros Alistair Francis
2016-01-30  1:00 ` [Qemu-devel] [PATCH v3 06/16] register: QOMify Alistair Francis
2016-02-09 11:49   ` Alex Bennée
2016-02-09 18:09     ` Alistair Francis
2016-01-30  1:00 ` [Qemu-devel] [PATCH v3 07/16] register: Add block initialise helper Alistair Francis
2016-02-09 16:12   ` Alex Bennée
2016-02-09 19:50     ` Alistair Francis
2016-01-30  1:01 ` [Qemu-devel] [PATCH v3 08/16] bitops: Add ONES macro Alistair Francis
2016-01-30  1:01 ` [Qemu-devel] [PATCH v3 09/16] dma: Add Xilinx Zynq devcfg device model Alistair Francis
2016-02-09 17:08   ` Alex Bennée
2016-02-09 21:47     ` Alistair Francis
2016-01-30  1:01 ` [Qemu-devel] [PATCH v3 10/16] xilinx_zynq: add devcfg to machine model Alistair Francis
2016-01-30  1:01 ` [Qemu-devel] [PATCH v3 11/16] qdev: Define qdev_get_gpio_out Alistair Francis
2016-01-30  1:01 ` [Qemu-devel] [PATCH v3 12/16] qdev: Add qdev_pass_all_gpios API Alistair Francis
2016-01-30  1:01 ` [Qemu-devel] [PATCH v3 13/16] irq: Add opaque setter routine Alistair Francis
2016-01-30  1:01 ` Alistair Francis [this message]
2016-01-30  1:01 ` [Qemu-devel] [PATCH v3 15/16] misc: Introduce ZynqMP IOU SLCR Alistair Francis
2016-02-09 17:22 ` [Qemu-devel] [PATCH v3 00/16] data-driven device registers Alex Bennée
2016-02-09 21:56   ` 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=3fceee1de45a06a01119003f253c78ca42a17910.1454115217.git.alistair.francis@xilinx.com \
    --to=alistair.francis@xilinx.com \
    --cc=afaerber@suse.de \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).