• R/O
  • HTTP
  • SSH
  • HTTPS

提交

標籤
無標籤

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

修訂6d81f4887f66995ca94fce5f896b26339a537bd9 (tree)
時間2022-01-20 20:47:52
作者Philippe Mathieu-Daudé <f4bug@amsa...>
CommiterPeter Maydell

Log Message

hw/net: Move MV88W8618 network device out of hw/arm/ directory

The Marvell 88W8618 network device is hidden in the Musicpal
machine. Move it into a new unit file under the hw/net/ directory.

Acked-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-id: 20220107184429.423572-4-f4bug@amsat.org
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Change Summary

差異

--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -774,6 +774,8 @@ M: Peter Maydell <peter.maydell@linaro.org>
774774 L: qemu-arm@nongnu.org
775775 S: Odd Fixes
776776 F: hw/arm/musicpal.c
777+F: hw/net/mv88w8618_eth.c
778+F: include/hw/net/mv88w8618_eth.h
777779 F: docs/system/arm/musicpal.rst
778780
779781 Nuvoton NPCM7xx
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -34,12 +34,12 @@
3434 #include "ui/pixel_ops.h"
3535 #include "qemu/cutils.h"
3636 #include "qom/object.h"
37+#include "hw/net/mv88w8618_eth.h"
3738
3839 #define MP_MISC_BASE 0x80002000
3940 #define MP_MISC_SIZE 0x00001000
4041
4142 #define MP_ETH_BASE 0x80008000
42-#define MP_ETH_SIZE 0x00001000
4343
4444 #define MP_WLAN_BASE 0x8000C000
4545 #define MP_WLAN_SIZE 0x00000800
@@ -84,384 +84,6 @@
8484 /* Wolfson 8750 I2C address */
8585 #define MP_WM_ADDR 0x1A
8686
87-/* Ethernet register offsets */
88-#define MP_ETH_SMIR 0x010
89-#define MP_ETH_PCXR 0x408
90-#define MP_ETH_SDCMR 0x448
91-#define MP_ETH_ICR 0x450
92-#define MP_ETH_IMR 0x458
93-#define MP_ETH_FRDP0 0x480
94-#define MP_ETH_FRDP1 0x484
95-#define MP_ETH_FRDP2 0x488
96-#define MP_ETH_FRDP3 0x48C
97-#define MP_ETH_CRDP0 0x4A0
98-#define MP_ETH_CRDP1 0x4A4
99-#define MP_ETH_CRDP2 0x4A8
100-#define MP_ETH_CRDP3 0x4AC
101-#define MP_ETH_CTDP0 0x4E0
102-#define MP_ETH_CTDP1 0x4E4
103-
104-/* MII PHY access */
105-#define MP_ETH_SMIR_DATA 0x0000FFFF
106-#define MP_ETH_SMIR_ADDR 0x03FF0000
107-#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
108-#define MP_ETH_SMIR_RDVALID (1 << 27)
109-
110-/* PHY registers */
111-#define MP_ETH_PHY1_BMSR 0x00210000
112-#define MP_ETH_PHY1_PHYSID1 0x00410000
113-#define MP_ETH_PHY1_PHYSID2 0x00610000
114-
115-#define MP_PHY_BMSR_LINK 0x0004
116-#define MP_PHY_BMSR_AUTONEG 0x0008
117-
118-#define MP_PHY_88E3015 0x01410E20
119-
120-/* TX descriptor status */
121-#define MP_ETH_TX_OWN (1U << 31)
122-
123-/* RX descriptor status */
124-#define MP_ETH_RX_OWN (1U << 31)
125-
126-/* Interrupt cause/mask bits */
127-#define MP_ETH_IRQ_RX_BIT 0
128-#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
129-#define MP_ETH_IRQ_TXHI_BIT 2
130-#define MP_ETH_IRQ_TXLO_BIT 3
131-
132-/* Port config bits */
133-#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
134-
135-/* SDMA command bits */
136-#define MP_ETH_CMD_TXHI (1 << 23)
137-#define MP_ETH_CMD_TXLO (1 << 22)
138-
139-typedef struct mv88w8618_tx_desc {
140- uint32_t cmdstat;
141- uint16_t res;
142- uint16_t bytes;
143- uint32_t buffer;
144- uint32_t next;
145-} mv88w8618_tx_desc;
146-
147-typedef struct mv88w8618_rx_desc {
148- uint32_t cmdstat;
149- uint16_t bytes;
150- uint16_t buffer_size;
151- uint32_t buffer;
152- uint32_t next;
153-} mv88w8618_rx_desc;
154-
155-#define TYPE_MV88W8618_ETH "mv88w8618_eth"
156-OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
157-
158-struct mv88w8618_eth_state {
159- /*< private >*/
160- SysBusDevice parent_obj;
161- /*< public >*/
162-
163- MemoryRegion iomem;
164- qemu_irq irq;
165- MemoryRegion *dma_mr;
166- AddressSpace dma_as;
167- uint32_t smir;
168- uint32_t icr;
169- uint32_t imr;
170- int mmio_index;
171- uint32_t vlan_header;
172- uint32_t tx_queue[2];
173- uint32_t rx_queue[4];
174- uint32_t frx_queue[4];
175- uint32_t cur_rx[4];
176- NICState *nic;
177- NICConf conf;
178-};
179-
180-static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
181- mv88w8618_rx_desc *desc)
182-{
183- cpu_to_le32s(&desc->cmdstat);
184- cpu_to_le16s(&desc->bytes);
185- cpu_to_le16s(&desc->buffer_size);
186- cpu_to_le32s(&desc->buffer);
187- cpu_to_le32s(&desc->next);
188- dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
189-}
190-
191-static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
192- mv88w8618_rx_desc *desc)
193-{
194- dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
195- le32_to_cpus(&desc->cmdstat);
196- le16_to_cpus(&desc->bytes);
197- le16_to_cpus(&desc->buffer_size);
198- le32_to_cpus(&desc->buffer);
199- le32_to_cpus(&desc->next);
200-}
201-
202-static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
203-{
204- mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
205- uint32_t desc_addr;
206- mv88w8618_rx_desc desc;
207- int i;
208-
209- for (i = 0; i < 4; i++) {
210- desc_addr = s->cur_rx[i];
211- if (!desc_addr) {
212- continue;
213- }
214- do {
215- eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
216- if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
217- dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
218- buf, size, MEMTXATTRS_UNSPECIFIED);
219- desc.bytes = size + s->vlan_header;
220- desc.cmdstat &= ~MP_ETH_RX_OWN;
221- s->cur_rx[i] = desc.next;
222-
223- s->icr |= MP_ETH_IRQ_RX;
224- if (s->icr & s->imr) {
225- qemu_irq_raise(s->irq);
226- }
227- eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
228- return size;
229- }
230- desc_addr = desc.next;
231- } while (desc_addr != s->rx_queue[i]);
232- }
233- return size;
234-}
235-
236-static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
237- mv88w8618_tx_desc *desc)
238-{
239- cpu_to_le32s(&desc->cmdstat);
240- cpu_to_le16s(&desc->res);
241- cpu_to_le16s(&desc->bytes);
242- cpu_to_le32s(&desc->buffer);
243- cpu_to_le32s(&desc->next);
244- dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
245-}
246-
247-static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
248- mv88w8618_tx_desc *desc)
249-{
250- dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
251- le32_to_cpus(&desc->cmdstat);
252- le16_to_cpus(&desc->res);
253- le16_to_cpus(&desc->bytes);
254- le32_to_cpus(&desc->buffer);
255- le32_to_cpus(&desc->next);
256-}
257-
258-static void eth_send(mv88w8618_eth_state *s, int queue_index)
259-{
260- uint32_t desc_addr = s->tx_queue[queue_index];
261- mv88w8618_tx_desc desc;
262- uint32_t next_desc;
263- uint8_t buf[2048];
264- int len;
265-
266- do {
267- eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
268- next_desc = desc.next;
269- if (desc.cmdstat & MP_ETH_TX_OWN) {
270- len = desc.bytes;
271- if (len < 2048) {
272- dma_memory_read(&s->dma_as, desc.buffer, buf, len,
273- MEMTXATTRS_UNSPECIFIED);
274- qemu_send_packet(qemu_get_queue(s->nic), buf, len);
275- }
276- desc.cmdstat &= ~MP_ETH_TX_OWN;
277- s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
278- eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
279- }
280- desc_addr = next_desc;
281- } while (desc_addr != s->tx_queue[queue_index]);
282-}
283-
284-static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
285- unsigned size)
286-{
287- mv88w8618_eth_state *s = opaque;
288-
289- switch (offset) {
290- case MP_ETH_SMIR:
291- if (s->smir & MP_ETH_SMIR_OPCODE) {
292- switch (s->smir & MP_ETH_SMIR_ADDR) {
293- case MP_ETH_PHY1_BMSR:
294- return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
295- MP_ETH_SMIR_RDVALID;
296- case MP_ETH_PHY1_PHYSID1:
297- return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
298- case MP_ETH_PHY1_PHYSID2:
299- return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
300- default:
301- return MP_ETH_SMIR_RDVALID;
302- }
303- }
304- return 0;
305-
306- case MP_ETH_ICR:
307- return s->icr;
308-
309- case MP_ETH_IMR:
310- return s->imr;
311-
312- case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
313- return s->frx_queue[(offset - MP_ETH_FRDP0) / 4];
314-
315- case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
316- return s->rx_queue[(offset - MP_ETH_CRDP0) / 4];
317-
318- case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
319- return s->tx_queue[(offset - MP_ETH_CTDP0) / 4];
320-
321- default:
322- return 0;
323- }
324-}
325-
326-static void mv88w8618_eth_write(void *opaque, hwaddr offset,
327- uint64_t value, unsigned size)
328-{
329- mv88w8618_eth_state *s = opaque;
330-
331- switch (offset) {
332- case MP_ETH_SMIR:
333- s->smir = value;
334- break;
335-
336- case MP_ETH_PCXR:
337- s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
338- break;
339-
340- case MP_ETH_SDCMR:
341- if (value & MP_ETH_CMD_TXHI) {
342- eth_send(s, 1);
343- }
344- if (value & MP_ETH_CMD_TXLO) {
345- eth_send(s, 0);
346- }
347- if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
348- qemu_irq_raise(s->irq);
349- }
350- break;
351-
352- case MP_ETH_ICR:
353- s->icr &= value;
354- break;
355-
356- case MP_ETH_IMR:
357- s->imr = value;
358- if (s->icr & s->imr) {
359- qemu_irq_raise(s->irq);
360- }
361- break;
362-
363- case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
364- s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
365- break;
366-
367- case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
368- s->rx_queue[(offset - MP_ETH_CRDP0) / 4] =
369- s->cur_rx[(offset - MP_ETH_CRDP0) / 4] = value;
370- break;
371-
372- case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
373- s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
374- break;
375- }
376-}
377-
378-static const MemoryRegionOps mv88w8618_eth_ops = {
379- .read = mv88w8618_eth_read,
380- .write = mv88w8618_eth_write,
381- .endianness = DEVICE_NATIVE_ENDIAN,
382-};
383-
384-static void eth_cleanup(NetClientState *nc)
385-{
386- mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
387-
388- s->nic = NULL;
389-}
390-
391-static NetClientInfo net_mv88w8618_info = {
392- .type = NET_CLIENT_DRIVER_NIC,
393- .size = sizeof(NICState),
394- .receive = eth_receive,
395- .cleanup = eth_cleanup,
396-};
397-
398-static void mv88w8618_eth_init(Object *obj)
399-{
400- SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
401- DeviceState *dev = DEVICE(sbd);
402- mv88w8618_eth_state *s = MV88W8618_ETH(dev);
403-
404- sysbus_init_irq(sbd, &s->irq);
405- memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
406- "mv88w8618-eth", MP_ETH_SIZE);
407- sysbus_init_mmio(sbd, &s->iomem);
408-}
409-
410-static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
411-{
412- mv88w8618_eth_state *s = MV88W8618_ETH(dev);
413-
414- if (!s->dma_mr) {
415- error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
416- return;
417- }
418-
419- address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
420- s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
421- object_get_typename(OBJECT(dev)), dev->id, s);
422-}
423-
424-static const VMStateDescription mv88w8618_eth_vmsd = {
425- .name = "mv88w8618_eth",
426- .version_id = 1,
427- .minimum_version_id = 1,
428- .fields = (VMStateField[]) {
429- VMSTATE_UINT32(smir, mv88w8618_eth_state),
430- VMSTATE_UINT32(icr, mv88w8618_eth_state),
431- VMSTATE_UINT32(imr, mv88w8618_eth_state),
432- VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
433- VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
434- VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
435- VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
436- VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
437- VMSTATE_END_OF_LIST()
438- }
439-};
440-
441-static Property mv88w8618_eth_properties[] = {
442- DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
443- DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
444- TYPE_MEMORY_REGION, MemoryRegion *),
445- DEFINE_PROP_END_OF_LIST(),
446-};
447-
448-static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
449-{
450- DeviceClass *dc = DEVICE_CLASS(klass);
451-
452- dc->vmsd = &mv88w8618_eth_vmsd;
453- device_class_set_props(dc, mv88w8618_eth_properties);
454- dc->realize = mv88w8618_eth_realize;
455-}
456-
457-static const TypeInfo mv88w8618_eth_info = {
458- .name = TYPE_MV88W8618_ETH,
459- .parent = TYPE_SYS_BUS_DEVICE,
460- .instance_size = sizeof(mv88w8618_eth_state),
461- .instance_init = mv88w8618_eth_init,
462- .class_init = mv88w8618_eth_class_init,
463-};
464-
46587 /* LCD register offsets */
46688 #define MP_LCD_IRQCTRL 0x180
46789 #define MP_LCD_IRQSTAT 0x184
@@ -1746,7 +1368,6 @@ static void musicpal_register_types(void)
17461368 type_register_static(&mv88w8618_pic_info);
17471369 type_register_static(&mv88w8618_pit_info);
17481370 type_register_static(&mv88w8618_flashcfg_info);
1749- type_register_static(&mv88w8618_eth_info);
17501371 type_register_static(&mv88w8618_wlan_info);
17511372 type_register_static(&musicpal_lcd_info);
17521373 type_register_static(&musicpal_gpio_info);
--- a/hw/net/meson.build
+++ b/hw/net/meson.build
@@ -26,6 +26,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')
2626 softmmu_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c'))
2727 softmmu_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c'))
2828 softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c'))
29+softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c'))
2930
3031 softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c'))
3132 softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c'))
--- /dev/null
+++ b/hw/net/mv88w8618_eth.c
@@ -0,0 +1,403 @@
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+
--- /dev/null
+++ b/include/hw/net/mv88w8618_eth.h
@@ -0,0 +1,12 @@
1+/* SPDX-License-Identifier: GPL-2.0-or-later */
2+/*
3+ * Marvell MV88W8618 / Freecom MusicPal emulation.
4+ *
5+ * Copyright (c) 2008-2021 QEMU contributors
6+ */
7+#ifndef HW_NET_MV88W8618_H
8+#define HW_NET_MV88W8618_H
9+
10+#define TYPE_MV88W8618_ETH "mv88w8618_eth"
11+
12+#endif