xref: /openbmc/qemu/hw/net/mipsnet.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
1e8d40465SPeter Maydell #include "qemu/osdep.h"
264552b6bSMarkus Armbruster #include "hw/irq.h"
3a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
449ab747fSPaolo Bonzini #include "net/net.h"
50b8fa32fSMarkus Armbruster #include "qemu/module.h"
649ab747fSPaolo Bonzini #include "trace.h"
749ab747fSPaolo Bonzini #include "hw/sysbus.h"
8d6454270SMarkus Armbruster #include "migration/vmstate.h"
9db1015e9SEduardo Habkost #include "qom/object.h"
1049ab747fSPaolo Bonzini 
1149ab747fSPaolo Bonzini /* MIPSnet register offsets */
1249ab747fSPaolo Bonzini 
1349ab747fSPaolo Bonzini #define MIPSNET_DEV_ID          0x00
1449ab747fSPaolo Bonzini #define MIPSNET_BUSY            0x08
1549ab747fSPaolo Bonzini #define MIPSNET_RX_DATA_COUNT   0x0c
1649ab747fSPaolo Bonzini #define MIPSNET_TX_DATA_COUNT   0x10
1749ab747fSPaolo Bonzini #define MIPSNET_INT_CTL         0x14
1849ab747fSPaolo Bonzini # define MIPSNET_INTCTL_TXDONE          0x00000001
1949ab747fSPaolo Bonzini # define MIPSNET_INTCTL_RXDONE          0x00000002
2049ab747fSPaolo Bonzini # define MIPSNET_INTCTL_TESTBIT         0x80000000
2149ab747fSPaolo Bonzini #define MIPSNET_INTERRUPT_INFO  0x18
2249ab747fSPaolo Bonzini #define MIPSNET_RX_DATA_BUFFER  0x1c
2349ab747fSPaolo Bonzini #define MIPSNET_TX_DATA_BUFFER  0x20
2449ab747fSPaolo Bonzini 
2549ab747fSPaolo Bonzini #define MAX_ETH_FRAME_SIZE      1514
2649ab747fSPaolo Bonzini 
27a4dbb8bdSAndreas Färber #define TYPE_MIPS_NET "mipsnet"
288063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(MIPSnetState, MIPS_NET)
29a4dbb8bdSAndreas Färber 
30db1015e9SEduardo Habkost struct MIPSnetState {
31a4dbb8bdSAndreas Färber     SysBusDevice parent_obj;
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini     uint32_t busy;
3449ab747fSPaolo Bonzini     uint32_t rx_count;
3549ab747fSPaolo Bonzini     uint32_t rx_read;
3649ab747fSPaolo Bonzini     uint32_t tx_count;
3749ab747fSPaolo Bonzini     uint32_t tx_written;
3849ab747fSPaolo Bonzini     uint32_t intctl;
3949ab747fSPaolo Bonzini     uint8_t rx_buffer[MAX_ETH_FRAME_SIZE];
4049ab747fSPaolo Bonzini     uint8_t tx_buffer[MAX_ETH_FRAME_SIZE];
4149ab747fSPaolo Bonzini     MemoryRegion io;
4249ab747fSPaolo Bonzini     qemu_irq irq;
4349ab747fSPaolo Bonzini     NICState *nic;
4449ab747fSPaolo Bonzini     NICConf conf;
45db1015e9SEduardo Habkost };
4649ab747fSPaolo Bonzini 
mipsnet_reset(MIPSnetState * s)4749ab747fSPaolo Bonzini static void mipsnet_reset(MIPSnetState *s)
4849ab747fSPaolo Bonzini {
4949ab747fSPaolo Bonzini     s->busy = 1;
5049ab747fSPaolo Bonzini     s->rx_count = 0;
5149ab747fSPaolo Bonzini     s->rx_read = 0;
5249ab747fSPaolo Bonzini     s->tx_count = 0;
5349ab747fSPaolo Bonzini     s->tx_written = 0;
5449ab747fSPaolo Bonzini     s->intctl = 0;
5549ab747fSPaolo Bonzini     memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
5649ab747fSPaolo Bonzini     memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
5749ab747fSPaolo Bonzini }
5849ab747fSPaolo Bonzini 
mipsnet_update_irq(MIPSnetState * s)5949ab747fSPaolo Bonzini static void mipsnet_update_irq(MIPSnetState *s)
6049ab747fSPaolo Bonzini {
6149ab747fSPaolo Bonzini     int isr = !!s->intctl;
6249ab747fSPaolo Bonzini     trace_mipsnet_irq(isr, s->intctl);
6349ab747fSPaolo Bonzini     qemu_set_irq(s->irq, isr);
6449ab747fSPaolo Bonzini }
6549ab747fSPaolo Bonzini 
mipsnet_buffer_full(MIPSnetState * s)6649ab747fSPaolo Bonzini static int mipsnet_buffer_full(MIPSnetState *s)
6749ab747fSPaolo Bonzini {
6883aecbaaSFilip Bozuta     if (s->rx_count >= MAX_ETH_FRAME_SIZE) {
6949ab747fSPaolo Bonzini         return 1;
7083aecbaaSFilip Bozuta     }
7149ab747fSPaolo Bonzini     return 0;
7249ab747fSPaolo Bonzini }
7349ab747fSPaolo Bonzini 
mipsnet_can_receive(NetClientState * nc)7449ab747fSPaolo Bonzini static int mipsnet_can_receive(NetClientState *nc)
7549ab747fSPaolo Bonzini {
7649ab747fSPaolo Bonzini     MIPSnetState *s = qemu_get_nic_opaque(nc);
7749ab747fSPaolo Bonzini 
7883aecbaaSFilip Bozuta     if (s->busy) {
7949ab747fSPaolo Bonzini         return 0;
8083aecbaaSFilip Bozuta     }
8149ab747fSPaolo Bonzini     return !mipsnet_buffer_full(s);
8249ab747fSPaolo Bonzini }
8349ab747fSPaolo Bonzini 
mipsnet_receive(NetClientState * nc,const uint8_t * buf,size_t size)8483aecbaaSFilip Bozuta static ssize_t mipsnet_receive(NetClientState *nc,
8583aecbaaSFilip Bozuta                                const uint8_t *buf, size_t size)
8649ab747fSPaolo Bonzini {
8749ab747fSPaolo Bonzini     MIPSnetState *s = qemu_get_nic_opaque(nc);
8849ab747fSPaolo Bonzini 
8949ab747fSPaolo Bonzini     trace_mipsnet_receive(size);
9083aecbaaSFilip Bozuta     if (!mipsnet_can_receive(nc)) {
911dd58ae0SFam Zheng         return 0;
9283aecbaaSFilip Bozuta     }
9349ab747fSPaolo Bonzini 
943af9187fSPrasad J Pandit     if (size >= sizeof(s->rx_buffer)) {
953af9187fSPrasad J Pandit         return 0;
963af9187fSPrasad J Pandit     }
9749ab747fSPaolo Bonzini     s->busy = 1;
9849ab747fSPaolo Bonzini 
9949ab747fSPaolo Bonzini     /* Just accept everything. */
10049ab747fSPaolo Bonzini 
10149ab747fSPaolo Bonzini     /* Write packet data. */
10249ab747fSPaolo Bonzini     memcpy(s->rx_buffer, buf, size);
10349ab747fSPaolo Bonzini 
10449ab747fSPaolo Bonzini     s->rx_count = size;
10549ab747fSPaolo Bonzini     s->rx_read = 0;
10649ab747fSPaolo Bonzini 
10749ab747fSPaolo Bonzini     /* Now we can signal we have received something. */
10849ab747fSPaolo Bonzini     s->intctl |= MIPSNET_INTCTL_RXDONE;
10949ab747fSPaolo Bonzini     mipsnet_update_irq(s);
11049ab747fSPaolo Bonzini 
11149ab747fSPaolo Bonzini     return size;
11249ab747fSPaolo Bonzini }
11349ab747fSPaolo Bonzini 
mipsnet_ioport_read(void * opaque,hwaddr addr,unsigned int size)11449ab747fSPaolo Bonzini static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr,
11549ab747fSPaolo Bonzini                                     unsigned int size)
11649ab747fSPaolo Bonzini {
11749ab747fSPaolo Bonzini     MIPSnetState *s = opaque;
11849ab747fSPaolo Bonzini     int ret = 0;
11949ab747fSPaolo Bonzini 
12049ab747fSPaolo Bonzini     addr &= 0x3f;
12149ab747fSPaolo Bonzini     switch (addr) {
12249ab747fSPaolo Bonzini     case MIPSNET_DEV_ID:
12349ab747fSPaolo Bonzini         ret = be32_to_cpu(0x4d495053);          /* MIPS */
12449ab747fSPaolo Bonzini         break;
12549ab747fSPaolo Bonzini     case MIPSNET_DEV_ID + 4:
12649ab747fSPaolo Bonzini         ret = be32_to_cpu(0x4e455430);          /* NET0 */
12749ab747fSPaolo Bonzini         break;
12849ab747fSPaolo Bonzini     case MIPSNET_BUSY:
12949ab747fSPaolo Bonzini         ret = s->busy;
13049ab747fSPaolo Bonzini         break;
13149ab747fSPaolo Bonzini     case MIPSNET_RX_DATA_COUNT:
13249ab747fSPaolo Bonzini         ret = s->rx_count;
13349ab747fSPaolo Bonzini         break;
13449ab747fSPaolo Bonzini     case MIPSNET_TX_DATA_COUNT:
13549ab747fSPaolo Bonzini         ret = s->tx_count;
13649ab747fSPaolo Bonzini         break;
13749ab747fSPaolo Bonzini     case MIPSNET_INT_CTL:
13849ab747fSPaolo Bonzini         ret = s->intctl;
13949ab747fSPaolo Bonzini         s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
14049ab747fSPaolo Bonzini         break;
14149ab747fSPaolo Bonzini     case MIPSNET_INTERRUPT_INFO:
14249ab747fSPaolo Bonzini         /* XXX: This seems to be a per-VPE interrupt number. */
14349ab747fSPaolo Bonzini         ret = 0;
14449ab747fSPaolo Bonzini         break;
14549ab747fSPaolo Bonzini     case MIPSNET_RX_DATA_BUFFER:
14649ab747fSPaolo Bonzini         if (s->rx_count) {
14749ab747fSPaolo Bonzini             s->rx_count--;
14849ab747fSPaolo Bonzini             ret = s->rx_buffer[s->rx_read++];
1491dd58ae0SFam Zheng             if (mipsnet_can_receive(s->nic->ncs)) {
1501dd58ae0SFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
1511dd58ae0SFam Zheng             }
15249ab747fSPaolo Bonzini         }
15349ab747fSPaolo Bonzini         break;
15449ab747fSPaolo Bonzini     /* Reads as zero. */
15549ab747fSPaolo Bonzini     case MIPSNET_TX_DATA_BUFFER:
15649ab747fSPaolo Bonzini     default:
15749ab747fSPaolo Bonzini         break;
15849ab747fSPaolo Bonzini     }
15949ab747fSPaolo Bonzini     trace_mipsnet_read(addr, ret);
16049ab747fSPaolo Bonzini     return ret;
16149ab747fSPaolo Bonzini }
16249ab747fSPaolo Bonzini 
mipsnet_ioport_write(void * opaque,hwaddr addr,uint64_t val,unsigned int size)16349ab747fSPaolo Bonzini static void mipsnet_ioport_write(void *opaque, hwaddr addr,
16449ab747fSPaolo Bonzini                                  uint64_t val, unsigned int size)
16549ab747fSPaolo Bonzini {
16649ab747fSPaolo Bonzini     MIPSnetState *s = opaque;
16749ab747fSPaolo Bonzini 
16849ab747fSPaolo Bonzini     addr &= 0x3f;
16949ab747fSPaolo Bonzini     trace_mipsnet_write(addr, val);
17049ab747fSPaolo Bonzini     switch (addr) {
17149ab747fSPaolo Bonzini     case MIPSNET_TX_DATA_COUNT:
17249ab747fSPaolo Bonzini         s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
17349ab747fSPaolo Bonzini         s->tx_written = 0;
17449ab747fSPaolo Bonzini         break;
17549ab747fSPaolo Bonzini     case MIPSNET_INT_CTL:
17649ab747fSPaolo Bonzini         if (val & MIPSNET_INTCTL_TXDONE) {
17749ab747fSPaolo Bonzini             s->intctl &= ~MIPSNET_INTCTL_TXDONE;
17849ab747fSPaolo Bonzini         } else if (val & MIPSNET_INTCTL_RXDONE) {
17949ab747fSPaolo Bonzini             s->intctl &= ~MIPSNET_INTCTL_RXDONE;
18049ab747fSPaolo Bonzini         } else if (val & MIPSNET_INTCTL_TESTBIT) {
18149ab747fSPaolo Bonzini             mipsnet_reset(s);
18249ab747fSPaolo Bonzini             s->intctl |= MIPSNET_INTCTL_TESTBIT;
18349ab747fSPaolo Bonzini         } else if (!val) {
18449ab747fSPaolo Bonzini             /* ACK testbit interrupt, flag was cleared on read. */
18549ab747fSPaolo Bonzini         }
18649ab747fSPaolo Bonzini         s->busy = !!s->intctl;
18749ab747fSPaolo Bonzini         mipsnet_update_irq(s);
1881dd58ae0SFam Zheng         if (mipsnet_can_receive(s->nic->ncs)) {
1891dd58ae0SFam Zheng             qemu_flush_queued_packets(qemu_get_queue(s->nic));
1901dd58ae0SFam Zheng         }
19149ab747fSPaolo Bonzini         break;
19249ab747fSPaolo Bonzini     case MIPSNET_TX_DATA_BUFFER:
19349ab747fSPaolo Bonzini         s->tx_buffer[s->tx_written++] = val;
194d88d3a09SPrasad J Pandit         if ((s->tx_written >= MAX_ETH_FRAME_SIZE)
195d88d3a09SPrasad J Pandit             || (s->tx_written == s->tx_count)) {
19649ab747fSPaolo Bonzini             /* Send buffer. */
197d88d3a09SPrasad J Pandit             trace_mipsnet_send(s->tx_written);
198d88d3a09SPrasad J Pandit             qemu_send_packet(qemu_get_queue(s->nic),
199d88d3a09SPrasad J Pandit                                 s->tx_buffer, s->tx_written);
20049ab747fSPaolo Bonzini             s->tx_count = s->tx_written = 0;
20149ab747fSPaolo Bonzini             s->intctl |= MIPSNET_INTCTL_TXDONE;
20249ab747fSPaolo Bonzini             s->busy = 1;
20349ab747fSPaolo Bonzini             mipsnet_update_irq(s);
20449ab747fSPaolo Bonzini         }
20549ab747fSPaolo Bonzini         break;
20649ab747fSPaolo Bonzini     /* Read-only registers */
20749ab747fSPaolo Bonzini     case MIPSNET_DEV_ID:
20849ab747fSPaolo Bonzini     case MIPSNET_BUSY:
20949ab747fSPaolo Bonzini     case MIPSNET_RX_DATA_COUNT:
21049ab747fSPaolo Bonzini     case MIPSNET_INTERRUPT_INFO:
21149ab747fSPaolo Bonzini     case MIPSNET_RX_DATA_BUFFER:
21249ab747fSPaolo Bonzini     default:
21349ab747fSPaolo Bonzini         break;
21449ab747fSPaolo Bonzini     }
21549ab747fSPaolo Bonzini }
21649ab747fSPaolo Bonzini 
21749ab747fSPaolo Bonzini static const VMStateDescription vmstate_mipsnet = {
21849ab747fSPaolo Bonzini     .name = "mipsnet",
21949ab747fSPaolo Bonzini     .version_id = 0,
22049ab747fSPaolo Bonzini     .minimum_version_id = 0,
2211de81b42SRichard Henderson     .fields = (const VMStateField[]) {
22249ab747fSPaolo Bonzini         VMSTATE_UINT32(busy, MIPSnetState),
22349ab747fSPaolo Bonzini         VMSTATE_UINT32(rx_count, MIPSnetState),
22449ab747fSPaolo Bonzini         VMSTATE_UINT32(rx_read, MIPSnetState),
22549ab747fSPaolo Bonzini         VMSTATE_UINT32(tx_count, MIPSnetState),
22649ab747fSPaolo Bonzini         VMSTATE_UINT32(tx_written, MIPSnetState),
22749ab747fSPaolo Bonzini         VMSTATE_UINT32(intctl, MIPSnetState),
22849ab747fSPaolo Bonzini         VMSTATE_BUFFER(rx_buffer, MIPSnetState),
22949ab747fSPaolo Bonzini         VMSTATE_BUFFER(tx_buffer, MIPSnetState),
23049ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
23149ab747fSPaolo Bonzini     }
23249ab747fSPaolo Bonzini };
23349ab747fSPaolo Bonzini 
23449ab747fSPaolo Bonzini static NetClientInfo net_mipsnet_info = {
235f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
23649ab747fSPaolo Bonzini     .size = sizeof(NICState),
23749ab747fSPaolo Bonzini     .receive = mipsnet_receive,
23849ab747fSPaolo Bonzini };
23949ab747fSPaolo Bonzini 
24049ab747fSPaolo Bonzini static const MemoryRegionOps mipsnet_ioport_ops = {
24149ab747fSPaolo Bonzini     .read = mipsnet_ioport_read,
24249ab747fSPaolo Bonzini     .write = mipsnet_ioport_write,
24349ab747fSPaolo Bonzini     .impl.min_access_size = 1,
24449ab747fSPaolo Bonzini     .impl.max_access_size = 4,
24549ab747fSPaolo Bonzini };
24649ab747fSPaolo Bonzini 
mipsnet_realize(DeviceState * dev,Error ** errp)24704cb1572SCédric Le Goater static void mipsnet_realize(DeviceState *dev, Error **errp)
24849ab747fSPaolo Bonzini {
24904cb1572SCédric Le Goater     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
250a4dbb8bdSAndreas Färber     MIPSnetState *s = MIPS_NET(dev);
25149ab747fSPaolo Bonzini 
252eedfac6fSPaolo Bonzini     memory_region_init_io(&s->io, OBJECT(dev), &mipsnet_ioport_ops, s,
253eedfac6fSPaolo Bonzini                           "mipsnet-io", 36);
254a4dbb8bdSAndreas Färber     sysbus_init_mmio(sbd, &s->io);
255a4dbb8bdSAndreas Färber     sysbus_init_irq(sbd, &s->irq);
25649ab747fSPaolo Bonzini 
25749ab747fSPaolo Bonzini     s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
2587d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), dev->id,
2597d0fefdfSAkihiko Odaki                           &dev->mem_reentrancy_guard, s);
26049ab747fSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
26149ab747fSPaolo Bonzini }
26249ab747fSPaolo Bonzini 
mipsnet_sysbus_reset(DeviceState * dev)26349ab747fSPaolo Bonzini static void mipsnet_sysbus_reset(DeviceState *dev)
26449ab747fSPaolo Bonzini {
265a4dbb8bdSAndreas Färber     MIPSnetState *s = MIPS_NET(dev);
26649ab747fSPaolo Bonzini     mipsnet_reset(s);
26749ab747fSPaolo Bonzini }
26849ab747fSPaolo Bonzini 
26949ab747fSPaolo Bonzini static Property mipsnet_properties[] = {
27049ab747fSPaolo Bonzini     DEFINE_NIC_PROPERTIES(MIPSnetState, conf),
27149ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
27249ab747fSPaolo Bonzini };
27349ab747fSPaolo Bonzini 
mipsnet_class_init(ObjectClass * klass,void * data)27449ab747fSPaolo Bonzini static void mipsnet_class_init(ObjectClass *klass, void *data)
27549ab747fSPaolo Bonzini {
27649ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
27749ab747fSPaolo Bonzini 
27804cb1572SCédric Le Goater     dc->realize = mipsnet_realize;
279125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
28049ab747fSPaolo Bonzini     dc->desc = "MIPS Simulator network device";
281*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, mipsnet_sysbus_reset);
28249ab747fSPaolo Bonzini     dc->vmsd = &vmstate_mipsnet;
2834f67d30bSMarc-André Lureau     device_class_set_props(dc, mipsnet_properties);
28449ab747fSPaolo Bonzini }
28549ab747fSPaolo Bonzini 
28649ab747fSPaolo Bonzini static const TypeInfo mipsnet_info = {
287a4dbb8bdSAndreas Färber     .name          = TYPE_MIPS_NET,
28849ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
28949ab747fSPaolo Bonzini     .instance_size = sizeof(MIPSnetState),
29049ab747fSPaolo Bonzini     .class_init    = mipsnet_class_init,
29149ab747fSPaolo Bonzini };
29249ab747fSPaolo Bonzini 
mipsnet_register_types(void)29349ab747fSPaolo Bonzini static void mipsnet_register_types(void)
29449ab747fSPaolo Bonzini {
29549ab747fSPaolo Bonzini     type_register_static(&mipsnet_info);
29649ab747fSPaolo Bonzini }
29749ab747fSPaolo Bonzini 
29849ab747fSPaolo Bonzini type_init(mipsnet_register_types)
299