qemu/hw/net/mv88w8618_eth.c
<<
>>
Prefs
   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
  72typedef 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
  80typedef 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
  88OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
  89
  90struct 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
 112static 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
 123static 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
 134static 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
 168static 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
 179static 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
 190static 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
 216static 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
 258static 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
 310static const MemoryRegionOps mv88w8618_eth_ops = {
 311    .read = mv88w8618_eth_read,
 312    .write = mv88w8618_eth_write,
 313    .endianness = DEVICE_NATIVE_ENDIAN,
 314};
 315
 316static void eth_cleanup(NetClientState *nc)
 317{
 318    mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
 319
 320    s->nic = NULL;
 321}
 322
 323static NetClientInfo net_mv88w8618_info = {
 324    .type = NET_CLIENT_DRIVER_NIC,
 325    .size = sizeof(NICState),
 326    .receive = eth_receive,
 327    .cleanup = eth_cleanup,
 328};
 329
 330static 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
 342static 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
 356static 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
 373static 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
 380static 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
 389static 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
 397static void musicpal_register_types(void)
 398{
 399    type_register_static(&mv88w8618_eth_info);
 400}
 401
 402type_init(musicpal_register_types)
 403
 404