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