xref: /qemu/hw/net/dp8393x.c (revision a3cce2825a0b12bb717a5106daaca245557cc9ae)
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"
2164552b6bSMarkus Armbruster #include "hw/irq.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
23104655a5SHervé Poussineau #include "hw/sysbus.h"
24d6454270SMarkus Armbruster #include "migration/vmstate.h"
251422e32dSPaolo Bonzini #include "net/net.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
28104655a5SHervé Poussineau #include "qemu/timer.h"
29f2f62c4dSHervé Poussineau #include <zlib.h>
30a65f56eeSaurel32 
31a65f56eeSaurel32 //#define DEBUG_SONIC
32a65f56eeSaurel32 
3389ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
34a65f56eeSaurel32 
35a65f56eeSaurel32 #ifdef DEBUG_SONIC
36001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
37001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
38a65f56eeSaurel32 static const char* reg_names[] = {
39a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
40a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
41a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
42a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
43a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
44a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
45a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
46a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
47a65f56eeSaurel32 #else
48001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
49a65f56eeSaurel32 #endif
50a65f56eeSaurel32 
51001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
52001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
53a65f56eeSaurel32 
54a65f56eeSaurel32 #define SONIC_CR     0x00
55a65f56eeSaurel32 #define SONIC_DCR    0x01
56a65f56eeSaurel32 #define SONIC_RCR    0x02
57a65f56eeSaurel32 #define SONIC_TCR    0x03
58a65f56eeSaurel32 #define SONIC_IMR    0x04
59a65f56eeSaurel32 #define SONIC_ISR    0x05
60a65f56eeSaurel32 #define SONIC_UTDA   0x06
61a65f56eeSaurel32 #define SONIC_CTDA   0x07
62a65f56eeSaurel32 #define SONIC_TPS    0x08
63a65f56eeSaurel32 #define SONIC_TFC    0x09
64a65f56eeSaurel32 #define SONIC_TSA0   0x0a
65a65f56eeSaurel32 #define SONIC_TSA1   0x0b
66a65f56eeSaurel32 #define SONIC_TFS    0x0c
67a65f56eeSaurel32 #define SONIC_URDA   0x0d
68a65f56eeSaurel32 #define SONIC_CRDA   0x0e
69a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
70a65f56eeSaurel32 #define SONIC_CRBA1  0x10
71a65f56eeSaurel32 #define SONIC_RBWC0  0x11
72a65f56eeSaurel32 #define SONIC_RBWC1  0x12
73a65f56eeSaurel32 #define SONIC_EOBC   0x13
74a65f56eeSaurel32 #define SONIC_URRA   0x14
75a65f56eeSaurel32 #define SONIC_RSA    0x15
76a65f56eeSaurel32 #define SONIC_REA    0x16
77a65f56eeSaurel32 #define SONIC_RRP    0x17
78a65f56eeSaurel32 #define SONIC_RWP    0x18
79a65f56eeSaurel32 #define SONIC_TRBA0  0x19
80a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
81a65f56eeSaurel32 #define SONIC_LLFA   0x1f
82a65f56eeSaurel32 #define SONIC_TTDA   0x20
83a65f56eeSaurel32 #define SONIC_CEP    0x21
84a65f56eeSaurel32 #define SONIC_CAP2   0x22
85a65f56eeSaurel32 #define SONIC_CAP1   0x23
86a65f56eeSaurel32 #define SONIC_CAP0   0x24
87a65f56eeSaurel32 #define SONIC_CE     0x25
88a65f56eeSaurel32 #define SONIC_CDP    0x26
89a65f56eeSaurel32 #define SONIC_CDC    0x27
90a65f56eeSaurel32 #define SONIC_SR     0x28
91a65f56eeSaurel32 #define SONIC_WT0    0x29
92a65f56eeSaurel32 #define SONIC_WT1    0x2a
93a65f56eeSaurel32 #define SONIC_RSC    0x2b
94a65f56eeSaurel32 #define SONIC_CRCT   0x2c
95a65f56eeSaurel32 #define SONIC_FAET   0x2d
96a65f56eeSaurel32 #define SONIC_MPT    0x2e
97a65f56eeSaurel32 #define SONIC_MDT    0x2f
98a65f56eeSaurel32 #define SONIC_DCR2   0x3f
99a65f56eeSaurel32 
100a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
101a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
102a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
103a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
104a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
105a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
106a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
107a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
108a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
109a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
110a65f56eeSaurel32 
111a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
112a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
113a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
114a65f56eeSaurel32 
115a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
116a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
117a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
118a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
119a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
120a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
121a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
122a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
123a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
124a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
125a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
126a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
127a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
128a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
129a65f56eeSaurel32 
130a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
131a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
132a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
133a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
134a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
135a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
136a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
137a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
138a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
139a65f56eeSaurel32 
140a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
141a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
142a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
143a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
144a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
145a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
146a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
147a65f56eeSaurel32 
14888f632fbSFinn Thain #define SONIC_DESC_EOL   0x0001
14988f632fbSFinn Thain #define SONIC_DESC_ADDR  0xFFFE
15088f632fbSFinn Thain 
151104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
152104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
153104655a5SHervé Poussineau 
154a65f56eeSaurel32 typedef struct dp8393xState {
155104655a5SHervé Poussineau     SysBusDevice parent_obj;
156104655a5SHervé Poussineau 
157a65f56eeSaurel32     /* Hardware */
158104655a5SHervé Poussineau     uint8_t it_shift;
159be920841SLaurent Vivier     bool big_endian;
160a65f56eeSaurel32     qemu_irq irq;
161a65f56eeSaurel32 #ifdef DEBUG_SONIC
162a65f56eeSaurel32     int irq_level;
163a65f56eeSaurel32 #endif
164a65f56eeSaurel32     QEMUTimer *watchdog;
165a65f56eeSaurel32     int64_t wt_last_update;
16605f41fe3SMark McLoughlin     NICConf conf;
16705f41fe3SMark McLoughlin     NICState *nic;
168024e5bb6SAvi Kivity     MemoryRegion mmio;
16989ae0ff9SHervé Poussineau     MemoryRegion prom;
170a65f56eeSaurel32 
171a65f56eeSaurel32     /* Registers */
172a65f56eeSaurel32     uint8_t cam[16][6];
173a65f56eeSaurel32     uint16_t regs[0x40];
174a65f56eeSaurel32 
175a65f56eeSaurel32     /* Temporaries */
176a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
177af9f0be3SLaurent Vivier     uint16_t data[12];
178a65f56eeSaurel32     int loopback_packet;
179a65f56eeSaurel32 
180a65f56eeSaurel32     /* Memory access */
1813110ce81SMarc-André Lureau     MemoryRegion *dma_mr;
182dd820513SHervé Poussineau     AddressSpace as;
183a65f56eeSaurel32 } dp8393xState;
184a65f56eeSaurel32 
185581f7b12SPeter Maydell /* Accessor functions for values which are formed by
186581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
187581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
188581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
189581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
190581f7b12SPeter Maydell  */
191581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
192581f7b12SPeter Maydell {
193581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
194581f7b12SPeter Maydell }
195581f7b12SPeter Maydell 
196581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
197581f7b12SPeter Maydell {
198581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
199581f7b12SPeter Maydell }
200581f7b12SPeter Maydell 
201581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
202581f7b12SPeter Maydell {
20388f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
20488f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
205581f7b12SPeter Maydell }
206581f7b12SPeter Maydell 
207581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
208581f7b12SPeter Maydell {
209581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
210581f7b12SPeter Maydell }
211581f7b12SPeter Maydell 
212581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
213581f7b12SPeter Maydell {
214581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
215581f7b12SPeter Maydell }
216581f7b12SPeter Maydell 
217581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
218581f7b12SPeter Maydell {
219581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
220581f7b12SPeter Maydell }
221581f7b12SPeter Maydell 
222581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
223581f7b12SPeter Maydell {
22488f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
22588f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
226581f7b12SPeter Maydell }
227581f7b12SPeter Maydell 
228581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
229581f7b12SPeter Maydell {
230581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
231581f7b12SPeter Maydell }
232581f7b12SPeter Maydell 
233af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset)
234be920841SLaurent Vivier {
235be920841SLaurent Vivier     uint16_t val;
236be920841SLaurent Vivier 
237be920841SLaurent Vivier     if (s->big_endian) {
238af9f0be3SLaurent Vivier         val = be16_to_cpu(s->data[offset * width + width - 1]);
239be920841SLaurent Vivier     } else {
240af9f0be3SLaurent Vivier         val = le16_to_cpu(s->data[offset * width]);
241be920841SLaurent Vivier     }
242be920841SLaurent Vivier     return val;
243be920841SLaurent Vivier }
244be920841SLaurent Vivier 
245af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset,
246be920841SLaurent Vivier                         uint16_t val)
247be920841SLaurent Vivier {
248be920841SLaurent Vivier     if (s->big_endian) {
2493fe9a838SFinn Thain         if (width == 2) {
2503fe9a838SFinn Thain             s->data[offset * 2] = 0;
2513fe9a838SFinn Thain             s->data[offset * 2 + 1] = cpu_to_be16(val);
252be920841SLaurent Vivier         } else {
2533fe9a838SFinn Thain             s->data[offset] = cpu_to_be16(val);
2543fe9a838SFinn Thain         }
2553fe9a838SFinn Thain     } else {
2563fe9a838SFinn Thain         if (width == 2) {
2573fe9a838SFinn Thain             s->data[offset * 2] = cpu_to_le16(val);
2583fe9a838SFinn Thain             s->data[offset * 2 + 1] = 0;
2593fe9a838SFinn Thain         } else {
2603fe9a838SFinn Thain             s->data[offset] = cpu_to_le16(val);
2613fe9a838SFinn Thain         }
262be920841SLaurent Vivier     }
263be920841SLaurent Vivier }
264be920841SLaurent Vivier 
265a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
266a65f56eeSaurel32 {
267a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
268a65f56eeSaurel32 
269a65f56eeSaurel32 #ifdef DEBUG_SONIC
270a65f56eeSaurel32     if (level != s->irq_level) {
271a65f56eeSaurel32         s->irq_level = level;
272a65f56eeSaurel32         if (level) {
273a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
274a65f56eeSaurel32         } else {
275a65f56eeSaurel32             DPRINTF("lower irq\n");
276a65f56eeSaurel32         }
277a65f56eeSaurel32     }
278a65f56eeSaurel32 #endif
279a65f56eeSaurel32 
280a65f56eeSaurel32     qemu_set_irq(s->irq, level);
281a65f56eeSaurel32 }
282a65f56eeSaurel32 
2833df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
284a65f56eeSaurel32 {
285a65f56eeSaurel32     int width, size;
286a65f56eeSaurel32     uint16_t index = 0;
287a65f56eeSaurel32 
288a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
289a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
290a65f56eeSaurel32 
291a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
292a65f56eeSaurel32         /* Fill current entry */
29319f70347SPeter Maydell         address_space_read(&s->as, dp8393x_cdp(s),
29419f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
295af9f0be3SLaurent Vivier         s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff;
296af9f0be3SLaurent Vivier         s->cam[index][1] = dp8393x_get(s, width, 1) >> 8;
297af9f0be3SLaurent Vivier         s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff;
298af9f0be3SLaurent Vivier         s->cam[index][3] = dp8393x_get(s, width, 2) >> 8;
299af9f0be3SLaurent Vivier         s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff;
300af9f0be3SLaurent Vivier         s->cam[index][5] = dp8393x_get(s, width, 3) >> 8;
301a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
302a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
303a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
304a65f56eeSaurel32         /* Move to next entry */
305a65f56eeSaurel32         s->regs[SONIC_CDC]--;
306a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
307a65f56eeSaurel32         index++;
308a65f56eeSaurel32     }
309a65f56eeSaurel32 
310a65f56eeSaurel32     /* Read CAM enable */
31119f70347SPeter Maydell     address_space_read(&s->as, dp8393x_cdp(s),
31219f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
313af9f0be3SLaurent Vivier     s->regs[SONIC_CE] = dp8393x_get(s, width, 0);
314a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
315a65f56eeSaurel32 
316a65f56eeSaurel32     /* Done */
317a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
318a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
319a65f56eeSaurel32     dp8393x_update_irq(s);
320a65f56eeSaurel32 }
321a65f56eeSaurel32 
3223df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
323a65f56eeSaurel32 {
324a65f56eeSaurel32     int width, size;
325a65f56eeSaurel32 
326a65f56eeSaurel32     /* Read memory */
327a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
328a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
32919f70347SPeter Maydell     address_space_read(&s->as, dp8393x_rrp(s),
33019f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
331a65f56eeSaurel32 
332a65f56eeSaurel32     /* Update SONIC registers */
333af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0);
334af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1);
335af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2);
336af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3);
337a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
338a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
339a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
340a65f56eeSaurel32 
341a65f56eeSaurel32     /* Go to next entry */
342a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
343a65f56eeSaurel32 
344a65f56eeSaurel32     /* Handle wrap */
345a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
346a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
347a65f56eeSaurel32     }
348a65f56eeSaurel32 
349a65f56eeSaurel32     /* Check resource exhaustion */
350a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
351a65f56eeSaurel32     {
352a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
353a65f56eeSaurel32         dp8393x_update_irq(s);
354a65f56eeSaurel32     }
355a65f56eeSaurel32 }
356a65f56eeSaurel32 
3573df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
358a65f56eeSaurel32 {
359bc72ad67SAlex Bligh     timer_del(s->watchdog);
360a65f56eeSaurel32 
361a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
362a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
363a65f56eeSaurel32 }
364a65f56eeSaurel32 
3653df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
366a65f56eeSaurel32 {
367a65f56eeSaurel32     uint32_t ticks;
368a65f56eeSaurel32     int64_t delay;
369a65f56eeSaurel32 
370a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
371bc72ad67SAlex Bligh         timer_del(s->watchdog);
372a65f56eeSaurel32         return;
373a65f56eeSaurel32     }
374a65f56eeSaurel32 
375581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
376bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
37773bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
378bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
379a65f56eeSaurel32 }
380a65f56eeSaurel32 
3813df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
382a65f56eeSaurel32 {
383a65f56eeSaurel32     int64_t elapsed;
384a65f56eeSaurel32     uint32_t val;
385a65f56eeSaurel32 
386a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
387bc72ad67SAlex Bligh         timer_del(s->watchdog);
388a65f56eeSaurel32         return;
389a65f56eeSaurel32     }
390a65f56eeSaurel32 
391bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
392581f7b12SPeter Maydell     val = dp8393x_wt(s);
393a65f56eeSaurel32     val -= elapsed / 5000000;
394a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
395a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3963df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
397a65f56eeSaurel32 
398a65f56eeSaurel32 }
399a65f56eeSaurel32 
4003df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
401a65f56eeSaurel32 {
402a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
4033df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
404a65f56eeSaurel32 }
405a65f56eeSaurel32 
4063df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
407a65f56eeSaurel32 {
408a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
4093df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
410a65f56eeSaurel32 }
411a65f56eeSaurel32 
4124594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc);
4134594f93aSFam Zheng 
4143df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
415a65f56eeSaurel32 {
416a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
4174594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
4184594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
4194594f93aSFam Zheng     }
420a65f56eeSaurel32 }
421a65f56eeSaurel32 
4223df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
423a65f56eeSaurel32 {
424a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
425a65f56eeSaurel32 }
426a65f56eeSaurel32 
4273df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
428a65f56eeSaurel32 {
429b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
430a65f56eeSaurel32     int width, size;
431a65f56eeSaurel32     int tx_len, len;
432a65f56eeSaurel32     uint16_t i;
433a65f56eeSaurel32 
434a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
435a65f56eeSaurel32 
436a65f56eeSaurel32     while (1) {
437a65f56eeSaurel32         /* Read memory */
438a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
439a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
440581f7b12SPeter Maydell         DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
44119f70347SPeter Maydell         address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width,
44219f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
443a65f56eeSaurel32         tx_len = 0;
444a65f56eeSaurel32 
445a65f56eeSaurel32         /* Update registers */
446af9f0be3SLaurent Vivier         s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000;
447af9f0be3SLaurent Vivier         s->regs[SONIC_TPS] = dp8393x_get(s, width, 1);
448af9f0be3SLaurent Vivier         s->regs[SONIC_TFC] = dp8393x_get(s, width, 2);
449af9f0be3SLaurent Vivier         s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3);
450af9f0be3SLaurent Vivier         s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4);
451af9f0be3SLaurent Vivier         s->regs[SONIC_TFS] = dp8393x_get(s, width, 5);
452a65f56eeSaurel32 
453a65f56eeSaurel32         /* Handle programmable interrupt */
454a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
455a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
456a65f56eeSaurel32         } else {
457a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
458a65f56eeSaurel32         }
459a65f56eeSaurel32 
460a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
461a65f56eeSaurel32             /* Append fragment */
462a65f56eeSaurel32             len = s->regs[SONIC_TFS];
463a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
464a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
465a65f56eeSaurel32             }
46619f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
46719f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
468a65f56eeSaurel32             tx_len += len;
469a65f56eeSaurel32 
470a65f56eeSaurel32             i++;
471a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
472a65f56eeSaurel32                 /* Read next fragment details */
473a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
47419f70347SPeter Maydell                 address_space_read(&s->as,
47519f70347SPeter Maydell                                    dp8393x_ttda(s)
47619f70347SPeter Maydell                                    + sizeof(uint16_t) * width * (4 + 3 * i),
47719f70347SPeter Maydell                                    MEMTXATTRS_UNSPECIFIED, s->data,
47819f70347SPeter Maydell                                    size);
479af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0);
480af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1);
481af9f0be3SLaurent Vivier                 s->regs[SONIC_TFS] = dp8393x_get(s, width, 2);
482a65f56eeSaurel32             }
483a65f56eeSaurel32         }
484a65f56eeSaurel32 
485a65f56eeSaurel32         /* Handle Ethernet checksum */
486a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
487a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
488a65f56eeSaurel32              * which don't have one */
489a65f56eeSaurel32         } else {
490a65f56eeSaurel32             /* Remove existing FCS */
491a65f56eeSaurel32             tx_len -= 4;
492a65f56eeSaurel32         }
493a65f56eeSaurel32 
494a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
495a65f56eeSaurel32             /* Loopback */
496a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
497b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
498a65f56eeSaurel32                 s->loopback_packet = 1;
499b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
500a65f56eeSaurel32             }
501a65f56eeSaurel32         } else {
502a65f56eeSaurel32             /* Transmit packet */
503b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
504a65f56eeSaurel32         }
505a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
506a65f56eeSaurel32 
507a65f56eeSaurel32         /* Write status */
508af9f0be3SLaurent Vivier         dp8393x_put(s, width, 0,
509be920841SLaurent Vivier                     s->regs[SONIC_TCR] & 0x0fff); /* status */
510a65f56eeSaurel32         size = sizeof(uint16_t) * width;
51119f70347SPeter Maydell         address_space_write(&s->as, dp8393x_ttda(s),
51219f70347SPeter Maydell                             MEMTXATTRS_UNSPECIFIED, s->data, size);
513a65f56eeSaurel32 
514a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
515a65f56eeSaurel32             /* Read footer of packet */
516a65f56eeSaurel32             size = sizeof(uint16_t) * width;
51719f70347SPeter Maydell             address_space_read(&s->as,
51819f70347SPeter Maydell                                dp8393x_ttda(s)
51919f70347SPeter Maydell                                + sizeof(uint16_t) * width
52019f70347SPeter Maydell                                  * (4 + 3 * s->regs[SONIC_TFC]),
52119f70347SPeter Maydell                                MEMTXATTRS_UNSPECIFIED, s->data,
52219f70347SPeter Maydell                                size);
523af9f0be3SLaurent Vivier             s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0) & ~0x1;
52488f632fbSFinn Thain             if (dp8393x_get(s, width, 0) & SONIC_DESC_EOL) {
525a65f56eeSaurel32                 /* EOL detected */
526a65f56eeSaurel32                 break;
527a65f56eeSaurel32             }
528a65f56eeSaurel32         }
529a65f56eeSaurel32     }
530a65f56eeSaurel32 
531a65f56eeSaurel32     /* Done */
532a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
533a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
534a65f56eeSaurel32     dp8393x_update_irq(s);
535a65f56eeSaurel32 }
536a65f56eeSaurel32 
5373df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
538a65f56eeSaurel32 {
539a65f56eeSaurel32     /* Nothing to do */
540a65f56eeSaurel32 }
541a65f56eeSaurel32 
5423df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
543a65f56eeSaurel32 {
544a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
545a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
546a65f56eeSaurel32         return;
547a65f56eeSaurel32     }
548a65f56eeSaurel32 
549a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
550a65f56eeSaurel32 
551a65f56eeSaurel32     if (command & SONIC_CR_HTX)
5523df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
553a65f56eeSaurel32     if (command & SONIC_CR_TXP)
5543df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
555a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
5563df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
557a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
5583df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
559a65f56eeSaurel32     if (command & SONIC_CR_STP)
5603df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
561a65f56eeSaurel32     if (command & SONIC_CR_ST)
5623df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
563a65f56eeSaurel32     if (command & SONIC_CR_RST)
5643df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
565*a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5663df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
567*a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
568*a3cce282SFinn Thain     }
569a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
5703df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
571a65f56eeSaurel32 }
572a65f56eeSaurel32 
57384689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
574a65f56eeSaurel32 {
57584689cbbSHervé Poussineau     dp8393xState *s = opaque;
57684689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
577a65f56eeSaurel32     uint16_t val = 0;
578a65f56eeSaurel32 
579a65f56eeSaurel32     switch (reg) {
580a65f56eeSaurel32         /* Update data before reading it */
581a65f56eeSaurel32         case SONIC_WT0:
582a65f56eeSaurel32         case SONIC_WT1:
5833df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
584a65f56eeSaurel32             val = s->regs[reg];
585a65f56eeSaurel32             break;
586a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
587a65f56eeSaurel32         case SONIC_CAP2:
588a65f56eeSaurel32         case SONIC_CAP1:
589a65f56eeSaurel32         case SONIC_CAP0:
590a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
591a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
592a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
593a65f56eeSaurel32             }
594a65f56eeSaurel32             break;
595a65f56eeSaurel32         /* All other registers have no special contrainst */
596a65f56eeSaurel32         default:
597a65f56eeSaurel32             val = s->regs[reg];
598a65f56eeSaurel32     }
599a65f56eeSaurel32 
600a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
601a65f56eeSaurel32 
6023fe9a838SFinn Thain     return s->big_endian ? val << 16 : val;
603a65f56eeSaurel32 }
604a65f56eeSaurel32 
60584689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
60684689cbbSHervé Poussineau                           unsigned int size)
607a65f56eeSaurel32 {
60884689cbbSHervé Poussineau     dp8393xState *s = opaque;
60984689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
6103fe9a838SFinn Thain     uint32_t val = s->big_endian ? data >> 16 : data;
61184689cbbSHervé Poussineau 
6123fe9a838SFinn Thain     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)val, reg_names[reg]);
613a65f56eeSaurel32 
614a65f56eeSaurel32     switch (reg) {
615a65f56eeSaurel32         /* Command register */
616a65f56eeSaurel32         case SONIC_CR:
6173fe9a838SFinn Thain             dp8393x_do_command(s, val);
618a65f56eeSaurel32             break;
619a65f56eeSaurel32         /* Prevent write to read-only registers */
620a65f56eeSaurel32         case SONIC_CAP2:
621a65f56eeSaurel32         case SONIC_CAP1:
622a65f56eeSaurel32         case SONIC_CAP0:
623a65f56eeSaurel32         case SONIC_SR:
624a65f56eeSaurel32         case SONIC_MDT:
625a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
626a65f56eeSaurel32             break;
627a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
628a65f56eeSaurel32         case SONIC_DCR:
629a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6303fe9a838SFinn Thain                 s->regs[reg] = val & 0xbfff;
631a65f56eeSaurel32             } else {
632a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
633a65f56eeSaurel32             }
634a65f56eeSaurel32             break;
635a65f56eeSaurel32         case SONIC_DCR2:
636a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6373fe9a838SFinn Thain                 s->regs[reg] = val & 0xf017;
638a65f56eeSaurel32             } else {
639a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
640a65f56eeSaurel32             }
641a65f56eeSaurel32             break;
642a65f56eeSaurel32         /* 12 lower bytes are Read Only */
643a65f56eeSaurel32         case SONIC_TCR:
6443fe9a838SFinn Thain             s->regs[reg] = val & 0xf000;
645a65f56eeSaurel32             break;
646a65f56eeSaurel32         /* 9 lower bytes are Read Only */
647a65f56eeSaurel32         case SONIC_RCR:
6483fe9a838SFinn Thain             s->regs[reg] = val & 0xffe0;
649a65f56eeSaurel32             break;
650a65f56eeSaurel32         /* Ignore most significant bit */
651a65f56eeSaurel32         case SONIC_IMR:
6523fe9a838SFinn Thain             s->regs[reg] = val & 0x7fff;
653a65f56eeSaurel32             dp8393x_update_irq(s);
654a65f56eeSaurel32             break;
655a65f56eeSaurel32         /* Clear bits by writing 1 to them */
656a65f56eeSaurel32         case SONIC_ISR:
6573fe9a838SFinn Thain             val &= s->regs[reg];
6583fe9a838SFinn Thain             s->regs[reg] &= ~val;
6593fe9a838SFinn Thain             if (val & SONIC_ISR_RBE) {
6603df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
661a65f56eeSaurel32             }
662a65f56eeSaurel32             dp8393x_update_irq(s);
6634594f93aSFam Zheng             if (dp8393x_can_receive(s->nic->ncs)) {
6644594f93aSFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
6654594f93aSFam Zheng             }
666a65f56eeSaurel32             break;
667a65f56eeSaurel32         /* Ignore least significant bit */
668a65f56eeSaurel32         case SONIC_RSA:
669a65f56eeSaurel32         case SONIC_REA:
670a65f56eeSaurel32         case SONIC_RRP:
671a65f56eeSaurel32         case SONIC_RWP:
6723fe9a838SFinn Thain             s->regs[reg] = val & 0xfffe;
673a65f56eeSaurel32             break;
674a65f56eeSaurel32         /* Invert written value for some registers */
675a65f56eeSaurel32         case SONIC_CRCT:
676a65f56eeSaurel32         case SONIC_FAET:
677a65f56eeSaurel32         case SONIC_MPT:
6783fe9a838SFinn Thain             s->regs[reg] = val ^ 0xffff;
679a65f56eeSaurel32             break;
680a65f56eeSaurel32         /* All other registers have no special contrainst */
681a65f56eeSaurel32         default:
6823fe9a838SFinn Thain             s->regs[reg] = val;
683a65f56eeSaurel32     }
684a65f56eeSaurel32 
685a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6863df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
687a65f56eeSaurel32     }
688a65f56eeSaurel32 }
689a65f56eeSaurel32 
69084689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
69184689cbbSHervé Poussineau     .read = dp8393x_read,
69284689cbbSHervé Poussineau     .write = dp8393x_write,
6933fe9a838SFinn Thain     .impl.min_access_size = 4,
6943fe9a838SFinn Thain     .impl.max_access_size = 4,
69584689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
69684689cbbSHervé Poussineau };
69784689cbbSHervé Poussineau 
698a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
699a65f56eeSaurel32 {
700a65f56eeSaurel32     dp8393xState *s = opaque;
701a65f56eeSaurel32 
702a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
703a65f56eeSaurel32         return;
704a65f56eeSaurel32     }
705a65f56eeSaurel32 
706a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
707a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
7083df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
709a65f56eeSaurel32 
710a65f56eeSaurel32     /* Signal underflow */
711a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
712a65f56eeSaurel32     dp8393x_update_irq(s);
713a65f56eeSaurel32 }
714a65f56eeSaurel32 
7153df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
716a65f56eeSaurel32 {
717cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
718a65f56eeSaurel32 
719a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
720a65f56eeSaurel32         return 0;
721a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
722a65f56eeSaurel32         return 0;
723a65f56eeSaurel32     return 1;
724a65f56eeSaurel32 }
725a65f56eeSaurel32 
7263df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
7273df5de64SHervé Poussineau                                   int size)
728a65f56eeSaurel32 {
729a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
730a65f56eeSaurel32     int i;
731a65f56eeSaurel32 
732a65f56eeSaurel32     /* Check promiscuous mode */
733a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
734a65f56eeSaurel32         return 0;
735a65f56eeSaurel32     }
736a65f56eeSaurel32 
737a65f56eeSaurel32     /* Check multicast packets */
738a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
739a65f56eeSaurel32         return SONIC_RCR_MC;
740a65f56eeSaurel32     }
741a65f56eeSaurel32 
742a65f56eeSaurel32     /* Check broadcast */
743a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
744a65f56eeSaurel32         return SONIC_RCR_BC;
745a65f56eeSaurel32     }
746a65f56eeSaurel32 
747a65f56eeSaurel32     /* Check CAM */
748a65f56eeSaurel32     for (i = 0; i < 16; i++) {
749a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
750a65f56eeSaurel32              /* Entry enabled */
751a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
752a65f56eeSaurel32                  return 0;
753a65f56eeSaurel32              }
754a65f56eeSaurel32         }
755a65f56eeSaurel32     }
756a65f56eeSaurel32 
757a65f56eeSaurel32     return -1;
758a65f56eeSaurel32 }
759a65f56eeSaurel32 
7603df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7619e3cd456SFinn Thain                                size_t pkt_size)
762a65f56eeSaurel32 {
763cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
764a65f56eeSaurel32     int packet_type;
765a65f56eeSaurel32     uint32_t available, address;
7669e3cd456SFinn Thain     int width, rx_len = pkt_size;
767a65f56eeSaurel32     uint32_t checksum;
7689e3cd456SFinn Thain     int size;
769a65f56eeSaurel32 
770a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
771a65f56eeSaurel32 
772a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
773a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
774a65f56eeSaurel32 
7759e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
776a65f56eeSaurel32     if (packet_type < 0) {
777a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
7784f1c942bSMark McLoughlin         return -1;
779a65f56eeSaurel32     }
780a65f56eeSaurel32 
781a65f56eeSaurel32     /* Check for EOL */
78288f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
783a65f56eeSaurel32         /* Are we still in resource exhaustion? */
784a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
785581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
78619f70347SPeter Maydell         address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED,
78719f70347SPeter Maydell                            s->data, size);
7885b0c98fcSFinn Thain         s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
7895b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
790a65f56eeSaurel32             /* Still EOL ; stop reception */
7914f1c942bSMark McLoughlin             return -1;
792a65f56eeSaurel32         }
7935b0c98fcSFinn Thain         /* Link has been updated by host */
7945b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
795a65f56eeSaurel32     }
796a65f56eeSaurel32 
797a65f56eeSaurel32     /* Save current position */
798a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
799a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
800a65f56eeSaurel32 
801a65f56eeSaurel32     /* Calculate the ethernet checksum */
802a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
803a65f56eeSaurel32 
804a65f56eeSaurel32     /* Put packet into RBA */
805581f7b12SPeter Maydell     DPRINTF("Receive packet at %08x\n", dp8393x_crba(s));
806581f7b12SPeter Maydell     address = dp8393x_crba(s);
80719f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
80819f70347SPeter Maydell                         buf, rx_len);
809a65f56eeSaurel32     address += rx_len;
81019f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
81119f70347SPeter Maydell                         &checksum, 4);
812a65f56eeSaurel32     rx_len += 4;
813a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
814a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
815581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
816a65f56eeSaurel32     available -= rx_len / 2;
817a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
818a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
819a65f56eeSaurel32 
820a65f56eeSaurel32     /* Update status */
821581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
822a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
823a65f56eeSaurel32     }
824a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
825a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
826a65f56eeSaurel32     if (s->loopback_packet) {
827a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
828a65f56eeSaurel32         s->loopback_packet = 0;
829a65f56eeSaurel32     }
830a65f56eeSaurel32 
831a65f56eeSaurel32     /* Write status to memory */
832581f7b12SPeter Maydell     DPRINTF("Write status at %08x\n", dp8393x_crda(s));
833af9f0be3SLaurent Vivier     dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */
834af9f0be3SLaurent Vivier     dp8393x_put(s, width, 1, rx_len); /* byte count */
835af9f0be3SLaurent Vivier     dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
836af9f0be3SLaurent Vivier     dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
837af9f0be3SLaurent Vivier     dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */
838a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
83919f70347SPeter Maydell     address_space_write(&s->as, dp8393x_crda(s),
84019f70347SPeter Maydell                         MEMTXATTRS_UNSPECIFIED,
84119f70347SPeter Maydell                         s->data, size);
842a65f56eeSaurel32 
8435b0c98fcSFinn Thain     /* Check link field */
844a65f56eeSaurel32     size = sizeof(uint16_t) * width;
84519f70347SPeter Maydell     address_space_read(&s->as,
84619f70347SPeter Maydell                        dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
84719f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
848af9f0be3SLaurent Vivier     s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
84988f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
850a65f56eeSaurel32         /* EOL detected */
851a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
852a65f56eeSaurel32     } else {
85346ffee9aSFinn Thain         /* Clear in_use */
85446ffee9aSFinn Thain         size = sizeof(uint16_t) * width;
85546ffee9aSFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
85646ffee9aSFinn Thain         dp8393x_put(s, width, 0, 0);
85746ffee9aSFinn Thain         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
85846ffee9aSFinn Thain                             s->data, size);
8595b0c98fcSFinn Thain 
8605b0c98fcSFinn Thain         /* Move to next descriptor */
861a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
862a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
863a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
864a65f56eeSaurel32 
865a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
866a65f56eeSaurel32             /* Read next RRA */
8673df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
868a65f56eeSaurel32         }
869a65f56eeSaurel32     }
870a65f56eeSaurel32 
871a65f56eeSaurel32     /* Done */
872a65f56eeSaurel32     dp8393x_update_irq(s);
8734f1c942bSMark McLoughlin 
8749e3cd456SFinn Thain     return pkt_size;
875a65f56eeSaurel32 }
876a65f56eeSaurel32 
877104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
878a65f56eeSaurel32 {
879104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
880bc72ad67SAlex Bligh     timer_del(s->watchdog);
881a65f56eeSaurel32 
882bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
883a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
884a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
885a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
886a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
887a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
888a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
889a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
890a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
891a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
892a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
893a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
894a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
895a65f56eeSaurel32 
896a65f56eeSaurel32     /* Network cable is connected */
897a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
898a65f56eeSaurel32 
899a65f56eeSaurel32     dp8393x_update_irq(s);
900a65f56eeSaurel32 }
901a65f56eeSaurel32 
90205f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
903f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
90405f41fe3SMark McLoughlin     .size = sizeof(NICState),
9053df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
9063df5de64SHervé Poussineau     .receive = dp8393x_receive,
90705f41fe3SMark McLoughlin };
90805f41fe3SMark McLoughlin 
909104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
910a65f56eeSaurel32 {
911104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
912104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
913a65f56eeSaurel32 
914104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
91589ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
916104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
917104655a5SHervé Poussineau }
918a65f56eeSaurel32 
919104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
920104655a5SHervé Poussineau {
921104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
92289ae0ff9SHervé Poussineau     int i, checksum;
92389ae0ff9SHervé Poussineau     uint8_t *prom;
92452579c68SHervé Poussineau     Error *local_err = NULL;
925a65f56eeSaurel32 
926104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
927104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
928104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
929104655a5SHervé Poussineau 
930104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
931104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
932104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
933104655a5SHervé Poussineau 
934bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
935a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
93689ae0ff9SHervé Poussineau 
9378fad0a65SPeter Maydell     memory_region_init_ram(&s->prom, OBJECT(dev),
93852579c68SHervé Poussineau                            "dp8393x-prom", SONIC_PROM_SIZE, &local_err);
93952579c68SHervé Poussineau     if (local_err) {
94052579c68SHervé Poussineau         error_propagate(errp, local_err);
94152579c68SHervé Poussineau         return;
94252579c68SHervé Poussineau     }
94352579c68SHervé Poussineau     memory_region_set_readonly(&s->prom, true);
94489ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
94589ae0ff9SHervé Poussineau     checksum = 0;
94689ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
94789ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
94889ae0ff9SHervé Poussineau         checksum += prom[i];
94989ae0ff9SHervé Poussineau         if (checksum > 0xff) {
95089ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
95189ae0ff9SHervé Poussineau         }
95289ae0ff9SHervé Poussineau     }
95389ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
954a65f56eeSaurel32 }
955104655a5SHervé Poussineau 
9561670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9571670735dSHervé Poussineau     .name = "dp8393x",
9581670735dSHervé Poussineau     .version_id = 0,
9591670735dSHervé Poussineau     .minimum_version_id = 0,
9601670735dSHervé Poussineau     .fields = (VMStateField []) {
9611670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
9621670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
9631670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9641670735dSHervé Poussineau     }
9651670735dSHervé Poussineau };
9661670735dSHervé Poussineau 
967104655a5SHervé Poussineau static Property dp8393x_properties[] = {
968104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
9693110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
9703110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
971104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
972be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
973104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
974104655a5SHervé Poussineau };
975104655a5SHervé Poussineau 
976104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
977104655a5SHervé Poussineau {
978104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
979104655a5SHervé Poussineau 
980104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
981104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
982104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
9831670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
9844f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
985104655a5SHervé Poussineau }
986104655a5SHervé Poussineau 
987104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
988104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
989104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
990104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
991104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
992104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
993104655a5SHervé Poussineau };
994104655a5SHervé Poussineau 
995104655a5SHervé Poussineau static void dp8393x_register_types(void)
996104655a5SHervé Poussineau {
997104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
998104655a5SHervé Poussineau }
999104655a5SHervé Poussineau 
1000104655a5SHervé Poussineau type_init(dp8393x_register_types)
1001