xref: /qemu/hw/net/dp8393x.c (revision ea2270279bc2e1635cb6e909e22e17e630198773)
1a65f56eeSaurel32 /*
2a65f56eeSaurel32  * QEMU NS SONIC DP8393x netcard
3a65f56eeSaurel32  *
4a65f56eeSaurel32  * Copyright (c) 2008-2009 Herve Poussineau
5a65f56eeSaurel32  *
6a65f56eeSaurel32  * This program is free software; you can redistribute it and/or
7a65f56eeSaurel32  * modify it under the terms of the GNU General Public License as
8a65f56eeSaurel32  * published by the Free Software Foundation; either version 2 of
9a65f56eeSaurel32  * the License, or (at your option) any later version.
10a65f56eeSaurel32  *
11a65f56eeSaurel32  * This program is distributed in the hope that it will be useful,
12a65f56eeSaurel32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13a65f56eeSaurel32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14a65f56eeSaurel32  * GNU General Public License for more details.
15a65f56eeSaurel32  *
16a65f56eeSaurel32  * You should have received a copy of the GNU General Public License along
178167ee88SBlue Swirl  * with this program; if not, see <http://www.gnu.org/licenses/>.
18a65f56eeSaurel32  */
19a65f56eeSaurel32 
20e8d40465SPeter Maydell #include "qemu/osdep.h"
2164552b6bSMarkus Armbruster #include "hw/irq.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
23104655a5SHervé Poussineau #include "hw/sysbus.h"
24d6454270SMarkus Armbruster #include "migration/vmstate.h"
251422e32dSPaolo Bonzini #include "net/net.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
28104655a5SHervé Poussineau #include "qemu/timer.h"
29f2f62c4dSHervé Poussineau #include <zlib.h>
30a65f56eeSaurel32 
31a65f56eeSaurel32 //#define DEBUG_SONIC
32a65f56eeSaurel32 
3389ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
34a65f56eeSaurel32 
35a65f56eeSaurel32 #ifdef DEBUG_SONIC
36001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
37001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
38a65f56eeSaurel32 static const char* reg_names[] = {
39a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
40a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
41a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
42a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
43a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
44a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
45a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
46a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
47a65f56eeSaurel32 #else
48001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
49a65f56eeSaurel32 #endif
50a65f56eeSaurel32 
51001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
52001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
53a65f56eeSaurel32 
54a65f56eeSaurel32 #define SONIC_CR     0x00
55a65f56eeSaurel32 #define SONIC_DCR    0x01
56a65f56eeSaurel32 #define SONIC_RCR    0x02
57a65f56eeSaurel32 #define SONIC_TCR    0x03
58a65f56eeSaurel32 #define SONIC_IMR    0x04
59a65f56eeSaurel32 #define SONIC_ISR    0x05
60a65f56eeSaurel32 #define SONIC_UTDA   0x06
61a65f56eeSaurel32 #define SONIC_CTDA   0x07
62a65f56eeSaurel32 #define SONIC_TPS    0x08
63a65f56eeSaurel32 #define SONIC_TFC    0x09
64a65f56eeSaurel32 #define SONIC_TSA0   0x0a
65a65f56eeSaurel32 #define SONIC_TSA1   0x0b
66a65f56eeSaurel32 #define SONIC_TFS    0x0c
67a65f56eeSaurel32 #define SONIC_URDA   0x0d
68a65f56eeSaurel32 #define SONIC_CRDA   0x0e
69a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
70a65f56eeSaurel32 #define SONIC_CRBA1  0x10
71a65f56eeSaurel32 #define SONIC_RBWC0  0x11
72a65f56eeSaurel32 #define SONIC_RBWC1  0x12
73a65f56eeSaurel32 #define SONIC_EOBC   0x13
74a65f56eeSaurel32 #define SONIC_URRA   0x14
75a65f56eeSaurel32 #define SONIC_RSA    0x15
76a65f56eeSaurel32 #define SONIC_REA    0x16
77a65f56eeSaurel32 #define SONIC_RRP    0x17
78a65f56eeSaurel32 #define SONIC_RWP    0x18
79a65f56eeSaurel32 #define SONIC_TRBA0  0x19
80a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
81a65f56eeSaurel32 #define SONIC_LLFA   0x1f
82a65f56eeSaurel32 #define SONIC_TTDA   0x20
83a65f56eeSaurel32 #define SONIC_CEP    0x21
84a65f56eeSaurel32 #define SONIC_CAP2   0x22
85a65f56eeSaurel32 #define SONIC_CAP1   0x23
86a65f56eeSaurel32 #define SONIC_CAP0   0x24
87a65f56eeSaurel32 #define SONIC_CE     0x25
88a65f56eeSaurel32 #define SONIC_CDP    0x26
89a65f56eeSaurel32 #define SONIC_CDC    0x27
90a65f56eeSaurel32 #define SONIC_SR     0x28
91a65f56eeSaurel32 #define SONIC_WT0    0x29
92a65f56eeSaurel32 #define SONIC_WT1    0x2a
93a65f56eeSaurel32 #define SONIC_RSC    0x2b
94a65f56eeSaurel32 #define SONIC_CRCT   0x2c
95a65f56eeSaurel32 #define SONIC_FAET   0x2d
96a65f56eeSaurel32 #define SONIC_MPT    0x2e
97a65f56eeSaurel32 #define SONIC_MDT    0x2f
98a65f56eeSaurel32 #define SONIC_DCR2   0x3f
99a65f56eeSaurel32 
100a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
101a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
102a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
103a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
104a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
105a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
106a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
107a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
108a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
109a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
110a65f56eeSaurel32 
111a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
112a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
113a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
114a65f56eeSaurel32 
115a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
116a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
117a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
118a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
119a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
120a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
121a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
122a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
123a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
124a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
125a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
126a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
127a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
128a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
129a65f56eeSaurel32 
130a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
131a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
132a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
133a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
134a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
135a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
136a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
137a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
138a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
139a65f56eeSaurel32 
140ada74315SFinn Thain #define SONIC_ISR_RBAE   0x0010
141a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
142a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
143a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
144a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
145a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
146a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
147a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
148a65f56eeSaurel32 
14988f632fbSFinn Thain #define SONIC_DESC_EOL   0x0001
15088f632fbSFinn Thain #define SONIC_DESC_ADDR  0xFFFE
15188f632fbSFinn Thain 
152104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
153104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
154104655a5SHervé Poussineau 
155a65f56eeSaurel32 typedef struct dp8393xState {
156104655a5SHervé Poussineau     SysBusDevice parent_obj;
157104655a5SHervé Poussineau 
158a65f56eeSaurel32     /* Hardware */
159104655a5SHervé Poussineau     uint8_t it_shift;
160be920841SLaurent Vivier     bool big_endian;
161a65f56eeSaurel32     qemu_irq irq;
162a65f56eeSaurel32 #ifdef DEBUG_SONIC
163a65f56eeSaurel32     int irq_level;
164a65f56eeSaurel32 #endif
165a65f56eeSaurel32     QEMUTimer *watchdog;
166a65f56eeSaurel32     int64_t wt_last_update;
16705f41fe3SMark McLoughlin     NICConf conf;
16805f41fe3SMark McLoughlin     NICState *nic;
169024e5bb6SAvi Kivity     MemoryRegion mmio;
17089ae0ff9SHervé Poussineau     MemoryRegion prom;
171a65f56eeSaurel32 
172a65f56eeSaurel32     /* Registers */
173a65f56eeSaurel32     uint8_t cam[16][6];
174a65f56eeSaurel32     uint16_t regs[0x40];
175a65f56eeSaurel32 
176a65f56eeSaurel32     /* Temporaries */
177a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
178af9f0be3SLaurent Vivier     uint16_t data[12];
179a65f56eeSaurel32     int loopback_packet;
180a65f56eeSaurel32 
181a65f56eeSaurel32     /* Memory access */
1823110ce81SMarc-André Lureau     MemoryRegion *dma_mr;
183dd820513SHervé Poussineau     AddressSpace as;
184a65f56eeSaurel32 } dp8393xState;
185a65f56eeSaurel32 
186581f7b12SPeter Maydell /* Accessor functions for values which are formed by
187581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
188581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
189581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
190581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
191581f7b12SPeter Maydell  */
192581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
193581f7b12SPeter Maydell {
194581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
195581f7b12SPeter Maydell }
196581f7b12SPeter Maydell 
197581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
198581f7b12SPeter Maydell {
199581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
200581f7b12SPeter Maydell }
201581f7b12SPeter Maydell 
202581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
203581f7b12SPeter Maydell {
20488f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
20588f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
206581f7b12SPeter Maydell }
207581f7b12SPeter Maydell 
208581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
209581f7b12SPeter Maydell {
210581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
211581f7b12SPeter Maydell }
212581f7b12SPeter Maydell 
213581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
214581f7b12SPeter Maydell {
215581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
216581f7b12SPeter Maydell }
217581f7b12SPeter Maydell 
218581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
219581f7b12SPeter Maydell {
220581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
221581f7b12SPeter Maydell }
222581f7b12SPeter Maydell 
223581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
224581f7b12SPeter Maydell {
22588f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
22688f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
227581f7b12SPeter Maydell }
228581f7b12SPeter Maydell 
229581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
230581f7b12SPeter Maydell {
231581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
232581f7b12SPeter Maydell }
233581f7b12SPeter Maydell 
234af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset)
235be920841SLaurent Vivier {
236be920841SLaurent Vivier     uint16_t val;
237be920841SLaurent Vivier 
238be920841SLaurent Vivier     if (s->big_endian) {
239af9f0be3SLaurent Vivier         val = be16_to_cpu(s->data[offset * width + width - 1]);
240be920841SLaurent Vivier     } else {
241af9f0be3SLaurent Vivier         val = le16_to_cpu(s->data[offset * width]);
242be920841SLaurent Vivier     }
243be920841SLaurent Vivier     return val;
244be920841SLaurent Vivier }
245be920841SLaurent Vivier 
246af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset,
247be920841SLaurent Vivier                         uint16_t val)
248be920841SLaurent Vivier {
249be920841SLaurent Vivier     if (s->big_endian) {
2503fe9a838SFinn Thain         if (width == 2) {
2513fe9a838SFinn Thain             s->data[offset * 2] = 0;
2523fe9a838SFinn Thain             s->data[offset * 2 + 1] = cpu_to_be16(val);
253be920841SLaurent Vivier         } else {
2543fe9a838SFinn Thain             s->data[offset] = cpu_to_be16(val);
2553fe9a838SFinn Thain         }
2563fe9a838SFinn Thain     } else {
2573fe9a838SFinn Thain         if (width == 2) {
2583fe9a838SFinn Thain             s->data[offset * 2] = cpu_to_le16(val);
2593fe9a838SFinn Thain             s->data[offset * 2 + 1] = 0;
2603fe9a838SFinn Thain         } else {
2613fe9a838SFinn Thain             s->data[offset] = cpu_to_le16(val);
2623fe9a838SFinn Thain         }
263be920841SLaurent Vivier     }
264be920841SLaurent Vivier }
265be920841SLaurent Vivier 
266a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
267a65f56eeSaurel32 {
268a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
269a65f56eeSaurel32 
270a65f56eeSaurel32 #ifdef DEBUG_SONIC
271a65f56eeSaurel32     if (level != s->irq_level) {
272a65f56eeSaurel32         s->irq_level = level;
273a65f56eeSaurel32         if (level) {
274a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
275a65f56eeSaurel32         } else {
276a65f56eeSaurel32             DPRINTF("lower irq\n");
277a65f56eeSaurel32         }
278a65f56eeSaurel32     }
279a65f56eeSaurel32 #endif
280a65f56eeSaurel32 
281a65f56eeSaurel32     qemu_set_irq(s->irq, level);
282a65f56eeSaurel32 }
283a65f56eeSaurel32 
2843df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
285a65f56eeSaurel32 {
286a65f56eeSaurel32     int width, size;
287a65f56eeSaurel32     uint16_t index = 0;
288a65f56eeSaurel32 
289a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
290a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
291a65f56eeSaurel32 
292a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
293a65f56eeSaurel32         /* Fill current entry */
29419f70347SPeter Maydell         address_space_read(&s->as, dp8393x_cdp(s),
29519f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
296af9f0be3SLaurent Vivier         s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff;
297af9f0be3SLaurent Vivier         s->cam[index][1] = dp8393x_get(s, width, 1) >> 8;
298af9f0be3SLaurent Vivier         s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff;
299af9f0be3SLaurent Vivier         s->cam[index][3] = dp8393x_get(s, width, 2) >> 8;
300af9f0be3SLaurent Vivier         s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff;
301af9f0be3SLaurent Vivier         s->cam[index][5] = dp8393x_get(s, width, 3) >> 8;
302a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
303a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
304a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
305a65f56eeSaurel32         /* Move to next entry */
306a65f56eeSaurel32         s->regs[SONIC_CDC]--;
307a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
308a65f56eeSaurel32         index++;
309a65f56eeSaurel32     }
310a65f56eeSaurel32 
311a65f56eeSaurel32     /* Read CAM enable */
31219f70347SPeter Maydell     address_space_read(&s->as, dp8393x_cdp(s),
31319f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
314af9f0be3SLaurent Vivier     s->regs[SONIC_CE] = dp8393x_get(s, width, 0);
315a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
316a65f56eeSaurel32 
317a65f56eeSaurel32     /* Done */
318a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
319a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
320a65f56eeSaurel32     dp8393x_update_irq(s);
321a65f56eeSaurel32 }
322a65f56eeSaurel32 
3233df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
324a65f56eeSaurel32 {
325a65f56eeSaurel32     int width, size;
326a65f56eeSaurel32 
327a65f56eeSaurel32     /* Read memory */
328a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
329a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
33019f70347SPeter Maydell     address_space_read(&s->as, dp8393x_rrp(s),
33119f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
332a65f56eeSaurel32 
333a65f56eeSaurel32     /* Update SONIC registers */
334af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0);
335af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1);
336af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2);
337af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3);
338a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
339a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
340a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
341a65f56eeSaurel32 
342a65f56eeSaurel32     /* Go to next entry */
343a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
344a65f56eeSaurel32 
345a65f56eeSaurel32     /* Handle wrap */
346a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
347a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
348a65f56eeSaurel32     }
349a65f56eeSaurel32 
350a65f56eeSaurel32     /* Check resource exhaustion */
351a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
352a65f56eeSaurel32     {
353a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
354a65f56eeSaurel32         dp8393x_update_irq(s);
355a65f56eeSaurel32     }
356a65f56eeSaurel32 }
357a65f56eeSaurel32 
3583df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
359a65f56eeSaurel32 {
360bc72ad67SAlex Bligh     timer_del(s->watchdog);
361a65f56eeSaurel32 
362a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
363a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
364a65f56eeSaurel32 }
365a65f56eeSaurel32 
3663df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
367a65f56eeSaurel32 {
368a65f56eeSaurel32     uint32_t ticks;
369a65f56eeSaurel32     int64_t delay;
370a65f56eeSaurel32 
371a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
372bc72ad67SAlex Bligh         timer_del(s->watchdog);
373a65f56eeSaurel32         return;
374a65f56eeSaurel32     }
375a65f56eeSaurel32 
376581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
377bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
37873bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
379bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
380a65f56eeSaurel32 }
381a65f56eeSaurel32 
3823df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
383a65f56eeSaurel32 {
384a65f56eeSaurel32     int64_t elapsed;
385a65f56eeSaurel32     uint32_t val;
386a65f56eeSaurel32 
387a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
388bc72ad67SAlex Bligh         timer_del(s->watchdog);
389a65f56eeSaurel32         return;
390a65f56eeSaurel32     }
391a65f56eeSaurel32 
392bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
393581f7b12SPeter Maydell     val = dp8393x_wt(s);
394a65f56eeSaurel32     val -= elapsed / 5000000;
395a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
396a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3973df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
398a65f56eeSaurel32 
399a65f56eeSaurel32 }
400a65f56eeSaurel32 
4013df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
402a65f56eeSaurel32 {
403a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
4043df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
405a65f56eeSaurel32 }
406a65f56eeSaurel32 
4073df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
408a65f56eeSaurel32 {
409a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
4103df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
411a65f56eeSaurel32 }
412a65f56eeSaurel32 
4134594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc);
4144594f93aSFam Zheng 
4153df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
416a65f56eeSaurel32 {
417a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
4184594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
4194594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
4204594f93aSFam Zheng     }
421a65f56eeSaurel32 }
422a65f56eeSaurel32 
4233df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
424a65f56eeSaurel32 {
425a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
426a65f56eeSaurel32 }
427a65f56eeSaurel32 
4283df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
429a65f56eeSaurel32 {
430b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
431a65f56eeSaurel32     int width, size;
432a65f56eeSaurel32     int tx_len, len;
433a65f56eeSaurel32     uint16_t i;
434a65f56eeSaurel32 
435a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
436a65f56eeSaurel32 
437a65f56eeSaurel32     while (1) {
438a65f56eeSaurel32         /* Read memory */
439a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
440a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
441581f7b12SPeter Maydell         DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
44219f70347SPeter Maydell         address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width,
44319f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
444a65f56eeSaurel32         tx_len = 0;
445a65f56eeSaurel32 
446a65f56eeSaurel32         /* Update registers */
447af9f0be3SLaurent Vivier         s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000;
448af9f0be3SLaurent Vivier         s->regs[SONIC_TPS] = dp8393x_get(s, width, 1);
449af9f0be3SLaurent Vivier         s->regs[SONIC_TFC] = dp8393x_get(s, width, 2);
450af9f0be3SLaurent Vivier         s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3);
451af9f0be3SLaurent Vivier         s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4);
452af9f0be3SLaurent Vivier         s->regs[SONIC_TFS] = dp8393x_get(s, width, 5);
453a65f56eeSaurel32 
454a65f56eeSaurel32         /* Handle programmable interrupt */
455a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
456a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
457a65f56eeSaurel32         } else {
458a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
459a65f56eeSaurel32         }
460a65f56eeSaurel32 
461a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
462a65f56eeSaurel32             /* Append fragment */
463a65f56eeSaurel32             len = s->regs[SONIC_TFS];
464a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
465a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
466a65f56eeSaurel32             }
46719f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
46819f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
469a65f56eeSaurel32             tx_len += len;
470a65f56eeSaurel32 
471a65f56eeSaurel32             i++;
472a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
473a65f56eeSaurel32                 /* Read next fragment details */
474a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
47519f70347SPeter Maydell                 address_space_read(&s->as,
47619f70347SPeter Maydell                                    dp8393x_ttda(s)
47719f70347SPeter Maydell                                    + sizeof(uint16_t) * width * (4 + 3 * i),
47819f70347SPeter Maydell                                    MEMTXATTRS_UNSPECIFIED, s->data,
47919f70347SPeter Maydell                                    size);
480af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0);
481af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1);
482af9f0be3SLaurent Vivier                 s->regs[SONIC_TFS] = dp8393x_get(s, width, 2);
483a65f56eeSaurel32             }
484a65f56eeSaurel32         }
485a65f56eeSaurel32 
486a65f56eeSaurel32         /* Handle Ethernet checksum */
487a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
488a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
489a65f56eeSaurel32              * which don't have one */
490a65f56eeSaurel32         } else {
491a65f56eeSaurel32             /* Remove existing FCS */
492a65f56eeSaurel32             tx_len -= 4;
493a65f56eeSaurel32         }
494a65f56eeSaurel32 
495a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
496a65f56eeSaurel32             /* Loopback */
497a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
498b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
499a65f56eeSaurel32                 s->loopback_packet = 1;
500b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
501a65f56eeSaurel32             }
502a65f56eeSaurel32         } else {
503a65f56eeSaurel32             /* Transmit packet */
504b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
505a65f56eeSaurel32         }
506a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
507a65f56eeSaurel32 
508a65f56eeSaurel32         /* Write status */
509af9f0be3SLaurent Vivier         dp8393x_put(s, width, 0,
510be920841SLaurent Vivier                     s->regs[SONIC_TCR] & 0x0fff); /* status */
511a65f56eeSaurel32         size = sizeof(uint16_t) * width;
51219f70347SPeter Maydell         address_space_write(&s->as, dp8393x_ttda(s),
51319f70347SPeter Maydell                             MEMTXATTRS_UNSPECIFIED, s->data, size);
514a65f56eeSaurel32 
515a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
516a65f56eeSaurel32             /* Read footer of packet */
517a65f56eeSaurel32             size = sizeof(uint16_t) * width;
51819f70347SPeter Maydell             address_space_read(&s->as,
51919f70347SPeter Maydell                                dp8393x_ttda(s)
52019f70347SPeter Maydell                                + sizeof(uint16_t) * width
52119f70347SPeter Maydell                                  * (4 + 3 * s->regs[SONIC_TFC]),
52219f70347SPeter Maydell                                MEMTXATTRS_UNSPECIFIED, s->data,
52319f70347SPeter Maydell                                size);
524af9f0be3SLaurent Vivier             s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0) & ~0x1;
52588f632fbSFinn Thain             if (dp8393x_get(s, width, 0) & SONIC_DESC_EOL) {
526a65f56eeSaurel32                 /* EOL detected */
527a65f56eeSaurel32                 break;
528a65f56eeSaurel32             }
529a65f56eeSaurel32         }
530a65f56eeSaurel32     }
531a65f56eeSaurel32 
532a65f56eeSaurel32     /* Done */
533a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
534a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
535a65f56eeSaurel32     dp8393x_update_irq(s);
536a65f56eeSaurel32 }
537a65f56eeSaurel32 
5383df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
539a65f56eeSaurel32 {
540a65f56eeSaurel32     /* Nothing to do */
541a65f56eeSaurel32 }
542a65f56eeSaurel32 
5433df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
544a65f56eeSaurel32 {
545a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
546a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
547a65f56eeSaurel32         return;
548a65f56eeSaurel32     }
549a65f56eeSaurel32 
550a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
551a65f56eeSaurel32 
552a65f56eeSaurel32     if (command & SONIC_CR_HTX)
5533df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
554a65f56eeSaurel32     if (command & SONIC_CR_TXP)
5553df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
556a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
5573df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
558a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
5593df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
560a65f56eeSaurel32     if (command & SONIC_CR_STP)
5613df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
562a65f56eeSaurel32     if (command & SONIC_CR_ST)
5633df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
564a65f56eeSaurel32     if (command & SONIC_CR_RST)
5653df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
566a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5673df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
568a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
569a3cce282SFinn Thain     }
570a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
5713df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
572a65f56eeSaurel32 }
573a65f56eeSaurel32 
57484689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
575a65f56eeSaurel32 {
57684689cbbSHervé Poussineau     dp8393xState *s = opaque;
57784689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
578a65f56eeSaurel32     uint16_t val = 0;
579a65f56eeSaurel32 
580a65f56eeSaurel32     switch (reg) {
581a65f56eeSaurel32         /* Update data before reading it */
582a65f56eeSaurel32         case SONIC_WT0:
583a65f56eeSaurel32         case SONIC_WT1:
5843df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
585a65f56eeSaurel32             val = s->regs[reg];
586a65f56eeSaurel32             break;
587a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
588a65f56eeSaurel32         case SONIC_CAP2:
589a65f56eeSaurel32         case SONIC_CAP1:
590a65f56eeSaurel32         case SONIC_CAP0:
591a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
592a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
593a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
594a65f56eeSaurel32             }
595a65f56eeSaurel32             break;
596a65f56eeSaurel32         /* All other registers have no special contrainst */
597a65f56eeSaurel32         default:
598a65f56eeSaurel32             val = s->regs[reg];
599a65f56eeSaurel32     }
600a65f56eeSaurel32 
601a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
602a65f56eeSaurel32 
6033fe9a838SFinn Thain     return s->big_endian ? val << 16 : val;
604a65f56eeSaurel32 }
605a65f56eeSaurel32 
60684689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
60784689cbbSHervé Poussineau                           unsigned int size)
608a65f56eeSaurel32 {
60984689cbbSHervé Poussineau     dp8393xState *s = opaque;
61084689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
6113fe9a838SFinn Thain     uint32_t val = s->big_endian ? data >> 16 : data;
61284689cbbSHervé Poussineau 
6133fe9a838SFinn Thain     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)val, reg_names[reg]);
614a65f56eeSaurel32 
615a65f56eeSaurel32     switch (reg) {
616a65f56eeSaurel32         /* Command register */
617a65f56eeSaurel32         case SONIC_CR:
6183fe9a838SFinn Thain             dp8393x_do_command(s, val);
619a65f56eeSaurel32             break;
620a65f56eeSaurel32         /* Prevent write to read-only registers */
621a65f56eeSaurel32         case SONIC_CAP2:
622a65f56eeSaurel32         case SONIC_CAP1:
623a65f56eeSaurel32         case SONIC_CAP0:
624a65f56eeSaurel32         case SONIC_SR:
625a65f56eeSaurel32         case SONIC_MDT:
626a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
627a65f56eeSaurel32             break;
628a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
629a65f56eeSaurel32         case SONIC_DCR:
630a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6313fe9a838SFinn Thain                 s->regs[reg] = val & 0xbfff;
632a65f56eeSaurel32             } else {
633a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
634a65f56eeSaurel32             }
635a65f56eeSaurel32             break;
636a65f56eeSaurel32         case SONIC_DCR2:
637a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6383fe9a838SFinn Thain                 s->regs[reg] = val & 0xf017;
639a65f56eeSaurel32             } else {
640a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
641a65f56eeSaurel32             }
642a65f56eeSaurel32             break;
643a65f56eeSaurel32         /* 12 lower bytes are Read Only */
644a65f56eeSaurel32         case SONIC_TCR:
6453fe9a838SFinn Thain             s->regs[reg] = val & 0xf000;
646a65f56eeSaurel32             break;
647a65f56eeSaurel32         /* 9 lower bytes are Read Only */
648a65f56eeSaurel32         case SONIC_RCR:
6493fe9a838SFinn Thain             s->regs[reg] = val & 0xffe0;
650a65f56eeSaurel32             break;
651a65f56eeSaurel32         /* Ignore most significant bit */
652a65f56eeSaurel32         case SONIC_IMR:
6533fe9a838SFinn Thain             s->regs[reg] = val & 0x7fff;
654a65f56eeSaurel32             dp8393x_update_irq(s);
655a65f56eeSaurel32             break;
656a65f56eeSaurel32         /* Clear bits by writing 1 to them */
657a65f56eeSaurel32         case SONIC_ISR:
6583fe9a838SFinn Thain             val &= s->regs[reg];
6593fe9a838SFinn Thain             s->regs[reg] &= ~val;
6603fe9a838SFinn Thain             if (val & SONIC_ISR_RBE) {
6613df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
662a65f56eeSaurel32             }
663a65f56eeSaurel32             dp8393x_update_irq(s);
6644594f93aSFam Zheng             if (dp8393x_can_receive(s->nic->ncs)) {
6654594f93aSFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
6664594f93aSFam Zheng             }
667a65f56eeSaurel32             break;
668*ea227027SFinn Thain         /* The guest is required to store aligned pointers here */
669a65f56eeSaurel32         case SONIC_RSA:
670a65f56eeSaurel32         case SONIC_REA:
671a65f56eeSaurel32         case SONIC_RRP:
672a65f56eeSaurel32         case SONIC_RWP:
673*ea227027SFinn Thain             if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
674*ea227027SFinn Thain                 s->regs[reg] = val & 0xfffc;
675*ea227027SFinn Thain             } else {
6763fe9a838SFinn Thain                 s->regs[reg] = val & 0xfffe;
677*ea227027SFinn Thain             }
678a65f56eeSaurel32             break;
679a65f56eeSaurel32         /* Invert written value for some registers */
680a65f56eeSaurel32         case SONIC_CRCT:
681a65f56eeSaurel32         case SONIC_FAET:
682a65f56eeSaurel32         case SONIC_MPT:
6833fe9a838SFinn Thain             s->regs[reg] = val ^ 0xffff;
684a65f56eeSaurel32             break;
685a65f56eeSaurel32         /* All other registers have no special contrainst */
686a65f56eeSaurel32         default:
6873fe9a838SFinn Thain             s->regs[reg] = val;
688a65f56eeSaurel32     }
689a65f56eeSaurel32 
690a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6913df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
692a65f56eeSaurel32     }
693a65f56eeSaurel32 }
694a65f56eeSaurel32 
69584689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
69684689cbbSHervé Poussineau     .read = dp8393x_read,
69784689cbbSHervé Poussineau     .write = dp8393x_write,
6983fe9a838SFinn Thain     .impl.min_access_size = 4,
6993fe9a838SFinn Thain     .impl.max_access_size = 4,
70084689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
70184689cbbSHervé Poussineau };
70284689cbbSHervé Poussineau 
703a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
704a65f56eeSaurel32 {
705a65f56eeSaurel32     dp8393xState *s = opaque;
706a65f56eeSaurel32 
707a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
708a65f56eeSaurel32         return;
709a65f56eeSaurel32     }
710a65f56eeSaurel32 
711a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
712a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
7133df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
714a65f56eeSaurel32 
715a65f56eeSaurel32     /* Signal underflow */
716a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
717a65f56eeSaurel32     dp8393x_update_irq(s);
718a65f56eeSaurel32 }
719a65f56eeSaurel32 
7203df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
721a65f56eeSaurel32 {
722cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
723a65f56eeSaurel32 
724a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
725a65f56eeSaurel32         return 0;
726a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
727a65f56eeSaurel32         return 0;
728a65f56eeSaurel32     return 1;
729a65f56eeSaurel32 }
730a65f56eeSaurel32 
7313df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
7323df5de64SHervé Poussineau                                   int size)
733a65f56eeSaurel32 {
734a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
735a65f56eeSaurel32     int i;
736a65f56eeSaurel32 
737a65f56eeSaurel32     /* Check promiscuous mode */
738a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
739a65f56eeSaurel32         return 0;
740a65f56eeSaurel32     }
741a65f56eeSaurel32 
742a65f56eeSaurel32     /* Check multicast packets */
743a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
744a65f56eeSaurel32         return SONIC_RCR_MC;
745a65f56eeSaurel32     }
746a65f56eeSaurel32 
747a65f56eeSaurel32     /* Check broadcast */
748a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
749a65f56eeSaurel32         return SONIC_RCR_BC;
750a65f56eeSaurel32     }
751a65f56eeSaurel32 
752a65f56eeSaurel32     /* Check CAM */
753a65f56eeSaurel32     for (i = 0; i < 16; i++) {
754a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
755a65f56eeSaurel32              /* Entry enabled */
756a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
757a65f56eeSaurel32                  return 0;
758a65f56eeSaurel32              }
759a65f56eeSaurel32         }
760a65f56eeSaurel32     }
761a65f56eeSaurel32 
762a65f56eeSaurel32     return -1;
763a65f56eeSaurel32 }
764a65f56eeSaurel32 
7653df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7669e3cd456SFinn Thain                                size_t pkt_size)
767a65f56eeSaurel32 {
768cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
769a65f56eeSaurel32     int packet_type;
770a65f56eeSaurel32     uint32_t available, address;
7719e3cd456SFinn Thain     int width, rx_len = pkt_size;
772a65f56eeSaurel32     uint32_t checksum;
7739e3cd456SFinn Thain     int size;
774a65f56eeSaurel32 
775a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
776a65f56eeSaurel32 
777a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
778a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
779a65f56eeSaurel32 
780ada74315SFinn Thain     if (pkt_size + 4 > dp8393x_rbwc(s) * 2) {
781ada74315SFinn Thain         DPRINTF("oversize packet, pkt_size is %d\n", pkt_size);
782ada74315SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_RBAE;
783ada74315SFinn Thain         dp8393x_update_irq(s);
784ada74315SFinn Thain         dp8393x_do_read_rra(s);
785ada74315SFinn Thain         return pkt_size;
786ada74315SFinn Thain     }
787ada74315SFinn Thain 
7889e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
789a65f56eeSaurel32     if (packet_type < 0) {
790a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
7914f1c942bSMark McLoughlin         return -1;
792a65f56eeSaurel32     }
793a65f56eeSaurel32 
794a65f56eeSaurel32     /* Check for EOL */
79588f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
796a65f56eeSaurel32         /* Are we still in resource exhaustion? */
797a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
798581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
79919f70347SPeter Maydell         address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED,
80019f70347SPeter Maydell                            s->data, size);
8015b0c98fcSFinn Thain         s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
8025b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
803a65f56eeSaurel32             /* Still EOL ; stop reception */
8044f1c942bSMark McLoughlin             return -1;
805a65f56eeSaurel32         }
8065b0c98fcSFinn Thain         /* Link has been updated by host */
8075b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
808a65f56eeSaurel32     }
809a65f56eeSaurel32 
810a65f56eeSaurel32     /* Save current position */
811a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
812a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
813a65f56eeSaurel32 
814a65f56eeSaurel32     /* Calculate the ethernet checksum */
815a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
816a65f56eeSaurel32 
817a65f56eeSaurel32     /* Put packet into RBA */
818581f7b12SPeter Maydell     DPRINTF("Receive packet at %08x\n", dp8393x_crba(s));
819581f7b12SPeter Maydell     address = dp8393x_crba(s);
82019f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
82119f70347SPeter Maydell                         buf, rx_len);
822a65f56eeSaurel32     address += rx_len;
82319f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
82419f70347SPeter Maydell                         &checksum, 4);
825bae112b8SFinn Thain     address += 4;
826a65f56eeSaurel32     rx_len += 4;
827a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
828a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
829581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
830a65f56eeSaurel32     available -= rx_len / 2;
831a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
832a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
833a65f56eeSaurel32 
834a65f56eeSaurel32     /* Update status */
835581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
836a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
837a65f56eeSaurel32     }
838a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
839a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
840a65f56eeSaurel32     if (s->loopback_packet) {
841a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
842a65f56eeSaurel32         s->loopback_packet = 0;
843a65f56eeSaurel32     }
844a65f56eeSaurel32 
845a65f56eeSaurel32     /* Write status to memory */
846581f7b12SPeter Maydell     DPRINTF("Write status at %08x\n", dp8393x_crda(s));
847af9f0be3SLaurent Vivier     dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */
848af9f0be3SLaurent Vivier     dp8393x_put(s, width, 1, rx_len); /* byte count */
849af9f0be3SLaurent Vivier     dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
850af9f0be3SLaurent Vivier     dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
851af9f0be3SLaurent Vivier     dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */
852a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
85319f70347SPeter Maydell     address_space_write(&s->as, dp8393x_crda(s),
85419f70347SPeter Maydell                         MEMTXATTRS_UNSPECIFIED,
85519f70347SPeter Maydell                         s->data, size);
856a65f56eeSaurel32 
8575b0c98fcSFinn Thain     /* Check link field */
858a65f56eeSaurel32     size = sizeof(uint16_t) * width;
85919f70347SPeter Maydell     address_space_read(&s->as,
86019f70347SPeter Maydell                        dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
86119f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
862af9f0be3SLaurent Vivier     s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
86388f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
864a65f56eeSaurel32         /* EOL detected */
865a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
866a65f56eeSaurel32     } else {
86746ffee9aSFinn Thain         /* Clear in_use */
86846ffee9aSFinn Thain         size = sizeof(uint16_t) * width;
86946ffee9aSFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
87046ffee9aSFinn Thain         dp8393x_put(s, width, 0, 0);
87146ffee9aSFinn Thain         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
87246ffee9aSFinn Thain                             s->data, size);
8735b0c98fcSFinn Thain 
8745b0c98fcSFinn Thain         /* Move to next descriptor */
875a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
876a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
877a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
878a65f56eeSaurel32 
879a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
880a65f56eeSaurel32             /* Read next RRA */
8813df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
882a65f56eeSaurel32         }
883a65f56eeSaurel32     }
884a65f56eeSaurel32 
885a65f56eeSaurel32     /* Done */
886a65f56eeSaurel32     dp8393x_update_irq(s);
8874f1c942bSMark McLoughlin 
8889e3cd456SFinn Thain     return pkt_size;
889a65f56eeSaurel32 }
890a65f56eeSaurel32 
891104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
892a65f56eeSaurel32 {
893104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
894bc72ad67SAlex Bligh     timer_del(s->watchdog);
895a65f56eeSaurel32 
896bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
897a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
898a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
899a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
900a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
901a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
902a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
903a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
904a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
905a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
906a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
907a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
908a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
909a65f56eeSaurel32 
910a65f56eeSaurel32     /* Network cable is connected */
911a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
912a65f56eeSaurel32 
913a65f56eeSaurel32     dp8393x_update_irq(s);
914a65f56eeSaurel32 }
915a65f56eeSaurel32 
91605f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
917f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
91805f41fe3SMark McLoughlin     .size = sizeof(NICState),
9193df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
9203df5de64SHervé Poussineau     .receive = dp8393x_receive,
92105f41fe3SMark McLoughlin };
92205f41fe3SMark McLoughlin 
923104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
924a65f56eeSaurel32 {
925104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
926104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
927a65f56eeSaurel32 
928104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
92989ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
930104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
931104655a5SHervé Poussineau }
932a65f56eeSaurel32 
933104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
934104655a5SHervé Poussineau {
935104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
93689ae0ff9SHervé Poussineau     int i, checksum;
93789ae0ff9SHervé Poussineau     uint8_t *prom;
93852579c68SHervé Poussineau     Error *local_err = NULL;
939a65f56eeSaurel32 
940104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
941104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
942104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
943104655a5SHervé Poussineau 
944104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
945104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
946104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
947104655a5SHervé Poussineau 
948bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
949a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
95089ae0ff9SHervé Poussineau 
9518fad0a65SPeter Maydell     memory_region_init_ram(&s->prom, OBJECT(dev),
95252579c68SHervé Poussineau                            "dp8393x-prom", SONIC_PROM_SIZE, &local_err);
95352579c68SHervé Poussineau     if (local_err) {
95452579c68SHervé Poussineau         error_propagate(errp, local_err);
95552579c68SHervé Poussineau         return;
95652579c68SHervé Poussineau     }
95752579c68SHervé Poussineau     memory_region_set_readonly(&s->prom, true);
95889ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
95989ae0ff9SHervé Poussineau     checksum = 0;
96089ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
96189ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
96289ae0ff9SHervé Poussineau         checksum += prom[i];
96389ae0ff9SHervé Poussineau         if (checksum > 0xff) {
96489ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
96589ae0ff9SHervé Poussineau         }
96689ae0ff9SHervé Poussineau     }
96789ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
968a65f56eeSaurel32 }
969104655a5SHervé Poussineau 
9701670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9711670735dSHervé Poussineau     .name = "dp8393x",
9721670735dSHervé Poussineau     .version_id = 0,
9731670735dSHervé Poussineau     .minimum_version_id = 0,
9741670735dSHervé Poussineau     .fields = (VMStateField []) {
9751670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
9761670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
9771670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9781670735dSHervé Poussineau     }
9791670735dSHervé Poussineau };
9801670735dSHervé Poussineau 
981104655a5SHervé Poussineau static Property dp8393x_properties[] = {
982104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
9833110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
9843110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
985104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
986be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
987104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
988104655a5SHervé Poussineau };
989104655a5SHervé Poussineau 
990104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
991104655a5SHervé Poussineau {
992104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
993104655a5SHervé Poussineau 
994104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
995104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
996104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
9971670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
9984f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
999104655a5SHervé Poussineau }
1000104655a5SHervé Poussineau 
1001104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
1002104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
1003104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
1004104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
1005104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
1006104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
1007104655a5SHervé Poussineau };
1008104655a5SHervé Poussineau 
1009104655a5SHervé Poussineau static void dp8393x_register_types(void)
1010104655a5SHervé Poussineau {
1011104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
1012104655a5SHervé Poussineau }
1013104655a5SHervé Poussineau 
1014104655a5SHervé Poussineau type_init(dp8393x_register_types)
1015