mipsnet.c 7.4 KB
Newer Older
P
Peter Maydell 已提交
1
#include "qemu/osdep.h"
2
#include "hw/hw.h"
P
Paolo Bonzini 已提交
3
#include "net/net.h"
H
Hervé Poussineau 已提交
4
#include "trace.h"
5
#include "hw/sysbus.h"
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

/* MIPSnet register offsets */

#define MIPSNET_DEV_ID		0x00
#define MIPSNET_BUSY		0x08
#define MIPSNET_RX_DATA_COUNT	0x0c
#define MIPSNET_TX_DATA_COUNT	0x10
#define MIPSNET_INT_CTL		0x14
# define MIPSNET_INTCTL_TXDONE		0x00000001
# define MIPSNET_INTCTL_RXDONE		0x00000002
# define MIPSNET_INTCTL_TESTBIT		0x80000000
#define MIPSNET_INTERRUPT_INFO	0x18
#define MIPSNET_RX_DATA_BUFFER	0x1c
#define MIPSNET_TX_DATA_BUFFER	0x20

#define MAX_ETH_FRAME_SIZE	1514

A
Andreas Färber 已提交
23 24 25
#define TYPE_MIPS_NET "mipsnet"
#define MIPS_NET(obj) OBJECT_CHECK(MIPSnetState, (obj), TYPE_MIPS_NET)

26
typedef struct MIPSnetState {
A
Andreas Färber 已提交
27
    SysBusDevice parent_obj;
H
Hervé Poussineau 已提交
28

29 30 31 32 33 34 35 36
    uint32_t busy;
    uint32_t rx_count;
    uint32_t rx_read;
    uint32_t tx_count;
    uint32_t tx_written;
    uint32_t intctl;
    uint8_t rx_buffer[MAX_ETH_FRAME_SIZE];
    uint8_t tx_buffer[MAX_ETH_FRAME_SIZE];
H
Hervé Poussineau 已提交
37
    MemoryRegion io;
38
    qemu_irq irq;
M
Mark McLoughlin 已提交
39 40
    NICState *nic;
    NICConf conf;
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
} MIPSnetState;

static void mipsnet_reset(MIPSnetState *s)
{
    s->busy = 1;
    s->rx_count = 0;
    s->rx_read = 0;
    s->tx_count = 0;
    s->tx_written = 0;
    s->intctl = 0;
    memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
    memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
}

static void mipsnet_update_irq(MIPSnetState *s)
{
    int isr = !!s->intctl;
H
Hervé Poussineau 已提交
58
    trace_mipsnet_irq(isr, s->intctl);
59 60 61 62 63 64 65 66 67 68
    qemu_set_irq(s->irq, isr);
}

static int mipsnet_buffer_full(MIPSnetState *s)
{
    if (s->rx_count >= MAX_ETH_FRAME_SIZE)
        return 1;
    return 0;
}

69
static int mipsnet_can_receive(NetClientState *nc)
70
{
J
Jason Wang 已提交
71
    MIPSnetState *s = qemu_get_nic_opaque(nc);
72 73 74 75 76 77

    if (s->busy)
        return 0;
    return !mipsnet_buffer_full(s);
}

78
static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
79
{
J
Jason Wang 已提交
80
    MIPSnetState *s = qemu_get_nic_opaque(nc);
81

H
Hervé Poussineau 已提交
82
    trace_mipsnet_receive(size);
M
Mark McLoughlin 已提交
83
    if (!mipsnet_can_receive(nc))
84
        return 0;
85 86 87 88 89 90 91 92 93 94 95 96 97 98

    s->busy = 1;

    /* Just accept everything. */

    /* Write packet data. */
    memcpy(s->rx_buffer, buf, size);

    s->rx_count = size;
    s->rx_read = 0;

    /* Now we can signal we have received something. */
    s->intctl |= MIPSNET_INTCTL_RXDONE;
    mipsnet_update_irq(s);
99 100

    return size;
101 102
}

A
Avi Kivity 已提交
103
static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr,
H
Hervé Poussineau 已提交
104
                                    unsigned int size)
105 106 107 108 109 110 111
{
    MIPSnetState *s = opaque;
    int ret = 0;

    addr &= 0x3f;
    switch (addr) {
    case MIPSNET_DEV_ID:
112
	ret = be32_to_cpu(0x4d495053);		/* MIPS */
113 114
        break;
    case MIPSNET_DEV_ID + 4:
115
	ret = be32_to_cpu(0x4e455430);		/* NET0 */
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
        break;
    case MIPSNET_BUSY:
	ret = s->busy;
        break;
    case MIPSNET_RX_DATA_COUNT:
	ret = s->rx_count;
        break;
    case MIPSNET_TX_DATA_COUNT:
	ret = s->tx_count;
        break;
    case MIPSNET_INT_CTL:
	ret = s->intctl;
        s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
        break;
    case MIPSNET_INTERRUPT_INFO:
        /* XXX: This seems to be a per-VPE interrupt number. */
	ret = 0;
        break;
    case MIPSNET_RX_DATA_BUFFER:
        if (s->rx_count) {
            s->rx_count--;
            ret = s->rx_buffer[s->rx_read++];
138 139 140
            if (mipsnet_can_receive(s->nic->ncs)) {
                qemu_flush_queued_packets(qemu_get_queue(s->nic));
            }
141 142 143 144 145 146 147
        }
        break;
    /* Reads as zero. */
    case MIPSNET_TX_DATA_BUFFER:
    default:
        break;
    }
H
Hervé Poussineau 已提交
148
    trace_mipsnet_read(addr, ret);
149 150 151
    return ret;
}

A
Avi Kivity 已提交
152
static void mipsnet_ioport_write(void *opaque, hwaddr addr,
H
Hervé Poussineau 已提交
153
                                 uint64_t val, unsigned int size)
154 155 156 157
{
    MIPSnetState *s = opaque;

    addr &= 0x3f;
H
Hervé Poussineau 已提交
158
    trace_mipsnet_write(addr, val);
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    switch (addr) {
    case MIPSNET_TX_DATA_COUNT:
	s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
        s->tx_written = 0;
        break;
    case MIPSNET_INT_CTL:
        if (val & MIPSNET_INTCTL_TXDONE) {
            s->intctl &= ~MIPSNET_INTCTL_TXDONE;
        } else if (val & MIPSNET_INTCTL_RXDONE) {
            s->intctl &= ~MIPSNET_INTCTL_RXDONE;
        } else if (val & MIPSNET_INTCTL_TESTBIT) {
            mipsnet_reset(s);
            s->intctl |= MIPSNET_INTCTL_TESTBIT;
        } else if (!val) {
            /* ACK testbit interrupt, flag was cleared on read. */
        }
        s->busy = !!s->intctl;
        mipsnet_update_irq(s);
177 178 179
        if (mipsnet_can_receive(s->nic->ncs)) {
            qemu_flush_queued_packets(qemu_get_queue(s->nic));
        }
180 181 182 183 184
        break;
    case MIPSNET_TX_DATA_BUFFER:
        s->tx_buffer[s->tx_written++] = val;
        if (s->tx_written == s->tx_count) {
            /* Send buffer. */
H
Hervé Poussineau 已提交
185
            trace_mipsnet_send(s->tx_count);
J
Jason Wang 已提交
186
            qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count);
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
            s->tx_count = s->tx_written = 0;
            s->intctl |= MIPSNET_INTCTL_TXDONE;
            s->busy = 1;
            mipsnet_update_irq(s);
        }
        break;
    /* Read-only registers */
    case MIPSNET_DEV_ID:
    case MIPSNET_BUSY:
    case MIPSNET_RX_DATA_COUNT:
    case MIPSNET_INTERRUPT_INFO:
    case MIPSNET_RX_DATA_BUFFER:
    default:
        break;
    }
}

J
Juan Quintela 已提交
204 205 206 207
static const VMStateDescription vmstate_mipsnet = {
    .name = "mipsnet",
    .version_id = 0,
    .minimum_version_id = 0,
208
    .fields = (VMStateField[]) {
J
Juan Quintela 已提交
209 210 211 212 213 214 215 216 217 218 219
        VMSTATE_UINT32(busy, MIPSnetState),
        VMSTATE_UINT32(rx_count, MIPSnetState),
        VMSTATE_UINT32(rx_read, MIPSnetState),
        VMSTATE_UINT32(tx_count, MIPSnetState),
        VMSTATE_UINT32(tx_written, MIPSnetState),
        VMSTATE_UINT32(intctl, MIPSnetState),
        VMSTATE_BUFFER(rx_buffer, MIPSnetState),
        VMSTATE_BUFFER(tx_buffer, MIPSnetState),
        VMSTATE_END_OF_LIST()
    }
};
220

M
Mark McLoughlin 已提交
221
static NetClientInfo net_mipsnet_info = {
222
    .type = NET_CLIENT_OPTIONS_KIND_NIC,
M
Mark McLoughlin 已提交
223 224 225 226
    .size = sizeof(NICState),
    .receive = mipsnet_receive,
};

227
static const MemoryRegionOps mipsnet_ioport_ops = {
H
Hervé Poussineau 已提交
228 229 230 231 232
    .read = mipsnet_ioport_read,
    .write = mipsnet_ioport_write,
    .impl.min_access_size = 1,
    .impl.max_access_size = 4,
};
233

A
Andreas Färber 已提交
234
static int mipsnet_sysbus_init(SysBusDevice *sbd)
H
Hervé Poussineau 已提交
235
{
A
Andreas Färber 已提交
236 237
    DeviceState *dev = DEVICE(sbd);
    MIPSnetState *s = MIPS_NET(dev);
238

239 240
    memory_region_init_io(&s->io, OBJECT(dev), &mipsnet_ioport_ops, s,
                          "mipsnet-io", 36);
A
Andreas Färber 已提交
241 242
    sysbus_init_mmio(sbd, &s->io);
    sysbus_init_irq(sbd, &s->irq);
243

H
Hervé Poussineau 已提交
244
    s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
A
Andreas Färber 已提交
245
                          object_get_typename(OBJECT(dev)), dev->id, s);
J
Jason Wang 已提交
246
    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
M
Mark McLoughlin 已提交
247

H
Hervé Poussineau 已提交
248 249
    return 0;
}
250

H
Hervé Poussineau 已提交
251 252
static void mipsnet_sysbus_reset(DeviceState *dev)
{
A
Andreas Färber 已提交
253
    MIPSnetState *s = MIPS_NET(dev);
H
Hervé Poussineau 已提交
254 255
    mipsnet_reset(s);
}
M
Mark McLoughlin 已提交
256

257 258 259 260 261 262 263
static Property mipsnet_properties[] = {
    DEFINE_NIC_PROPERTIES(MIPSnetState, conf),
    DEFINE_PROP_END_OF_LIST(),
};

static void mipsnet_class_init(ObjectClass *klass, void *data)
{
264
    DeviceClass *dc = DEVICE_CLASS(klass);
265 266 267
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

    k->init = mipsnet_sysbus_init;
268
    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
269 270 271 272
    dc->desc = "MIPS Simulator network device";
    dc->reset = mipsnet_sysbus_reset;
    dc->vmsd = &vmstate_mipsnet;
    dc->props = mipsnet_properties;
273 274
}

275
static const TypeInfo mipsnet_info = {
A
Andreas Färber 已提交
276
    .name          = TYPE_MIPS_NET,
277 278 279
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(MIPSnetState),
    .class_init    = mipsnet_class_init,
H
Hervé Poussineau 已提交
280
};
281

A
Andreas Färber 已提交
282
static void mipsnet_register_types(void)
H
Hervé Poussineau 已提交
283
{
284
    type_register_static(&mipsnet_info);
285
}
H
Hervé Poussineau 已提交
286

A
Andreas Färber 已提交
287
type_init(mipsnet_register_types)