xref: /openbmc/qemu/hw/net/xilinx_ethlite.c (revision 0bd0ba87a0fa22b5d3dda206f1920547df6eb918)
1 /*
2  * QEMU model of the Xilinx Ethernet Lite MAC.
3  *
4  * Copyright (c) 2009 Edgar E. Iglesias.
5  * Copyright (c) 2024 Linaro, Ltd
6  *
7  * DS580: https://docs.amd.com/v/u/en-US/xps_ethernetlite
8  * LogiCORE IP XPS Ethernet Lite Media Access Controller
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  */
28 
29 #include "qemu/osdep.h"
30 #include "qemu/module.h"
31 #include "qemu/bitops.h"
32 #include "qom/object.h"
33 #include "qapi/error.h"
34 #include "hw/sysbus.h"
35 #include "hw/irq.h"
36 #include "hw/qdev-properties.h"
37 #include "hw/misc/unimp.h"
38 #include "net/net.h"
39 #include "trace.h"
40 
41 #define BUFSZ_MAX      0x07e4
42 #define A_MDIO_BASE    0x07e4
43 #define A_TX_BASE0     0x07f4
44 #define A_TX_BASE1     0x0ff4
45 #define A_RX_BASE0     0x17fc
46 #define A_RX_BASE1     0x1ffc
47 
48 enum {
49     TX_LEN =  0,
50     TX_GIE =  1,
51     TX_CTRL = 2,
52     TX_MAX
53 };
54 
55 enum {
56     RX_CTRL = 0,
57     RX_MAX
58 };
59 
60 #define GIE_GIE    0x80000000
61 
62 #define CTRL_I     0x8
63 #define CTRL_P     0x2
64 #define CTRL_S     0x1
65 
66 typedef struct XlnxXpsEthLitePort {
67     MemoryRegion txio;
68     MemoryRegion rxio;
69     MemoryRegion txbuf;
70     MemoryRegion rxbuf;
71 
72     struct {
73         uint32_t tx_len;
74         uint32_t tx_gie;
75         uint32_t tx_ctrl;
76 
77         uint32_t rx_ctrl;
78     } reg;
79 } XlnxXpsEthLitePort;
80 
81 #define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite"
82 OBJECT_DECLARE_SIMPLE_TYPE(XlnxXpsEthLite, XILINX_ETHLITE)
83 
84 struct XlnxXpsEthLite
85 {
86     SysBusDevice parent_obj;
87 
88     MemoryRegion mmio;
89     qemu_irq irq;
90     NICState *nic;
91     NICConf conf;
92 
93     uint32_t c_tx_pingpong;
94     uint32_t c_rx_pingpong;
95     unsigned int port_index; /* dual port RAM index */
96 
97     UnimplementedDeviceState mdio;
98     XlnxXpsEthLitePort port[2];
99 };
100 
101 static inline void eth_pulse_irq(XlnxXpsEthLite *s)
102 {
103     /* Only the first gie reg is active.  */
104     if (s->port[0].reg.tx_gie & GIE_GIE) {
105         qemu_irq_pulse(s->irq);
106     }
107 }
108 
109 static unsigned addr_to_port_index(hwaddr addr)
110 {
111     return extract64(addr, 11, 1);
112 }
113 
114 static void *txbuf_ptr(XlnxXpsEthLite *s, unsigned port_index)
115 {
116     return memory_region_get_ram_ptr(&s->port[port_index].txbuf);
117 }
118 
119 static void *rxbuf_ptr(XlnxXpsEthLite *s, unsigned port_index)
120 {
121     return memory_region_get_ram_ptr(&s->port[port_index].rxbuf);
122 }
123 
124 static uint64_t port_tx_read(void *opaque, hwaddr addr, unsigned int size)
125 {
126     XlnxXpsEthLite *s = opaque;
127     unsigned port_index = addr_to_port_index(addr);
128     uint32_t r = 0;
129 
130     switch (addr >> 2) {
131     case TX_LEN:
132         r = s->port[port_index].reg.tx_len;
133         break;
134     case TX_GIE:
135         r = s->port[port_index].reg.tx_gie;
136         break;
137     case TX_CTRL:
138         r = s->port[port_index].reg.tx_ctrl;
139         break;
140     default:
141         g_assert_not_reached();
142     }
143 
144     return r;
145 }
146 
147 static void port_tx_write(void *opaque, hwaddr addr, uint64_t value,
148                           unsigned int size)
149 {
150     XlnxXpsEthLite *s = opaque;
151     unsigned port_index = addr_to_port_index(addr);
152 
153     switch (addr >> 2) {
154     case TX_LEN:
155         s->port[port_index].reg.tx_len = value;
156         break;
157     case TX_GIE:
158         s->port[port_index].reg.tx_gie = value;
159         break;
160     case TX_CTRL:
161         if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
162             qemu_send_packet(qemu_get_queue(s->nic),
163                              txbuf_ptr(s, port_index),
164                              s->port[port_index].reg.tx_len);
165             if (s->port[port_index].reg.tx_ctrl & CTRL_I) {
166                 eth_pulse_irq(s);
167             }
168         } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) {
169             memcpy(&s->conf.macaddr.a[0], txbuf_ptr(s, port_index), 6);
170             if (s->port[port_index].reg.tx_ctrl & CTRL_I) {
171                 eth_pulse_irq(s);
172             }
173         }
174         /*
175          * We are fast and get ready pretty much immediately
176          * so we actually never flip the S nor P bits to one.
177          */
178         s->port[port_index].reg.tx_ctrl = value & ~(CTRL_P | CTRL_S);
179         break;
180     default:
181         g_assert_not_reached();
182     }
183 }
184 
185 static const MemoryRegionOps eth_porttx_ops = {
186         .read = port_tx_read,
187         .write = port_tx_write,
188         .endianness = DEVICE_NATIVE_ENDIAN,
189         .impl = {
190             .min_access_size = 4,
191             .max_access_size = 4,
192         },
193         .valid = {
194             .min_access_size = 4,
195             .max_access_size = 4,
196         },
197 };
198 
199 static uint64_t port_rx_read(void *opaque, hwaddr addr, unsigned int size)
200 {
201     XlnxXpsEthLite *s = opaque;
202     unsigned port_index = addr_to_port_index(addr);
203     uint32_t r = 0;
204 
205     switch (addr >> 2) {
206     case RX_CTRL:
207         r = s->port[port_index].reg.rx_ctrl;
208         break;
209     default:
210         g_assert_not_reached();
211     }
212 
213     return r;
214 }
215 
216 static void port_rx_write(void *opaque, hwaddr addr, uint64_t value,
217                           unsigned int size)
218 {
219     XlnxXpsEthLite *s = opaque;
220     unsigned port_index = addr_to_port_index(addr);
221 
222     switch (addr >> 2) {
223     case RX_CTRL:
224         if (!(value & CTRL_S)) {
225             qemu_flush_queued_packets(qemu_get_queue(s->nic));
226         }
227         s->port[port_index].reg.rx_ctrl = value;
228         break;
229     default:
230         g_assert_not_reached();
231     }
232 }
233 
234 static const MemoryRegionOps eth_portrx_ops = {
235         .read = port_rx_read,
236         .write = port_rx_write,
237         .endianness = DEVICE_NATIVE_ENDIAN,
238         .impl = {
239             .min_access_size = 4,
240             .max_access_size = 4,
241         },
242         .valid = {
243             .min_access_size = 4,
244             .max_access_size = 4,
245         },
246 };
247 
248 static bool eth_can_rx(NetClientState *nc)
249 {
250     XlnxXpsEthLite *s = qemu_get_nic_opaque(nc);
251 
252     return !(s->port[s->port_index].reg.rx_ctrl & CTRL_S);
253 }
254 
255 static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
256 {
257     XlnxXpsEthLite *s = qemu_get_nic_opaque(nc);
258     unsigned int port_index = s->port_index;
259 
260     /* DA filter.  */
261     if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6))
262         return size;
263 
264     if (s->port[port_index].reg.rx_ctrl & CTRL_S) {
265         trace_ethlite_pkt_lost(s->port[port_index].reg.rx_ctrl);
266         return -1;
267     }
268 
269     if (size >= BUFSZ_MAX) {
270         trace_ethlite_pkt_size_too_big(size);
271         return -1;
272     }
273     memcpy(rxbuf_ptr(s, port_index), buf, size);
274 
275     s->port[port_index].reg.rx_ctrl |= CTRL_S;
276     if (s->port[port_index].reg.rx_ctrl & CTRL_I) {
277         eth_pulse_irq(s);
278     }
279 
280     /* If c_rx_pingpong was set flip buffers.  */
281     s->port_index ^= s->c_rx_pingpong;
282     return size;
283 }
284 
285 static void xilinx_ethlite_reset(DeviceState *dev)
286 {
287     XlnxXpsEthLite *s = XILINX_ETHLITE(dev);
288 
289     s->port_index = 0;
290 }
291 
292 static NetClientInfo net_xilinx_ethlite_info = {
293     .type = NET_CLIENT_DRIVER_NIC,
294     .size = sizeof(NICState),
295     .can_receive = eth_can_rx,
296     .receive = eth_rx,
297 };
298 
299 static void xilinx_ethlite_realize(DeviceState *dev, Error **errp)
300 {
301     XlnxXpsEthLite *s = XILINX_ETHLITE(dev);
302 
303     memory_region_init(&s->mmio, OBJECT(dev),
304                        "xlnx.xps-ethernetlite", 0x2000);
305 
306     object_initialize_child(OBJECT(dev), "ethlite.mdio", &s->mdio,
307                             TYPE_UNIMPLEMENTED_DEVICE);
308     qdev_prop_set_string(DEVICE(&s->mdio), "name", "ethlite.mdio");
309     qdev_prop_set_uint64(DEVICE(&s->mdio), "size", 4 * 4);
310     sysbus_realize(SYS_BUS_DEVICE(&s->mdio), &error_fatal);
311     memory_region_add_subregion(&s->mmio, A_MDIO_BASE,
312                            sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mdio), 0));
313 
314     for (unsigned i = 0; i < 2; i++) {
315         memory_region_init_ram(&s->port[i].txbuf, OBJECT(dev),
316                                i ? "ethlite.tx[1]buf" : "ethlite.tx[0]buf",
317                                BUFSZ_MAX, &error_abort);
318         memory_region_add_subregion(&s->mmio, 0x0800 * i, &s->port[i].txbuf);
319         memory_region_init_io(&s->port[i].txio, OBJECT(dev),
320                               &eth_porttx_ops, s,
321                               i ? "ethlite.tx[1]io" : "ethlite.tx[0]io",
322                               4 * TX_MAX);
323         memory_region_add_subregion(&s->mmio, i ? A_TX_BASE1 : A_TX_BASE0,
324                                     &s->port[i].txio);
325 
326         memory_region_init_ram(&s->port[i].rxbuf, OBJECT(dev),
327                                i ? "ethlite.rx[1]buf" : "ethlite.rx[0]buf",
328                                BUFSZ_MAX, &error_abort);
329         memory_region_add_subregion(&s->mmio, 0x1000 + 0x0800 * i,
330                                     &s->port[i].rxbuf);
331         memory_region_init_io(&s->port[i].rxio, OBJECT(dev),
332                               &eth_portrx_ops, s,
333                               i ? "ethlite.rx[1]io" : "ethlite.rx[0]io",
334                               4 * RX_MAX);
335         memory_region_add_subregion(&s->mmio, i ? A_RX_BASE1 : A_RX_BASE0,
336                                     &s->port[i].rxio);
337     }
338 
339     qemu_macaddr_default_if_unset(&s->conf.macaddr);
340     s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
341                           object_get_typename(OBJECT(dev)), dev->id,
342                           &dev->mem_reentrancy_guard, s);
343     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
344 }
345 
346 static void xilinx_ethlite_init(Object *obj)
347 {
348     XlnxXpsEthLite *s = XILINX_ETHLITE(obj);
349 
350     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
351     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
352 }
353 
354 static const Property xilinx_ethlite_properties[] = {
355     DEFINE_PROP_UINT32("tx-ping-pong", XlnxXpsEthLite, c_tx_pingpong, 1),
356     DEFINE_PROP_UINT32("rx-ping-pong", XlnxXpsEthLite, c_rx_pingpong, 1),
357     DEFINE_NIC_PROPERTIES(XlnxXpsEthLite, conf),
358 };
359 
360 static void xilinx_ethlite_class_init(ObjectClass *klass, void *data)
361 {
362     DeviceClass *dc = DEVICE_CLASS(klass);
363 
364     dc->realize = xilinx_ethlite_realize;
365     device_class_set_legacy_reset(dc, xilinx_ethlite_reset);
366     device_class_set_props(dc, xilinx_ethlite_properties);
367 }
368 
369 static const TypeInfo xilinx_ethlite_types[] = {
370     {
371         .name          = TYPE_XILINX_ETHLITE,
372         .parent        = TYPE_SYS_BUS_DEVICE,
373         .instance_size = sizeof(XlnxXpsEthLite),
374         .instance_init = xilinx_ethlite_init,
375         .class_init    = xilinx_ethlite_class_init,
376     },
377 };
378 
379 DEFINE_TYPES(xilinx_ethlite_types)
380