xref: /qemu/hw/net/dp8393x.c (revision 85e411d7ff7d62a084f318f3956d48a644632d6c)
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
88a65f56eeSaurel32 
89a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
90a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
91a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
92a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
93a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
94a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
95a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
96a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
97a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
98a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
99a65f56eeSaurel32 
100a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
101a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
102a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
103a65f56eeSaurel32 
104a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
105a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
106a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
107a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
108a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
109a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
110a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
111a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
112a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
113a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
114a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
115a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
116a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
117a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
118a65f56eeSaurel32 
119a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
120a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
121a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
122a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
123a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
124a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
125a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
126a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
127a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
128a65f56eeSaurel32 
129ada74315SFinn Thain #define SONIC_ISR_RBAE   0x0010
130a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
131a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
132a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
133a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
134a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
135a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
136a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
137a65f56eeSaurel32 
13888f632fbSFinn Thain #define SONIC_DESC_EOL   0x0001
13988f632fbSFinn Thain #define SONIC_DESC_ADDR  0xFFFE
14088f632fbSFinn Thain 
141104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
1428063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(dp8393xState, DP8393X)
143104655a5SHervé Poussineau 
144db1015e9SEduardo Habkost struct dp8393xState {
145104655a5SHervé Poussineau     SysBusDevice parent_obj;
146104655a5SHervé Poussineau 
147a65f56eeSaurel32     /* Hardware */
148104655a5SHervé Poussineau     uint8_t it_shift;
149be920841SLaurent Vivier     bool big_endian;
150c2279bd0SFinn Thain     bool last_rba_is_full;
151a65f56eeSaurel32     qemu_irq irq;
152a65f56eeSaurel32     int irq_level;
153a65f56eeSaurel32     QEMUTimer *watchdog;
154a65f56eeSaurel32     int64_t wt_last_update;
15505f41fe3SMark McLoughlin     NICConf conf;
15605f41fe3SMark McLoughlin     NICState *nic;
157024e5bb6SAvi Kivity     MemoryRegion mmio;
158a65f56eeSaurel32 
159a65f56eeSaurel32     /* Registers */
160a65f56eeSaurel32     uint8_t cam[16][6];
161a65f56eeSaurel32     uint16_t regs[0x40];
162a65f56eeSaurel32 
163a65f56eeSaurel32     /* Temporaries */
164a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
165af9f0be3SLaurent Vivier     uint16_t data[12];
166a65f56eeSaurel32     int loopback_packet;
167a65f56eeSaurel32 
168a65f56eeSaurel32     /* Memory access */
1693110ce81SMarc-André Lureau     MemoryRegion *dma_mr;
170dd820513SHervé Poussineau     AddressSpace as;
171db1015e9SEduardo Habkost };
172a65f56eeSaurel32 
1731ca82a8dSMark Cave-Ayland /*
1741ca82a8dSMark Cave-Ayland  * Accessor functions for values which are formed by
175581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
176581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
177581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
178581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
179581f7b12SPeter Maydell  */
180581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
181581f7b12SPeter Maydell {
182581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
183581f7b12SPeter Maydell }
184581f7b12SPeter Maydell 
185581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
186581f7b12SPeter Maydell {
187581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
188581f7b12SPeter Maydell }
189581f7b12SPeter Maydell 
190581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
191581f7b12SPeter Maydell {
19288f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
19388f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
194581f7b12SPeter Maydell }
195581f7b12SPeter Maydell 
196581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
197581f7b12SPeter Maydell {
198581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
199581f7b12SPeter Maydell }
200581f7b12SPeter Maydell 
201581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
202581f7b12SPeter Maydell {
203581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
204581f7b12SPeter Maydell }
205581f7b12SPeter Maydell 
206581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
207581f7b12SPeter Maydell {
208581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
209581f7b12SPeter Maydell }
210581f7b12SPeter Maydell 
211581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
212581f7b12SPeter Maydell {
21388f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
21488f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
215581f7b12SPeter Maydell }
216581f7b12SPeter Maydell 
217581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
218581f7b12SPeter Maydell {
219581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
220581f7b12SPeter Maydell }
221581f7b12SPeter Maydell 
222af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset)
223be920841SLaurent Vivier {
224be920841SLaurent Vivier     uint16_t val;
225be920841SLaurent Vivier 
226be920841SLaurent Vivier     if (s->big_endian) {
227af9f0be3SLaurent Vivier         val = be16_to_cpu(s->data[offset * width + width - 1]);
228be920841SLaurent Vivier     } else {
229af9f0be3SLaurent Vivier         val = le16_to_cpu(s->data[offset * width]);
230be920841SLaurent Vivier     }
231be920841SLaurent Vivier     return val;
232be920841SLaurent Vivier }
233be920841SLaurent Vivier 
234af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset,
235be920841SLaurent Vivier                         uint16_t val)
236be920841SLaurent Vivier {
237be920841SLaurent Vivier     if (s->big_endian) {
2383fe9a838SFinn Thain         if (width == 2) {
2393fe9a838SFinn Thain             s->data[offset * 2] = 0;
2403fe9a838SFinn Thain             s->data[offset * 2 + 1] = cpu_to_be16(val);
241be920841SLaurent Vivier         } else {
2423fe9a838SFinn Thain             s->data[offset] = cpu_to_be16(val);
2433fe9a838SFinn Thain         }
2443fe9a838SFinn Thain     } else {
2453fe9a838SFinn Thain         if (width == 2) {
2463fe9a838SFinn Thain             s->data[offset * 2] = cpu_to_le16(val);
2473fe9a838SFinn Thain             s->data[offset * 2 + 1] = 0;
2483fe9a838SFinn Thain         } else {
2493fe9a838SFinn Thain             s->data[offset] = cpu_to_le16(val);
2503fe9a838SFinn Thain         }
251be920841SLaurent Vivier     }
252be920841SLaurent Vivier }
253be920841SLaurent Vivier 
254a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
255a65f56eeSaurel32 {
256a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
257a65f56eeSaurel32 
258a65f56eeSaurel32     if (level != s->irq_level) {
259a65f56eeSaurel32         s->irq_level = level;
260a65f56eeSaurel32         if (level) {
261c0af04a4SMark Cave-Ayland             trace_dp8393x_raise_irq(s->regs[SONIC_ISR]);
262a65f56eeSaurel32         } else {
263c0af04a4SMark Cave-Ayland             trace_dp8393x_lower_irq();
264a65f56eeSaurel32         }
265a65f56eeSaurel32     }
266a65f56eeSaurel32 
267a65f56eeSaurel32     qemu_set_irq(s->irq, level);
268a65f56eeSaurel32 }
269a65f56eeSaurel32 
2703df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
271a65f56eeSaurel32 {
272a65f56eeSaurel32     int width, size;
273*85e411d7SMark Cave-Ayland     uint16_t index;
274a65f56eeSaurel32 
275a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
276a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
277a65f56eeSaurel32 
278a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
279a65f56eeSaurel32         /* Fill current entry */
28019f70347SPeter Maydell         address_space_read(&s->as, dp8393x_cdp(s),
28119f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
282*85e411d7SMark Cave-Ayland         index = dp8393x_get(s, width, 0) & 0xf;
283af9f0be3SLaurent Vivier         s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff;
284af9f0be3SLaurent Vivier         s->cam[index][1] = dp8393x_get(s, width, 1) >> 8;
285af9f0be3SLaurent Vivier         s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff;
286af9f0be3SLaurent Vivier         s->cam[index][3] = dp8393x_get(s, width, 2) >> 8;
287af9f0be3SLaurent Vivier         s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff;
288af9f0be3SLaurent Vivier         s->cam[index][5] = dp8393x_get(s, width, 3) >> 8;
289c0af04a4SMark Cave-Ayland         trace_dp8393x_load_cam(index, s->cam[index][0], s->cam[index][1],
290c0af04a4SMark Cave-Ayland                                s->cam[index][2], s->cam[index][3],
291c0af04a4SMark Cave-Ayland                                s->cam[index][4], s->cam[index][5]);
292a65f56eeSaurel32         /* Move to next entry */
293a65f56eeSaurel32         s->regs[SONIC_CDC]--;
294a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
295a65f56eeSaurel32     }
296a65f56eeSaurel32 
297a65f56eeSaurel32     /* Read CAM enable */
29819f70347SPeter Maydell     address_space_read(&s->as, dp8393x_cdp(s),
29919f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
300af9f0be3SLaurent Vivier     s->regs[SONIC_CE] = dp8393x_get(s, width, 0);
301c0af04a4SMark Cave-Ayland     trace_dp8393x_load_cam_done(s->regs[SONIC_CE]);
302a65f56eeSaurel32 
303a65f56eeSaurel32     /* Done */
304a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
305a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
306a65f56eeSaurel32     dp8393x_update_irq(s);
307a65f56eeSaurel32 }
308a65f56eeSaurel32 
3093df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
310a65f56eeSaurel32 {
311a65f56eeSaurel32     int width, size;
312a65f56eeSaurel32 
313a65f56eeSaurel32     /* Read memory */
314a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
315a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
31619f70347SPeter Maydell     address_space_read(&s->as, dp8393x_rrp(s),
31719f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
318a65f56eeSaurel32 
319a65f56eeSaurel32     /* Update SONIC registers */
320af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0);
321af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1);
322af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2);
323af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3);
324c0af04a4SMark Cave-Ayland     trace_dp8393x_read_rra_regs(s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
325a65f56eeSaurel32                                 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
326a65f56eeSaurel32 
327a65f56eeSaurel32     /* Go to next entry */
328a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
329a65f56eeSaurel32 
330a65f56eeSaurel32     /* Handle wrap */
331a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
332a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
333a65f56eeSaurel32     }
334a65f56eeSaurel32 
335c2279bd0SFinn Thain     /* Warn the host if CRBA now has the last available resource */
3361ca82a8dSMark Cave-Ayland     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
337a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
338a65f56eeSaurel32         dp8393x_update_irq(s);
339a65f56eeSaurel32     }
340c2279bd0SFinn Thain 
341c2279bd0SFinn Thain     /* Allow packet reception */
342c2279bd0SFinn Thain     s->last_rba_is_full = false;
343a65f56eeSaurel32 }
344a65f56eeSaurel32 
3453df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
346a65f56eeSaurel32 {
347bc72ad67SAlex Bligh     timer_del(s->watchdog);
348a65f56eeSaurel32 
3491ca82a8dSMark Cave-Ayland     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP |
3501ca82a8dSMark Cave-Ayland                            SONIC_CR_HTX);
351a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
352a65f56eeSaurel32 }
353a65f56eeSaurel32 
3543df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
355a65f56eeSaurel32 {
356a65f56eeSaurel32     uint32_t ticks;
357a65f56eeSaurel32     int64_t delay;
358a65f56eeSaurel32 
359a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
360bc72ad67SAlex Bligh         timer_del(s->watchdog);
361a65f56eeSaurel32         return;
362a65f56eeSaurel32     }
363a65f56eeSaurel32 
364581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
365bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
36673bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
367bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
368a65f56eeSaurel32 }
369a65f56eeSaurel32 
3703df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
371a65f56eeSaurel32 {
372a65f56eeSaurel32     int64_t elapsed;
373a65f56eeSaurel32     uint32_t val;
374a65f56eeSaurel32 
375a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
376bc72ad67SAlex Bligh         timer_del(s->watchdog);
377a65f56eeSaurel32         return;
378a65f56eeSaurel32     }
379a65f56eeSaurel32 
380bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
381581f7b12SPeter Maydell     val = dp8393x_wt(s);
382a65f56eeSaurel32     val -= elapsed / 5000000;
383a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
384a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3853df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
386a65f56eeSaurel32 
387a65f56eeSaurel32 }
388a65f56eeSaurel32 
3893df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
390a65f56eeSaurel32 {
391a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3923df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
393a65f56eeSaurel32 }
394a65f56eeSaurel32 
3953df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
396a65f56eeSaurel32 {
397a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3983df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
399a65f56eeSaurel32 }
400a65f56eeSaurel32 
401b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc);
4024594f93aSFam Zheng 
4033df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
404a65f56eeSaurel32 {
405a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
4064594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
4074594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
4084594f93aSFam Zheng     }
409a65f56eeSaurel32 }
410a65f56eeSaurel32 
4113df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
412a65f56eeSaurel32 {
413a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
414a65f56eeSaurel32 }
415a65f56eeSaurel32 
4163df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
417a65f56eeSaurel32 {
418b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
419a65f56eeSaurel32     int width, size;
420a65f56eeSaurel32     int tx_len, len;
421a65f56eeSaurel32     uint16_t i;
422a65f56eeSaurel32 
423a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
424a65f56eeSaurel32 
425a65f56eeSaurel32     while (1) {
426a65f56eeSaurel32         /* Read memory */
427a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
428a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
429c0af04a4SMark Cave-Ayland         trace_dp8393x_transmit_packet(dp8393x_ttda(s));
43019f70347SPeter Maydell         address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width,
43119f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
432a65f56eeSaurel32         tx_len = 0;
433a65f56eeSaurel32 
434a65f56eeSaurel32         /* Update registers */
435af9f0be3SLaurent Vivier         s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000;
436af9f0be3SLaurent Vivier         s->regs[SONIC_TPS] = dp8393x_get(s, width, 1);
437af9f0be3SLaurent Vivier         s->regs[SONIC_TFC] = dp8393x_get(s, width, 2);
438af9f0be3SLaurent Vivier         s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3);
439af9f0be3SLaurent Vivier         s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4);
440af9f0be3SLaurent Vivier         s->regs[SONIC_TFS] = dp8393x_get(s, width, 5);
441a65f56eeSaurel32 
442a65f56eeSaurel32         /* Handle programmable interrupt */
443a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
444a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
445a65f56eeSaurel32         } else {
446a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
447a65f56eeSaurel32         }
448a65f56eeSaurel32 
449a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
450a65f56eeSaurel32             /* Append fragment */
451a65f56eeSaurel32             len = s->regs[SONIC_TFS];
452a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
453a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
454a65f56eeSaurel32             }
45519f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
45619f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
457a65f56eeSaurel32             tx_len += len;
458a65f56eeSaurel32 
459a65f56eeSaurel32             i++;
460a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
461a65f56eeSaurel32                 /* Read next fragment details */
462a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
46319f70347SPeter Maydell                 address_space_read(&s->as,
46419f70347SPeter Maydell                                    dp8393x_ttda(s)
46519f70347SPeter Maydell                                    + sizeof(uint16_t) * width * (4 + 3 * i),
46619f70347SPeter Maydell                                    MEMTXATTRS_UNSPECIFIED, s->data,
46719f70347SPeter Maydell                                    size);
468af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0);
469af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1);
470af9f0be3SLaurent Vivier                 s->regs[SONIC_TFS] = dp8393x_get(s, width, 2);
471a65f56eeSaurel32             }
472a65f56eeSaurel32         }
473a65f56eeSaurel32 
474a65f56eeSaurel32         /* Handle Ethernet checksum */
475a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
4761ca82a8dSMark Cave-Ayland             /*
4771ca82a8dSMark Cave-Ayland              * Don't append FCS there, to look like slirp packets
4781ca82a8dSMark Cave-Ayland              * which don't have one
4791ca82a8dSMark Cave-Ayland              */
480a65f56eeSaurel32         } else {
481a65f56eeSaurel32             /* Remove existing FCS */
482a65f56eeSaurel32             tx_len -= 4;
483915976bdSMauro Matteo Cascella             if (tx_len < 0) {
484c0af04a4SMark Cave-Ayland                 trace_dp8393x_transmit_txlen_error(tx_len);
485915976bdSMauro Matteo Cascella                 break;
486915976bdSMauro Matteo Cascella             }
487a65f56eeSaurel32         }
488a65f56eeSaurel32 
489a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
490a65f56eeSaurel32             /* Loopback */
491a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
492b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
493a65f56eeSaurel32                 s->loopback_packet = 1;
494331d2ac9SJason Wang                 qemu_receive_packet(nc, s->tx_buffer, tx_len);
495a65f56eeSaurel32             }
496a65f56eeSaurel32         } else {
497a65f56eeSaurel32             /* Transmit packet */
498b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
499a65f56eeSaurel32         }
500a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
501a65f56eeSaurel32 
502a65f56eeSaurel32         /* Write status */
503af9f0be3SLaurent Vivier         dp8393x_put(s, width, 0,
504be920841SLaurent Vivier                     s->regs[SONIC_TCR] & 0x0fff); /* status */
505a65f56eeSaurel32         size = sizeof(uint16_t) * width;
50619f70347SPeter Maydell         address_space_write(&s->as, dp8393x_ttda(s),
50719f70347SPeter Maydell                             MEMTXATTRS_UNSPECIFIED, s->data, size);
508a65f56eeSaurel32 
509a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
510a65f56eeSaurel32             /* Read footer of packet */
511a65f56eeSaurel32             size = sizeof(uint16_t) * width;
51219f70347SPeter Maydell             address_space_read(&s->as,
51319f70347SPeter Maydell                                dp8393x_ttda(s)
51419f70347SPeter Maydell                                + sizeof(uint16_t) * width
51519f70347SPeter Maydell                                  * (4 + 3 * s->regs[SONIC_TFC]),
51619f70347SPeter Maydell                                MEMTXATTRS_UNSPECIFIED, s->data,
51719f70347SPeter Maydell                                size);
518a0cf4297SFinn Thain             s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0);
519a0cf4297SFinn Thain             if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) {
520a65f56eeSaurel32                 /* EOL detected */
521a65f56eeSaurel32                 break;
522a65f56eeSaurel32             }
523a65f56eeSaurel32         }
524a65f56eeSaurel32     }
525a65f56eeSaurel32 
526a65f56eeSaurel32     /* Done */
527a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
528a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
529a65f56eeSaurel32     dp8393x_update_irq(s);
530a65f56eeSaurel32 }
531a65f56eeSaurel32 
5323df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
533a65f56eeSaurel32 {
534a65f56eeSaurel32     /* Nothing to do */
535a65f56eeSaurel32 }
536a65f56eeSaurel32 
5373df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
538a65f56eeSaurel32 {
539a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
540a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
541a65f56eeSaurel32         return;
542a65f56eeSaurel32     }
543a65f56eeSaurel32 
544a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
545a65f56eeSaurel32 
5461ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_HTX) {
5473df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
5481ca82a8dSMark Cave-Ayland     }
5491ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_TXP) {
5503df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
5511ca82a8dSMark Cave-Ayland     }
5521ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXDIS) {
5533df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
5541ca82a8dSMark Cave-Ayland     }
5551ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXEN) {
5563df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
5571ca82a8dSMark Cave-Ayland     }
5581ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_STP) {
5593df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
5601ca82a8dSMark Cave-Ayland     }
5611ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_ST) {
5623df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
5631ca82a8dSMark Cave-Ayland     }
5641ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RST) {
5653df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
5661ca82a8dSMark Cave-Ayland     }
567a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5683df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
569a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
570a3cce282SFinn Thain     }
5711ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_LCAM) {
5723df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
573a65f56eeSaurel32     }
5741ca82a8dSMark Cave-Ayland }
575a65f56eeSaurel32 
57684689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
577a65f56eeSaurel32 {
57884689cbbSHervé Poussineau     dp8393xState *s = opaque;
57984689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
580a65f56eeSaurel32     uint16_t val = 0;
581a65f56eeSaurel32 
582a65f56eeSaurel32     switch (reg) {
583a65f56eeSaurel32     /* Update data before reading it */
584a65f56eeSaurel32     case SONIC_WT0:
585a65f56eeSaurel32     case SONIC_WT1:
5863df5de64SHervé Poussineau         dp8393x_update_wt_regs(s);
587a65f56eeSaurel32         val = s->regs[reg];
588a65f56eeSaurel32         break;
589a65f56eeSaurel32     /* Accept read to some registers only when in reset mode */
590a65f56eeSaurel32     case SONIC_CAP2:
591a65f56eeSaurel32     case SONIC_CAP1:
592a65f56eeSaurel32     case SONIC_CAP0:
593a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
594a65f56eeSaurel32             val = s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg) + 1] << 8;
595a65f56eeSaurel32             val |= s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg)];
596a65f56eeSaurel32         }
597a65f56eeSaurel32         break;
5981ca82a8dSMark Cave-Ayland     /* All other registers have no special contraints */
599a65f56eeSaurel32     default:
600a65f56eeSaurel32         val = s->regs[reg];
601a65f56eeSaurel32     }
602a65f56eeSaurel32 
603c0af04a4SMark Cave-Ayland     trace_dp8393x_read(reg, reg_names[reg], val, size);
604a65f56eeSaurel32 
6053fe9a838SFinn Thain     return s->big_endian ? val << 16 : val;
606a65f56eeSaurel32 }
607a65f56eeSaurel32 
60884689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
60984689cbbSHervé Poussineau                           unsigned int size)
610a65f56eeSaurel32 {
61184689cbbSHervé Poussineau     dp8393xState *s = opaque;
61284689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
6133fe9a838SFinn Thain     uint32_t val = s->big_endian ? data >> 16 : data;
61484689cbbSHervé Poussineau 
615c0af04a4SMark Cave-Ayland     trace_dp8393x_write(reg, reg_names[reg], val, size);
616a65f56eeSaurel32 
617a65f56eeSaurel32     switch (reg) {
618a65f56eeSaurel32     /* Command register */
619a65f56eeSaurel32     case SONIC_CR:
6203fe9a838SFinn Thain         dp8393x_do_command(s, val);
621a65f56eeSaurel32         break;
622a65f56eeSaurel32     /* Prevent write to read-only registers */
623a65f56eeSaurel32     case SONIC_CAP2:
624a65f56eeSaurel32     case SONIC_CAP1:
625a65f56eeSaurel32     case SONIC_CAP0:
626a65f56eeSaurel32     case SONIC_SR:
627a65f56eeSaurel32     case SONIC_MDT:
628c0af04a4SMark Cave-Ayland         trace_dp8393x_write_invalid(reg);
629a65f56eeSaurel32         break;
630a65f56eeSaurel32     /* Accept write to some registers only when in reset mode */
631a65f56eeSaurel32     case SONIC_DCR:
632a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6333fe9a838SFinn Thain             s->regs[reg] = val & 0xbfff;
634a65f56eeSaurel32         } else {
635c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR");
636a65f56eeSaurel32         }
637a65f56eeSaurel32         break;
638a65f56eeSaurel32     case SONIC_DCR2:
639a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6403fe9a838SFinn Thain             s->regs[reg] = val & 0xf017;
641a65f56eeSaurel32         } else {
642c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR2");
643a65f56eeSaurel32         }
644a65f56eeSaurel32         break;
645a65f56eeSaurel32     /* 12 lower bytes are Read Only */
646a65f56eeSaurel32     case SONIC_TCR:
6473fe9a838SFinn Thain         s->regs[reg] = val & 0xf000;
648a65f56eeSaurel32         break;
649a65f56eeSaurel32     /* 9 lower bytes are Read Only */
650a65f56eeSaurel32     case SONIC_RCR:
6513fe9a838SFinn Thain         s->regs[reg] = val & 0xffe0;
652a65f56eeSaurel32         break;
653a65f56eeSaurel32     /* Ignore most significant bit */
654a65f56eeSaurel32     case SONIC_IMR:
6553fe9a838SFinn Thain         s->regs[reg] = val & 0x7fff;
656a65f56eeSaurel32         dp8393x_update_irq(s);
657a65f56eeSaurel32         break;
658a65f56eeSaurel32     /* Clear bits by writing 1 to them */
659a65f56eeSaurel32     case SONIC_ISR:
6603fe9a838SFinn Thain         val &= s->regs[reg];
6613fe9a838SFinn Thain         s->regs[reg] &= ~val;
6623fe9a838SFinn Thain         if (val & SONIC_ISR_RBE) {
6633df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
664a65f56eeSaurel32         }
665a65f56eeSaurel32         dp8393x_update_irq(s);
666a65f56eeSaurel32         break;
667ea227027SFinn Thain     /* The guest is required to store aligned pointers here */
668a65f56eeSaurel32     case SONIC_RSA:
669a65f56eeSaurel32     case SONIC_REA:
670a65f56eeSaurel32     case SONIC_RRP:
671a65f56eeSaurel32     case SONIC_RWP:
672ea227027SFinn Thain         if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
673ea227027SFinn Thain             s->regs[reg] = val & 0xfffc;
674ea227027SFinn Thain         } else {
6753fe9a838SFinn Thain             s->regs[reg] = val & 0xfffe;
676ea227027SFinn Thain         }
677a65f56eeSaurel32         break;
678a65f56eeSaurel32     /* Invert written value for some registers */
679a65f56eeSaurel32     case SONIC_CRCT:
680a65f56eeSaurel32     case SONIC_FAET:
681a65f56eeSaurel32     case SONIC_MPT:
6823fe9a838SFinn Thain         s->regs[reg] = val ^ 0xffff;
683a65f56eeSaurel32         break;
684a65f56eeSaurel32     /* All other registers have no special contrainst */
685a65f56eeSaurel32     default:
6863fe9a838SFinn Thain         s->regs[reg] = val;
687a65f56eeSaurel32     }
688a65f56eeSaurel32 
689a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6903df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
691a65f56eeSaurel32     }
692a65f56eeSaurel32 }
693a65f56eeSaurel32 
69484689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
69584689cbbSHervé Poussineau     .read = dp8393x_read,
69684689cbbSHervé Poussineau     .write = dp8393x_write,
6973fe9a838SFinn Thain     .impl.min_access_size = 4,
6983fe9a838SFinn Thain     .impl.max_access_size = 4,
69984689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
70084689cbbSHervé Poussineau };
70184689cbbSHervé Poussineau 
702a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
703a65f56eeSaurel32 {
704a65f56eeSaurel32     dp8393xState *s = opaque;
705a65f56eeSaurel32 
706a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
707a65f56eeSaurel32         return;
708a65f56eeSaurel32     }
709a65f56eeSaurel32 
710a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
711a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
7123df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
713a65f56eeSaurel32 
714a65f56eeSaurel32     /* Signal underflow */
715a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
716a65f56eeSaurel32     dp8393x_update_irq(s);
717a65f56eeSaurel32 }
718a65f56eeSaurel32 
719b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc)
720a65f56eeSaurel32 {
721cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
722a65f56eeSaurel32 
723b8c4b67eSPhilippe Mathieu-Daudé     return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN);
724a65f56eeSaurel32 }
725a65f56eeSaurel32 
7263df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
7273df5de64SHervé Poussineau                                   int size)
728a65f56eeSaurel32 {
729a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
730a65f56eeSaurel32     int i;
731a65f56eeSaurel32 
732a65f56eeSaurel32     /* Check promiscuous mode */
733a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
734a65f56eeSaurel32         return 0;
735a65f56eeSaurel32     }
736a65f56eeSaurel32 
737a65f56eeSaurel32     /* Check multicast packets */
738a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
739a65f56eeSaurel32         return SONIC_RCR_MC;
740a65f56eeSaurel32     }
741a65f56eeSaurel32 
742a65f56eeSaurel32     /* Check broadcast */
7431ca82a8dSMark Cave-Ayland     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) &&
7441ca82a8dSMark Cave-Ayland          !memcmp(buf, bcast, sizeof(bcast))) {
745a65f56eeSaurel32         return SONIC_RCR_BC;
746a65f56eeSaurel32     }
747a65f56eeSaurel32 
748a65f56eeSaurel32     /* Check CAM */
749a65f56eeSaurel32     for (i = 0; i < 16; i++) {
750a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
751a65f56eeSaurel32             /* Entry enabled */
752a65f56eeSaurel32             if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
753a65f56eeSaurel32                 return 0;
754a65f56eeSaurel32             }
755a65f56eeSaurel32         }
756a65f56eeSaurel32     }
757a65f56eeSaurel32 
758a65f56eeSaurel32     return -1;
759a65f56eeSaurel32 }
760a65f56eeSaurel32 
7613df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7629e3cd456SFinn Thain                                size_t pkt_size)
763a65f56eeSaurel32 {
764cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
765a65f56eeSaurel32     int packet_type;
766a65f56eeSaurel32     uint32_t available, address;
767350e7d9aSFinn Thain     int width, rx_len, padded_len;
768a65f56eeSaurel32     uint32_t checksum;
7699e3cd456SFinn Thain     int size;
770a65f56eeSaurel32 
771a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
772a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
773a65f56eeSaurel32 
774c2279bd0SFinn Thain     if (s->last_rba_is_full) {
775c2279bd0SFinn Thain         return pkt_size;
776c2279bd0SFinn Thain     }
777c2279bd0SFinn Thain 
778350e7d9aSFinn Thain     rx_len = pkt_size + sizeof(checksum);
779350e7d9aSFinn Thain     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
780350e7d9aSFinn Thain         width = 2;
781350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 3) + 1;
782350e7d9aSFinn Thain     } else {
783350e7d9aSFinn Thain         width = 1;
784350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 1) + 1;
785350e7d9aSFinn Thain     }
786350e7d9aSFinn Thain 
787350e7d9aSFinn Thain     if (padded_len > dp8393x_rbwc(s) * 2) {
788c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_oversize(pkt_size);
789ada74315SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_RBAE;
790ada74315SFinn Thain         dp8393x_update_irq(s);
791c2279bd0SFinn Thain         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
792c2279bd0SFinn Thain         goto done;
793ada74315SFinn Thain     }
794ada74315SFinn Thain 
7959e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
796a65f56eeSaurel32     if (packet_type < 0) {
797c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_not_netcard();
7984f1c942bSMark McLoughlin         return -1;
799a65f56eeSaurel32     }
800a65f56eeSaurel32 
801a65f56eeSaurel32     /* Check for EOL */
80288f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
803a65f56eeSaurel32         /* Are we still in resource exhaustion? */
804a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
805581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
80619f70347SPeter Maydell         address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED,
80719f70347SPeter Maydell                            s->data, size);
8085b0c98fcSFinn Thain         s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
8095b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
810a65f56eeSaurel32             /* Still EOL ; stop reception */
8114f1c942bSMark McLoughlin             return -1;
812a65f56eeSaurel32         }
8135b0c98fcSFinn Thain         /* Link has been updated by host */
814d9fae131SFinn Thain 
815d9fae131SFinn Thain         /* Clear in_use */
816d9fae131SFinn Thain         size = sizeof(uint16_t) * width;
817d9fae131SFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
818d9fae131SFinn Thain         dp8393x_put(s, width, 0, 0);
819d9fae131SFinn Thain         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
820d9fae131SFinn Thain                          (uint8_t *)s->data, size, 1);
821d9fae131SFinn Thain 
822d9fae131SFinn Thain         /* Move to next descriptor */
8235b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
824d9fae131SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
825a65f56eeSaurel32     }
826a65f56eeSaurel32 
827a65f56eeSaurel32     /* Save current position */
828a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
829a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
830a65f56eeSaurel32 
831a65f56eeSaurel32     /* Calculate the ethernet checksum */
832350e7d9aSFinn Thain     checksum = cpu_to_le32(crc32(0, buf, pkt_size));
833a65f56eeSaurel32 
834a65f56eeSaurel32     /* Put packet into RBA */
835c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_packet(dp8393x_crba(s));
836581f7b12SPeter Maydell     address = dp8393x_crba(s);
83719f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
838350e7d9aSFinn Thain                         buf, pkt_size);
839350e7d9aSFinn Thain     address += pkt_size;
840350e7d9aSFinn Thain 
841350e7d9aSFinn Thain     /* Put frame checksum into RBA */
84219f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
843350e7d9aSFinn Thain                         &checksum, sizeof(checksum));
844350e7d9aSFinn Thain     address += sizeof(checksum);
845350e7d9aSFinn Thain 
846350e7d9aSFinn Thain     /* Pad short packets to keep pointers aligned */
847350e7d9aSFinn Thain     if (rx_len < padded_len) {
848350e7d9aSFinn Thain         size = padded_len - rx_len;
849350e7d9aSFinn Thain         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
850350e7d9aSFinn Thain             (uint8_t *)"\xFF\xFF\xFF", size, 1);
851350e7d9aSFinn Thain         address += size;
852350e7d9aSFinn Thain     }
853350e7d9aSFinn Thain 
854a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
855a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
856581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
857350e7d9aSFinn Thain     available -= padded_len >> 1;
858a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
859a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
860a65f56eeSaurel32 
861a65f56eeSaurel32     /* Update status */
862581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
863a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
864a65f56eeSaurel32     }
865a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
866a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
867a65f56eeSaurel32     if (s->loopback_packet) {
868a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
869a65f56eeSaurel32         s->loopback_packet = 0;
870a65f56eeSaurel32     }
871a65f56eeSaurel32 
872a65f56eeSaurel32     /* Write status to memory */
873c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_write_status(dp8393x_crda(s));
874af9f0be3SLaurent Vivier     dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */
875af9f0be3SLaurent Vivier     dp8393x_put(s, width, 1, rx_len); /* byte count */
876af9f0be3SLaurent Vivier     dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
877af9f0be3SLaurent Vivier     dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
878af9f0be3SLaurent Vivier     dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */
879a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
88019f70347SPeter Maydell     address_space_write(&s->as, dp8393x_crda(s),
88119f70347SPeter Maydell                         MEMTXATTRS_UNSPECIFIED,
88219f70347SPeter Maydell                         s->data, size);
883a65f56eeSaurel32 
8845b0c98fcSFinn Thain     /* Check link field */
885a65f56eeSaurel32     size = sizeof(uint16_t) * width;
88619f70347SPeter Maydell     address_space_read(&s->as,
88719f70347SPeter Maydell                        dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
88819f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
889af9f0be3SLaurent Vivier     s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
89088f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
891a65f56eeSaurel32         /* EOL detected */
892a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
893a65f56eeSaurel32     } else {
89446ffee9aSFinn Thain         /* Clear in_use */
89546ffee9aSFinn Thain         size = sizeof(uint16_t) * width;
89646ffee9aSFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
89746ffee9aSFinn Thain         dp8393x_put(s, width, 0, 0);
89846ffee9aSFinn Thain         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
89946ffee9aSFinn Thain                             s->data, size);
9005b0c98fcSFinn Thain 
9015b0c98fcSFinn Thain         /* Move to next descriptor */
902a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
903a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
90480b60673SFinn Thain     }
90580b60673SFinn Thain 
906c2279bd0SFinn Thain     dp8393x_update_irq(s);
907c2279bd0SFinn Thain 
90880b60673SFinn Thain     s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) |
90980b60673SFinn Thain                          ((s->regs[SONIC_RSC] + 1) & 0x00ff);
910a65f56eeSaurel32 
911c2279bd0SFinn Thain done:
912c2279bd0SFinn Thain 
913a65f56eeSaurel32     if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
914c2279bd0SFinn Thain         if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
915c2279bd0SFinn Thain             /* Stop packet reception */
916c2279bd0SFinn Thain             s->last_rba_is_full = true;
917c2279bd0SFinn Thain         } else {
918c2279bd0SFinn Thain             /* Read next resource */
9193df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
920a65f56eeSaurel32         }
921c2279bd0SFinn Thain     }
9224f1c942bSMark McLoughlin 
9239e3cd456SFinn Thain     return pkt_size;
924a65f56eeSaurel32 }
925a65f56eeSaurel32 
926104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
927a65f56eeSaurel32 {
928104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
929bc72ad67SAlex Bligh     timer_del(s->watchdog);
930a65f56eeSaurel32 
931bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
932083e21bbSFinn Thain     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */
933a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
934a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
9351ca82a8dSMark Cave-Ayland     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD |
9361ca82a8dSMark Cave-Ayland                             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);
966104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
967104655a5SHervé Poussineau }
968a65f56eeSaurel32 
969104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
970104655a5SHervé Poussineau {
971104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
972a65f56eeSaurel32 
973104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
974104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
975104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
976104655a5SHervé Poussineau 
977104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
978104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
979104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
980104655a5SHervé Poussineau 
981bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
982a65f56eeSaurel32 }
983104655a5SHervé Poussineau 
9841670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9851670735dSHervé Poussineau     .name = "dp8393x",
9861670735dSHervé Poussineau     .version_id = 0,
9871670735dSHervé Poussineau     .minimum_version_id = 0,
9881670735dSHervé Poussineau     .fields = (VMStateField []) {
9891670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
9901670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
9911670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9921670735dSHervé Poussineau     }
9931670735dSHervé Poussineau };
9941670735dSHervé Poussineau 
995104655a5SHervé Poussineau static Property dp8393x_properties[] = {
996104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
9973110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
9983110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
999104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
1000be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
1001104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
1002104655a5SHervé Poussineau };
1003104655a5SHervé Poussineau 
1004104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
1005104655a5SHervé Poussineau {
1006104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
1007104655a5SHervé Poussineau 
1008104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
1009104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
1010104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
10111670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
10124f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
1013104655a5SHervé Poussineau }
1014104655a5SHervé Poussineau 
1015104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
1016104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
1017104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
1018104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
1019104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
1020104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
1021104655a5SHervé Poussineau };
1022104655a5SHervé Poussineau 
1023104655a5SHervé Poussineau static void dp8393x_register_types(void)
1024104655a5SHervé Poussineau {
1025104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
1026104655a5SHervé Poussineau }
1027104655a5SHervé Poussineau 
1028104655a5SHervé Poussineau type_init(dp8393x_register_types)
1029