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