xref: /openbmc/qemu/hw/net/xilinx_ethlite.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
1d7e35d4aSPaolo Bonzini /*
2d7e35d4aSPaolo Bonzini  * QEMU model of the Xilinx Ethernet Lite MAC.
3d7e35d4aSPaolo Bonzini  *
4d7e35d4aSPaolo Bonzini  * Copyright (c) 2009 Edgar E. Iglesias.
5d7e35d4aSPaolo Bonzini  *
6d7e35d4aSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
7d7e35d4aSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
8d7e35d4aSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
9d7e35d4aSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10d7e35d4aSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
11d7e35d4aSPaolo Bonzini  * furnished to do so, subject to the following conditions:
12d7e35d4aSPaolo Bonzini  *
13d7e35d4aSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
14d7e35d4aSPaolo Bonzini  * all copies or substantial portions of the Software.
15d7e35d4aSPaolo Bonzini  *
16d7e35d4aSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d7e35d4aSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d7e35d4aSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19d7e35d4aSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d7e35d4aSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21d7e35d4aSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22d7e35d4aSPaolo Bonzini  * THE SOFTWARE.
23d7e35d4aSPaolo Bonzini  */
24d7e35d4aSPaolo Bonzini 
25e8d40465SPeter Maydell #include "qemu/osdep.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
27db1015e9SEduardo Habkost #include "qom/object.h"
28a9ea0a9cSThomas Huth #include "exec/tswap.h"
29d7e35d4aSPaolo Bonzini #include "hw/sysbus.h"
3064552b6bSMarkus Armbruster #include "hw/irq.h"
31a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
32d7e35d4aSPaolo Bonzini #include "net/net.h"
33d7e35d4aSPaolo Bonzini 
34d7e35d4aSPaolo Bonzini #define D(x)
35d7e35d4aSPaolo Bonzini #define R_TX_BUF0     0
36d7e35d4aSPaolo Bonzini #define R_TX_LEN0     (0x07f4 / 4)
37d7e35d4aSPaolo Bonzini #define R_TX_GIE0     (0x07f8 / 4)
38d7e35d4aSPaolo Bonzini #define R_TX_CTRL0    (0x07fc / 4)
39d7e35d4aSPaolo Bonzini #define R_TX_BUF1     (0x0800 / 4)
40d7e35d4aSPaolo Bonzini #define R_TX_LEN1     (0x0ff4 / 4)
41d7e35d4aSPaolo Bonzini #define R_TX_CTRL1    (0x0ffc / 4)
42d7e35d4aSPaolo Bonzini 
43d7e35d4aSPaolo Bonzini #define R_RX_BUF0     (0x1000 / 4)
44d7e35d4aSPaolo Bonzini #define R_RX_CTRL0    (0x17fc / 4)
45d7e35d4aSPaolo Bonzini #define R_RX_BUF1     (0x1800 / 4)
46d7e35d4aSPaolo Bonzini #define R_RX_CTRL1    (0x1ffc / 4)
47d7e35d4aSPaolo Bonzini #define R_MAX         (0x2000 / 4)
48d7e35d4aSPaolo Bonzini 
49d7e35d4aSPaolo Bonzini #define GIE_GIE    0x80000000
50d7e35d4aSPaolo Bonzini 
51d7e35d4aSPaolo Bonzini #define CTRL_I     0x8
52d7e35d4aSPaolo Bonzini #define CTRL_P     0x2
53d7e35d4aSPaolo Bonzini #define CTRL_S     0x1
54d7e35d4aSPaolo Bonzini 
5591a28042SAndreas Färber #define TYPE_XILINX_ETHLITE "xlnx.xps-ethernetlite"
568110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(struct xlx_ethlite, XILINX_ETHLITE,
578110fa1dSEduardo Habkost                          TYPE_XILINX_ETHLITE)
5891a28042SAndreas Färber 
59d7e35d4aSPaolo Bonzini struct xlx_ethlite
60d7e35d4aSPaolo Bonzini {
6191a28042SAndreas Färber     SysBusDevice parent_obj;
6291a28042SAndreas Färber 
63d7e35d4aSPaolo Bonzini     MemoryRegion mmio;
64d7e35d4aSPaolo Bonzini     qemu_irq irq;
65d7e35d4aSPaolo Bonzini     NICState *nic;
66d7e35d4aSPaolo Bonzini     NICConf conf;
67d7e35d4aSPaolo Bonzini 
68d7e35d4aSPaolo Bonzini     uint32_t c_tx_pingpong;
69d7e35d4aSPaolo Bonzini     uint32_t c_rx_pingpong;
70d7e35d4aSPaolo Bonzini     unsigned int txbuf;
71d7e35d4aSPaolo Bonzini     unsigned int rxbuf;
72d7e35d4aSPaolo Bonzini 
73d7e35d4aSPaolo Bonzini     uint32_t regs[R_MAX];
74d7e35d4aSPaolo Bonzini };
75d7e35d4aSPaolo Bonzini 
eth_pulse_irq(struct xlx_ethlite * s)76d7e35d4aSPaolo Bonzini static inline void eth_pulse_irq(struct xlx_ethlite *s)
77d7e35d4aSPaolo Bonzini {
78d7e35d4aSPaolo Bonzini     /* Only the first gie reg is active.  */
79d7e35d4aSPaolo Bonzini     if (s->regs[R_TX_GIE0] & GIE_GIE) {
80d7e35d4aSPaolo Bonzini         qemu_irq_pulse(s->irq);
81d7e35d4aSPaolo Bonzini     }
82d7e35d4aSPaolo Bonzini }
83d7e35d4aSPaolo Bonzini 
84d7e35d4aSPaolo Bonzini static uint64_t
eth_read(void * opaque,hwaddr addr,unsigned int size)85d7e35d4aSPaolo Bonzini eth_read(void *opaque, hwaddr addr, unsigned int size)
86d7e35d4aSPaolo Bonzini {
87d7e35d4aSPaolo Bonzini     struct xlx_ethlite *s = opaque;
88d7e35d4aSPaolo Bonzini     uint32_t r = 0;
89d7e35d4aSPaolo Bonzini 
90d7e35d4aSPaolo Bonzini     addr >>= 2;
91d7e35d4aSPaolo Bonzini 
92d7e35d4aSPaolo Bonzini     switch (addr)
93d7e35d4aSPaolo Bonzini     {
94d7e35d4aSPaolo Bonzini         case R_TX_GIE0:
95d7e35d4aSPaolo Bonzini         case R_TX_LEN0:
96d7e35d4aSPaolo Bonzini         case R_TX_LEN1:
97d7e35d4aSPaolo Bonzini         case R_TX_CTRL1:
98d7e35d4aSPaolo Bonzini         case R_TX_CTRL0:
99d7e35d4aSPaolo Bonzini         case R_RX_CTRL1:
100d7e35d4aSPaolo Bonzini         case R_RX_CTRL0:
101d7e35d4aSPaolo Bonzini             r = s->regs[addr];
102883f2c59SPhilippe Mathieu-Daudé             D(qemu_log("%s " HWADDR_FMT_plx "=%x\n", __func__, addr * 4, r));
103d7e35d4aSPaolo Bonzini             break;
104d7e35d4aSPaolo Bonzini 
105d7e35d4aSPaolo Bonzini         default:
106d7e35d4aSPaolo Bonzini             r = tswap32(s->regs[addr]);
107d7e35d4aSPaolo Bonzini             break;
108d7e35d4aSPaolo Bonzini     }
109d7e35d4aSPaolo Bonzini     return r;
110d7e35d4aSPaolo Bonzini }
111d7e35d4aSPaolo Bonzini 
112d7e35d4aSPaolo Bonzini static void
eth_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)113d7e35d4aSPaolo Bonzini eth_write(void *opaque, hwaddr addr,
114d7e35d4aSPaolo Bonzini           uint64_t val64, unsigned int size)
115d7e35d4aSPaolo Bonzini {
116d7e35d4aSPaolo Bonzini     struct xlx_ethlite *s = opaque;
117d7e35d4aSPaolo Bonzini     unsigned int base = 0;
118d7e35d4aSPaolo Bonzini     uint32_t value = val64;
119d7e35d4aSPaolo Bonzini 
120d7e35d4aSPaolo Bonzini     addr >>= 2;
121d7e35d4aSPaolo Bonzini     switch (addr)
122d7e35d4aSPaolo Bonzini     {
123d7e35d4aSPaolo Bonzini         case R_TX_CTRL0:
124d7e35d4aSPaolo Bonzini         case R_TX_CTRL1:
125d7e35d4aSPaolo Bonzini             if (addr == R_TX_CTRL1)
126d7e35d4aSPaolo Bonzini                 base = 0x800 / 4;
127d7e35d4aSPaolo Bonzini 
128883f2c59SPhilippe Mathieu-Daudé             D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n",
129d7e35d4aSPaolo Bonzini                        __func__, addr * 4, value));
130d7e35d4aSPaolo Bonzini             if ((value & (CTRL_P | CTRL_S)) == CTRL_S) {
131d7e35d4aSPaolo Bonzini                 qemu_send_packet(qemu_get_queue(s->nic),
132d7e35d4aSPaolo Bonzini                                  (void *) &s->regs[base],
133d7e35d4aSPaolo Bonzini                                  s->regs[base + R_TX_LEN0]);
134d7e35d4aSPaolo Bonzini                 D(qemu_log("eth_tx %d\n", s->regs[base + R_TX_LEN0]));
135d7e35d4aSPaolo Bonzini                 if (s->regs[base + R_TX_CTRL0] & CTRL_I)
136d7e35d4aSPaolo Bonzini                     eth_pulse_irq(s);
137d7e35d4aSPaolo Bonzini             } else if ((value & (CTRL_P | CTRL_S)) == (CTRL_P | CTRL_S)) {
138d7e35d4aSPaolo Bonzini                 memcpy(&s->conf.macaddr.a[0], &s->regs[base], 6);
139d7e35d4aSPaolo Bonzini                 if (s->regs[base + R_TX_CTRL0] & CTRL_I)
140d7e35d4aSPaolo Bonzini                     eth_pulse_irq(s);
141d7e35d4aSPaolo Bonzini             }
142d7e35d4aSPaolo Bonzini 
143d7e35d4aSPaolo Bonzini             /* We are fast and get ready pretty much immediately so
144d7e35d4aSPaolo Bonzini                we actually never flip the S nor P bits to one.  */
145d7e35d4aSPaolo Bonzini             s->regs[addr] = value & ~(CTRL_P | CTRL_S);
146d7e35d4aSPaolo Bonzini             break;
147d7e35d4aSPaolo Bonzini 
148d7e35d4aSPaolo Bonzini         /* Keep these native.  */
149d7e35d4aSPaolo Bonzini         case R_RX_CTRL0:
150d7e35d4aSPaolo Bonzini         case R_RX_CTRL1:
151d7e35d4aSPaolo Bonzini             if (!(value & CTRL_S)) {
152d7e35d4aSPaolo Bonzini                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
153d7e35d4aSPaolo Bonzini             }
15431da45ceSMarkus Armbruster             /* fall through */
155d7e35d4aSPaolo Bonzini         case R_TX_LEN0:
156d7e35d4aSPaolo Bonzini         case R_TX_LEN1:
157d7e35d4aSPaolo Bonzini         case R_TX_GIE0:
158883f2c59SPhilippe Mathieu-Daudé             D(qemu_log("%s addr=" HWADDR_FMT_plx " val=%x\n",
159d7e35d4aSPaolo Bonzini                        __func__, addr * 4, value));
160d7e35d4aSPaolo Bonzini             s->regs[addr] = value;
161d7e35d4aSPaolo Bonzini             break;
162d7e35d4aSPaolo Bonzini 
163d7e35d4aSPaolo Bonzini         default:
164d7e35d4aSPaolo Bonzini             s->regs[addr] = tswap32(value);
165d7e35d4aSPaolo Bonzini             break;
166d7e35d4aSPaolo Bonzini     }
167d7e35d4aSPaolo Bonzini }
168d7e35d4aSPaolo Bonzini 
169d7e35d4aSPaolo Bonzini static const MemoryRegionOps eth_ops = {
170d7e35d4aSPaolo Bonzini     .read = eth_read,
171d7e35d4aSPaolo Bonzini     .write = eth_write,
172d7e35d4aSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
173d7e35d4aSPaolo Bonzini     .valid = {
174d7e35d4aSPaolo Bonzini         .min_access_size = 4,
175d7e35d4aSPaolo Bonzini         .max_access_size = 4
176d7e35d4aSPaolo Bonzini     }
177d7e35d4aSPaolo Bonzini };
178d7e35d4aSPaolo Bonzini 
eth_can_rx(NetClientState * nc)179b8c4b67eSPhilippe Mathieu-Daudé static bool eth_can_rx(NetClientState *nc)
180d7e35d4aSPaolo Bonzini {
181d7e35d4aSPaolo Bonzini     struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
182d7e35d4aSPaolo Bonzini     unsigned int rxbase = s->rxbuf * (0x800 / 4);
183d7e35d4aSPaolo Bonzini 
184d7e35d4aSPaolo Bonzini     return !(s->regs[rxbase + R_RX_CTRL0] & CTRL_S);
185d7e35d4aSPaolo Bonzini }
186d7e35d4aSPaolo Bonzini 
eth_rx(NetClientState * nc,const uint8_t * buf,size_t size)187d7e35d4aSPaolo Bonzini static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
188d7e35d4aSPaolo Bonzini {
189d7e35d4aSPaolo Bonzini     struct xlx_ethlite *s = qemu_get_nic_opaque(nc);
190d7e35d4aSPaolo Bonzini     unsigned int rxbase = s->rxbuf * (0x800 / 4);
191d7e35d4aSPaolo Bonzini 
192d7e35d4aSPaolo Bonzini     /* DA filter.  */
193d7e35d4aSPaolo Bonzini     if (!(buf[0] & 0x80) && memcmp(&s->conf.macaddr.a[0], buf, 6))
194d7e35d4aSPaolo Bonzini         return size;
195d7e35d4aSPaolo Bonzini 
196d7e35d4aSPaolo Bonzini     if (s->regs[rxbase + R_RX_CTRL0] & CTRL_S) {
197d7e35d4aSPaolo Bonzini         D(qemu_log("ethlite lost packet %x\n", s->regs[R_RX_CTRL0]));
198d7e35d4aSPaolo Bonzini         return -1;
199d7e35d4aSPaolo Bonzini     }
200d7e35d4aSPaolo Bonzini 
201d7e35d4aSPaolo Bonzini     D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase));
202a0d1cbdaSchaojianhu     if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) {
203a0d1cbdaSchaojianhu         D(qemu_log("ethlite packet is too big, size=%x\n", size));
204a0d1cbdaSchaojianhu         return -1;
205a0d1cbdaSchaojianhu     }
206d7e35d4aSPaolo Bonzini     memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size);
207d7e35d4aSPaolo Bonzini 
208d7e35d4aSPaolo Bonzini     s->regs[rxbase + R_RX_CTRL0] |= CTRL_S;
20940e76f73SPeter Crosthwaite     if (s->regs[R_RX_CTRL0] & CTRL_I) {
210d7e35d4aSPaolo Bonzini         eth_pulse_irq(s);
21140e76f73SPeter Crosthwaite     }
212d7e35d4aSPaolo Bonzini 
213d7e35d4aSPaolo Bonzini     /* If c_rx_pingpong was set flip buffers.  */
214d7e35d4aSPaolo Bonzini     s->rxbuf ^= s->c_rx_pingpong;
215d7e35d4aSPaolo Bonzini     return size;
216d7e35d4aSPaolo Bonzini }
217d7e35d4aSPaolo Bonzini 
xilinx_ethlite_reset(DeviceState * dev)2188c6d9672SPeter Crosthwaite static void xilinx_ethlite_reset(DeviceState *dev)
2198c6d9672SPeter Crosthwaite {
2208c6d9672SPeter Crosthwaite     struct xlx_ethlite *s = XILINX_ETHLITE(dev);
2218c6d9672SPeter Crosthwaite 
2228c6d9672SPeter Crosthwaite     s->rxbuf = 0;
2238c6d9672SPeter Crosthwaite }
2248c6d9672SPeter Crosthwaite 
225d7e35d4aSPaolo Bonzini static NetClientInfo net_xilinx_ethlite_info = {
226f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
227d7e35d4aSPaolo Bonzini     .size = sizeof(NICState),
228d7e35d4aSPaolo Bonzini     .can_receive = eth_can_rx,
229d7e35d4aSPaolo Bonzini     .receive = eth_rx,
230d7e35d4aSPaolo Bonzini };
231d7e35d4aSPaolo Bonzini 
xilinx_ethlite_realize(DeviceState * dev,Error ** errp)232e8198f6eSPeter Crosthwaite static void xilinx_ethlite_realize(DeviceState *dev, Error **errp)
233d7e35d4aSPaolo Bonzini {
23491a28042SAndreas Färber     struct xlx_ethlite *s = XILINX_ETHLITE(dev);
235d7e35d4aSPaolo Bonzini 
236d7e35d4aSPaolo Bonzini     qemu_macaddr_default_if_unset(&s->conf.macaddr);
237d7e35d4aSPaolo Bonzini     s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf,
2387d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), dev->id,
2397d0fefdfSAkihiko Odaki                           &dev->mem_reentrancy_guard, s);
240d7e35d4aSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
241e8198f6eSPeter Crosthwaite }
242e8198f6eSPeter Crosthwaite 
xilinx_ethlite_init(Object * obj)243e8198f6eSPeter Crosthwaite static void xilinx_ethlite_init(Object *obj)
244e8198f6eSPeter Crosthwaite {
245e8198f6eSPeter Crosthwaite     struct xlx_ethlite *s = XILINX_ETHLITE(obj);
246e8198f6eSPeter Crosthwaite 
247e8198f6eSPeter Crosthwaite     sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
248e8198f6eSPeter Crosthwaite 
249e8198f6eSPeter Crosthwaite     memory_region_init_io(&s->mmio, obj, &eth_ops, s,
250e8198f6eSPeter Crosthwaite                           "xlnx.xps-ethernetlite", R_MAX * 4);
251e8198f6eSPeter Crosthwaite     sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
252d7e35d4aSPaolo Bonzini }
253d7e35d4aSPaolo Bonzini 
254d7e35d4aSPaolo Bonzini static Property xilinx_ethlite_properties[] = {
255d7e35d4aSPaolo Bonzini     DEFINE_PROP_UINT32("tx-ping-pong", struct xlx_ethlite, c_tx_pingpong, 1),
256d7e35d4aSPaolo Bonzini     DEFINE_PROP_UINT32("rx-ping-pong", struct xlx_ethlite, c_rx_pingpong, 1),
257d7e35d4aSPaolo Bonzini     DEFINE_NIC_PROPERTIES(struct xlx_ethlite, conf),
258d7e35d4aSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
259d7e35d4aSPaolo Bonzini };
260d7e35d4aSPaolo Bonzini 
xilinx_ethlite_class_init(ObjectClass * klass,void * data)261d7e35d4aSPaolo Bonzini static void xilinx_ethlite_class_init(ObjectClass *klass, void *data)
262d7e35d4aSPaolo Bonzini {
263d7e35d4aSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
264d7e35d4aSPaolo Bonzini 
265e8198f6eSPeter Crosthwaite     dc->realize = xilinx_ethlite_realize;
266*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, xilinx_ethlite_reset);
2674f67d30bSMarc-André Lureau     device_class_set_props(dc, xilinx_ethlite_properties);
268d7e35d4aSPaolo Bonzini }
269d7e35d4aSPaolo Bonzini 
270d7e35d4aSPaolo Bonzini static const TypeInfo xilinx_ethlite_info = {
27191a28042SAndreas Färber     .name          = TYPE_XILINX_ETHLITE,
272d7e35d4aSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
273d7e35d4aSPaolo Bonzini     .instance_size = sizeof(struct xlx_ethlite),
274e8198f6eSPeter Crosthwaite     .instance_init = xilinx_ethlite_init,
275d7e35d4aSPaolo Bonzini     .class_init    = xilinx_ethlite_class_init,
276d7e35d4aSPaolo Bonzini };
277d7e35d4aSPaolo Bonzini 
xilinx_ethlite_register_types(void)278d7e35d4aSPaolo Bonzini static void xilinx_ethlite_register_types(void)
279d7e35d4aSPaolo Bonzini {
280d7e35d4aSPaolo Bonzini     type_register_static(&xilinx_ethlite_info);
281d7e35d4aSPaolo Bonzini }
282d7e35d4aSPaolo Bonzini 
283d7e35d4aSPaolo Bonzini type_init(xilinx_ethlite_register_types)
284