xref: /qemu/hw/net/dp8393x.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
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 
20e8d40465SPeter Maydell #include "qemu/osdep.h"
21104655a5SHervé Poussineau #include "hw/sysbus.h"
221422e32dSPaolo Bonzini #include "net/net.h"
23da34e65cSMarkus Armbruster #include "qapi/error.h"
24*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
25104655a5SHervé Poussineau #include "qemu/timer.h"
26f2f62c4dSHervé Poussineau #include <zlib.h>
27a65f56eeSaurel32 
28a65f56eeSaurel32 //#define DEBUG_SONIC
29a65f56eeSaurel32 
3089ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
31a65f56eeSaurel32 
32a65f56eeSaurel32 #ifdef DEBUG_SONIC
33001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
34001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
35a65f56eeSaurel32 static const char* reg_names[] = {
36a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
37a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
38a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
39a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
40a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
41a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
42a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
43a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
44a65f56eeSaurel32 #else
45001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
46a65f56eeSaurel32 #endif
47a65f56eeSaurel32 
48001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
49001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
50a65f56eeSaurel32 
51a65f56eeSaurel32 #define SONIC_CR     0x00
52a65f56eeSaurel32 #define SONIC_DCR    0x01
53a65f56eeSaurel32 #define SONIC_RCR    0x02
54a65f56eeSaurel32 #define SONIC_TCR    0x03
55a65f56eeSaurel32 #define SONIC_IMR    0x04
56a65f56eeSaurel32 #define SONIC_ISR    0x05
57a65f56eeSaurel32 #define SONIC_UTDA   0x06
58a65f56eeSaurel32 #define SONIC_CTDA   0x07
59a65f56eeSaurel32 #define SONIC_TPS    0x08
60a65f56eeSaurel32 #define SONIC_TFC    0x09
61a65f56eeSaurel32 #define SONIC_TSA0   0x0a
62a65f56eeSaurel32 #define SONIC_TSA1   0x0b
63a65f56eeSaurel32 #define SONIC_TFS    0x0c
64a65f56eeSaurel32 #define SONIC_URDA   0x0d
65a65f56eeSaurel32 #define SONIC_CRDA   0x0e
66a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
67a65f56eeSaurel32 #define SONIC_CRBA1  0x10
68a65f56eeSaurel32 #define SONIC_RBWC0  0x11
69a65f56eeSaurel32 #define SONIC_RBWC1  0x12
70a65f56eeSaurel32 #define SONIC_EOBC   0x13
71a65f56eeSaurel32 #define SONIC_URRA   0x14
72a65f56eeSaurel32 #define SONIC_RSA    0x15
73a65f56eeSaurel32 #define SONIC_REA    0x16
74a65f56eeSaurel32 #define SONIC_RRP    0x17
75a65f56eeSaurel32 #define SONIC_RWP    0x18
76a65f56eeSaurel32 #define SONIC_TRBA0  0x19
77a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
78a65f56eeSaurel32 #define SONIC_LLFA   0x1f
79a65f56eeSaurel32 #define SONIC_TTDA   0x20
80a65f56eeSaurel32 #define SONIC_CEP    0x21
81a65f56eeSaurel32 #define SONIC_CAP2   0x22
82a65f56eeSaurel32 #define SONIC_CAP1   0x23
83a65f56eeSaurel32 #define SONIC_CAP0   0x24
84a65f56eeSaurel32 #define SONIC_CE     0x25
85a65f56eeSaurel32 #define SONIC_CDP    0x26
86a65f56eeSaurel32 #define SONIC_CDC    0x27
87a65f56eeSaurel32 #define SONIC_SR     0x28
88a65f56eeSaurel32 #define SONIC_WT0    0x29
89a65f56eeSaurel32 #define SONIC_WT1    0x2a
90a65f56eeSaurel32 #define SONIC_RSC    0x2b
91a65f56eeSaurel32 #define SONIC_CRCT   0x2c
92a65f56eeSaurel32 #define SONIC_FAET   0x2d
93a65f56eeSaurel32 #define SONIC_MPT    0x2e
94a65f56eeSaurel32 #define SONIC_MDT    0x2f
95a65f56eeSaurel32 #define SONIC_DCR2   0x3f
96a65f56eeSaurel32 
97a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
98a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
99a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
100a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
101a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
102a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
103a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
104a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
105a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
106a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
107a65f56eeSaurel32 
108a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
109a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
110a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
111a65f56eeSaurel32 
112a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
113a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
114a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
115a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
116a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
117a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
118a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
119a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
120a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
121a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
122a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
123a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
124a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
125a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
126a65f56eeSaurel32 
127a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
128a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
129a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
130a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
131a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
132a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
133a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
134a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
135a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
136a65f56eeSaurel32 
137a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
138a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
139a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
140a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
141a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
142a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
143a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
144a65f56eeSaurel32 
145104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
146104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
147104655a5SHervé Poussineau 
148a65f56eeSaurel32 typedef struct dp8393xState {
149104655a5SHervé Poussineau     SysBusDevice parent_obj;
150104655a5SHervé Poussineau 
151a65f56eeSaurel32     /* Hardware */
152104655a5SHervé Poussineau     uint8_t it_shift;
153a65f56eeSaurel32     qemu_irq irq;
154a65f56eeSaurel32 #ifdef DEBUG_SONIC
155a65f56eeSaurel32     int irq_level;
156a65f56eeSaurel32 #endif
157a65f56eeSaurel32     QEMUTimer *watchdog;
158a65f56eeSaurel32     int64_t wt_last_update;
15905f41fe3SMark McLoughlin     NICConf conf;
16005f41fe3SMark McLoughlin     NICState *nic;
161024e5bb6SAvi Kivity     MemoryRegion mmio;
16289ae0ff9SHervé Poussineau     MemoryRegion prom;
163a65f56eeSaurel32 
164a65f56eeSaurel32     /* Registers */
165a65f56eeSaurel32     uint8_t cam[16][6];
166a65f56eeSaurel32     uint16_t regs[0x40];
167a65f56eeSaurel32 
168a65f56eeSaurel32     /* Temporaries */
169a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
170a65f56eeSaurel32     int loopback_packet;
171a65f56eeSaurel32 
172a65f56eeSaurel32     /* Memory access */
173104655a5SHervé Poussineau     void *dma_mr;
174dd820513SHervé Poussineau     AddressSpace as;
175a65f56eeSaurel32 } dp8393xState;
176a65f56eeSaurel32 
177581f7b12SPeter Maydell /* Accessor functions for values which are formed by
178581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
179581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
180581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
181581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
182581f7b12SPeter Maydell  */
183581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
184581f7b12SPeter Maydell {
185581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
186581f7b12SPeter Maydell }
187581f7b12SPeter Maydell 
188581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
189581f7b12SPeter Maydell {
190581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
191581f7b12SPeter Maydell }
192581f7b12SPeter Maydell 
193581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
194581f7b12SPeter Maydell {
195581f7b12SPeter Maydell     return (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA];
196581f7b12SPeter Maydell }
197581f7b12SPeter Maydell 
198581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
199581f7b12SPeter Maydell {
200581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
201581f7b12SPeter Maydell }
202581f7b12SPeter Maydell 
203581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
204581f7b12SPeter Maydell {
205581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
206581f7b12SPeter Maydell }
207581f7b12SPeter Maydell 
208581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
209581f7b12SPeter Maydell {
210581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
211581f7b12SPeter Maydell }
212581f7b12SPeter Maydell 
213581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
214581f7b12SPeter Maydell {
215581f7b12SPeter Maydell     return (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA];
216581f7b12SPeter Maydell }
217581f7b12SPeter Maydell 
218581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
219581f7b12SPeter Maydell {
220581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
221581f7b12SPeter Maydell }
222581f7b12SPeter Maydell 
223a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
224a65f56eeSaurel32 {
225a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
226a65f56eeSaurel32 
227a65f56eeSaurel32 #ifdef DEBUG_SONIC
228a65f56eeSaurel32     if (level != s->irq_level) {
229a65f56eeSaurel32         s->irq_level = level;
230a65f56eeSaurel32         if (level) {
231a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
232a65f56eeSaurel32         } else {
233a65f56eeSaurel32             DPRINTF("lower irq\n");
234a65f56eeSaurel32         }
235a65f56eeSaurel32     }
236a65f56eeSaurel32 #endif
237a65f56eeSaurel32 
238a65f56eeSaurel32     qemu_set_irq(s->irq, level);
239a65f56eeSaurel32 }
240a65f56eeSaurel32 
2413df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
242a65f56eeSaurel32 {
243a65f56eeSaurel32     uint16_t data[8];
244a65f56eeSaurel32     int width, size;
245a65f56eeSaurel32     uint16_t index = 0;
246a65f56eeSaurel32 
247a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
248a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
249a65f56eeSaurel32 
250a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
251a65f56eeSaurel32         /* Fill current entry */
252581f7b12SPeter Maydell         address_space_rw(&s->as, dp8393x_cdp(s),
253dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
254a65f56eeSaurel32         s->cam[index][0] = data[1 * width] & 0xff;
255a65f56eeSaurel32         s->cam[index][1] = data[1 * width] >> 8;
256a65f56eeSaurel32         s->cam[index][2] = data[2 * width] & 0xff;
257a65f56eeSaurel32         s->cam[index][3] = data[2 * width] >> 8;
258a65f56eeSaurel32         s->cam[index][4] = data[3 * width] & 0xff;
259a65f56eeSaurel32         s->cam[index][5] = data[3 * width] >> 8;
260a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
261a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
262a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
263a65f56eeSaurel32         /* Move to next entry */
264a65f56eeSaurel32         s->regs[SONIC_CDC]--;
265a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
266a65f56eeSaurel32         index++;
267a65f56eeSaurel32     }
268a65f56eeSaurel32 
269a65f56eeSaurel32     /* Read CAM enable */
270581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_cdp(s),
271dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
272a65f56eeSaurel32     s->regs[SONIC_CE] = data[0 * width];
273a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
274a65f56eeSaurel32 
275a65f56eeSaurel32     /* Done */
276a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
277a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
278a65f56eeSaurel32     dp8393x_update_irq(s);
279a65f56eeSaurel32 }
280a65f56eeSaurel32 
2813df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
282a65f56eeSaurel32 {
283a65f56eeSaurel32     uint16_t data[8];
284a65f56eeSaurel32     int width, size;
285a65f56eeSaurel32 
286a65f56eeSaurel32     /* Read memory */
287a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
288a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
289581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_rrp(s),
290dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
291a65f56eeSaurel32 
292a65f56eeSaurel32     /* Update SONIC registers */
293a65f56eeSaurel32     s->regs[SONIC_CRBA0] = data[0 * width];
294a65f56eeSaurel32     s->regs[SONIC_CRBA1] = data[1 * width];
295a65f56eeSaurel32     s->regs[SONIC_RBWC0] = data[2 * width];
296a65f56eeSaurel32     s->regs[SONIC_RBWC1] = data[3 * width];
297a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
298a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
299a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
300a65f56eeSaurel32 
301a65f56eeSaurel32     /* Go to next entry */
302a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
303a65f56eeSaurel32 
304a65f56eeSaurel32     /* Handle wrap */
305a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
306a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
307a65f56eeSaurel32     }
308a65f56eeSaurel32 
309a65f56eeSaurel32     /* Check resource exhaustion */
310a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
311a65f56eeSaurel32     {
312a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
313a65f56eeSaurel32         dp8393x_update_irq(s);
314a65f56eeSaurel32     }
315a65f56eeSaurel32 
316a65f56eeSaurel32     /* Done */
317a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
318a65f56eeSaurel32 }
319a65f56eeSaurel32 
3203df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
321a65f56eeSaurel32 {
322bc72ad67SAlex Bligh     timer_del(s->watchdog);
323a65f56eeSaurel32 
324a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
325a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
326a65f56eeSaurel32 }
327a65f56eeSaurel32 
3283df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
329a65f56eeSaurel32 {
330a65f56eeSaurel32     uint32_t ticks;
331a65f56eeSaurel32     int64_t delay;
332a65f56eeSaurel32 
333a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
334bc72ad67SAlex Bligh         timer_del(s->watchdog);
335a65f56eeSaurel32         return;
336a65f56eeSaurel32     }
337a65f56eeSaurel32 
338581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
339bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
34073bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
341bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
342a65f56eeSaurel32 }
343a65f56eeSaurel32 
3443df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
345a65f56eeSaurel32 {
346a65f56eeSaurel32     int64_t elapsed;
347a65f56eeSaurel32     uint32_t val;
348a65f56eeSaurel32 
349a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
350bc72ad67SAlex Bligh         timer_del(s->watchdog);
351a65f56eeSaurel32         return;
352a65f56eeSaurel32     }
353a65f56eeSaurel32 
354bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
355581f7b12SPeter Maydell     val = dp8393x_wt(s);
356a65f56eeSaurel32     val -= elapsed / 5000000;
357a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
358a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3593df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
360a65f56eeSaurel32 
361a65f56eeSaurel32 }
362a65f56eeSaurel32 
3633df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
364a65f56eeSaurel32 {
365a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3663df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
367a65f56eeSaurel32 }
368a65f56eeSaurel32 
3693df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
370a65f56eeSaurel32 {
371a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3723df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
373a65f56eeSaurel32 }
374a65f56eeSaurel32 
3754594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc);
3764594f93aSFam Zheng 
3773df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
378a65f56eeSaurel32 {
379a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
3804594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
3814594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
3824594f93aSFam Zheng     }
383a65f56eeSaurel32 }
384a65f56eeSaurel32 
3853df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
386a65f56eeSaurel32 {
387a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
388a65f56eeSaurel32 }
389a65f56eeSaurel32 
3903df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
391a65f56eeSaurel32 {
392b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
393a65f56eeSaurel32     uint16_t data[12];
394a65f56eeSaurel32     int width, size;
395a65f56eeSaurel32     int tx_len, len;
396a65f56eeSaurel32     uint16_t i;
397a65f56eeSaurel32 
398a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
399a65f56eeSaurel32 
400a65f56eeSaurel32     while (1) {
401a65f56eeSaurel32         /* Read memory */
402a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
403a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
404581f7b12SPeter Maydell         DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
405dd820513SHervé Poussineau         address_space_rw(&s->as,
406581f7b12SPeter Maydell             dp8393x_ttda(s) + sizeof(uint16_t) * width,
407dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
408a65f56eeSaurel32         tx_len = 0;
409a65f56eeSaurel32 
410a65f56eeSaurel32         /* Update registers */
411a65f56eeSaurel32         s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
412a65f56eeSaurel32         s->regs[SONIC_TPS] = data[1 * width];
413a65f56eeSaurel32         s->regs[SONIC_TFC] = data[2 * width];
414a65f56eeSaurel32         s->regs[SONIC_TSA0] = data[3 * width];
415a65f56eeSaurel32         s->regs[SONIC_TSA1] = data[4 * width];
416a65f56eeSaurel32         s->regs[SONIC_TFS] = data[5 * width];
417a65f56eeSaurel32 
418a65f56eeSaurel32         /* Handle programmable interrupt */
419a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
420a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
421a65f56eeSaurel32         } else {
422a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
423a65f56eeSaurel32         }
424a65f56eeSaurel32 
425a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
426a65f56eeSaurel32             /* Append fragment */
427a65f56eeSaurel32             len = s->regs[SONIC_TFS];
428a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
429a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
430a65f56eeSaurel32             }
431581f7b12SPeter Maydell             address_space_rw(&s->as, dp8393x_tsa(s),
432dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
433a65f56eeSaurel32             tx_len += len;
434a65f56eeSaurel32 
435a65f56eeSaurel32             i++;
436a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
437a65f56eeSaurel32                 /* Read next fragment details */
438a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
439dd820513SHervé Poussineau                 address_space_rw(&s->as,
440581f7b12SPeter Maydell                     dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width,
441dd820513SHervé Poussineau                     MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
442a65f56eeSaurel32                 s->regs[SONIC_TSA0] = data[0 * width];
443a65f56eeSaurel32                 s->regs[SONIC_TSA1] = data[1 * width];
444a65f56eeSaurel32                 s->regs[SONIC_TFS] = data[2 * width];
445a65f56eeSaurel32             }
446a65f56eeSaurel32         }
447a65f56eeSaurel32 
448a65f56eeSaurel32         /* Handle Ethernet checksum */
449a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
450a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
451a65f56eeSaurel32              * which don't have one */
452a65f56eeSaurel32         } else {
453a65f56eeSaurel32             /* Remove existing FCS */
454a65f56eeSaurel32             tx_len -= 4;
455a65f56eeSaurel32         }
456a65f56eeSaurel32 
457a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
458a65f56eeSaurel32             /* Loopback */
459a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
460b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
461a65f56eeSaurel32                 s->loopback_packet = 1;
462b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
463a65f56eeSaurel32             }
464a65f56eeSaurel32         } else {
465a65f56eeSaurel32             /* Transmit packet */
466b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
467a65f56eeSaurel32         }
468a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
469a65f56eeSaurel32 
470a65f56eeSaurel32         /* Write status */
471a65f56eeSaurel32         data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
472a65f56eeSaurel32         size = sizeof(uint16_t) * width;
473dd820513SHervé Poussineau         address_space_rw(&s->as,
474581f7b12SPeter Maydell             dp8393x_ttda(s),
475dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
476a65f56eeSaurel32 
477a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
478a65f56eeSaurel32             /* Read footer of packet */
479a65f56eeSaurel32             size = sizeof(uint16_t) * width;
480dd820513SHervé Poussineau             address_space_rw(&s->as,
481581f7b12SPeter Maydell                 dp8393x_ttda(s) +
482581f7b12SPeter Maydell                              sizeof(uint16_t) *
483581f7b12SPeter Maydell                              (4 + 3 * s->regs[SONIC_TFC]) * width,
484dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
485a65f56eeSaurel32             s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
486a65f56eeSaurel32             if (data[0 * width] & 0x1) {
487a65f56eeSaurel32                 /* EOL detected */
488a65f56eeSaurel32                 break;
489a65f56eeSaurel32             }
490a65f56eeSaurel32         }
491a65f56eeSaurel32     }
492a65f56eeSaurel32 
493a65f56eeSaurel32     /* Done */
494a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
495a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
496a65f56eeSaurel32     dp8393x_update_irq(s);
497a65f56eeSaurel32 }
498a65f56eeSaurel32 
4993df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
500a65f56eeSaurel32 {
501a65f56eeSaurel32     /* Nothing to do */
502a65f56eeSaurel32 }
503a65f56eeSaurel32 
5043df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
505a65f56eeSaurel32 {
506a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
507a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
508a65f56eeSaurel32         return;
509a65f56eeSaurel32     }
510a65f56eeSaurel32 
511a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
512a65f56eeSaurel32 
513a65f56eeSaurel32     if (command & SONIC_CR_HTX)
5143df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
515a65f56eeSaurel32     if (command & SONIC_CR_TXP)
5163df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
517a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
5183df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
519a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
5203df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
521a65f56eeSaurel32     if (command & SONIC_CR_STP)
5223df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
523a65f56eeSaurel32     if (command & SONIC_CR_ST)
5243df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
525a65f56eeSaurel32     if (command & SONIC_CR_RST)
5263df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
527a65f56eeSaurel32     if (command & SONIC_CR_RRRA)
5283df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
529a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
5303df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
531a65f56eeSaurel32 }
532a65f56eeSaurel32 
53384689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
534a65f56eeSaurel32 {
53584689cbbSHervé Poussineau     dp8393xState *s = opaque;
53684689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
537a65f56eeSaurel32     uint16_t val = 0;
538a65f56eeSaurel32 
539a65f56eeSaurel32     switch (reg) {
540a65f56eeSaurel32         /* Update data before reading it */
541a65f56eeSaurel32         case SONIC_WT0:
542a65f56eeSaurel32         case SONIC_WT1:
5433df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
544a65f56eeSaurel32             val = s->regs[reg];
545a65f56eeSaurel32             break;
546a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
547a65f56eeSaurel32         case SONIC_CAP2:
548a65f56eeSaurel32         case SONIC_CAP1:
549a65f56eeSaurel32         case SONIC_CAP0:
550a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
551a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
552a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
553a65f56eeSaurel32             }
554a65f56eeSaurel32             break;
555a65f56eeSaurel32         /* All other registers have no special contrainst */
556a65f56eeSaurel32         default:
557a65f56eeSaurel32             val = s->regs[reg];
558a65f56eeSaurel32     }
559a65f56eeSaurel32 
560a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
561a65f56eeSaurel32 
562a65f56eeSaurel32     return val;
563a65f56eeSaurel32 }
564a65f56eeSaurel32 
56584689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
56684689cbbSHervé Poussineau                           unsigned int size)
567a65f56eeSaurel32 {
56884689cbbSHervé Poussineau     dp8393xState *s = opaque;
56984689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
57084689cbbSHervé Poussineau 
57184689cbbSHervé Poussineau     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
572a65f56eeSaurel32 
573a65f56eeSaurel32     switch (reg) {
574a65f56eeSaurel32         /* Command register */
575a65f56eeSaurel32         case SONIC_CR:
5763df5de64SHervé Poussineau             dp8393x_do_command(s, data);
577a65f56eeSaurel32             break;
578a65f56eeSaurel32         /* Prevent write to read-only registers */
579a65f56eeSaurel32         case SONIC_CAP2:
580a65f56eeSaurel32         case SONIC_CAP1:
581a65f56eeSaurel32         case SONIC_CAP0:
582a65f56eeSaurel32         case SONIC_SR:
583a65f56eeSaurel32         case SONIC_MDT:
584a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
585a65f56eeSaurel32             break;
586a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
587a65f56eeSaurel32         case SONIC_DCR:
588a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
58984689cbbSHervé Poussineau                 s->regs[reg] = data & 0xbfff;
590a65f56eeSaurel32             } else {
591a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
592a65f56eeSaurel32             }
593a65f56eeSaurel32             break;
594a65f56eeSaurel32         case SONIC_DCR2:
595a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
59684689cbbSHervé Poussineau                 s->regs[reg] = data & 0xf017;
597a65f56eeSaurel32             } else {
598a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
599a65f56eeSaurel32             }
600a65f56eeSaurel32             break;
601a65f56eeSaurel32         /* 12 lower bytes are Read Only */
602a65f56eeSaurel32         case SONIC_TCR:
60384689cbbSHervé Poussineau             s->regs[reg] = data & 0xf000;
604a65f56eeSaurel32             break;
605a65f56eeSaurel32         /* 9 lower bytes are Read Only */
606a65f56eeSaurel32         case SONIC_RCR:
60784689cbbSHervé Poussineau             s->regs[reg] = data & 0xffe0;
608a65f56eeSaurel32             break;
609a65f56eeSaurel32         /* Ignore most significant bit */
610a65f56eeSaurel32         case SONIC_IMR:
61184689cbbSHervé Poussineau             s->regs[reg] = data & 0x7fff;
612a65f56eeSaurel32             dp8393x_update_irq(s);
613a65f56eeSaurel32             break;
614a65f56eeSaurel32         /* Clear bits by writing 1 to them */
615a65f56eeSaurel32         case SONIC_ISR:
61684689cbbSHervé Poussineau             data &= s->regs[reg];
61784689cbbSHervé Poussineau             s->regs[reg] &= ~data;
61884689cbbSHervé Poussineau             if (data & SONIC_ISR_RBE) {
6193df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
620a65f56eeSaurel32             }
621a65f56eeSaurel32             dp8393x_update_irq(s);
6224594f93aSFam Zheng             if (dp8393x_can_receive(s->nic->ncs)) {
6234594f93aSFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
6244594f93aSFam Zheng             }
625a65f56eeSaurel32             break;
626a65f56eeSaurel32         /* Ignore least significant bit */
627a65f56eeSaurel32         case SONIC_RSA:
628a65f56eeSaurel32         case SONIC_REA:
629a65f56eeSaurel32         case SONIC_RRP:
630a65f56eeSaurel32         case SONIC_RWP:
63184689cbbSHervé Poussineau             s->regs[reg] = data & 0xfffe;
632a65f56eeSaurel32             break;
633a65f56eeSaurel32         /* Invert written value for some registers */
634a65f56eeSaurel32         case SONIC_CRCT:
635a65f56eeSaurel32         case SONIC_FAET:
636a65f56eeSaurel32         case SONIC_MPT:
63784689cbbSHervé Poussineau             s->regs[reg] = data ^ 0xffff;
638a65f56eeSaurel32             break;
639a65f56eeSaurel32         /* All other registers have no special contrainst */
640a65f56eeSaurel32         default:
64184689cbbSHervé Poussineau             s->regs[reg] = data;
642a65f56eeSaurel32     }
643a65f56eeSaurel32 
644a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6453df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
646a65f56eeSaurel32     }
647a65f56eeSaurel32 }
648a65f56eeSaurel32 
64984689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
65084689cbbSHervé Poussineau     .read = dp8393x_read,
65184689cbbSHervé Poussineau     .write = dp8393x_write,
65284689cbbSHervé Poussineau     .impl.min_access_size = 2,
65384689cbbSHervé Poussineau     .impl.max_access_size = 2,
65484689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
65584689cbbSHervé Poussineau };
65684689cbbSHervé Poussineau 
657a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
658a65f56eeSaurel32 {
659a65f56eeSaurel32     dp8393xState *s = opaque;
660a65f56eeSaurel32 
661a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
662a65f56eeSaurel32         return;
663a65f56eeSaurel32     }
664a65f56eeSaurel32 
665a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
666a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
6673df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
668a65f56eeSaurel32 
669a65f56eeSaurel32     /* Signal underflow */
670a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
671a65f56eeSaurel32     dp8393x_update_irq(s);
672a65f56eeSaurel32 }
673a65f56eeSaurel32 
6743df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
675a65f56eeSaurel32 {
676cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
677a65f56eeSaurel32 
678a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
679a65f56eeSaurel32         return 0;
680a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
681a65f56eeSaurel32         return 0;
682a65f56eeSaurel32     return 1;
683a65f56eeSaurel32 }
684a65f56eeSaurel32 
6853df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
6863df5de64SHervé Poussineau                                   int size)
687a65f56eeSaurel32 {
688a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
689a65f56eeSaurel32     int i;
690a65f56eeSaurel32 
691a65f56eeSaurel32     /* Check promiscuous mode */
692a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
693a65f56eeSaurel32         return 0;
694a65f56eeSaurel32     }
695a65f56eeSaurel32 
696a65f56eeSaurel32     /* Check multicast packets */
697a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
698a65f56eeSaurel32         return SONIC_RCR_MC;
699a65f56eeSaurel32     }
700a65f56eeSaurel32 
701a65f56eeSaurel32     /* Check broadcast */
702a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
703a65f56eeSaurel32         return SONIC_RCR_BC;
704a65f56eeSaurel32     }
705a65f56eeSaurel32 
706a65f56eeSaurel32     /* Check CAM */
707a65f56eeSaurel32     for (i = 0; i < 16; i++) {
708a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
709a65f56eeSaurel32              /* Entry enabled */
710a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
711a65f56eeSaurel32                  return 0;
712a65f56eeSaurel32              }
713a65f56eeSaurel32         }
714a65f56eeSaurel32     }
715a65f56eeSaurel32 
716a65f56eeSaurel32     return -1;
717a65f56eeSaurel32 }
718a65f56eeSaurel32 
7193df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7203df5de64SHervé Poussineau                                size_t size)
721a65f56eeSaurel32 {
722cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
723a65f56eeSaurel32     uint16_t data[10];
724a65f56eeSaurel32     int packet_type;
725a65f56eeSaurel32     uint32_t available, address;
726a65f56eeSaurel32     int width, rx_len = size;
727a65f56eeSaurel32     uint32_t checksum;
728a65f56eeSaurel32 
729a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
730a65f56eeSaurel32 
731a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
732a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
733a65f56eeSaurel32 
7343df5de64SHervé Poussineau     packet_type = dp8393x_receive_filter(s, buf, size);
735a65f56eeSaurel32     if (packet_type < 0) {
736a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
7374f1c942bSMark McLoughlin         return -1;
738a65f56eeSaurel32     }
739a65f56eeSaurel32 
740a65f56eeSaurel32     /* XXX: Check byte ordering */
741a65f56eeSaurel32 
742a65f56eeSaurel32     /* Check for EOL */
743a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
744a65f56eeSaurel32         /* Are we still in resource exhaustion? */
745a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
746581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
747dd820513SHervé Poussineau         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
748dd820513SHervé Poussineau                          (uint8_t *)data, size, 0);
749a65f56eeSaurel32         if (data[0 * width] & 0x1) {
750a65f56eeSaurel32             /* Still EOL ; stop reception */
7514f1c942bSMark McLoughlin             return -1;
752a65f56eeSaurel32         } else {
753a65f56eeSaurel32             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
754a65f56eeSaurel32         }
755a65f56eeSaurel32     }
756a65f56eeSaurel32 
757a65f56eeSaurel32     /* Save current position */
758a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
759a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
760a65f56eeSaurel32 
761a65f56eeSaurel32     /* Calculate the ethernet checksum */
762a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
763a65f56eeSaurel32 
764a65f56eeSaurel32     /* Put packet into RBA */
765581f7b12SPeter Maydell     DPRINTF("Receive packet at %08x\n", dp8393x_crba(s));
766581f7b12SPeter Maydell     address = dp8393x_crba(s);
767dd820513SHervé Poussineau     address_space_rw(&s->as, address,
768dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
769a65f56eeSaurel32     address += rx_len;
770dd820513SHervé Poussineau     address_space_rw(&s->as, address,
771dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
772a65f56eeSaurel32     rx_len += 4;
773a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
774a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
775581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
776a65f56eeSaurel32     available -= rx_len / 2;
777a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
778a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
779a65f56eeSaurel32 
780a65f56eeSaurel32     /* Update status */
781581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
782a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
783a65f56eeSaurel32     }
784a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
785a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
786a65f56eeSaurel32     if (s->loopback_packet) {
787a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
788a65f56eeSaurel32         s->loopback_packet = 0;
789a65f56eeSaurel32     }
790a65f56eeSaurel32 
791a65f56eeSaurel32     /* Write status to memory */
792581f7b12SPeter Maydell     DPRINTF("Write status at %08x\n", dp8393x_crda(s));
793a65f56eeSaurel32     data[0 * width] = s->regs[SONIC_RCR]; /* status */
794a65f56eeSaurel32     data[1 * width] = rx_len; /* byte count */
795a65f56eeSaurel32     data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
796a65f56eeSaurel32     data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
797a65f56eeSaurel32     data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
798a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
799581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_crda(s),
800dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
801a65f56eeSaurel32 
802a65f56eeSaurel32     /* Move to next descriptor */
803a65f56eeSaurel32     size = sizeof(uint16_t) * width;
804581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
805dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
806a65f56eeSaurel32     s->regs[SONIC_LLFA] = data[0 * width];
807a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
808a65f56eeSaurel32         /* EOL detected */
809a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
810a65f56eeSaurel32     } else {
811a65f56eeSaurel32         data[0 * width] = 0; /* in_use */
812581f7b12SPeter Maydell         address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 6 * width,
813409b52bfSHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
814a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
815a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
816a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
817a65f56eeSaurel32 
818a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
819a65f56eeSaurel32             /* Read next RRA */
8203df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
821a65f56eeSaurel32         }
822a65f56eeSaurel32     }
823a65f56eeSaurel32 
824a65f56eeSaurel32     /* Done */
825a65f56eeSaurel32     dp8393x_update_irq(s);
8264f1c942bSMark McLoughlin 
8274f1c942bSMark McLoughlin     return size;
828a65f56eeSaurel32 }
829a65f56eeSaurel32 
830104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
831a65f56eeSaurel32 {
832104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
833bc72ad67SAlex Bligh     timer_del(s->watchdog);
834a65f56eeSaurel32 
835bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
836a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
837a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
838a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
839a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
840a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
841a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
842a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
843a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
844a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
845a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
846a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
847a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
848a65f56eeSaurel32 
849a65f56eeSaurel32     /* Network cable is connected */
850a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
851a65f56eeSaurel32 
852a65f56eeSaurel32     dp8393x_update_irq(s);
853a65f56eeSaurel32 }
854a65f56eeSaurel32 
85505f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
856f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
85705f41fe3SMark McLoughlin     .size = sizeof(NICState),
8583df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8593df5de64SHervé Poussineau     .receive = dp8393x_receive,
86005f41fe3SMark McLoughlin };
86105f41fe3SMark McLoughlin 
862104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
863a65f56eeSaurel32 {
864104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
865104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
866a65f56eeSaurel32 
867104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
86889ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
869104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
870104655a5SHervé Poussineau }
871a65f56eeSaurel32 
872104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
873104655a5SHervé Poussineau {
874104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
87589ae0ff9SHervé Poussineau     int i, checksum;
87689ae0ff9SHervé Poussineau     uint8_t *prom;
87752579c68SHervé Poussineau     Error *local_err = NULL;
878a65f56eeSaurel32 
879104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
880104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
881104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
882104655a5SHervé Poussineau 
883104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
884104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
885104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
886104655a5SHervé Poussineau 
887bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
888a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
88989ae0ff9SHervé Poussineau 
8908fad0a65SPeter Maydell     memory_region_init_ram(&s->prom, OBJECT(dev),
89152579c68SHervé Poussineau                            "dp8393x-prom", SONIC_PROM_SIZE, &local_err);
89252579c68SHervé Poussineau     if (local_err) {
89352579c68SHervé Poussineau         error_propagate(errp, local_err);
89452579c68SHervé Poussineau         return;
89552579c68SHervé Poussineau     }
89652579c68SHervé Poussineau     memory_region_set_readonly(&s->prom, true);
89789ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
89889ae0ff9SHervé Poussineau     checksum = 0;
89989ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
90089ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
90189ae0ff9SHervé Poussineau         checksum += prom[i];
90289ae0ff9SHervé Poussineau         if (checksum > 0xff) {
90389ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
90489ae0ff9SHervé Poussineau         }
90589ae0ff9SHervé Poussineau     }
90689ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
907a65f56eeSaurel32 }
908104655a5SHervé Poussineau 
9091670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9101670735dSHervé Poussineau     .name = "dp8393x",
9111670735dSHervé Poussineau     .version_id = 0,
9121670735dSHervé Poussineau     .minimum_version_id = 0,
9131670735dSHervé Poussineau     .fields = (VMStateField []) {
9141670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
9151670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
9161670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9171670735dSHervé Poussineau     }
9181670735dSHervé Poussineau };
9191670735dSHervé Poussineau 
920104655a5SHervé Poussineau static Property dp8393x_properties[] = {
921104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
922104655a5SHervé Poussineau     DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
923104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
924104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
925104655a5SHervé Poussineau };
926104655a5SHervé Poussineau 
927104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
928104655a5SHervé Poussineau {
929104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
930104655a5SHervé Poussineau 
931104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
932104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
933104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
9341670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
935104655a5SHervé Poussineau     dc->props = dp8393x_properties;
936f6351288SHervé Poussineau     /* Reason: dma_mr property can't be set */
937e90f2a8cSEduardo Habkost     dc->user_creatable = false;
938104655a5SHervé Poussineau }
939104655a5SHervé Poussineau 
940104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
941104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
942104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
943104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
944104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
945104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
946104655a5SHervé Poussineau };
947104655a5SHervé Poussineau 
948104655a5SHervé Poussineau static void dp8393x_register_types(void)
949104655a5SHervé Poussineau {
950104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
951104655a5SHervé Poussineau }
952104655a5SHervé Poussineau 
953104655a5SHervé Poussineau type_init(dp8393x_register_types)
954