xref: /openbmc/qemu/hw/net/mv88w8618_eth.c (revision 28004fb7)
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,
354                           &dev->mem_reentrancy_guard, s);
355 }
356 
357 static const VMStateDescription mv88w8618_eth_vmsd = {
358     .name = "mv88w8618_eth",
359     .version_id = 1,
360     .minimum_version_id = 1,
361     .fields = (const 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 
374 static 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 
381 static 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 
390 static 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 
398 static void musicpal_register_types(void)
399 {
400     type_register_static(&mv88w8618_eth_info);
401 }
402 
403 type_init(musicpal_register_types)
404 
405