root / hw / opencores_eth.c @ 0200db65
History | View | Annotate | Download (18.3 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 | 342407fd | Max Filippov | #include "net.h" |
37 | 342407fd | Max Filippov | #include "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 | 342407fd | Max Filippov | static void open_eth_set_link_status(VLANClientState *nc) |
315 | 342407fd | Max Filippov | { |
316 | 342407fd | Max Filippov | OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
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 | 342407fd | Max Filippov | open_eth_set_link_status(&s->nic->nc); |
343 | 342407fd | Max Filippov | } |
344 | 342407fd | Max Filippov | |
345 | 342407fd | Max Filippov | static int open_eth_can_receive(VLANClientState *nc) |
346 | 342407fd | Max Filippov | { |
347 | 342407fd | Max Filippov | OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
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 | 342407fd | Max Filippov | #define POLYNOMIAL 0x04c11db6 |
355 | 342407fd | Max Filippov | |
356 | 342407fd | Max Filippov | /* From FreeBSD */
|
357 | 342407fd | Max Filippov | /* XXX: optimize */
|
358 | 342407fd | Max Filippov | static unsigned compute_mcast_idx(const uint8_t *ep) |
359 | 342407fd | Max Filippov | { |
360 | 342407fd | Max Filippov | uint32_t crc; |
361 | 342407fd | Max Filippov | int carry, i, j;
|
362 | 342407fd | Max Filippov | uint8_t b; |
363 | 342407fd | Max Filippov | |
364 | 342407fd | Max Filippov | crc = 0xffffffff;
|
365 | 342407fd | Max Filippov | for (i = 0; i < 6; i++) { |
366 | 342407fd | Max Filippov | b = *ep++; |
367 | 342407fd | Max Filippov | for (j = 0; j < 8; j++) { |
368 | 342407fd | Max Filippov | carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); |
369 | 342407fd | Max Filippov | crc <<= 1;
|
370 | 342407fd | Max Filippov | b >>= 1;
|
371 | 342407fd | Max Filippov | if (carry) {
|
372 | 342407fd | Max Filippov | crc = ((crc ^ POLYNOMIAL) | carry); |
373 | 342407fd | Max Filippov | } |
374 | 342407fd | Max Filippov | } |
375 | 342407fd | Max Filippov | } |
376 | 342407fd | Max Filippov | return crc >> 26; |
377 | 342407fd | Max Filippov | } |
378 | 342407fd | Max Filippov | |
379 | 342407fd | Max Filippov | static ssize_t open_eth_receive(VLANClientState *nc,
|
380 | 342407fd | Max Filippov | const uint8_t *buf, size_t size)
|
381 | 342407fd | Max Filippov | { |
382 | 342407fd | Max Filippov | OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
383 | 342407fd | Max Filippov | size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL); |
384 | 342407fd | Max Filippov | size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL); |
385 | 342407fd | Max Filippov | bool miss = true; |
386 | 342407fd | Max Filippov | |
387 | 342407fd | Max Filippov | trace_open_eth_receive((unsigned)size);
|
388 | 342407fd | Max Filippov | |
389 | 342407fd | Max Filippov | if (size >= 6) { |
390 | 342407fd | Max Filippov | static const uint8_t bcast_addr[] = { |
391 | 342407fd | Max Filippov | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff |
392 | 342407fd | Max Filippov | }; |
393 | 342407fd | Max Filippov | if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) { |
394 | 342407fd | Max Filippov | miss = GET_REGBIT(s, MODER, BRO); |
395 | 342407fd | Max Filippov | } else if ((buf[0] & 0x1) || GET_REGBIT(s, MODER, IAM)) { |
396 | 342407fd | Max Filippov | unsigned mcast_idx = compute_mcast_idx(buf);
|
397 | 342407fd | Max Filippov | miss = !(s->regs[HASH0 + mcast_idx / 32] &
|
398 | 342407fd | Max Filippov | (1 << (mcast_idx % 32))); |
399 | 342407fd | Max Filippov | trace_open_eth_receive_mcast( |
400 | 342407fd | Max Filippov | mcast_idx, s->regs[HASH0], s->regs[HASH1]); |
401 | 342407fd | Max Filippov | } else {
|
402 | 342407fd | Max Filippov | miss = GET_REGFIELD(s, MAC_ADDR1, BYTE0) != buf[0] ||
|
403 | 342407fd | Max Filippov | GET_REGFIELD(s, MAC_ADDR1, BYTE1) != buf[1] ||
|
404 | 342407fd | Max Filippov | GET_REGFIELD(s, MAC_ADDR0, BYTE2) != buf[2] ||
|
405 | 342407fd | Max Filippov | GET_REGFIELD(s, MAC_ADDR0, BYTE3) != buf[3] ||
|
406 | 342407fd | Max Filippov | GET_REGFIELD(s, MAC_ADDR0, BYTE4) != buf[4] ||
|
407 | 342407fd | Max Filippov | GET_REGFIELD(s, MAC_ADDR0, BYTE5) != buf[5];
|
408 | 342407fd | Max Filippov | } |
409 | 342407fd | Max Filippov | } |
410 | 342407fd | Max Filippov | |
411 | 342407fd | Max Filippov | if (miss && !GET_REGBIT(s, MODER, PRO)) {
|
412 | 342407fd | Max Filippov | trace_open_eth_receive_reject(); |
413 | 342407fd | Max Filippov | return size;
|
414 | 342407fd | Max Filippov | } |
415 | 342407fd | Max Filippov | |
416 | 342407fd | Max Filippov | #ifdef USE_RECSMALL
|
417 | 342407fd | Max Filippov | if (GET_REGBIT(s, MODER, RECSMALL) || size >= minfl) {
|
418 | 342407fd | Max Filippov | #else
|
419 | 342407fd | Max Filippov | { |
420 | 342407fd | Max Filippov | #endif
|
421 | 342407fd | Max Filippov | desc *desc = rx_desc(s); |
422 | 342407fd | Max Filippov | size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl;
|
423 | 342407fd | Max Filippov | |
424 | 342407fd | Max Filippov | desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR | |
425 | 342407fd | Max Filippov | RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC); |
426 | 342407fd | Max Filippov | |
427 | 342407fd | Max Filippov | if (copy_size > size) {
|
428 | 342407fd | Max Filippov | copy_size = size; |
429 | 342407fd | Max Filippov | } |
430 | 342407fd | Max Filippov | if (miss) {
|
431 | 342407fd | Max Filippov | desc->len_flags |= RXD_M; |
432 | 342407fd | Max Filippov | } |
433 | 342407fd | Max Filippov | if (size > maxfl) {
|
434 | 342407fd | Max Filippov | desc->len_flags |= RXD_TL; |
435 | 342407fd | Max Filippov | } |
436 | 342407fd | Max Filippov | #ifdef USE_RECSMALL
|
437 | 342407fd | Max Filippov | if (size < minfl) {
|
438 | 342407fd | Max Filippov | desc->len_flags |= RXD_SF; |
439 | 342407fd | Max Filippov | } |
440 | 342407fd | Max Filippov | #endif
|
441 | 342407fd | Max Filippov | |
442 | 342407fd | Max Filippov | cpu_physical_memory_write(desc->buf_ptr, buf, copy_size); |
443 | 342407fd | Max Filippov | |
444 | 342407fd | Max Filippov | if (GET_REGBIT(s, MODER, PAD) && copy_size < minfl) {
|
445 | 342407fd | Max Filippov | static const uint8_t zero[65536] = {0}; |
446 | 342407fd | Max Filippov | |
447 | 342407fd | Max Filippov | cpu_physical_memory_write(desc->buf_ptr + copy_size, |
448 | 342407fd | Max Filippov | zero, minfl - copy_size); |
449 | 342407fd | Max Filippov | copy_size = minfl; |
450 | 342407fd | Max Filippov | } |
451 | 342407fd | Max Filippov | |
452 | 342407fd | Max Filippov | SET_FIELD(desc->len_flags, RXD_LEN, copy_size); |
453 | 342407fd | Max Filippov | |
454 | 342407fd | Max Filippov | if ((desc->len_flags & RXD_WRAP) || s->rx_desc == 0x7f) { |
455 | 342407fd | Max Filippov | s->rx_desc = s->regs[TX_BD_NUM]; |
456 | 342407fd | Max Filippov | } else {
|
457 | 342407fd | Max Filippov | ++s->rx_desc; |
458 | 342407fd | Max Filippov | } |
459 | 342407fd | Max Filippov | desc->len_flags &= ~RXD_E; |
460 | 342407fd | Max Filippov | |
461 | 342407fd | Max Filippov | trace_open_eth_receive_desc(desc->buf_ptr, desc->len_flags); |
462 | 342407fd | Max Filippov | |
463 | 342407fd | Max Filippov | if (desc->len_flags & RXD_IRQ) {
|
464 | 342407fd | Max Filippov | open_eth_int_source_write(s, |
465 | 342407fd | Max Filippov | s->regs[INT_SOURCE] | INT_SOURCE_RXB); |
466 | 342407fd | Max Filippov | } |
467 | 342407fd | Max Filippov | } |
468 | 342407fd | Max Filippov | return size;
|
469 | 342407fd | Max Filippov | } |
470 | 342407fd | Max Filippov | |
471 | 342407fd | Max Filippov | static void open_eth_cleanup(VLANClientState *nc) |
472 | 342407fd | Max Filippov | { |
473 | 342407fd | Max Filippov | } |
474 | 342407fd | Max Filippov | |
475 | 342407fd | Max Filippov | static NetClientInfo net_open_eth_info = {
|
476 | 342407fd | Max Filippov | .type = NET_CLIENT_TYPE_NIC, |
477 | 342407fd | Max Filippov | .size = sizeof(NICState),
|
478 | 342407fd | Max Filippov | .can_receive = open_eth_can_receive, |
479 | 342407fd | Max Filippov | .receive = open_eth_receive, |
480 | 342407fd | Max Filippov | .cleanup = open_eth_cleanup, |
481 | 342407fd | Max Filippov | .link_status_changed = open_eth_set_link_status, |
482 | 342407fd | Max Filippov | }; |
483 | 342407fd | Max Filippov | |
484 | 342407fd | Max Filippov | static void open_eth_start_xmit(OpenEthState *s, desc *tx) |
485 | 342407fd | Max Filippov | { |
486 | 342407fd | Max Filippov | uint8_t buf[65536];
|
487 | 342407fd | Max Filippov | unsigned len = GET_FIELD(tx->len_flags, TXD_LEN);
|
488 | 342407fd | Max Filippov | unsigned tx_len = len;
|
489 | 342407fd | Max Filippov | |
490 | 342407fd | Max Filippov | if ((tx->len_flags & TXD_PAD) &&
|
491 | 342407fd | Max Filippov | tx_len < GET_REGFIELD(s, PACKETLEN, MINFL)) { |
492 | 342407fd | Max Filippov | tx_len = GET_REGFIELD(s, PACKETLEN, MINFL); |
493 | 342407fd | Max Filippov | } |
494 | 342407fd | Max Filippov | if (!GET_REGBIT(s, MODER, HUGEN) &&
|
495 | 342407fd | Max Filippov | tx_len > GET_REGFIELD(s, PACKETLEN, MAXFL)) { |
496 | 342407fd | Max Filippov | tx_len = GET_REGFIELD(s, PACKETLEN, MAXFL); |
497 | 342407fd | Max Filippov | } |
498 | 342407fd | Max Filippov | |
499 | 342407fd | Max Filippov | trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len); |
500 | 342407fd | Max Filippov | |
501 | 342407fd | Max Filippov | if (len > tx_len) {
|
502 | 342407fd | Max Filippov | len = tx_len; |
503 | 342407fd | Max Filippov | } |
504 | 342407fd | Max Filippov | cpu_physical_memory_read(tx->buf_ptr, buf, len); |
505 | 342407fd | Max Filippov | if (tx_len > len) {
|
506 | 342407fd | Max Filippov | memset(buf + len, 0, tx_len - len);
|
507 | 342407fd | Max Filippov | } |
508 | 342407fd | Max Filippov | qemu_send_packet(&s->nic->nc, buf, tx_len); |
509 | 342407fd | Max Filippov | |
510 | 342407fd | Max Filippov | if (tx->len_flags & TXD_WR) {
|
511 | 342407fd | Max Filippov | s->tx_desc = 0;
|
512 | 342407fd | Max Filippov | } else {
|
513 | 342407fd | Max Filippov | ++s->tx_desc; |
514 | 342407fd | Max Filippov | if (s->tx_desc >= s->regs[TX_BD_NUM]) {
|
515 | 342407fd | Max Filippov | s->tx_desc = 0;
|
516 | 342407fd | Max Filippov | } |
517 | 342407fd | Max Filippov | } |
518 | 342407fd | Max Filippov | tx->len_flags &= ~(TXD_RD | TXD_UR | |
519 | 342407fd | Max Filippov | TXD_RTRY | TXD_RL | TXD_LC | TXD_DF | TXD_CS); |
520 | 342407fd | Max Filippov | if (tx->len_flags & TXD_IRQ) {
|
521 | 342407fd | Max Filippov | open_eth_int_source_write(s, s->regs[INT_SOURCE] | INT_SOURCE_TXB); |
522 | 342407fd | Max Filippov | } |
523 | 342407fd | Max Filippov | |
524 | 342407fd | Max Filippov | } |
525 | 342407fd | Max Filippov | |
526 | 342407fd | Max Filippov | static void open_eth_check_start_xmit(OpenEthState *s) |
527 | 342407fd | Max Filippov | { |
528 | 342407fd | Max Filippov | desc *tx = tx_desc(s); |
529 | 342407fd | Max Filippov | if (GET_REGBIT(s, MODER, TXEN) && s->regs[TX_BD_NUM] > 0 && |
530 | 342407fd | Max Filippov | (tx->len_flags & TXD_RD) && |
531 | 342407fd | Max Filippov | GET_FIELD(tx->len_flags, TXD_LEN) > 4) {
|
532 | 342407fd | Max Filippov | open_eth_start_xmit(s, tx); |
533 | 342407fd | Max Filippov | } |
534 | 342407fd | Max Filippov | } |
535 | 342407fd | Max Filippov | |
536 | 342407fd | Max Filippov | static uint64_t open_eth_reg_read(void *opaque, |
537 | 342407fd | Max Filippov | target_phys_addr_t addr, unsigned int size) |
538 | 342407fd | Max Filippov | { |
539 | 342407fd | Max Filippov | static uint32_t (*reg_read[REG_MAX])(OpenEthState *s) = {
|
540 | 342407fd | Max Filippov | }; |
541 | 342407fd | Max Filippov | OpenEthState *s = opaque; |
542 | 342407fd | Max Filippov | unsigned idx = addr / 4; |
543 | 342407fd | Max Filippov | uint64_t v = 0;
|
544 | 342407fd | Max Filippov | |
545 | 342407fd | Max Filippov | if (idx < REG_MAX) {
|
546 | 342407fd | Max Filippov | if (reg_read[idx]) {
|
547 | 342407fd | Max Filippov | v = reg_read[idx](s); |
548 | 342407fd | Max Filippov | } else {
|
549 | 342407fd | Max Filippov | v = s->regs[idx]; |
550 | 342407fd | Max Filippov | } |
551 | 342407fd | Max Filippov | } |
552 | 342407fd | Max Filippov | trace_open_eth_reg_read((uint32_t)addr, (uint32_t)v); |
553 | 342407fd | Max Filippov | return v;
|
554 | 342407fd | Max Filippov | } |
555 | 342407fd | Max Filippov | |
556 | 342407fd | Max Filippov | static void open_eth_ro(OpenEthState *s, uint32_t val) |
557 | 342407fd | Max Filippov | { |
558 | 342407fd | Max Filippov | } |
559 | 342407fd | Max Filippov | |
560 | 342407fd | Max Filippov | static void open_eth_moder_host_write(OpenEthState *s, uint32_t val) |
561 | 342407fd | Max Filippov | { |
562 | 342407fd | Max Filippov | uint32_t set = val & ~s->regs[MODER]; |
563 | 342407fd | Max Filippov | |
564 | 342407fd | Max Filippov | if (set & MODER_RST) {
|
565 | 342407fd | Max Filippov | open_eth_reset(s); |
566 | 342407fd | Max Filippov | } |
567 | 342407fd | Max Filippov | |
568 | 342407fd | Max Filippov | s->regs[MODER] = val; |
569 | 342407fd | Max Filippov | |
570 | 342407fd | Max Filippov | if (set & MODER_RXEN) {
|
571 | 342407fd | Max Filippov | s->rx_desc = s->regs[TX_BD_NUM]; |
572 | 342407fd | Max Filippov | } |
573 | 342407fd | Max Filippov | if (set & MODER_TXEN) {
|
574 | 342407fd | Max Filippov | s->tx_desc = 0;
|
575 | 342407fd | Max Filippov | open_eth_check_start_xmit(s); |
576 | 342407fd | Max Filippov | } |
577 | 342407fd | Max Filippov | } |
578 | 342407fd | Max Filippov | |
579 | 342407fd | Max Filippov | static void open_eth_int_source_host_write(OpenEthState *s, uint32_t val) |
580 | 342407fd | Max Filippov | { |
581 | 342407fd | Max Filippov | uint32_t old = s->regs[INT_SOURCE]; |
582 | 342407fd | Max Filippov | |
583 | 342407fd | Max Filippov | s->regs[INT_SOURCE] &= ~val; |
584 | 342407fd | Max Filippov | open_eth_update_irq(s, old & s->regs[INT_MASK], |
585 | 342407fd | Max Filippov | s->regs[INT_SOURCE] & s->regs[INT_MASK]); |
586 | 342407fd | Max Filippov | } |
587 | 342407fd | Max Filippov | |
588 | 342407fd | Max Filippov | static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val) |
589 | 342407fd | Max Filippov | { |
590 | 342407fd | Max Filippov | uint32_t old = s->regs[INT_MASK]; |
591 | 342407fd | Max Filippov | |
592 | 342407fd | Max Filippov | s->regs[INT_MASK] = val; |
593 | 342407fd | Max Filippov | open_eth_update_irq(s, s->regs[INT_SOURCE] & old, |
594 | 342407fd | Max Filippov | s->regs[INT_SOURCE] & s->regs[INT_MASK]); |
595 | 342407fd | Max Filippov | } |
596 | 342407fd | Max Filippov | |
597 | 342407fd | Max Filippov | static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val) |
598 | 342407fd | Max Filippov | { |
599 | 342407fd | Max Filippov | unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD);
|
600 | 342407fd | Max Filippov | unsigned rgad = GET_REGFIELD(s, MIIADDRESS, RGAD);
|
601 | 342407fd | Max Filippov | |
602 | 342407fd | Max Filippov | if (val & MIICOMMAND_WCTRLDATA) {
|
603 | 342407fd | Max Filippov | if (fiad == DEFAULT_PHY) {
|
604 | 342407fd | Max Filippov | mii_write_host(&s->mii, rgad, |
605 | 342407fd | Max Filippov | GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); |
606 | 342407fd | Max Filippov | } |
607 | 342407fd | Max Filippov | } |
608 | 342407fd | Max Filippov | if (val & MIICOMMAND_RSTAT) {
|
609 | 342407fd | Max Filippov | if (fiad == DEFAULT_PHY) {
|
610 | 342407fd | Max Filippov | SET_REGFIELD(s, MIIRX_DATA, PRSD, |
611 | 342407fd | Max Filippov | mii_read_host(&s->mii, rgad)); |
612 | 342407fd | Max Filippov | } else {
|
613 | 342407fd | Max Filippov | s->regs[MIIRX_DATA] = 0xffff;
|
614 | 342407fd | Max Filippov | } |
615 | 342407fd | Max Filippov | SET_REGFIELD(s, MIISTATUS, LINKFAIL, s->nic->nc.link_down); |
616 | 342407fd | Max Filippov | } |
617 | 342407fd | Max Filippov | } |
618 | 342407fd | Max Filippov | |
619 | 342407fd | Max Filippov | static void open_eth_mii_tx_host_write(OpenEthState *s, uint32_t val) |
620 | 342407fd | Max Filippov | { |
621 | 342407fd | Max Filippov | SET_REGFIELD(s, MIITX_DATA, CTRLDATA, val); |
622 | 342407fd | Max Filippov | if (GET_REGFIELD(s, MIIADDRESS, FIAD) == DEFAULT_PHY) {
|
623 | 342407fd | Max Filippov | mii_write_host(&s->mii, GET_REGFIELD(s, MIIADDRESS, RGAD), |
624 | 342407fd | Max Filippov | GET_REGFIELD(s, MIITX_DATA, CTRLDATA)); |
625 | 342407fd | Max Filippov | } |
626 | 342407fd | Max Filippov | } |
627 | 342407fd | Max Filippov | |
628 | 342407fd | Max Filippov | static void open_eth_reg_write(void *opaque, |
629 | 342407fd | Max Filippov | target_phys_addr_t addr, uint64_t val, unsigned int size) |
630 | 342407fd | Max Filippov | { |
631 | 342407fd | Max Filippov | static void (*reg_write[REG_MAX])(OpenEthState *s, uint32_t val) = { |
632 | 342407fd | Max Filippov | [MODER] = open_eth_moder_host_write, |
633 | 342407fd | Max Filippov | [INT_SOURCE] = open_eth_int_source_host_write, |
634 | 342407fd | Max Filippov | [INT_MASK] = open_eth_int_mask_host_write, |
635 | 342407fd | Max Filippov | [MIICOMMAND] = open_eth_mii_command_host_write, |
636 | 342407fd | Max Filippov | [MIITX_DATA] = open_eth_mii_tx_host_write, |
637 | 342407fd | Max Filippov | [MIISTATUS] = open_eth_ro, |
638 | 342407fd | Max Filippov | }; |
639 | 342407fd | Max Filippov | OpenEthState *s = opaque; |
640 | 342407fd | Max Filippov | unsigned idx = addr / 4; |
641 | 342407fd | Max Filippov | |
642 | 342407fd | Max Filippov | if (idx < REG_MAX) {
|
643 | 342407fd | Max Filippov | trace_open_eth_reg_write((uint32_t)addr, (uint32_t)val); |
644 | 342407fd | Max Filippov | if (reg_write[idx]) {
|
645 | 342407fd | Max Filippov | reg_write[idx](s, val); |
646 | 342407fd | Max Filippov | } else {
|
647 | 342407fd | Max Filippov | s->regs[idx] = val; |
648 | 342407fd | Max Filippov | } |
649 | 342407fd | Max Filippov | } |
650 | 342407fd | Max Filippov | } |
651 | 342407fd | Max Filippov | |
652 | 342407fd | Max Filippov | static uint64_t open_eth_desc_read(void *opaque, |
653 | 342407fd | Max Filippov | target_phys_addr_t addr, unsigned int size) |
654 | 342407fd | Max Filippov | { |
655 | 342407fd | Max Filippov | OpenEthState *s = opaque; |
656 | 342407fd | Max Filippov | uint64_t v = 0;
|
657 | 342407fd | Max Filippov | |
658 | 342407fd | Max Filippov | addr &= 0x3ff;
|
659 | 342407fd | Max Filippov | memcpy(&v, (uint8_t *)s->desc + addr, size); |
660 | 342407fd | Max Filippov | trace_open_eth_desc_read((uint32_t)addr, (uint32_t)v); |
661 | 342407fd | Max Filippov | return v;
|
662 | 342407fd | Max Filippov | } |
663 | 342407fd | Max Filippov | |
664 | 342407fd | Max Filippov | static void open_eth_desc_write(void *opaque, |
665 | 342407fd | Max Filippov | target_phys_addr_t addr, uint64_t val, unsigned int size) |
666 | 342407fd | Max Filippov | { |
667 | 342407fd | Max Filippov | OpenEthState *s = opaque; |
668 | 342407fd | Max Filippov | |
669 | 342407fd | Max Filippov | addr &= 0x3ff;
|
670 | 342407fd | Max Filippov | trace_open_eth_desc_write((uint32_t)addr, (uint32_t)val); |
671 | 342407fd | Max Filippov | memcpy((uint8_t *)s->desc + addr, &val, size); |
672 | 342407fd | Max Filippov | open_eth_check_start_xmit(s); |
673 | 342407fd | Max Filippov | } |
674 | 342407fd | Max Filippov | |
675 | 342407fd | Max Filippov | |
676 | 342407fd | Max Filippov | static MemoryRegionOps open_eth_reg_ops = {
|
677 | 342407fd | Max Filippov | .read = open_eth_reg_read, |
678 | 342407fd | Max Filippov | .write = open_eth_reg_write, |
679 | 342407fd | Max Filippov | }; |
680 | 342407fd | Max Filippov | |
681 | 342407fd | Max Filippov | static MemoryRegionOps open_eth_desc_ops = {
|
682 | 342407fd | Max Filippov | .read = open_eth_desc_read, |
683 | 342407fd | Max Filippov | .write = open_eth_desc_write, |
684 | 342407fd | Max Filippov | }; |
685 | 342407fd | Max Filippov | |
686 | 342407fd | Max Filippov | static int sysbus_open_eth_init(SysBusDevice *dev) |
687 | 342407fd | Max Filippov | { |
688 | 342407fd | Max Filippov | OpenEthState *s = DO_UPCAST(OpenEthState, dev, dev); |
689 | 342407fd | Max Filippov | |
690 | 342407fd | Max Filippov | memory_region_init_io(&s->reg_io, &open_eth_reg_ops, s, |
691 | 342407fd | Max Filippov | "open_eth.regs", 0x54); |
692 | 342407fd | Max Filippov | sysbus_init_mmio_region(dev, &s->reg_io); |
693 | 342407fd | Max Filippov | |
694 | 342407fd | Max Filippov | memory_region_init_io(&s->desc_io, &open_eth_desc_ops, s, |
695 | 342407fd | Max Filippov | "open_eth.desc", 0x400); |
696 | 342407fd | Max Filippov | sysbus_init_mmio_region(dev, &s->desc_io); |
697 | 342407fd | Max Filippov | |
698 | 342407fd | Max Filippov | sysbus_init_irq(dev, &s->irq); |
699 | 342407fd | Max Filippov | |
700 | 342407fd | Max Filippov | s->nic = qemu_new_nic(&net_open_eth_info, &s->conf, |
701 | 342407fd | Max Filippov | s->dev.qdev.info->name, s->dev.qdev.id, s); |
702 | 342407fd | Max Filippov | return 0; |
703 | 342407fd | Max Filippov | } |
704 | 342407fd | Max Filippov | |
705 | 342407fd | Max Filippov | static void qdev_open_eth_reset(DeviceState *dev) |
706 | 342407fd | Max Filippov | { |
707 | 342407fd | Max Filippov | OpenEthState *d = DO_UPCAST(OpenEthState, dev.qdev, dev); |
708 | 342407fd | Max Filippov | open_eth_reset(d); |
709 | 342407fd | Max Filippov | } |
710 | 342407fd | Max Filippov | |
711 | 342407fd | Max Filippov | static SysBusDeviceInfo open_eth_info = {
|
712 | 342407fd | Max Filippov | .qdev.name = "open_eth",
|
713 | 342407fd | Max Filippov | .qdev.desc = "Opencores 10/100 Mbit Ethernet",
|
714 | 342407fd | Max Filippov | .qdev.size = sizeof(OpenEthState),
|
715 | 342407fd | Max Filippov | .qdev.reset = qdev_open_eth_reset, |
716 | 342407fd | Max Filippov | .init = sysbus_open_eth_init, |
717 | 342407fd | Max Filippov | .qdev.props = (Property[]) { |
718 | 342407fd | Max Filippov | DEFINE_NIC_PROPERTIES(OpenEthState, conf), |
719 | 342407fd | Max Filippov | DEFINE_PROP_END_OF_LIST(), |
720 | 342407fd | Max Filippov | } |
721 | 342407fd | Max Filippov | }; |
722 | 342407fd | Max Filippov | |
723 | 342407fd | Max Filippov | static void open_eth_register_devices(void) |
724 | 342407fd | Max Filippov | { |
725 | 342407fd | Max Filippov | sysbus_register_withprop(&open_eth_info); |
726 | 342407fd | Max Filippov | } |
727 | 342407fd | Max Filippov | |
728 | 342407fd | Max Filippov | device_init(open_eth_register_devices) |