xref: /qemu/hw/net/dp8393x.c (revision b8c4b67e3ec3918a40234e5c56221f780c7856fc)
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 
140ada74315SFinn Thain #define SONIC_ISR_RBAE   0x0010
141a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
142a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
143a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
144a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
145a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
146a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
147a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
148a65f56eeSaurel32 
14988f632fbSFinn Thain #define SONIC_DESC_EOL   0x0001
15088f632fbSFinn Thain #define SONIC_DESC_ADDR  0xFFFE
15188f632fbSFinn Thain 
152104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
153104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
154104655a5SHervé Poussineau 
155a65f56eeSaurel32 typedef struct dp8393xState {
156104655a5SHervé Poussineau     SysBusDevice parent_obj;
157104655a5SHervé Poussineau 
158a65f56eeSaurel32     /* Hardware */
159104655a5SHervé Poussineau     uint8_t it_shift;
160be920841SLaurent Vivier     bool big_endian;
161c2279bd0SFinn Thain     bool last_rba_is_full;
162a65f56eeSaurel32     qemu_irq irq;
163a65f56eeSaurel32 #ifdef DEBUG_SONIC
164a65f56eeSaurel32     int irq_level;
165a65f56eeSaurel32 #endif
166a65f56eeSaurel32     QEMUTimer *watchdog;
167a65f56eeSaurel32     int64_t wt_last_update;
16805f41fe3SMark McLoughlin     NICConf conf;
16905f41fe3SMark McLoughlin     NICState *nic;
170024e5bb6SAvi Kivity     MemoryRegion mmio;
17189ae0ff9SHervé Poussineau     MemoryRegion prom;
172a65f56eeSaurel32 
173a65f56eeSaurel32     /* Registers */
174a65f56eeSaurel32     uint8_t cam[16][6];
175a65f56eeSaurel32     uint16_t regs[0x40];
176a65f56eeSaurel32 
177a65f56eeSaurel32     /* Temporaries */
178a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
179af9f0be3SLaurent Vivier     uint16_t data[12];
180a65f56eeSaurel32     int loopback_packet;
181a65f56eeSaurel32 
182a65f56eeSaurel32     /* Memory access */
1833110ce81SMarc-André Lureau     MemoryRegion *dma_mr;
184dd820513SHervé Poussineau     AddressSpace as;
185a65f56eeSaurel32 } dp8393xState;
186a65f56eeSaurel32 
187581f7b12SPeter Maydell /* Accessor functions for values which are formed by
188581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
189581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
190581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
191581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
192581f7b12SPeter Maydell  */
193581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
194581f7b12SPeter Maydell {
195581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
196581f7b12SPeter Maydell }
197581f7b12SPeter Maydell 
198581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
199581f7b12SPeter Maydell {
200581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
201581f7b12SPeter Maydell }
202581f7b12SPeter Maydell 
203581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
204581f7b12SPeter Maydell {
20588f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
20688f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
207581f7b12SPeter Maydell }
208581f7b12SPeter Maydell 
209581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
210581f7b12SPeter Maydell {
211581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
212581f7b12SPeter Maydell }
213581f7b12SPeter Maydell 
214581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
215581f7b12SPeter Maydell {
216581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
217581f7b12SPeter Maydell }
218581f7b12SPeter Maydell 
219581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
220581f7b12SPeter Maydell {
221581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
222581f7b12SPeter Maydell }
223581f7b12SPeter Maydell 
224581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
225581f7b12SPeter Maydell {
22688f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
22788f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
228581f7b12SPeter Maydell }
229581f7b12SPeter Maydell 
230581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
231581f7b12SPeter Maydell {
232581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
233581f7b12SPeter Maydell }
234581f7b12SPeter Maydell 
235af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset)
236be920841SLaurent Vivier {
237be920841SLaurent Vivier     uint16_t val;
238be920841SLaurent Vivier 
239be920841SLaurent Vivier     if (s->big_endian) {
240af9f0be3SLaurent Vivier         val = be16_to_cpu(s->data[offset * width + width - 1]);
241be920841SLaurent Vivier     } else {
242af9f0be3SLaurent Vivier         val = le16_to_cpu(s->data[offset * width]);
243be920841SLaurent Vivier     }
244be920841SLaurent Vivier     return val;
245be920841SLaurent Vivier }
246be920841SLaurent Vivier 
247af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset,
248be920841SLaurent Vivier                         uint16_t val)
249be920841SLaurent Vivier {
250be920841SLaurent Vivier     if (s->big_endian) {
2513fe9a838SFinn Thain         if (width == 2) {
2523fe9a838SFinn Thain             s->data[offset * 2] = 0;
2533fe9a838SFinn Thain             s->data[offset * 2 + 1] = cpu_to_be16(val);
254be920841SLaurent Vivier         } else {
2553fe9a838SFinn Thain             s->data[offset] = cpu_to_be16(val);
2563fe9a838SFinn Thain         }
2573fe9a838SFinn Thain     } else {
2583fe9a838SFinn Thain         if (width == 2) {
2593fe9a838SFinn Thain             s->data[offset * 2] = cpu_to_le16(val);
2603fe9a838SFinn Thain             s->data[offset * 2 + 1] = 0;
2613fe9a838SFinn Thain         } else {
2623fe9a838SFinn Thain             s->data[offset] = cpu_to_le16(val);
2633fe9a838SFinn Thain         }
264be920841SLaurent Vivier     }
265be920841SLaurent Vivier }
266be920841SLaurent Vivier 
267a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
268a65f56eeSaurel32 {
269a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
270a65f56eeSaurel32 
271a65f56eeSaurel32 #ifdef DEBUG_SONIC
272a65f56eeSaurel32     if (level != s->irq_level) {
273a65f56eeSaurel32         s->irq_level = level;
274a65f56eeSaurel32         if (level) {
275a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
276a65f56eeSaurel32         } else {
277a65f56eeSaurel32             DPRINTF("lower irq\n");
278a65f56eeSaurel32         }
279a65f56eeSaurel32     }
280a65f56eeSaurel32 #endif
281a65f56eeSaurel32 
282a65f56eeSaurel32     qemu_set_irq(s->irq, level);
283a65f56eeSaurel32 }
284a65f56eeSaurel32 
2853df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
286a65f56eeSaurel32 {
287a65f56eeSaurel32     int width, size;
288a65f56eeSaurel32     uint16_t index = 0;
289a65f56eeSaurel32 
290a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
291a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
292a65f56eeSaurel32 
293a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
294a65f56eeSaurel32         /* Fill current entry */
29519f70347SPeter Maydell         address_space_read(&s->as, dp8393x_cdp(s),
29619f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
297af9f0be3SLaurent Vivier         s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff;
298af9f0be3SLaurent Vivier         s->cam[index][1] = dp8393x_get(s, width, 1) >> 8;
299af9f0be3SLaurent Vivier         s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff;
300af9f0be3SLaurent Vivier         s->cam[index][3] = dp8393x_get(s, width, 2) >> 8;
301af9f0be3SLaurent Vivier         s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff;
302af9f0be3SLaurent Vivier         s->cam[index][5] = dp8393x_get(s, width, 3) >> 8;
303a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
304a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
305a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
306a65f56eeSaurel32         /* Move to next entry */
307a65f56eeSaurel32         s->regs[SONIC_CDC]--;
308a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
309a65f56eeSaurel32         index++;
310a65f56eeSaurel32     }
311a65f56eeSaurel32 
312a65f56eeSaurel32     /* Read CAM enable */
31319f70347SPeter Maydell     address_space_read(&s->as, dp8393x_cdp(s),
31419f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
315af9f0be3SLaurent Vivier     s->regs[SONIC_CE] = dp8393x_get(s, width, 0);
316a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
317a65f56eeSaurel32 
318a65f56eeSaurel32     /* Done */
319a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
320a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
321a65f56eeSaurel32     dp8393x_update_irq(s);
322a65f56eeSaurel32 }
323a65f56eeSaurel32 
3243df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
325a65f56eeSaurel32 {
326a65f56eeSaurel32     int width, size;
327a65f56eeSaurel32 
328a65f56eeSaurel32     /* Read memory */
329a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
330a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
33119f70347SPeter Maydell     address_space_read(&s->as, dp8393x_rrp(s),
33219f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
333a65f56eeSaurel32 
334a65f56eeSaurel32     /* Update SONIC registers */
335af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0);
336af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1);
337af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2);
338af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3);
339a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
340a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
341a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
342a65f56eeSaurel32 
343a65f56eeSaurel32     /* Go to next entry */
344a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
345a65f56eeSaurel32 
346a65f56eeSaurel32     /* Handle wrap */
347a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
348a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
349a65f56eeSaurel32     }
350a65f56eeSaurel32 
351c2279bd0SFinn Thain     /* Warn the host if CRBA now has the last available resource */
352a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
353a65f56eeSaurel32     {
354a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
355a65f56eeSaurel32         dp8393x_update_irq(s);
356a65f56eeSaurel32     }
357c2279bd0SFinn Thain 
358c2279bd0SFinn Thain     /* Allow packet reception */
359c2279bd0SFinn Thain     s->last_rba_is_full = false;
360a65f56eeSaurel32 }
361a65f56eeSaurel32 
3623df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
363a65f56eeSaurel32 {
364bc72ad67SAlex Bligh     timer_del(s->watchdog);
365a65f56eeSaurel32 
366a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
367a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
368a65f56eeSaurel32 }
369a65f56eeSaurel32 
3703df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
371a65f56eeSaurel32 {
372a65f56eeSaurel32     uint32_t ticks;
373a65f56eeSaurel32     int64_t delay;
374a65f56eeSaurel32 
375a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
376bc72ad67SAlex Bligh         timer_del(s->watchdog);
377a65f56eeSaurel32         return;
378a65f56eeSaurel32     }
379a65f56eeSaurel32 
380581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
381bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
38273bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
383bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
384a65f56eeSaurel32 }
385a65f56eeSaurel32 
3863df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
387a65f56eeSaurel32 {
388a65f56eeSaurel32     int64_t elapsed;
389a65f56eeSaurel32     uint32_t val;
390a65f56eeSaurel32 
391a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
392bc72ad67SAlex Bligh         timer_del(s->watchdog);
393a65f56eeSaurel32         return;
394a65f56eeSaurel32     }
395a65f56eeSaurel32 
396bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
397581f7b12SPeter Maydell     val = dp8393x_wt(s);
398a65f56eeSaurel32     val -= elapsed / 5000000;
399a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
400a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
4013df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
402a65f56eeSaurel32 
403a65f56eeSaurel32 }
404a65f56eeSaurel32 
4053df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
406a65f56eeSaurel32 {
407a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
4083df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
409a65f56eeSaurel32 }
410a65f56eeSaurel32 
4113df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
412a65f56eeSaurel32 {
413a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
4143df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
415a65f56eeSaurel32 }
416a65f56eeSaurel32 
417*b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc);
4184594f93aSFam Zheng 
4193df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
420a65f56eeSaurel32 {
421a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
4224594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
4234594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
4244594f93aSFam Zheng     }
425a65f56eeSaurel32 }
426a65f56eeSaurel32 
4273df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
428a65f56eeSaurel32 {
429a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
430a65f56eeSaurel32 }
431a65f56eeSaurel32 
4323df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
433a65f56eeSaurel32 {
434b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
435a65f56eeSaurel32     int width, size;
436a65f56eeSaurel32     int tx_len, len;
437a65f56eeSaurel32     uint16_t i;
438a65f56eeSaurel32 
439a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
440a65f56eeSaurel32 
441a65f56eeSaurel32     while (1) {
442a65f56eeSaurel32         /* Read memory */
443a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
444a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
445581f7b12SPeter Maydell         DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
44619f70347SPeter Maydell         address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width,
44719f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
448a65f56eeSaurel32         tx_len = 0;
449a65f56eeSaurel32 
450a65f56eeSaurel32         /* Update registers */
451af9f0be3SLaurent Vivier         s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000;
452af9f0be3SLaurent Vivier         s->regs[SONIC_TPS] = dp8393x_get(s, width, 1);
453af9f0be3SLaurent Vivier         s->regs[SONIC_TFC] = dp8393x_get(s, width, 2);
454af9f0be3SLaurent Vivier         s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3);
455af9f0be3SLaurent Vivier         s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4);
456af9f0be3SLaurent Vivier         s->regs[SONIC_TFS] = dp8393x_get(s, width, 5);
457a65f56eeSaurel32 
458a65f56eeSaurel32         /* Handle programmable interrupt */
459a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
460a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
461a65f56eeSaurel32         } else {
462a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
463a65f56eeSaurel32         }
464a65f56eeSaurel32 
465a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
466a65f56eeSaurel32             /* Append fragment */
467a65f56eeSaurel32             len = s->regs[SONIC_TFS];
468a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
469a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
470a65f56eeSaurel32             }
47119f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
47219f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
473a65f56eeSaurel32             tx_len += len;
474a65f56eeSaurel32 
475a65f56eeSaurel32             i++;
476a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
477a65f56eeSaurel32                 /* Read next fragment details */
478a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
47919f70347SPeter Maydell                 address_space_read(&s->as,
48019f70347SPeter Maydell                                    dp8393x_ttda(s)
48119f70347SPeter Maydell                                    + sizeof(uint16_t) * width * (4 + 3 * i),
48219f70347SPeter Maydell                                    MEMTXATTRS_UNSPECIFIED, s->data,
48319f70347SPeter Maydell                                    size);
484af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0);
485af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1);
486af9f0be3SLaurent Vivier                 s->regs[SONIC_TFS] = dp8393x_get(s, width, 2);
487a65f56eeSaurel32             }
488a65f56eeSaurel32         }
489a65f56eeSaurel32 
490a65f56eeSaurel32         /* Handle Ethernet checksum */
491a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
492a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
493a65f56eeSaurel32              * which don't have one */
494a65f56eeSaurel32         } else {
495a65f56eeSaurel32             /* Remove existing FCS */
496a65f56eeSaurel32             tx_len -= 4;
497a65f56eeSaurel32         }
498a65f56eeSaurel32 
499a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
500a65f56eeSaurel32             /* Loopback */
501a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
502b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
503a65f56eeSaurel32                 s->loopback_packet = 1;
504b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
505a65f56eeSaurel32             }
506a65f56eeSaurel32         } else {
507a65f56eeSaurel32             /* Transmit packet */
508b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
509a65f56eeSaurel32         }
510a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
511a65f56eeSaurel32 
512a65f56eeSaurel32         /* Write status */
513af9f0be3SLaurent Vivier         dp8393x_put(s, width, 0,
514be920841SLaurent Vivier                     s->regs[SONIC_TCR] & 0x0fff); /* status */
515a65f56eeSaurel32         size = sizeof(uint16_t) * width;
51619f70347SPeter Maydell         address_space_write(&s->as, dp8393x_ttda(s),
51719f70347SPeter Maydell                             MEMTXATTRS_UNSPECIFIED, s->data, size);
518a65f56eeSaurel32 
519a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
520a65f56eeSaurel32             /* Read footer of packet */
521a65f56eeSaurel32             size = sizeof(uint16_t) * width;
52219f70347SPeter Maydell             address_space_read(&s->as,
52319f70347SPeter Maydell                                dp8393x_ttda(s)
52419f70347SPeter Maydell                                + sizeof(uint16_t) * width
52519f70347SPeter Maydell                                  * (4 + 3 * s->regs[SONIC_TFC]),
52619f70347SPeter Maydell                                MEMTXATTRS_UNSPECIFIED, s->data,
52719f70347SPeter Maydell                                size);
528a0cf4297SFinn Thain             s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0);
529a0cf4297SFinn Thain             if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) {
530a65f56eeSaurel32                 /* EOL detected */
531a65f56eeSaurel32                 break;
532a65f56eeSaurel32             }
533a65f56eeSaurel32         }
534a65f56eeSaurel32     }
535a65f56eeSaurel32 
536a65f56eeSaurel32     /* Done */
537a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
538a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
539a65f56eeSaurel32     dp8393x_update_irq(s);
540a65f56eeSaurel32 }
541a65f56eeSaurel32 
5423df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
543a65f56eeSaurel32 {
544a65f56eeSaurel32     /* Nothing to do */
545a65f56eeSaurel32 }
546a65f56eeSaurel32 
5473df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
548a65f56eeSaurel32 {
549a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
550a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
551a65f56eeSaurel32         return;
552a65f56eeSaurel32     }
553a65f56eeSaurel32 
554a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
555a65f56eeSaurel32 
556a65f56eeSaurel32     if (command & SONIC_CR_HTX)
5573df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
558a65f56eeSaurel32     if (command & SONIC_CR_TXP)
5593df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
560a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
5613df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
562a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
5633df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
564a65f56eeSaurel32     if (command & SONIC_CR_STP)
5653df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
566a65f56eeSaurel32     if (command & SONIC_CR_ST)
5673df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
568a65f56eeSaurel32     if (command & SONIC_CR_RST)
5693df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
570a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5713df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
572a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
573a3cce282SFinn Thain     }
574a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
5753df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
576a65f56eeSaurel32 }
577a65f56eeSaurel32 
57884689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
579a65f56eeSaurel32 {
58084689cbbSHervé Poussineau     dp8393xState *s = opaque;
58184689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
582a65f56eeSaurel32     uint16_t val = 0;
583a65f56eeSaurel32 
584a65f56eeSaurel32     switch (reg) {
585a65f56eeSaurel32         /* Update data before reading it */
586a65f56eeSaurel32         case SONIC_WT0:
587a65f56eeSaurel32         case SONIC_WT1:
5883df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
589a65f56eeSaurel32             val = s->regs[reg];
590a65f56eeSaurel32             break;
591a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
592a65f56eeSaurel32         case SONIC_CAP2:
593a65f56eeSaurel32         case SONIC_CAP1:
594a65f56eeSaurel32         case SONIC_CAP0:
595a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
596a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
597a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
598a65f56eeSaurel32             }
599a65f56eeSaurel32             break;
600a65f56eeSaurel32         /* All other registers have no special contrainst */
601a65f56eeSaurel32         default:
602a65f56eeSaurel32             val = s->regs[reg];
603a65f56eeSaurel32     }
604a65f56eeSaurel32 
605a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
606a65f56eeSaurel32 
6073fe9a838SFinn Thain     return s->big_endian ? val << 16 : val;
608a65f56eeSaurel32 }
609a65f56eeSaurel32 
61084689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
61184689cbbSHervé Poussineau                           unsigned int size)
612a65f56eeSaurel32 {
61384689cbbSHervé Poussineau     dp8393xState *s = opaque;
61484689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
6153fe9a838SFinn Thain     uint32_t val = s->big_endian ? data >> 16 : data;
61684689cbbSHervé Poussineau 
6173fe9a838SFinn Thain     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)val, reg_names[reg]);
618a65f56eeSaurel32 
619a65f56eeSaurel32     switch (reg) {
620a65f56eeSaurel32         /* Command register */
621a65f56eeSaurel32         case SONIC_CR:
6223fe9a838SFinn Thain             dp8393x_do_command(s, val);
623a65f56eeSaurel32             break;
624a65f56eeSaurel32         /* Prevent write to read-only registers */
625a65f56eeSaurel32         case SONIC_CAP2:
626a65f56eeSaurel32         case SONIC_CAP1:
627a65f56eeSaurel32         case SONIC_CAP0:
628a65f56eeSaurel32         case SONIC_SR:
629a65f56eeSaurel32         case SONIC_MDT:
630a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
631a65f56eeSaurel32             break;
632a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
633a65f56eeSaurel32         case SONIC_DCR:
634a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6353fe9a838SFinn Thain                 s->regs[reg] = val & 0xbfff;
636a65f56eeSaurel32             } else {
637a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
638a65f56eeSaurel32             }
639a65f56eeSaurel32             break;
640a65f56eeSaurel32         case SONIC_DCR2:
641a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6423fe9a838SFinn Thain                 s->regs[reg] = val & 0xf017;
643a65f56eeSaurel32             } else {
644a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
645a65f56eeSaurel32             }
646a65f56eeSaurel32             break;
647a65f56eeSaurel32         /* 12 lower bytes are Read Only */
648a65f56eeSaurel32         case SONIC_TCR:
6493fe9a838SFinn Thain             s->regs[reg] = val & 0xf000;
650a65f56eeSaurel32             break;
651a65f56eeSaurel32         /* 9 lower bytes are Read Only */
652a65f56eeSaurel32         case SONIC_RCR:
6533fe9a838SFinn Thain             s->regs[reg] = val & 0xffe0;
654a65f56eeSaurel32             break;
655a65f56eeSaurel32         /* Ignore most significant bit */
656a65f56eeSaurel32         case SONIC_IMR:
6573fe9a838SFinn Thain             s->regs[reg] = val & 0x7fff;
658a65f56eeSaurel32             dp8393x_update_irq(s);
659a65f56eeSaurel32             break;
660a65f56eeSaurel32         /* Clear bits by writing 1 to them */
661a65f56eeSaurel32         case SONIC_ISR:
6623fe9a838SFinn Thain             val &= s->regs[reg];
6633fe9a838SFinn Thain             s->regs[reg] &= ~val;
6643fe9a838SFinn Thain             if (val & SONIC_ISR_RBE) {
6653df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
666a65f56eeSaurel32             }
667a65f56eeSaurel32             dp8393x_update_irq(s);
668a65f56eeSaurel32             break;
669ea227027SFinn Thain         /* The guest is required to store aligned pointers here */
670a65f56eeSaurel32         case SONIC_RSA:
671a65f56eeSaurel32         case SONIC_REA:
672a65f56eeSaurel32         case SONIC_RRP:
673a65f56eeSaurel32         case SONIC_RWP:
674ea227027SFinn Thain             if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
675ea227027SFinn Thain                 s->regs[reg] = val & 0xfffc;
676ea227027SFinn Thain             } else {
6773fe9a838SFinn Thain                 s->regs[reg] = val & 0xfffe;
678ea227027SFinn Thain             }
679a65f56eeSaurel32             break;
680a65f56eeSaurel32         /* Invert written value for some registers */
681a65f56eeSaurel32         case SONIC_CRCT:
682a65f56eeSaurel32         case SONIC_FAET:
683a65f56eeSaurel32         case SONIC_MPT:
6843fe9a838SFinn Thain             s->regs[reg] = val ^ 0xffff;
685a65f56eeSaurel32             break;
686a65f56eeSaurel32         /* All other registers have no special contrainst */
687a65f56eeSaurel32         default:
6883fe9a838SFinn Thain             s->regs[reg] = val;
689a65f56eeSaurel32     }
690a65f56eeSaurel32 
691a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6923df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
693a65f56eeSaurel32     }
694a65f56eeSaurel32 }
695a65f56eeSaurel32 
69684689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
69784689cbbSHervé Poussineau     .read = dp8393x_read,
69884689cbbSHervé Poussineau     .write = dp8393x_write,
6993fe9a838SFinn Thain     .impl.min_access_size = 4,
7003fe9a838SFinn Thain     .impl.max_access_size = 4,
70184689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
70284689cbbSHervé Poussineau };
70384689cbbSHervé Poussineau 
704a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
705a65f56eeSaurel32 {
706a65f56eeSaurel32     dp8393xState *s = opaque;
707a65f56eeSaurel32 
708a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
709a65f56eeSaurel32         return;
710a65f56eeSaurel32     }
711a65f56eeSaurel32 
712a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
713a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
7143df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
715a65f56eeSaurel32 
716a65f56eeSaurel32     /* Signal underflow */
717a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
718a65f56eeSaurel32     dp8393x_update_irq(s);
719a65f56eeSaurel32 }
720a65f56eeSaurel32 
721*b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc)
722a65f56eeSaurel32 {
723cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
724a65f56eeSaurel32 
725*b8c4b67eSPhilippe Mathieu-Daudé     return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN);
726a65f56eeSaurel32 }
727a65f56eeSaurel32 
7283df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
7293df5de64SHervé Poussineau                                   int size)
730a65f56eeSaurel32 {
731a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
732a65f56eeSaurel32     int i;
733a65f56eeSaurel32 
734a65f56eeSaurel32     /* Check promiscuous mode */
735a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
736a65f56eeSaurel32         return 0;
737a65f56eeSaurel32     }
738a65f56eeSaurel32 
739a65f56eeSaurel32     /* Check multicast packets */
740a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
741a65f56eeSaurel32         return SONIC_RCR_MC;
742a65f56eeSaurel32     }
743a65f56eeSaurel32 
744a65f56eeSaurel32     /* Check broadcast */
745a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
746a65f56eeSaurel32         return SONIC_RCR_BC;
747a65f56eeSaurel32     }
748a65f56eeSaurel32 
749a65f56eeSaurel32     /* Check CAM */
750a65f56eeSaurel32     for (i = 0; i < 16; i++) {
751a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
752a65f56eeSaurel32              /* Entry enabled */
753a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
754a65f56eeSaurel32                  return 0;
755a65f56eeSaurel32              }
756a65f56eeSaurel32         }
757a65f56eeSaurel32     }
758a65f56eeSaurel32 
759a65f56eeSaurel32     return -1;
760a65f56eeSaurel32 }
761a65f56eeSaurel32 
7623df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7639e3cd456SFinn Thain                                size_t pkt_size)
764a65f56eeSaurel32 {
765cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
766a65f56eeSaurel32     int packet_type;
767a65f56eeSaurel32     uint32_t available, address;
768350e7d9aSFinn Thain     int width, rx_len, padded_len;
769a65f56eeSaurel32     uint32_t checksum;
7709e3cd456SFinn Thain     int size;
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 
775c2279bd0SFinn Thain     if (s->last_rba_is_full) {
776c2279bd0SFinn Thain         return pkt_size;
777c2279bd0SFinn Thain     }
778c2279bd0SFinn Thain 
779350e7d9aSFinn Thain     rx_len = pkt_size + sizeof(checksum);
780350e7d9aSFinn Thain     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
781350e7d9aSFinn Thain         width = 2;
782350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 3) + 1;
783350e7d9aSFinn Thain     } else {
784350e7d9aSFinn Thain         width = 1;
785350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 1) + 1;
786350e7d9aSFinn Thain     }
787350e7d9aSFinn Thain 
788350e7d9aSFinn Thain     if (padded_len > dp8393x_rbwc(s) * 2) {
789ada74315SFinn Thain         DPRINTF("oversize packet, pkt_size is %d\n", pkt_size);
790ada74315SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_RBAE;
791ada74315SFinn Thain         dp8393x_update_irq(s);
792c2279bd0SFinn Thain         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
793c2279bd0SFinn Thain         goto done;
794ada74315SFinn Thain     }
795ada74315SFinn Thain 
7969e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
797a65f56eeSaurel32     if (packet_type < 0) {
798a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
7994f1c942bSMark McLoughlin         return -1;
800a65f56eeSaurel32     }
801a65f56eeSaurel32 
802a65f56eeSaurel32     /* Check for EOL */
80388f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
804a65f56eeSaurel32         /* Are we still in resource exhaustion? */
805a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
806581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
80719f70347SPeter Maydell         address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED,
80819f70347SPeter Maydell                            s->data, size);
8095b0c98fcSFinn Thain         s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
8105b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
811a65f56eeSaurel32             /* Still EOL ; stop reception */
8124f1c942bSMark McLoughlin             return -1;
813a65f56eeSaurel32         }
8145b0c98fcSFinn Thain         /* Link has been updated by host */
815d9fae131SFinn Thain 
816d9fae131SFinn Thain         /* Clear in_use */
817d9fae131SFinn Thain         size = sizeof(uint16_t) * width;
818d9fae131SFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
819d9fae131SFinn Thain         dp8393x_put(s, width, 0, 0);
820d9fae131SFinn Thain         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
821d9fae131SFinn Thain                          (uint8_t *)s->data, size, 1);
822d9fae131SFinn Thain 
823d9fae131SFinn Thain         /* Move to next descriptor */
8245b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
825d9fae131SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
826a65f56eeSaurel32     }
827a65f56eeSaurel32 
828a65f56eeSaurel32     /* Save current position */
829a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
830a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
831a65f56eeSaurel32 
832a65f56eeSaurel32     /* Calculate the ethernet checksum */
833350e7d9aSFinn Thain     checksum = cpu_to_le32(crc32(0, buf, pkt_size));
834a65f56eeSaurel32 
835a65f56eeSaurel32     /* Put packet into RBA */
836581f7b12SPeter Maydell     DPRINTF("Receive packet at %08x\n", dp8393x_crba(s));
837581f7b12SPeter Maydell     address = dp8393x_crba(s);
83819f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
839350e7d9aSFinn Thain                         buf, pkt_size);
840350e7d9aSFinn Thain     address += pkt_size;
841350e7d9aSFinn Thain 
842350e7d9aSFinn Thain     /* Put frame checksum into RBA */
84319f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
844350e7d9aSFinn Thain                         &checksum, sizeof(checksum));
845350e7d9aSFinn Thain     address += sizeof(checksum);
846350e7d9aSFinn Thain 
847350e7d9aSFinn Thain     /* Pad short packets to keep pointers aligned */
848350e7d9aSFinn Thain     if (rx_len < padded_len) {
849350e7d9aSFinn Thain         size = padded_len - rx_len;
850350e7d9aSFinn Thain         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
851350e7d9aSFinn Thain             (uint8_t *)"\xFF\xFF\xFF", size, 1);
852350e7d9aSFinn Thain         address += size;
853350e7d9aSFinn Thain     }
854350e7d9aSFinn Thain 
855a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
856a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
857581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
858350e7d9aSFinn Thain     available -= padded_len >> 1;
859a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
860a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
861a65f56eeSaurel32 
862a65f56eeSaurel32     /* Update status */
863581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
864a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
865a65f56eeSaurel32     }
866a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
867a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
868a65f56eeSaurel32     if (s->loopback_packet) {
869a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
870a65f56eeSaurel32         s->loopback_packet = 0;
871a65f56eeSaurel32     }
872a65f56eeSaurel32 
873a65f56eeSaurel32     /* Write status to memory */
874581f7b12SPeter Maydell     DPRINTF("Write status at %08x\n", dp8393x_crda(s));
875af9f0be3SLaurent Vivier     dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */
876af9f0be3SLaurent Vivier     dp8393x_put(s, width, 1, rx_len); /* byte count */
877af9f0be3SLaurent Vivier     dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
878af9f0be3SLaurent Vivier     dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
879af9f0be3SLaurent Vivier     dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */
880a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
88119f70347SPeter Maydell     address_space_write(&s->as, dp8393x_crda(s),
88219f70347SPeter Maydell                         MEMTXATTRS_UNSPECIFIED,
88319f70347SPeter Maydell                         s->data, size);
884a65f56eeSaurel32 
8855b0c98fcSFinn Thain     /* Check link field */
886a65f56eeSaurel32     size = sizeof(uint16_t) * width;
88719f70347SPeter Maydell     address_space_read(&s->as,
88819f70347SPeter Maydell                        dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
88919f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
890af9f0be3SLaurent Vivier     s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
89188f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
892a65f56eeSaurel32         /* EOL detected */
893a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
894a65f56eeSaurel32     } else {
89546ffee9aSFinn Thain         /* Clear in_use */
89646ffee9aSFinn Thain         size = sizeof(uint16_t) * width;
89746ffee9aSFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
89846ffee9aSFinn Thain         dp8393x_put(s, width, 0, 0);
89946ffee9aSFinn Thain         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
90046ffee9aSFinn Thain                             s->data, size);
9015b0c98fcSFinn Thain 
9025b0c98fcSFinn Thain         /* Move to next descriptor */
903a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
904a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
90580b60673SFinn Thain     }
90680b60673SFinn Thain 
907c2279bd0SFinn Thain     dp8393x_update_irq(s);
908c2279bd0SFinn Thain 
90980b60673SFinn Thain     s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) |
91080b60673SFinn Thain                          ((s->regs[SONIC_RSC] + 1) & 0x00ff);
911a65f56eeSaurel32 
912c2279bd0SFinn Thain done:
913c2279bd0SFinn Thain 
914a65f56eeSaurel32     if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
915c2279bd0SFinn Thain         if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
916c2279bd0SFinn Thain             /* Stop packet reception */
917c2279bd0SFinn Thain             s->last_rba_is_full = true;
918c2279bd0SFinn Thain         } else {
919c2279bd0SFinn Thain             /* Read next resource */
9203df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
921a65f56eeSaurel32         }
922c2279bd0SFinn Thain     }
9234f1c942bSMark McLoughlin 
9249e3cd456SFinn Thain     return pkt_size;
925a65f56eeSaurel32 }
926a65f56eeSaurel32 
927104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
928a65f56eeSaurel32 {
929104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
930bc72ad67SAlex Bligh     timer_del(s->watchdog);
931a65f56eeSaurel32 
932bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
933083e21bbSFinn Thain     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */
934a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
935a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
936a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
937a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
938a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
939a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
940a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
941a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
942a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
943a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
944a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
945a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
946a65f56eeSaurel32 
947a65f56eeSaurel32     /* Network cable is connected */
948a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
949a65f56eeSaurel32 
950a65f56eeSaurel32     dp8393x_update_irq(s);
951a65f56eeSaurel32 }
952a65f56eeSaurel32 
95305f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
954f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
95505f41fe3SMark McLoughlin     .size = sizeof(NICState),
9563df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
9573df5de64SHervé Poussineau     .receive = dp8393x_receive,
95805f41fe3SMark McLoughlin };
95905f41fe3SMark McLoughlin 
960104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
961a65f56eeSaurel32 {
962104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
963104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
964a65f56eeSaurel32 
965104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
96689ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
967104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
968104655a5SHervé Poussineau }
969a65f56eeSaurel32 
970104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
971104655a5SHervé Poussineau {
972104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
97389ae0ff9SHervé Poussineau     int i, checksum;
97489ae0ff9SHervé Poussineau     uint8_t *prom;
97552579c68SHervé Poussineau     Error *local_err = NULL;
976a65f56eeSaurel32 
977104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
978104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
979104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
980104655a5SHervé Poussineau 
981104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
982104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
983104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
984104655a5SHervé Poussineau 
985bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
98689ae0ff9SHervé Poussineau 
987fcd3b085SPhilippe Mathieu-Daudé     memory_region_init_rom(&s->prom, OBJECT(dev), "dp8393x-prom",
988fcd3b085SPhilippe Mathieu-Daudé                            SONIC_PROM_SIZE, &local_err);
98952579c68SHervé Poussineau     if (local_err) {
99052579c68SHervé Poussineau         error_propagate(errp, local_err);
99152579c68SHervé Poussineau         return;
99252579c68SHervé Poussineau     }
99389ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
99489ae0ff9SHervé Poussineau     checksum = 0;
99589ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
99689ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
99789ae0ff9SHervé Poussineau         checksum += prom[i];
99889ae0ff9SHervé Poussineau         if (checksum > 0xff) {
99989ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
100089ae0ff9SHervé Poussineau         }
100189ae0ff9SHervé Poussineau     }
100289ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
1003a65f56eeSaurel32 }
1004104655a5SHervé Poussineau 
10051670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
10061670735dSHervé Poussineau     .name = "dp8393x",
10071670735dSHervé Poussineau     .version_id = 0,
10081670735dSHervé Poussineau     .minimum_version_id = 0,
10091670735dSHervé Poussineau     .fields = (VMStateField []) {
10101670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
10111670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
10121670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
10131670735dSHervé Poussineau     }
10141670735dSHervé Poussineau };
10151670735dSHervé Poussineau 
1016104655a5SHervé Poussineau static Property dp8393x_properties[] = {
1017104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
10183110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
10193110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
1020104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
1021be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
1022104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
1023104655a5SHervé Poussineau };
1024104655a5SHervé Poussineau 
1025104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
1026104655a5SHervé Poussineau {
1027104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
1028104655a5SHervé Poussineau 
1029104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
1030104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
1031104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
10321670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
10334f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
1034104655a5SHervé Poussineau }
1035104655a5SHervé Poussineau 
1036104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
1037104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
1038104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
1039104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
1040104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
1041104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
1042104655a5SHervé Poussineau };
1043104655a5SHervé Poussineau 
1044104655a5SHervé Poussineau static void dp8393x_register_types(void)
1045104655a5SHervé Poussineau {
1046104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
1047104655a5SHervé Poussineau }
1048104655a5SHervé Poussineau 
1049104655a5SHervé Poussineau type_init(dp8393x_register_types)
1050