qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH v3 15/20] arm: add Faraday FTMAC110 10/100Mbps e


From: Kuo-Jung Su
Subject: Re: [Qemu-devel] [PATCH v3 15/20] arm: add Faraday FTMAC110 10/100Mbps ethernet support
Date: Mon, 18 Feb 2013 10:45:05 +0800

2013/2/9 Blue Swirl <address@hidden>:
> On Wed, Feb 6, 2013 at 9:45 AM, Kuo-Jung Su <address@hidden> wrote:
>> From: Kuo-Jung Su <address@hidden>
>>
>> The FTMAC110 is an Ethernet controller that provides AHB master capability
>> and is in full compliance with the IEEE 802.3 10/100 Mbps specifications.
>> Its DMA controller handles all data transfers between system memory
>> and on-chip memories.
>> It supports half-word data transfer for Linux. However it has a weird DMA
>> alignment issue:
>>
>> (1) Tx DMA Buffer Address:
>>     1 bytes aligned: Invalid
>>     2 bytes aligned: O.K
>>     4 bytes aligned: O.K
>>
>> (2) Rx DMA Buffer Address:
>>     1 bytes aligned: Invalid
>>     2 bytes aligned: O.K
>>     4 bytes aligned: Invalid (It means 0x0, 0x4, 0x8, 0xC are invalid)
>>
>> Signed-off-by: Kuo-Jung Su <address@hidden>
>> ---
>>  hw/arm/Makefile.objs  |    1 +
>>  hw/arm/faraday_a360.c |   10 +
>>  hw/arm/ftmac110.c     |  681 
>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>  hw/arm/ftmac110.h     |  131 ++++++++++
>>  4 files changed, 823 insertions(+)
>>  create mode 100644 hw/arm/ftmac110.c
>>  create mode 100644 hw/arm/ftmac110.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 70d4f25..f5eeaeb 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -47,3 +47,4 @@ obj-y += ftapbbrg020.o
>>  obj-y += ftnandc021.o
>>  obj-y += fti2c010.o
>>  obj-y += ftssp010.o
>> +obj-y += ftmac110.o
>> diff --git a/hw/arm/faraday_a360.c b/hw/arm/faraday_a360.c
>> index 52cfcec..51e8649 100644
>> --- a/hw/arm/faraday_a360.c
>> +++ b/hw/arm/faraday_a360.c
>> @@ -31,6 +31,7 @@ a360_device_init(A360State *s)
>>      qemu_irq *pic;
>>      DeviceState *ds, *fl;
>>      SSIBus *spi;
>> +    int done_nic = 0;
>>      int i, nr_flash;
>>      qemu_irq cs_line;
>>      qemu_irq ack, req;
>> @@ -122,6 +123,15 @@ a360_device_init(A360State *s)
>>      req = qdev_get_gpio_in(s->pdma[0], 2);
>>      qdev_connect_gpio_out(s->pdma[0], 2, ack);
>>      qdev_connect_gpio_out(ds, 1, req);
>> +
>> +    /* ftmac110 */
>> +    for (i = 0; i < nb_nics; i++) {
>> +        NICInfo *nd = &nd_table[i];
>> +        if (!done_nic && (!nd->model || strcmp(nd->model, "ftmac110") == 
>> 0)) {
>> +            ftmac110_init(nd, 0x90900000, pic[25]);
>> +            done_nic = 1;
>> +        }
>> +    }
>>  }
>>
>>  static void
>> diff --git a/hw/arm/ftmac110.c b/hw/arm/ftmac110.c
>> new file mode 100644
>> index 0000000..d45f4ba
>> --- /dev/null
>> +++ b/hw/arm/ftmac110.c
>> @@ -0,0 +1,681 @@
>> +/*
>> + * QEMU model of the FTMAC110 Controller
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <address@hidden>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + */
>> +
>> +/*******************************************************************/
>> +/*               FTMAC110 DMA design issue                         */
>> +/*                                             Dante Su 2010.02.03 */
>> +/*                                                                 */
>> +/* The DMA engine has a weird restriction that its Rx DMA engine   */
>> +/* accepts only 16-bits aligned address, 32-bits aligned is still  */
>> +/* invalid. However this restriction does not apply to Tx DMA.     */
>> +/* Conclusion:                                                     */
>> +/* (1) Tx DMA Buffer Address:                                      */
>> +/*     1 bytes aligned: Invalid                                    */
>> +/*     2 bytes aligned: O.K                                        */
>> +/*     4 bytes aligned: O.K (-> u-boot ZeroCopy is possible)       */
>> +/* (2) Rx DMA Buffer Address:                                      */
>> +/*     1 bytes aligned: Invalid                                    */
>> +/*     2 bytes aligned: O.K                                        */
>> +/*     4 bytes aligned: Invalid                                    */
>> +/*******************************************************************/
>
> Does this also apply to descriptors?

No, it only apply to the packet/frame buffer.

>
>> +
>> +#include <hw/sysbus.h>
>> +#include <sysemu/sysemu.h>
>> +#include <net/net.h>
>> +
>> +#include "faraday.h"
>> +#include "ftmac110.h"
>> +
>> +#define TYPE_FTMAC110    "ftmac110"
>> +
>> +typedef struct Ftmac110State {
>> +    SysBusDevice busdev;
>> +    MemoryRegion mmio;
>> +
>> +    QEMUBH *bh;
>> +    qemu_irq irq;
>> +    NICState *nic;
>> +    NICConf conf;
>> +
>> +    uint32_t isr;
>> +    uint32_t ier;
>> +    uint32_t mhash[2];
>> +    uint32_t tx_bar;
>> +    uint32_t rx_bar;
>> +    uint32_t tx_idx;
>> +    uint32_t rx_idx;
>> +    uint32_t maccr;
>> +    uint32_t macsr;
>> +    uint32_t phycr;
>> +    uint32_t phycr_rd;
>> +
>> +    struct {
>> +        uint8_t  buf[2048];
>> +        uint32_t len;
>> +    } txbuff;
>> +
>> +    uint32_t rx_pkt;
>> +    uint32_t rx_bcst;
>> +    uint32_t rx_mcst;
>> +    uint16_t rx_runt;
>> +    uint16_t rx_drop;
>> +    uint16_t rx_crc;
>> +    uint16_t rx_ftl;
>> +    uint32_t tx_pkt;
>> +
>> +} Ftmac110State;
>> +
>> +#define FTMAC110(obj) \
>> +    OBJECT_CHECK(Ftmac110State, obj, TYPE_FTMAC110)
>> +
>> +static uint8_t bitrev8(uint8_t v)
>> +{
>> +    int i;
>> +    uint8_t r = 0;
>> +    for (i = 0; i < 8; ++i) {
>> +        if (v & (1 << i)) {
>> +            r |= (1 << (7 - i));
>> +        }
>> +    }
>> +    return r;
>> +}
>> +
>> +static int ftmac110_mcast_hash(int len, const uint8_t *p)
>> +{
>> +#define CRCPOLY_LE 0xedb88320
>> +    int i;
>> +    uint32_t crc = 0xFFFFFFFF;
>> +
>> +    while (len--) {
>> +        crc ^= *p++;
>> +        for (i = 0; i < 8; i++) {
>> +            crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
>> +        }
>> +    }
>> +
>> +    /* Reverse CRC32 and return MSB 6 bits only */
>> +    return bitrev8(crc >> 24) >> 2;
>> +}
>> +
>> +static void ftmac110_read_rxdesc(hwaddr addr, Ftmac110RXD *desc)
>> +{
>> +    int i;
>> +    uint32_t *p = (uint32_t *)desc;
>> +
>> +    if (addr & 0x0f) {
>> +        hw_error("ftmac110: Rx desc is not 16-byte aligned!\n"
>> +                 "It's fine in QEMU but the real HW would panic.\n");
>> +    }
>> +
>> +    cpu_physical_memory_read(addr, desc, sizeof(*desc));
>> +
>> +    for (i = 0; i < sizeof(*desc); i += 4) {
>> +        *p = le32_to_cpu(*p);
>> +    }
>> +
>> +    if ((desc->buf & 0x1) || !(desc->buf % 4)) {
>> +        hw_error("ftmac110: rx buffer is not exactly 16-bit aligned.\n");
>> +    }
>> +}
>> +
>> +static void ftmac110_write_rxdesc(hwaddr addr, Ftmac110RXD *desc)
>> +{
>> +    int i;
>> +    uint32_t *p = (uint32_t *)desc;
>> +
>
> Alignment check probably also here.

Since the decriptor is supplied from the software drivers, and the
FTMAC110 always checks the OWNER bit before taking any actions,
so a descriptor read is always called prior to any descriptor write,
and the alignment check is invoked inside ftmac110_read_rxdesc(...)
and ftmac110_read_rxdesc(...).

BTW, the descriptor alignment is 16 bytes.

>
>> +    for (i = 0; i < sizeof(*desc); i += 4) {
>> +        *p = cpu_to_le32(*p);
>> +    }
>> +
>> +    cpu_physical_memory_write(addr, desc, sizeof(*desc));
>> +}
>> +
>> +static void ftmac110_read_txdesc(hwaddr addr, Ftmac110TXD *desc)
>> +{
>> +    int i;
>> +    uint32_t *p = (uint32_t *)desc;
>> +
>> +    if (addr & 0x0f) {
>> +        hw_error("ftmac110: Tx desc is not 16-byte aligned!\n"
>> +                 "It's fine in QEMU but the real HW would panic.\n");
>> +    }
>> +
>> +    cpu_physical_memory_read(addr, desc, sizeof(*desc));
>> +
>> +    for (i = 0; i < sizeof(*desc); i += 4) {
>> +        *p = le32_to_cpu(*p);
>> +    }
>> +
>> +    if (desc->buf & 0x1) {
>> +        hw_error("ftmac110: tx buffer is not 16-bit aligned.\n");
>> +    }
>> +}
>> +
>> +static void ftmac110_write_txdesc(hwaddr addr, Ftmac110TXD *desc)
>> +{
>> +    int i;
>> +    uint32_t *p = (uint32_t *)desc;
>> +
>> +    for (i = 0; i < sizeof(*desc); i += 4) {
>> +        *p = cpu_to_le32(*p);
>> +    }
>> +
>
> Ditto.
>
>> +    cpu_physical_memory_write(addr, desc, sizeof(*desc));
>> +}
>> +
>> +static void ftmac110_update_irq(Ftmac110State *s)
>> +{
>> +    if (s->isr & s->ier) {
>> +        qemu_set_irq(s->irq, 1);
>> +    } else {
>> +        qemu_set_irq(s->irq, 0);
>> +    }
>> +}
>> +
>> +static int ftmac110_can_receive(NetClientState *nc)
>> +{
>> +    Ftmac110State *s = FTMAC110(DO_UPCAST(NICState, nc, nc)->opaque);
>> +    Ftmac110RXD rxd;
>> +    hwaddr off = s->rx_bar + s->rx_idx * sizeof(rxd);
>> +
>> +    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN))
>> +            != (MACCR_RCV_EN | MACCR_RDMA_EN)) {
>> +        return 0;
>> +    }
>> +
>> +    ftmac110_read_rxdesc(off, &rxd);
>> +
>> +    return rxd.owner;
>> +}
>> +
>> +static ssize_t ftmac110_receive(NetClientState *nc,
>> +                                const uint8_t  *buf,
>> +                                size_t          size)
>> +{
>> +    const uint8_t *ptr = buf;
>> +    hwaddr off;
>> +    size_t len;
>> +    Ftmac110RXD rxd;
>> +    Ftmac110State *s = FTMAC110(DO_UPCAST(NICState, nc, nc)->opaque);
>> +    int bcst, mcst, ftl, proto;
>> +
>> +    if ((s->maccr & (MACCR_RCV_EN | MACCR_RDMA_EN))
>> +            != (MACCR_RCV_EN | MACCR_RDMA_EN)) {
>> +        return -1;
>> +    }
>> +
>> +    /*
>> +     * Check if it's a long frame. (CRC32 is excluded)
>> +     */
>> +    proto = (buf[12] << 8) | buf[13];
>> +    if (proto == 0x8100) {  /* 802.1Q VLAN */
>> +        ftl = (size > 1518) ? 1 : 0;
>> +    } else {
>> +        ftl = (size > 1514) ? 1 : 0;
>> +    }
>> +    if (ftl) {
>> +        DPRINTF("ftmac110_receive: frame too long, drop it\n");
>> +        return -1;
>> +    }
>> +
>> +    /* if it's a broadcast */
>> +    if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)
>> +            && (buf[3] == 0xff) && (buf[4] == 0xff) && (buf[5] == 0xff)) {
>> +        bcst = 1;
>> +        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_BROADPKT)) 
>> {
>> +            DPRINTF("ftmac110_receive: bcst filtered\n");
>> +            return -1;
>> +        }
>> +    } else {
>> +        bcst = 0;
>> +    }
>> +
>> +    /* if it's a multicast */
>> +    if ((buf[0] == 0x01) && (buf[1] == 0x00) && (buf[2] == 0x5e)
>> +        && (buf[3] <= 0x7f)) {
>> +        mcst = 1;
>> +        if (!(s->maccr & MACCR_RCV_ALL) && !(s->maccr & MACCR_RX_MULTIPKT)) 
>> {
>> +            int hash;
>> +            if (!(s->maccr & MACCR_HT_MULTI_EN)) {
>> +                DPRINTF("ftmac110_receive: mcst filtered\n");
>> +                return -1;
>> +            }
>> +            hash = ftmac110_mcast_hash(6, buf);
>> +            if (!(s->mhash[hash / 32] & (1 << (hash % 32)))) {
>> +                DPRINTF("ftmac110_receive: mcst filtered\n");
>> +                return -1;
>> +            }
>> +        }
>> +    } else {
>> +        mcst = 0;
>> +    }
>> +
>> +    /* check if the destination matches NIC mac address */
>> +    if (!(s->maccr & MACCR_RCV_ALL) && !bcst && !mcst) {
>> +        if (memcmp(s->conf.macaddr.a, buf, 6)) {
>> +            return -1;
>> +        }
>> +    }
>> +
>> +    while (size > 0) {
>> +        off = s->rx_bar + s->rx_idx * sizeof(rxd);
>> +        ftmac110_read_rxdesc(off, &rxd);
>> +        if (!rxd.owner) {
>> +            s->isr |= ISR_NORXBUF;
>> +            DPRINTF("ftmac110: out of rxd!?\n");
>> +            return -1;
>> +        }
>> +
>> +        if (ptr == buf) {
>> +            rxd.frs = 1;
>> +        } else {
>> +            rxd.frs = 0;
>> +        }
>> +
>> +        len = size > rxd.bufsz ? rxd.bufsz : size;
>
> This write below is the DMA that the comment above talks about, but
> there's no alignment check here either.
>

The alignment check has been invoked inside
ftmac110_read_txdesc() and ftmac110_read_rxdesc()

>> +        cpu_physical_memory_write(rxd.buf, (uint8_t *)ptr, len);
>> +        ptr  += len;
>> +        size -= len;
>> +
>> +        if (size <= 0) {
>> +            rxd.lrs = 1;
>> +        } else {
>> +            rxd.lrs = 0;
>> +        }
>> +
>> +        rxd.len = len;
>> +        rxd.bcast = bcst;
>> +        rxd.mcast = mcst;
>> +        rxd.owner = 0;
>> +
>> +        /* write-back the rx descriptor */
>> +        ftmac110_write_rxdesc(off, &rxd);
>> +
>> +        if (rxd.end) {
>> +            s->rx_idx = 0;
>> +        } else {
>> +            s->rx_idx += 1;
>> +        }
>> +    }
>> +
>> +    /* update interrupt signal */
>> +    s->isr |= ISR_RPKT_OK | ISR_RPKT_FINISH;
>> +    ftmac110_update_irq(s);
>> +
>> +    return (ssize_t)((uint32_t)ptr - (uint32_t)buf);
>> +}
>> +
>> +static void ftmac110_transmit(Ftmac110State *s, uint32_t *bar, uint32_t 
>> *idx)
>> +{
>> +    hwaddr off;
>> +    uint8_t *buf;
>> +    int ftl, proto;
>> +    Ftmac110TXD txd;
>> +
>> +    if ((s->maccr & (MACCR_XMT_EN | MACCR_XDMA_EN))
>> +            != (MACCR_XMT_EN | MACCR_XDMA_EN)) {
>> +        return;
>> +    }
>> +
>> +    do {
>> +        off = (*bar) + (*idx) * sizeof(txd);
>> +        ftmac110_read_txdesc(off, &txd);
>> +        if (!txd.owner) {
>> +            s->isr |= ISR_NOTXBUF;
>> +            break;
>> +        }
>> +        if (txd.fts) {
>> +            s->txbuff.len = 0;
>> +        }
>> +        if (txd.len + s->txbuff.len > sizeof(s->txbuff.buf)) {
>> +            hw_error("ftmac110: tx buffer overflow!\n");
>> +            exit(1);
>> +        }
>> +        buf = s->txbuff.buf + s->txbuff.len;
>
> Ditto.
>
>> +        cpu_physical_memory_read(txd.buf, (uint8_t *)buf, txd.len);
>> +        s->txbuff.len += txd.len;
>> +        /* Check if it's a long frame. (CRC32 is excluded) */
>> +        proto = (s->txbuff.buf[12] << 8) | s->txbuff.buf[13];
>> +        if (proto == 0x8100) {
>> +            ftl = (s->txbuff.len > 1518) ? 1 : 0;
>> +        } else {
>> +            ftl = (s->txbuff.len > 1514) ? 1 : 0;
>> +        }
>> +        if (ftl) {
>> +            hw_error("ftmac110_transmit: frame too long\n");
>> +            exit(1);
>> +        }
>> +        if (txd.lts) {
>> +            if (s->maccr & MACCR_LOOP_EN) {
>> +                ftmac110_receive(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
>> +            } else {
>> +                qemu_send_packet(&s->nic->nc, s->txbuff.buf, s->txbuff.len);
>> +            }
>> +        }
>> +        if (txd.end) {
>> +            *idx = 0;
>> +        } else {
>> +            *idx += 1;
>> +        }
>> +        if (txd.tx2fic) {
>> +            s->isr |= ISR_XPKT_OK;
>> +        }
>> +        if (txd.txic) {
>> +            s->isr |= ISR_XPKT_FINISH;
>> +        }
>> +        txd.owner = 0;
>> +        ftmac110_write_txdesc(off, &txd);
>> +    } while (1);
>> +}
>> +
>> +static void ftmac110_bh(void *opaque)
>> +{
>> +    Ftmac110State *s = FTMAC110(opaque);
>> +
>> +    if (s->tx_bar) {
>> +        ftmac110_transmit(s, &s->tx_bar, &s->tx_idx);
>> +    }
>> +
>> +    ftmac110_update_irq(s);
>> +}
>> +
>> +static uint64_t ftmac110_mem_read(void    *opaque,
>> +                                  hwaddr   addr,
>> +                                  unsigned size)
>> +{
>> +    Ftmac110State *s = FTMAC110(opaque);
>> +    uint32_t rc = 0;
>> +
>> +    switch (addr) {
>> +    case REG_ISR:
>> +        rc = s->isr;
>> +        s->isr = 0;
>> +        ftmac110_update_irq(s);
>> +        break;
>> +    case REG_IMR:
>> +        return s->ier;
>> +    case REG_HMAC:
>> +        return s->conf.macaddr.a[1]
>> +               | (s->conf.macaddr.a[0] << 8);
>> +    case REG_LMAC:
>> +        return s->conf.macaddr.a[5]
>> +               | (s->conf.macaddr.a[4] << 8)
>> +               | (s->conf.macaddr.a[3] << 16)
>> +               | (s->conf.macaddr.a[2] << 24);
>> +    case REG_MHASH0:
>> +        return s->mhash[0];
>> +    case REG_MHASH1:
>> +        return s->mhash[1];
>> +    case REG_TXBAR:
>> +        return s->tx_bar;
>> +    case REG_RXBAR:
>> +        return s->rx_bar;
>> +    case REG_MACCR:
>> +        return s->maccr;
>> +    case REG_MACSR:
>> +        rc = s->macsr;
>> +        s->macsr = 0;
>> +        break;
>> +    case REG_PHYCTRL:
>> +        do {
>> +            uint8_t dev = (s->phycr >> 16) & 0x1f;
>> +            uint8_t reg = (s->phycr >> 21) & 0x1f;
>> +            if (dev != 0) {
>> +                break;
>> +            }
>> +            if (s->phycr_rd) {
>> +                switch (reg) {
>> +                case 0:    /* PHY control register */
>> +                    return s->phycr | 0x1140;
>> +                case 1:    /* PHY status register */
>> +                    return s->phycr | 0x796d;
>> +                case 2:    /* PHY ID 1 register */
>> +                    return s->phycr | 0x0141;
>> +                case 3:    /* PHY ID 2 register */
>> +                    return s->phycr | 0x0cc2;
>> +                case 4:    /* Autonegotiation advertisement register */
>> +                    return s->phycr | 0x0de1;
>> +                case 5:    /* Autonegotiation partner abilities register */
>> +                    return s->phycr | 0x45e1;
>> +                }
>> +            }
>> +        } while (0);
>> +        break;
>> +    case REG_FCR:
>> +        return 0x0000a400;
>> +    case REG_BPR:
>> +        return 0x00000400;
>> +    case REG_TXPKT:
>> +        return s->tx_pkt;
>> +    case REG_RXPKT:
>> +        return s->rx_pkt;
>> +    case REG_RXBCST:
>> +        return s->rx_bcst;
>> +    case REG_RXMCST:
>> +        return s->rx_mcst;
>> +    case REG_RXRUNT:
>> +        return s->rx_runt << 16;
>> +    case REG_RXCRCFTL:
>> +        return (s->rx_crc << 16) | (s->rx_ftl);
>> +    case REG_REV:
>> +        return 0x00000700;
>> +    case REG_FEA:
>> +        return 0x00000007;
>> +    default:
>> +        break;
>> +    }
>> +
>> +    return rc;
>> +}
>> +
>> +static void ftmac110_chip_reset(Ftmac110State *s)
>> +{
>> +    s->isr = 0;
>> +    s->ier = 0;
>> +    s->mhash[0] = 0;
>> +    s->mhash[1] = 0;
>> +    s->tx_bar = 0;
>> +    s->rx_bar = 0;
>> +    s->tx_idx = 0;
>> +    s->rx_idx = 0;
>> +    s->maccr = 0;
>> +    s->macsr = 0;
>> +    s->phycr = 0;
>> +    s->txbuff.len = 0;
>> +    s->rx_pkt = 0;
>> +    s->rx_bcst = 0;
>> +    s->rx_mcst = 0;
>> +    s->rx_runt = 0;
>> +    s->rx_drop = 0;
>> +    s->rx_crc = 0;
>> +    s->rx_ftl = 0;
>> +    s->tx_pkt = 0;
>> +
>> +    if (s->bh) {
>> +        qemu_bh_cancel(s->bh);
>> +    }
>> +
>> +    ftmac110_update_irq(s);
>> +}
>> +
>> +static void ftmac110_mem_write(void    *opaque,
>> +                               hwaddr   addr,
>> +                               uint64_t val,
>> +                               unsigned size)
>> +{
>> +    Ftmac110State *s = FTMAC110(opaque);
>> +
>> +    switch (addr) {
>> +    case REG_IMR:
>> +        s->ier = (uint32_t)val;
>> +        ftmac110_update_irq(s);
>> +        break;
>> +    case REG_HMAC:
>> +        s->conf.macaddr.a[1] = (val >> 0) & 0xff;
>> +        s->conf.macaddr.a[0] = (val >> 8) & 0xff;
>> +        break;
>> +    case REG_LMAC:
>> +        s->conf.macaddr.a[5] = (val >> 0) & 0xff;
>> +        s->conf.macaddr.a[4] = (val >> 8) & 0xff;
>> +        s->conf.macaddr.a[3] = (val >> 16) & 0xff;
>> +        s->conf.macaddr.a[2] = (val >> 24) & 0xff;
>> +        break;
>> +    case REG_MHASH0:
>> +        s->mhash[0] = (uint32_t)val;
>> +        break;
>> +    case REG_MHASH1:
>> +        s->mhash[1] = (uint32_t)val;
>> +        break;
>> +    case REG_TXBAR:
>> +        s->tx_bar = (uint32_t)val;
>> +        break;
>> +    case REG_RXBAR:
>> +        s->rx_bar = (uint32_t)val;
>> +        break;
>> +    case REG_MACCR:
>> +        s->maccr = (uint32_t)val;
>> +        if (s->maccr & MACCR_SW_RST) {
>> +            ftmac110_chip_reset(s);
>> +            s->maccr &= ~MACCR_SW_RST;
>> +        }
>> +        break;
>> +    case REG_PHYCTRL:
>> +        s->phycr = (uint32_t)val;
>> +        if (s->phycr & PHYCR_MDIORD) {
>> +            s->phycr_rd = 1;
>> +        } else {
>> +            s->phycr_rd = 0;
>> +        }
>> +        s->phycr &= ~(PHYCR_MDIOWR | PHYCR_MDIORD);
>> +        break;
>> +    case REG_TXPD:
>> +        qemu_bh_schedule(s->bh);
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps bus_ops = {
>> +    .read  = ftmac110_mem_read,
>> +    .write = ftmac110_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4
>> +    }
>> +};
>> +
>> +static void ftmac110_cleanup(NetClientState *nc)
>> +{
>> +    Ftmac110State *s = FTMAC110(DO_UPCAST(NICState, nc, nc)->opaque);
>> +
>> +    s->nic = NULL;
>> +}
>> +
>> +static NetClientInfo net_ftmac110_info = {
>> +    .type = NET_CLIENT_OPTIONS_KIND_NIC,
>> +    .size = sizeof(NICState),
>> +    .can_receive = ftmac110_can_receive,
>> +    .receive = ftmac110_receive,
>> +    .cleanup = ftmac110_cleanup,
>> +};
>> +
>> +static void ftmac110_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftmac110State *s = FTMAC110(FROM_SYSBUS(Ftmac110State, busdev));
>> +
>> +    ftmac110_chip_reset(s);
>> +}
>> +
>> +static int ftmac110_init1(SysBusDevice *dev)
>> +{
>> +    Ftmac110State *s = FTMAC110(FROM_SYSBUS(Ftmac110State, dev));
>> +
>> +    memory_region_init_io(&s->mmio, &bus_ops, s, TYPE_FTMAC110, 0x1000);
>> +    sysbus_init_mmio(dev, &s->mmio);
>> +    sysbus_init_irq(dev, &s->irq);
>> +
>> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
>> +    s->nic = qemu_new_nic(&net_ftmac110_info, &s->conf,
>> +                          object_get_typename(OBJECT(dev)), dev->qdev.id, 
>> s);
>> +    qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
>> +
>> +    s->bh = qemu_bh_new(ftmac110_bh, s);
>> +
>> +    ftmac110_chip_reset(s);
>> +
>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_ftmac110 = {
>> +    .name = TYPE_FTMAC110,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32(ier, Ftmac110State),
>> +        VMSTATE_UINT32(tx_bar, Ftmac110State),
>> +        VMSTATE_UINT32(rx_bar, Ftmac110State),
>> +        VMSTATE_UINT32(tx_idx, Ftmac110State),
>> +        VMSTATE_UINT32(rx_idx, Ftmac110State),
>> +        VMSTATE_UINT32(maccr, Ftmac110State),
>> +        VMSTATE_UINT32(macsr, Ftmac110State),
>> +        VMSTATE_UINT32(phycr, Ftmac110State),
>> +        VMSTATE_UINT32_ARRAY(mhash, Ftmac110State, 2),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static Property ftmac110_properties[] = {
>> +    DEFINE_NIC_PROPERTIES(Ftmac110State, conf),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void ftmac110_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> +    k->init   = ftmac110_init1;
>> +    dc->reset = ftmac110_reset;
>> +    dc->vmsd  = &vmstate_ftmac110;
>> +    dc->props = ftmac110_properties;
>> +}
>> +
>> +static const TypeInfo ftmac110_info = {
>> +    .name           = TYPE_FTMAC110,
>> +    .parent         = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size  = sizeof(Ftmac110State),
>> +    .class_init     = ftmac110_class_init,
>> +};
>> +
>> +static void ftmac110_register_types(void)
>> +{
>> +    type_register_static(&ftmac110_info);
>> +}
>> +
>> +/* Legacy helper function.  Should go away when machine config files are
>> +   implemented.  */
>> +void ftmac110_init(NICInfo *nd, uint32_t base, qemu_irq irq)
>> +{
>> +    DeviceState *dev;
>> +    SysBusDevice *s;
>> +
>> +    qemu_check_nic_model(nd, TYPE_FTMAC110);
>> +    dev = qdev_create(NULL, TYPE_FTMAC110);
>> +    qdev_set_nic_properties(dev, nd);
>> +    qdev_init_nofail(dev);
>> +    s = SYS_BUS_DEVICE(dev);
>> +    sysbus_mmio_map(s, 0, base);
>> +    sysbus_connect_irq(s, 0, irq);
>> +}
>> +
>> +type_init(ftmac110_register_types)
>> diff --git a/hw/arm/ftmac110.h b/hw/arm/ftmac110.h
>> new file mode 100644
>> index 0000000..61f44e7
>> --- /dev/null
>> +++ b/hw/arm/ftmac110.h
>> @@ -0,0 +1,131 @@
>> +/*
>> + * QEMU model of the FTMAC110 Controller
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <address@hidden>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + */
>> +
>> +#ifndef HW_ARM_FTMAC110_H
>> +#define HW_ARM_FTMAC110_H
>> +
>> +#define REG_ISR             0x00
>> +#define REG_IMR             0x04
>> +#define REG_HMAC            0x08
>> +#define REG_LMAC            0x0c
>> +#define REG_MHASH0          0x10
>> +#define REG_MHASH1          0x14
>> +#define REG_TXPD            0x18
>> +#define REG_RXPD            0x1c
>> +#define REG_TXBAR           0x20
>> +#define REG_RXBAR           0x24
>> +#define REG_ITC             0x28
>> +#define REG_APTC            0x2C
>> +#define REG_DBLAC           0x30
>> +#define REG_REV             0x34
>> +#define REG_FEA             0x38
>> +
>> +#define REG_MACCR           0x88
>> +#define REG_MACSR           0x8C
>> +#define REG_PHYCTRL         0x90
>> +#define REG_PHYDATA         0x94
>> +#define REG_FCR             0x98
>> +#define REG_BPR             0x9c
>> +
>> +#define REG_TXPKT           0xf8
>> +#define REG_RXPKT           0xf4
>> +#define REG_RXBCST          0xec
>> +#define REG_RXMCST          0xf0
>> +#define REG_RXRUNT          0xe0
>> +#define REG_RXCRCFTL        0xe4
>> +
>> +/* interrupt status register */
>> +#define ISR_PHYSTS_CHG      (1UL<<9)
>> +#define ISR_AHB_ERR         (1UL<<8)
>> +#define ISR_RPKT_LOST       (1UL<<7)
>> +#define ISR_RPKT_OK         (1UL<<6)
>> +#define ISR_XPKT_LOST       (1UL<<5)
>> +#define ISR_XPKT_OK         (1UL<<4)
>> +#define ISR_NOTXBUF         (1UL<<3)
>> +#define ISR_XPKT_FINISH     (1UL<<2)
>> +#define ISR_NORXBUF         (1UL<<1)
>> +#define ISR_RPKT_FINISH     (1UL<<0)
>> +
>> +/* MAC control register */
>> +#define MACCR_100M              (1UL<<18)
>> +#define MACCR_RX_BROADPKT       (1UL<<17)
>> +#define MACCR_RX_MULTIPKT       (1UL<<16)
>> +#define MACCR_FULLDUP           (1UL<<15)
>> +#define MACCR_CRC_APD           (1UL<<14)
>> +#define MACCR_RCV_ALL           (1UL<<12)
>> +#define MACCR_RX_FTL            (1UL<<11)
>> +#define MACCR_RX_RUNT           (1UL<<10)
>> +#define MACCR_HT_MULTI_EN       (1UL<<9)
>> +#define MACCR_RCV_EN            (1UL<<8)
>> +#define MACCR_ENRX_IN_HALFTX    (1UL<<6)
>> +#define MACCR_XMT_EN            (1UL<<5)
>> +#define MACCR_CRC_DIS           (1UL<<4)
>> +#define MACCR_LOOP_EN           (1UL<<3)
>> +#define MACCR_SW_RST            (1UL<<2)
>> +#define MACCR_RDMA_EN           (1UL<<1)
>> +#define MACCR_XDMA_EN           (1UL<<0)
>> +
>> +/*
>> + * MDIO
>> + */
>> +#define PHYCR_MDIOWR            (1 << 27)
>> +#define PHYCR_MDIORD            (1 << 26)
>> +
>> +/*
>> + * Tx/Rx descriptors
>> + */
>> +typedef struct Ftmac110RXD {
>> +    /* RXDES0 */
>> +    uint32_t len:11;
>> +    uint32_t rsvd1:5;
>> +    uint32_t mcast:1;
>> +    uint32_t bcast:1;
>> +    uint32_t error:5;
>> +    uint32_t rsvd2:5;
>> +    uint32_t lrs:1;
>> +    uint32_t frs:1;
>> +    uint32_t rsvd3:1;
>> +    uint32_t owner:1;   /* BIT: 31 - 1:HW, 0: SW */
>> +
>> +    /* RXDES1 */
>> +    uint32_t bufsz:11;
>> +    uint32_t rsvd4:20;
>> +    uint32_t end:1;     /* BIT: 31 */
>> +
>> +    /* RXDES2 */
>> +    uint32_t buf;
>> +
>> +    /* RXDES3 */
>> +    void     *skb;
>> +} __attribute__ ((aligned (16))) Ftmac110RXD;
>
> The alignment attributes are not useful since we are not accessing
> memory directly.
>

It's here because of the following codes:

static void ftmac110_read_txdesc(hwaddr addr, Ftmac110TXD *desc)
{
......
    cpu_physical_memory_read(addr, desc, sizeof(*desc));

    for (i = 0; i < sizeof(*desc); i += 4) {
        *p = le32_to_cpu(*p);
    }
......
}

In other words, it would be better to make sure these descriptors are
always aligned to 4 bytes boundary. And to avoid ambigous, I use the
real hardware
algnement (16  bytes)  instead of the emulator alignment (4 bytes).

P.S
In x86 platforms, the unaligned access is always resolved by the hardware
cores, so it's no harm if we have some unaligned issues.

>> +
>> +typedef struct Ftmac110TXD {
>> +    /* TXDES0 */
>> +    uint32_t error:2;
>> +    uint32_t rsvd1:29;
>> +    uint32_t owner:1;   /* BIT: 31 - 1:HW, 0: SW */
>> +
>> +    /* TXDES1 */
>> +    uint32_t len:11;
>> +    uint32_t rsvd2:16;
>> +    uint32_t lts:1;
>> +    uint32_t fts:1;
>> +    uint32_t tx2fic:1;
>> +    uint32_t txic:1;
>> +    uint32_t end:1;     /* BIT: 31 */
>> +
>> +    /* TXDES2 */
>> +    uint32_t buf;
>> +
>> +    /* TXDES3 */
>> +    void     *skb;
>> +
>> +} __attribute__ ((aligned (16))) Ftmac110TXD;
>> +
>> +#endif  /* FTMAC_H */
>
> HW_ARM_FTMAC110_H
>
>> --
>> 1.7.9.5
>>



-- 
Best wishes,
Kuo-Jung Su



reply via email to

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