Commit bfb27e60 authored by Colin Leitner's avatar Colin Leitner Committed by Peter Maydell
Browse files

pl061: implement input interrupt logic



This patch adds the missing input interrupt logic to the pl061 GPIO device. To
keep the floating output pins to stay high, the old state variable had to be
split into two separate ones for input and output - which brings the vmstate
version to 3.

Edge level interrupts and I/O were tested under Linux 3.14. Level interrupt
handling hasn't been tested.

Signed-off-by: default avatarColin Leitner <colin.leitner@googlemail.com>
Message-id: 54024FD2.9080204@gmail.com
Reviewed-by: default avatarPeter Maydell <peter.maydell@linaro.org>
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent c3c8d6b3
Loading
Loading
Loading
Loading
+45 −14
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ typedef struct PL061State {
    MemoryRegion iomem;
    uint32_t locked;
    uint32_t data;
    uint32_t old_data;
    uint32_t old_out_data;
    uint32_t old_in_data;
    uint32_t dir;
    uint32_t isense;
    uint32_t ibe;
@@ -63,12 +64,13 @@ typedef struct PL061State {

static const VMStateDescription vmstate_pl061 = {
    .name = "pl061",
    .version_id = 2,
    .minimum_version_id = 1,
    .version_id = 3,
    .minimum_version_id = 3,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(locked, PL061State),
        VMSTATE_UINT32(data, PL061State),
        VMSTATE_UINT32(old_data, PL061State),
        VMSTATE_UINT32(old_out_data, PL061State),
        VMSTATE_UINT32(old_in_data, PL061State),
        VMSTATE_UINT32(dir, PL061State),
        VMSTATE_UINT32(isense, PL061State),
        VMSTATE_UINT32(ibe, PL061State),
@@ -98,14 +100,14 @@ static void pl061_update(PL061State *s)
    uint8_t out;
    int i;

    DPRINTF("dir = %d, data = %d\n", s->dir, s->data);

    /* Outputs float high.  */
    /* FIXME: This is board dependent.  */
    out = (s->data & s->dir) | ~s->dir;
    changed = s->old_data ^ out;
    if (!changed)
        return;

    s->old_data = out;
    changed = s->old_out_data ^ out;
    if (changed) {
        s->old_out_data = out;
        for (i = 0; i < 8; i++) {
            mask = 1 << i;
            if (changed & mask) {
@@ -113,8 +115,37 @@ static void pl061_update(PL061State *s)
                qemu_set_irq(s->out[i], (out & mask) != 0);
            }
        }
    }

    /* Inputs */
    changed = (s->old_in_data ^ s->data) & ~s->dir;
    if (changed) {
        s->old_in_data = s->data;
        for (i = 0; i < 8; i++) {
            mask = 1 << i;
            if (changed & mask) {
                DPRINTF("Changed input %d = %d\n", i, (s->data & mask) != 0);

                if (!(s->isense & mask)) {
                    /* Edge interrupt */
                    if (s->ibe & mask) {
                        /* Any edge triggers the interrupt */
                        s->istate |= mask;
                    } else {
                        /* Edge is selected by IEV */
                        s->istate |= ~(s->data ^ s->iev) & mask;
                    }
                }
            }
        }
    }

    /* Level interrupt */
    s->istate |= ~(s->data ^ s->iev) & s->isense;

    DPRINTF("istate = %02X\n", s->istate);

    /* FIXME: Implement input interrupts.  */
    qemu_set_irq(s->irq, (s->istate & s->im) != 0);
}

static uint64_t pl061_read(void *opaque, hwaddr offset,