migration: Allow user to specify available switchover bandwidth
[qemu/kevin.git] / hw / net / mv88w8618_eth.c
blobef30b0d4a6ac68ed5588bf5603f4262eb6c2d905
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * Marvell MV88W8618 / Freecom MusicPal emulation.
5 * Copyright (c) 2008 Jan Kiszka
6 */
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"
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
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)
43 /* PHY registers */
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 {
73 uint32_t cmdstat;
74 uint16_t res;
75 uint16_t bytes;
76 uint32_t buffer;
77 uint32_t next;
78 } mv88w8618_tx_desc;
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;
88 OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
90 struct mv88w8618_eth_state {
91 /*< private >*/
92 SysBusDevice parent_obj;
93 /*< public >*/
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;
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);
137 uint32_t desc_addr;
138 mv88w8618_rx_desc desc;
139 int i;
141 for (i = 0; i < 4; i++) {
142 desc_addr = s->cur_rx[i];
143 if (!desc_addr) {
144 continue;
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;
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);
160 return size;
162 desc_addr = desc.next;
163 } while (desc_addr != s->rx_queue[i]);
165 return size;
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;
194 uint32_t next_desc;
195 uint8_t buf[2048];
196 int len;
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);
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,
217 unsigned size)
219 mv88w8618_eth_state *s = opaque;
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;
236 return 0;
238 case MP_ETH_ICR:
239 return s->icr;
241 case MP_ETH_IMR:
242 return s->imr;
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];
253 default:
254 return 0;
258 static void mv88w8618_eth_write(void *opaque, hwaddr offset,
259 uint64_t value, unsigned size)
261 mv88w8618_eth_state *s = opaque;
263 switch (offset) {
264 case MP_ETH_SMIR:
265 s->smir = value;
266 break;
268 case MP_ETH_PCXR:
269 s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
270 break;
272 case MP_ETH_SDCMR:
273 if (value & MP_ETH_CMD_TXHI) {
274 eth_send(s, 1);
276 if (value & MP_ETH_CMD_TXLO) {
277 eth_send(s, 0);
279 if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
280 qemu_irq_raise(s->irq);
282 break;
284 case MP_ETH_ICR:
285 s->icr &= value;
286 break;
288 case MP_ETH_IMR:
289 s->imr = value;
290 if (s->icr & s->imr) {
291 qemu_irq_raise(s->irq);
293 break;
295 case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
296 s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
297 break;
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;
304 case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
305 s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
306 break;
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);
320 s->nic = NULL;
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);
346 if (!s->dma_mr) {
347 error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
348 return;
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",
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()
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)