xref: /qemu/hw/net/mv88w8618_eth.c (revision 1de81b42)
16d81f488SPhilippe Mathieu-Daudé /* SPDX-License-Identifier: GPL-2.0-or-later */
26d81f488SPhilippe Mathieu-Daudé /*
36d81f488SPhilippe Mathieu-Daudé  * Marvell MV88W8618 / Freecom MusicPal emulation.
46d81f488SPhilippe Mathieu-Daudé  *
56d81f488SPhilippe Mathieu-Daudé  * Copyright (c) 2008 Jan Kiszka
66d81f488SPhilippe Mathieu-Daudé  */
76d81f488SPhilippe Mathieu-Daudé 
86d81f488SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
96d81f488SPhilippe Mathieu-Daudé #include "qapi/error.h"
106d81f488SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
116d81f488SPhilippe Mathieu-Daudé #include "hw/sysbus.h"
126d81f488SPhilippe Mathieu-Daudé #include "hw/irq.h"
136d81f488SPhilippe Mathieu-Daudé #include "hw/net/mv88w8618_eth.h"
146d81f488SPhilippe Mathieu-Daudé #include "migration/vmstate.h"
156d81f488SPhilippe Mathieu-Daudé #include "sysemu/dma.h"
166d81f488SPhilippe Mathieu-Daudé #include "net/net.h"
176d81f488SPhilippe Mathieu-Daudé 
186d81f488SPhilippe Mathieu-Daudé #define MP_ETH_SIZE             0x00001000
196d81f488SPhilippe Mathieu-Daudé 
206d81f488SPhilippe Mathieu-Daudé /* Ethernet register offsets */
216d81f488SPhilippe Mathieu-Daudé #define MP_ETH_SMIR             0x010
226d81f488SPhilippe Mathieu-Daudé #define MP_ETH_PCXR             0x408
236d81f488SPhilippe Mathieu-Daudé #define MP_ETH_SDCMR            0x448
246d81f488SPhilippe Mathieu-Daudé #define MP_ETH_ICR              0x450
256d81f488SPhilippe Mathieu-Daudé #define MP_ETH_IMR              0x458
266d81f488SPhilippe Mathieu-Daudé #define MP_ETH_FRDP0            0x480
276d81f488SPhilippe Mathieu-Daudé #define MP_ETH_FRDP1            0x484
286d81f488SPhilippe Mathieu-Daudé #define MP_ETH_FRDP2            0x488
296d81f488SPhilippe Mathieu-Daudé #define MP_ETH_FRDP3            0x48C
306d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CRDP0            0x4A0
316d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CRDP1            0x4A4
326d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CRDP2            0x4A8
336d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CRDP3            0x4AC
346d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CTDP0            0x4E0
356d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CTDP1            0x4E4
366d81f488SPhilippe Mathieu-Daudé 
376d81f488SPhilippe Mathieu-Daudé /* MII PHY access */
386d81f488SPhilippe Mathieu-Daudé #define MP_ETH_SMIR_DATA        0x0000FFFF
396d81f488SPhilippe Mathieu-Daudé #define MP_ETH_SMIR_ADDR        0x03FF0000
406d81f488SPhilippe Mathieu-Daudé #define MP_ETH_SMIR_OPCODE      (1 << 26) /* Read value */
416d81f488SPhilippe Mathieu-Daudé #define MP_ETH_SMIR_RDVALID     (1 << 27)
426d81f488SPhilippe Mathieu-Daudé 
436d81f488SPhilippe Mathieu-Daudé /* PHY registers */
446d81f488SPhilippe Mathieu-Daudé #define MP_ETH_PHY1_BMSR        0x00210000
456d81f488SPhilippe Mathieu-Daudé #define MP_ETH_PHY1_PHYSID1     0x00410000
466d81f488SPhilippe Mathieu-Daudé #define MP_ETH_PHY1_PHYSID2     0x00610000
476d81f488SPhilippe Mathieu-Daudé 
486d81f488SPhilippe Mathieu-Daudé #define MP_PHY_BMSR_LINK        0x0004
496d81f488SPhilippe Mathieu-Daudé #define MP_PHY_BMSR_AUTONEG     0x0008
506d81f488SPhilippe Mathieu-Daudé 
516d81f488SPhilippe Mathieu-Daudé #define MP_PHY_88E3015          0x01410E20
526d81f488SPhilippe Mathieu-Daudé 
536d81f488SPhilippe Mathieu-Daudé /* TX descriptor status */
546d81f488SPhilippe Mathieu-Daudé #define MP_ETH_TX_OWN           (1U << 31)
556d81f488SPhilippe Mathieu-Daudé 
566d81f488SPhilippe Mathieu-Daudé /* RX descriptor status */
576d81f488SPhilippe Mathieu-Daudé #define MP_ETH_RX_OWN           (1U << 31)
586d81f488SPhilippe Mathieu-Daudé 
596d81f488SPhilippe Mathieu-Daudé /* Interrupt cause/mask bits */
606d81f488SPhilippe Mathieu-Daudé #define MP_ETH_IRQ_RX_BIT       0
616d81f488SPhilippe Mathieu-Daudé #define MP_ETH_IRQ_RX           (1 << MP_ETH_IRQ_RX_BIT)
626d81f488SPhilippe Mathieu-Daudé #define MP_ETH_IRQ_TXHI_BIT     2
636d81f488SPhilippe Mathieu-Daudé #define MP_ETH_IRQ_TXLO_BIT     3
646d81f488SPhilippe Mathieu-Daudé 
656d81f488SPhilippe Mathieu-Daudé /* Port config bits */
666d81f488SPhilippe Mathieu-Daudé #define MP_ETH_PCXR_2BSM_BIT    28 /* 2-byte incoming suffix */
676d81f488SPhilippe Mathieu-Daudé 
686d81f488SPhilippe Mathieu-Daudé /* SDMA command bits */
696d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CMD_TXHI         (1 << 23)
706d81f488SPhilippe Mathieu-Daudé #define MP_ETH_CMD_TXLO         (1 << 22)
716d81f488SPhilippe Mathieu-Daudé 
726d81f488SPhilippe Mathieu-Daudé typedef struct mv88w8618_tx_desc {
736d81f488SPhilippe Mathieu-Daudé     uint32_t cmdstat;
746d81f488SPhilippe Mathieu-Daudé     uint16_t res;
756d81f488SPhilippe Mathieu-Daudé     uint16_t bytes;
766d81f488SPhilippe Mathieu-Daudé     uint32_t buffer;
776d81f488SPhilippe Mathieu-Daudé     uint32_t next;
786d81f488SPhilippe Mathieu-Daudé } mv88w8618_tx_desc;
796d81f488SPhilippe Mathieu-Daudé 
806d81f488SPhilippe Mathieu-Daudé typedef struct mv88w8618_rx_desc {
816d81f488SPhilippe Mathieu-Daudé     uint32_t cmdstat;
826d81f488SPhilippe Mathieu-Daudé     uint16_t bytes;
836d81f488SPhilippe Mathieu-Daudé     uint16_t buffer_size;
846d81f488SPhilippe Mathieu-Daudé     uint32_t buffer;
856d81f488SPhilippe Mathieu-Daudé     uint32_t next;
866d81f488SPhilippe Mathieu-Daudé } mv88w8618_rx_desc;
876d81f488SPhilippe Mathieu-Daudé 
886d81f488SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
896d81f488SPhilippe Mathieu-Daudé 
906d81f488SPhilippe Mathieu-Daudé struct mv88w8618_eth_state {
916d81f488SPhilippe Mathieu-Daudé     /*< private >*/
926d81f488SPhilippe Mathieu-Daudé     SysBusDevice parent_obj;
936d81f488SPhilippe Mathieu-Daudé     /*< public >*/
946d81f488SPhilippe Mathieu-Daudé 
956d81f488SPhilippe Mathieu-Daudé     MemoryRegion iomem;
966d81f488SPhilippe Mathieu-Daudé     qemu_irq irq;
976d81f488SPhilippe Mathieu-Daudé     MemoryRegion *dma_mr;
986d81f488SPhilippe Mathieu-Daudé     AddressSpace dma_as;
996d81f488SPhilippe Mathieu-Daudé     uint32_t smir;
1006d81f488SPhilippe Mathieu-Daudé     uint32_t icr;
1016d81f488SPhilippe Mathieu-Daudé     uint32_t imr;
1026d81f488SPhilippe Mathieu-Daudé     int mmio_index;
1036d81f488SPhilippe Mathieu-Daudé     uint32_t vlan_header;
1046d81f488SPhilippe Mathieu-Daudé     uint32_t tx_queue[2];
1056d81f488SPhilippe Mathieu-Daudé     uint32_t rx_queue[4];
1066d81f488SPhilippe Mathieu-Daudé     uint32_t frx_queue[4];
1076d81f488SPhilippe Mathieu-Daudé     uint32_t cur_rx[4];
1086d81f488SPhilippe Mathieu-Daudé     NICState *nic;
1096d81f488SPhilippe Mathieu-Daudé     NICConf conf;
1106d81f488SPhilippe Mathieu-Daudé };
1116d81f488SPhilippe Mathieu-Daudé 
eth_rx_desc_put(AddressSpace * dma_as,uint32_t addr,mv88w8618_rx_desc * desc)1126d81f488SPhilippe Mathieu-Daudé static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
1136d81f488SPhilippe Mathieu-Daudé                             mv88w8618_rx_desc *desc)
1146d81f488SPhilippe Mathieu-Daudé {
1156d81f488SPhilippe Mathieu-Daudé     cpu_to_le32s(&desc->cmdstat);
1166d81f488SPhilippe Mathieu-Daudé     cpu_to_le16s(&desc->bytes);
1176d81f488SPhilippe Mathieu-Daudé     cpu_to_le16s(&desc->buffer_size);
1186d81f488SPhilippe Mathieu-Daudé     cpu_to_le32s(&desc->buffer);
1196d81f488SPhilippe Mathieu-Daudé     cpu_to_le32s(&desc->next);
1206d81f488SPhilippe Mathieu-Daudé     dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
1216d81f488SPhilippe Mathieu-Daudé }
1226d81f488SPhilippe Mathieu-Daudé 
eth_rx_desc_get(AddressSpace * dma_as,uint32_t addr,mv88w8618_rx_desc * desc)1236d81f488SPhilippe Mathieu-Daudé static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
1246d81f488SPhilippe Mathieu-Daudé                             mv88w8618_rx_desc *desc)
1256d81f488SPhilippe Mathieu-Daudé {
1266d81f488SPhilippe Mathieu-Daudé     dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
1276d81f488SPhilippe Mathieu-Daudé     le32_to_cpus(&desc->cmdstat);
1286d81f488SPhilippe Mathieu-Daudé     le16_to_cpus(&desc->bytes);
1296d81f488SPhilippe Mathieu-Daudé     le16_to_cpus(&desc->buffer_size);
1306d81f488SPhilippe Mathieu-Daudé     le32_to_cpus(&desc->buffer);
1316d81f488SPhilippe Mathieu-Daudé     le32_to_cpus(&desc->next);
1326d81f488SPhilippe Mathieu-Daudé }
1336d81f488SPhilippe Mathieu-Daudé 
eth_receive(NetClientState * nc,const uint8_t * buf,size_t size)1346d81f488SPhilippe Mathieu-Daudé static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
1356d81f488SPhilippe Mathieu-Daudé {
1366d81f488SPhilippe Mathieu-Daudé     mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
1376d81f488SPhilippe Mathieu-Daudé     uint32_t desc_addr;
1386d81f488SPhilippe Mathieu-Daudé     mv88w8618_rx_desc desc;
1396d81f488SPhilippe Mathieu-Daudé     int i;
1406d81f488SPhilippe Mathieu-Daudé 
1416d81f488SPhilippe Mathieu-Daudé     for (i = 0; i < 4; i++) {
1426d81f488SPhilippe Mathieu-Daudé         desc_addr = s->cur_rx[i];
1436d81f488SPhilippe Mathieu-Daudé         if (!desc_addr) {
1446d81f488SPhilippe Mathieu-Daudé             continue;
1456d81f488SPhilippe Mathieu-Daudé         }
1466d81f488SPhilippe Mathieu-Daudé         do {
1476d81f488SPhilippe Mathieu-Daudé             eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
1486d81f488SPhilippe Mathieu-Daudé             if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
1496d81f488SPhilippe Mathieu-Daudé                 dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
1506d81f488SPhilippe Mathieu-Daudé                                  buf, size, MEMTXATTRS_UNSPECIFIED);
1516d81f488SPhilippe Mathieu-Daudé                 desc.bytes = size + s->vlan_header;
1526d81f488SPhilippe Mathieu-Daudé                 desc.cmdstat &= ~MP_ETH_RX_OWN;
1536d81f488SPhilippe Mathieu-Daudé                 s->cur_rx[i] = desc.next;
1546d81f488SPhilippe Mathieu-Daudé 
1556d81f488SPhilippe Mathieu-Daudé                 s->icr |= MP_ETH_IRQ_RX;
1566d81f488SPhilippe Mathieu-Daudé                 if (s->icr & s->imr) {
1576d81f488SPhilippe Mathieu-Daudé                     qemu_irq_raise(s->irq);
1586d81f488SPhilippe Mathieu-Daudé                 }
1596d81f488SPhilippe Mathieu-Daudé                 eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
1606d81f488SPhilippe Mathieu-Daudé                 return size;
1616d81f488SPhilippe Mathieu-Daudé             }
1626d81f488SPhilippe Mathieu-Daudé             desc_addr = desc.next;
1636d81f488SPhilippe Mathieu-Daudé         } while (desc_addr != s->rx_queue[i]);
1646d81f488SPhilippe Mathieu-Daudé     }
1656d81f488SPhilippe Mathieu-Daudé     return size;
1666d81f488SPhilippe Mathieu-Daudé }
1676d81f488SPhilippe Mathieu-Daudé 
eth_tx_desc_put(AddressSpace * dma_as,uint32_t addr,mv88w8618_tx_desc * desc)1686d81f488SPhilippe Mathieu-Daudé static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
1696d81f488SPhilippe Mathieu-Daudé                             mv88w8618_tx_desc *desc)
1706d81f488SPhilippe Mathieu-Daudé {
1716d81f488SPhilippe Mathieu-Daudé     cpu_to_le32s(&desc->cmdstat);
1726d81f488SPhilippe Mathieu-Daudé     cpu_to_le16s(&desc->res);
1736d81f488SPhilippe Mathieu-Daudé     cpu_to_le16s(&desc->bytes);
1746d81f488SPhilippe Mathieu-Daudé     cpu_to_le32s(&desc->buffer);
1756d81f488SPhilippe Mathieu-Daudé     cpu_to_le32s(&desc->next);
1766d81f488SPhilippe Mathieu-Daudé     dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
1776d81f488SPhilippe Mathieu-Daudé }
1786d81f488SPhilippe Mathieu-Daudé 
eth_tx_desc_get(AddressSpace * dma_as,uint32_t addr,mv88w8618_tx_desc * desc)1796d81f488SPhilippe Mathieu-Daudé static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
1806d81f488SPhilippe Mathieu-Daudé                             mv88w8618_tx_desc *desc)
1816d81f488SPhilippe Mathieu-Daudé {
1826d81f488SPhilippe Mathieu-Daudé     dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
1836d81f488SPhilippe Mathieu-Daudé     le32_to_cpus(&desc->cmdstat);
1846d81f488SPhilippe Mathieu-Daudé     le16_to_cpus(&desc->res);
1856d81f488SPhilippe Mathieu-Daudé     le16_to_cpus(&desc->bytes);
1866d81f488SPhilippe Mathieu-Daudé     le32_to_cpus(&desc->buffer);
1876d81f488SPhilippe Mathieu-Daudé     le32_to_cpus(&desc->next);
1886d81f488SPhilippe Mathieu-Daudé }
1896d81f488SPhilippe Mathieu-Daudé 
eth_send(mv88w8618_eth_state * s,int queue_index)1906d81f488SPhilippe Mathieu-Daudé static void eth_send(mv88w8618_eth_state *s, int queue_index)
1916d81f488SPhilippe Mathieu-Daudé {
1926d81f488SPhilippe Mathieu-Daudé     uint32_t desc_addr = s->tx_queue[queue_index];
1936d81f488SPhilippe Mathieu-Daudé     mv88w8618_tx_desc desc;
1946d81f488SPhilippe Mathieu-Daudé     uint32_t next_desc;
1956d81f488SPhilippe Mathieu-Daudé     uint8_t buf[2048];
1966d81f488SPhilippe Mathieu-Daudé     int len;
1976d81f488SPhilippe Mathieu-Daudé 
1986d81f488SPhilippe Mathieu-Daudé     do {
1996d81f488SPhilippe Mathieu-Daudé         eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
2006d81f488SPhilippe Mathieu-Daudé         next_desc = desc.next;
2016d81f488SPhilippe Mathieu-Daudé         if (desc.cmdstat & MP_ETH_TX_OWN) {
2026d81f488SPhilippe Mathieu-Daudé             len = desc.bytes;
2036d81f488SPhilippe Mathieu-Daudé             if (len < 2048) {
2046d81f488SPhilippe Mathieu-Daudé                 dma_memory_read(&s->dma_as, desc.buffer, buf, len,
2056d81f488SPhilippe Mathieu-Daudé                                 MEMTXATTRS_UNSPECIFIED);
2066d81f488SPhilippe Mathieu-Daudé                 qemu_send_packet(qemu_get_queue(s->nic), buf, len);
2076d81f488SPhilippe Mathieu-Daudé             }
2086d81f488SPhilippe Mathieu-Daudé             desc.cmdstat &= ~MP_ETH_TX_OWN;
2096d81f488SPhilippe Mathieu-Daudé             s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
2106d81f488SPhilippe Mathieu-Daudé             eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
2116d81f488SPhilippe Mathieu-Daudé         }
2126d81f488SPhilippe Mathieu-Daudé         desc_addr = next_desc;
2136d81f488SPhilippe Mathieu-Daudé     } while (desc_addr != s->tx_queue[queue_index]);
2146d81f488SPhilippe Mathieu-Daudé }
2156d81f488SPhilippe Mathieu-Daudé 
mv88w8618_eth_read(void * opaque,hwaddr offset,unsigned size)2166d81f488SPhilippe Mathieu-Daudé static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
2176d81f488SPhilippe Mathieu-Daudé                                    unsigned size)
2186d81f488SPhilippe Mathieu-Daudé {
2196d81f488SPhilippe Mathieu-Daudé     mv88w8618_eth_state *s = opaque;
2206d81f488SPhilippe Mathieu-Daudé 
2216d81f488SPhilippe Mathieu-Daudé     switch (offset) {
2226d81f488SPhilippe Mathieu-Daudé     case MP_ETH_SMIR:
2236d81f488SPhilippe Mathieu-Daudé         if (s->smir & MP_ETH_SMIR_OPCODE) {
2246d81f488SPhilippe Mathieu-Daudé             switch (s->smir & MP_ETH_SMIR_ADDR) {
2256d81f488SPhilippe Mathieu-Daudé             case MP_ETH_PHY1_BMSR:
2266d81f488SPhilippe Mathieu-Daudé                 return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
2276d81f488SPhilippe Mathieu-Daudé                        MP_ETH_SMIR_RDVALID;
2286d81f488SPhilippe Mathieu-Daudé             case MP_ETH_PHY1_PHYSID1:
2296d81f488SPhilippe Mathieu-Daudé                 return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
2306d81f488SPhilippe Mathieu-Daudé             case MP_ETH_PHY1_PHYSID2:
2316d81f488SPhilippe Mathieu-Daudé                 return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
2326d81f488SPhilippe Mathieu-Daudé             default:
2336d81f488SPhilippe Mathieu-Daudé                 return MP_ETH_SMIR_RDVALID;
2346d81f488SPhilippe Mathieu-Daudé             }
2356d81f488SPhilippe Mathieu-Daudé         }
2366d81f488SPhilippe Mathieu-Daudé         return 0;
2376d81f488SPhilippe Mathieu-Daudé 
2386d81f488SPhilippe Mathieu-Daudé     case MP_ETH_ICR:
2396d81f488SPhilippe Mathieu-Daudé         return s->icr;
2406d81f488SPhilippe Mathieu-Daudé 
2416d81f488SPhilippe Mathieu-Daudé     case MP_ETH_IMR:
2426d81f488SPhilippe Mathieu-Daudé         return s->imr;
2436d81f488SPhilippe Mathieu-Daudé 
2446d81f488SPhilippe Mathieu-Daudé     case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
2456d81f488SPhilippe Mathieu-Daudé         return s->frx_queue[(offset - MP_ETH_FRDP0) / 4];
2466d81f488SPhilippe Mathieu-Daudé 
2476d81f488SPhilippe Mathieu-Daudé     case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
2486d81f488SPhilippe Mathieu-Daudé         return s->rx_queue[(offset - MP_ETH_CRDP0) / 4];
2496d81f488SPhilippe Mathieu-Daudé 
2506d81f488SPhilippe Mathieu-Daudé     case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
2516d81f488SPhilippe Mathieu-Daudé         return s->tx_queue[(offset - MP_ETH_CTDP0) / 4];
2526d81f488SPhilippe Mathieu-Daudé 
2536d81f488SPhilippe Mathieu-Daudé     default:
2546d81f488SPhilippe Mathieu-Daudé         return 0;
2556d81f488SPhilippe Mathieu-Daudé     }
2566d81f488SPhilippe Mathieu-Daudé }
2576d81f488SPhilippe Mathieu-Daudé 
mv88w8618_eth_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)2586d81f488SPhilippe Mathieu-Daudé static void mv88w8618_eth_write(void *opaque, hwaddr offset,
2596d81f488SPhilippe Mathieu-Daudé                                 uint64_t value, unsigned size)
2606d81f488SPhilippe Mathieu-Daudé {
2616d81f488SPhilippe Mathieu-Daudé     mv88w8618_eth_state *s = opaque;
2626d81f488SPhilippe Mathieu-Daudé 
2636d81f488SPhilippe Mathieu-Daudé     switch (offset) {
2646d81f488SPhilippe Mathieu-Daudé     case MP_ETH_SMIR:
2656d81f488SPhilippe Mathieu-Daudé         s->smir = value;
2666d81f488SPhilippe Mathieu-Daudé         break;
2676d81f488SPhilippe Mathieu-Daudé 
2686d81f488SPhilippe Mathieu-Daudé     case MP_ETH_PCXR:
2696d81f488SPhilippe Mathieu-Daudé         s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
2706d81f488SPhilippe Mathieu-Daudé         break;
2716d81f488SPhilippe Mathieu-Daudé 
2726d81f488SPhilippe Mathieu-Daudé     case MP_ETH_SDCMR:
2736d81f488SPhilippe Mathieu-Daudé         if (value & MP_ETH_CMD_TXHI) {
2746d81f488SPhilippe Mathieu-Daudé             eth_send(s, 1);
2756d81f488SPhilippe Mathieu-Daudé         }
2766d81f488SPhilippe Mathieu-Daudé         if (value & MP_ETH_CMD_TXLO) {
2776d81f488SPhilippe Mathieu-Daudé             eth_send(s, 0);
2786d81f488SPhilippe Mathieu-Daudé         }
2796d81f488SPhilippe Mathieu-Daudé         if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
2806d81f488SPhilippe Mathieu-Daudé             qemu_irq_raise(s->irq);
2816d81f488SPhilippe Mathieu-Daudé         }
2826d81f488SPhilippe Mathieu-Daudé         break;
2836d81f488SPhilippe Mathieu-Daudé 
2846d81f488SPhilippe Mathieu-Daudé     case MP_ETH_ICR:
2856d81f488SPhilippe Mathieu-Daudé         s->icr &= value;
2866d81f488SPhilippe Mathieu-Daudé         break;
2876d81f488SPhilippe Mathieu-Daudé 
2886d81f488SPhilippe Mathieu-Daudé     case MP_ETH_IMR:
2896d81f488SPhilippe Mathieu-Daudé         s->imr = value;
2906d81f488SPhilippe Mathieu-Daudé         if (s->icr & s->imr) {
2916d81f488SPhilippe Mathieu-Daudé             qemu_irq_raise(s->irq);
2926d81f488SPhilippe Mathieu-Daudé         }
2936d81f488SPhilippe Mathieu-Daudé         break;
2946d81f488SPhilippe Mathieu-Daudé 
2956d81f488SPhilippe Mathieu-Daudé     case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
2966d81f488SPhilippe Mathieu-Daudé         s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
2976d81f488SPhilippe Mathieu-Daudé         break;
2986d81f488SPhilippe Mathieu-Daudé 
2996d81f488SPhilippe Mathieu-Daudé     case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
3006d81f488SPhilippe Mathieu-Daudé         s->rx_queue[(offset - MP_ETH_CRDP0) / 4] =
3016d81f488SPhilippe Mathieu-Daudé             s->cur_rx[(offset - MP_ETH_CRDP0) / 4] = value;
3026d81f488SPhilippe Mathieu-Daudé         break;
3036d81f488SPhilippe Mathieu-Daudé 
3046d81f488SPhilippe Mathieu-Daudé     case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
3056d81f488SPhilippe Mathieu-Daudé         s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
3066d81f488SPhilippe Mathieu-Daudé         break;
3076d81f488SPhilippe Mathieu-Daudé     }
3086d81f488SPhilippe Mathieu-Daudé }
3096d81f488SPhilippe Mathieu-Daudé 
3106d81f488SPhilippe Mathieu-Daudé static const MemoryRegionOps mv88w8618_eth_ops = {
3116d81f488SPhilippe Mathieu-Daudé     .read = mv88w8618_eth_read,
3126d81f488SPhilippe Mathieu-Daudé     .write = mv88w8618_eth_write,
3136d81f488SPhilippe Mathieu-Daudé     .endianness = DEVICE_NATIVE_ENDIAN,
3146d81f488SPhilippe Mathieu-Daudé };
3156d81f488SPhilippe Mathieu-Daudé 
eth_cleanup(NetClientState * nc)3166d81f488SPhilippe Mathieu-Daudé static void eth_cleanup(NetClientState *nc)
3176d81f488SPhilippe Mathieu-Daudé {
3186d81f488SPhilippe Mathieu-Daudé     mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
3196d81f488SPhilippe Mathieu-Daudé 
3206d81f488SPhilippe Mathieu-Daudé     s->nic = NULL;
3216d81f488SPhilippe Mathieu-Daudé }
3226d81f488SPhilippe Mathieu-Daudé 
3236d81f488SPhilippe Mathieu-Daudé static NetClientInfo net_mv88w8618_info = {
3246d81f488SPhilippe Mathieu-Daudé     .type = NET_CLIENT_DRIVER_NIC,
3256d81f488SPhilippe Mathieu-Daudé     .size = sizeof(NICState),
3266d81f488SPhilippe Mathieu-Daudé     .receive = eth_receive,
3276d81f488SPhilippe Mathieu-Daudé     .cleanup = eth_cleanup,
3286d81f488SPhilippe Mathieu-Daudé };
3296d81f488SPhilippe Mathieu-Daudé 
mv88w8618_eth_init(Object * obj)3306d81f488SPhilippe Mathieu-Daudé static void mv88w8618_eth_init(Object *obj)
3316d81f488SPhilippe Mathieu-Daudé {
3326d81f488SPhilippe Mathieu-Daudé     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
3336d81f488SPhilippe Mathieu-Daudé     DeviceState *dev = DEVICE(sbd);
3346d81f488SPhilippe Mathieu-Daudé     mv88w8618_eth_state *s = MV88W8618_ETH(dev);
3356d81f488SPhilippe Mathieu-Daudé 
3366d81f488SPhilippe Mathieu-Daudé     sysbus_init_irq(sbd, &s->irq);
3376d81f488SPhilippe Mathieu-Daudé     memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
3386d81f488SPhilippe Mathieu-Daudé                           "mv88w8618-eth", MP_ETH_SIZE);
3396d81f488SPhilippe Mathieu-Daudé     sysbus_init_mmio(sbd, &s->iomem);
3406d81f488SPhilippe Mathieu-Daudé }
3416d81f488SPhilippe Mathieu-Daudé 
mv88w8618_eth_realize(DeviceState * dev,Error ** errp)3426d81f488SPhilippe Mathieu-Daudé static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
3436d81f488SPhilippe Mathieu-Daudé {
3446d81f488SPhilippe Mathieu-Daudé     mv88w8618_eth_state *s = MV88W8618_ETH(dev);
3456d81f488SPhilippe Mathieu-Daudé 
3466d81f488SPhilippe Mathieu-Daudé     if (!s->dma_mr) {
3476d81f488SPhilippe Mathieu-Daudé         error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
3486d81f488SPhilippe Mathieu-Daudé         return;
3496d81f488SPhilippe Mathieu-Daudé     }
3506d81f488SPhilippe Mathieu-Daudé 
3516d81f488SPhilippe Mathieu-Daudé     address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
3526d81f488SPhilippe Mathieu-Daudé     s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
3537d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), dev->id,
3547d0fefdfSAkihiko Odaki                           &dev->mem_reentrancy_guard, s);
3556d81f488SPhilippe Mathieu-Daudé }
3566d81f488SPhilippe Mathieu-Daudé 
3576d81f488SPhilippe Mathieu-Daudé static const VMStateDescription mv88w8618_eth_vmsd = {
3586d81f488SPhilippe Mathieu-Daudé     .name = "mv88w8618_eth",
3596d81f488SPhilippe Mathieu-Daudé     .version_id = 1,
3606d81f488SPhilippe Mathieu-Daudé     .minimum_version_id = 1,
361*1de81b42SRichard Henderson     .fields = (const VMStateField[]) {
3626d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32(smir, mv88w8618_eth_state),
3636d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32(icr, mv88w8618_eth_state),
3646d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32(imr, mv88w8618_eth_state),
3656d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
3666d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
3676d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
3686d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
3696d81f488SPhilippe Mathieu-Daudé         VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
3706d81f488SPhilippe Mathieu-Daudé         VMSTATE_END_OF_LIST()
3716d81f488SPhilippe Mathieu-Daudé     }
3726d81f488SPhilippe Mathieu-Daudé };
3736d81f488SPhilippe Mathieu-Daudé 
3746d81f488SPhilippe Mathieu-Daudé static Property mv88w8618_eth_properties[] = {
3756d81f488SPhilippe Mathieu-Daudé     DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
3766d81f488SPhilippe Mathieu-Daudé     DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
3776d81f488SPhilippe Mathieu-Daudé                      TYPE_MEMORY_REGION, MemoryRegion *),
3786d81f488SPhilippe Mathieu-Daudé     DEFINE_PROP_END_OF_LIST(),
3796d81f488SPhilippe Mathieu-Daudé };
3806d81f488SPhilippe Mathieu-Daudé 
mv88w8618_eth_class_init(ObjectClass * klass,void * data)3816d81f488SPhilippe Mathieu-Daudé static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
3826d81f488SPhilippe Mathieu-Daudé {
3836d81f488SPhilippe Mathieu-Daudé     DeviceClass *dc = DEVICE_CLASS(klass);
3846d81f488SPhilippe Mathieu-Daudé 
3856d81f488SPhilippe Mathieu-Daudé     dc->vmsd = &mv88w8618_eth_vmsd;
3866d81f488SPhilippe Mathieu-Daudé     device_class_set_props(dc, mv88w8618_eth_properties);
3876d81f488SPhilippe Mathieu-Daudé     dc->realize = mv88w8618_eth_realize;
3886d81f488SPhilippe Mathieu-Daudé }
3896d81f488SPhilippe Mathieu-Daudé 
3906d81f488SPhilippe Mathieu-Daudé static const TypeInfo mv88w8618_eth_info = {
3916d81f488SPhilippe Mathieu-Daudé     .name          = TYPE_MV88W8618_ETH,
3926d81f488SPhilippe Mathieu-Daudé     .parent        = TYPE_SYS_BUS_DEVICE,
3936d81f488SPhilippe Mathieu-Daudé     .instance_size = sizeof(mv88w8618_eth_state),
3946d81f488SPhilippe Mathieu-Daudé     .instance_init = mv88w8618_eth_init,
3956d81f488SPhilippe Mathieu-Daudé     .class_init    = mv88w8618_eth_class_init,
3966d81f488SPhilippe Mathieu-Daudé };
3976d81f488SPhilippe Mathieu-Daudé 
musicpal_register_types(void)3986d81f488SPhilippe Mathieu-Daudé static void musicpal_register_types(void)
3996d81f488SPhilippe Mathieu-Daudé {
4006d81f488SPhilippe Mathieu-Daudé     type_register_static(&mv88w8618_eth_info);
4016d81f488SPhilippe Mathieu-Daudé }
4026d81f488SPhilippe Mathieu-Daudé 
4036d81f488SPhilippe Mathieu-Daudé type_init(musicpal_register_types)
4046d81f488SPhilippe Mathieu-Daudé 
405