@@ -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));
@@ -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