mipsnet.c 7.2 KB
Newer Older
1
#include "hw/hw.h"
P
Paolo Bonzini 已提交
2
#include "net/net.h"
H
Hervé Poussineau 已提交
3
#include "trace.h"
4
#include "hw/sysbus.h"
5 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

typedef struct MIPSnetState {
H
Hervé Poussineau 已提交
23 24
    SysBusDevice busdev;

25 26 27 28 29 30 31 32
    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 已提交
33
    MemoryRegion io;
34
    qemu_irq irq;
M
Mark McLoughlin 已提交
35 36
    NICState *nic;
    NICConf conf;
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
} 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 已提交
54
    trace_mipsnet_irq(isr, s->intctl);
55 56 57 58 59 60 61 62 63 64
    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;
}

65
static int mipsnet_can_receive(NetClientState *nc)
66
{
J
Jason Wang 已提交
67
    MIPSnetState *s = qemu_get_nic_opaque(nc);
68 69 70 71 72 73

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

74
static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
75
{
J
Jason Wang 已提交
76
    MIPSnetState *s = qemu_get_nic_opaque(nc);
77

H
Hervé Poussineau 已提交
78
    trace_mipsnet_receive(size);
M
Mark McLoughlin 已提交
79
    if (!mipsnet_can_receive(nc))
80
        return -1;
81 82 83 84 85 86 87 88 89 90 91 92 93 94

    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);
95 96

    return size;
97 98
}

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

    addr &= 0x3f;
    switch (addr) {
    case MIPSNET_DEV_ID:
108
	ret = be32_to_cpu(0x4d495053);		/* MIPS */
109 110
        break;
    case MIPSNET_DEV_ID + 4:
111
	ret = be32_to_cpu(0x4e455430);		/* NET0 */
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
        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++];
        }
        break;
    /* Reads as zero. */
    case MIPSNET_TX_DATA_BUFFER:
    default:
        break;
    }
H
Hervé Poussineau 已提交
141
    trace_mipsnet_read(addr, ret);
142 143 144
    return ret;
}

A
Avi Kivity 已提交
145
static void mipsnet_ioport_write(void *opaque, hwaddr addr,
H
Hervé Poussineau 已提交
146
                                 uint64_t val, unsigned int size)
147 148 149 150
{
    MIPSnetState *s = opaque;

    addr &= 0x3f;
H
Hervé Poussineau 已提交
151
    trace_mipsnet_write(addr, val);
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    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);
        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 已提交
175
            trace_mipsnet_send(s->tx_count);
J
Jason Wang 已提交
176
            qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count);
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
            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 已提交
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
static const VMStateDescription vmstate_mipsnet = {
    .name = "mipsnet",
    .version_id = 0,
    .minimum_version_id = 0,
    .minimum_version_id_old = 0,
    .fields      = (VMStateField[]) {
        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()
    }
};
211

212
static void mipsnet_cleanup(NetClientState *nc)
213
{
J
Jason Wang 已提交
214
    MIPSnetState *s = qemu_get_nic_opaque(nc);
215

H
Hervé Poussineau 已提交
216
    s->nic = NULL;
217 218
}

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

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

H
Hervé Poussineau 已提交
234 235 236
static int mipsnet_sysbus_init(SysBusDevice *dev)
{
    MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev, dev);
237

238
    memory_region_init_io(&s->io, NULL, &mipsnet_ioport_ops, s, "mipsnet-io", 36);
239
    sysbus_init_mmio(dev, &s->io);
H
Hervé Poussineau 已提交
240
    sysbus_init_irq(dev, &s->irq);
241

H
Hervé Poussineau 已提交
242
    s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
243
                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
J
Jason Wang 已提交
244
    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
M
Mark McLoughlin 已提交
245

H
Hervé Poussineau 已提交
246 247
    return 0;
}
248

H
Hervé Poussineau 已提交
249 250 251 252 253
static void mipsnet_sysbus_reset(DeviceState *dev)
{
    MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev.qdev, dev);
    mipsnet_reset(s);
}
M
Mark McLoughlin 已提交
254

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

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

    k->init = mipsnet_sysbus_init;
266 267 268 269
    dc->desc = "MIPS Simulator network device";
    dc->reset = mipsnet_sysbus_reset;
    dc->vmsd = &vmstate_mipsnet;
    dc->props = mipsnet_properties;
270 271
}

272
static const TypeInfo mipsnet_info = {
273 274 275 276
    .name          = "mipsnet",
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(MIPSnetState),
    .class_init    = mipsnet_class_init,
H
Hervé Poussineau 已提交
277
};
278

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

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