xref: /qemu/hw/net/dp8393x.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
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"
232db48d03SMark Cave-Ayland #include "hw/net/dp8393x.h"
24104655a5SHervé Poussineau #include "hw/sysbus.h"
25d6454270SMarkus Armbruster #include "migration/vmstate.h"
261422e32dSPaolo Bonzini #include "net/net.h"
27da34e65cSMarkus Armbruster #include "qapi/error.h"
280b8fa32fSMarkus Armbruster #include "qemu/module.h"
29104655a5SHervé Poussineau #include "qemu/timer.h"
305691f477SMichael Tokarev #include <zlib.h> /* for crc32 */
31db1015e9SEduardo Habkost #include "qom/object.h"
32c0af04a4SMark Cave-Ayland #include "trace.h"
33a65f56eeSaurel32 
34a65f56eeSaurel32 static const char *reg_names[] = {
35a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
36a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
37a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
38a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
39a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
40a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
41a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
42a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
43a65f56eeSaurel32 
44a65f56eeSaurel32 #define SONIC_CR     0x00
45a65f56eeSaurel32 #define SONIC_DCR    0x01
46a65f56eeSaurel32 #define SONIC_RCR    0x02
47a65f56eeSaurel32 #define SONIC_TCR    0x03
48a65f56eeSaurel32 #define SONIC_IMR    0x04
49a65f56eeSaurel32 #define SONIC_ISR    0x05
50a65f56eeSaurel32 #define SONIC_UTDA   0x06
51a65f56eeSaurel32 #define SONIC_CTDA   0x07
52a65f56eeSaurel32 #define SONIC_TPS    0x08
53a65f56eeSaurel32 #define SONIC_TFC    0x09
54a65f56eeSaurel32 #define SONIC_TSA0   0x0a
55a65f56eeSaurel32 #define SONIC_TSA1   0x0b
56a65f56eeSaurel32 #define SONIC_TFS    0x0c
57a65f56eeSaurel32 #define SONIC_URDA   0x0d
58a65f56eeSaurel32 #define SONIC_CRDA   0x0e
59a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
60a65f56eeSaurel32 #define SONIC_CRBA1  0x10
61a65f56eeSaurel32 #define SONIC_RBWC0  0x11
62a65f56eeSaurel32 #define SONIC_RBWC1  0x12
63a65f56eeSaurel32 #define SONIC_EOBC   0x13
64a65f56eeSaurel32 #define SONIC_URRA   0x14
65a65f56eeSaurel32 #define SONIC_RSA    0x15
66a65f56eeSaurel32 #define SONIC_REA    0x16
67a65f56eeSaurel32 #define SONIC_RRP    0x17
68a65f56eeSaurel32 #define SONIC_RWP    0x18
69a65f56eeSaurel32 #define SONIC_TRBA0  0x19
70a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
71a65f56eeSaurel32 #define SONIC_LLFA   0x1f
72a65f56eeSaurel32 #define SONIC_TTDA   0x20
73a65f56eeSaurel32 #define SONIC_CEP    0x21
74a65f56eeSaurel32 #define SONIC_CAP2   0x22
75a65f56eeSaurel32 #define SONIC_CAP1   0x23
76a65f56eeSaurel32 #define SONIC_CAP0   0x24
77a65f56eeSaurel32 #define SONIC_CE     0x25
78a65f56eeSaurel32 #define SONIC_CDP    0x26
79a65f56eeSaurel32 #define SONIC_CDC    0x27
80a65f56eeSaurel32 #define SONIC_SR     0x28
81a65f56eeSaurel32 #define SONIC_WT0    0x29
82a65f56eeSaurel32 #define SONIC_WT1    0x2a
83a65f56eeSaurel32 #define SONIC_RSC    0x2b
84a65f56eeSaurel32 #define SONIC_CRCT   0x2c
85a65f56eeSaurel32 #define SONIC_FAET   0x2d
86a65f56eeSaurel32 #define SONIC_MPT    0x2e
87a65f56eeSaurel32 #define SONIC_MDT    0x2f
88a65f56eeSaurel32 #define SONIC_DCR2   0x3f
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 
142a65f56eeSaurel32 
1431ca82a8dSMark Cave-Ayland /*
1441ca82a8dSMark Cave-Ayland  * Accessor functions for values which are formed by
145581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
146581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
147581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
148581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
149581f7b12SPeter Maydell  */
dp8393x_cdp(dp8393xState * s)150581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
151581f7b12SPeter Maydell {
152581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
153581f7b12SPeter Maydell }
154581f7b12SPeter Maydell 
dp8393x_crba(dp8393xState * s)155581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
156581f7b12SPeter Maydell {
157581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
158581f7b12SPeter Maydell }
159581f7b12SPeter Maydell 
dp8393x_crda(dp8393xState * s)160581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
161581f7b12SPeter Maydell {
16288f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
16388f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
164581f7b12SPeter Maydell }
165581f7b12SPeter Maydell 
dp8393x_rbwc(dp8393xState * s)166581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
167581f7b12SPeter Maydell {
168581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
169581f7b12SPeter Maydell }
170581f7b12SPeter Maydell 
dp8393x_rrp(dp8393xState * s)171581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
172581f7b12SPeter Maydell {
173581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
174581f7b12SPeter Maydell }
175581f7b12SPeter Maydell 
dp8393x_tsa(dp8393xState * s)176581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
177581f7b12SPeter Maydell {
178581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
179581f7b12SPeter Maydell }
180581f7b12SPeter Maydell 
dp8393x_ttda(dp8393xState * s)181581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
182581f7b12SPeter Maydell {
18388f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
18488f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
185581f7b12SPeter Maydell }
186581f7b12SPeter Maydell 
dp8393x_wt(dp8393xState * s)187581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
188581f7b12SPeter Maydell {
189581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
190581f7b12SPeter Maydell }
191581f7b12SPeter Maydell 
dp8393x_get(dp8393xState * s,hwaddr addr,int offset)19282adabf7SPhilippe Mathieu-Daudé static uint16_t dp8393x_get(dp8393xState *s, hwaddr addr, int offset)
193be920841SLaurent Vivier {
19482adabf7SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
195be920841SLaurent Vivier     uint16_t val;
196be920841SLaurent Vivier 
19782adabf7SPhilippe Mathieu-Daudé     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
19882adabf7SPhilippe Mathieu-Daudé         addr += offset << 2;
199be920841SLaurent Vivier         if (s->big_endian) {
20082adabf7SPhilippe Mathieu-Daudé             val = address_space_ldl_be(&s->as, addr, attrs, NULL);
201be920841SLaurent Vivier         } else {
20282adabf7SPhilippe Mathieu-Daudé             val = address_space_ldl_le(&s->as, addr, attrs, NULL);
203be920841SLaurent Vivier         }
20482adabf7SPhilippe Mathieu-Daudé     } else {
20582adabf7SPhilippe Mathieu-Daudé         addr += offset << 1;
20682adabf7SPhilippe Mathieu-Daudé         if (s->big_endian) {
20782adabf7SPhilippe Mathieu-Daudé             val = address_space_lduw_be(&s->as, addr, attrs, NULL);
20882adabf7SPhilippe Mathieu-Daudé         } else {
20982adabf7SPhilippe Mathieu-Daudé             val = address_space_lduw_le(&s->as, addr, attrs, NULL);
21082adabf7SPhilippe Mathieu-Daudé         }
21182adabf7SPhilippe Mathieu-Daudé     }
21282adabf7SPhilippe Mathieu-Daudé 
213be920841SLaurent Vivier     return val;
214be920841SLaurent Vivier }
215be920841SLaurent Vivier 
dp8393x_put(dp8393xState * s,hwaddr addr,int offset,uint16_t val)21682adabf7SPhilippe Mathieu-Daudé static void dp8393x_put(dp8393xState *s,
21782adabf7SPhilippe Mathieu-Daudé                         hwaddr addr, int offset, uint16_t val)
218be920841SLaurent Vivier {
21982adabf7SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
22082adabf7SPhilippe Mathieu-Daudé 
22182adabf7SPhilippe Mathieu-Daudé     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
22282adabf7SPhilippe Mathieu-Daudé         addr += offset << 2;
223be920841SLaurent Vivier         if (s->big_endian) {
22482adabf7SPhilippe Mathieu-Daudé             address_space_stl_be(&s->as, addr, val, attrs, NULL);
225be920841SLaurent Vivier         } else {
22682adabf7SPhilippe Mathieu-Daudé             address_space_stl_le(&s->as, addr, val, attrs, NULL);
2273fe9a838SFinn Thain         }
2283fe9a838SFinn Thain     } else {
22982adabf7SPhilippe Mathieu-Daudé         addr += offset << 1;
23082adabf7SPhilippe Mathieu-Daudé         if (s->big_endian) {
23182adabf7SPhilippe Mathieu-Daudé             address_space_stw_be(&s->as, addr, val, attrs, NULL);
2323fe9a838SFinn Thain         } else {
23382adabf7SPhilippe Mathieu-Daudé             address_space_stw_le(&s->as, addr, val, attrs, NULL);
2343fe9a838SFinn Thain         }
235be920841SLaurent Vivier     }
236be920841SLaurent Vivier }
237be920841SLaurent Vivier 
dp8393x_update_irq(dp8393xState * s)238a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
239a65f56eeSaurel32 {
240a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
241a65f56eeSaurel32 
242a65f56eeSaurel32     if (level != s->irq_level) {
243a65f56eeSaurel32         s->irq_level = level;
244a65f56eeSaurel32         if (level) {
245c0af04a4SMark Cave-Ayland             trace_dp8393x_raise_irq(s->regs[SONIC_ISR]);
246a65f56eeSaurel32         } else {
247c0af04a4SMark Cave-Ayland             trace_dp8393x_lower_irq();
248a65f56eeSaurel32         }
249a65f56eeSaurel32     }
250a65f56eeSaurel32 
251a65f56eeSaurel32     qemu_set_irq(s->irq, level);
252a65f56eeSaurel32 }
253a65f56eeSaurel32 
dp8393x_do_load_cam(dp8393xState * s)2543df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
255a65f56eeSaurel32 {
256a65f56eeSaurel32     int width, size;
25785e411d7SMark Cave-Ayland     uint16_t index;
258a65f56eeSaurel32 
259a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
260a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
261a65f56eeSaurel32 
262a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
263a65f56eeSaurel32         /* Fill current entry */
26482adabf7SPhilippe Mathieu-Daudé         index = dp8393x_get(s, dp8393x_cdp(s), 0) & 0xf;
26582adabf7SPhilippe Mathieu-Daudé         s->cam[index][0] = dp8393x_get(s, dp8393x_cdp(s), 1);
26682adabf7SPhilippe Mathieu-Daudé         s->cam[index][1] = dp8393x_get(s, dp8393x_cdp(s), 2);
26782adabf7SPhilippe Mathieu-Daudé         s->cam[index][2] = dp8393x_get(s, dp8393x_cdp(s), 3);
2688ac2ffb5SPhilippe Mathieu-Daudé         trace_dp8393x_load_cam(index,
2698ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][0] >> 8, s->cam[index][0] & 0xff,
2708ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][1] >> 8, s->cam[index][1] & 0xff,
2718ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][2] >> 8, s->cam[index][2] & 0xff);
272a65f56eeSaurel32         /* Move to next entry */
273a65f56eeSaurel32         s->regs[SONIC_CDC]--;
274a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
275a65f56eeSaurel32     }
276a65f56eeSaurel32 
277a65f56eeSaurel32     /* Read CAM enable */
27882adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_CE] = dp8393x_get(s, dp8393x_cdp(s), 0);
279c0af04a4SMark Cave-Ayland     trace_dp8393x_load_cam_done(s->regs[SONIC_CE]);
280a65f56eeSaurel32 
281a65f56eeSaurel32     /* Done */
282a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
283a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
284a65f56eeSaurel32     dp8393x_update_irq(s);
285a65f56eeSaurel32 }
286a65f56eeSaurel32 
dp8393x_do_read_rra(dp8393xState * s)2873df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
288a65f56eeSaurel32 {
289a65f56eeSaurel32     int width, size;
290a65f56eeSaurel32 
291a65f56eeSaurel32     /* Read memory */
292a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
293a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
294a65f56eeSaurel32 
295a65f56eeSaurel32     /* Update SONIC registers */
29682adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_CRBA0] = dp8393x_get(s, dp8393x_rrp(s), 0);
29782adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_CRBA1] = dp8393x_get(s, dp8393x_rrp(s), 1);
29882adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_RBWC0] = dp8393x_get(s, dp8393x_rrp(s), 2);
29982adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_RBWC1] = dp8393x_get(s, dp8393x_rrp(s), 3);
300c0af04a4SMark Cave-Ayland     trace_dp8393x_read_rra_regs(s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
301a65f56eeSaurel32                                 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
302a65f56eeSaurel32 
303a65f56eeSaurel32     /* Go to next entry */
304a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
305a65f56eeSaurel32 
306a65f56eeSaurel32     /* Handle wrap */
307a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
308a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
309a65f56eeSaurel32     }
310a65f56eeSaurel32 
311c2279bd0SFinn Thain     /* Warn the host if CRBA now has the last available resource */
3121ca82a8dSMark Cave-Ayland     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
313a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
314a65f56eeSaurel32         dp8393x_update_irq(s);
315a65f56eeSaurel32     }
316c2279bd0SFinn Thain 
317c2279bd0SFinn Thain     /* Allow packet reception */
318c2279bd0SFinn Thain     s->last_rba_is_full = false;
319a65f56eeSaurel32 }
320a65f56eeSaurel32 
dp8393x_do_software_reset(dp8393xState * s)3213df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
322a65f56eeSaurel32 {
323bc72ad67SAlex Bligh     timer_del(s->watchdog);
324a65f56eeSaurel32 
3251ca82a8dSMark Cave-Ayland     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP |
3261ca82a8dSMark Cave-Ayland                            SONIC_CR_HTX);
327a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
328a65f56eeSaurel32 }
329a65f56eeSaurel32 
dp8393x_set_next_tick(dp8393xState * s)3303df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
331a65f56eeSaurel32 {
332a65f56eeSaurel32     uint32_t ticks;
333a65f56eeSaurel32     int64_t delay;
334a65f56eeSaurel32 
335a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
336bc72ad67SAlex Bligh         timer_del(s->watchdog);
337a65f56eeSaurel32         return;
338a65f56eeSaurel32     }
339a65f56eeSaurel32 
340581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
341bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
34273bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
343bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
344a65f56eeSaurel32 }
345a65f56eeSaurel32 
dp8393x_update_wt_regs(dp8393xState * s)3463df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
347a65f56eeSaurel32 {
348a65f56eeSaurel32     int64_t elapsed;
349a65f56eeSaurel32     uint32_t val;
350a65f56eeSaurel32 
351a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
352bc72ad67SAlex Bligh         timer_del(s->watchdog);
353a65f56eeSaurel32         return;
354a65f56eeSaurel32     }
355a65f56eeSaurel32 
356bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
357581f7b12SPeter Maydell     val = dp8393x_wt(s);
358a65f56eeSaurel32     val -= elapsed / 5000000;
359a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
360a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3613df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
362a65f56eeSaurel32 
363a65f56eeSaurel32 }
364a65f56eeSaurel32 
dp8393x_do_start_timer(dp8393xState * s)3653df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
366a65f56eeSaurel32 {
367a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3683df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
369a65f56eeSaurel32 }
370a65f56eeSaurel32 
dp8393x_do_stop_timer(dp8393xState * s)3713df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
372a65f56eeSaurel32 {
373a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3743df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
375a65f56eeSaurel32 }
376a65f56eeSaurel32 
377b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc);
3784594f93aSFam Zheng 
dp8393x_do_receiver_enable(dp8393xState * s)3793df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
380a65f56eeSaurel32 {
381a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
3824594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
3834594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
3844594f93aSFam Zheng     }
385a65f56eeSaurel32 }
386a65f56eeSaurel32 
dp8393x_do_receiver_disable(dp8393xState * s)3873df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
388a65f56eeSaurel32 {
389a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
390a65f56eeSaurel32 }
391a65f56eeSaurel32 
dp8393x_do_transmit_packets(dp8393xState * s)3923df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
393a65f56eeSaurel32 {
394b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
395a65f56eeSaurel32     int tx_len, len;
396a65f56eeSaurel32     uint16_t i;
397a65f56eeSaurel32 
398a65f56eeSaurel32     while (1) {
399a65f56eeSaurel32         /* Read memory */
400a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
401c0af04a4SMark Cave-Ayland         trace_dp8393x_transmit_packet(dp8393x_ttda(s));
402a65f56eeSaurel32         tx_len = 0;
403a65f56eeSaurel32 
404a65f56eeSaurel32         /* Update registers */
40582adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TCR] = dp8393x_get(s, dp8393x_ttda(s), 1) & 0xf000;
40682adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TPS] = dp8393x_get(s, dp8393x_ttda(s), 2);
40782adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TFC] = dp8393x_get(s, dp8393x_ttda(s), 3);
40882adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TSA0] = dp8393x_get(s, dp8393x_ttda(s), 4);
40982adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TSA1] = dp8393x_get(s, dp8393x_ttda(s), 5);
41082adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TFS] = dp8393x_get(s, dp8393x_ttda(s), 6);
411a65f56eeSaurel32 
412a65f56eeSaurel32         /* Handle programmable interrupt */
413a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
414a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
415a65f56eeSaurel32         } else {
416a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
417a65f56eeSaurel32         }
418a65f56eeSaurel32 
419a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
420a65f56eeSaurel32             /* Append fragment */
421a65f56eeSaurel32             len = s->regs[SONIC_TFS];
422a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
423a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
424a65f56eeSaurel32             }
42519f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
42619f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
427a65f56eeSaurel32             tx_len += len;
428a65f56eeSaurel32 
429a65f56eeSaurel32             i++;
430a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
431a65f56eeSaurel32                 /* Read next fragment details */
43282adabf7SPhilippe Mathieu-Daudé                 s->regs[SONIC_TSA0] = dp8393x_get(s, dp8393x_ttda(s),
43382adabf7SPhilippe Mathieu-Daudé                                                   4 + 3 * i);
43482adabf7SPhilippe Mathieu-Daudé                 s->regs[SONIC_TSA1] = dp8393x_get(s, dp8393x_ttda(s),
43582adabf7SPhilippe Mathieu-Daudé                                                   5 + 3 * i);
43682adabf7SPhilippe Mathieu-Daudé                 s->regs[SONIC_TFS] = dp8393x_get(s, dp8393x_ttda(s),
43782adabf7SPhilippe Mathieu-Daudé                                                  6 + 3 * i);
438a65f56eeSaurel32             }
439a65f56eeSaurel32         }
440a65f56eeSaurel32 
441a65f56eeSaurel32         /* Handle Ethernet checksum */
442a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
4431ca82a8dSMark Cave-Ayland             /*
4441ca82a8dSMark Cave-Ayland              * Don't append FCS there, to look like slirp packets
4451ca82a8dSMark Cave-Ayland              * which don't have one
4461ca82a8dSMark Cave-Ayland              */
447a65f56eeSaurel32         } else {
448a65f56eeSaurel32             /* Remove existing FCS */
449a65f56eeSaurel32             tx_len -= 4;
450915976bdSMauro Matteo Cascella             if (tx_len < 0) {
451c0af04a4SMark Cave-Ayland                 trace_dp8393x_transmit_txlen_error(tx_len);
452915976bdSMauro Matteo Cascella                 break;
453915976bdSMauro Matteo Cascella             }
454a65f56eeSaurel32         }
455a65f56eeSaurel32 
456a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
457a65f56eeSaurel32             /* Loopback */
458a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
459b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
460a65f56eeSaurel32                 s->loopback_packet = 1;
461331d2ac9SJason Wang                 qemu_receive_packet(nc, s->tx_buffer, tx_len);
462a65f56eeSaurel32             }
463a65f56eeSaurel32         } else {
464a65f56eeSaurel32             /* Transmit packet */
465b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
466a65f56eeSaurel32         }
467a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
468a65f56eeSaurel32 
469a65f56eeSaurel32         /* Write status */
47082adabf7SPhilippe Mathieu-Daudé         dp8393x_put(s, dp8393x_ttda(s), 0, s->regs[SONIC_TCR] & 0x0fff);
471a65f56eeSaurel32 
472a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
473a65f56eeSaurel32             /* Read footer of packet */
47482adabf7SPhilippe Mathieu-Daudé             s->regs[SONIC_CTDA] = dp8393x_get(s, dp8393x_ttda(s),
47582adabf7SPhilippe Mathieu-Daudé                                               4 + 3 * s->regs[SONIC_TFC]);
476a0cf4297SFinn Thain             if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) {
477a65f56eeSaurel32                 /* EOL detected */
478a65f56eeSaurel32                 break;
479a65f56eeSaurel32             }
480a65f56eeSaurel32         }
481a65f56eeSaurel32     }
482a65f56eeSaurel32 
483a65f56eeSaurel32     /* Done */
484a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
485a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
486a65f56eeSaurel32     dp8393x_update_irq(s);
487a65f56eeSaurel32 }
488a65f56eeSaurel32 
dp8393x_do_halt_transmission(dp8393xState * s)4893df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
490a65f56eeSaurel32 {
491a65f56eeSaurel32     /* Nothing to do */
492a65f56eeSaurel32 }
493a65f56eeSaurel32 
dp8393x_do_command(dp8393xState * s,uint16_t command)4943df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
495a65f56eeSaurel32 {
496a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
497a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
498a65f56eeSaurel32         return;
499a65f56eeSaurel32     }
500a65f56eeSaurel32 
501a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
502a65f56eeSaurel32 
5031ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_HTX) {
5043df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
5051ca82a8dSMark Cave-Ayland     }
5061ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_TXP) {
5073df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
5081ca82a8dSMark Cave-Ayland     }
5091ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXDIS) {
5103df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
5111ca82a8dSMark Cave-Ayland     }
5121ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXEN) {
5133df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
5141ca82a8dSMark Cave-Ayland     }
5151ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_STP) {
5163df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
5171ca82a8dSMark Cave-Ayland     }
5181ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_ST) {
5193df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
5201ca82a8dSMark Cave-Ayland     }
5211ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RST) {
5223df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
5231ca82a8dSMark Cave-Ayland     }
524a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5253df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
526a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
527a3cce282SFinn Thain     }
5281ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_LCAM) {
5293df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
530a65f56eeSaurel32     }
5311ca82a8dSMark Cave-Ayland }
532a65f56eeSaurel32 
dp8393x_read(void * opaque,hwaddr addr,unsigned int size)53384689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
534a65f56eeSaurel32 {
53584689cbbSHervé Poussineau     dp8393xState *s = opaque;
53684689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
537a65f56eeSaurel32     uint16_t val = 0;
538a65f56eeSaurel32 
539a65f56eeSaurel32     switch (reg) {
540a65f56eeSaurel32     /* Update data before reading it */
541a65f56eeSaurel32     case SONIC_WT0:
542a65f56eeSaurel32     case SONIC_WT1:
5433df5de64SHervé Poussineau         dp8393x_update_wt_regs(s);
544a65f56eeSaurel32         val = s->regs[reg];
545a65f56eeSaurel32         break;
546a65f56eeSaurel32     /* Accept read to some registers only when in reset mode */
547a65f56eeSaurel32     case SONIC_CAP2:
548a65f56eeSaurel32     case SONIC_CAP1:
549a65f56eeSaurel32     case SONIC_CAP0:
550a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
5518ac2ffb5SPhilippe Mathieu-Daudé             val = s->cam[s->regs[SONIC_CEP] & 0xf][SONIC_CAP0 - reg];
552a65f56eeSaurel32         }
553a65f56eeSaurel32         break;
5542431f4f1SMichael Tokarev     /* All other registers have no special constraints */
555a65f56eeSaurel32     default:
556a65f56eeSaurel32         val = s->regs[reg];
557a65f56eeSaurel32     }
558a65f56eeSaurel32 
559c0af04a4SMark Cave-Ayland     trace_dp8393x_read(reg, reg_names[reg], val, size);
560a65f56eeSaurel32 
56139d9919fSMark Cave-Ayland     return val;
562a65f56eeSaurel32 }
563a65f56eeSaurel32 
dp8393x_write(void * opaque,hwaddr addr,uint64_t val,unsigned int size)56439d9919fSMark Cave-Ayland static void dp8393x_write(void *opaque, hwaddr addr, uint64_t val,
56584689cbbSHervé Poussineau                           unsigned int size)
566a65f56eeSaurel32 {
56784689cbbSHervé Poussineau     dp8393xState *s = opaque;
56884689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
56984689cbbSHervé Poussineau 
570c0af04a4SMark Cave-Ayland     trace_dp8393x_write(reg, reg_names[reg], val, size);
571a65f56eeSaurel32 
572a65f56eeSaurel32     switch (reg) {
573a65f56eeSaurel32     /* Command register */
574a65f56eeSaurel32     case SONIC_CR:
5753fe9a838SFinn Thain         dp8393x_do_command(s, val);
576a65f56eeSaurel32         break;
577a65f56eeSaurel32     /* Prevent write to read-only registers */
578a65f56eeSaurel32     case SONIC_CAP2:
579a65f56eeSaurel32     case SONIC_CAP1:
580a65f56eeSaurel32     case SONIC_CAP0:
581a65f56eeSaurel32     case SONIC_SR:
582a65f56eeSaurel32     case SONIC_MDT:
583c0af04a4SMark Cave-Ayland         trace_dp8393x_write_invalid(reg);
584a65f56eeSaurel32         break;
585a65f56eeSaurel32     /* Accept write to some registers only when in reset mode */
586a65f56eeSaurel32     case SONIC_DCR:
587a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
5883fe9a838SFinn Thain             s->regs[reg] = val & 0xbfff;
589a65f56eeSaurel32         } else {
590c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR");
591a65f56eeSaurel32         }
592a65f56eeSaurel32         break;
593a65f56eeSaurel32     case SONIC_DCR2:
594a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
5953fe9a838SFinn Thain             s->regs[reg] = val & 0xf017;
596a65f56eeSaurel32         } else {
597c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR2");
598a65f56eeSaurel32         }
599a65f56eeSaurel32         break;
600a65f56eeSaurel32     /* 12 lower bytes are Read Only */
601a65f56eeSaurel32     case SONIC_TCR:
6023fe9a838SFinn Thain         s->regs[reg] = val & 0xf000;
603a65f56eeSaurel32         break;
604a65f56eeSaurel32     /* 9 lower bytes are Read Only */
605a65f56eeSaurel32     case SONIC_RCR:
6063fe9a838SFinn Thain         s->regs[reg] = val & 0xffe0;
607a65f56eeSaurel32         break;
608a65f56eeSaurel32     /* Ignore most significant bit */
609a65f56eeSaurel32     case SONIC_IMR:
6103fe9a838SFinn Thain         s->regs[reg] = val & 0x7fff;
611a65f56eeSaurel32         dp8393x_update_irq(s);
612a65f56eeSaurel32         break;
613a65f56eeSaurel32     /* Clear bits by writing 1 to them */
614a65f56eeSaurel32     case SONIC_ISR:
6153fe9a838SFinn Thain         val &= s->regs[reg];
6163fe9a838SFinn Thain         s->regs[reg] &= ~val;
6173fe9a838SFinn Thain         if (val & SONIC_ISR_RBE) {
6183df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
619a65f56eeSaurel32         }
620a65f56eeSaurel32         dp8393x_update_irq(s);
621a65f56eeSaurel32         break;
622ea227027SFinn Thain     /* The guest is required to store aligned pointers here */
623a65f56eeSaurel32     case SONIC_RSA:
624a65f56eeSaurel32     case SONIC_REA:
625a65f56eeSaurel32     case SONIC_RRP:
626a65f56eeSaurel32     case SONIC_RWP:
627ea227027SFinn Thain         if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
628ea227027SFinn Thain             s->regs[reg] = val & 0xfffc;
629ea227027SFinn Thain         } else {
6303fe9a838SFinn Thain             s->regs[reg] = val & 0xfffe;
631ea227027SFinn Thain         }
632a65f56eeSaurel32         break;
633a65f56eeSaurel32     /* Invert written value for some registers */
634a65f56eeSaurel32     case SONIC_CRCT:
635a65f56eeSaurel32     case SONIC_FAET:
636a65f56eeSaurel32     case SONIC_MPT:
6373fe9a838SFinn Thain         s->regs[reg] = val ^ 0xffff;
638a65f56eeSaurel32         break;
639a65f56eeSaurel32     /* All other registers have no special contrainst */
640a65f56eeSaurel32     default:
6413fe9a838SFinn Thain         s->regs[reg] = val;
642a65f56eeSaurel32     }
643a65f56eeSaurel32 
644a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6453df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
646a65f56eeSaurel32     }
647a65f56eeSaurel32 }
648a65f56eeSaurel32 
64939d9919fSMark Cave-Ayland /*
65039d9919fSMark Cave-Ayland  * Since .impl.max_access_size is effectively controlled by the it_shift
65139d9919fSMark Cave-Ayland  * property, leave it unspecified for now to allow the memory API to
65239d9919fSMark Cave-Ayland  * correctly zero extend the 16-bit register values to the access size up to and
65339d9919fSMark Cave-Ayland  * including it_shift.
65439d9919fSMark Cave-Ayland  */
65584689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
65684689cbbSHervé Poussineau     .read = dp8393x_read,
65784689cbbSHervé Poussineau     .write = dp8393x_write,
65839d9919fSMark Cave-Ayland     .impl.min_access_size = 2,
65984689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
66084689cbbSHervé Poussineau };
66184689cbbSHervé Poussineau 
dp8393x_watchdog(void * opaque)662a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
663a65f56eeSaurel32 {
664a65f56eeSaurel32     dp8393xState *s = opaque;
665a65f56eeSaurel32 
666a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
667a65f56eeSaurel32         return;
668a65f56eeSaurel32     }
669a65f56eeSaurel32 
670a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
671a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
6723df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
673a65f56eeSaurel32 
674a65f56eeSaurel32     /* Signal underflow */
675a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
676a65f56eeSaurel32     dp8393x_update_irq(s);
677a65f56eeSaurel32 }
678a65f56eeSaurel32 
dp8393x_can_receive(NetClientState * nc)679b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc)
680a65f56eeSaurel32 {
681cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
682a65f56eeSaurel32 
683b8c4b67eSPhilippe Mathieu-Daudé     return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN);
684a65f56eeSaurel32 }
685a65f56eeSaurel32 
dp8393x_receive_filter(dp8393xState * s,const uint8_t * buf,int size)6863df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
6873df5de64SHervé Poussineau                                   int size)
688a65f56eeSaurel32 {
689a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
690a65f56eeSaurel32     int i;
691a65f56eeSaurel32 
692a65f56eeSaurel32     /* Check promiscuous mode */
693a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
694a65f56eeSaurel32         return 0;
695a65f56eeSaurel32     }
696a65f56eeSaurel32 
697a65f56eeSaurel32     /* Check multicast packets */
698a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
699a65f56eeSaurel32         return SONIC_RCR_MC;
700a65f56eeSaurel32     }
701a65f56eeSaurel32 
702a65f56eeSaurel32     /* Check broadcast */
7031ca82a8dSMark Cave-Ayland     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) &&
7041ca82a8dSMark Cave-Ayland          !memcmp(buf, bcast, sizeof(bcast))) {
705a65f56eeSaurel32         return SONIC_RCR_BC;
706a65f56eeSaurel32     }
707a65f56eeSaurel32 
708a65f56eeSaurel32     /* Check CAM */
709a65f56eeSaurel32     for (i = 0; i < 16; i++) {
710a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
711a65f56eeSaurel32             /* Entry enabled */
712a65f56eeSaurel32             if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
713a65f56eeSaurel32                 return 0;
714a65f56eeSaurel32             }
715a65f56eeSaurel32         }
716a65f56eeSaurel32     }
717a65f56eeSaurel32 
718a65f56eeSaurel32     return -1;
719a65f56eeSaurel32 }
720a65f56eeSaurel32 
dp8393x_receive(NetClientState * nc,const uint8_t * buf,size_t pkt_size)7213df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7229e3cd456SFinn Thain                                size_t pkt_size)
723a65f56eeSaurel32 {
724cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
725a65f56eeSaurel32     int packet_type;
726a65f56eeSaurel32     uint32_t available, address;
72782adabf7SPhilippe Mathieu-Daudé     int rx_len, padded_len;
728a65f56eeSaurel32     uint32_t checksum;
7299e3cd456SFinn Thain     int size;
730a65f56eeSaurel32 
731a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
732a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
733a65f56eeSaurel32 
734c2279bd0SFinn Thain     if (s->last_rba_is_full) {
735c2279bd0SFinn Thain         return pkt_size;
736c2279bd0SFinn Thain     }
737c2279bd0SFinn Thain 
738350e7d9aSFinn Thain     rx_len = pkt_size + sizeof(checksum);
739350e7d9aSFinn Thain     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
740350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 3) + 1;
741350e7d9aSFinn Thain     } else {
742350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 1) + 1;
743350e7d9aSFinn Thain     }
744350e7d9aSFinn Thain 
745350e7d9aSFinn Thain     if (padded_len > dp8393x_rbwc(s) * 2) {
746c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_oversize(pkt_size);
747ada74315SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_RBAE;
748ada74315SFinn Thain         dp8393x_update_irq(s);
749c2279bd0SFinn Thain         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
750c2279bd0SFinn Thain         goto done;
751ada74315SFinn Thain     }
752ada74315SFinn Thain 
7539e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
754a65f56eeSaurel32     if (packet_type < 0) {
755c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_not_netcard();
7564f1c942bSMark McLoughlin         return -1;
757a65f56eeSaurel32     }
758a65f56eeSaurel32 
759a65f56eeSaurel32     /* Check for EOL */
76088f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
761a65f56eeSaurel32         /* Are we still in resource exhaustion? */
76282adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_LLFA] = dp8393x_get(s, dp8393x_crda(s), 5);
7635b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
764a65f56eeSaurel32             /* Still EOL ; stop reception */
7654f1c942bSMark McLoughlin             return -1;
766a65f56eeSaurel32         }
7675b0c98fcSFinn Thain         /* Link has been updated by host */
768d9fae131SFinn Thain 
769d9fae131SFinn Thain         /* Clear in_use */
77082adabf7SPhilippe Mathieu-Daudé         dp8393x_put(s, dp8393x_crda(s), 6, 0x0000);
771d9fae131SFinn Thain 
772d9fae131SFinn Thain         /* Move to next descriptor */
7735b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
774d9fae131SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
775a65f56eeSaurel32     }
776a65f56eeSaurel32 
777a65f56eeSaurel32     /* Save current position */
778a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
779a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
780a65f56eeSaurel32 
781a65f56eeSaurel32     /* Calculate the ethernet checksum */
782350e7d9aSFinn Thain     checksum = cpu_to_le32(crc32(0, buf, pkt_size));
783a65f56eeSaurel32 
784a65f56eeSaurel32     /* Put packet into RBA */
785c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_packet(dp8393x_crba(s));
786581f7b12SPeter Maydell     address = dp8393x_crba(s);
78719f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
788350e7d9aSFinn Thain                         buf, pkt_size);
789350e7d9aSFinn Thain     address += pkt_size;
790350e7d9aSFinn Thain 
791350e7d9aSFinn Thain     /* Put frame checksum into RBA */
79219f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
793350e7d9aSFinn Thain                         &checksum, sizeof(checksum));
794350e7d9aSFinn Thain     address += sizeof(checksum);
795350e7d9aSFinn Thain 
796350e7d9aSFinn Thain     /* Pad short packets to keep pointers aligned */
797350e7d9aSFinn Thain     if (rx_len < padded_len) {
798350e7d9aSFinn Thain         size = padded_len - rx_len;
799197ade0dSPhilippe Mathieu-Daudé         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
800197ade0dSPhilippe Mathieu-Daudé                             "\xFF\xFF\xFF", size);
801350e7d9aSFinn Thain         address += size;
802350e7d9aSFinn Thain     }
803350e7d9aSFinn Thain 
804a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
805a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
806581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
807350e7d9aSFinn Thain     available -= padded_len >> 1;
808a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
809a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
810a65f56eeSaurel32 
811a65f56eeSaurel32     /* Update status */
812581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
813a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
814a65f56eeSaurel32     }
815a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
816a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
817a65f56eeSaurel32     if (s->loopback_packet) {
818a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
819a65f56eeSaurel32         s->loopback_packet = 0;
820a65f56eeSaurel32     }
821a65f56eeSaurel32 
822a65f56eeSaurel32     /* Write status to memory */
823c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_write_status(dp8393x_crda(s));
82482adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 0, s->regs[SONIC_RCR]); /* status */
82582adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 1, rx_len); /* byte count */
82682adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
82782adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
82882adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 4, s->regs[SONIC_RSC]); /* seq_no */
829a65f56eeSaurel32 
8305b0c98fcSFinn Thain     /* Check link field */
83182adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_LLFA] = dp8393x_get(s, dp8393x_crda(s), 5);
83288f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
833a65f56eeSaurel32         /* EOL detected */
834a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
835a65f56eeSaurel32     } else {
83646ffee9aSFinn Thain         /* Clear in_use */
83782adabf7SPhilippe Mathieu-Daudé         dp8393x_put(s, dp8393x_crda(s), 6, 0x0000);
8385b0c98fcSFinn Thain 
8395b0c98fcSFinn Thain         /* Move to next descriptor */
840a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
841a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
84280b60673SFinn Thain     }
84380b60673SFinn Thain 
844c2279bd0SFinn Thain     dp8393x_update_irq(s);
845c2279bd0SFinn Thain 
84680b60673SFinn Thain     s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) |
84780b60673SFinn Thain                          ((s->regs[SONIC_RSC] + 1) & 0x00ff);
848a65f56eeSaurel32 
849c2279bd0SFinn Thain done:
850c2279bd0SFinn Thain 
851a65f56eeSaurel32     if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
852c2279bd0SFinn Thain         if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
853c2279bd0SFinn Thain             /* Stop packet reception */
854c2279bd0SFinn Thain             s->last_rba_is_full = true;
855c2279bd0SFinn Thain         } else {
856c2279bd0SFinn Thain             /* Read next resource */
8573df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
858a65f56eeSaurel32         }
859c2279bd0SFinn Thain     }
8604f1c942bSMark McLoughlin 
8619e3cd456SFinn Thain     return pkt_size;
862a65f56eeSaurel32 }
863a65f56eeSaurel32 
dp8393x_reset(DeviceState * dev)864104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
865a65f56eeSaurel32 {
866104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
867bc72ad67SAlex Bligh     timer_del(s->watchdog);
868a65f56eeSaurel32 
869bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
870083e21bbSFinn Thain     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */
871a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
872a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
8731ca82a8dSMark Cave-Ayland     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD |
8741ca82a8dSMark Cave-Ayland                             SONIC_RCR_RNT);
875a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
876a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
877a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
878a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
879a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
880a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
881a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
882a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
883a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
884a65f56eeSaurel32 
885a65f56eeSaurel32     /* Network cable is connected */
886a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
887a65f56eeSaurel32 
888a65f56eeSaurel32     dp8393x_update_irq(s);
889a65f56eeSaurel32 }
890a65f56eeSaurel32 
89105f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
892f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
89305f41fe3SMark McLoughlin     .size = sizeof(NICState),
8943df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8953df5de64SHervé Poussineau     .receive = dp8393x_receive,
89605f41fe3SMark McLoughlin };
89705f41fe3SMark McLoughlin 
dp8393x_instance_init(Object * obj)898104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
899a65f56eeSaurel32 {
900104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
901104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
902a65f56eeSaurel32 
903104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
904104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
905104655a5SHervé Poussineau }
906a65f56eeSaurel32 
dp8393x_realize(DeviceState * dev,Error ** errp)907104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
908104655a5SHervé Poussineau {
909104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
910a65f56eeSaurel32 
911104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
912104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
91367b38ddfSPhilippe Mathieu-Daudé                           "dp8393x-regs", SONIC_REG_COUNT << s->it_shift);
914104655a5SHervé Poussineau 
915104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
9167d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), dev->id,
9177d0fefdfSAkihiko Odaki                           &dev->mem_reentrancy_guard, s);
918104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
919104655a5SHervé Poussineau 
920bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
921a65f56eeSaurel32 }
922104655a5SHervé Poussineau 
9231670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9241670735dSHervé Poussineau     .name = "dp8393x",
9258ac2ffb5SPhilippe Mathieu-Daudé     .version_id = 1,
9268ac2ffb5SPhilippe Mathieu-Daudé     .minimum_version_id = 1,
9271de81b42SRichard Henderson     .fields = (const VMStateField []) {
9288ac2ffb5SPhilippe Mathieu-Daudé         VMSTATE_UINT16_2DARRAY(cam, dp8393xState, 16, 3),
92967b38ddfSPhilippe Mathieu-Daudé         VMSTATE_UINT16_ARRAY(regs, dp8393xState, SONIC_REG_COUNT),
9301670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9311670735dSHervé Poussineau     }
9321670735dSHervé Poussineau };
9331670735dSHervé Poussineau 
934e732f00fSRichard Henderson static const Property dp8393x_properties[] = {
935104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
9363110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
9373110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
938104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
939be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
940104655a5SHervé Poussineau };
941104655a5SHervé Poussineau 
dp8393x_class_init(ObjectClass * klass,const void * data)942*12d1a768SPhilippe Mathieu-Daudé static void dp8393x_class_init(ObjectClass *klass, const void *data)
943104655a5SHervé Poussineau {
944104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
945104655a5SHervé Poussineau 
946104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
947104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
948e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, dp8393x_reset);
9491670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
9504f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
951104655a5SHervé Poussineau }
952104655a5SHervé Poussineau 
953104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
954104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
955104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
956104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
957104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
958104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
959104655a5SHervé Poussineau };
960104655a5SHervé Poussineau 
dp8393x_register_types(void)961104655a5SHervé Poussineau static void dp8393x_register_types(void)
962104655a5SHervé Poussineau {
963104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
964104655a5SHervé Poussineau }
965104655a5SHervé Poussineau 
966104655a5SHervé Poussineau type_init(dp8393x_register_types)
967