Hi Peter,

Thank you for your comments.

I used the PL050 component as a starting point, but I did not clean things
up well after I saw it working. I will clean it up before sending the new
patch version.

On Fri, Sep 15, 2023 at 4:23 PM Peter Maydell <peter.mayd...@linaro.org>
wrote:

> On Tue, 5 Sept 2023 at 21:14, Strahinja Jankovic
> <strahinjapjanko...@gmail.com> wrote:
> >
> > Add emulation for PS2-0 and PS2-1 for keyboard/mouse.
> >
> > Signed-off-by: Strahinja Jankovic <strahinja.p.janko...@gmail.com>
>
>
>
> > +static int allwinner_a10_ps2_fctl_is_irq(AwA10PS2State *s)
> > +{
> > +    return (s->regs[REG_INDEX(REG_FCTL)] & FIELD_REG_FCTL_TXRDY_IEN) ||
> > +        (s->pending &&
> > +         (s->regs[REG_INDEX(REG_FCTL)] & FIELD_REG_FCTL_RXRDY_IEN));
>
> It looks a little odd that you need a separate s->pending bool here.
> Sometimes hardware does odd things, but the most usual situation
> is that the pending status of an interrupt is directly reflected
> in a register bit somewhere, and "is the interrupt high" logic
> is then just "is the pending bit set and is the enable bit set".
> Often the bit positions are deliberately the same in the two
> registers and then "is an interrupt set" is something like
>   if (s->regs[REG_INDEX(REG_FCTL)] & s->regs[REG_INDEX(REG_FSTS)] &
>       (TXRDY_IEN | RXRDY_IEN))
>
>
Yes, that makes sense. I will try to improve this.


>
> > +}
> > +
> > +static void allwinner_a10_ps2_update_irq(AwA10PS2State *s)
> > +{
> > +    int level = (s->regs[REG_INDEX(REG_GCTL)] & FIELD_REG_GCTL_INT_EN)
> &&
> > +        allwinner_a10_ps2_fctl_is_irq(s);
> > +
> > +    qemu_set_irq(s->irq, level);
> > +}
> > +
> > +static void allwinner_a10_ps2_set_irq(void *opaque, int n, int level)
> > +{
> > +    AwA10PS2State *s = (AwA10PS2State *)opaque;
> > +
> > +    s->pending = level;
> > +    allwinner_a10_ps2_update_irq(s);
> > +}
> > +
> > +static uint64_t allwinner_a10_ps2_read(void *opaque, hwaddr offset,
> > +                                       unsigned size)
> > +{
> > +    AwA10PS2State *s = AW_A10_PS2(opaque);
> > +    const uint32_t idx = REG_INDEX(offset);
> > +
> > +    switch (offset) {
> > +    case REG_FSTS:
> > +        {
> > +            uint32_t stat = FIELD_REG_FSTS_TX_RDY;
> > +            if (s->pending) {
> > +                stat |= FIELD_REG_FSTS_RX_LEVEL1 |
> FIELD_REG_FSTS_RX_RDY;
> > +            }
> > +            return stat;
>
> The logic here also suggests that the code would be simpler if you
> keep the TX_RDY and RX_RDY state directly in this register value,
> rather than hardcoding TX_RDY to always-set and keeping RX_RDY
> in a separate pending field.
>
>
That makes sense, I'll fix it.


> > +        }
> > +        break;
> > +    case REG_DATA:
> > +        if (s->pending) {
> > +            s->last = ps2_read_data(s->ps2dev);
> > +        }
> > +        return s->last;
>
> You could keep the last returned data in s->regs[REG_IDX(REG_DATA)]
> and avoid having to have an extra s->last field in the state struct.
>
>
Ok.


> > +    case REG_GCTL:
> > +        {
> > +            if (allwinner_a10_ps2_fctl_is_irq(s)) {
> > +                s->regs[idx] |= FIELD_REG_GCTL_INT_FLAG;
> > +            } else {
> > +                s->regs[idx] &= FIELD_REG_GCTL_INT_FLAG;
> > +            }
> > +        }
> > +        break;
> > +    case REG_LCTL:
> > +    case REG_LSTS:
> > +    case REG_FCTL:
> > +    case REG_CLKDR:
> > +        break;
> > +    case 0x1C ... AW_A10_PS2_IOSIZE:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> > +                      __func__, (uint32_t)offset);
> > +        return 0;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "%s: unimplemented read offset
> 0x%04x\n",
> > +                      __func__, (uint32_t)offset);
> > +        return 0;
> > +    }
> > +
> > +    return s->regs[idx];
> > +}
> > +
> > +static void allwinner_a10_ps2_write(void *opaque, hwaddr offset,
> > +                                   uint64_t val, unsigned size)
> > +{
> > +    AwA10PS2State *s = AW_A10_PS2(opaque);
> > +    const uint32_t idx = REG_INDEX(offset);
> > +
> > +    s->regs[idx] = (uint32_t) val;
> > +
> > +    switch (offset) {
> > +    case REG_GCTL:
> > +        allwinner_a10_ps2_update_irq(s);
> > +        s->regs[idx] &= ~FIELD_REG_GCTL_SOFT_RST;
> > +        break;
> > +    case REG_DATA:
> > +        /* ??? This should toggle the TX interrupt line.  */
> > +        /* ??? This means kbd/mouse can block each other.  */
>
> I don't understand this comment. It looks like it was cut-and-pasted
> from another device where it was originally written in 2005 (and
> I don't understand it there either). We should either understand
> what we mean here, or else not have the comment at all...
>
>
Yes, unfortunately I missed this one before sending it out...


> > +        if (s->is_mouse) {
> > +            ps2_write_mouse(PS2_MOUSE_DEVICE(s->ps2dev), val);
> > +        } else {
> > +            ps2_write_keyboard(PS2_KBD_DEVICE(s->ps2dev), val);
> > +        }
> > +        break;
> > +    case REG_LCTL:
> > +    case REG_LSTS:
> > +    case REG_FCTL:
> > +    case REG_FSTS:
> > +    case REG_CLKDR:
> > +        break;
> > +    case 0x1C ... AW_A10_PS2_IOSIZE:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: out-of-bounds offset
> 0x%04x\n",
> > +                      __func__, (uint32_t)offset);
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_UNIMP, "%s: unimplemented write offset
> 0x%04x\n",
> > +                      __func__, (uint32_t)offset);
> > +        break;
> > +    }
> > +}
>
> thanks
> -- PMM
>

Thanks!

Best regards,
Strahinja

Reply via email to