[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC
From: |
Peter Crosthwaite |
Subject: |
Re: [Qemu-devel] [PATCH v3 1/2] zynq_gpio: GPIO model for Zynq SoC |
Date: |
Tue, 20 Jan 2015 14:05:18 -0800 |
Hi Colin,
Sorry about the delay, review below.
On Tue, Jan 6, 2015 at 2:26 PM, Colin Leitner
<address@hidden> wrote:
> Based on the pl061 model. This model implements all four banks with 32 I/Os
> each.
>
> The I/Os are placed in named groups:
>
> * bankX_in for the 32 inputs of each bank
> * bankX_out for the 32 outputs of each bank
>
> Basic I/O and IRQ support tested with the Zynq GPIO driver in Linux 3.12.
>
> Signed-off-by: Colin Leitner <address@hidden>
> ---
> hw/gpio/Makefile.objs | 1 +
> hw/gpio/zynq-gpio.c | 402
> +++++++++++++++++++++++++++++++++++++++++++
> include/hw/gpio/zynq-gpio.h | 79 +++++++++
> 3 files changed, 482 insertions(+)
> create mode 100644 hw/gpio/zynq-gpio.c
> create mode 100644 include/hw/gpio/zynq-gpio.h
>
> diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
> index 1abcf17..c927c66 100644
> --- a/hw/gpio/Makefile.objs
> +++ b/hw/gpio/Makefile.objs
> @@ -5,3 +5,4 @@ common-obj-$(CONFIG_ZAURUS) += zaurus.o
> common-obj-$(CONFIG_E500) += mpc8xxx.o
>
> obj-$(CONFIG_OMAP) += omap_gpio.o
> +obj-$(CONFIG_ZYNQ) += zynq-gpio.o
> diff --git a/hw/gpio/zynq-gpio.c b/hw/gpio/zynq-gpio.c
> new file mode 100644
> index 0000000..8cf4262
> --- /dev/null
> +++ b/hw/gpio/zynq-gpio.c
> @@ -0,0 +1,402 @@
> +/*
> + * Zynq General Purpose IO
> + *
> + * Copyright (C) 2014 Colin Leitner <address@hidden>
> + *
> + * Based on the PL061 model:
> + * Copyright (c) 2007 CodeSourcery.
> + * Written by Paul Brook
> + *
> + * This code is licensed under the GPL.
> + */
> +
> +/*
> + * We model all banks as if they were fully populated. MIO pins are usually
> + * limited to 54 pins, but this is probably device dependent and shouldn't
> + * cause too much trouble. One noticeable difference is the reset value of
> + * INT_TYPE_1, which is 0x003fffff according to the TRM and 0xffffffff here.
> + *
> + * The output enable pins are not modeled.
> + */
> +
> +#include "hw/gpio/zynq-gpio.h"
> +#include "qemu/bitops.h"
> +
> +#ifndef ZYNQ_GPIO_ERR_DEBUG
> +#define ZYNQ_GPIO_ERR_DEBUG 0
> +#endif
> +
> +#define DB_PRINT_L(lvl, fmt, args...) do {\
> + if (ZYNQ_GPIO_ERR_DEBUG >= lvl) {\
> + qemu_log("zynq-gpio: %s:" fmt, __func__, ## args);\
> + } \
> +} while (0);
> +
> +#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
> +
> +static void zynq_gpio_update_out(ZynqGPIOBank *bank)
> +{
> + uint32_t changed;
> + uint32_t mask;
> + uint32_t out;
> + int i;
> +
> + DB_PRINT("dir = %d, data = %d\n", bank->dir, bank->out_data);
> +
> + /*
> + * We assume non-driven (DIRM = 0) outputs float high. On real hardware
> this
> + * could be different, but here we have to decide which value to set the
> + * output IRQ to if the direction register switches the I/O to an input.
> + */
> + /* FIXME: This is board dependent. */
> + out = (bank->out_data & bank->dir) | ~bank->dir;
> +
> + changed = bank->old_out_data ^ out;
> + bank->old_out_data = out;
> +
> + for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
> + mask = 1 << i;
> + if (changed & mask) {
> + DB_PRINT("Set output %d = %d\n", i, (out & mask) != 0);
> + qemu_set_irq(bank->out[i], (out & mask) != 0);
> + }
> + }
> +}
> +
> +static void zynq_gpio_update_in(ZynqGPIOBank *bank)
> +{
> + uint32_t changed;
> + uint32_t mask;
> + int i;
> +
> + changed = bank->old_in_data ^ bank->in_data;
> + bank->old_in_data = bank->in_data;
> +
> + for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
> + mask = 1 << i;
> + if (changed & mask) {
> + DB_PRINT("Changed input %d = %d\n", i, (bank->in_data & mask) !=
> 0);
> +
> + if (bank->itype & mask) {
> + /* Edge interrupt */
> + if (bank->iany & mask) {
> + /* Any edge triggers the interrupt */
> + bank->istat |= mask;
> + } else {
> + /* Edge is selected by INT_POLARITY */
> + bank->istat |= ~(bank->in_data ^ bank->ipolarity) & mask;
> + }
> + }
> + }
> + }
> +
> + /* Level interrupt */
> + bank->istat |= ~(bank->in_data ^ bank->ipolarity) & ~bank->itype;
> +
> + DB_PRINT("istat = %08X\n", bank->istat);
> +}
> +
> +static void zynq_gpio_set_in_irq(ZynqGPIOState *s)
> +{
> + int b;
> + uint32_t istat = 0;
> +
> + for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> + ZynqGPIOBank *bank = &s->banks[b];
> +
> + istat |= bank->istat & ~bank->imask;
> + }
> +
> + DB_PRINT("IRQ = %d\n", istat != 0);
> +
> + qemu_set_irq(s->irq, istat != 0);
> +}
> +
> +static void zynq_gpio_update(ZynqGPIOState *s)
> +{
> + int b;
> +
> + for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> + ZynqGPIOBank *bank = &s->banks[b];
> +
> + zynq_gpio_update_out(bank);
> + zynq_gpio_update_in(bank);
> + }
> +
> + zynq_gpio_set_in_irq(s);
> +}
> +
> +static uint64_t zynq_gpio_read(void *opaque, hwaddr offset,
> + unsigned int size)
> +{
> + ZynqGPIOState *s = opaque;
> + int b;
> + int shift;
> + ZynqGPIOBank *bank;
> +
> + switch (offset) {
> + case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
> + b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
> + shift = (offset & 0x8) ? 16 : 0;
> + return extract32(s->banks[b].mask_data, shift, 16);
> +
> + case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
> + b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
> + return s->banks[b].out_data;
> +
> + case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
> + b = (offset - ZYNQ_GPIO_REG_DATA_0_RO) / 4;
> + return s->banks[b].in_data;
> + }
> +
> + if (offset < 0x204 || offset > 0x2e4) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n",
> offset);
> + return 0;
> + }
> +
> + b = (offset - 0x200) / 0x40;
> + bank = &s->banks[b];
> +
> + switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
> + case ZYNQ_GPIO_BANK_REG_DIRM:
> + return bank->dir;
> + case ZYNQ_GPIO_BANK_REG_OEN:
> + return bank->oen;
> + case ZYNQ_GPIO_BANK_REG_INT_MASK:
> + return bank->imask;
> + case ZYNQ_GPIO_BANK_REG_INT_EN:
> + return 0;
> + case ZYNQ_GPIO_BANK_REG_INT_DIS:
> + return 0;
> + case ZYNQ_GPIO_BANK_REG_INT_STAT:
> + return bank->istat;
> + case ZYNQ_GPIO_BANK_REG_INT_TYPE:
> + return bank->itype;
> + case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
> + return bank->ipolarity;
> + case ZYNQ_GPIO_BANK_REG_INT_ANY:
> + return bank->iany;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "zynq_gpio_read: Bad offset %" HWADDR_PRIx "\n",
> offset);
> + return 0;
> + }
> +}
> +
> +static void zynq_gpio_mask_data(ZynqGPIOBank *bank, int bit_offset,
> + uint32_t mask_data)
> +{
> + DB_PRINT("mask data offset = %d, mask_data = %08X\n", bit_offset,
> mask_data);
> +
> + /* MASK_DATA registers are R/W on their data part */
> + bank->mask_data = deposit32(bank->mask_data, bit_offset, 16, mask_data);
> + bank->out_data = deposit32(bank->out_data, bit_offset, 16, mask_data);
> +
> + zynq_gpio_update_out(bank);
> +}
> +
> +static void zynq_gpio_data(ZynqGPIOBank *bank, uint32_t data)
> +{
> + bank->out_data = data;
> + zynq_gpio_update_out(bank);
> +}
> +
> +static void zynq_gpio_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned int size)
> +{
> + ZynqGPIOState *s = opaque;
> + int b;
> + int shift;
> + ZynqGPIOBank *bank;
> +
> + switch (offset) {
> + case ZYNQ_GPIO_REG_MASK_DATA_0_LSW ... ZYNQ_GPIO_REG_MASK_DATA_3_MSW:
> + b = extract32(offset - ZYNQ_GPIO_REG_MASK_DATA_0_LSW, 3, 2);
> + shift = (offset & 0x8) ? 16 : 0;
> + zynq_gpio_mask_data(&s->banks[b], shift, value);
> + return;
> +
> + case ZYNQ_GPIO_REG_DATA_0 ... ZYNQ_GPIO_REG_DATA_3:
> + b = (offset - ZYNQ_GPIO_REG_DATA_0) / 4;
> + zynq_gpio_data(&s->banks[b], value);
> + return;
> +
> + case ZYNQ_GPIO_REG_DATA_0_RO ... ZYNQ_GPIO_REG_DATA_3_RO:
> + return;
> + }
> +
> + if (offset < 0x204 || offset > 0x2e4) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n",
> offset);
> + return;
> + }
> +
> + b = (offset - 0x200) / 0x40;
> + bank = &s->banks[b];
> +
> + switch (offset - ZYNQ_GPIO_BANK_OFFSET(b)) {
> + case ZYNQ_GPIO_BANK_REG_DIRM:
> + bank->dir = value;
> + break;
> + case ZYNQ_GPIO_BANK_REG_OEN:
> + bank->oen = value;
> + qemu_log_mask(LOG_UNIMP,
> + "zynq_gpio_write: Output enable lines not
> implemented\n");
> + break;
> + case ZYNQ_GPIO_BANK_REG_INT_MASK:
> + return;
> + case ZYNQ_GPIO_BANK_REG_INT_EN:
> + bank->imask &= ~value;
> + break;
> + case ZYNQ_GPIO_BANK_REG_INT_DIS:
> + bank->imask |= value;
> + break;
> + case ZYNQ_GPIO_BANK_REG_INT_STAT:
> + bank->istat &= ~value;
> + break;
> + case ZYNQ_GPIO_BANK_REG_INT_TYPE:
> + bank->itype = value;
> + break;
> + case ZYNQ_GPIO_BANK_REG_INT_POLARITY:
> + bank->ipolarity = value;
> + break;
> + case ZYNQ_GPIO_BANK_REG_INT_ANY:
> + bank->iany = value;
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "zynq_gpio_write: Bad offset %" HWADDR_PRIx "\n",
> offset);
> + return;
> + }
> +
> + zynq_gpio_update(s);
> +}
> +
> +static void zynq_gpio_reset(DeviceState *dev)
> +{
> + ZynqGPIOState *s = ZYNQ_GPIO(dev);
> + int b;
> +
> + for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> + ZynqGPIOBank *bank = &s->banks[b];
> + int i;
> +
> + bank->mask_data = 0x00000000;
> + bank->dir = 0x00000000;
> + bank->oen = 0x00000000;
> + bank->imask = 0x00000000;
> + bank->istat = 0x00000000;
> + bank->itype = 0xffffffff;
> + bank->ipolarity = 0x00000000;
> + bank->iany = 0x00000000;
> +
> + bank->out_data = 0x00000000;
> + bank->old_out_data = 0x00000000;
> + for (i = 0; i < ZYNQ_GPIO_IOS_PER_BANK; i++) {
> + qemu_set_irq(bank->out[i], 0);
> + }
> +
> + bank->old_in_data = bank->in_data;
> + }
> +}
> +
> +static void zynq_gpio_set_irq(ZynqGPIOBank *bank, int irq, int level)
> +{
> + uint32_t mask = 1 << irq;
> +
> + bank->in_data &= ~mask;
> + if (level)
> + bank->in_data |= mask;
> +
You need braces {} around the if body. Checkpatch should catch this.
> + zynq_gpio_update_in(bank);
> +
> + zynq_gpio_set_in_irq(bank->parent);
> +}
> +
> +static void zynq_gpio_set_bank0_irq(void *opaque, int irq, int level)
> +{
> + ZynqGPIOState *s = opaque;
Blank line here between declarations and function body.
> + zynq_gpio_set_irq(&s->banks[0], irq, level);
> +}
> +
> +static void zynq_gpio_set_bank1_irq(void *opaque, int irq, int level)
> +{
> + ZynqGPIOState *s = opaque;
> + zynq_gpio_set_irq(&s->banks[1], irq, level);
> +}
> +
> +static void zynq_gpio_set_bank2_irq(void *opaque, int irq, int level)
> +{
> + ZynqGPIOState *s = opaque;
> + zynq_gpio_set_irq(&s->banks[2], irq, level);
> +}
> +
> +static void zynq_gpio_set_bank3_irq(void *opaque, int irq, int level)
> +{
> + ZynqGPIOState *s = opaque;
> + zynq_gpio_set_irq(&s->banks[3], irq, level);
> +}
> +
> +static const MemoryRegionOps zynq_gpio_ops = {
> + .read = zynq_gpio_read,
> + .write = zynq_gpio_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void zynq_gpio_realize(DeviceState *dev, Error **errp)
> +{
> + ZynqGPIOState *s = ZYNQ_GPIO(dev);
> + int b;
> +
> + memory_region_init_io(&s->iomem, OBJECT(s), &zynq_gpio_ops, s,
> "zynq-gpio", 0x1000);
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
> + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
> +
> + for (b = 0; b < ZYNQ_GPIO_BANKS; b++) {
> + ZynqGPIOBank *bank = &s->banks[b];
> + char name[16];
> +
> + memset(bank, 0, sizeof(*bank));
> +
Is this needed? QOM should init struct fields to 0 for you.
> + bank->parent = s;
> +
> + snprintf(name, sizeof(name), "bank%d_out", b);
> + qdev_init_gpio_out_named(dev, bank->out, name,
> ZYNQ_GPIO_IOS_PER_BANK);
> + /*
> + * TODO: it would be nice if we could pass the bank to the handler.
> This
> + * would allow us to remove the 4 callbacks and use zynq_gpio_set_irq
> + * directly.
> + */
> +#if 0
> + snprintf(name, sizeof(name), "bank%d_in", b);
> + qdev_init_gpio_in_named(dev, zynq_gpio_set_irq, name,
> ZYNQ_GPIO_IOS_PER_BANK, bank);
> +#endif
Yes I am seeing the problem here now. But this is a better solution
than V1 so I think we are good here, pending a rethink of the GPIO API
WRT this problem.
With the small style issues fixed and a checkpatch pass, please add
Reviewed-by: Peter Crosthwaite <address@hidden>
To the next rev.
Regards,
Peter
> + }
> + qdev_init_gpio_in_named(dev, zynq_gpio_set_bank0_irq, "bank0_in",
> ZYNQ_GPIO_IOS_PER_BANK);
> + qdev_init_gpio_in_named(dev, zynq_gpio_set_bank1_irq, "bank1_in",
> ZYNQ_GPIO_IOS_PER_BANK);
> + qdev_init_gpio_in_named(dev, zynq_gpio_set_bank2_irq, "bank2_in",
> ZYNQ_GPIO_IOS_PER_BANK);
> + qdev_init_gpio_in_named(dev, zynq_gpio_set_bank3_irq, "bank3_in",
> ZYNQ_GPIO_IOS_PER_BANK);
> +}
> +
> +static void zynq_gpio_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->realize = zynq_gpio_realize;
> + dc->reset = zynq_gpio_reset;
> +}
> +
> +static const TypeInfo zynq_gpio_info = {
> + .name = TYPE_ZYNQ_GPIO,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(ZynqGPIOState),
> + .class_init = zynq_gpio_class_init,
> +};
> +
> +static void zynq_gpio_register_type(void)
> +{
> + type_register_static(&zynq_gpio_info);
> +}
> +
> +type_init(zynq_gpio_register_type)
> diff --git a/include/hw/gpio/zynq-gpio.h b/include/hw/gpio/zynq-gpio.h
> new file mode 100644
> index 0000000..8146c1f
> --- /dev/null
> +++ b/include/hw/gpio/zynq-gpio.h
> @@ -0,0 +1,79 @@
> +/*
> + * Zynq General Purpose IO
> + *
> + * Copyright (C) 2014 Colin Leitner <address@hidden>
> + *
> + * This code is licensed under the GPL.
> + */
> +
> +#ifndef HW_ZYNQ_GPIO_H
> +#define HW_ZYNQ_GPIO_H
> +
> +#include "hw/sysbus.h"
> +
> +#define TYPE_ZYNQ_GPIO "zynq-gpio"
> +#define ZYNQ_GPIO(obj) OBJECT_CHECK(ZynqGPIOState, (obj), TYPE_ZYNQ_GPIO)
> +
> +#define ZYNQ_GPIO_BANKS 4
> +#define ZYNQ_GPIO_IOS_PER_BANK 32
> +
> +typedef struct {
> + struct ZynqGPIOState *parent;
> +
> + uint32_t mask_data;
> + uint32_t out_data;
> + uint32_t old_out_data;
> + uint32_t in_data;
> + uint32_t old_in_data;
> + uint32_t dir;
> + uint32_t oen;
> + uint32_t imask;
> + uint32_t istat;
> + uint32_t itype;
> + uint32_t ipolarity;
> + uint32_t iany;
> +
> + qemu_irq out[ZYNQ_GPIO_IOS_PER_BANK];
> +} ZynqGPIOBank;
> +
> +typedef struct ZynqGPIOState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion iomem;
> + ZynqGPIOBank banks[ZYNQ_GPIO_BANKS];
> + qemu_irq irq;
> +} ZynqGPIOState;
> +
> +#define ZYNQ_GPIO_REG_MASK_DATA_0_LSW 0x000
> +#define ZYNQ_GPIO_REG_MASK_DATA_0_MSW 0x004
> +#define ZYNQ_GPIO_REG_MASK_DATA_1_LSW 0x008
> +#define ZYNQ_GPIO_REG_MASK_DATA_1_MSW 0x00C
> +#define ZYNQ_GPIO_REG_MASK_DATA_2_LSW 0x010
> +#define ZYNQ_GPIO_REG_MASK_DATA_2_MSW 0x014
> +#define ZYNQ_GPIO_REG_MASK_DATA_3_LSW 0x018
> +#define ZYNQ_GPIO_REG_MASK_DATA_3_MSW 0x01C
> +#define ZYNQ_GPIO_REG_DATA_0 0x040
> +#define ZYNQ_GPIO_REG_DATA_1 0x044
> +#define ZYNQ_GPIO_REG_DATA_2 0x048
> +#define ZYNQ_GPIO_REG_DATA_3 0x04C
> +#define ZYNQ_GPIO_REG_DATA_0_RO 0x060
> +#define ZYNQ_GPIO_REG_DATA_1_RO 0x064
> +#define ZYNQ_GPIO_REG_DATA_2_RO 0x068
> +#define ZYNQ_GPIO_REG_DATA_3_RO 0x06C
> +
> +/*
> + * Oddly enough these registers are neatly grouped per bank and not
> interleaved
> + * like the data registers
> + */
> +#define ZYNQ_GPIO_BANK_OFFSET(bank) (0x200 + 0x40 * (bank))
> +#define ZYNQ_GPIO_BANK_REG_DIRM 0x04
> +#define ZYNQ_GPIO_BANK_REG_OEN 0x08
> +#define ZYNQ_GPIO_BANK_REG_INT_MASK 0x0C
> +#define ZYNQ_GPIO_BANK_REG_INT_EN 0x10
> +#define ZYNQ_GPIO_BANK_REG_INT_DIS 0x14
> +#define ZYNQ_GPIO_BANK_REG_INT_STAT 0x18
> +#define ZYNQ_GPIO_BANK_REG_INT_TYPE 0x1C
> +#define ZYNQ_GPIO_BANK_REG_INT_POLARITY 0x20
> +#define ZYNQ_GPIO_BANK_REG_INT_ANY 0x24
> +
> +#endif
> --
> 1.7.10.4
>
>