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