mipsnet.c 7.4 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

/* 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 已提交
22 23 24
#define TYPE_MIPS_NET "mipsnet"
#define MIPS_NET(obj) OBJECT_CHECK(MIPSnetState, (obj), TYPE_MIPS_NET)

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

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

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

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

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

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

    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);
98 99

    return size;
100 101
}

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

    addr &= 0x3f;
    switch (addr) {
    case MIPSNET_DEV_ID:
111
	ret = be32_to_cpu(0x4d495053);		/* MIPS */
112 113
        break;
    case MIPSNET_DEV_ID + 4:
114
	ret = be32_to_cpu(0x4e455430);		/* NET0 */
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 141 142 143
        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 已提交
144
    trace_mipsnet_read(addr, ret);
145 146 147
    return ret;
}

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

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

215
static void mipsnet_cleanup(NetClientState *nc)
216
{
J
Jason Wang 已提交
217
    MIPSnetState *s = qemu_get_nic_opaque(nc);
218

H
Hervé Poussineau 已提交
219
    s->nic = NULL;
220 221
}

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

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

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

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

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

H
Hervé Poussineau 已提交
251 252
    return 0;
}
253

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

260 261 262 263 264 265 266
static Property mipsnet_properties[] = {
    DEFINE_NIC_PROPERTIES(MIPSnetState, conf),
    DEFINE_PROP_END_OF_LIST(),
};

static void mipsnet_class_init(ObjectClass *klass, void *data)
{
267
    DeviceClass *dc = DEVICE_CLASS(klass);
268 269 270
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);

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

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

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

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