qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 09/14] xilinx_spi: Initial impl. of Xilinx SPI c


From: Peter Crosthwaite
Subject: Re: [Qemu-devel] [PATCH 09/14] xilinx_spi: Initial impl. of Xilinx SPI controller
Date: Fri, 5 Oct 2012 22:49:12 +1000

On Fri, Oct 5, 2012 at 10:42 PM, Peter Maydell <address@hidden> wrote:
> On 5 October 2012 01:08, Peter Crosthwaite
> <address@hidden> wrote:
>> From: Peter A. G. Crosthwaite <address@hidden>
>>
>> Device model for xilinx XPS SPI controller (v2.0)
>>
>> Signed-off-by: Peter A. G. Crosthwaite <address@hidden>
>> ---
>>  hw/microblaze/Makefile.objs |    1 +
>>  hw/xilinx_spi.c             |  390 
>> +++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 391 insertions(+), 0 deletions(-)
>>  create mode 100644 hw/xilinx_spi.c
>>
>> diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
>> index 274d2c5..3028e65 100644
>> --- a/hw/microblaze/Makefile.objs
>> +++ b/hw/microblaze/Makefile.objs
>> @@ -1,6 +1,7 @@
>>  obj-y = petalogix_s3adsp1800_mmu.o
>>  obj-y += petalogix_ml605_mmu.o
>>  obj-y += microblaze_boot.o
>> +obj-y += xilinx_spi.o
>>
>>  obj-y += microblaze_pic_cpu.o
>>  obj-y += xilinx_ethlite.o
>> diff --git a/hw/xilinx_spi.c b/hw/xilinx_spi.c
>> new file mode 100644
>> index 0000000..0ace004
>> --- /dev/null
>> +++ b/hw/xilinx_spi.c
>> @@ -0,0 +1,390 @@
>> +/*
>> + * QEMU model of the Xilinx SPI Controller
>> + *
>> + * Copyright (C) 2010 Edgar E. Iglesias.
>> + * Copyright (C) 2012 Peter A. G. Crosthwaite <address@hidden>
>> + * Copyright (C) 2012 PetaLogix
>> + *
>> + * Permission is hereby granted, free of charge, to any person obtaining a 
>> copy
>> + * of this software and associated documentation files (the "Software"), to 
>> deal
>> + * in the Software without restriction, including without limitation the 
>> rights
>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
>> + * copies of the Software, and to permit persons to whom the Software is
>> + * furnished to do so, subject to the following conditions:
>> + *
>> + * The above copyright notice and this permission notice shall be included 
>> in
>> + * all copies or substantial portions of the Software.
>> + *
>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
>> OR
>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 
>> OTHER
>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
>> FROM,
>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
>> + * THE SOFTWARE.
>> + */
>> +
>> +#include "sysbus.h"
>> +#include "sysemu.h"
>> +#include "qemu-log.h"
>> +#include "fifo.h"
>> +
>> +#include "ssi.h"
>> +
>> +#ifdef XILINX_SPI_ERR_DEBUG
>> +#define DB_PRINT(...) do { \
>> +    fprintf(stderr,  ": %s: ", __func__); \
>> +    fprintf(stderr, ## __VA_ARGS__); \
>> +    } while (0);
>> +#else
>> +    #define DB_PRINT(...)
>> +#endif
>> +
>> +#define R_DGIER     (0x1c / 4)
>> +#define R_DGIER_IE  (1 << 31)
>> +
>> +#define R_IPISR     (0x20 / 4)
>> +#define IRQ_DRR_NOT_EMPTY    (1 << (31 - 23))
>> +#define IRQ_DRR_OVERRUN      (1 << (31 - 26))
>> +#define IRQ_DRR_FULL         (1 << (31 - 27))
>> +#define IRQ_TX_FF_HALF_EMPTY (1 << 6)
>> +#define IRQ_DTR_UNDERRUN     (1 << 3)
>> +#define IRQ_DTR_EMPTY        (1 << (31 - 29))
>> +
>> +#define R_IPIER     (0x28 / 4)
>> +#define R_SRR       (0x40 / 4)
>> +#define R_SPICR     (0x60 / 4)
>> +#define R_SPICR_TXFF_RST     (1 << 5)
>> +#define R_SPICR_RXFF_RST     (1 << 6)
>> +#define R_SPICR_MTI          (1 << 8)
>> +
>> +#define R_SPISR     (0x64 / 4)
>> +#define SR_TX_FULL    (1 << 3)
>> +#define SR_TX_EMPTY   (1 << 2)
>> +#define SR_RX_FULL    (1 << 1)
>> +#define SR_RX_EMPTY   (1 << 0)
>> +
>> +#define R_SPIDTR    (0x68 / 4)
>> +#define R_SPIDRR    (0x6C / 4)
>> +#define R_SPISSR    (0x70 / 4)
>> +#define R_TX_FF_OCY (0x74 / 4)
>> +#define R_RX_FF_OCY (0x78 / 4)
>> +#define R_MAX       (0x7C / 4)
>> +
>> +#define FIFO_CAPACITY 256
>> +
>> +typedef struct XilinxSPI {
>> +    SysBusDevice busdev;
>> +    MemoryRegion mmio;
>> +
>> +    qemu_irq irq;
>> +    int irqline;
>> +
>> +    uint8_t num_cs;
>> +    qemu_irq *cs_lines;
>> +
>> +    SSIBus *spi;
>> +
>> +    Fifo8 rx_fifo;
>> +    Fifo8 tx_fifo;
>> +
>> +    uint32_t regs[R_MAX];
>> +} XilinxSPI;
>> +
>> +static void txfifo_reset(XilinxSPI *s)
>> +{
>> +    fifo8_reset(&s->tx_fifo);
>> +
>> +    s->regs[R_SPISR] &= ~SR_TX_FULL;
>> +    s->regs[R_SPISR] |= SR_TX_EMPTY;
>> +}
>> +
>> +static void rxfifo_reset(XilinxSPI *s)
>> +{
>> +    fifo8_reset(&s->rx_fifo);
>> +
>> +    s->regs[R_SPISR] |= SR_RX_EMPTY;
>> +    s->regs[R_SPISR] &= ~SR_RX_FULL;
>> +}
>> +
>> +static void xlx_spi_update_cs(XilinxSPI *s)
>> +{
>> +   int i;
>> +
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        qemu_set_irq(s->cs_lines[i], !(~s->regs[R_SPISSR] & 1 << i));
>> +    }
>> +}
>> +
>> +static void xlx_spi_update_irq(XilinxSPI *s)
>> +{
>> +    uint32_t pending;
>> +
>> +    s->regs[R_IPISR] |=
>> +            (!fifo8_is_empty(&s->rx_fifo) ? IRQ_DRR_NOT_EMPTY : 0) |
>> +            (fifo8_is_full(&s->rx_fifo) ? IRQ_DRR_FULL : 0);
>> +
>> +    pending = s->regs[R_IPISR] & s->regs[R_IPIER];
>> +
>> +    pending = pending && (s->regs[R_DGIER] & R_DGIER_IE);
>> +    pending = !!pending;
>> +
>> +    /* This call lies right in the data paths so don't call the
>> +       irq chain unless things really changed.  */
>> +    if (pending != s->irqline) {
>> +        s->irqline = pending;
>> +        DB_PRINT("irq_change of state %d ISR:%x IER:%X\n",
>> +                    pending, s->regs[R_IPISR], s->regs[R_IPIER]);
>> +        qemu_set_irq(s->irq, pending);
>> +    }
>> +
>> +}
>> +
>> +static void xlx_spi_do_reset(XilinxSPI *s)
>> +{
>> +    memset(s->regs, 0, sizeof s->regs);
>> +
>> +    rxfifo_reset(s);
>> +    txfifo_reset(s);
>> +
>> +    s->regs[R_SPISSR] = ~0;
>> +    xlx_spi_update_irq(s);
>> +    xlx_spi_update_cs(s);
>> +}
>> +
>> +static void xlx_spi_reset(DeviceState *d)
>> +{
>> +    xlx_spi_do_reset(DO_UPCAST(XilinxSPI, busdev.qdev, d));
>> +}
>> +
>> +static inline int spi_master_enabled(XilinxSPI *s)
>> +{
>> +    return !(s->regs[R_SPICR] & R_SPICR_MTI);
>> +}
>> +
>> +static void spi_flush_txfifo(XilinxSPI *s)
>> +{
>> +    uint32_t tx;
>> +    uint32_t rx;
>> +
>> +    while (!fifo8_is_empty(&s->tx_fifo)) {
>> +        tx = (uint32_t)fifo8_pop(&s->tx_fifo);
>> +        DB_PRINT("data tx:%x\n", tx);
>> +        rx = ssi_transfer(s->spi, tx);
>> +        DB_PRINT("data rx:%x\n", rx);
>> +        if (fifo8_is_full(&s->rx_fifo)) {
>> +            s->regs[R_IPISR] |= IRQ_DRR_OVERRUN;
>> +        } else {
>> +            fifo8_push(&s->rx_fifo, (uint8_t)rx);
>> +            if (fifo8_is_full(&s->rx_fifo)) {
>> +                s->regs[R_SPISR] |= SR_RX_FULL;
>> +                s->regs[R_IPISR] |= IRQ_DRR_FULL;
>> +            }
>> +        }
>> +
>> +        s->regs[R_SPISR] &= ~SR_RX_EMPTY;
>> +        s->regs[R_SPISR] &= ~SR_TX_FULL;
>> +        s->regs[R_SPISR] |= SR_TX_EMPTY;
>> +
>> +        s->regs[R_IPISR] |= IRQ_DTR_EMPTY;
>> +        s->regs[R_IPISR] |= IRQ_DRR_NOT_EMPTY;
>> +    }
>> +
>> +}
>> +
>> +static uint64_t
>> +spi_read(void *opaque, target_phys_addr_t addr, unsigned int size)
>> +{
>> +    XilinxSPI *s = opaque;
>> +    uint32_t r = 0;
>> +
>> +    addr >>= 2;
>> +    switch (addr) {
>> +    case R_SPIDRR:
>> +        if (fifo8_is_empty(&s->rx_fifo)) {
>> +            DB_PRINT("Read from empty FIFO!\n");
>> +            return 0xdeadbeef;
>> +        }
>> +
>> +        s->regs[R_SPISR] &= ~SR_RX_FULL;
>> +        r = fifo8_pop(&s->rx_fifo);
>> +        if (fifo8_is_empty(&s->rx_fifo)) {
>> +            s->regs[R_SPISR] |= SR_RX_EMPTY;
>> +        }
>> +        break;
>> +
>> +    case R_SPISR:
>> +        r = s->regs[addr];
>> +        break;
>> +
>> +    default:
>> +        if (addr < ARRAY_SIZE(s->regs)) {
>> +            r = s->regs[addr];
>> +        }
>> +        break;
>> +
>> +    }
>> +    DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr * 4, r);
>> +    xlx_spi_update_irq(s);
>> +    return r;
>> +}
>> +
>> +static void
>> +spi_write(void *opaque, target_phys_addr_t addr,
>> +            uint64_t val64, unsigned int size)
>> +{
>> +    XilinxSPI *s = opaque;
>> +    uint32_t value = val64;
>> +
>> +    DB_PRINT("addr=" TARGET_FMT_plx " = %x\n", addr, value);
>> +    addr >>= 2;
>> +    switch (addr) {
>> +    case R_SRR:
>> +        if (value != 0xa) {
>> +            DB_PRINT("Invalid write to SRR %x\n", value);
>> +        } else {
>> +            xlx_spi_do_reset(s);
>> +        }
>> +        break;
>> +
>> +    case R_SPIDTR:
>> +        s->regs[R_SPISR] &= ~SR_TX_EMPTY;
>> +        fifo8_push(&s->tx_fifo, (uint8_t)value);
>> +        if (fifo8_is_full(&s->tx_fifo)) {
>> +            s->regs[R_SPISR] |= SR_TX_FULL;
>> +        }
>> +        if (!spi_master_enabled(s)) {
>> +            goto done;
>> +        } else {
>> +            DB_PRINT("DTR and master enabled\n");
>> +        }
>> +        spi_flush_txfifo(s);
>> +        break;
>> +
>> +    case R_SPISR:
>> +        DB_PRINT("Invalid write to SPISR %x\n", value);
>> +        break;
>> +
>> +    case R_IPISR:
>> +        /* Toggle the bits.  */
>> +        s->regs[addr] ^= value;
>> +        break;
>> +
>> +    /* Slave Select Register.  */
>> +    case R_SPISSR:
>> +        s->regs[addr] = value;
>> +        xlx_spi_update_cs(s);
>> +        break;
>> +
>> +    case R_SPICR:
>> +        /* FIXME: reset irq and sr state to empty queues.  */
>> +        if (value & R_SPICR_RXFF_RST) {
>> +            rxfifo_reset(s);
>> +        }
>> +
>> +        if (value & R_SPICR_TXFF_RST) {
>> +            txfifo_reset(s);
>> +        }
>> +        value &= ~(R_SPICR_RXFF_RST | R_SPICR_TXFF_RST);
>> +        s->regs[addr] = value;
>> +
>> +        if (!(value & R_SPICR_MTI)) {
>> +            spi_flush_txfifo(s);
>> +        }
>> +        break;
>> +
>> +    default:
>> +        if (addr < ARRAY_SIZE(s->regs)) {
>> +            s->regs[addr] = value;
>> +        }
>> +        break;
>> +    }
>> +
>> +done:
>> +    xlx_spi_update_irq(s);
>> +}
>> +
>> +static const MemoryRegionOps spi_ops = {
>> +    .read = spi_read,
>> +    .write = spi_write,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4
>> +    }
>> +};
>> +
>> +static int xilinx_spi_init(SysBusDevice *dev)
>> +{
>> +    int i;
>> +    XilinxSPI *s = FROM_SYSBUS(typeof(*s), dev);
>> +
>> +    DB_PRINT("\n");
>> +    sysbus_init_irq(dev, &s->irq);
>> +    s->cs_lines = g_new(qemu_irq, s->num_cs);
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        sysbus_init_irq(dev, &s->cs_lines[i]);
>> +    }
>> +
>> +    memory_region_init_io(&s->mmio, &spi_ops, s, "xilinx-spi", R_MAX * 4);
>> +    sysbus_init_mmio(dev, &s->mmio);
>> +
>> +    s->irqline = -1;
>> +
>> +    s->spi = ssi_create_bus(&dev->qdev, "spi");
>> +
>> +    fifo8_create(&s->tx_fifo, FIFO_CAPACITY);
>> +    fifo8_create(&s->rx_fifo, FIFO_CAPACITY);
>> +
>> +    return 0;
>> +}
>> +
>> +static int xilinx_spi_post_load(void *opaque, int version_id)
>> +{
>> +    xlx_spi_update_irq((XilinxSPI *)opaque);
>
> Don't assert outbound IRQs following load: the device on the
> other end should handle its own state restoration so after
> everybody has loaded both ends should consistently believe
> the IRQ has been asserted. If you reassert it in a post_load
> hook you'll put the device on the other end into the wrong
> state if it was restored before you were.
>

Ok

>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_xilinx_spi = {
>> +    .name = "xilinx_spi",
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .post_load = xilinx_spi_post_load,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_FIFO8(tx_fifo, XilinxSPI),
>> +        VMSTATE_FIFO8(rx_fifo, XilinxSPI),
>> +        VMSTATE_UINT32_ARRAY(regs, XilinxSPI, R_MAX),
>
> You need also an entry for irqline.
>

Shouldnt need it as irqline is just a cached copy of the IRQ state to
save on qemu_set_irq() spamming. It is not true device state. Init
will just set irqline == -1 on post load which means the device will
conservatively force to IRQ regardless of previous state to the
correct value.

>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static Property xilinx_spi_properties[] = {
>> +    DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void xilinx_spi_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> +    k->init = xilinx_spi_init;
>> +    dc->reset = xlx_spi_reset;
>> +    dc->props = xilinx_spi_properties;
>> +    dc->vmsd = &vmstate_xilinx_spi;
>> +}
>> +
>> +static TypeInfo xilinx_spi_info = {
>> +    .name           = "xlnx.xps-spi",
>> +    .parent         = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size  = sizeof(XilinxSPI),
>> +    .class_init     = xilinx_spi_class_init,
>> +};
>> +
>> +static void xilinx_spi_register_types(void)
>> +{
>> +    type_register_static(&xilinx_spi_info);
>> +}
>> +
>> +type_init(xilinx_spi_register_types)
>> --
>> 1.7.0.4
>>
>>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]