root / hw / opencores_eth.c @ 37952117
History | View | Annotate | Download (18.8 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 | static ssize_t open_eth_receive(VLANClientState *nc,
|
355 | 342407fd | Max Filippov | const uint8_t *buf, size_t size)
|
356 | 342407fd | Max Filippov | { |
357 | 342407fd | Max Filippov | OpenEthState *s = DO_UPCAST(NICState, nc, nc)->opaque; |
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 | 342407fd | Max Filippov | static void open_eth_cleanup(VLANClientState *nc) |
466 | 342407fd | Max Filippov | { |
467 | 342407fd | Max Filippov | } |
468 | 342407fd | Max Filippov | |
469 | 342407fd | Max Filippov | static NetClientInfo net_open_eth_info = {
|
470 | 342407fd | Max Filippov | .type = NET_CLIENT_TYPE_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 | 342407fd | Max Filippov | qemu_send_packet(&s->nic->nc, 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 | 342407fd | Max Filippov | target_phys_addr_t 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 | 342407fd | Max Filippov | SET_REGFIELD(s, MIISTATUS, LINKFAIL, s->nic->nc.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 | 342407fd | Max Filippov | target_phys_addr_t 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 | 342407fd | Max Filippov | target_phys_addr_t 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 | 342407fd | Max Filippov | target_phys_addr_t 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 | 39bffca2 | Anthony Liguori | static 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) |