blob: 2185f1131a94a8c7f4bad94f1310f1b6e019166c [file] [log] [blame]
Philippe Mathieu-Daudé6d81f482022-01-07 19:44:29 +01001/* 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
72typedef 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
80typedef 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
88OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
89
90struct 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
112static 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
123static 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
134static 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
168static 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
179static 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
190static 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
216static 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
258static 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
310static const MemoryRegionOps mv88w8618_eth_ops = {
311 .read = mv88w8618_eth_read,
312 .write = mv88w8618_eth_write,
313 .endianness = DEVICE_NATIVE_ENDIAN,
314};
315
316static void eth_cleanup(NetClientState *nc)
317{
318 mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
319
320 s->nic = NULL;
321}
322
323static NetClientInfo net_mv88w8618_info = {
324 .type = NET_CLIENT_DRIVER_NIC,
325 .size = sizeof(NICState),
326 .receive = eth_receive,
327 .cleanup = eth_cleanup,
328};
329
330static 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
342static 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,
Akihiko Odaki7d0fefd2023-06-01 12:18:58 +0900353 object_get_typename(OBJECT(dev)), dev->id,
354 &dev->mem_reentrancy_guard, s);
Philippe Mathieu-Daudé6d81f482022-01-07 19:44:29 +0100355}
356
357static const VMStateDescription mv88w8618_eth_vmsd = {
358 .name = "mv88w8618_eth",
359 .version_id = 1,
360 .minimum_version_id = 1,
361 .fields = (VMStateField[]) {
362 VMSTATE_UINT32(smir, mv88w8618_eth_state),
363 VMSTATE_UINT32(icr, mv88w8618_eth_state),
364 VMSTATE_UINT32(imr, mv88w8618_eth_state),
365 VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
366 VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
367 VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
368 VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
369 VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
370 VMSTATE_END_OF_LIST()
371 }
372};
373
374static Property mv88w8618_eth_properties[] = {
375 DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
376 DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
377 TYPE_MEMORY_REGION, MemoryRegion *),
378 DEFINE_PROP_END_OF_LIST(),
379};
380
381static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
382{
383 DeviceClass *dc = DEVICE_CLASS(klass);
384
385 dc->vmsd = &mv88w8618_eth_vmsd;
386 device_class_set_props(dc, mv88w8618_eth_properties);
387 dc->realize = mv88w8618_eth_realize;
388}
389
390static const TypeInfo mv88w8618_eth_info = {
391 .name = TYPE_MV88W8618_ETH,
392 .parent = TYPE_SYS_BUS_DEVICE,
393 .instance_size = sizeof(mv88w8618_eth_state),
394 .instance_init = mv88w8618_eth_init,
395 .class_init = mv88w8618_eth_class_init,
396};
397
398static void musicpal_register_types(void)
399{
400 type_register_static(&mv88w8618_eth_info);
401}
402
403type_init(musicpal_register_types)
404