Statistics
| Branch: | Revision:

root / hw / opencores_eth.c @ 9a6ee9fd

History | View | Annotate | Download (18.7 kB)

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