修訂 | 6d81f4887f66995ca94fce5f896b26339a537bd9 (tree) |
---|---|
時間 | 2022-01-20 20:47:52 |
作者 | Philippe Mathieu-Daudé <f4bug@amsa...> |
Commiter | Peter Maydell |
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>
@@ -774,6 +774,8 @@ M: Peter Maydell <peter.maydell@linaro.org> | ||
774 | 774 | L: qemu-arm@nongnu.org |
775 | 775 | S: Odd Fixes |
776 | 776 | F: hw/arm/musicpal.c |
777 | +F: hw/net/mv88w8618_eth.c | |
778 | +F: include/hw/net/mv88w8618_eth.h | |
777 | 779 | F: docs/system/arm/musicpal.rst |
778 | 780 | |
779 | 781 | Nuvoton NPCM7xx |
@@ -34,12 +34,12 @@ | ||
34 | 34 | #include "ui/pixel_ops.h" |
35 | 35 | #include "qemu/cutils.h" |
36 | 36 | #include "qom/object.h" |
37 | +#include "hw/net/mv88w8618_eth.h" | |
37 | 38 | |
38 | 39 | #define MP_MISC_BASE 0x80002000 |
39 | 40 | #define MP_MISC_SIZE 0x00001000 |
40 | 41 | |
41 | 42 | #define MP_ETH_BASE 0x80008000 |
42 | -#define MP_ETH_SIZE 0x00001000 | |
43 | 43 | |
44 | 44 | #define MP_WLAN_BASE 0x8000C000 |
45 | 45 | #define MP_WLAN_SIZE 0x00000800 |
@@ -84,384 +84,6 @@ | ||
84 | 84 | /* Wolfson 8750 I2C address */ |
85 | 85 | #define MP_WM_ADDR 0x1A |
86 | 86 | |
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 | - | |
465 | 87 | /* LCD register offsets */ |
466 | 88 | #define MP_LCD_IRQCTRL 0x180 |
467 | 89 | #define MP_LCD_IRQSTAT 0x184 |
@@ -1746,7 +1368,6 @@ static void musicpal_register_types(void) | ||
1746 | 1368 | type_register_static(&mv88w8618_pic_info); |
1747 | 1369 | type_register_static(&mv88w8618_pit_info); |
1748 | 1370 | type_register_static(&mv88w8618_flashcfg_info); |
1749 | - type_register_static(&mv88w8618_eth_info); | |
1750 | 1371 | type_register_static(&mv88w8618_wlan_info); |
1751 | 1372 | type_register_static(&musicpal_lcd_info); |
1752 | 1373 | type_register_static(&musicpal_gpio_info); |
@@ -26,6 +26,7 @@ softmmu_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c') | ||
26 | 26 | softmmu_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) |
27 | 27 | softmmu_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c')) |
28 | 28 | 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')) | |
29 | 30 | |
30 | 31 | softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c')) |
31 | 32 | softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.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 | + |
@@ -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 |