root / hw / opencores_eth.c @ 0cdd3d14
History | View | Annotate | Download (18.8 kB)
1 |
/*
|
---|---|
2 |
* OpenCores Ethernet MAC 10/100 + subset of
|
3 |
* National Semiconductors DP83848C 10/100 PHY
|
4 |
*
|
5 |
* http://opencores.org/svnget,ethmac?file=%2Ftrunk%2F%2Fdoc%2Feth_speci.pdf
|
6 |
* http://cache.national.com/ds/DP/DP83848C.pdf
|
7 |
*
|
8 |
* Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
|
9 |
* All rights reserved.
|
10 |
*
|
11 |
* Redistribution and use in source and binary forms, with or without
|
12 |
* modification, are permitted provided that the following conditions are met:
|
13 |
* * Redistributions of source code must retain the above copyright
|
14 |
* notice, this list of conditions and the following disclaimer.
|
15 |
* * Redistributions in binary form must reproduce the above copyright
|
16 |
* notice, this list of conditions and the following disclaimer in the
|
17 |
* documentation and/or other materials provided with the distribution.
|
18 |
* * Neither the name of the Open Source and Linux Lab nor the
|
19 |
* names of its contributors may be used to endorse or promote products
|
20 |
* derived from this software without specific prior written permission.
|
21 |
*
|
22 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23 |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
26 |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
27 |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
28 |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
29 |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
30 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
31 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32 |
*/
|
33 |
|
34 |
#include "hw.h" |
35 |
#include "sysbus.h" |
36 |
#include "net.h" |
37 |
#include "sysemu.h" |
38 |
#include "trace.h" |
39 |
|
40 |
/* RECSMALL is not used because it breaks tap networking in linux:
|
41 |
* incoming ARP responses are too short
|
42 |
*/
|
43 |
#undef USE_RECSMALL
|
44 |
|
45 |
#define GET_FIELD(v, field) (((v) & (field)) >> (field ## _LBN)) |
46 |
#define GET_REGBIT(s, reg, field) ((s)->regs[reg] & (reg ## _ ## field)) |
47 |
#define GET_REGFIELD(s, reg, field) \
|
48 |
GET_FIELD((s)->regs[reg], reg ## _ ## field) |
49 |
|
50 |
#define SET_FIELD(v, field, data) \
|
51 |
((v) = (((v) & ~(field)) | (((data) << (field ## _LBN)) & (field)))) |
52 |
#define SET_REGFIELD(s, reg, field, data) \
|
53 |
SET_FIELD((s)->regs[reg], reg ## _ ## field, data) |
54 |
|
55 |
/* PHY MII registers */
|
56 |
enum {
|
57 |
MII_BMCR, |
58 |
MII_BMSR, |
59 |
MII_PHYIDR1, |
60 |
MII_PHYIDR2, |
61 |
MII_ANAR, |
62 |
MII_ANLPAR, |
63 |
MII_REG_MAX = 16,
|
64 |
}; |
65 |
|
66 |
typedef struct Mii { |
67 |
uint16_t regs[MII_REG_MAX]; |
68 |
bool link_ok;
|
69 |
} Mii; |
70 |
|
71 |
static void mii_set_link(Mii *s, bool link_ok) |
72 |
{ |
73 |
if (link_ok) {
|
74 |
s->regs[MII_BMSR] |= 0x4;
|
75 |
s->regs[MII_ANLPAR] |= 0x01e1;
|
76 |
} else {
|
77 |
s->regs[MII_BMSR] &= ~0x4;
|
78 |
s->regs[MII_ANLPAR] &= 0x01ff;
|
79 |
} |
80 |
s->link_ok = link_ok; |
81 |
} |
82 |
|
83 |
static void mii_reset(Mii *s) |
84 |
{ |
85 |
memset(s->regs, 0, sizeof(s->regs)); |
86 |
s->regs[MII_BMCR] = 0x1000;
|
87 |
s->regs[MII_BMSR] = 0x7848; /* no ext regs */ |
88 |
s->regs[MII_PHYIDR1] = 0x2000;
|
89 |
s->regs[MII_PHYIDR2] = 0x5c90;
|
90 |
s->regs[MII_ANAR] = 0x01e1;
|
91 |
mii_set_link(s, s->link_ok); |
92 |
} |
93 |
|
94 |
static void mii_ro(Mii *s, uint16_t v) |
95 |
{ |
96 |
} |
97 |
|
98 |
static void mii_write_bmcr(Mii *s, uint16_t v) |
99 |
{ |
100 |
if (v & 0x8000) { |
101 |
mii_reset(s); |
102 |
} else {
|
103 |
s->regs[MII_BMCR] = v; |
104 |
} |
105 |
} |
106 |
|
107 |
static void mii_write_host(Mii *s, unsigned idx, uint16_t v) |
108 |
{ |
109 |
static void (*reg_write[MII_REG_MAX])(Mii *s, uint16_t v) = { |
110 |
[MII_BMCR] = mii_write_bmcr, |
111 |
[MII_BMSR] = mii_ro, |
112 |
[MII_PHYIDR1] = mii_ro, |
113 |
[MII_PHYIDR2] = mii_ro, |
114 |
}; |
115 |
|
116 |
if (idx < MII_REG_MAX) {
|
117 |
trace_open_eth_mii_write(idx, v); |
118 |
if (reg_write[idx]) {
|
119 |
reg_write[idx](s, v); |
120 |
} else {
|
121 |
s->regs[idx] = v; |
122 |
} |
123 |
} |
124 |
} |
125 |
|
126 |
static uint16_t mii_read_host(Mii *s, unsigned idx) |
127 |
{ |
128 |
trace_open_eth_mii_read(idx, s->regs[idx]); |
129 |
return s->regs[idx];
|
130 |
} |
131 |
|
132 |
/* OpenCores Ethernet registers */
|
133 |
enum {
|
134 |
MODER, |
135 |
INT_SOURCE, |
136 |
INT_MASK, |
137 |
IPGT, |
138 |
IPGR1, |
139 |
IPGR2, |
140 |
PACKETLEN, |
141 |
COLLCONF, |
142 |
TX_BD_NUM, |
143 |
CTRLMODER, |
144 |
MIIMODER, |
145 |
MIICOMMAND, |
146 |
MIIADDRESS, |
147 |
MIITX_DATA, |
148 |
MIIRX_DATA, |
149 |
MIISTATUS, |
150 |
MAC_ADDR0, |
151 |
MAC_ADDR1, |
152 |
HASH0, |
153 |
HASH1, |
154 |
TXCTRL, |
155 |
REG_MAX, |
156 |
}; |
157 |
|
158 |
enum {
|
159 |
MODER_RECSMALL = 0x10000,
|
160 |
MODER_PAD = 0x8000,
|
161 |
MODER_HUGEN = 0x4000,
|
162 |
MODER_RST = 0x800,
|
163 |
MODER_LOOPBCK = 0x80,
|
164 |
MODER_PRO = 0x20,
|
165 |
MODER_IAM = 0x10,
|
166 |
MODER_BRO = 0x8,
|
167 |
MODER_TXEN = 0x2,
|
168 |
MODER_RXEN = 0x1,
|
169 |
}; |
170 |
|
171 |
enum {
|
172 |
INT_SOURCE_RXB = 0x4,
|
173 |
INT_SOURCE_TXB = 0x1,
|
174 |
}; |
175 |
|
176 |
enum {
|
177 |
PACKETLEN_MINFL = 0xffff0000,
|
178 |
PACKETLEN_MINFL_LBN = 16,
|
179 |
PACKETLEN_MAXFL = 0xffff,
|
180 |
PACKETLEN_MAXFL_LBN = 0,
|
181 |
}; |
182 |
|
183 |
enum {
|
184 |
MIICOMMAND_WCTRLDATA = 0x4,
|
185 |
MIICOMMAND_RSTAT = 0x2,
|
186 |
MIICOMMAND_SCANSTAT = 0x1,
|
187 |
}; |
188 |
|
189 |
enum {
|
190 |
MIIADDRESS_RGAD = 0x1f00,
|
191 |
MIIADDRESS_RGAD_LBN = 8,
|
192 |
MIIADDRESS_FIAD = 0x1f,
|
193 |
MIIADDRESS_FIAD_LBN = 0,
|
194 |
}; |
195 |
|
196 |
enum {
|
197 |
MIITX_DATA_CTRLDATA = 0xffff,
|
198 |
MIITX_DATA_CTRLDATA_LBN = 0,
|
199 |
}; |
200 |
|
201 |
enum {
|
202 |
MIIRX_DATA_PRSD = 0xffff,
|
203 |
MIIRX_DATA_PRSD_LBN = 0,
|
204 |
}; |
205 |
|
206 |
enum {
|
207 |
MIISTATUS_LINKFAIL = 0x1,
|
208 |
MIISTATUS_LINKFAIL_LBN = 0,
|
209 |
}; |
210 |
|
211 |
enum {
|
212 |
MAC_ADDR0_BYTE2 = 0xff000000,
|
213 |
MAC_ADDR0_BYTE2_LBN = 24,
|
214 |
MAC_ADDR0_BYTE3 = 0xff0000,
|
215 |
MAC_ADDR0_BYTE3_LBN = 16,
|
216 |
MAC_ADDR0_BYTE4 = 0xff00,
|
217 |
MAC_ADDR0_BYTE4_LBN = 8,
|
218 |
MAC_ADDR0_BYTE5 = 0xff,
|
219 |
MAC_ADDR0_BYTE5_LBN = 0,
|
220 |
}; |
221 |
|
222 |
enum {
|
223 |
MAC_ADDR1_BYTE0 = 0xff00,
|
224 |
MAC_ADDR1_BYTE0_LBN = 8,
|
225 |
MAC_ADDR1_BYTE1 = 0xff,
|
226 |
MAC_ADDR1_BYTE1_LBN = 0,
|
227 |
}; |
228 |
|
229 |
enum {
|
230 |
TXD_LEN = 0xffff0000,
|
231 |
TXD_LEN_LBN = 16,
|
232 |
TXD_RD = 0x8000,
|
233 |
TXD_IRQ = 0x4000,
|
234 |
TXD_WR = 0x2000,
|
235 |
TXD_PAD = 0x1000,
|
236 |
TXD_CRC = 0x800,
|
237 |
TXD_UR = 0x100,
|
238 |
TXD_RTRY = 0xf0,
|
239 |
TXD_RTRY_LBN = 4,
|
240 |
TXD_RL = 0x8,
|
241 |
TXD_LC = 0x4,
|
242 |
TXD_DF = 0x2,
|
243 |
TXD_CS = 0x1,
|
244 |
}; |
245 |
|
246 |
enum {
|
247 |
RXD_LEN = 0xffff0000,
|
248 |
RXD_LEN_LBN = 16,
|
249 |
RXD_E = 0x8000,
|
250 |
RXD_IRQ = 0x4000,
|
251 |
RXD_WRAP = 0x2000,
|
252 |
RXD_CF = 0x100,
|
253 |
RXD_M = 0x80,
|
254 |
RXD_OR = 0x40,
|
255 |
RXD_IS = 0x20,
|
256 |
RXD_DN = 0x10,
|
257 |
RXD_TL = 0x8,
|
258 |
RXD_SF = 0x4,
|
259 |
RXD_CRC = 0x2,
|
260 |
RXD_LC = 0x1,
|
261 |
}; |
262 |
|
263 |
typedef struct desc { |
264 |
uint32_t len_flags; |
265 |
uint32_t buf_ptr; |
266 |
} desc; |
267 |
|
268 |
#define DEFAULT_PHY 1 |
269 |
|
270 |
typedef struct OpenEthState { |
271 |
SysBusDevice dev; |
272 |
NICState *nic; |
273 |
NICConf conf; |
274 |
MemoryRegion reg_io; |
275 |
MemoryRegion desc_io; |
276 |
qemu_irq irq; |
277 |
|
278 |
Mii mii; |
279 |
uint32_t regs[REG_MAX]; |
280 |
unsigned tx_desc;
|
281 |
unsigned rx_desc;
|
282 |
desc desc[128];
|
283 |
} OpenEthState; |
284 |
|
285 |
static desc *rx_desc(OpenEthState *s)
|
286 |
{ |
287 |
return s->desc + s->rx_desc;
|
288 |
} |
289 |
|
290 |
static desc *tx_desc(OpenEthState *s)
|
291 |
{ |
292 |
return s->desc + s->tx_desc;
|
293 |
} |
294 |
|
295 |
static void open_eth_update_irq(OpenEthState *s, |
296 |
uint32_t old, uint32_t new) |
297 |
{ |
298 |
if (!old != !new) {
|
299 |
trace_open_eth_update_irq(new); |
300 |
qemu_set_irq(s->irq, new); |
301 |
} |
302 |
} |
303 |
|
304 |
static void open_eth_int_source_write(OpenEthState *s, |
305 |
uint32_t val) |
306 |
{ |
307 |
uint32_t old_val = s->regs[INT_SOURCE]; |
308 |
|
309 |
s->regs[INT_SOURCE] = val; |
310 |
open_eth_update_irq(s, old_val & s->regs[INT_MASK], |
311 |
s->regs[INT_SOURCE] & s->regs[INT_MASK]); |
312 |
} |
313 |
|
314 |
static void open_eth_set_link_status(VLANClientState *nc) |
315 |
{ |
316 |
OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
317 |
|
318 |
if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) {
|
319 |
SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down); |
320 |
} |
321 |
mii_set_link(&s->mii, !nc->link_down); |
322 |
} |
323 |
|
324 |
static void open_eth_reset(void *opaque) |
325 |
{ |
326 |
OpenEthState *s = opaque; |
327 |
|
328 |
memset(s->regs, 0, sizeof(s->regs)); |
329 |
s->regs[MODER] = 0xa000;
|
330 |
s->regs[IPGT] = 0x12;
|
331 |
s->regs[IPGR1] = 0xc;
|
332 |
s->regs[IPGR2] = 0x12;
|
333 |
s->regs[PACKETLEN] = 0x400600;
|
334 |
s->regs[COLLCONF] = 0xf003f;
|
335 |
s->regs[TX_BD_NUM] = 0x40;
|
336 |
s->regs[MIIMODER] = 0x64;
|
337 |
|
338 |
s->tx_desc = 0;
|
339 |
s->rx_desc = 0x40;
|
340 |
|
341 |
mii_reset(&s->mii); |
342 |
open_eth_set_link_status(&s->nic->nc); |
343 |
} |
344 |
|
345 |
static int open_eth_can_receive(VLANClientState *nc) |
346 |
{ |
347 |
OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
348 |
|
349 |
return GET_REGBIT(s, MODER, RXEN) &&
|
350 |
(s->regs[TX_BD_NUM] < 0x80) &&
|
351 |
(rx_desc(s)->len_flags & RXD_E); |
352 |
} |
353 |
|
354 |
static ssize_t open_eth_receive(VLANClientState *nc,
|
355 |
const uint8_t *buf, size_t size)
|
356 |
{ |
357 |
OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
358 |
size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL); |
359 |
size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL); |
360 |
size_t fcsl = 4;
|
361 |
bool miss = true; |
362 |
|
363 |
trace_open_eth_receive((unsigned)size);
|
364 |
|
365 |
if (size >= 6) { |
366 |
static const uint8_t bcast_addr[] = { |
367 |
0xff, 0xff, 0xff, 0xff, 0xff, 0xff |
368 |
}; |
369 |
if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) { |
370 |
miss = GET_REGBIT(s, MODER, BRO); |
371 |
} else if ((buf[0] & 0x1) || GET_REGBIT(s, MODER, IAM)) { |
372 |
unsigned mcast_idx = compute_mcast_idx(buf);
|
373 |
miss = !(s->regs[HASH0 + mcast_idx / 32] &
|
374 |
(1 << (mcast_idx % 32))); |
375 |
trace_open_eth_receive_mcast( |
376 |
mcast_idx, s->regs[HASH0], s->regs[HASH1]); |
377 |
} else {
|
378 |
miss = GET_REGFIELD(s, MAC_ADDR1, BYTE0) != buf[0] ||
|
379 |
GET_REGFIELD(s, MAC_ADDR1, BYTE1) != buf[1] ||
|
380 |
GET_REGFIELD(s, MAC_ADDR0, BYTE2) != buf[2] ||
|
381 |
GET_REGFIELD(s, MAC_ADDR0, BYTE3) != buf[3] ||
|
382 |
GET_REGFIELD(s, MAC_ADDR0, BYTE4) != buf[4] ||
|
383 |
GET_REGFIELD(s, MAC_ADDR0, BYTE5) != buf[5];
|
384 |
} |
385 |
} |
386 |
|
387 |
if (miss && !GET_REGBIT(s, MODER, PRO)) {
|
388 |
trace_open_eth_receive_reject(); |
389 |
return size;
|
390 |
} |
391 |
|
392 |
#ifdef USE_RECSMALL
|
393 |
if (GET_REGBIT(s, MODER, RECSMALL) || size >= minfl) {
|
394 |
#else
|
395 |
{ |
396 |
#endif
|
397 |
static const uint8_t zero[64] = {0}; |
398 |
desc *desc = rx_desc(s); |
399 |
size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl;
|
400 |
|
401 |
desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR | |
402 |
RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC); |
403 |
|
404 |
if (copy_size > size) {
|
405 |
copy_size = size; |
406 |
} else {
|
407 |
fcsl = 0;
|
408 |
} |
409 |
if (miss) {
|
410 |
desc->len_flags |= RXD_M; |
411 |
} |
412 |
if (GET_REGBIT(s, MODER, HUGEN) && size > maxfl) {
|
413 |
desc->len_flags |= RXD_TL; |
414 |
} |
415 |
#ifdef USE_RECSMALL
|
416 |
if (size < minfl) {
|
417 |
desc->len_flags |= RXD_SF; |
418 |
} |
419 |
#endif
|
420 |
|
421 |
cpu_physical_memory_write(desc->buf_ptr, buf, copy_size); |
422 |
|
423 |
if (GET_REGBIT(s, MODER, PAD) && copy_size < minfl) {
|
424 |
if (minfl - copy_size > fcsl) {
|
425 |
fcsl = 0;
|
426 |
} else {
|
427 |
fcsl -= minfl - copy_size; |
428 |
} |
429 |
while (copy_size < minfl) {
|
430 |
size_t zero_sz = minfl - copy_size < sizeof(zero) ?
|
431 |
minfl - copy_size : sizeof(zero);
|
432 |
|
433 |
cpu_physical_memory_write(desc->buf_ptr + copy_size, |
434 |
zero, zero_sz); |
435 |
copy_size += zero_sz; |
436 |
} |
437 |
} |
438 |
|
439 |
/* There's no FCS in the frames handed to us by the QEMU, zero fill it.
|
440 |
* Don't do it if the frame is cut at the MAXFL or padded with 4 or
|
441 |
* more bytes to the MINFL.
|
442 |
*/
|
443 |
cpu_physical_memory_write(desc->buf_ptr + copy_size, zero, fcsl); |
444 |
copy_size += fcsl; |
445 |
|
446 |
SET_FIELD(desc->len_flags, RXD_LEN, copy_size); |
447 |
|
448 |
if ((desc->len_flags & RXD_WRAP) || s->rx_desc == 0x7f) { |
449 |
s->rx_desc = s->regs[TX_BD_NUM]; |
450 |
} else {
|
451 |
++s->rx_desc; |
452 |
} |
453 |
desc->len_flags &= ~RXD_E; |
454 |
|
455 |
trace_open_eth_receive_desc(desc->buf_ptr, desc->len_flags); |
456 |
|
457 |
if (desc->len_flags & RXD_IRQ) {
|
458 |
open_eth_int_source_write(s, |
459 |
s->regs[INT_SOURCE] | INT_SOURCE_RXB); |
460 |
} |
461 |
} |
462 |
return size;
|
463 |
} |
464 |
|
465 |
static void open_eth_cleanup(VLANClientState *nc) |
466 |
{ |
467 |
} |
468 |
|
469 |
static NetClientInfo net_open_eth_info = {
|
470 |
.type = NET_CLIENT_TYPE_NIC, |
471 |
.size = sizeof(NICState),
|
472 |
.can_receive = open_eth_can_receive, |
473 |
.receive = open_eth_receive, |
474 |
.cleanup = open_eth_cleanup, |
475 |
.link_status_changed = open_eth_set_link_status, |
476 |
}; |
477 |
|
478 |
static void open_eth_start_xmit(OpenEthState *s, desc *tx) |
479 |
{ |
480 |
uint8_t buf[65536];
|
481 |
unsigned len = GET_FIELD(tx->len_flags, TXD_LEN);
|
482 |
unsigned tx_len = len;
|
483 |
|
484 |
if ((tx->len_flags & TXD_PAD) &&
|
485 |
tx_len < GET_REGFIELD(s, PACKETLEN, MINFL)) { |
486 |
tx_len = GET_REGFIELD(s, PACKETLEN, MINFL); |
487 |
} |
488 |
if (!GET_REGBIT(s, MODER, HUGEN) &&
|
489 |
tx_len > GET_REGFIELD(s, PACKETLEN, MAXFL)) { |
490 |
tx_len = GET_REGFIELD(s, PACKETLEN, MAXFL); |
491 |
} |
492 |
|
493 |
trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len); |
494 |
|
495 |
if (len > tx_len) {
|
496 |
len = tx_len; |
497 |
} |
498 |
cpu_physical_memory_read(tx->buf_ptr, buf, len); |
499 |
if (tx_len > len) {
|
500 |
memset(buf + len, 0, tx_len - len);
|
501 |
} |
502 |
qemu_send_packet(&s->nic->nc, buf, tx_len); |
503 |
|
504 |
if (tx->len_flags & TXD_WR) {
|
505 |
s->tx_desc = 0;
|
506 |
} else {
|
507 |
++s->tx_desc; |
508 |
if (s->tx_desc >= s->regs[TX_BD_NUM]) {
|
509 |
s->tx_desc = 0;
|
510 |
} |
511 |
} |
512 |
tx->len_flags &= ~(TXD_RD | TXD_UR | |
513 |
TXD_RTRY | TXD_RL | TXD_LC | TXD_DF | TXD_CS); |
514 |
if (tx->len_flags & TXD_IRQ) {
|
515 |
open_eth_int_source_write(s, s->regs[INT_SOURCE] | INT_SOURCE_TXB); |
516 |
} |
517 |
|
518 |
} |
519 |
|
520 |
static void open_eth_check_start_xmit(OpenEthState *s) |
521 |
{ |
522 |
desc *tx = tx_desc(s); |
523 |
if (GET_REGBIT(s, MODER, TXEN) && s->regs[TX_BD_NUM] > 0 && |
524 |
(tx->len_flags & TXD_RD) && |
525 |
GET_FIELD(tx->len_flags, TXD_LEN) > 4) {
|
526 |
open_eth_start_xmit(s, tx); |
527 |
} |
528 |
} |
529 |
|
530 |
static uint64_t open_eth_reg_read(void *opaque, |
531 |
target_phys_addr_t addr, unsigned int size) |
532 |
{ |
533 |
static uint32_t (*reg_read[REG_MAX])(OpenEthState *s) = {
|
534 |
}; |
535 |
OpenEthState *s = opaque; |
536 |
unsigned idx = addr / 4; |
537 |
uint64_t v = 0;
|
538 |
|
539 |
if (idx < REG_MAX) {
|
540 |
if (reg_read[idx]) {
|
541 |
v = reg_read[idx](s); |
542 |
} else {
|
543 |
v = s->regs[idx]; |
544 |
} |
545 |
} |
546 |
trace_open_eth_reg_read((uint32_t)addr, (uint32_t)v); |
547 |
return v;
|
548 |
} |
549 |
|
550 |
static void open_eth_ro(OpenEthState *s, uint32_t val) |
551 |
{ |
552 |
} |
553 |
|
554 |
static void open_eth_moder_host_write(OpenEthState *s, uint32_t val) |
555 |
{ |
556 |
uint32_t set = val & ~s->regs[MODER]; |
557 |
|
558 |
if (set & MODER_RST) {
|
559 |
open_eth_reset(s); |
560 |
} |
561 |
|
562 |
s->regs[MODER] = val; |
563 |
|
564 |
if (set & MODER_RXEN) {
|
565 |
s->rx_desc = s->regs[TX_BD_NUM]; |
566 |
} |
567 |
if (set & MODER_TXEN) {
|
568 |
s->tx_desc = 0;
|
569 |
open_eth_check_start_xmit(s); |
570 |
} |
571 |
} |
572 |
|
573 |
static void open_eth_int_source_host_write(OpenEthState *s, uint32_t val) |
574 |
{ |
575 |
uint32_t old = s->regs[INT_SOURCE]; |
576 |
|
577 |
s->regs[INT_SOURCE] &= ~val; |
578 |
open_eth_update_irq(s, old & s->regs[INT_MASK], |
579 |
s->regs[INT_SOURCE] & s->regs[INT_MASK]); |
580 |
} |
581 |
|
582 |
static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val) |
583 |
{ |
584 |
uint32_t old = s->regs[INT_MASK]; |
585 |
|
586 |
s->regs[INT_MASK] = val; |
587 |
open_eth_update_irq(s, s->regs[INT_SOURCE] & old, |
588 |
s->regs[INT_SOURCE] & s->regs[INT_MASK]); |
589 |
} |
590 |
|
591 |
static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val) |
592 |
{ |
593 |
unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD);
|
594 |
unsigned rgad = GET_REGFIELD(s, MIIADDRESS, RGAD);
|
595 |
|
596 |
if (val & MIICOMMAND_WCTRLDATA) {
|
597 |
if (fiad == DEFAULT_PHY) {
|
598 |
mii_write_host(&s->mii, rgad, |
599 |
GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); |
600 |
} |
601 |
} |
602 |
if (val & MIICOMMAND_RSTAT) {
|
603 |
if (fiad == DEFAULT_PHY) {
|
604 |
SET_REGFIELD(s, MIIRX_DATA, PRSD, |
605 |
mii_read_host(&s->mii, rgad)); |
606 |
} else {
|
607 |
s->regs[MIIRX_DATA] = 0xffff;
|
608 |
} |
609 |
SET_REGFIELD(s, MIISTATUS, LINKFAIL, s->nic->nc.link_down); |
610 |
} |
611 |
} |
612 |
|
613 |
static void open_eth_mii_tx_host_write(OpenEthState *s, uint32_t val) |
614 |
{ |
615 |
SET_REGFIELD(s, MIITX_DATA, CTRLDATA, val); |
616 |
if (GET_REGFIELD(s, MIIADDRESS, FIAD) == DEFAULT_PHY) {
|
617 |
mii_write_host(&s->mii, GET_REGFIELD(s, MIIADDRESS, RGAD), |
618 |
GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); |
619 |
} |
620 |
} |
621 |
|
622 |
static void open_eth_reg_write(void *opaque, |
623 |
target_phys_addr_t addr, uint64_t val, unsigned int size) |
624 |
{ |
625 |
static void (*reg_write[REG_MAX])(OpenEthState *s, uint32_t val) = { |
626 |
[MODER] = open_eth_moder_host_write, |
627 |
[INT_SOURCE] = open_eth_int_source_host_write, |
628 |
[INT_MASK] = open_eth_int_mask_host_write, |
629 |
[MIICOMMAND] = open_eth_mii_command_host_write, |
630 |
[MIITX_DATA] = open_eth_mii_tx_host_write, |
631 |
[MIISTATUS] = open_eth_ro, |
632 |
}; |
633 |
OpenEthState *s = opaque; |
634 |
unsigned idx = addr / 4; |
635 |
|
636 |
if (idx < REG_MAX) {
|
637 |
trace_open_eth_reg_write((uint32_t)addr, (uint32_t)val); |
638 |
if (reg_write[idx]) {
|
639 |
reg_write[idx](s, val); |
640 |
} else {
|
641 |
s->regs[idx] = val; |
642 |
} |
643 |
} |
644 |
} |
645 |
|
646 |
static uint64_t open_eth_desc_read(void *opaque, |
647 |
target_phys_addr_t addr, unsigned int size) |
648 |
{ |
649 |
OpenEthState *s = opaque; |
650 |
uint64_t v = 0;
|
651 |
|
652 |
addr &= 0x3ff;
|
653 |
memcpy(&v, (uint8_t *)s->desc + addr, size); |
654 |
trace_open_eth_desc_read((uint32_t)addr, (uint32_t)v); |
655 |
return v;
|
656 |
} |
657 |
|
658 |
static void open_eth_desc_write(void *opaque, |
659 |
target_phys_addr_t addr, uint64_t val, unsigned int size) |
660 |
{ |
661 |
OpenEthState *s = opaque; |
662 |
|
663 |
addr &= 0x3ff;
|
664 |
trace_open_eth_desc_write((uint32_t)addr, (uint32_t)val); |
665 |
memcpy((uint8_t *)s->desc + addr, &val, size); |
666 |
open_eth_check_start_xmit(s); |
667 |
} |
668 |
|
669 |
|
670 |
static const MemoryRegionOps open_eth_reg_ops = { |
671 |
.read = open_eth_reg_read, |
672 |
.write = open_eth_reg_write, |
673 |
}; |
674 |
|
675 |
static const MemoryRegionOps open_eth_desc_ops = { |
676 |
.read = open_eth_desc_read, |
677 |
.write = open_eth_desc_write, |
678 |
}; |
679 |
|
680 |
static int sysbus_open_eth_init(SysBusDevice *dev) |
681 |
{ |
682 |
OpenEthState *s = DO_UPCAST(OpenEthState, dev, dev); |
683 |
|
684 |
memory_region_init_io(&s->reg_io, &open_eth_reg_ops, s, |
685 |
"open_eth.regs", 0x54); |
686 |
sysbus_init_mmio(dev, &s->reg_io); |
687 |
|
688 |
memory_region_init_io(&s->desc_io, &open_eth_desc_ops, s, |
689 |
"open_eth.desc", 0x400); |
690 |
sysbus_init_mmio(dev, &s->desc_io); |
691 |
|
692 |
sysbus_init_irq(dev, &s->irq); |
693 |
|
694 |
s->nic = qemu_new_nic(&net_open_eth_info, &s->conf, |
695 |
object_get_typename(OBJECT(s)), s->dev.qdev.id, s); |
696 |
return 0; |
697 |
} |
698 |
|
699 |
static void qdev_open_eth_reset(DeviceState *dev) |
700 |
{ |
701 |
OpenEthState *d = DO_UPCAST(OpenEthState, dev.qdev, dev); |
702 |
open_eth_reset(d); |
703 |
} |
704 |
|
705 |
static Property open_eth_properties[] = {
|
706 |
DEFINE_NIC_PROPERTIES(OpenEthState, conf), |
707 |
DEFINE_PROP_END_OF_LIST(), |
708 |
}; |
709 |
|
710 |
static void open_eth_class_init(ObjectClass *klass, void *data) |
711 |
{ |
712 |
DeviceClass *dc = DEVICE_CLASS(klass); |
713 |
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
714 |
|
715 |
k->init = sysbus_open_eth_init; |
716 |
dc->desc = "Opencores 10/100 Mbit Ethernet";
|
717 |
dc->reset = qdev_open_eth_reset; |
718 |
dc->props = open_eth_properties; |
719 |
} |
720 |
|
721 |
static TypeInfo open_eth_info = {
|
722 |
.name = "open_eth",
|
723 |
.parent = TYPE_SYS_BUS_DEVICE, |
724 |
.instance_size = sizeof(OpenEthState),
|
725 |
.class_init = open_eth_class_init, |
726 |
}; |
727 |
|
728 |
static void open_eth_register_types(void) |
729 |
{ |
730 |
type_register_static(&open_eth_info); |
731 |
} |
732 |
|
733 |
type_init(open_eth_register_types) |