1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * Marvell MV88W8618 / Freecom MusicPal emulation.
5 * Copyright (c) 2008 Jan Kiszka
8 #include "qemu/osdep.h"
9 #include "qapi/error.h"
10 #include "hw/qdev-properties.h"
11 #include "hw/sysbus.h"
13 #include "hw/net/mv88w8618_eth.h"
14 #include "migration/vmstate.h"
15 #include "sysemu/dma.h"
18 #define MP_ETH_SIZE 0x00001000
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
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)
44 #define MP_ETH_PHY1_BMSR 0x00210000
45 #define MP_ETH_PHY1_PHYSID1 0x00410000
46 #define MP_ETH_PHY1_PHYSID2 0x00610000
48 #define MP_PHY_BMSR_LINK 0x0004
49 #define MP_PHY_BMSR_AUTONEG 0x0008
51 #define MP_PHY_88E3015 0x01410E20
53 /* TX descriptor status */
54 #define MP_ETH_TX_OWN (1U << 31)
56 /* RX descriptor status */
57 #define MP_ETH_RX_OWN (1U << 31)
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
65 /* Port config bits */
66 #define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
68 /* SDMA command bits */
69 #define MP_ETH_CMD_TXHI (1 << 23)
70 #define MP_ETH_CMD_TXLO (1 << 22)
72 typedef struct mv88w8618_tx_desc
{
80 typedef struct mv88w8618_rx_desc
{
88 OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state
, MV88W8618_ETH
)
90 struct mv88w8618_eth_state
{
92 SysBusDevice parent_obj
;
103 uint32_t vlan_header
;
104 uint32_t tx_queue
[2];
105 uint32_t rx_queue
[4];
106 uint32_t frx_queue
[4];
112 static void eth_rx_desc_put(AddressSpace
*dma_as
, uint32_t addr
,
113 mv88w8618_rx_desc
*desc
)
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
);
123 static void eth_rx_desc_get(AddressSpace
*dma_as
, uint32_t addr
,
124 mv88w8618_rx_desc
*desc
)
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
);
134 static ssize_t
eth_receive(NetClientState
*nc
, const uint8_t *buf
, size_t size
)
136 mv88w8618_eth_state
*s
= qemu_get_nic_opaque(nc
);
138 mv88w8618_rx_desc desc
;
141 for (i
= 0; i
< 4; i
++) {
142 desc_addr
= s
->cur_rx
[i
];
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
;
155 s
->icr
|= MP_ETH_IRQ_RX
;
156 if (s
->icr
& s
->imr
) {
157 qemu_irq_raise(s
->irq
);
159 eth_rx_desc_put(&s
->dma_as
, desc_addr
, &desc
);
162 desc_addr
= desc
.next
;
163 } while (desc_addr
!= s
->rx_queue
[i
]);
168 static void eth_tx_desc_put(AddressSpace
*dma_as
, uint32_t addr
,
169 mv88w8618_tx_desc
*desc
)
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
);
179 static void eth_tx_desc_get(AddressSpace
*dma_as
, uint32_t addr
,
180 mv88w8618_tx_desc
*desc
)
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
);
190 static void eth_send(mv88w8618_eth_state
*s
, int queue_index
)
192 uint32_t desc_addr
= s
->tx_queue
[queue_index
];
193 mv88w8618_tx_desc desc
;
199 eth_tx_desc_get(&s
->dma_as
, desc_addr
, &desc
);
200 next_desc
= desc
.next
;
201 if (desc
.cmdstat
& MP_ETH_TX_OWN
) {
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
);
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
);
212 desc_addr
= next_desc
;
213 } while (desc_addr
!= s
->tx_queue
[queue_index
]);
216 static uint64_t mv88w8618_eth_read(void *opaque
, hwaddr offset
,
219 mv88w8618_eth_state
*s
= opaque
;
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
|
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
;
233 return MP_ETH_SMIR_RDVALID
;
244 case MP_ETH_FRDP0
... MP_ETH_FRDP3
:
245 return s
->frx_queue
[(offset
- MP_ETH_FRDP0
) / 4];
247 case MP_ETH_CRDP0
... MP_ETH_CRDP3
:
248 return s
->rx_queue
[(offset
- MP_ETH_CRDP0
) / 4];
250 case MP_ETH_CTDP0
... MP_ETH_CTDP1
:
251 return s
->tx_queue
[(offset
- MP_ETH_CTDP0
) / 4];
258 static void mv88w8618_eth_write(void *opaque
, hwaddr offset
,
259 uint64_t value
, unsigned size
)
261 mv88w8618_eth_state
*s
= opaque
;
269 s
->vlan_header
= ((value
>> MP_ETH_PCXR_2BSM_BIT
) & 1) * 2;
273 if (value
& MP_ETH_CMD_TXHI
) {
276 if (value
& MP_ETH_CMD_TXLO
) {
279 if (value
& (MP_ETH_CMD_TXHI
| MP_ETH_CMD_TXLO
) && s
->icr
& s
->imr
) {
280 qemu_irq_raise(s
->irq
);
290 if (s
->icr
& s
->imr
) {
291 qemu_irq_raise(s
->irq
);
295 case MP_ETH_FRDP0
... MP_ETH_FRDP3
:
296 s
->frx_queue
[(offset
- MP_ETH_FRDP0
) / 4] = value
;
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
;
304 case MP_ETH_CTDP0
... MP_ETH_CTDP1
:
305 s
->tx_queue
[(offset
- MP_ETH_CTDP0
) / 4] = value
;
310 static const MemoryRegionOps mv88w8618_eth_ops
= {
311 .read
= mv88w8618_eth_read
,
312 .write
= mv88w8618_eth_write
,
313 .endianness
= DEVICE_NATIVE_ENDIAN
,
316 static void eth_cleanup(NetClientState
*nc
)
318 mv88w8618_eth_state
*s
= qemu_get_nic_opaque(nc
);
323 static NetClientInfo net_mv88w8618_info
= {
324 .type
= NET_CLIENT_DRIVER_NIC
,
325 .size
= sizeof(NICState
),
326 .receive
= eth_receive
,
327 .cleanup
= eth_cleanup
,
330 static void mv88w8618_eth_init(Object
*obj
)
332 SysBusDevice
*sbd
= SYS_BUS_DEVICE(obj
);
333 DeviceState
*dev
= DEVICE(sbd
);
334 mv88w8618_eth_state
*s
= MV88W8618_ETH(dev
);
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
);
342 static void mv88w8618_eth_realize(DeviceState
*dev
, Error
**errp
)
344 mv88w8618_eth_state
*s
= MV88W8618_ETH(dev
);
347 error_setg(errp
, TYPE_MV88W8618_ETH
" 'dma-memory' link not set");
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
);
356 static const VMStateDescription mv88w8618_eth_vmsd
= {
357 .name
= "mv88w8618_eth",
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()
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(),
380 static void mv88w8618_eth_class_init(ObjectClass
*klass
, void *data
)
382 DeviceClass
*dc
= DEVICE_CLASS(klass
);
384 dc
->vmsd
= &mv88w8618_eth_vmsd
;
385 device_class_set_props(dc
, mv88w8618_eth_properties
);
386 dc
->realize
= mv88w8618_eth_realize
;
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
,
397 static void musicpal_register_types(void)
399 type_register_static(&mv88w8618_eth_info
);
402 type_init(musicpal_register_types
)