]> git.proxmox.com Git - mirror_qemu.git/blob - hw/net/mv88w8618_eth.c
hw/ide/via: implement legacy/native mode switching
[mirror_qemu.git] / hw / net / mv88w8618_eth.c
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