root / hw / mipsnet.c @ 37952117
History | View | Annotate | Download (7.2 kB)
1 | 87ecb68b | pbrook | #include "hw.h" |
---|---|---|---|
2 | 87ecb68b | pbrook | #include "net.h" |
3 | 83818f7c | Hervé Poussineau | #include "trace.h" |
4 | d118d64a | Hervé Poussineau | #include "sysbus.h" |
5 | f0fc6f8f | ths | |
6 | f0fc6f8f | ths | /* MIPSnet register offsets */
|
7 | f0fc6f8f | ths | |
8 | f0fc6f8f | ths | #define MIPSNET_DEV_ID 0x00 |
9 | f0fc6f8f | ths | #define MIPSNET_BUSY 0x08 |
10 | f0fc6f8f | ths | #define MIPSNET_RX_DATA_COUNT 0x0c |
11 | f0fc6f8f | ths | #define MIPSNET_TX_DATA_COUNT 0x10 |
12 | f0fc6f8f | ths | #define MIPSNET_INT_CTL 0x14 |
13 | f0fc6f8f | ths | # define MIPSNET_INTCTL_TXDONE 0x00000001 |
14 | f0fc6f8f | ths | # define MIPSNET_INTCTL_RXDONE 0x00000002 |
15 | f0fc6f8f | ths | # define MIPSNET_INTCTL_TESTBIT 0x80000000 |
16 | f0fc6f8f | ths | #define MIPSNET_INTERRUPT_INFO 0x18 |
17 | f0fc6f8f | ths | #define MIPSNET_RX_DATA_BUFFER 0x1c |
18 | f0fc6f8f | ths | #define MIPSNET_TX_DATA_BUFFER 0x20 |
19 | f0fc6f8f | ths | |
20 | f0fc6f8f | ths | #define MAX_ETH_FRAME_SIZE 1514 |
21 | f0fc6f8f | ths | |
22 | f0fc6f8f | ths | typedef struct MIPSnetState { |
23 | d118d64a | Hervé Poussineau | SysBusDevice busdev; |
24 | d118d64a | Hervé Poussineau | |
25 | f0fc6f8f | ths | uint32_t busy; |
26 | f0fc6f8f | ths | uint32_t rx_count; |
27 | f0fc6f8f | ths | uint32_t rx_read; |
28 | f0fc6f8f | ths | uint32_t tx_count; |
29 | f0fc6f8f | ths | uint32_t tx_written; |
30 | f0fc6f8f | ths | uint32_t intctl; |
31 | f0fc6f8f | ths | uint8_t rx_buffer[MAX_ETH_FRAME_SIZE]; |
32 | f0fc6f8f | ths | uint8_t tx_buffer[MAX_ETH_FRAME_SIZE]; |
33 | d118d64a | Hervé Poussineau | MemoryRegion io; |
34 | f0fc6f8f | ths | qemu_irq irq; |
35 | 1f30d10a | Mark McLoughlin | NICState *nic; |
36 | 1f30d10a | Mark McLoughlin | NICConf conf; |
37 | f0fc6f8f | ths | } MIPSnetState; |
38 | f0fc6f8f | ths | |
39 | f0fc6f8f | ths | static void mipsnet_reset(MIPSnetState *s) |
40 | f0fc6f8f | ths | { |
41 | f0fc6f8f | ths | s->busy = 1;
|
42 | f0fc6f8f | ths | s->rx_count = 0;
|
43 | f0fc6f8f | ths | s->rx_read = 0;
|
44 | f0fc6f8f | ths | s->tx_count = 0;
|
45 | f0fc6f8f | ths | s->tx_written = 0;
|
46 | f0fc6f8f | ths | s->intctl = 0;
|
47 | f0fc6f8f | ths | memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
|
48 | f0fc6f8f | ths | memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
|
49 | f0fc6f8f | ths | } |
50 | f0fc6f8f | ths | |
51 | f0fc6f8f | ths | static void mipsnet_update_irq(MIPSnetState *s) |
52 | f0fc6f8f | ths | { |
53 | f0fc6f8f | ths | int isr = !!s->intctl;
|
54 | 83818f7c | Hervé Poussineau | trace_mipsnet_irq(isr, s->intctl); |
55 | f0fc6f8f | ths | qemu_set_irq(s->irq, isr); |
56 | f0fc6f8f | ths | } |
57 | f0fc6f8f | ths | |
58 | f0fc6f8f | ths | static int mipsnet_buffer_full(MIPSnetState *s) |
59 | f0fc6f8f | ths | { |
60 | f0fc6f8f | ths | if (s->rx_count >= MAX_ETH_FRAME_SIZE)
|
61 | f0fc6f8f | ths | return 1; |
62 | f0fc6f8f | ths | return 0; |
63 | f0fc6f8f | ths | } |
64 | f0fc6f8f | ths | |
65 | 1f30d10a | Mark McLoughlin | static int mipsnet_can_receive(VLANClientState *nc) |
66 | f0fc6f8f | ths | { |
67 | 1f30d10a | Mark McLoughlin | MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
68 | f0fc6f8f | ths | |
69 | f0fc6f8f | ths | if (s->busy)
|
70 | f0fc6f8f | ths | return 0; |
71 | f0fc6f8f | ths | return !mipsnet_buffer_full(s);
|
72 | f0fc6f8f | ths | } |
73 | f0fc6f8f | ths | |
74 | 1f30d10a | Mark McLoughlin | static ssize_t mipsnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size) |
75 | f0fc6f8f | ths | { |
76 | 1f30d10a | Mark McLoughlin | MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
77 | f0fc6f8f | ths | |
78 | 83818f7c | Hervé Poussineau | trace_mipsnet_receive(size); |
79 | 1f30d10a | Mark McLoughlin | if (!mipsnet_can_receive(nc))
|
80 | 4f1c942b | Mark McLoughlin | return -1; |
81 | f0fc6f8f | ths | |
82 | f0fc6f8f | ths | s->busy = 1;
|
83 | f0fc6f8f | ths | |
84 | f0fc6f8f | ths | /* Just accept everything. */
|
85 | f0fc6f8f | ths | |
86 | f0fc6f8f | ths | /* Write packet data. */
|
87 | f0fc6f8f | ths | memcpy(s->rx_buffer, buf, size); |
88 | f0fc6f8f | ths | |
89 | f0fc6f8f | ths | s->rx_count = size; |
90 | f0fc6f8f | ths | s->rx_read = 0;
|
91 | f0fc6f8f | ths | |
92 | f0fc6f8f | ths | /* Now we can signal we have received something. */
|
93 | f0fc6f8f | ths | s->intctl |= MIPSNET_INTCTL_RXDONE; |
94 | f0fc6f8f | ths | mipsnet_update_irq(s); |
95 | 4f1c942b | Mark McLoughlin | |
96 | 4f1c942b | Mark McLoughlin | return size;
|
97 | f0fc6f8f | ths | } |
98 | f0fc6f8f | ths | |
99 | d118d64a | Hervé Poussineau | static uint64_t mipsnet_ioport_read(void *opaque, target_phys_addr_t addr, |
100 | d118d64a | Hervé Poussineau | unsigned int size) |
101 | f0fc6f8f | ths | { |
102 | f0fc6f8f | ths | MIPSnetState *s = opaque; |
103 | f0fc6f8f | ths | int ret = 0; |
104 | f0fc6f8f | ths | |
105 | f0fc6f8f | ths | addr &= 0x3f;
|
106 | f0fc6f8f | ths | switch (addr) {
|
107 | f0fc6f8f | ths | case MIPSNET_DEV_ID:
|
108 | 9b595395 | aurel32 | ret = be32_to_cpu(0x4d495053); /* MIPS */ |
109 | f0fc6f8f | ths | break;
|
110 | f0fc6f8f | ths | case MIPSNET_DEV_ID + 4: |
111 | 9b595395 | aurel32 | ret = be32_to_cpu(0x4e455430); /* NET0 */ |
112 | f0fc6f8f | ths | break;
|
113 | f0fc6f8f | ths | case MIPSNET_BUSY:
|
114 | f0fc6f8f | ths | ret = s->busy; |
115 | f0fc6f8f | ths | break;
|
116 | f0fc6f8f | ths | case MIPSNET_RX_DATA_COUNT:
|
117 | f0fc6f8f | ths | ret = s->rx_count; |
118 | f0fc6f8f | ths | break;
|
119 | f0fc6f8f | ths | case MIPSNET_TX_DATA_COUNT:
|
120 | f0fc6f8f | ths | ret = s->tx_count; |
121 | f0fc6f8f | ths | break;
|
122 | f0fc6f8f | ths | case MIPSNET_INT_CTL:
|
123 | f0fc6f8f | ths | ret = s->intctl; |
124 | f0fc6f8f | ths | s->intctl &= ~MIPSNET_INTCTL_TESTBIT; |
125 | f0fc6f8f | ths | break;
|
126 | f0fc6f8f | ths | case MIPSNET_INTERRUPT_INFO:
|
127 | f0fc6f8f | ths | /* XXX: This seems to be a per-VPE interrupt number. */
|
128 | f0fc6f8f | ths | ret = 0;
|
129 | f0fc6f8f | ths | break;
|
130 | f0fc6f8f | ths | case MIPSNET_RX_DATA_BUFFER:
|
131 | f0fc6f8f | ths | if (s->rx_count) {
|
132 | f0fc6f8f | ths | s->rx_count--; |
133 | f0fc6f8f | ths | ret = s->rx_buffer[s->rx_read++]; |
134 | f0fc6f8f | ths | } |
135 | f0fc6f8f | ths | break;
|
136 | f0fc6f8f | ths | /* Reads as zero. */
|
137 | f0fc6f8f | ths | case MIPSNET_TX_DATA_BUFFER:
|
138 | f0fc6f8f | ths | default:
|
139 | f0fc6f8f | ths | break;
|
140 | f0fc6f8f | ths | } |
141 | 83818f7c | Hervé Poussineau | trace_mipsnet_read(addr, ret); |
142 | f0fc6f8f | ths | return ret;
|
143 | f0fc6f8f | ths | } |
144 | f0fc6f8f | ths | |
145 | d118d64a | Hervé Poussineau | static void mipsnet_ioport_write(void *opaque, target_phys_addr_t addr, |
146 | d118d64a | Hervé Poussineau | uint64_t val, unsigned int size) |
147 | f0fc6f8f | ths | { |
148 | f0fc6f8f | ths | MIPSnetState *s = opaque; |
149 | f0fc6f8f | ths | |
150 | f0fc6f8f | ths | addr &= 0x3f;
|
151 | 83818f7c | Hervé Poussineau | trace_mipsnet_write(addr, val); |
152 | f0fc6f8f | ths | switch (addr) {
|
153 | f0fc6f8f | ths | case MIPSNET_TX_DATA_COUNT:
|
154 | f0fc6f8f | ths | s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
|
155 | f0fc6f8f | ths | s->tx_written = 0;
|
156 | f0fc6f8f | ths | break;
|
157 | f0fc6f8f | ths | case MIPSNET_INT_CTL:
|
158 | f0fc6f8f | ths | if (val & MIPSNET_INTCTL_TXDONE) {
|
159 | f0fc6f8f | ths | s->intctl &= ~MIPSNET_INTCTL_TXDONE; |
160 | f0fc6f8f | ths | } else if (val & MIPSNET_INTCTL_RXDONE) { |
161 | f0fc6f8f | ths | s->intctl &= ~MIPSNET_INTCTL_RXDONE; |
162 | f0fc6f8f | ths | } else if (val & MIPSNET_INTCTL_TESTBIT) { |
163 | f0fc6f8f | ths | mipsnet_reset(s); |
164 | f0fc6f8f | ths | s->intctl |= MIPSNET_INTCTL_TESTBIT; |
165 | f0fc6f8f | ths | } else if (!val) { |
166 | f0fc6f8f | ths | /* ACK testbit interrupt, flag was cleared on read. */
|
167 | f0fc6f8f | ths | } |
168 | f0fc6f8f | ths | s->busy = !!s->intctl; |
169 | f0fc6f8f | ths | mipsnet_update_irq(s); |
170 | f0fc6f8f | ths | break;
|
171 | f0fc6f8f | ths | case MIPSNET_TX_DATA_BUFFER:
|
172 | f0fc6f8f | ths | s->tx_buffer[s->tx_written++] = val; |
173 | f0fc6f8f | ths | if (s->tx_written == s->tx_count) {
|
174 | f0fc6f8f | ths | /* Send buffer. */
|
175 | 83818f7c | Hervé Poussineau | trace_mipsnet_send(s->tx_count); |
176 | 1f30d10a | Mark McLoughlin | qemu_send_packet(&s->nic->nc, s->tx_buffer, s->tx_count); |
177 | f0fc6f8f | ths | s->tx_count = s->tx_written = 0;
|
178 | f0fc6f8f | ths | s->intctl |= MIPSNET_INTCTL_TXDONE; |
179 | f0fc6f8f | ths | s->busy = 1;
|
180 | f0fc6f8f | ths | mipsnet_update_irq(s); |
181 | f0fc6f8f | ths | } |
182 | f0fc6f8f | ths | break;
|
183 | f0fc6f8f | ths | /* Read-only registers */
|
184 | f0fc6f8f | ths | case MIPSNET_DEV_ID:
|
185 | f0fc6f8f | ths | case MIPSNET_BUSY:
|
186 | f0fc6f8f | ths | case MIPSNET_RX_DATA_COUNT:
|
187 | f0fc6f8f | ths | case MIPSNET_INTERRUPT_INFO:
|
188 | f0fc6f8f | ths | case MIPSNET_RX_DATA_BUFFER:
|
189 | f0fc6f8f | ths | default:
|
190 | f0fc6f8f | ths | break;
|
191 | f0fc6f8f | ths | } |
192 | f0fc6f8f | ths | } |
193 | f0fc6f8f | ths | |
194 | c7298ab2 | Juan Quintela | static const VMStateDescription vmstate_mipsnet = { |
195 | c7298ab2 | Juan Quintela | .name = "mipsnet",
|
196 | c7298ab2 | Juan Quintela | .version_id = 0,
|
197 | c7298ab2 | Juan Quintela | .minimum_version_id = 0,
|
198 | c7298ab2 | Juan Quintela | .minimum_version_id_old = 0,
|
199 | c7298ab2 | Juan Quintela | .fields = (VMStateField[]) { |
200 | c7298ab2 | Juan Quintela | VMSTATE_UINT32(busy, MIPSnetState), |
201 | c7298ab2 | Juan Quintela | VMSTATE_UINT32(rx_count, MIPSnetState), |
202 | c7298ab2 | Juan Quintela | VMSTATE_UINT32(rx_read, MIPSnetState), |
203 | c7298ab2 | Juan Quintela | VMSTATE_UINT32(tx_count, MIPSnetState), |
204 | c7298ab2 | Juan Quintela | VMSTATE_UINT32(tx_written, MIPSnetState), |
205 | c7298ab2 | Juan Quintela | VMSTATE_UINT32(intctl, MIPSnetState), |
206 | c7298ab2 | Juan Quintela | VMSTATE_BUFFER(rx_buffer, MIPSnetState), |
207 | c7298ab2 | Juan Quintela | VMSTATE_BUFFER(tx_buffer, MIPSnetState), |
208 | c7298ab2 | Juan Quintela | VMSTATE_END_OF_LIST() |
209 | c7298ab2 | Juan Quintela | } |
210 | c7298ab2 | Juan Quintela | }; |
211 | f0fc6f8f | ths | |
212 | 1f30d10a | Mark McLoughlin | static void mipsnet_cleanup(VLANClientState *nc) |
213 | b946a153 | aliguori | { |
214 | 1f30d10a | Mark McLoughlin | MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
215 | b946a153 | aliguori | |
216 | d118d64a | Hervé Poussineau | s->nic = NULL;
|
217 | b946a153 | aliguori | } |
218 | b946a153 | aliguori | |
219 | 1f30d10a | Mark McLoughlin | static NetClientInfo net_mipsnet_info = {
|
220 | 1f30d10a | Mark McLoughlin | .type = NET_CLIENT_TYPE_NIC, |
221 | 1f30d10a | Mark McLoughlin | .size = sizeof(NICState),
|
222 | 1f30d10a | Mark McLoughlin | .can_receive = mipsnet_can_receive, |
223 | 1f30d10a | Mark McLoughlin | .receive = mipsnet_receive, |
224 | 1f30d10a | Mark McLoughlin | .cleanup = mipsnet_cleanup, |
225 | 1f30d10a | Mark McLoughlin | }; |
226 | 1f30d10a | Mark McLoughlin | |
227 | a348f108 | Stefan Weil | static const MemoryRegionOps mipsnet_ioport_ops = { |
228 | d118d64a | Hervé Poussineau | .read = mipsnet_ioport_read, |
229 | d118d64a | Hervé Poussineau | .write = mipsnet_ioport_write, |
230 | d118d64a | Hervé Poussineau | .impl.min_access_size = 1,
|
231 | d118d64a | Hervé Poussineau | .impl.max_access_size = 4,
|
232 | d118d64a | Hervé Poussineau | }; |
233 | 0ae18cee | aliguori | |
234 | d118d64a | Hervé Poussineau | static int mipsnet_sysbus_init(SysBusDevice *dev) |
235 | d118d64a | Hervé Poussineau | { |
236 | d118d64a | Hervé Poussineau | MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev, dev); |
237 | f0fc6f8f | ths | |
238 | d118d64a | Hervé Poussineau | memory_region_init_io(&s->io, &mipsnet_ioport_ops, s, "mipsnet-io", 36); |
239 | 750ecd44 | Avi Kivity | sysbus_init_mmio(dev, &s->io); |
240 | d118d64a | Hervé Poussineau | sysbus_init_irq(dev, &s->irq); |
241 | f0fc6f8f | ths | |
242 | d118d64a | Hervé Poussineau | s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, |
243 | f79f2bfc | Anthony Liguori | object_get_typename(OBJECT(dev)), dev->qdev.id, s); |
244 | d118d64a | Hervé Poussineau | qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); |
245 | 1f30d10a | Mark McLoughlin | |
246 | d118d64a | Hervé Poussineau | return 0; |
247 | d118d64a | Hervé Poussineau | } |
248 | f0fc6f8f | ths | |
249 | d118d64a | Hervé Poussineau | static void mipsnet_sysbus_reset(DeviceState *dev) |
250 | d118d64a | Hervé Poussineau | { |
251 | d118d64a | Hervé Poussineau | MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev.qdev, dev); |
252 | d118d64a | Hervé Poussineau | mipsnet_reset(s); |
253 | d118d64a | Hervé Poussineau | } |
254 | 1f30d10a | Mark McLoughlin | |
255 | 999e12bb | Anthony Liguori | static Property mipsnet_properties[] = {
|
256 | 999e12bb | Anthony Liguori | DEFINE_NIC_PROPERTIES(MIPSnetState, conf), |
257 | 999e12bb | Anthony Liguori | DEFINE_PROP_END_OF_LIST(), |
258 | 999e12bb | Anthony Liguori | }; |
259 | 999e12bb | Anthony Liguori | |
260 | 999e12bb | Anthony Liguori | static void mipsnet_class_init(ObjectClass *klass, void *data) |
261 | 999e12bb | Anthony Liguori | { |
262 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
263 | 999e12bb | Anthony Liguori | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
264 | 999e12bb | Anthony Liguori | |
265 | 999e12bb | Anthony Liguori | k->init = mipsnet_sysbus_init; |
266 | 39bffca2 | Anthony Liguori | dc->desc = "MIPS Simulator network device";
|
267 | 39bffca2 | Anthony Liguori | dc->reset = mipsnet_sysbus_reset; |
268 | 39bffca2 | Anthony Liguori | dc->vmsd = &vmstate_mipsnet; |
269 | 39bffca2 | Anthony Liguori | dc->props = mipsnet_properties; |
270 | 999e12bb | Anthony Liguori | } |
271 | 999e12bb | Anthony Liguori | |
272 | 39bffca2 | Anthony Liguori | static TypeInfo mipsnet_info = {
|
273 | 39bffca2 | Anthony Liguori | .name = "mipsnet",
|
274 | 39bffca2 | Anthony Liguori | .parent = TYPE_SYS_BUS_DEVICE, |
275 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(MIPSnetState),
|
276 | 39bffca2 | Anthony Liguori | .class_init = mipsnet_class_init, |
277 | d118d64a | Hervé Poussineau | }; |
278 | f0fc6f8f | ths | |
279 | 83f7d43a | Andreas Färber | static void mipsnet_register_types(void) |
280 | d118d64a | Hervé Poussineau | { |
281 | 39bffca2 | Anthony Liguori | type_register_static(&mipsnet_info); |
282 | f0fc6f8f | ths | } |
283 | d118d64a | Hervé Poussineau | |
284 | 83f7d43a | Andreas Färber | type_init(mipsnet_register_types) |