From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55402) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZKXue-0005CX-9d for qemu-devel@nongnu.org; Wed, 29 Jul 2015 16:25:21 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZKXuZ-0002ze-GU for qemu-devel@nongnu.org; Wed, 29 Jul 2015 16:25:16 -0400 Received: from mail-bn1bon0055.outbound.protection.outlook.com ([157.56.111.55]:45688 helo=na01-bn1-obe.outbound.protection.outlook.com) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZKXuZ-0002zX-BX for qemu-devel@nongnu.org; Wed, 29 Jul 2015 16:25:11 -0400 From: Alistair Francis Date: Wed, 29 Jul 2015 13:25:05 -0700 Message-ID: <44974299264a33f6879bcafee73c519a58a3b97a.1438200827.git.alistair.francis@xilinx.com> In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain Subject: [Qemu-devel] [PATCH v1 13/15] register: Add GPIO API List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: edgar.iglesias@xilinx.com, peter.maydell@linaro.org, alistair.francis@xilinx.com, crosthwaitepeter@gmail.com, edgar.iglesias@gmail.com, afaerber@suse.de From: Peter Crosthwaite 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 [ 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 --- hw/core/register.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/register.h | 26 +++++++++++++ 2 files changed, 120 insertions(+), 0 deletions(-) diff --git a/hw/core/register.c b/hw/core/register.c index 4e59122..9ab118b 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -135,6 +135,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); } @@ -177,18 +179,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; @@ -197,6 +266,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 || @@ -276,6 +369,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 f3e4c2c..7a4c1c7 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; uint8_t flags; @@ -161,6 +176,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 -- 1.7.1