xref: /qemu/hw/net/dp8393x.c (revision 8ac2ffb584590b0398ae4e1a08a0b5d209b6f250)
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>
30db1015e9SEduardo Habkost #include "qom/object.h"
31c0af04a4SMark Cave-Ayland #include "trace.h"
32a65f56eeSaurel32 
33a65f56eeSaurel32 static const char *reg_names[] = {
34a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
35a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
36a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
37a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
38a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
39a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
40a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
41a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
42a65f56eeSaurel32 
43a65f56eeSaurel32 #define SONIC_CR     0x00
44a65f56eeSaurel32 #define SONIC_DCR    0x01
45a65f56eeSaurel32 #define SONIC_RCR    0x02
46a65f56eeSaurel32 #define SONIC_TCR    0x03
47a65f56eeSaurel32 #define SONIC_IMR    0x04
48a65f56eeSaurel32 #define SONIC_ISR    0x05
49a65f56eeSaurel32 #define SONIC_UTDA   0x06
50a65f56eeSaurel32 #define SONIC_CTDA   0x07
51a65f56eeSaurel32 #define SONIC_TPS    0x08
52a65f56eeSaurel32 #define SONIC_TFC    0x09
53a65f56eeSaurel32 #define SONIC_TSA0   0x0a
54a65f56eeSaurel32 #define SONIC_TSA1   0x0b
55a65f56eeSaurel32 #define SONIC_TFS    0x0c
56a65f56eeSaurel32 #define SONIC_URDA   0x0d
57a65f56eeSaurel32 #define SONIC_CRDA   0x0e
58a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
59a65f56eeSaurel32 #define SONIC_CRBA1  0x10
60a65f56eeSaurel32 #define SONIC_RBWC0  0x11
61a65f56eeSaurel32 #define SONIC_RBWC1  0x12
62a65f56eeSaurel32 #define SONIC_EOBC   0x13
63a65f56eeSaurel32 #define SONIC_URRA   0x14
64a65f56eeSaurel32 #define SONIC_RSA    0x15
65a65f56eeSaurel32 #define SONIC_REA    0x16
66a65f56eeSaurel32 #define SONIC_RRP    0x17
67a65f56eeSaurel32 #define SONIC_RWP    0x18
68a65f56eeSaurel32 #define SONIC_TRBA0  0x19
69a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
70a65f56eeSaurel32 #define SONIC_LLFA   0x1f
71a65f56eeSaurel32 #define SONIC_TTDA   0x20
72a65f56eeSaurel32 #define SONIC_CEP    0x21
73a65f56eeSaurel32 #define SONIC_CAP2   0x22
74a65f56eeSaurel32 #define SONIC_CAP1   0x23
75a65f56eeSaurel32 #define SONIC_CAP0   0x24
76a65f56eeSaurel32 #define SONIC_CE     0x25
77a65f56eeSaurel32 #define SONIC_CDP    0x26
78a65f56eeSaurel32 #define SONIC_CDC    0x27
79a65f56eeSaurel32 #define SONIC_SR     0x28
80a65f56eeSaurel32 #define SONIC_WT0    0x29
81a65f56eeSaurel32 #define SONIC_WT1    0x2a
82a65f56eeSaurel32 #define SONIC_RSC    0x2b
83a65f56eeSaurel32 #define SONIC_CRCT   0x2c
84a65f56eeSaurel32 #define SONIC_FAET   0x2d
85a65f56eeSaurel32 #define SONIC_MPT    0x2e
86a65f56eeSaurel32 #define SONIC_MDT    0x2f
87a65f56eeSaurel32 #define SONIC_DCR2   0x3f
8867b38ddfSPhilippe Mathieu-Daudé #define SONIC_REG_COUNT  0x40
89a65f56eeSaurel32 
90a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
91a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
92a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
93a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
94a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
95a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
96a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
97a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
98a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
99a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
100a65f56eeSaurel32 
101a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
102a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
103a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
104a65f56eeSaurel32 
105a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
106a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
107a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
108a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
109a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
110a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
111a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
112a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
113a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
114a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
115a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
116a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
117a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
118a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
119a65f56eeSaurel32 
120a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
121a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
122a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
123a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
124a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
125a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
126a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
127a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
128a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
129a65f56eeSaurel32 
130ada74315SFinn Thain #define SONIC_ISR_RBAE   0x0010
131a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
132a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
133a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
134a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
135a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
136a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
137a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
138a65f56eeSaurel32 
13988f632fbSFinn Thain #define SONIC_DESC_EOL   0x0001
14088f632fbSFinn Thain #define SONIC_DESC_ADDR  0xFFFE
14188f632fbSFinn Thain 
142104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
1438063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(dp8393xState, DP8393X)
144104655a5SHervé Poussineau 
145db1015e9SEduardo Habkost struct dp8393xState {
146104655a5SHervé Poussineau     SysBusDevice parent_obj;
147104655a5SHervé Poussineau 
148a65f56eeSaurel32     /* Hardware */
149104655a5SHervé Poussineau     uint8_t it_shift;
150be920841SLaurent Vivier     bool big_endian;
151c2279bd0SFinn Thain     bool last_rba_is_full;
152a65f56eeSaurel32     qemu_irq irq;
153a65f56eeSaurel32     int irq_level;
154a65f56eeSaurel32     QEMUTimer *watchdog;
155a65f56eeSaurel32     int64_t wt_last_update;
15605f41fe3SMark McLoughlin     NICConf conf;
15705f41fe3SMark McLoughlin     NICState *nic;
158024e5bb6SAvi Kivity     MemoryRegion mmio;
159a65f56eeSaurel32 
160a65f56eeSaurel32     /* Registers */
161*8ac2ffb5SPhilippe Mathieu-Daudé     uint16_t cam[16][3];
16267b38ddfSPhilippe Mathieu-Daudé     uint16_t regs[SONIC_REG_COUNT];
163a65f56eeSaurel32 
164a65f56eeSaurel32     /* Temporaries */
165a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
166af9f0be3SLaurent Vivier     uint16_t data[12];
167a65f56eeSaurel32     int loopback_packet;
168a65f56eeSaurel32 
169a65f56eeSaurel32     /* Memory access */
1703110ce81SMarc-André Lureau     MemoryRegion *dma_mr;
171dd820513SHervé Poussineau     AddressSpace as;
172db1015e9SEduardo Habkost };
173a65f56eeSaurel32 
1741ca82a8dSMark Cave-Ayland /*
1751ca82a8dSMark Cave-Ayland  * Accessor functions for values which are formed by
176581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
177581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
178581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
179581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
180581f7b12SPeter Maydell  */
181581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
182581f7b12SPeter Maydell {
183581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
184581f7b12SPeter Maydell }
185581f7b12SPeter Maydell 
186581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
187581f7b12SPeter Maydell {
188581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
189581f7b12SPeter Maydell }
190581f7b12SPeter Maydell 
191581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
192581f7b12SPeter Maydell {
19388f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
19488f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
195581f7b12SPeter Maydell }
196581f7b12SPeter Maydell 
197581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
198581f7b12SPeter Maydell {
199581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
200581f7b12SPeter Maydell }
201581f7b12SPeter Maydell 
202581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
203581f7b12SPeter Maydell {
204581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
205581f7b12SPeter Maydell }
206581f7b12SPeter Maydell 
207581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
208581f7b12SPeter Maydell {
209581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
210581f7b12SPeter Maydell }
211581f7b12SPeter Maydell 
212581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
213581f7b12SPeter Maydell {
21488f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
21588f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
216581f7b12SPeter Maydell }
217581f7b12SPeter Maydell 
218581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
219581f7b12SPeter Maydell {
220581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
221581f7b12SPeter Maydell }
222581f7b12SPeter Maydell 
223af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset)
224be920841SLaurent Vivier {
225be920841SLaurent Vivier     uint16_t val;
226be920841SLaurent Vivier 
227be920841SLaurent Vivier     if (s->big_endian) {
228af9f0be3SLaurent Vivier         val = be16_to_cpu(s->data[offset * width + width - 1]);
229be920841SLaurent Vivier     } else {
230af9f0be3SLaurent Vivier         val = le16_to_cpu(s->data[offset * width]);
231be920841SLaurent Vivier     }
232be920841SLaurent Vivier     return val;
233be920841SLaurent Vivier }
234be920841SLaurent Vivier 
235af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset,
236be920841SLaurent Vivier                         uint16_t val)
237be920841SLaurent Vivier {
238be920841SLaurent Vivier     if (s->big_endian) {
2393fe9a838SFinn Thain         if (width == 2) {
2403fe9a838SFinn Thain             s->data[offset * 2] = 0;
2413fe9a838SFinn Thain             s->data[offset * 2 + 1] = cpu_to_be16(val);
242be920841SLaurent Vivier         } else {
2433fe9a838SFinn Thain             s->data[offset] = cpu_to_be16(val);
2443fe9a838SFinn Thain         }
2453fe9a838SFinn Thain     } else {
2463fe9a838SFinn Thain         if (width == 2) {
2473fe9a838SFinn Thain             s->data[offset * 2] = cpu_to_le16(val);
2483fe9a838SFinn Thain             s->data[offset * 2 + 1] = 0;
2493fe9a838SFinn Thain         } else {
2503fe9a838SFinn Thain             s->data[offset] = cpu_to_le16(val);
2513fe9a838SFinn Thain         }
252be920841SLaurent Vivier     }
253be920841SLaurent Vivier }
254be920841SLaurent Vivier 
255a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
256a65f56eeSaurel32 {
257a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
258a65f56eeSaurel32 
259a65f56eeSaurel32     if (level != s->irq_level) {
260a65f56eeSaurel32         s->irq_level = level;
261a65f56eeSaurel32         if (level) {
262c0af04a4SMark Cave-Ayland             trace_dp8393x_raise_irq(s->regs[SONIC_ISR]);
263a65f56eeSaurel32         } else {
264c0af04a4SMark Cave-Ayland             trace_dp8393x_lower_irq();
265a65f56eeSaurel32         }
266a65f56eeSaurel32     }
267a65f56eeSaurel32 
268a65f56eeSaurel32     qemu_set_irq(s->irq, level);
269a65f56eeSaurel32 }
270a65f56eeSaurel32 
2713df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
272a65f56eeSaurel32 {
273a65f56eeSaurel32     int width, size;
27485e411d7SMark Cave-Ayland     uint16_t index;
275a65f56eeSaurel32 
276a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
277a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
278a65f56eeSaurel32 
279a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
280a65f56eeSaurel32         /* Fill current entry */
28119f70347SPeter Maydell         address_space_read(&s->as, dp8393x_cdp(s),
28219f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
28385e411d7SMark Cave-Ayland         index = dp8393x_get(s, width, 0) & 0xf;
284*8ac2ffb5SPhilippe Mathieu-Daudé         s->cam[index][0] = dp8393x_get(s, width, 1);
285*8ac2ffb5SPhilippe Mathieu-Daudé         s->cam[index][1] = dp8393x_get(s, width, 2);
286*8ac2ffb5SPhilippe Mathieu-Daudé         s->cam[index][2] = dp8393x_get(s, width, 3);
287*8ac2ffb5SPhilippe Mathieu-Daudé         trace_dp8393x_load_cam(index,
288*8ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][0] >> 8, s->cam[index][0] & 0xff,
289*8ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][1] >> 8, s->cam[index][1] & 0xff,
290*8ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][2] >> 8, s->cam[index][2] & 0xff);
291a65f56eeSaurel32         /* Move to next entry */
292a65f56eeSaurel32         s->regs[SONIC_CDC]--;
293a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
294a65f56eeSaurel32     }
295a65f56eeSaurel32 
296a65f56eeSaurel32     /* Read CAM enable */
29719f70347SPeter Maydell     address_space_read(&s->as, dp8393x_cdp(s),
29819f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
299af9f0be3SLaurent Vivier     s->regs[SONIC_CE] = dp8393x_get(s, width, 0);
300c0af04a4SMark Cave-Ayland     trace_dp8393x_load_cam_done(s->regs[SONIC_CE]);
301a65f56eeSaurel32 
302a65f56eeSaurel32     /* Done */
303a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
304a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
305a65f56eeSaurel32     dp8393x_update_irq(s);
306a65f56eeSaurel32 }
307a65f56eeSaurel32 
3083df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
309a65f56eeSaurel32 {
310a65f56eeSaurel32     int width, size;
311a65f56eeSaurel32 
312a65f56eeSaurel32     /* Read memory */
313a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
314a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
31519f70347SPeter Maydell     address_space_read(&s->as, dp8393x_rrp(s),
31619f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
317a65f56eeSaurel32 
318a65f56eeSaurel32     /* Update SONIC registers */
319af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0);
320af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1);
321af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2);
322af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3);
323c0af04a4SMark Cave-Ayland     trace_dp8393x_read_rra_regs(s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
324a65f56eeSaurel32                                 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
325a65f56eeSaurel32 
326a65f56eeSaurel32     /* Go to next entry */
327a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
328a65f56eeSaurel32 
329a65f56eeSaurel32     /* Handle wrap */
330a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
331a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
332a65f56eeSaurel32     }
333a65f56eeSaurel32 
334c2279bd0SFinn Thain     /* Warn the host if CRBA now has the last available resource */
3351ca82a8dSMark Cave-Ayland     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
336a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
337a65f56eeSaurel32         dp8393x_update_irq(s);
338a65f56eeSaurel32     }
339c2279bd0SFinn Thain 
340c2279bd0SFinn Thain     /* Allow packet reception */
341c2279bd0SFinn Thain     s->last_rba_is_full = false;
342a65f56eeSaurel32 }
343a65f56eeSaurel32 
3443df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
345a65f56eeSaurel32 {
346bc72ad67SAlex Bligh     timer_del(s->watchdog);
347a65f56eeSaurel32 
3481ca82a8dSMark Cave-Ayland     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP |
3491ca82a8dSMark Cave-Ayland                            SONIC_CR_HTX);
350a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
351a65f56eeSaurel32 }
352a65f56eeSaurel32 
3533df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
354a65f56eeSaurel32 {
355a65f56eeSaurel32     uint32_t ticks;
356a65f56eeSaurel32     int64_t delay;
357a65f56eeSaurel32 
358a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
359bc72ad67SAlex Bligh         timer_del(s->watchdog);
360a65f56eeSaurel32         return;
361a65f56eeSaurel32     }
362a65f56eeSaurel32 
363581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
364bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
36573bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
366bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
367a65f56eeSaurel32 }
368a65f56eeSaurel32 
3693df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
370a65f56eeSaurel32 {
371a65f56eeSaurel32     int64_t elapsed;
372a65f56eeSaurel32     uint32_t val;
373a65f56eeSaurel32 
374a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
375bc72ad67SAlex Bligh         timer_del(s->watchdog);
376a65f56eeSaurel32         return;
377a65f56eeSaurel32     }
378a65f56eeSaurel32 
379bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
380581f7b12SPeter Maydell     val = dp8393x_wt(s);
381a65f56eeSaurel32     val -= elapsed / 5000000;
382a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
383a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3843df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
385a65f56eeSaurel32 
386a65f56eeSaurel32 }
387a65f56eeSaurel32 
3883df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
389a65f56eeSaurel32 {
390a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3913df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
392a65f56eeSaurel32 }
393a65f56eeSaurel32 
3943df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
395a65f56eeSaurel32 {
396a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3973df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
398a65f56eeSaurel32 }
399a65f56eeSaurel32 
400b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc);
4014594f93aSFam Zheng 
4023df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
403a65f56eeSaurel32 {
404a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
4054594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
4064594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
4074594f93aSFam Zheng     }
408a65f56eeSaurel32 }
409a65f56eeSaurel32 
4103df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
411a65f56eeSaurel32 {
412a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
413a65f56eeSaurel32 }
414a65f56eeSaurel32 
4153df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
416a65f56eeSaurel32 {
417b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
418a65f56eeSaurel32     int width, size;
419a65f56eeSaurel32     int tx_len, len;
420a65f56eeSaurel32     uint16_t i;
421a65f56eeSaurel32 
422a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
423a65f56eeSaurel32 
424a65f56eeSaurel32     while (1) {
425a65f56eeSaurel32         /* Read memory */
426a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
427a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
428c0af04a4SMark Cave-Ayland         trace_dp8393x_transmit_packet(dp8393x_ttda(s));
42919f70347SPeter Maydell         address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width,
43019f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
431a65f56eeSaurel32         tx_len = 0;
432a65f56eeSaurel32 
433a65f56eeSaurel32         /* Update registers */
434af9f0be3SLaurent Vivier         s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000;
435af9f0be3SLaurent Vivier         s->regs[SONIC_TPS] = dp8393x_get(s, width, 1);
436af9f0be3SLaurent Vivier         s->regs[SONIC_TFC] = dp8393x_get(s, width, 2);
437af9f0be3SLaurent Vivier         s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3);
438af9f0be3SLaurent Vivier         s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4);
439af9f0be3SLaurent Vivier         s->regs[SONIC_TFS] = dp8393x_get(s, width, 5);
440a65f56eeSaurel32 
441a65f56eeSaurel32         /* Handle programmable interrupt */
442a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
443a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
444a65f56eeSaurel32         } else {
445a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
446a65f56eeSaurel32         }
447a65f56eeSaurel32 
448a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
449a65f56eeSaurel32             /* Append fragment */
450a65f56eeSaurel32             len = s->regs[SONIC_TFS];
451a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
452a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
453a65f56eeSaurel32             }
45419f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
45519f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
456a65f56eeSaurel32             tx_len += len;
457a65f56eeSaurel32 
458a65f56eeSaurel32             i++;
459a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
460a65f56eeSaurel32                 /* Read next fragment details */
461a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
46219f70347SPeter Maydell                 address_space_read(&s->as,
46319f70347SPeter Maydell                                    dp8393x_ttda(s)
46419f70347SPeter Maydell                                    + sizeof(uint16_t) * width * (4 + 3 * i),
46519f70347SPeter Maydell                                    MEMTXATTRS_UNSPECIFIED, s->data,
46619f70347SPeter Maydell                                    size);
467af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0);
468af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1);
469af9f0be3SLaurent Vivier                 s->regs[SONIC_TFS] = dp8393x_get(s, width, 2);
470a65f56eeSaurel32             }
471a65f56eeSaurel32         }
472a65f56eeSaurel32 
473a65f56eeSaurel32         /* Handle Ethernet checksum */
474a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
4751ca82a8dSMark Cave-Ayland             /*
4761ca82a8dSMark Cave-Ayland              * Don't append FCS there, to look like slirp packets
4771ca82a8dSMark Cave-Ayland              * which don't have one
4781ca82a8dSMark Cave-Ayland              */
479a65f56eeSaurel32         } else {
480a65f56eeSaurel32             /* Remove existing FCS */
481a65f56eeSaurel32             tx_len -= 4;
482915976bdSMauro Matteo Cascella             if (tx_len < 0) {
483c0af04a4SMark Cave-Ayland                 trace_dp8393x_transmit_txlen_error(tx_len);
484915976bdSMauro Matteo Cascella                 break;
485915976bdSMauro Matteo Cascella             }
486a65f56eeSaurel32         }
487a65f56eeSaurel32 
488a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
489a65f56eeSaurel32             /* Loopback */
490a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
491b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
492a65f56eeSaurel32                 s->loopback_packet = 1;
493331d2ac9SJason Wang                 qemu_receive_packet(nc, s->tx_buffer, tx_len);
494a65f56eeSaurel32             }
495a65f56eeSaurel32         } else {
496a65f56eeSaurel32             /* Transmit packet */
497b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
498a65f56eeSaurel32         }
499a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
500a65f56eeSaurel32 
501a65f56eeSaurel32         /* Write status */
502af9f0be3SLaurent Vivier         dp8393x_put(s, width, 0,
503be920841SLaurent Vivier                     s->regs[SONIC_TCR] & 0x0fff); /* status */
504a65f56eeSaurel32         size = sizeof(uint16_t) * width;
50519f70347SPeter Maydell         address_space_write(&s->as, dp8393x_ttda(s),
50619f70347SPeter Maydell                             MEMTXATTRS_UNSPECIFIED, s->data, size);
507a65f56eeSaurel32 
508a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
509a65f56eeSaurel32             /* Read footer of packet */
510a65f56eeSaurel32             size = sizeof(uint16_t) * width;
51119f70347SPeter Maydell             address_space_read(&s->as,
51219f70347SPeter Maydell                                dp8393x_ttda(s)
51319f70347SPeter Maydell                                + sizeof(uint16_t) * width
51419f70347SPeter Maydell                                  * (4 + 3 * s->regs[SONIC_TFC]),
51519f70347SPeter Maydell                                MEMTXATTRS_UNSPECIFIED, s->data,
51619f70347SPeter Maydell                                size);
517a0cf4297SFinn Thain             s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0);
518a0cf4297SFinn Thain             if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) {
519a65f56eeSaurel32                 /* EOL detected */
520a65f56eeSaurel32                 break;
521a65f56eeSaurel32             }
522a65f56eeSaurel32         }
523a65f56eeSaurel32     }
524a65f56eeSaurel32 
525a65f56eeSaurel32     /* Done */
526a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
527a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
528a65f56eeSaurel32     dp8393x_update_irq(s);
529a65f56eeSaurel32 }
530a65f56eeSaurel32 
5313df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
532a65f56eeSaurel32 {
533a65f56eeSaurel32     /* Nothing to do */
534a65f56eeSaurel32 }
535a65f56eeSaurel32 
5363df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
537a65f56eeSaurel32 {
538a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
539a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
540a65f56eeSaurel32         return;
541a65f56eeSaurel32     }
542a65f56eeSaurel32 
543a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
544a65f56eeSaurel32 
5451ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_HTX) {
5463df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
5471ca82a8dSMark Cave-Ayland     }
5481ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_TXP) {
5493df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
5501ca82a8dSMark Cave-Ayland     }
5511ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXDIS) {
5523df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
5531ca82a8dSMark Cave-Ayland     }
5541ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXEN) {
5553df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
5561ca82a8dSMark Cave-Ayland     }
5571ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_STP) {
5583df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
5591ca82a8dSMark Cave-Ayland     }
5601ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_ST) {
5613df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
5621ca82a8dSMark Cave-Ayland     }
5631ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RST) {
5643df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
5651ca82a8dSMark Cave-Ayland     }
566a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5673df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
568a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
569a3cce282SFinn Thain     }
5701ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_LCAM) {
5713df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
572a65f56eeSaurel32     }
5731ca82a8dSMark Cave-Ayland }
574a65f56eeSaurel32 
57584689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
576a65f56eeSaurel32 {
57784689cbbSHervé Poussineau     dp8393xState *s = opaque;
57884689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
579a65f56eeSaurel32     uint16_t val = 0;
580a65f56eeSaurel32 
581a65f56eeSaurel32     switch (reg) {
582a65f56eeSaurel32     /* Update data before reading it */
583a65f56eeSaurel32     case SONIC_WT0:
584a65f56eeSaurel32     case SONIC_WT1:
5853df5de64SHervé Poussineau         dp8393x_update_wt_regs(s);
586a65f56eeSaurel32         val = s->regs[reg];
587a65f56eeSaurel32         break;
588a65f56eeSaurel32     /* Accept read to some registers only when in reset mode */
589a65f56eeSaurel32     case SONIC_CAP2:
590a65f56eeSaurel32     case SONIC_CAP1:
591a65f56eeSaurel32     case SONIC_CAP0:
592a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
593*8ac2ffb5SPhilippe Mathieu-Daudé             val = s->cam[s->regs[SONIC_CEP] & 0xf][SONIC_CAP0 - reg];
594a65f56eeSaurel32         }
595a65f56eeSaurel32         break;
5961ca82a8dSMark Cave-Ayland     /* All other registers have no special contraints */
597a65f56eeSaurel32     default:
598a65f56eeSaurel32         val = s->regs[reg];
599a65f56eeSaurel32     }
600a65f56eeSaurel32 
601c0af04a4SMark Cave-Ayland     trace_dp8393x_read(reg, reg_names[reg], val, size);
602a65f56eeSaurel32 
6033fe9a838SFinn Thain     return s->big_endian ? val << 16 : val;
604a65f56eeSaurel32 }
605a65f56eeSaurel32 
60684689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
60784689cbbSHervé Poussineau                           unsigned int size)
608a65f56eeSaurel32 {
60984689cbbSHervé Poussineau     dp8393xState *s = opaque;
61084689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
6113fe9a838SFinn Thain     uint32_t val = s->big_endian ? data >> 16 : data;
61284689cbbSHervé Poussineau 
613c0af04a4SMark Cave-Ayland     trace_dp8393x_write(reg, reg_names[reg], val, size);
614a65f56eeSaurel32 
615a65f56eeSaurel32     switch (reg) {
616a65f56eeSaurel32     /* Command register */
617a65f56eeSaurel32     case SONIC_CR:
6183fe9a838SFinn Thain         dp8393x_do_command(s, val);
619a65f56eeSaurel32         break;
620a65f56eeSaurel32     /* Prevent write to read-only registers */
621a65f56eeSaurel32     case SONIC_CAP2:
622a65f56eeSaurel32     case SONIC_CAP1:
623a65f56eeSaurel32     case SONIC_CAP0:
624a65f56eeSaurel32     case SONIC_SR:
625a65f56eeSaurel32     case SONIC_MDT:
626c0af04a4SMark Cave-Ayland         trace_dp8393x_write_invalid(reg);
627a65f56eeSaurel32         break;
628a65f56eeSaurel32     /* Accept write to some registers only when in reset mode */
629a65f56eeSaurel32     case SONIC_DCR:
630a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6313fe9a838SFinn Thain             s->regs[reg] = val & 0xbfff;
632a65f56eeSaurel32         } else {
633c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR");
634a65f56eeSaurel32         }
635a65f56eeSaurel32         break;
636a65f56eeSaurel32     case SONIC_DCR2:
637a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6383fe9a838SFinn Thain             s->regs[reg] = val & 0xf017;
639a65f56eeSaurel32         } else {
640c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR2");
641a65f56eeSaurel32         }
642a65f56eeSaurel32         break;
643a65f56eeSaurel32     /* 12 lower bytes are Read Only */
644a65f56eeSaurel32     case SONIC_TCR:
6453fe9a838SFinn Thain         s->regs[reg] = val & 0xf000;
646a65f56eeSaurel32         break;
647a65f56eeSaurel32     /* 9 lower bytes are Read Only */
648a65f56eeSaurel32     case SONIC_RCR:
6493fe9a838SFinn Thain         s->regs[reg] = val & 0xffe0;
650a65f56eeSaurel32         break;
651a65f56eeSaurel32     /* Ignore most significant bit */
652a65f56eeSaurel32     case SONIC_IMR:
6533fe9a838SFinn Thain         s->regs[reg] = val & 0x7fff;
654a65f56eeSaurel32         dp8393x_update_irq(s);
655a65f56eeSaurel32         break;
656a65f56eeSaurel32     /* Clear bits by writing 1 to them */
657a65f56eeSaurel32     case SONIC_ISR:
6583fe9a838SFinn Thain         val &= s->regs[reg];
6593fe9a838SFinn Thain         s->regs[reg] &= ~val;
6603fe9a838SFinn Thain         if (val & SONIC_ISR_RBE) {
6613df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
662a65f56eeSaurel32         }
663a65f56eeSaurel32         dp8393x_update_irq(s);
664a65f56eeSaurel32         break;
665ea227027SFinn Thain     /* The guest is required to store aligned pointers here */
666a65f56eeSaurel32     case SONIC_RSA:
667a65f56eeSaurel32     case SONIC_REA:
668a65f56eeSaurel32     case SONIC_RRP:
669a65f56eeSaurel32     case SONIC_RWP:
670ea227027SFinn Thain         if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
671ea227027SFinn Thain             s->regs[reg] = val & 0xfffc;
672ea227027SFinn Thain         } else {
6733fe9a838SFinn Thain             s->regs[reg] = val & 0xfffe;
674ea227027SFinn Thain         }
675a65f56eeSaurel32         break;
676a65f56eeSaurel32     /* Invert written value for some registers */
677a65f56eeSaurel32     case SONIC_CRCT:
678a65f56eeSaurel32     case SONIC_FAET:
679a65f56eeSaurel32     case SONIC_MPT:
6803fe9a838SFinn Thain         s->regs[reg] = val ^ 0xffff;
681a65f56eeSaurel32         break;
682a65f56eeSaurel32     /* All other registers have no special contrainst */
683a65f56eeSaurel32     default:
6843fe9a838SFinn Thain         s->regs[reg] = val;
685a65f56eeSaurel32     }
686a65f56eeSaurel32 
687a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6883df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
689a65f56eeSaurel32     }
690a65f56eeSaurel32 }
691a65f56eeSaurel32 
69284689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
69384689cbbSHervé Poussineau     .read = dp8393x_read,
69484689cbbSHervé Poussineau     .write = dp8393x_write,
6953fe9a838SFinn Thain     .impl.min_access_size = 4,
6963fe9a838SFinn Thain     .impl.max_access_size = 4,
69784689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
69884689cbbSHervé Poussineau };
69984689cbbSHervé Poussineau 
700a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
701a65f56eeSaurel32 {
702a65f56eeSaurel32     dp8393xState *s = opaque;
703a65f56eeSaurel32 
704a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
705a65f56eeSaurel32         return;
706a65f56eeSaurel32     }
707a65f56eeSaurel32 
708a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
709a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
7103df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
711a65f56eeSaurel32 
712a65f56eeSaurel32     /* Signal underflow */
713a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
714a65f56eeSaurel32     dp8393x_update_irq(s);
715a65f56eeSaurel32 }
716a65f56eeSaurel32 
717b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc)
718a65f56eeSaurel32 {
719cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
720a65f56eeSaurel32 
721b8c4b67eSPhilippe Mathieu-Daudé     return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN);
722a65f56eeSaurel32 }
723a65f56eeSaurel32 
7243df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
7253df5de64SHervé Poussineau                                   int size)
726a65f56eeSaurel32 {
727a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
728a65f56eeSaurel32     int i;
729a65f56eeSaurel32 
730a65f56eeSaurel32     /* Check promiscuous mode */
731a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
732a65f56eeSaurel32         return 0;
733a65f56eeSaurel32     }
734a65f56eeSaurel32 
735a65f56eeSaurel32     /* Check multicast packets */
736a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
737a65f56eeSaurel32         return SONIC_RCR_MC;
738a65f56eeSaurel32     }
739a65f56eeSaurel32 
740a65f56eeSaurel32     /* Check broadcast */
7411ca82a8dSMark Cave-Ayland     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) &&
7421ca82a8dSMark Cave-Ayland          !memcmp(buf, bcast, sizeof(bcast))) {
743a65f56eeSaurel32         return SONIC_RCR_BC;
744a65f56eeSaurel32     }
745a65f56eeSaurel32 
746a65f56eeSaurel32     /* Check CAM */
747a65f56eeSaurel32     for (i = 0; i < 16; i++) {
748a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
749a65f56eeSaurel32             /* Entry enabled */
750a65f56eeSaurel32             if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
751a65f56eeSaurel32                 return 0;
752a65f56eeSaurel32             }
753a65f56eeSaurel32         }
754a65f56eeSaurel32     }
755a65f56eeSaurel32 
756a65f56eeSaurel32     return -1;
757a65f56eeSaurel32 }
758a65f56eeSaurel32 
7593df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7609e3cd456SFinn Thain                                size_t pkt_size)
761a65f56eeSaurel32 {
762cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
763a65f56eeSaurel32     int packet_type;
764a65f56eeSaurel32     uint32_t available, address;
765350e7d9aSFinn Thain     int width, rx_len, padded_len;
766a65f56eeSaurel32     uint32_t checksum;
7679e3cd456SFinn Thain     int size;
768a65f56eeSaurel32 
769a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
770a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
771a65f56eeSaurel32 
772c2279bd0SFinn Thain     if (s->last_rba_is_full) {
773c2279bd0SFinn Thain         return pkt_size;
774c2279bd0SFinn Thain     }
775c2279bd0SFinn Thain 
776350e7d9aSFinn Thain     rx_len = pkt_size + sizeof(checksum);
777350e7d9aSFinn Thain     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
778350e7d9aSFinn Thain         width = 2;
779350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 3) + 1;
780350e7d9aSFinn Thain     } else {
781350e7d9aSFinn Thain         width = 1;
782350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 1) + 1;
783350e7d9aSFinn Thain     }
784350e7d9aSFinn Thain 
785350e7d9aSFinn Thain     if (padded_len > dp8393x_rbwc(s) * 2) {
786c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_oversize(pkt_size);
787ada74315SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_RBAE;
788ada74315SFinn Thain         dp8393x_update_irq(s);
789c2279bd0SFinn Thain         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
790c2279bd0SFinn Thain         goto done;
791ada74315SFinn Thain     }
792ada74315SFinn Thain 
7939e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
794a65f56eeSaurel32     if (packet_type < 0) {
795c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_not_netcard();
7964f1c942bSMark McLoughlin         return -1;
797a65f56eeSaurel32     }
798a65f56eeSaurel32 
799a65f56eeSaurel32     /* Check for EOL */
80088f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
801a65f56eeSaurel32         /* Are we still in resource exhaustion? */
802a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
803581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
80419f70347SPeter Maydell         address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED,
80519f70347SPeter Maydell                            s->data, size);
8065b0c98fcSFinn Thain         s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
8075b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
808a65f56eeSaurel32             /* Still EOL ; stop reception */
8094f1c942bSMark McLoughlin             return -1;
810a65f56eeSaurel32         }
8115b0c98fcSFinn Thain         /* Link has been updated by host */
812d9fae131SFinn Thain 
813d9fae131SFinn Thain         /* Clear in_use */
814d9fae131SFinn Thain         size = sizeof(uint16_t) * width;
815d9fae131SFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
816d9fae131SFinn Thain         dp8393x_put(s, width, 0, 0);
817197ade0dSPhilippe Mathieu-Daudé         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
818197ade0dSPhilippe Mathieu-Daudé                             s->data, size);
819d9fae131SFinn Thain 
820d9fae131SFinn Thain         /* Move to next descriptor */
8215b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
822d9fae131SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
823a65f56eeSaurel32     }
824a65f56eeSaurel32 
825a65f56eeSaurel32     /* Save current position */
826a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
827a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
828a65f56eeSaurel32 
829a65f56eeSaurel32     /* Calculate the ethernet checksum */
830350e7d9aSFinn Thain     checksum = cpu_to_le32(crc32(0, buf, pkt_size));
831a65f56eeSaurel32 
832a65f56eeSaurel32     /* Put packet into RBA */
833c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_packet(dp8393x_crba(s));
834581f7b12SPeter Maydell     address = dp8393x_crba(s);
83519f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
836350e7d9aSFinn Thain                         buf, pkt_size);
837350e7d9aSFinn Thain     address += pkt_size;
838350e7d9aSFinn Thain 
839350e7d9aSFinn Thain     /* Put frame checksum into RBA */
84019f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
841350e7d9aSFinn Thain                         &checksum, sizeof(checksum));
842350e7d9aSFinn Thain     address += sizeof(checksum);
843350e7d9aSFinn Thain 
844350e7d9aSFinn Thain     /* Pad short packets to keep pointers aligned */
845350e7d9aSFinn Thain     if (rx_len < padded_len) {
846350e7d9aSFinn Thain         size = padded_len - rx_len;
847197ade0dSPhilippe Mathieu-Daudé         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
848197ade0dSPhilippe Mathieu-Daudé                             "\xFF\xFF\xFF", size);
849350e7d9aSFinn Thain         address += size;
850350e7d9aSFinn Thain     }
851350e7d9aSFinn Thain 
852a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
853a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
854581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
855350e7d9aSFinn Thain     available -= padded_len >> 1;
856a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
857a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
858a65f56eeSaurel32 
859a65f56eeSaurel32     /* Update status */
860581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
861a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
862a65f56eeSaurel32     }
863a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
864a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
865a65f56eeSaurel32     if (s->loopback_packet) {
866a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
867a65f56eeSaurel32         s->loopback_packet = 0;
868a65f56eeSaurel32     }
869a65f56eeSaurel32 
870a65f56eeSaurel32     /* Write status to memory */
871c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_write_status(dp8393x_crda(s));
872af9f0be3SLaurent Vivier     dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */
873af9f0be3SLaurent Vivier     dp8393x_put(s, width, 1, rx_len); /* byte count */
874af9f0be3SLaurent Vivier     dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
875af9f0be3SLaurent Vivier     dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
876af9f0be3SLaurent Vivier     dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */
877a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
87819f70347SPeter Maydell     address_space_write(&s->as, dp8393x_crda(s),
87919f70347SPeter Maydell                         MEMTXATTRS_UNSPECIFIED,
88019f70347SPeter Maydell                         s->data, size);
881a65f56eeSaurel32 
8825b0c98fcSFinn Thain     /* Check link field */
883a65f56eeSaurel32     size = sizeof(uint16_t) * width;
88419f70347SPeter Maydell     address_space_read(&s->as,
88519f70347SPeter Maydell                        dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
88619f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
887af9f0be3SLaurent Vivier     s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
88888f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
889a65f56eeSaurel32         /* EOL detected */
890a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
891a65f56eeSaurel32     } else {
89246ffee9aSFinn Thain         /* Clear in_use */
89346ffee9aSFinn Thain         size = sizeof(uint16_t) * width;
89446ffee9aSFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
89546ffee9aSFinn Thain         dp8393x_put(s, width, 0, 0);
89646ffee9aSFinn Thain         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
89746ffee9aSFinn Thain                             s->data, size);
8985b0c98fcSFinn Thain 
8995b0c98fcSFinn Thain         /* Move to next descriptor */
900a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
901a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
90280b60673SFinn Thain     }
90380b60673SFinn Thain 
904c2279bd0SFinn Thain     dp8393x_update_irq(s);
905c2279bd0SFinn Thain 
90680b60673SFinn Thain     s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) |
90780b60673SFinn Thain                          ((s->regs[SONIC_RSC] + 1) & 0x00ff);
908a65f56eeSaurel32 
909c2279bd0SFinn Thain done:
910c2279bd0SFinn Thain 
911a65f56eeSaurel32     if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
912c2279bd0SFinn Thain         if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
913c2279bd0SFinn Thain             /* Stop packet reception */
914c2279bd0SFinn Thain             s->last_rba_is_full = true;
915c2279bd0SFinn Thain         } else {
916c2279bd0SFinn Thain             /* Read next resource */
9173df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
918a65f56eeSaurel32         }
919c2279bd0SFinn Thain     }
9204f1c942bSMark McLoughlin 
9219e3cd456SFinn Thain     return pkt_size;
922a65f56eeSaurel32 }
923a65f56eeSaurel32 
924104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
925a65f56eeSaurel32 {
926104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
927bc72ad67SAlex Bligh     timer_del(s->watchdog);
928a65f56eeSaurel32 
929bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
930083e21bbSFinn Thain     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */
931a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
932a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
9331ca82a8dSMark Cave-Ayland     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD |
9341ca82a8dSMark Cave-Ayland                             SONIC_RCR_RNT);
935a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
936a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
937a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
938a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
939a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
940a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
941a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
942a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
943a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
944a65f56eeSaurel32 
945a65f56eeSaurel32     /* Network cable is connected */
946a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
947a65f56eeSaurel32 
948a65f56eeSaurel32     dp8393x_update_irq(s);
949a65f56eeSaurel32 }
950a65f56eeSaurel32 
95105f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
952f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
95305f41fe3SMark McLoughlin     .size = sizeof(NICState),
9543df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
9553df5de64SHervé Poussineau     .receive = dp8393x_receive,
95605f41fe3SMark McLoughlin };
95705f41fe3SMark McLoughlin 
958104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
959a65f56eeSaurel32 {
960104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
961104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
962a65f56eeSaurel32 
963104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
964104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
965104655a5SHervé Poussineau }
966a65f56eeSaurel32 
967104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
968104655a5SHervé Poussineau {
969104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
970a65f56eeSaurel32 
971104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
972104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
97367b38ddfSPhilippe Mathieu-Daudé                           "dp8393x-regs", SONIC_REG_COUNT << s->it_shift);
974104655a5SHervé Poussineau 
975104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
976104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
977104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
978104655a5SHervé Poussineau 
979bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
980a65f56eeSaurel32 }
981104655a5SHervé Poussineau 
9821670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9831670735dSHervé Poussineau     .name = "dp8393x",
984*8ac2ffb5SPhilippe Mathieu-Daudé     .version_id = 1,
985*8ac2ffb5SPhilippe Mathieu-Daudé     .minimum_version_id = 1,
9861670735dSHervé Poussineau     .fields = (VMStateField []) {
987*8ac2ffb5SPhilippe Mathieu-Daudé         VMSTATE_UINT16_2DARRAY(cam, dp8393xState, 16, 3),
98867b38ddfSPhilippe Mathieu-Daudé         VMSTATE_UINT16_ARRAY(regs, dp8393xState, SONIC_REG_COUNT),
9891670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9901670735dSHervé Poussineau     }
9911670735dSHervé Poussineau };
9921670735dSHervé Poussineau 
993104655a5SHervé Poussineau static Property dp8393x_properties[] = {
994104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
9953110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
9963110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
997104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
998be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
999104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
1000104655a5SHervé Poussineau };
1001104655a5SHervé Poussineau 
1002104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
1003104655a5SHervé Poussineau {
1004104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
1005104655a5SHervé Poussineau 
1006104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
1007104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
1008104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
10091670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
10104f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
1011104655a5SHervé Poussineau }
1012104655a5SHervé Poussineau 
1013104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
1014104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
1015104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
1016104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
1017104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
1018104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
1019104655a5SHervé Poussineau };
1020104655a5SHervé Poussineau 
1021104655a5SHervé Poussineau static void dp8393x_register_types(void)
1022104655a5SHervé Poussineau {
1023104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
1024104655a5SHervé Poussineau }
1025104655a5SHervé Poussineau 
1026104655a5SHervé Poussineau type_init(dp8393x_register_types)
1027