xref: /qemu/hw/net/dp8393x.c (revision bc72ad67543f5c5d39c005ff0ca72da37642a1fb)
1a65f56eeSaurel32 /*
2a65f56eeSaurel32  * QEMU NS SONIC DP8393x netcard
3a65f56eeSaurel32  *
4a65f56eeSaurel32  * Copyright (c) 2008-2009 Herve Poussineau
5a65f56eeSaurel32  *
6a65f56eeSaurel32  * This program is free software; you can redistribute it and/or
7a65f56eeSaurel32  * modify it under the terms of the GNU General Public License as
8a65f56eeSaurel32  * published by the Free Software Foundation; either version 2 of
9a65f56eeSaurel32  * the License, or (at your option) any later version.
10a65f56eeSaurel32  *
11a65f56eeSaurel32  * This program is distributed in the hope that it will be useful,
12a65f56eeSaurel32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13a65f56eeSaurel32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14a65f56eeSaurel32  * GNU General Public License for more details.
15a65f56eeSaurel32  *
16a65f56eeSaurel32  * You should have received a copy of the GNU General Public License along
178167ee88SBlue Swirl  * with this program; if not, see <http://www.gnu.org/licenses/>.
18a65f56eeSaurel32  */
19a65f56eeSaurel32 
2083c9f4caSPaolo Bonzini #include "hw/hw.h"
211de7afc9SPaolo Bonzini #include "qemu/timer.h"
221422e32dSPaolo Bonzini #include "net/net.h"
230d09e41aSPaolo Bonzini #include "hw/mips/mips.h"
24a65f56eeSaurel32 
25a65f56eeSaurel32 //#define DEBUG_SONIC
26a65f56eeSaurel32 
27a65f56eeSaurel32 /* Calculate CRCs properly on Rx packets */
28a65f56eeSaurel32 #define SONIC_CALCULATE_RXCRC
29a65f56eeSaurel32 
30a65f56eeSaurel32 #if defined(SONIC_CALCULATE_RXCRC)
31a65f56eeSaurel32 /* For crc32 */
32a65f56eeSaurel32 #include <zlib.h>
33a65f56eeSaurel32 #endif
34a65f56eeSaurel32 
35a65f56eeSaurel32 #ifdef DEBUG_SONIC
36001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
37001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
38a65f56eeSaurel32 static const char* reg_names[] = {
39a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
40a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
41a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
42a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
43a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
44a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
45a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
46a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
47a65f56eeSaurel32 #else
48001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
49a65f56eeSaurel32 #endif
50a65f56eeSaurel32 
51001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
52001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
53a65f56eeSaurel32 
54a65f56eeSaurel32 #define SONIC_CR     0x00
55a65f56eeSaurel32 #define SONIC_DCR    0x01
56a65f56eeSaurel32 #define SONIC_RCR    0x02
57a65f56eeSaurel32 #define SONIC_TCR    0x03
58a65f56eeSaurel32 #define SONIC_IMR    0x04
59a65f56eeSaurel32 #define SONIC_ISR    0x05
60a65f56eeSaurel32 #define SONIC_UTDA   0x06
61a65f56eeSaurel32 #define SONIC_CTDA   0x07
62a65f56eeSaurel32 #define SONIC_TPS    0x08
63a65f56eeSaurel32 #define SONIC_TFC    0x09
64a65f56eeSaurel32 #define SONIC_TSA0   0x0a
65a65f56eeSaurel32 #define SONIC_TSA1   0x0b
66a65f56eeSaurel32 #define SONIC_TFS    0x0c
67a65f56eeSaurel32 #define SONIC_URDA   0x0d
68a65f56eeSaurel32 #define SONIC_CRDA   0x0e
69a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
70a65f56eeSaurel32 #define SONIC_CRBA1  0x10
71a65f56eeSaurel32 #define SONIC_RBWC0  0x11
72a65f56eeSaurel32 #define SONIC_RBWC1  0x12
73a65f56eeSaurel32 #define SONIC_EOBC   0x13
74a65f56eeSaurel32 #define SONIC_URRA   0x14
75a65f56eeSaurel32 #define SONIC_RSA    0x15
76a65f56eeSaurel32 #define SONIC_REA    0x16
77a65f56eeSaurel32 #define SONIC_RRP    0x17
78a65f56eeSaurel32 #define SONIC_RWP    0x18
79a65f56eeSaurel32 #define SONIC_TRBA0  0x19
80a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
81a65f56eeSaurel32 #define SONIC_LLFA   0x1f
82a65f56eeSaurel32 #define SONIC_TTDA   0x20
83a65f56eeSaurel32 #define SONIC_CEP    0x21
84a65f56eeSaurel32 #define SONIC_CAP2   0x22
85a65f56eeSaurel32 #define SONIC_CAP1   0x23
86a65f56eeSaurel32 #define SONIC_CAP0   0x24
87a65f56eeSaurel32 #define SONIC_CE     0x25
88a65f56eeSaurel32 #define SONIC_CDP    0x26
89a65f56eeSaurel32 #define SONIC_CDC    0x27
90a65f56eeSaurel32 #define SONIC_SR     0x28
91a65f56eeSaurel32 #define SONIC_WT0    0x29
92a65f56eeSaurel32 #define SONIC_WT1    0x2a
93a65f56eeSaurel32 #define SONIC_RSC    0x2b
94a65f56eeSaurel32 #define SONIC_CRCT   0x2c
95a65f56eeSaurel32 #define SONIC_FAET   0x2d
96a65f56eeSaurel32 #define SONIC_MPT    0x2e
97a65f56eeSaurel32 #define SONIC_MDT    0x2f
98a65f56eeSaurel32 #define SONIC_DCR2   0x3f
99a65f56eeSaurel32 
100a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
101a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
102a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
103a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
104a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
105a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
106a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
107a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
108a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
109a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
110a65f56eeSaurel32 
111a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
112a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
113a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
114a65f56eeSaurel32 
115a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
116a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
117a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
118a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
119a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
120a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
121a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
122a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
123a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
124a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
125a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
126a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
127a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
128a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
129a65f56eeSaurel32 
130a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
131a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
132a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
133a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
134a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
135a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
136a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
137a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
138a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
139a65f56eeSaurel32 
140a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
141a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
142a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
143a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
144a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
145a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
146a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
147a65f56eeSaurel32 
148a65f56eeSaurel32 typedef struct dp8393xState {
149a65f56eeSaurel32     /* Hardware */
150a65f56eeSaurel32     int it_shift;
151a65f56eeSaurel32     qemu_irq irq;
152a65f56eeSaurel32 #ifdef DEBUG_SONIC
153a65f56eeSaurel32     int irq_level;
154a65f56eeSaurel32 #endif
155a65f56eeSaurel32     QEMUTimer *watchdog;
156a65f56eeSaurel32     int64_t wt_last_update;
15705f41fe3SMark McLoughlin     NICConf conf;
15805f41fe3SMark McLoughlin     NICState *nic;
159024e5bb6SAvi Kivity     MemoryRegion *address_space;
160024e5bb6SAvi Kivity     MemoryRegion mmio;
161a65f56eeSaurel32 
162a65f56eeSaurel32     /* Registers */
163a65f56eeSaurel32     uint8_t cam[16][6];
164a65f56eeSaurel32     uint16_t regs[0x40];
165a65f56eeSaurel32 
166a65f56eeSaurel32     /* Temporaries */
167a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
168a65f56eeSaurel32     int loopback_packet;
169a65f56eeSaurel32 
170a65f56eeSaurel32     /* Memory access */
171a8170e5eSAvi Kivity     void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write);
172a65f56eeSaurel32     void* mem_opaque;
173a65f56eeSaurel32 } dp8393xState;
174a65f56eeSaurel32 
175a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
176a65f56eeSaurel32 {
177a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
178a65f56eeSaurel32 
179a65f56eeSaurel32 #ifdef DEBUG_SONIC
180a65f56eeSaurel32     if (level != s->irq_level) {
181a65f56eeSaurel32         s->irq_level = level;
182a65f56eeSaurel32         if (level) {
183a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
184a65f56eeSaurel32         } else {
185a65f56eeSaurel32             DPRINTF("lower irq\n");
186a65f56eeSaurel32         }
187a65f56eeSaurel32     }
188a65f56eeSaurel32 #endif
189a65f56eeSaurel32 
190a65f56eeSaurel32     qemu_set_irq(s->irq, level);
191a65f56eeSaurel32 }
192a65f56eeSaurel32 
193a65f56eeSaurel32 static void do_load_cam(dp8393xState *s)
194a65f56eeSaurel32 {
195a65f56eeSaurel32     uint16_t data[8];
196a65f56eeSaurel32     int width, size;
197a65f56eeSaurel32     uint16_t index = 0;
198a65f56eeSaurel32 
199a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
200a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
201a65f56eeSaurel32 
202a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
203a65f56eeSaurel32         /* Fill current entry */
204a65f56eeSaurel32         s->memory_rw(s->mem_opaque,
205a65f56eeSaurel32             (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
206a65f56eeSaurel32             (uint8_t *)data, size, 0);
207a65f56eeSaurel32         s->cam[index][0] = data[1 * width] & 0xff;
208a65f56eeSaurel32         s->cam[index][1] = data[1 * width] >> 8;
209a65f56eeSaurel32         s->cam[index][2] = data[2 * width] & 0xff;
210a65f56eeSaurel32         s->cam[index][3] = data[2 * width] >> 8;
211a65f56eeSaurel32         s->cam[index][4] = data[3 * width] & 0xff;
212a65f56eeSaurel32         s->cam[index][5] = data[3 * width] >> 8;
213a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
214a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
215a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
216a65f56eeSaurel32         /* Move to next entry */
217a65f56eeSaurel32         s->regs[SONIC_CDC]--;
218a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
219a65f56eeSaurel32         index++;
220a65f56eeSaurel32     }
221a65f56eeSaurel32 
222a65f56eeSaurel32     /* Read CAM enable */
223a65f56eeSaurel32     s->memory_rw(s->mem_opaque,
224a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
225a65f56eeSaurel32         (uint8_t *)data, size, 0);
226a65f56eeSaurel32     s->regs[SONIC_CE] = data[0 * width];
227a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
228a65f56eeSaurel32 
229a65f56eeSaurel32     /* Done */
230a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
231a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
232a65f56eeSaurel32     dp8393x_update_irq(s);
233a65f56eeSaurel32 }
234a65f56eeSaurel32 
235a65f56eeSaurel32 static void do_read_rra(dp8393xState *s)
236a65f56eeSaurel32 {
237a65f56eeSaurel32     uint16_t data[8];
238a65f56eeSaurel32     int width, size;
239a65f56eeSaurel32 
240a65f56eeSaurel32     /* Read memory */
241a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
242a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
243a65f56eeSaurel32     s->memory_rw(s->mem_opaque,
244a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
245a65f56eeSaurel32         (uint8_t *)data, size, 0);
246a65f56eeSaurel32 
247a65f56eeSaurel32     /* Update SONIC registers */
248a65f56eeSaurel32     s->regs[SONIC_CRBA0] = data[0 * width];
249a65f56eeSaurel32     s->regs[SONIC_CRBA1] = data[1 * width];
250a65f56eeSaurel32     s->regs[SONIC_RBWC0] = data[2 * width];
251a65f56eeSaurel32     s->regs[SONIC_RBWC1] = data[3 * width];
252a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
253a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
254a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
255a65f56eeSaurel32 
256a65f56eeSaurel32     /* Go to next entry */
257a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
258a65f56eeSaurel32 
259a65f56eeSaurel32     /* Handle wrap */
260a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
261a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
262a65f56eeSaurel32     }
263a65f56eeSaurel32 
264a65f56eeSaurel32     /* Check resource exhaustion */
265a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
266a65f56eeSaurel32     {
267a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
268a65f56eeSaurel32         dp8393x_update_irq(s);
269a65f56eeSaurel32     }
270a65f56eeSaurel32 
271a65f56eeSaurel32     /* Done */
272a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
273a65f56eeSaurel32 }
274a65f56eeSaurel32 
275a65f56eeSaurel32 static void do_software_reset(dp8393xState *s)
276a65f56eeSaurel32 {
277*bc72ad67SAlex Bligh     timer_del(s->watchdog);
278a65f56eeSaurel32 
279a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
280a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
281a65f56eeSaurel32 }
282a65f56eeSaurel32 
283a65f56eeSaurel32 static void set_next_tick(dp8393xState *s)
284a65f56eeSaurel32 {
285a65f56eeSaurel32     uint32_t ticks;
286a65f56eeSaurel32     int64_t delay;
287a65f56eeSaurel32 
288a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
289*bc72ad67SAlex Bligh         timer_del(s->watchdog);
290a65f56eeSaurel32         return;
291a65f56eeSaurel32     }
292a65f56eeSaurel32 
293a65f56eeSaurel32     ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
294*bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2956ee093c9SJuan Quintela     delay = get_ticks_per_sec() * ticks / 5000000;
296*bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
297a65f56eeSaurel32 }
298a65f56eeSaurel32 
299a65f56eeSaurel32 static void update_wt_regs(dp8393xState *s)
300a65f56eeSaurel32 {
301a65f56eeSaurel32     int64_t elapsed;
302a65f56eeSaurel32     uint32_t val;
303a65f56eeSaurel32 
304a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
305*bc72ad67SAlex Bligh         timer_del(s->watchdog);
306a65f56eeSaurel32         return;
307a65f56eeSaurel32     }
308a65f56eeSaurel32 
309*bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
310a65f56eeSaurel32     val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
311a65f56eeSaurel32     val -= elapsed / 5000000;
312a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
313a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
314a65f56eeSaurel32     set_next_tick(s);
315a65f56eeSaurel32 
316a65f56eeSaurel32 }
317a65f56eeSaurel32 
318a65f56eeSaurel32 static void do_start_timer(dp8393xState *s)
319a65f56eeSaurel32 {
320a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
321a65f56eeSaurel32     set_next_tick(s);
322a65f56eeSaurel32 }
323a65f56eeSaurel32 
324a65f56eeSaurel32 static void do_stop_timer(dp8393xState *s)
325a65f56eeSaurel32 {
326a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
327a65f56eeSaurel32     update_wt_regs(s);
328a65f56eeSaurel32 }
329a65f56eeSaurel32 
330a65f56eeSaurel32 static void do_receiver_enable(dp8393xState *s)
331a65f56eeSaurel32 {
332a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
333a65f56eeSaurel32 }
334a65f56eeSaurel32 
335a65f56eeSaurel32 static void do_receiver_disable(dp8393xState *s)
336a65f56eeSaurel32 {
337a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
338a65f56eeSaurel32 }
339a65f56eeSaurel32 
340a65f56eeSaurel32 static void do_transmit_packets(dp8393xState *s)
341a65f56eeSaurel32 {
342b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
343a65f56eeSaurel32     uint16_t data[12];
344a65f56eeSaurel32     int width, size;
345a65f56eeSaurel32     int tx_len, len;
346a65f56eeSaurel32     uint16_t i;
347a65f56eeSaurel32 
348a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
349a65f56eeSaurel32 
350a65f56eeSaurel32     while (1) {
351a65f56eeSaurel32         /* Read memory */
352a65f56eeSaurel32         DPRINTF("Transmit packet at %08x\n",
353a65f56eeSaurel32                 (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
354a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
355a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
356a65f56eeSaurel32         s->memory_rw(s->mem_opaque,
357a65f56eeSaurel32             ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
358a65f56eeSaurel32             (uint8_t *)data, size, 0);
359a65f56eeSaurel32         tx_len = 0;
360a65f56eeSaurel32 
361a65f56eeSaurel32         /* Update registers */
362a65f56eeSaurel32         s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
363a65f56eeSaurel32         s->regs[SONIC_TPS] = data[1 * width];
364a65f56eeSaurel32         s->regs[SONIC_TFC] = data[2 * width];
365a65f56eeSaurel32         s->regs[SONIC_TSA0] = data[3 * width];
366a65f56eeSaurel32         s->regs[SONIC_TSA1] = data[4 * width];
367a65f56eeSaurel32         s->regs[SONIC_TFS] = data[5 * width];
368a65f56eeSaurel32 
369a65f56eeSaurel32         /* Handle programmable interrupt */
370a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
371a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
372a65f56eeSaurel32         } else {
373a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
374a65f56eeSaurel32         }
375a65f56eeSaurel32 
376a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
377a65f56eeSaurel32             /* Append fragment */
378a65f56eeSaurel32             len = s->regs[SONIC_TFS];
379a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
380a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
381a65f56eeSaurel32             }
382a65f56eeSaurel32             s->memory_rw(s->mem_opaque,
383a65f56eeSaurel32                 (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
384a65f56eeSaurel32                 &s->tx_buffer[tx_len], len, 0);
385a65f56eeSaurel32             tx_len += len;
386a65f56eeSaurel32 
387a65f56eeSaurel32             i++;
388a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
389a65f56eeSaurel32                 /* Read next fragment details */
390a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
391a65f56eeSaurel32                 s->memory_rw(s->mem_opaque,
392a65f56eeSaurel32                     ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
393a65f56eeSaurel32                     (uint8_t *)data, size, 0);
394a65f56eeSaurel32                 s->regs[SONIC_TSA0] = data[0 * width];
395a65f56eeSaurel32                 s->regs[SONIC_TSA1] = data[1 * width];
396a65f56eeSaurel32                 s->regs[SONIC_TFS] = data[2 * width];
397a65f56eeSaurel32             }
398a65f56eeSaurel32         }
399a65f56eeSaurel32 
400a65f56eeSaurel32         /* Handle Ethernet checksum */
401a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
402a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
403a65f56eeSaurel32              * which don't have one */
404a65f56eeSaurel32         } else {
405a65f56eeSaurel32             /* Remove existing FCS */
406a65f56eeSaurel32             tx_len -= 4;
407a65f56eeSaurel32         }
408a65f56eeSaurel32 
409a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
410a65f56eeSaurel32             /* Loopback */
411a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
412b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
413a65f56eeSaurel32                 s->loopback_packet = 1;
414b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
415a65f56eeSaurel32             }
416a65f56eeSaurel32         } else {
417a65f56eeSaurel32             /* Transmit packet */
418b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
419a65f56eeSaurel32         }
420a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
421a65f56eeSaurel32 
422a65f56eeSaurel32         /* Write status */
423a65f56eeSaurel32         data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
424a65f56eeSaurel32         size = sizeof(uint16_t) * width;
425a65f56eeSaurel32         s->memory_rw(s->mem_opaque,
426a65f56eeSaurel32             (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
427a65f56eeSaurel32             (uint8_t *)data, size, 1);
428a65f56eeSaurel32 
429a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
430a65f56eeSaurel32             /* Read footer of packet */
431a65f56eeSaurel32             size = sizeof(uint16_t) * width;
432a65f56eeSaurel32             s->memory_rw(s->mem_opaque,
433a65f56eeSaurel32                 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
434a65f56eeSaurel32                 (uint8_t *)data, size, 0);
435a65f56eeSaurel32             s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
436a65f56eeSaurel32             if (data[0 * width] & 0x1) {
437a65f56eeSaurel32                 /* EOL detected */
438a65f56eeSaurel32                 break;
439a65f56eeSaurel32             }
440a65f56eeSaurel32         }
441a65f56eeSaurel32     }
442a65f56eeSaurel32 
443a65f56eeSaurel32     /* Done */
444a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
445a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
446a65f56eeSaurel32     dp8393x_update_irq(s);
447a65f56eeSaurel32 }
448a65f56eeSaurel32 
449a65f56eeSaurel32 static void do_halt_transmission(dp8393xState *s)
450a65f56eeSaurel32 {
451a65f56eeSaurel32     /* Nothing to do */
452a65f56eeSaurel32 }
453a65f56eeSaurel32 
454a65f56eeSaurel32 static void do_command(dp8393xState *s, uint16_t command)
455a65f56eeSaurel32 {
456a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
457a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
458a65f56eeSaurel32         return;
459a65f56eeSaurel32     }
460a65f56eeSaurel32 
461a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
462a65f56eeSaurel32 
463a65f56eeSaurel32     if (command & SONIC_CR_HTX)
464a65f56eeSaurel32         do_halt_transmission(s);
465a65f56eeSaurel32     if (command & SONIC_CR_TXP)
466a65f56eeSaurel32         do_transmit_packets(s);
467a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
468a65f56eeSaurel32         do_receiver_disable(s);
469a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
470a65f56eeSaurel32         do_receiver_enable(s);
471a65f56eeSaurel32     if (command & SONIC_CR_STP)
472a65f56eeSaurel32         do_stop_timer(s);
473a65f56eeSaurel32     if (command & SONIC_CR_ST)
474a65f56eeSaurel32         do_start_timer(s);
475a65f56eeSaurel32     if (command & SONIC_CR_RST)
476a65f56eeSaurel32         do_software_reset(s);
477a65f56eeSaurel32     if (command & SONIC_CR_RRRA)
478a65f56eeSaurel32         do_read_rra(s);
479a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
480a65f56eeSaurel32         do_load_cam(s);
481a65f56eeSaurel32 }
482a65f56eeSaurel32 
483a65f56eeSaurel32 static uint16_t read_register(dp8393xState *s, int reg)
484a65f56eeSaurel32 {
485a65f56eeSaurel32     uint16_t val = 0;
486a65f56eeSaurel32 
487a65f56eeSaurel32     switch (reg) {
488a65f56eeSaurel32         /* Update data before reading it */
489a65f56eeSaurel32         case SONIC_WT0:
490a65f56eeSaurel32         case SONIC_WT1:
491a65f56eeSaurel32             update_wt_regs(s);
492a65f56eeSaurel32             val = s->regs[reg];
493a65f56eeSaurel32             break;
494a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
495a65f56eeSaurel32         case SONIC_CAP2:
496a65f56eeSaurel32         case SONIC_CAP1:
497a65f56eeSaurel32         case SONIC_CAP0:
498a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
499a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
500a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
501a65f56eeSaurel32             }
502a65f56eeSaurel32             break;
503a65f56eeSaurel32         /* All other registers have no special contrainst */
504a65f56eeSaurel32         default:
505a65f56eeSaurel32             val = s->regs[reg];
506a65f56eeSaurel32     }
507a65f56eeSaurel32 
508a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
509a65f56eeSaurel32 
510a65f56eeSaurel32     return val;
511a65f56eeSaurel32 }
512a65f56eeSaurel32 
513a65f56eeSaurel32 static void write_register(dp8393xState *s, int reg, uint16_t val)
514a65f56eeSaurel32 {
515a65f56eeSaurel32     DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]);
516a65f56eeSaurel32 
517a65f56eeSaurel32     switch (reg) {
518a65f56eeSaurel32         /* Command register */
519a65f56eeSaurel32         case SONIC_CR:
520d1805896SHervé Poussineau             do_command(s, val);
521a65f56eeSaurel32             break;
522a65f56eeSaurel32         /* Prevent write to read-only registers */
523a65f56eeSaurel32         case SONIC_CAP2:
524a65f56eeSaurel32         case SONIC_CAP1:
525a65f56eeSaurel32         case SONIC_CAP0:
526a65f56eeSaurel32         case SONIC_SR:
527a65f56eeSaurel32         case SONIC_MDT:
528a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
529a65f56eeSaurel32             break;
530a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
531a65f56eeSaurel32         case SONIC_DCR:
532a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
533a65f56eeSaurel32                 s->regs[reg] = val & 0xbfff;
534a65f56eeSaurel32             } else {
535a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
536a65f56eeSaurel32             }
537a65f56eeSaurel32             break;
538a65f56eeSaurel32         case SONIC_DCR2:
539a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
540a65f56eeSaurel32                 s->regs[reg] = val & 0xf017;
541a65f56eeSaurel32             } else {
542a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
543a65f56eeSaurel32             }
544a65f56eeSaurel32             break;
545a65f56eeSaurel32         /* 12 lower bytes are Read Only */
546a65f56eeSaurel32         case SONIC_TCR:
547a65f56eeSaurel32             s->regs[reg] = val & 0xf000;
548a65f56eeSaurel32             break;
549a65f56eeSaurel32         /* 9 lower bytes are Read Only */
550a65f56eeSaurel32         case SONIC_RCR:
551a65f56eeSaurel32             s->regs[reg] = val & 0xffe0;
552a65f56eeSaurel32             break;
553a65f56eeSaurel32         /* Ignore most significant bit */
554a65f56eeSaurel32         case SONIC_IMR:
555a65f56eeSaurel32             s->regs[reg] = val & 0x7fff;
556a65f56eeSaurel32             dp8393x_update_irq(s);
557a65f56eeSaurel32             break;
558a65f56eeSaurel32         /* Clear bits by writing 1 to them */
559a65f56eeSaurel32         case SONIC_ISR:
560a65f56eeSaurel32             val &= s->regs[reg];
561a65f56eeSaurel32             s->regs[reg] &= ~val;
562a65f56eeSaurel32             if (val & SONIC_ISR_RBE) {
563a65f56eeSaurel32                 do_read_rra(s);
564a65f56eeSaurel32             }
565a65f56eeSaurel32             dp8393x_update_irq(s);
566a65f56eeSaurel32             break;
567a65f56eeSaurel32         /* Ignore least significant bit */
568a65f56eeSaurel32         case SONIC_RSA:
569a65f56eeSaurel32         case SONIC_REA:
570a65f56eeSaurel32         case SONIC_RRP:
571a65f56eeSaurel32         case SONIC_RWP:
572a65f56eeSaurel32             s->regs[reg] = val & 0xfffe;
573a65f56eeSaurel32             break;
574a65f56eeSaurel32         /* Invert written value for some registers */
575a65f56eeSaurel32         case SONIC_CRCT:
576a65f56eeSaurel32         case SONIC_FAET:
577a65f56eeSaurel32         case SONIC_MPT:
578a65f56eeSaurel32             s->regs[reg] = val ^ 0xffff;
579a65f56eeSaurel32             break;
580a65f56eeSaurel32         /* All other registers have no special contrainst */
581a65f56eeSaurel32         default:
582a65f56eeSaurel32             s->regs[reg] = val;
583a65f56eeSaurel32     }
584a65f56eeSaurel32 
585a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
586a65f56eeSaurel32         set_next_tick(s);
587a65f56eeSaurel32     }
588a65f56eeSaurel32 }
589a65f56eeSaurel32 
590a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
591a65f56eeSaurel32 {
592a65f56eeSaurel32     dp8393xState *s = opaque;
593a65f56eeSaurel32 
594a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
595a65f56eeSaurel32         return;
596a65f56eeSaurel32     }
597a65f56eeSaurel32 
598a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
599a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
600a65f56eeSaurel32     set_next_tick(s);
601a65f56eeSaurel32 
602a65f56eeSaurel32     /* Signal underflow */
603a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
604a65f56eeSaurel32     dp8393x_update_irq(s);
605a65f56eeSaurel32 }
606a65f56eeSaurel32 
607a8170e5eSAvi Kivity static uint32_t dp8393x_readw(void *opaque, hwaddr addr)
608a65f56eeSaurel32 {
609a65f56eeSaurel32     dp8393xState *s = opaque;
610a65f56eeSaurel32     int reg;
611a65f56eeSaurel32 
612a65f56eeSaurel32     if ((addr & ((1 << s->it_shift) - 1)) != 0) {
613a65f56eeSaurel32         return 0;
614a65f56eeSaurel32     }
615a65f56eeSaurel32 
616a65f56eeSaurel32     reg = addr >> s->it_shift;
617a65f56eeSaurel32     return read_register(s, reg);
618a65f56eeSaurel32 }
619a65f56eeSaurel32 
620a8170e5eSAvi Kivity static uint32_t dp8393x_readb(void *opaque, hwaddr addr)
621a65f56eeSaurel32 {
622a65f56eeSaurel32     uint16_t v = dp8393x_readw(opaque, addr & ~0x1);
623a65f56eeSaurel32     return (v >> (8 * (addr & 0x1))) & 0xff;
624a65f56eeSaurel32 }
625a65f56eeSaurel32 
626a8170e5eSAvi Kivity static uint32_t dp8393x_readl(void *opaque, hwaddr addr)
627a65f56eeSaurel32 {
628a65f56eeSaurel32     uint32_t v;
629a65f56eeSaurel32     v = dp8393x_readw(opaque, addr);
630a65f56eeSaurel32     v |= dp8393x_readw(opaque, addr + 2) << 16;
631a65f56eeSaurel32     return v;
632a65f56eeSaurel32 }
633a65f56eeSaurel32 
634a8170e5eSAvi Kivity static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val)
635a65f56eeSaurel32 {
636a65f56eeSaurel32     dp8393xState *s = opaque;
637a65f56eeSaurel32     int reg;
638a65f56eeSaurel32 
639a65f56eeSaurel32     if ((addr & ((1 << s->it_shift) - 1)) != 0) {
640a65f56eeSaurel32         return;
641a65f56eeSaurel32     }
642a65f56eeSaurel32 
643a65f56eeSaurel32     reg = addr >> s->it_shift;
644a65f56eeSaurel32 
645a65f56eeSaurel32     write_register(s, reg, (uint16_t)val);
646a65f56eeSaurel32 }
647a65f56eeSaurel32 
648a8170e5eSAvi Kivity static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val)
649a65f56eeSaurel32 {
650a65f56eeSaurel32     uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1);
651a65f56eeSaurel32 
652a65f56eeSaurel32     switch (addr & 3) {
653a65f56eeSaurel32     case 0:
654a65f56eeSaurel32         val = val | (old_val & 0xff00);
655a65f56eeSaurel32         break;
656a65f56eeSaurel32     case 1:
657a65f56eeSaurel32         val = (val << 8) | (old_val & 0x00ff);
658a65f56eeSaurel32         break;
659a65f56eeSaurel32     }
660a65f56eeSaurel32     dp8393x_writew(opaque, addr & ~0x1, val);
661a65f56eeSaurel32 }
662a65f56eeSaurel32 
663a8170e5eSAvi Kivity static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val)
664a65f56eeSaurel32 {
665a65f56eeSaurel32     dp8393x_writew(opaque, addr, val & 0xffff);
666a65f56eeSaurel32     dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff);
667a65f56eeSaurel32 }
668a65f56eeSaurel32 
669024e5bb6SAvi Kivity static const MemoryRegionOps dp8393x_ops = {
670024e5bb6SAvi Kivity     .old_mmio = {
671024e5bb6SAvi Kivity         .read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, },
672024e5bb6SAvi Kivity         .write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, },
673024e5bb6SAvi Kivity     },
674024e5bb6SAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
675a65f56eeSaurel32 };
676a65f56eeSaurel32 
6774e68f7a0SStefan Hajnoczi static int nic_can_receive(NetClientState *nc)
678a65f56eeSaurel32 {
679cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
680a65f56eeSaurel32 
681a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
682a65f56eeSaurel32         return 0;
683a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
684a65f56eeSaurel32         return 0;
685a65f56eeSaurel32     return 1;
686a65f56eeSaurel32 }
687a65f56eeSaurel32 
688a65f56eeSaurel32 static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
689a65f56eeSaurel32 {
690a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
691a65f56eeSaurel32     int i;
692a65f56eeSaurel32 
693a65f56eeSaurel32     /* Check for runt packet (remember that checksum is not there) */
694a65f56eeSaurel32     if (size < 64 - 4) {
695a65f56eeSaurel32         return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1;
696a65f56eeSaurel32     }
697a65f56eeSaurel32 
698a65f56eeSaurel32     /* Check promiscuous mode */
699a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
700a65f56eeSaurel32         return 0;
701a65f56eeSaurel32     }
702a65f56eeSaurel32 
703a65f56eeSaurel32     /* Check multicast packets */
704a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
705a65f56eeSaurel32         return SONIC_RCR_MC;
706a65f56eeSaurel32     }
707a65f56eeSaurel32 
708a65f56eeSaurel32     /* Check broadcast */
709a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
710a65f56eeSaurel32         return SONIC_RCR_BC;
711a65f56eeSaurel32     }
712a65f56eeSaurel32 
713a65f56eeSaurel32     /* Check CAM */
714a65f56eeSaurel32     for (i = 0; i < 16; i++) {
715a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
716a65f56eeSaurel32              /* Entry enabled */
717a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
718a65f56eeSaurel32                  return 0;
719a65f56eeSaurel32              }
720a65f56eeSaurel32         }
721a65f56eeSaurel32     }
722a65f56eeSaurel32 
723a65f56eeSaurel32     return -1;
724a65f56eeSaurel32 }
725a65f56eeSaurel32 
7264e68f7a0SStefan Hajnoczi static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
727a65f56eeSaurel32 {
728cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
729a65f56eeSaurel32     uint16_t data[10];
730a65f56eeSaurel32     int packet_type;
731a65f56eeSaurel32     uint32_t available, address;
732a65f56eeSaurel32     int width, rx_len = size;
733a65f56eeSaurel32     uint32_t checksum;
734a65f56eeSaurel32 
735a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
736a65f56eeSaurel32 
737a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
738a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
739a65f56eeSaurel32 
740a65f56eeSaurel32     packet_type = receive_filter(s, buf, size);
741a65f56eeSaurel32     if (packet_type < 0) {
742a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
7434f1c942bSMark McLoughlin         return -1;
744a65f56eeSaurel32     }
745a65f56eeSaurel32 
746a65f56eeSaurel32     /* XXX: Check byte ordering */
747a65f56eeSaurel32 
748a65f56eeSaurel32     /* Check for EOL */
749a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
750a65f56eeSaurel32         /* Are we still in resource exhaustion? */
751a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
752a65f56eeSaurel32         address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
753a65f56eeSaurel32         s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
754a65f56eeSaurel32         if (data[0 * width] & 0x1) {
755a65f56eeSaurel32             /* Still EOL ; stop reception */
7564f1c942bSMark McLoughlin             return -1;
757a65f56eeSaurel32         } else {
758a65f56eeSaurel32             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
759a65f56eeSaurel32         }
760a65f56eeSaurel32     }
761a65f56eeSaurel32 
762a65f56eeSaurel32     /* Save current position */
763a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
764a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
765a65f56eeSaurel32 
766a65f56eeSaurel32     /* Calculate the ethernet checksum */
767a65f56eeSaurel32 #ifdef SONIC_CALCULATE_RXCRC
768a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
769a65f56eeSaurel32 #else
770a65f56eeSaurel32     checksum = 0;
771a65f56eeSaurel32 #endif
772a65f56eeSaurel32 
773a65f56eeSaurel32     /* Put packet into RBA */
774a65f56eeSaurel32     DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
775a65f56eeSaurel32     address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
776a65f56eeSaurel32     s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1);
777a65f56eeSaurel32     address += rx_len;
778a65f56eeSaurel32     s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1);
779a65f56eeSaurel32     rx_len += 4;
780a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
781a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
782a65f56eeSaurel32     available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
783a65f56eeSaurel32     available -= rx_len / 2;
784a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
785a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
786a65f56eeSaurel32 
787a65f56eeSaurel32     /* Update status */
788a65f56eeSaurel32     if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) {
789a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
790a65f56eeSaurel32     }
791a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
792a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
793a65f56eeSaurel32     if (s->loopback_packet) {
794a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
795a65f56eeSaurel32         s->loopback_packet = 0;
796a65f56eeSaurel32     }
797a65f56eeSaurel32 
798a65f56eeSaurel32     /* Write status to memory */
799a65f56eeSaurel32     DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]);
800a65f56eeSaurel32     data[0 * width] = s->regs[SONIC_RCR]; /* status */
801a65f56eeSaurel32     data[1 * width] = rx_len; /* byte count */
802a65f56eeSaurel32     data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
803a65f56eeSaurel32     data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
804a65f56eeSaurel32     data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
805a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
806a65f56eeSaurel32     s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1);
807a65f56eeSaurel32 
808a65f56eeSaurel32     /* Move to next descriptor */
809a65f56eeSaurel32     size = sizeof(uint16_t) * width;
810a65f56eeSaurel32     s->memory_rw(s->mem_opaque,
811a65f56eeSaurel32         ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
812a65f56eeSaurel32         (uint8_t *)data, size, 0);
813a65f56eeSaurel32     s->regs[SONIC_LLFA] = data[0 * width];
814a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
815a65f56eeSaurel32         /* EOL detected */
816a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
817a65f56eeSaurel32     } else {
818a65f56eeSaurel32         data[0 * width] = 0; /* in_use */
819a65f56eeSaurel32         s->memory_rw(s->mem_opaque,
820a65f56eeSaurel32             ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
821a65f56eeSaurel32             (uint8_t *)data, size, 1);
822a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
823a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
824a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
825a65f56eeSaurel32 
826a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
827a65f56eeSaurel32             /* Read next RRA */
828a65f56eeSaurel32             do_read_rra(s);
829a65f56eeSaurel32         }
830a65f56eeSaurel32     }
831a65f56eeSaurel32 
832a65f56eeSaurel32     /* Done */
833a65f56eeSaurel32     dp8393x_update_irq(s);
8344f1c942bSMark McLoughlin 
8354f1c942bSMark McLoughlin     return size;
836a65f56eeSaurel32 }
837a65f56eeSaurel32 
838a65f56eeSaurel32 static void nic_reset(void *opaque)
839a65f56eeSaurel32 {
840a65f56eeSaurel32     dp8393xState *s = opaque;
841*bc72ad67SAlex Bligh     timer_del(s->watchdog);
842a65f56eeSaurel32 
843a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
844a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
845a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
846a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
847a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
848a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
849a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
850a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
851a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
852a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
853a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
854a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
855a65f56eeSaurel32 
856a65f56eeSaurel32     /* Network cable is connected */
857a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
858a65f56eeSaurel32 
859a65f56eeSaurel32     dp8393x_update_irq(s);
860a65f56eeSaurel32 }
861a65f56eeSaurel32 
8624e68f7a0SStefan Hajnoczi static void nic_cleanup(NetClientState *nc)
863b946a153Saliguori {
864cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
865b946a153Saliguori 
866024e5bb6SAvi Kivity     memory_region_del_subregion(s->address_space, &s->mmio);
867024e5bb6SAvi Kivity     memory_region_destroy(&s->mmio);
868b946a153Saliguori 
869*bc72ad67SAlex Bligh     timer_del(s->watchdog);
870*bc72ad67SAlex Bligh     timer_free(s->watchdog);
871b946a153Saliguori 
8727267c094SAnthony Liguori     g_free(s);
873b946a153Saliguori }
874b946a153Saliguori 
87505f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
8762be64a68SLaszlo Ersek     .type = NET_CLIENT_OPTIONS_KIND_NIC,
87705f41fe3SMark McLoughlin     .size = sizeof(NICState),
87805f41fe3SMark McLoughlin     .can_receive = nic_can_receive,
87905f41fe3SMark McLoughlin     .receive = nic_receive,
88005f41fe3SMark McLoughlin     .cleanup = nic_cleanup,
88105f41fe3SMark McLoughlin };
88205f41fe3SMark McLoughlin 
883a8170e5eSAvi Kivity void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
884024e5bb6SAvi Kivity                   MemoryRegion *address_space,
885a65f56eeSaurel32                   qemu_irq irq, void* mem_opaque,
886a8170e5eSAvi Kivity                   void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write))
887a65f56eeSaurel32 {
888a65f56eeSaurel32     dp8393xState *s;
889a65f56eeSaurel32 
890a65f56eeSaurel32     qemu_check_nic_model(nd, "dp83932");
891a65f56eeSaurel32 
8927267c094SAnthony Liguori     s = g_malloc0(sizeof(dp8393xState));
893a65f56eeSaurel32 
894024e5bb6SAvi Kivity     s->address_space = address_space;
895a65f56eeSaurel32     s->mem_opaque = mem_opaque;
896a65f56eeSaurel32     s->memory_rw = memory_rw;
897a65f56eeSaurel32     s->it_shift = it_shift;
898a65f56eeSaurel32     s->irq = irq;
899*bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
900a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
901a65f56eeSaurel32 
9026eed1856SJan Kiszka     s->conf.macaddr = nd->macaddr;
9031ceef9f2SJason Wang     s->conf.peers.ncs[0] = nd->netdev;
904a65f56eeSaurel32 
90505f41fe3SMark McLoughlin     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
90605f41fe3SMark McLoughlin 
907b356f76dSJason Wang     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
908a08d4367SJan Kiszka     qemu_register_reset(nic_reset, s);
909a65f56eeSaurel32     nic_reset(s);
910a65f56eeSaurel32 
9112c9b15caSPaolo Bonzini     memory_region_init_io(&s->mmio, NULL, &dp8393x_ops, s,
912024e5bb6SAvi Kivity                           "dp8393x", 0x40 << it_shift);
913024e5bb6SAvi Kivity     memory_region_add_subregion(address_space, base, &s->mmio);
914a65f56eeSaurel32 }
915