xref: /qemu/hw/net/dp8393x.c (revision 104655a5c818ea8de1329cef50d1cc8defc524f3)
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 
20*104655a5SHervé Poussineau #include "hw/sysbus.h"
21*104655a5SHervé Poussineau #include "hw/devices.h"
221422e32dSPaolo Bonzini #include "net/net.h"
23*104655a5SHervé Poussineau #include "qemu/timer.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 
142*104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
143*104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
144*104655a5SHervé Poussineau 
145a65f56eeSaurel32 typedef struct dp8393xState {
146*104655a5SHervé Poussineau     SysBusDevice parent_obj;
147*104655a5SHervé Poussineau 
148a65f56eeSaurel32     /* Hardware */
149*104655a5SHervé Poussineau     uint8_t it_shift;
150a65f56eeSaurel32     qemu_irq irq;
151a65f56eeSaurel32 #ifdef DEBUG_SONIC
152a65f56eeSaurel32     int irq_level;
153a65f56eeSaurel32 #endif
154a65f56eeSaurel32     QEMUTimer *watchdog;
155a65f56eeSaurel32     int64_t wt_last_update;
15605f41fe3SMark McLoughlin     NICConf conf;
15705f41fe3SMark McLoughlin     NICState *nic;
158024e5bb6SAvi Kivity     MemoryRegion mmio;
159a65f56eeSaurel32 
160a65f56eeSaurel32     /* Registers */
161a65f56eeSaurel32     uint8_t cam[16][6];
162a65f56eeSaurel32     uint16_t regs[0x40];
163a65f56eeSaurel32 
164a65f56eeSaurel32     /* Temporaries */
165a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
166a65f56eeSaurel32     int loopback_packet;
167a65f56eeSaurel32 
168a65f56eeSaurel32     /* Memory access */
169*104655a5SHervé Poussineau     void *dma_mr;
170dd820513SHervé Poussineau     AddressSpace as;
171a65f56eeSaurel32 } dp8393xState;
172a65f56eeSaurel32 
173a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
174a65f56eeSaurel32 {
175a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
176a65f56eeSaurel32 
177a65f56eeSaurel32 #ifdef DEBUG_SONIC
178a65f56eeSaurel32     if (level != s->irq_level) {
179a65f56eeSaurel32         s->irq_level = level;
180a65f56eeSaurel32         if (level) {
181a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
182a65f56eeSaurel32         } else {
183a65f56eeSaurel32             DPRINTF("lower irq\n");
184a65f56eeSaurel32         }
185a65f56eeSaurel32     }
186a65f56eeSaurel32 #endif
187a65f56eeSaurel32 
188a65f56eeSaurel32     qemu_set_irq(s->irq, level);
189a65f56eeSaurel32 }
190a65f56eeSaurel32 
1913df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
192a65f56eeSaurel32 {
193a65f56eeSaurel32     uint16_t data[8];
194a65f56eeSaurel32     int width, size;
195a65f56eeSaurel32     uint16_t index = 0;
196a65f56eeSaurel32 
197a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
198a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
199a65f56eeSaurel32 
200a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
201a65f56eeSaurel32         /* Fill current entry */
202dd820513SHervé Poussineau         address_space_rw(&s->as,
203a65f56eeSaurel32             (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
204dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
205a65f56eeSaurel32         s->cam[index][0] = data[1 * width] & 0xff;
206a65f56eeSaurel32         s->cam[index][1] = data[1 * width] >> 8;
207a65f56eeSaurel32         s->cam[index][2] = data[2 * width] & 0xff;
208a65f56eeSaurel32         s->cam[index][3] = data[2 * width] >> 8;
209a65f56eeSaurel32         s->cam[index][4] = data[3 * width] & 0xff;
210a65f56eeSaurel32         s->cam[index][5] = data[3 * width] >> 8;
211a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
212a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
213a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
214a65f56eeSaurel32         /* Move to next entry */
215a65f56eeSaurel32         s->regs[SONIC_CDC]--;
216a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
217a65f56eeSaurel32         index++;
218a65f56eeSaurel32     }
219a65f56eeSaurel32 
220a65f56eeSaurel32     /* Read CAM enable */
221dd820513SHervé Poussineau     address_space_rw(&s->as,
222a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
223dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
224a65f56eeSaurel32     s->regs[SONIC_CE] = data[0 * width];
225a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
226a65f56eeSaurel32 
227a65f56eeSaurel32     /* Done */
228a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
229a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
230a65f56eeSaurel32     dp8393x_update_irq(s);
231a65f56eeSaurel32 }
232a65f56eeSaurel32 
2333df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
234a65f56eeSaurel32 {
235a65f56eeSaurel32     uint16_t data[8];
236a65f56eeSaurel32     int width, size;
237a65f56eeSaurel32 
238a65f56eeSaurel32     /* Read memory */
239a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
240a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
241dd820513SHervé Poussineau     address_space_rw(&s->as,
242a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
243dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
244a65f56eeSaurel32 
245a65f56eeSaurel32     /* Update SONIC registers */
246a65f56eeSaurel32     s->regs[SONIC_CRBA0] = data[0 * width];
247a65f56eeSaurel32     s->regs[SONIC_CRBA1] = data[1 * width];
248a65f56eeSaurel32     s->regs[SONIC_RBWC0] = data[2 * width];
249a65f56eeSaurel32     s->regs[SONIC_RBWC1] = data[3 * width];
250a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
251a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
252a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
253a65f56eeSaurel32 
254a65f56eeSaurel32     /* Go to next entry */
255a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
256a65f56eeSaurel32 
257a65f56eeSaurel32     /* Handle wrap */
258a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
259a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
260a65f56eeSaurel32     }
261a65f56eeSaurel32 
262a65f56eeSaurel32     /* Check resource exhaustion */
263a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
264a65f56eeSaurel32     {
265a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
266a65f56eeSaurel32         dp8393x_update_irq(s);
267a65f56eeSaurel32     }
268a65f56eeSaurel32 
269a65f56eeSaurel32     /* Done */
270a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
271a65f56eeSaurel32 }
272a65f56eeSaurel32 
2733df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
274a65f56eeSaurel32 {
275bc72ad67SAlex Bligh     timer_del(s->watchdog);
276a65f56eeSaurel32 
277a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
278a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
279a65f56eeSaurel32 }
280a65f56eeSaurel32 
2813df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
282a65f56eeSaurel32 {
283a65f56eeSaurel32     uint32_t ticks;
284a65f56eeSaurel32     int64_t delay;
285a65f56eeSaurel32 
286a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
287bc72ad67SAlex Bligh         timer_del(s->watchdog);
288a65f56eeSaurel32         return;
289a65f56eeSaurel32     }
290a65f56eeSaurel32 
291a65f56eeSaurel32     ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
292bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2936ee093c9SJuan Quintela     delay = get_ticks_per_sec() * ticks / 5000000;
294bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
295a65f56eeSaurel32 }
296a65f56eeSaurel32 
2973df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
298a65f56eeSaurel32 {
299a65f56eeSaurel32     int64_t elapsed;
300a65f56eeSaurel32     uint32_t val;
301a65f56eeSaurel32 
302a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
303bc72ad67SAlex Bligh         timer_del(s->watchdog);
304a65f56eeSaurel32         return;
305a65f56eeSaurel32     }
306a65f56eeSaurel32 
307bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
308a65f56eeSaurel32     val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
309a65f56eeSaurel32     val -= elapsed / 5000000;
310a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
311a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3123df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
313a65f56eeSaurel32 
314a65f56eeSaurel32 }
315a65f56eeSaurel32 
3163df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
317a65f56eeSaurel32 {
318a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3193df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
320a65f56eeSaurel32 }
321a65f56eeSaurel32 
3223df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
323a65f56eeSaurel32 {
324a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3253df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
326a65f56eeSaurel32 }
327a65f56eeSaurel32 
3283df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
329a65f56eeSaurel32 {
330a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
331a65f56eeSaurel32 }
332a65f56eeSaurel32 
3333df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
334a65f56eeSaurel32 {
335a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
336a65f56eeSaurel32 }
337a65f56eeSaurel32 
3383df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
339a65f56eeSaurel32 {
340b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
341a65f56eeSaurel32     uint16_t data[12];
342a65f56eeSaurel32     int width, size;
343a65f56eeSaurel32     int tx_len, len;
344a65f56eeSaurel32     uint16_t i;
345a65f56eeSaurel32 
346a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
347a65f56eeSaurel32 
348a65f56eeSaurel32     while (1) {
349a65f56eeSaurel32         /* Read memory */
350a65f56eeSaurel32         DPRINTF("Transmit packet at %08x\n",
351a65f56eeSaurel32                 (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
352a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
353a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
354dd820513SHervé Poussineau         address_space_rw(&s->as,
355a65f56eeSaurel32             ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
356dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
357a65f56eeSaurel32         tx_len = 0;
358a65f56eeSaurel32 
359a65f56eeSaurel32         /* Update registers */
360a65f56eeSaurel32         s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
361a65f56eeSaurel32         s->regs[SONIC_TPS] = data[1 * width];
362a65f56eeSaurel32         s->regs[SONIC_TFC] = data[2 * width];
363a65f56eeSaurel32         s->regs[SONIC_TSA0] = data[3 * width];
364a65f56eeSaurel32         s->regs[SONIC_TSA1] = data[4 * width];
365a65f56eeSaurel32         s->regs[SONIC_TFS] = data[5 * width];
366a65f56eeSaurel32 
367a65f56eeSaurel32         /* Handle programmable interrupt */
368a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
369a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
370a65f56eeSaurel32         } else {
371a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
372a65f56eeSaurel32         }
373a65f56eeSaurel32 
374a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
375a65f56eeSaurel32             /* Append fragment */
376a65f56eeSaurel32             len = s->regs[SONIC_TFS];
377a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
378a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
379a65f56eeSaurel32             }
380dd820513SHervé Poussineau             address_space_rw(&s->as,
381a65f56eeSaurel32                 (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
382dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
383a65f56eeSaurel32             tx_len += len;
384a65f56eeSaurel32 
385a65f56eeSaurel32             i++;
386a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
387a65f56eeSaurel32                 /* Read next fragment details */
388a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
389dd820513SHervé Poussineau                 address_space_rw(&s->as,
390a65f56eeSaurel32                     ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
391dd820513SHervé Poussineau                     MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
392a65f56eeSaurel32                 s->regs[SONIC_TSA0] = data[0 * width];
393a65f56eeSaurel32                 s->regs[SONIC_TSA1] = data[1 * width];
394a65f56eeSaurel32                 s->regs[SONIC_TFS] = data[2 * width];
395a65f56eeSaurel32             }
396a65f56eeSaurel32         }
397a65f56eeSaurel32 
398a65f56eeSaurel32         /* Handle Ethernet checksum */
399a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
400a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
401a65f56eeSaurel32              * which don't have one */
402a65f56eeSaurel32         } else {
403a65f56eeSaurel32             /* Remove existing FCS */
404a65f56eeSaurel32             tx_len -= 4;
405a65f56eeSaurel32         }
406a65f56eeSaurel32 
407a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
408a65f56eeSaurel32             /* Loopback */
409a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
410b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
411a65f56eeSaurel32                 s->loopback_packet = 1;
412b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
413a65f56eeSaurel32             }
414a65f56eeSaurel32         } else {
415a65f56eeSaurel32             /* Transmit packet */
416b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
417a65f56eeSaurel32         }
418a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
419a65f56eeSaurel32 
420a65f56eeSaurel32         /* Write status */
421a65f56eeSaurel32         data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
422a65f56eeSaurel32         size = sizeof(uint16_t) * width;
423dd820513SHervé Poussineau         address_space_rw(&s->as,
424a65f56eeSaurel32             (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
425dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
426a65f56eeSaurel32 
427a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
428a65f56eeSaurel32             /* Read footer of packet */
429a65f56eeSaurel32             size = sizeof(uint16_t) * width;
430dd820513SHervé Poussineau             address_space_rw(&s->as,
431a65f56eeSaurel32                 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
432dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
433a65f56eeSaurel32             s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
434a65f56eeSaurel32             if (data[0 * width] & 0x1) {
435a65f56eeSaurel32                 /* EOL detected */
436a65f56eeSaurel32                 break;
437a65f56eeSaurel32             }
438a65f56eeSaurel32         }
439a65f56eeSaurel32     }
440a65f56eeSaurel32 
441a65f56eeSaurel32     /* Done */
442a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
443a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
444a65f56eeSaurel32     dp8393x_update_irq(s);
445a65f56eeSaurel32 }
446a65f56eeSaurel32 
4473df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
448a65f56eeSaurel32 {
449a65f56eeSaurel32     /* Nothing to do */
450a65f56eeSaurel32 }
451a65f56eeSaurel32 
4523df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
453a65f56eeSaurel32 {
454a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
455a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
456a65f56eeSaurel32         return;
457a65f56eeSaurel32     }
458a65f56eeSaurel32 
459a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
460a65f56eeSaurel32 
461a65f56eeSaurel32     if (command & SONIC_CR_HTX)
4623df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
463a65f56eeSaurel32     if (command & SONIC_CR_TXP)
4643df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
465a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
4663df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
467a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
4683df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
469a65f56eeSaurel32     if (command & SONIC_CR_STP)
4703df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
471a65f56eeSaurel32     if (command & SONIC_CR_ST)
4723df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
473a65f56eeSaurel32     if (command & SONIC_CR_RST)
4743df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
475a65f56eeSaurel32     if (command & SONIC_CR_RRRA)
4763df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
477a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
4783df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
479a65f56eeSaurel32 }
480a65f56eeSaurel32 
48184689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
482a65f56eeSaurel32 {
48384689cbbSHervé Poussineau     dp8393xState *s = opaque;
48484689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
485a65f56eeSaurel32     uint16_t val = 0;
486a65f56eeSaurel32 
487a65f56eeSaurel32     switch (reg) {
488a65f56eeSaurel32         /* Update data before reading it */
489a65f56eeSaurel32         case SONIC_WT0:
490a65f56eeSaurel32         case SONIC_WT1:
4913df5de64SHervé Poussineau             dp8393x_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 
51384689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
51484689cbbSHervé Poussineau                           unsigned int size)
515a65f56eeSaurel32 {
51684689cbbSHervé Poussineau     dp8393xState *s = opaque;
51784689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
51884689cbbSHervé Poussineau 
51984689cbbSHervé Poussineau     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
520a65f56eeSaurel32 
521a65f56eeSaurel32     switch (reg) {
522a65f56eeSaurel32         /* Command register */
523a65f56eeSaurel32         case SONIC_CR:
5243df5de64SHervé Poussineau             dp8393x_do_command(s, data);
525a65f56eeSaurel32             break;
526a65f56eeSaurel32         /* Prevent write to read-only registers */
527a65f56eeSaurel32         case SONIC_CAP2:
528a65f56eeSaurel32         case SONIC_CAP1:
529a65f56eeSaurel32         case SONIC_CAP0:
530a65f56eeSaurel32         case SONIC_SR:
531a65f56eeSaurel32         case SONIC_MDT:
532a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
533a65f56eeSaurel32             break;
534a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
535a65f56eeSaurel32         case SONIC_DCR:
536a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
53784689cbbSHervé Poussineau                 s->regs[reg] = data & 0xbfff;
538a65f56eeSaurel32             } else {
539a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
540a65f56eeSaurel32             }
541a65f56eeSaurel32             break;
542a65f56eeSaurel32         case SONIC_DCR2:
543a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
54484689cbbSHervé Poussineau                 s->regs[reg] = data & 0xf017;
545a65f56eeSaurel32             } else {
546a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
547a65f56eeSaurel32             }
548a65f56eeSaurel32             break;
549a65f56eeSaurel32         /* 12 lower bytes are Read Only */
550a65f56eeSaurel32         case SONIC_TCR:
55184689cbbSHervé Poussineau             s->regs[reg] = data & 0xf000;
552a65f56eeSaurel32             break;
553a65f56eeSaurel32         /* 9 lower bytes are Read Only */
554a65f56eeSaurel32         case SONIC_RCR:
55584689cbbSHervé Poussineau             s->regs[reg] = data & 0xffe0;
556a65f56eeSaurel32             break;
557a65f56eeSaurel32         /* Ignore most significant bit */
558a65f56eeSaurel32         case SONIC_IMR:
55984689cbbSHervé Poussineau             s->regs[reg] = data & 0x7fff;
560a65f56eeSaurel32             dp8393x_update_irq(s);
561a65f56eeSaurel32             break;
562a65f56eeSaurel32         /* Clear bits by writing 1 to them */
563a65f56eeSaurel32         case SONIC_ISR:
56484689cbbSHervé Poussineau             data &= s->regs[reg];
56584689cbbSHervé Poussineau             s->regs[reg] &= ~data;
56684689cbbSHervé Poussineau             if (data & SONIC_ISR_RBE) {
5673df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
568a65f56eeSaurel32             }
569a65f56eeSaurel32             dp8393x_update_irq(s);
570a65f56eeSaurel32             break;
571a65f56eeSaurel32         /* Ignore least significant bit */
572a65f56eeSaurel32         case SONIC_RSA:
573a65f56eeSaurel32         case SONIC_REA:
574a65f56eeSaurel32         case SONIC_RRP:
575a65f56eeSaurel32         case SONIC_RWP:
57684689cbbSHervé Poussineau             s->regs[reg] = data & 0xfffe;
577a65f56eeSaurel32             break;
578a65f56eeSaurel32         /* Invert written value for some registers */
579a65f56eeSaurel32         case SONIC_CRCT:
580a65f56eeSaurel32         case SONIC_FAET:
581a65f56eeSaurel32         case SONIC_MPT:
58284689cbbSHervé Poussineau             s->regs[reg] = data ^ 0xffff;
583a65f56eeSaurel32             break;
584a65f56eeSaurel32         /* All other registers have no special contrainst */
585a65f56eeSaurel32         default:
58684689cbbSHervé Poussineau             s->regs[reg] = data;
587a65f56eeSaurel32     }
588a65f56eeSaurel32 
589a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
5903df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
591a65f56eeSaurel32     }
592a65f56eeSaurel32 }
593a65f56eeSaurel32 
59484689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
59584689cbbSHervé Poussineau     .read = dp8393x_read,
59684689cbbSHervé Poussineau     .write = dp8393x_write,
59784689cbbSHervé Poussineau     .impl.min_access_size = 2,
59884689cbbSHervé Poussineau     .impl.max_access_size = 2,
59984689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
60084689cbbSHervé Poussineau };
60184689cbbSHervé Poussineau 
602a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
603a65f56eeSaurel32 {
604a65f56eeSaurel32     dp8393xState *s = opaque;
605a65f56eeSaurel32 
606a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
607a65f56eeSaurel32         return;
608a65f56eeSaurel32     }
609a65f56eeSaurel32 
610a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
611a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
6123df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
613a65f56eeSaurel32 
614a65f56eeSaurel32     /* Signal underflow */
615a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
616a65f56eeSaurel32     dp8393x_update_irq(s);
617a65f56eeSaurel32 }
618a65f56eeSaurel32 
6193df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
620a65f56eeSaurel32 {
621cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
622a65f56eeSaurel32 
623a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
624a65f56eeSaurel32         return 0;
625a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
626a65f56eeSaurel32         return 0;
627a65f56eeSaurel32     return 1;
628a65f56eeSaurel32 }
629a65f56eeSaurel32 
6303df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
6313df5de64SHervé Poussineau                                   int size)
632a65f56eeSaurel32 {
633a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
634a65f56eeSaurel32     int i;
635a65f56eeSaurel32 
636a65f56eeSaurel32     /* Check for runt packet (remember that checksum is not there) */
637a65f56eeSaurel32     if (size < 64 - 4) {
638a65f56eeSaurel32         return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1;
639a65f56eeSaurel32     }
640a65f56eeSaurel32 
641a65f56eeSaurel32     /* Check promiscuous mode */
642a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
643a65f56eeSaurel32         return 0;
644a65f56eeSaurel32     }
645a65f56eeSaurel32 
646a65f56eeSaurel32     /* Check multicast packets */
647a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
648a65f56eeSaurel32         return SONIC_RCR_MC;
649a65f56eeSaurel32     }
650a65f56eeSaurel32 
651a65f56eeSaurel32     /* Check broadcast */
652a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
653a65f56eeSaurel32         return SONIC_RCR_BC;
654a65f56eeSaurel32     }
655a65f56eeSaurel32 
656a65f56eeSaurel32     /* Check CAM */
657a65f56eeSaurel32     for (i = 0; i < 16; i++) {
658a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
659a65f56eeSaurel32              /* Entry enabled */
660a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
661a65f56eeSaurel32                  return 0;
662a65f56eeSaurel32              }
663a65f56eeSaurel32         }
664a65f56eeSaurel32     }
665a65f56eeSaurel32 
666a65f56eeSaurel32     return -1;
667a65f56eeSaurel32 }
668a65f56eeSaurel32 
6693df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
6703df5de64SHervé Poussineau                                size_t size)
671a65f56eeSaurel32 {
672cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
673a65f56eeSaurel32     uint16_t data[10];
674a65f56eeSaurel32     int packet_type;
675a65f56eeSaurel32     uint32_t available, address;
676a65f56eeSaurel32     int width, rx_len = size;
677a65f56eeSaurel32     uint32_t checksum;
678a65f56eeSaurel32 
679a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
680a65f56eeSaurel32 
681a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
682a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
683a65f56eeSaurel32 
6843df5de64SHervé Poussineau     packet_type = dp8393x_receive_filter(s, buf, size);
685a65f56eeSaurel32     if (packet_type < 0) {
686a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
6874f1c942bSMark McLoughlin         return -1;
688a65f56eeSaurel32     }
689a65f56eeSaurel32 
690a65f56eeSaurel32     /* XXX: Check byte ordering */
691a65f56eeSaurel32 
692a65f56eeSaurel32     /* Check for EOL */
693a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
694a65f56eeSaurel32         /* Are we still in resource exhaustion? */
695a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
696a65f56eeSaurel32         address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
697dd820513SHervé Poussineau         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
698dd820513SHervé Poussineau                          (uint8_t *)data, size, 0);
699a65f56eeSaurel32         if (data[0 * width] & 0x1) {
700a65f56eeSaurel32             /* Still EOL ; stop reception */
7014f1c942bSMark McLoughlin             return -1;
702a65f56eeSaurel32         } else {
703a65f56eeSaurel32             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
704a65f56eeSaurel32         }
705a65f56eeSaurel32     }
706a65f56eeSaurel32 
707a65f56eeSaurel32     /* Save current position */
708a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
709a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
710a65f56eeSaurel32 
711a65f56eeSaurel32     /* Calculate the ethernet checksum */
712a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
713a65f56eeSaurel32 
714a65f56eeSaurel32     /* Put packet into RBA */
715a65f56eeSaurel32     DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
716a65f56eeSaurel32     address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
717dd820513SHervé Poussineau     address_space_rw(&s->as, address,
718dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
719a65f56eeSaurel32     address += rx_len;
720dd820513SHervé Poussineau     address_space_rw(&s->as, address,
721dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
722a65f56eeSaurel32     rx_len += 4;
723a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
724a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
725a65f56eeSaurel32     available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
726a65f56eeSaurel32     available -= rx_len / 2;
727a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
728a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
729a65f56eeSaurel32 
730a65f56eeSaurel32     /* Update status */
731a65f56eeSaurel32     if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) {
732a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
733a65f56eeSaurel32     }
734a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
735a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
736a65f56eeSaurel32     if (s->loopback_packet) {
737a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
738a65f56eeSaurel32         s->loopback_packet = 0;
739a65f56eeSaurel32     }
740a65f56eeSaurel32 
741a65f56eeSaurel32     /* Write status to memory */
742a65f56eeSaurel32     DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]);
743a65f56eeSaurel32     data[0 * width] = s->regs[SONIC_RCR]; /* status */
744a65f56eeSaurel32     data[1 * width] = rx_len; /* byte count */
745a65f56eeSaurel32     data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
746a65f56eeSaurel32     data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
747a65f56eeSaurel32     data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
748a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
749dd820513SHervé Poussineau     address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA],
750dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
751a65f56eeSaurel32 
752a65f56eeSaurel32     /* Move to next descriptor */
753a65f56eeSaurel32     size = sizeof(uint16_t) * width;
754dd820513SHervé Poussineau     address_space_rw(&s->as,
755a65f56eeSaurel32         ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
756dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
757a65f56eeSaurel32     s->regs[SONIC_LLFA] = data[0 * width];
758a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
759a65f56eeSaurel32         /* EOL detected */
760a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
761a65f56eeSaurel32     } else {
762a65f56eeSaurel32         data[0 * width] = 0; /* in_use */
763dd820513SHervé Poussineau         address_space_rw(&s->as,
764a65f56eeSaurel32             ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
765dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
766a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
767a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
768a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
769a65f56eeSaurel32 
770a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
771a65f56eeSaurel32             /* Read next RRA */
7723df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
773a65f56eeSaurel32         }
774a65f56eeSaurel32     }
775a65f56eeSaurel32 
776a65f56eeSaurel32     /* Done */
777a65f56eeSaurel32     dp8393x_update_irq(s);
7784f1c942bSMark McLoughlin 
7794f1c942bSMark McLoughlin     return size;
780a65f56eeSaurel32 }
781a65f56eeSaurel32 
782*104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
783a65f56eeSaurel32 {
784*104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
785bc72ad67SAlex Bligh     timer_del(s->watchdog);
786a65f56eeSaurel32 
787a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
788a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
789a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
790a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
791a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
792a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
793a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
794a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
795a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
796a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
797a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
798a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
799a65f56eeSaurel32 
800a65f56eeSaurel32     /* Network cable is connected */
801a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
802a65f56eeSaurel32 
803a65f56eeSaurel32     dp8393x_update_irq(s);
804a65f56eeSaurel32 }
805a65f56eeSaurel32 
80605f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
8072be64a68SLaszlo Ersek     .type = NET_CLIENT_OPTIONS_KIND_NIC,
80805f41fe3SMark McLoughlin     .size = sizeof(NICState),
8093df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8103df5de64SHervé Poussineau     .receive = dp8393x_receive,
81105f41fe3SMark McLoughlin };
81205f41fe3SMark McLoughlin 
813*104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
814a65f56eeSaurel32 {
815*104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
816*104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
817a65f56eeSaurel32 
818*104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
819*104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
820*104655a5SHervé Poussineau }
821a65f56eeSaurel32 
822*104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
823*104655a5SHervé Poussineau {
824*104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
825a65f56eeSaurel32 
826*104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
827*104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
828*104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
829*104655a5SHervé Poussineau 
830*104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
831*104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
832*104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
833*104655a5SHervé Poussineau 
834bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
835a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
836a65f56eeSaurel32 }
837*104655a5SHervé Poussineau 
838*104655a5SHervé Poussineau static Property dp8393x_properties[] = {
839*104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
840*104655a5SHervé Poussineau     DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
841*104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
842*104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
843*104655a5SHervé Poussineau };
844*104655a5SHervé Poussineau 
845*104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
846*104655a5SHervé Poussineau {
847*104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
848*104655a5SHervé Poussineau 
849*104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
850*104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
851*104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
852*104655a5SHervé Poussineau     dc->props = dp8393x_properties;
853*104655a5SHervé Poussineau }
854*104655a5SHervé Poussineau 
855*104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
856*104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
857*104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
858*104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
859*104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
860*104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
861*104655a5SHervé Poussineau };
862*104655a5SHervé Poussineau 
863*104655a5SHervé Poussineau static void dp8393x_register_types(void)
864*104655a5SHervé Poussineau {
865*104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
866*104655a5SHervé Poussineau }
867*104655a5SHervé Poussineau 
868*104655a5SHervé Poussineau type_init(dp8393x_register_types)
869