qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

etraxfs_eth.c (18017B)


      1 /*
      2  * QEMU ETRAX Ethernet Controller.
      3  *
      4  * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22  * THE SOFTWARE.
     23  */
     24 
     25 #include "qemu/osdep.h"
     26 #include "qapi/error.h"
     27 #include "hw/sysbus.h"
     28 #include "net/net.h"
     29 #include "hw/cris/etraxfs.h"
     30 #include "qemu/error-report.h"
     31 #include "qemu/module.h"
     32 #include "trace.h"
     33 #include "qom/object.h"
     34 
     35 #define D(x)
     36 
     37 /* Advertisement control register. */
     38 #define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
     39 #define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
     40 #define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
     41 #define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
     42 
     43 /*
     44  * The MDIO extensions in the TDK PHY model were reversed engineered from the
     45  * linux driver (PHYID and Diagnostics reg).
     46  * TODO: Add friendly names for the register nums.
     47  */
     48 struct qemu_phy
     49 {
     50     uint32_t regs[32];
     51 
     52     int link;
     53 
     54     unsigned int (*read)(struct qemu_phy *phy, unsigned int req);
     55     void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data);
     56 };
     57 
     58 static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req)
     59 {
     60     int regnum;
     61     unsigned r = 0;
     62 
     63     regnum = req & 0x1f;
     64 
     65     switch (regnum) {
     66     case 1:
     67         if (!phy->link) {
     68             break;
     69         }
     70         /* MR1.     */
     71         /* Speeds and modes.  */
     72         r |= (1 << 13) | (1 << 14);
     73         r |= (1 << 11) | (1 << 12);
     74         r |= (1 << 5); /* Autoneg complete.  */
     75         r |= (1 << 3); /* Autoneg able.     */
     76         r |= (1 << 2); /* link.     */
     77         break;
     78     case 5:
     79         /* Link partner ability.
     80            We are kind; always agree with whatever best mode
     81            the guest advertises.  */
     82         r = 1 << 14; /* Success.  */
     83         /* Copy advertised modes.  */
     84         r |= phy->regs[4] & (15 << 5);
     85         /* Autoneg support.  */
     86         r |= 1;
     87         break;
     88     case 18:
     89     {
     90         /* Diagnostics reg.  */
     91         int duplex = 0;
     92         int speed_100 = 0;
     93 
     94         if (!phy->link) {
     95             break;
     96         }
     97 
     98         /* Are we advertising 100 half or 100 duplex ? */
     99         speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
    100         speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
    101 
    102         /* Are we advertising 10 duplex or 100 duplex ? */
    103         duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
    104         duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
    105         r = (speed_100 << 10) | (duplex << 11);
    106     }
    107     break;
    108 
    109     default:
    110         r = phy->regs[regnum];
    111         break;
    112     }
    113     trace_mdio_phy_read(regnum, r);
    114     return r;
    115 }
    116 
    117 static void
    118 tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data)
    119 {
    120     int regnum;
    121 
    122     regnum = req & 0x1f;
    123     trace_mdio_phy_write(regnum, data);
    124     switch (regnum) {
    125     default:
    126         phy->regs[regnum] = data;
    127         break;
    128     }
    129 }
    130 
    131 static void
    132 tdk_reset(struct qemu_phy *phy)
    133 {
    134     phy->regs[0] = 0x3100;
    135     /* PHY Id.  */
    136     phy->regs[2] = 0x0300;
    137     phy->regs[3] = 0xe400;
    138     /* Autonegotiation advertisement reg.  */
    139     phy->regs[4] = 0x01E1;
    140     phy->link = 1;
    141 }
    142 
    143 struct qemu_mdio
    144 {
    145     /* bus.     */
    146     int mdc;
    147     int mdio;
    148 
    149     /* decoder.  */
    150     enum {
    151         PREAMBLE,
    152         SOF,
    153         OPC,
    154         ADDR,
    155         REQ,
    156         TURNAROUND,
    157         DATA
    158     } state;
    159     unsigned int drive;
    160 
    161     unsigned int cnt;
    162     unsigned int addr;
    163     unsigned int opc;
    164     unsigned int req;
    165     unsigned int data;
    166 
    167     struct qemu_phy *devs[32];
    168 };
    169 
    170 static void
    171 mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
    172 {
    173     bus->devs[addr & 0x1f] = phy;
    174 }
    175 
    176 #ifdef USE_THIS_DEAD_CODE
    177 static void
    178 mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr)
    179 {
    180     bus->devs[addr & 0x1f] = NULL;
    181 }
    182 #endif
    183 
    184 static void mdio_read_req(struct qemu_mdio *bus)
    185 {
    186     struct qemu_phy *phy;
    187 
    188     phy = bus->devs[bus->addr];
    189     if (phy && phy->read) {
    190         bus->data = phy->read(phy, bus->req);
    191     } else {
    192         bus->data = 0xffff;
    193     }
    194 }
    195 
    196 static void mdio_write_req(struct qemu_mdio *bus)
    197 {
    198     struct qemu_phy *phy;
    199 
    200     phy = bus->devs[bus->addr];
    201     if (phy && phy->write) {
    202         phy->write(phy, bus->req, bus->data);
    203     }
    204 }
    205 
    206 static void mdio_cycle(struct qemu_mdio *bus)
    207 {
    208     bus->cnt++;
    209 
    210     trace_mdio_bitbang(bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive);
    211 #if 0
    212     if (bus->mdc) {
    213         printf("%d", bus->mdio);
    214     }
    215 #endif
    216     switch (bus->state) {
    217     case PREAMBLE:
    218         if (bus->mdc) {
    219             if (bus->cnt >= (32 * 2) && !bus->mdio) {
    220                 bus->cnt = 0;
    221                 bus->state = SOF;
    222                 bus->data = 0;
    223             }
    224         }
    225         break;
    226     case SOF:
    227         if (bus->mdc) {
    228             if (bus->mdio != 1) {
    229                 printf("WARNING: no SOF\n");
    230             }
    231             if (bus->cnt == 1*2) {
    232                 bus->cnt = 0;
    233                 bus->opc = 0;
    234                 bus->state = OPC;
    235             }
    236         }
    237         break;
    238     case OPC:
    239         if (bus->mdc) {
    240             bus->opc <<= 1;
    241             bus->opc |= bus->mdio & 1;
    242             if (bus->cnt == 2*2) {
    243                 bus->cnt = 0;
    244                 bus->addr = 0;
    245                 bus->state = ADDR;
    246             }
    247         }
    248         break;
    249     case ADDR:
    250         if (bus->mdc) {
    251             bus->addr <<= 1;
    252             bus->addr |= bus->mdio & 1;
    253 
    254             if (bus->cnt == 5*2) {
    255                 bus->cnt = 0;
    256                 bus->req = 0;
    257                 bus->state = REQ;
    258             }
    259         }
    260         break;
    261     case REQ:
    262         if (bus->mdc) {
    263             bus->req <<= 1;
    264             bus->req |= bus->mdio & 1;
    265             if (bus->cnt == 5*2) {
    266                 bus->cnt = 0;
    267                 bus->state = TURNAROUND;
    268             }
    269         }
    270         break;
    271     case TURNAROUND:
    272         if (bus->mdc && bus->cnt == 2*2) {
    273             bus->mdio = 0;
    274             bus->cnt = 0;
    275 
    276             if (bus->opc == 2) {
    277                 bus->drive = 1;
    278                 mdio_read_req(bus);
    279                 bus->mdio = bus->data & 1;
    280             }
    281             bus->state = DATA;
    282         }
    283         break;
    284     case DATA:
    285         if (!bus->mdc) {
    286             if (bus->drive) {
    287                 bus->mdio = !!(bus->data & (1 << 15));
    288                 bus->data <<= 1;
    289             }
    290         } else {
    291             if (!bus->drive) {
    292                 bus->data <<= 1;
    293                 bus->data |= bus->mdio;
    294             }
    295             if (bus->cnt == 16 * 2) {
    296                 bus->cnt = 0;
    297                 bus->state = PREAMBLE;
    298                 if (!bus->drive) {
    299                     mdio_write_req(bus);
    300                 }
    301                 bus->drive = 0;
    302             }
    303         }
    304         break;
    305     default:
    306         break;
    307     }
    308 }
    309 
    310 /* ETRAX-FS Ethernet MAC block starts here.  */
    311 
    312 #define RW_MA0_LO      0x00
    313 #define RW_MA0_HI      0x01
    314 #define RW_MA1_LO      0x02
    315 #define RW_MA1_HI      0x03
    316 #define RW_GA_LO      0x04
    317 #define RW_GA_HI      0x05
    318 #define RW_GEN_CTRL      0x06
    319 #define RW_REC_CTRL      0x07
    320 #define RW_TR_CTRL      0x08
    321 #define RW_CLR_ERR      0x09
    322 #define RW_MGM_CTRL      0x0a
    323 #define R_STAT          0x0b
    324 #define FS_ETH_MAX_REGS      0x17
    325 
    326 #define TYPE_ETRAX_FS_ETH "etraxfs-eth"
    327 OBJECT_DECLARE_SIMPLE_TYPE(ETRAXFSEthState, ETRAX_FS_ETH)
    328 
    329 struct ETRAXFSEthState {
    330     SysBusDevice parent_obj;
    331 
    332     MemoryRegion mmio;
    333     NICState *nic;
    334     NICConf conf;
    335 
    336     /* Two addrs in the filter.  */
    337     uint8_t macaddr[2][6];
    338     uint32_t regs[FS_ETH_MAX_REGS];
    339 
    340     struct etraxfs_dma_client *dma_out;
    341     struct etraxfs_dma_client *dma_in;
    342 
    343     /* MDIO bus.  */
    344     struct qemu_mdio mdio_bus;
    345     unsigned int phyaddr;
    346     int duplex_mismatch;
    347 
    348     /* PHY.     */
    349     struct qemu_phy phy;
    350 };
    351 
    352 static void eth_validate_duplex(ETRAXFSEthState *eth)
    353 {
    354     struct qemu_phy *phy;
    355     unsigned int phy_duplex;
    356     unsigned int mac_duplex;
    357     int new_mm = 0;
    358 
    359     phy = eth->mdio_bus.devs[eth->phyaddr];
    360     phy_duplex = !!(phy->read(phy, 18) & (1 << 11));
    361     mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128);
    362 
    363     if (mac_duplex != phy_duplex) {
    364         new_mm = 1;
    365     }
    366 
    367     if (eth->regs[RW_GEN_CTRL] & 1) {
    368         if (new_mm != eth->duplex_mismatch) {
    369             if (new_mm) {
    370                 printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n",
    371                        mac_duplex, phy_duplex);
    372             } else {
    373                 printf("HW: ETH duplex ok.\n");
    374             }
    375         }
    376         eth->duplex_mismatch = new_mm;
    377     }
    378 }
    379 
    380 static uint64_t
    381 eth_read(void *opaque, hwaddr addr, unsigned int size)
    382 {
    383     ETRAXFSEthState *eth = opaque;
    384     uint32_t r = 0;
    385 
    386     addr >>= 2;
    387 
    388     switch (addr) {
    389     case R_STAT:
    390         r = eth->mdio_bus.mdio & 1;
    391         break;
    392     default:
    393         r = eth->regs[addr];
    394         D(printf("%s %x\n", __func__, addr * 4));
    395         break;
    396     }
    397     return r;
    398 }
    399 
    400 static void eth_update_ma(ETRAXFSEthState *eth, int ma)
    401 {
    402     int reg;
    403     int i = 0;
    404 
    405     ma &= 1;
    406 
    407     reg = RW_MA0_LO;
    408     if (ma) {
    409         reg = RW_MA1_LO;
    410     }
    411 
    412     eth->macaddr[ma][i++] = eth->regs[reg];
    413     eth->macaddr[ma][i++] = eth->regs[reg] >> 8;
    414     eth->macaddr[ma][i++] = eth->regs[reg] >> 16;
    415     eth->macaddr[ma][i++] = eth->regs[reg] >> 24;
    416     eth->macaddr[ma][i++] = eth->regs[reg + 1];
    417     eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8;
    418 
    419     D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma,
    420              eth->macaddr[ma][0], eth->macaddr[ma][1],
    421              eth->macaddr[ma][2], eth->macaddr[ma][3],
    422              eth->macaddr[ma][4], eth->macaddr[ma][5]));
    423 }
    424 
    425 static void
    426 eth_write(void *opaque, hwaddr addr,
    427           uint64_t val64, unsigned int size)
    428 {
    429     ETRAXFSEthState *eth = opaque;
    430     uint32_t value = val64;
    431 
    432     addr >>= 2;
    433     switch (addr) {
    434     case RW_MA0_LO:
    435     case RW_MA0_HI:
    436         eth->regs[addr] = value;
    437         eth_update_ma(eth, 0);
    438         break;
    439     case RW_MA1_LO:
    440     case RW_MA1_HI:
    441         eth->regs[addr] = value;
    442         eth_update_ma(eth, 1);
    443         break;
    444 
    445     case RW_MGM_CTRL:
    446         /* Attach an MDIO/PHY abstraction.  */
    447         if (value & 2) {
    448             eth->mdio_bus.mdio = value & 1;
    449         }
    450         if (eth->mdio_bus.mdc != (value & 4)) {
    451             mdio_cycle(&eth->mdio_bus);
    452             eth_validate_duplex(eth);
    453         }
    454         eth->mdio_bus.mdc = !!(value & 4);
    455         eth->regs[addr] = value;
    456         break;
    457 
    458     case RW_REC_CTRL:
    459         eth->regs[addr] = value;
    460         eth_validate_duplex(eth);
    461         break;
    462 
    463     default:
    464         eth->regs[addr] = value;
    465         D(printf("%s %x %x\n", __func__, addr, value));
    466         break;
    467     }
    468 }
    469 
    470 /* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom
    471    filter dropping group addresses we have not joined.    The filter has 64
    472    bits (m). The has function is a simple nible xor of the group addr.    */
    473 static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa)
    474 {
    475     unsigned int hsh;
    476     int m_individual = eth->regs[RW_REC_CTRL] & 4;
    477     int match;
    478 
    479     /* First bit on the wire of a MAC address signals multicast or
    480        physical address.  */
    481     if (!m_individual && !(sa[0] & 1)) {
    482         return 0;
    483     }
    484 
    485     /* Calculate the hash index for the GA registers. */
    486     hsh = 0;
    487     hsh ^= (*sa) & 0x3f;
    488     hsh ^= ((*sa) >> 6) & 0x03;
    489     ++sa;
    490     hsh ^= ((*sa) << 2) & 0x03c;
    491     hsh ^= ((*sa) >> 4) & 0xf;
    492     ++sa;
    493     hsh ^= ((*sa) << 4) & 0x30;
    494     hsh ^= ((*sa) >> 2) & 0x3f;
    495     ++sa;
    496     hsh ^= (*sa) & 0x3f;
    497     hsh ^= ((*sa) >> 6) & 0x03;
    498     ++sa;
    499     hsh ^= ((*sa) << 2) & 0x03c;
    500     hsh ^= ((*sa) >> 4) & 0xf;
    501     ++sa;
    502     hsh ^= ((*sa) << 4) & 0x30;
    503     hsh ^= ((*sa) >> 2) & 0x3f;
    504 
    505     hsh &= 63;
    506     if (hsh > 31) {
    507         match = eth->regs[RW_GA_HI] & (1 << (hsh - 32));
    508     } else {
    509         match = eth->regs[RW_GA_LO] & (1 << hsh);
    510     }
    511     D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh,
    512              eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match));
    513     return match;
    514 }
    515 
    516 static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
    517 {
    518     unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
    519     ETRAXFSEthState *eth = qemu_get_nic_opaque(nc);
    520     int use_ma0 = eth->regs[RW_REC_CTRL] & 1;
    521     int use_ma1 = eth->regs[RW_REC_CTRL] & 2;
    522     int r_bcast = eth->regs[RW_REC_CTRL] & 8;
    523 
    524     if (size < 12) {
    525         return -1;
    526     }
    527 
    528     D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n",
    529          buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
    530          use_ma0, use_ma1, r_bcast));
    531 
    532     /* Does the frame get through the address filters?  */
    533     if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6))
    534         && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6))
    535         && (!r_bcast || memcmp(buf, sa_bcast, 6))
    536         && !eth_match_groupaddr(eth, buf)) {
    537         return size;
    538     }
    539 
    540     /* FIXME: Find another way to pass on the fake csum.  */
    541     etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1);
    542 
    543     return size;
    544 }
    545 
    546 static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop)
    547 {
    548     ETRAXFSEthState *eth = opaque;
    549 
    550     D(printf("%s buf=%p len=%d\n", __func__, buf, len));
    551     qemu_send_packet(qemu_get_queue(eth->nic), buf, len);
    552     return len;
    553 }
    554 
    555 static void eth_set_link(NetClientState *nc)
    556 {
    557     ETRAXFSEthState *eth = qemu_get_nic_opaque(nc);
    558     D(printf("%s %d\n", __func__, nc->link_down));
    559     eth->phy.link = !nc->link_down;
    560 }
    561 
    562 static const MemoryRegionOps eth_ops = {
    563     .read = eth_read,
    564     .write = eth_write,
    565     .endianness = DEVICE_LITTLE_ENDIAN,
    566     .valid = {
    567         .min_access_size = 4,
    568         .max_access_size = 4
    569     }
    570 };
    571 
    572 static NetClientInfo net_etraxfs_info = {
    573     .type = NET_CLIENT_DRIVER_NIC,
    574     .size = sizeof(NICState),
    575     .receive = eth_receive,
    576     .link_status_changed = eth_set_link,
    577 };
    578 
    579 static void etraxfs_eth_reset(DeviceState *dev)
    580 {
    581     ETRAXFSEthState *s = ETRAX_FS_ETH(dev);
    582 
    583     memset(s->regs, 0, sizeof(s->regs));
    584     memset(s->macaddr, 0, sizeof(s->macaddr));
    585     s->duplex_mismatch = 0;
    586 
    587     s->mdio_bus.mdc = 0;
    588     s->mdio_bus.mdio = 0;
    589     s->mdio_bus.state = 0;
    590     s->mdio_bus.drive = 0;
    591     s->mdio_bus.cnt = 0;
    592     s->mdio_bus.addr = 0;
    593     s->mdio_bus.opc = 0;
    594     s->mdio_bus.req = 0;
    595     s->mdio_bus.data = 0;
    596 
    597     tdk_reset(&s->phy);
    598 }
    599 
    600 static void etraxfs_eth_realize(DeviceState *dev, Error **errp)
    601 {
    602     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    603     ETRAXFSEthState *s = ETRAX_FS_ETH(dev);
    604 
    605     if (!s->dma_out || !s->dma_in) {
    606         error_setg(errp, "Unconnected ETRAX-FS Ethernet MAC");
    607         return;
    608     }
    609 
    610     s->dma_out->client.push = eth_tx_push;
    611     s->dma_out->client.opaque = s;
    612     s->dma_in->client.opaque = s;
    613     s->dma_in->client.pull = NULL;
    614 
    615     memory_region_init_io(&s->mmio, OBJECT(dev), &eth_ops, s,
    616                           "etraxfs-eth", 0x5c);
    617     sysbus_init_mmio(sbd, &s->mmio);
    618 
    619     qemu_macaddr_default_if_unset(&s->conf.macaddr);
    620     s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf,
    621                           object_get_typename(OBJECT(s)), dev->id, s);
    622     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
    623 
    624     s->phy.read = tdk_read;
    625     s->phy.write = tdk_write;
    626     mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr);
    627 }
    628 
    629 static Property etraxfs_eth_properties[] = {
    630     DEFINE_PROP_UINT32("phyaddr", ETRAXFSEthState, phyaddr, 1),
    631     DEFINE_NIC_PROPERTIES(ETRAXFSEthState, conf),
    632     DEFINE_PROP_END_OF_LIST(),
    633 };
    634 
    635 static void etraxfs_eth_class_init(ObjectClass *klass, void *data)
    636 {
    637     DeviceClass *dc = DEVICE_CLASS(klass);
    638 
    639     dc->realize = etraxfs_eth_realize;
    640     dc->reset = etraxfs_eth_reset;
    641     device_class_set_props(dc, etraxfs_eth_properties);
    642     /* Reason: dma_out, dma_in are not user settable */
    643     dc->user_creatable = false;
    644 }
    645 
    646 
    647 /* Instantiate an ETRAXFS Ethernet MAC.  */
    648 DeviceState *
    649 etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr,
    650                  struct etraxfs_dma_client *dma_out,
    651                  struct etraxfs_dma_client *dma_in)
    652 {
    653     DeviceState *dev;
    654     qemu_check_nic_model(nd, "fseth");
    655 
    656     dev = qdev_new("etraxfs-eth");
    657     qdev_set_nic_properties(dev, nd);
    658     qdev_prop_set_uint32(dev, "phyaddr", phyaddr);
    659 
    660     /*
    661      * TODO: QOM design, define a QOM interface for "I am an etraxfs
    662      * DMA client" (which replaces the current 'struct
    663      * etraxfs_dma_client' ad-hoc interface), implement it on the
    664      * ethernet device, and then have QOM link properties on the DMA
    665      * controller device so that you can pass the interface
    666      * implementations to it.
    667      */
    668     ETRAX_FS_ETH(dev)->dma_out = dma_out;
    669     ETRAX_FS_ETH(dev)->dma_in = dma_in;
    670     sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
    671     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
    672 
    673     return dev;
    674 }
    675 
    676 static const TypeInfo etraxfs_eth_info = {
    677     .name          = TYPE_ETRAX_FS_ETH,
    678     .parent        = TYPE_SYS_BUS_DEVICE,
    679     .instance_size = sizeof(ETRAXFSEthState),
    680     .class_init    = etraxfs_eth_class_init,
    681 };
    682 
    683 static void etraxfs_eth_register_types(void)
    684 {
    685     type_register_static(&etraxfs_eth_info);
    686 }
    687 
    688 type_init(etraxfs_eth_register_types)