qemu

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

mv88w8618_eth.c (11618B)


      1 /* SPDX-License-Identifier: GPL-2.0-or-later */
      2 /*
      3  * Marvell MV88W8618 / Freecom MusicPal emulation.
      4  *
      5  * Copyright (c) 2008 Jan Kiszka
      6  */
      7 
      8 #include "qemu/osdep.h"
      9 #include "qapi/error.h"
     10 #include "hw/qdev-properties.h"
     11 #include "hw/sysbus.h"
     12 #include "hw/irq.h"
     13 #include "hw/net/mv88w8618_eth.h"
     14 #include "migration/vmstate.h"
     15 #include "sysemu/dma.h"
     16 #include "net/net.h"
     17 
     18 #define MP_ETH_SIZE             0x00001000
     19 
     20 /* Ethernet register offsets */
     21 #define MP_ETH_SMIR             0x010
     22 #define MP_ETH_PCXR             0x408
     23 #define MP_ETH_SDCMR            0x448
     24 #define MP_ETH_ICR              0x450
     25 #define MP_ETH_IMR              0x458
     26 #define MP_ETH_FRDP0            0x480
     27 #define MP_ETH_FRDP1            0x484
     28 #define MP_ETH_FRDP2            0x488
     29 #define MP_ETH_FRDP3            0x48C
     30 #define MP_ETH_CRDP0            0x4A0
     31 #define MP_ETH_CRDP1            0x4A4
     32 #define MP_ETH_CRDP2            0x4A8
     33 #define MP_ETH_CRDP3            0x4AC
     34 #define MP_ETH_CTDP0            0x4E0
     35 #define MP_ETH_CTDP1            0x4E4
     36 
     37 /* MII PHY access */
     38 #define MP_ETH_SMIR_DATA        0x0000FFFF
     39 #define MP_ETH_SMIR_ADDR        0x03FF0000
     40 #define MP_ETH_SMIR_OPCODE      (1 << 26) /* Read value */
     41 #define MP_ETH_SMIR_RDVALID     (1 << 27)
     42 
     43 /* PHY registers */
     44 #define MP_ETH_PHY1_BMSR        0x00210000
     45 #define MP_ETH_PHY1_PHYSID1     0x00410000
     46 #define MP_ETH_PHY1_PHYSID2     0x00610000
     47 
     48 #define MP_PHY_BMSR_LINK        0x0004
     49 #define MP_PHY_BMSR_AUTONEG     0x0008
     50 
     51 #define MP_PHY_88E3015          0x01410E20
     52 
     53 /* TX descriptor status */
     54 #define MP_ETH_TX_OWN           (1U << 31)
     55 
     56 /* RX descriptor status */
     57 #define MP_ETH_RX_OWN           (1U << 31)
     58 
     59 /* Interrupt cause/mask bits */
     60 #define MP_ETH_IRQ_RX_BIT       0
     61 #define MP_ETH_IRQ_RX           (1 << MP_ETH_IRQ_RX_BIT)
     62 #define MP_ETH_IRQ_TXHI_BIT     2
     63 #define MP_ETH_IRQ_TXLO_BIT     3
     64 
     65 /* Port config bits */
     66 #define MP_ETH_PCXR_2BSM_BIT    28 /* 2-byte incoming suffix */
     67 
     68 /* SDMA command bits */
     69 #define MP_ETH_CMD_TXHI         (1 << 23)
     70 #define MP_ETH_CMD_TXLO         (1 << 22)
     71 
     72 typedef struct mv88w8618_tx_desc {
     73     uint32_t cmdstat;
     74     uint16_t res;
     75     uint16_t bytes;
     76     uint32_t buffer;
     77     uint32_t next;
     78 } mv88w8618_tx_desc;
     79 
     80 typedef struct mv88w8618_rx_desc {
     81     uint32_t cmdstat;
     82     uint16_t bytes;
     83     uint16_t buffer_size;
     84     uint32_t buffer;
     85     uint32_t next;
     86 } mv88w8618_rx_desc;
     87 
     88 OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
     89 
     90 struct mv88w8618_eth_state {
     91     /*< private >*/
     92     SysBusDevice parent_obj;
     93     /*< public >*/
     94 
     95     MemoryRegion iomem;
     96     qemu_irq irq;
     97     MemoryRegion *dma_mr;
     98     AddressSpace dma_as;
     99     uint32_t smir;
    100     uint32_t icr;
    101     uint32_t imr;
    102     int mmio_index;
    103     uint32_t vlan_header;
    104     uint32_t tx_queue[2];
    105     uint32_t rx_queue[4];
    106     uint32_t frx_queue[4];
    107     uint32_t cur_rx[4];
    108     NICState *nic;
    109     NICConf conf;
    110 };
    111 
    112 static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
    113                             mv88w8618_rx_desc *desc)
    114 {
    115     cpu_to_le32s(&desc->cmdstat);
    116     cpu_to_le16s(&desc->bytes);
    117     cpu_to_le16s(&desc->buffer_size);
    118     cpu_to_le32s(&desc->buffer);
    119     cpu_to_le32s(&desc->next);
    120     dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
    121 }
    122 
    123 static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
    124                             mv88w8618_rx_desc *desc)
    125 {
    126     dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
    127     le32_to_cpus(&desc->cmdstat);
    128     le16_to_cpus(&desc->bytes);
    129     le16_to_cpus(&desc->buffer_size);
    130     le32_to_cpus(&desc->buffer);
    131     le32_to_cpus(&desc->next);
    132 }
    133 
    134 static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
    135 {
    136     mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
    137     uint32_t desc_addr;
    138     mv88w8618_rx_desc desc;
    139     int i;
    140 
    141     for (i = 0; i < 4; i++) {
    142         desc_addr = s->cur_rx[i];
    143         if (!desc_addr) {
    144             continue;
    145         }
    146         do {
    147             eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
    148             if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
    149                 dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
    150                                  buf, size, MEMTXATTRS_UNSPECIFIED);
    151                 desc.bytes = size + s->vlan_header;
    152                 desc.cmdstat &= ~MP_ETH_RX_OWN;
    153                 s->cur_rx[i] = desc.next;
    154 
    155                 s->icr |= MP_ETH_IRQ_RX;
    156                 if (s->icr & s->imr) {
    157                     qemu_irq_raise(s->irq);
    158                 }
    159                 eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
    160                 return size;
    161             }
    162             desc_addr = desc.next;
    163         } while (desc_addr != s->rx_queue[i]);
    164     }
    165     return size;
    166 }
    167 
    168 static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
    169                             mv88w8618_tx_desc *desc)
    170 {
    171     cpu_to_le32s(&desc->cmdstat);
    172     cpu_to_le16s(&desc->res);
    173     cpu_to_le16s(&desc->bytes);
    174     cpu_to_le32s(&desc->buffer);
    175     cpu_to_le32s(&desc->next);
    176     dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
    177 }
    178 
    179 static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
    180                             mv88w8618_tx_desc *desc)
    181 {
    182     dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
    183     le32_to_cpus(&desc->cmdstat);
    184     le16_to_cpus(&desc->res);
    185     le16_to_cpus(&desc->bytes);
    186     le32_to_cpus(&desc->buffer);
    187     le32_to_cpus(&desc->next);
    188 }
    189 
    190 static void eth_send(mv88w8618_eth_state *s, int queue_index)
    191 {
    192     uint32_t desc_addr = s->tx_queue[queue_index];
    193     mv88w8618_tx_desc desc;
    194     uint32_t next_desc;
    195     uint8_t buf[2048];
    196     int len;
    197 
    198     do {
    199         eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
    200         next_desc = desc.next;
    201         if (desc.cmdstat & MP_ETH_TX_OWN) {
    202             len = desc.bytes;
    203             if (len < 2048) {
    204                 dma_memory_read(&s->dma_as, desc.buffer, buf, len,
    205                                 MEMTXATTRS_UNSPECIFIED);
    206                 qemu_send_packet(qemu_get_queue(s->nic), buf, len);
    207             }
    208             desc.cmdstat &= ~MP_ETH_TX_OWN;
    209             s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
    210             eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
    211         }
    212         desc_addr = next_desc;
    213     } while (desc_addr != s->tx_queue[queue_index]);
    214 }
    215 
    216 static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
    217                                    unsigned size)
    218 {
    219     mv88w8618_eth_state *s = opaque;
    220 
    221     switch (offset) {
    222     case MP_ETH_SMIR:
    223         if (s->smir & MP_ETH_SMIR_OPCODE) {
    224             switch (s->smir & MP_ETH_SMIR_ADDR) {
    225             case MP_ETH_PHY1_BMSR:
    226                 return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
    227                        MP_ETH_SMIR_RDVALID;
    228             case MP_ETH_PHY1_PHYSID1:
    229                 return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
    230             case MP_ETH_PHY1_PHYSID2:
    231                 return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
    232             default:
    233                 return MP_ETH_SMIR_RDVALID;
    234             }
    235         }
    236         return 0;
    237 
    238     case MP_ETH_ICR:
    239         return s->icr;
    240 
    241     case MP_ETH_IMR:
    242         return s->imr;
    243 
    244     case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
    245         return s->frx_queue[(offset - MP_ETH_FRDP0) / 4];
    246 
    247     case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
    248         return s->rx_queue[(offset - MP_ETH_CRDP0) / 4];
    249 
    250     case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
    251         return s->tx_queue[(offset - MP_ETH_CTDP0) / 4];
    252 
    253     default:
    254         return 0;
    255     }
    256 }
    257 
    258 static void mv88w8618_eth_write(void *opaque, hwaddr offset,
    259                                 uint64_t value, unsigned size)
    260 {
    261     mv88w8618_eth_state *s = opaque;
    262 
    263     switch (offset) {
    264     case MP_ETH_SMIR:
    265         s->smir = value;
    266         break;
    267 
    268     case MP_ETH_PCXR:
    269         s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
    270         break;
    271 
    272     case MP_ETH_SDCMR:
    273         if (value & MP_ETH_CMD_TXHI) {
    274             eth_send(s, 1);
    275         }
    276         if (value & MP_ETH_CMD_TXLO) {
    277             eth_send(s, 0);
    278         }
    279         if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
    280             qemu_irq_raise(s->irq);
    281         }
    282         break;
    283 
    284     case MP_ETH_ICR:
    285         s->icr &= value;
    286         break;
    287 
    288     case MP_ETH_IMR:
    289         s->imr = value;
    290         if (s->icr & s->imr) {
    291             qemu_irq_raise(s->irq);
    292         }
    293         break;
    294 
    295     case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
    296         s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
    297         break;
    298 
    299     case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
    300         s->rx_queue[(offset - MP_ETH_CRDP0) / 4] =
    301             s->cur_rx[(offset - MP_ETH_CRDP0) / 4] = value;
    302         break;
    303 
    304     case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
    305         s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
    306         break;
    307     }
    308 }
    309 
    310 static const MemoryRegionOps mv88w8618_eth_ops = {
    311     .read = mv88w8618_eth_read,
    312     .write = mv88w8618_eth_write,
    313     .endianness = DEVICE_NATIVE_ENDIAN,
    314 };
    315 
    316 static void eth_cleanup(NetClientState *nc)
    317 {
    318     mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
    319 
    320     s->nic = NULL;
    321 }
    322 
    323 static NetClientInfo net_mv88w8618_info = {
    324     .type = NET_CLIENT_DRIVER_NIC,
    325     .size = sizeof(NICState),
    326     .receive = eth_receive,
    327     .cleanup = eth_cleanup,
    328 };
    329 
    330 static void mv88w8618_eth_init(Object *obj)
    331 {
    332     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    333     DeviceState *dev = DEVICE(sbd);
    334     mv88w8618_eth_state *s = MV88W8618_ETH(dev);
    335 
    336     sysbus_init_irq(sbd, &s->irq);
    337     memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
    338                           "mv88w8618-eth", MP_ETH_SIZE);
    339     sysbus_init_mmio(sbd, &s->iomem);
    340 }
    341 
    342 static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
    343 {
    344     mv88w8618_eth_state *s = MV88W8618_ETH(dev);
    345 
    346     if (!s->dma_mr) {
    347         error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
    348         return;
    349     }
    350 
    351     address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
    352     s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
    353                           object_get_typename(OBJECT(dev)), dev->id, s);
    354 }
    355 
    356 static const VMStateDescription mv88w8618_eth_vmsd = {
    357     .name = "mv88w8618_eth",
    358     .version_id = 1,
    359     .minimum_version_id = 1,
    360     .fields = (VMStateField[]) {
    361         VMSTATE_UINT32(smir, mv88w8618_eth_state),
    362         VMSTATE_UINT32(icr, mv88w8618_eth_state),
    363         VMSTATE_UINT32(imr, mv88w8618_eth_state),
    364         VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
    365         VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
    366         VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
    367         VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
    368         VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
    369         VMSTATE_END_OF_LIST()
    370     }
    371 };
    372 
    373 static Property mv88w8618_eth_properties[] = {
    374     DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
    375     DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
    376                      TYPE_MEMORY_REGION, MemoryRegion *),
    377     DEFINE_PROP_END_OF_LIST(),
    378 };
    379 
    380 static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
    381 {
    382     DeviceClass *dc = DEVICE_CLASS(klass);
    383 
    384     dc->vmsd = &mv88w8618_eth_vmsd;
    385     device_class_set_props(dc, mv88w8618_eth_properties);
    386     dc->realize = mv88w8618_eth_realize;
    387 }
    388 
    389 static const TypeInfo mv88w8618_eth_info = {
    390     .name          = TYPE_MV88W8618_ETH,
    391     .parent        = TYPE_SYS_BUS_DEVICE,
    392     .instance_size = sizeof(mv88w8618_eth_state),
    393     .instance_init = mv88w8618_eth_init,
    394     .class_init    = mv88w8618_eth_class_init,
    395 };
    396 
    397 static void musicpal_register_types(void)
    398 {
    399     type_register_static(&mv88w8618_eth_info);
    400 }
    401 
    402 type_init(musicpal_register_types)
    403