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