xref: /qemu/hw/net/dp8393x.c (revision d6454270575da1f16a8923c7cb240e46ef243f72)
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"
22104655a5SHervé Poussineau #include "hw/sysbus.h"
23*d6454270SMarkus Armbruster #include "migration/vmstate.h"
241422e32dSPaolo Bonzini #include "net/net.h"
25da34e65cSMarkus Armbruster #include "qapi/error.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
27104655a5SHervé Poussineau #include "qemu/timer.h"
28f2f62c4dSHervé Poussineau #include <zlib.h>
29a65f56eeSaurel32 
30a65f56eeSaurel32 //#define DEBUG_SONIC
31a65f56eeSaurel32 
3289ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
33a65f56eeSaurel32 
34a65f56eeSaurel32 #ifdef DEBUG_SONIC
35001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
36001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
37a65f56eeSaurel32 static const char* reg_names[] = {
38a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
39a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
40a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
41a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
42a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
43a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
44a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
45a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
46a65f56eeSaurel32 #else
47001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
48a65f56eeSaurel32 #endif
49a65f56eeSaurel32 
50001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
51001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
52a65f56eeSaurel32 
53a65f56eeSaurel32 #define SONIC_CR     0x00
54a65f56eeSaurel32 #define SONIC_DCR    0x01
55a65f56eeSaurel32 #define SONIC_RCR    0x02
56a65f56eeSaurel32 #define SONIC_TCR    0x03
57a65f56eeSaurel32 #define SONIC_IMR    0x04
58a65f56eeSaurel32 #define SONIC_ISR    0x05
59a65f56eeSaurel32 #define SONIC_UTDA   0x06
60a65f56eeSaurel32 #define SONIC_CTDA   0x07
61a65f56eeSaurel32 #define SONIC_TPS    0x08
62a65f56eeSaurel32 #define SONIC_TFC    0x09
63a65f56eeSaurel32 #define SONIC_TSA0   0x0a
64a65f56eeSaurel32 #define SONIC_TSA1   0x0b
65a65f56eeSaurel32 #define SONIC_TFS    0x0c
66a65f56eeSaurel32 #define SONIC_URDA   0x0d
67a65f56eeSaurel32 #define SONIC_CRDA   0x0e
68a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
69a65f56eeSaurel32 #define SONIC_CRBA1  0x10
70a65f56eeSaurel32 #define SONIC_RBWC0  0x11
71a65f56eeSaurel32 #define SONIC_RBWC1  0x12
72a65f56eeSaurel32 #define SONIC_EOBC   0x13
73a65f56eeSaurel32 #define SONIC_URRA   0x14
74a65f56eeSaurel32 #define SONIC_RSA    0x15
75a65f56eeSaurel32 #define SONIC_REA    0x16
76a65f56eeSaurel32 #define SONIC_RRP    0x17
77a65f56eeSaurel32 #define SONIC_RWP    0x18
78a65f56eeSaurel32 #define SONIC_TRBA0  0x19
79a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
80a65f56eeSaurel32 #define SONIC_LLFA   0x1f
81a65f56eeSaurel32 #define SONIC_TTDA   0x20
82a65f56eeSaurel32 #define SONIC_CEP    0x21
83a65f56eeSaurel32 #define SONIC_CAP2   0x22
84a65f56eeSaurel32 #define SONIC_CAP1   0x23
85a65f56eeSaurel32 #define SONIC_CAP0   0x24
86a65f56eeSaurel32 #define SONIC_CE     0x25
87a65f56eeSaurel32 #define SONIC_CDP    0x26
88a65f56eeSaurel32 #define SONIC_CDC    0x27
89a65f56eeSaurel32 #define SONIC_SR     0x28
90a65f56eeSaurel32 #define SONIC_WT0    0x29
91a65f56eeSaurel32 #define SONIC_WT1    0x2a
92a65f56eeSaurel32 #define SONIC_RSC    0x2b
93a65f56eeSaurel32 #define SONIC_CRCT   0x2c
94a65f56eeSaurel32 #define SONIC_FAET   0x2d
95a65f56eeSaurel32 #define SONIC_MPT    0x2e
96a65f56eeSaurel32 #define SONIC_MDT    0x2f
97a65f56eeSaurel32 #define SONIC_DCR2   0x3f
98a65f56eeSaurel32 
99a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
100a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
101a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
102a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
103a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
104a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
105a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
106a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
107a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
108a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
109a65f56eeSaurel32 
110a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
111a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
112a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
113a65f56eeSaurel32 
114a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
115a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
116a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
117a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
118a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
119a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
120a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
121a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
122a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
123a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
124a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
125a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
126a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
127a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
128a65f56eeSaurel32 
129a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
130a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
131a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
132a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
133a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
134a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
135a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
136a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
137a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
138a65f56eeSaurel32 
139a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
140a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
141a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
142a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
143a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
144a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
145a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
146a65f56eeSaurel32 
147104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
148104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
149104655a5SHervé Poussineau 
150a65f56eeSaurel32 typedef struct dp8393xState {
151104655a5SHervé Poussineau     SysBusDevice parent_obj;
152104655a5SHervé Poussineau 
153a65f56eeSaurel32     /* Hardware */
154104655a5SHervé Poussineau     uint8_t it_shift;
155a65f56eeSaurel32     qemu_irq irq;
156a65f56eeSaurel32 #ifdef DEBUG_SONIC
157a65f56eeSaurel32     int irq_level;
158a65f56eeSaurel32 #endif
159a65f56eeSaurel32     QEMUTimer *watchdog;
160a65f56eeSaurel32     int64_t wt_last_update;
16105f41fe3SMark McLoughlin     NICConf conf;
16205f41fe3SMark McLoughlin     NICState *nic;
163024e5bb6SAvi Kivity     MemoryRegion mmio;
16489ae0ff9SHervé Poussineau     MemoryRegion prom;
165a65f56eeSaurel32 
166a65f56eeSaurel32     /* Registers */
167a65f56eeSaurel32     uint8_t cam[16][6];
168a65f56eeSaurel32     uint16_t regs[0x40];
169a65f56eeSaurel32 
170a65f56eeSaurel32     /* Temporaries */
171a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
172a65f56eeSaurel32     int loopback_packet;
173a65f56eeSaurel32 
174a65f56eeSaurel32     /* Memory access */
175104655a5SHervé Poussineau     void *dma_mr;
176dd820513SHervé Poussineau     AddressSpace as;
177a65f56eeSaurel32 } dp8393xState;
178a65f56eeSaurel32 
179581f7b12SPeter Maydell /* Accessor functions for values which are formed by
180581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
181581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
182581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
183581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
184581f7b12SPeter Maydell  */
185581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
186581f7b12SPeter Maydell {
187581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
188581f7b12SPeter Maydell }
189581f7b12SPeter Maydell 
190581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
191581f7b12SPeter Maydell {
192581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
193581f7b12SPeter Maydell }
194581f7b12SPeter Maydell 
195581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
196581f7b12SPeter Maydell {
197581f7b12SPeter Maydell     return (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA];
198581f7b12SPeter Maydell }
199581f7b12SPeter Maydell 
200581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
201581f7b12SPeter Maydell {
202581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
203581f7b12SPeter Maydell }
204581f7b12SPeter Maydell 
205581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
206581f7b12SPeter Maydell {
207581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
208581f7b12SPeter Maydell }
209581f7b12SPeter Maydell 
210581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
211581f7b12SPeter Maydell {
212581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
213581f7b12SPeter Maydell }
214581f7b12SPeter Maydell 
215581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
216581f7b12SPeter Maydell {
217581f7b12SPeter Maydell     return (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA];
218581f7b12SPeter Maydell }
219581f7b12SPeter Maydell 
220581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
221581f7b12SPeter Maydell {
222581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
223581f7b12SPeter Maydell }
224581f7b12SPeter Maydell 
225a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
226a65f56eeSaurel32 {
227a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
228a65f56eeSaurel32 
229a65f56eeSaurel32 #ifdef DEBUG_SONIC
230a65f56eeSaurel32     if (level != s->irq_level) {
231a65f56eeSaurel32         s->irq_level = level;
232a65f56eeSaurel32         if (level) {
233a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
234a65f56eeSaurel32         } else {
235a65f56eeSaurel32             DPRINTF("lower irq\n");
236a65f56eeSaurel32         }
237a65f56eeSaurel32     }
238a65f56eeSaurel32 #endif
239a65f56eeSaurel32 
240a65f56eeSaurel32     qemu_set_irq(s->irq, level);
241a65f56eeSaurel32 }
242a65f56eeSaurel32 
2433df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
244a65f56eeSaurel32 {
245a65f56eeSaurel32     uint16_t data[8];
246a65f56eeSaurel32     int width, size;
247a65f56eeSaurel32     uint16_t index = 0;
248a65f56eeSaurel32 
249a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
250a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
251a65f56eeSaurel32 
252a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
253a65f56eeSaurel32         /* Fill current entry */
254581f7b12SPeter Maydell         address_space_rw(&s->as, dp8393x_cdp(s),
255dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
256a65f56eeSaurel32         s->cam[index][0] = data[1 * width] & 0xff;
257a65f56eeSaurel32         s->cam[index][1] = data[1 * width] >> 8;
258a65f56eeSaurel32         s->cam[index][2] = data[2 * width] & 0xff;
259a65f56eeSaurel32         s->cam[index][3] = data[2 * width] >> 8;
260a65f56eeSaurel32         s->cam[index][4] = data[3 * width] & 0xff;
261a65f56eeSaurel32         s->cam[index][5] = data[3 * width] >> 8;
262a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
263a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
264a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
265a65f56eeSaurel32         /* Move to next entry */
266a65f56eeSaurel32         s->regs[SONIC_CDC]--;
267a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
268a65f56eeSaurel32         index++;
269a65f56eeSaurel32     }
270a65f56eeSaurel32 
271a65f56eeSaurel32     /* Read CAM enable */
272581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_cdp(s),
273dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
274a65f56eeSaurel32     s->regs[SONIC_CE] = data[0 * width];
275a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
276a65f56eeSaurel32 
277a65f56eeSaurel32     /* Done */
278a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
279a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
280a65f56eeSaurel32     dp8393x_update_irq(s);
281a65f56eeSaurel32 }
282a65f56eeSaurel32 
2833df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
284a65f56eeSaurel32 {
285a65f56eeSaurel32     uint16_t data[8];
286a65f56eeSaurel32     int width, size;
287a65f56eeSaurel32 
288a65f56eeSaurel32     /* Read memory */
289a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
290a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
291581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_rrp(s),
292dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
293a65f56eeSaurel32 
294a65f56eeSaurel32     /* Update SONIC registers */
295a65f56eeSaurel32     s->regs[SONIC_CRBA0] = data[0 * width];
296a65f56eeSaurel32     s->regs[SONIC_CRBA1] = data[1 * width];
297a65f56eeSaurel32     s->regs[SONIC_RBWC0] = data[2 * width];
298a65f56eeSaurel32     s->regs[SONIC_RBWC1] = data[3 * width];
299a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
300a65f56eeSaurel32         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 
311a65f56eeSaurel32     /* Check resource exhaustion */
312a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
313a65f56eeSaurel32     {
314a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
315a65f56eeSaurel32         dp8393x_update_irq(s);
316a65f56eeSaurel32     }
317a65f56eeSaurel32 
318a65f56eeSaurel32     /* Done */
319a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
320a65f56eeSaurel32 }
321a65f56eeSaurel32 
3223df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
323a65f56eeSaurel32 {
324bc72ad67SAlex Bligh     timer_del(s->watchdog);
325a65f56eeSaurel32 
326a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
327a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
328a65f56eeSaurel32 }
329a65f56eeSaurel32 
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 
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 
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 
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 
3774594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc);
3784594f93aSFam Zheng 
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 
3873df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
388a65f56eeSaurel32 {
389a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
390a65f56eeSaurel32 }
391a65f56eeSaurel32 
3923df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
393a65f56eeSaurel32 {
394b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
395a65f56eeSaurel32     uint16_t data[12];
396a65f56eeSaurel32     int width, size;
397a65f56eeSaurel32     int tx_len, len;
398a65f56eeSaurel32     uint16_t i;
399a65f56eeSaurel32 
400a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
401a65f56eeSaurel32 
402a65f56eeSaurel32     while (1) {
403a65f56eeSaurel32         /* Read memory */
404a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
405a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
406581f7b12SPeter Maydell         DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
407dd820513SHervé Poussineau         address_space_rw(&s->as,
408581f7b12SPeter Maydell             dp8393x_ttda(s) + sizeof(uint16_t) * width,
409dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
410a65f56eeSaurel32         tx_len = 0;
411a65f56eeSaurel32 
412a65f56eeSaurel32         /* Update registers */
413a65f56eeSaurel32         s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
414a65f56eeSaurel32         s->regs[SONIC_TPS] = data[1 * width];
415a65f56eeSaurel32         s->regs[SONIC_TFC] = data[2 * width];
416a65f56eeSaurel32         s->regs[SONIC_TSA0] = data[3 * width];
417a65f56eeSaurel32         s->regs[SONIC_TSA1] = data[4 * width];
418a65f56eeSaurel32         s->regs[SONIC_TFS] = data[5 * width];
419a65f56eeSaurel32 
420a65f56eeSaurel32         /* Handle programmable interrupt */
421a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
422a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
423a65f56eeSaurel32         } else {
424a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
425a65f56eeSaurel32         }
426a65f56eeSaurel32 
427a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
428a65f56eeSaurel32             /* Append fragment */
429a65f56eeSaurel32             len = s->regs[SONIC_TFS];
430a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
431a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
432a65f56eeSaurel32             }
433581f7b12SPeter Maydell             address_space_rw(&s->as, dp8393x_tsa(s),
434dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
435a65f56eeSaurel32             tx_len += len;
436a65f56eeSaurel32 
437a65f56eeSaurel32             i++;
438a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
439a65f56eeSaurel32                 /* Read next fragment details */
440a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
441dd820513SHervé Poussineau                 address_space_rw(&s->as,
442581f7b12SPeter Maydell                     dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width,
443dd820513SHervé Poussineau                     MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
444a65f56eeSaurel32                 s->regs[SONIC_TSA0] = data[0 * width];
445a65f56eeSaurel32                 s->regs[SONIC_TSA1] = data[1 * width];
446a65f56eeSaurel32                 s->regs[SONIC_TFS] = data[2 * width];
447a65f56eeSaurel32             }
448a65f56eeSaurel32         }
449a65f56eeSaurel32 
450a65f56eeSaurel32         /* Handle Ethernet checksum */
451a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
452a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
453a65f56eeSaurel32              * which don't have one */
454a65f56eeSaurel32         } else {
455a65f56eeSaurel32             /* Remove existing FCS */
456a65f56eeSaurel32             tx_len -= 4;
457a65f56eeSaurel32         }
458a65f56eeSaurel32 
459a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
460a65f56eeSaurel32             /* Loopback */
461a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
462b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
463a65f56eeSaurel32                 s->loopback_packet = 1;
464b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
465a65f56eeSaurel32             }
466a65f56eeSaurel32         } else {
467a65f56eeSaurel32             /* Transmit packet */
468b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
469a65f56eeSaurel32         }
470a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
471a65f56eeSaurel32 
472a65f56eeSaurel32         /* Write status */
473a65f56eeSaurel32         data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
474a65f56eeSaurel32         size = sizeof(uint16_t) * width;
475dd820513SHervé Poussineau         address_space_rw(&s->as,
476581f7b12SPeter Maydell             dp8393x_ttda(s),
477dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
478a65f56eeSaurel32 
479a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
480a65f56eeSaurel32             /* Read footer of packet */
481a65f56eeSaurel32             size = sizeof(uint16_t) * width;
482dd820513SHervé Poussineau             address_space_rw(&s->as,
483581f7b12SPeter Maydell                 dp8393x_ttda(s) +
484581f7b12SPeter Maydell                              sizeof(uint16_t) *
485581f7b12SPeter Maydell                              (4 + 3 * s->regs[SONIC_TFC]) * width,
486dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
487a65f56eeSaurel32             s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
488a65f56eeSaurel32             if (data[0 * width] & 0x1) {
489a65f56eeSaurel32                 /* EOL detected */
490a65f56eeSaurel32                 break;
491a65f56eeSaurel32             }
492a65f56eeSaurel32         }
493a65f56eeSaurel32     }
494a65f56eeSaurel32 
495a65f56eeSaurel32     /* Done */
496a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
497a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
498a65f56eeSaurel32     dp8393x_update_irq(s);
499a65f56eeSaurel32 }
500a65f56eeSaurel32 
5013df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
502a65f56eeSaurel32 {
503a65f56eeSaurel32     /* Nothing to do */
504a65f56eeSaurel32 }
505a65f56eeSaurel32 
5063df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
507a65f56eeSaurel32 {
508a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
509a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
510a65f56eeSaurel32         return;
511a65f56eeSaurel32     }
512a65f56eeSaurel32 
513a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
514a65f56eeSaurel32 
515a65f56eeSaurel32     if (command & SONIC_CR_HTX)
5163df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
517a65f56eeSaurel32     if (command & SONIC_CR_TXP)
5183df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
519a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
5203df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
521a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
5223df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
523a65f56eeSaurel32     if (command & SONIC_CR_STP)
5243df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
525a65f56eeSaurel32     if (command & SONIC_CR_ST)
5263df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
527a65f56eeSaurel32     if (command & SONIC_CR_RST)
5283df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
529a65f56eeSaurel32     if (command & SONIC_CR_RRRA)
5303df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
531a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
5323df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
533a65f56eeSaurel32 }
534a65f56eeSaurel32 
53584689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
536a65f56eeSaurel32 {
53784689cbbSHervé Poussineau     dp8393xState *s = opaque;
53884689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
539a65f56eeSaurel32     uint16_t val = 0;
540a65f56eeSaurel32 
541a65f56eeSaurel32     switch (reg) {
542a65f56eeSaurel32         /* Update data before reading it */
543a65f56eeSaurel32         case SONIC_WT0:
544a65f56eeSaurel32         case SONIC_WT1:
5453df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
546a65f56eeSaurel32             val = s->regs[reg];
547a65f56eeSaurel32             break;
548a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
549a65f56eeSaurel32         case SONIC_CAP2:
550a65f56eeSaurel32         case SONIC_CAP1:
551a65f56eeSaurel32         case SONIC_CAP0:
552a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
553a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
554a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
555a65f56eeSaurel32             }
556a65f56eeSaurel32             break;
557a65f56eeSaurel32         /* All other registers have no special contrainst */
558a65f56eeSaurel32         default:
559a65f56eeSaurel32             val = s->regs[reg];
560a65f56eeSaurel32     }
561a65f56eeSaurel32 
562a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
563a65f56eeSaurel32 
564a65f56eeSaurel32     return val;
565a65f56eeSaurel32 }
566a65f56eeSaurel32 
56784689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
56884689cbbSHervé Poussineau                           unsigned int size)
569a65f56eeSaurel32 {
57084689cbbSHervé Poussineau     dp8393xState *s = opaque;
57184689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
57284689cbbSHervé Poussineau 
57384689cbbSHervé Poussineau     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
574a65f56eeSaurel32 
575a65f56eeSaurel32     switch (reg) {
576a65f56eeSaurel32         /* Command register */
577a65f56eeSaurel32         case SONIC_CR:
5783df5de64SHervé Poussineau             dp8393x_do_command(s, data);
579a65f56eeSaurel32             break;
580a65f56eeSaurel32         /* Prevent write to read-only registers */
581a65f56eeSaurel32         case SONIC_CAP2:
582a65f56eeSaurel32         case SONIC_CAP1:
583a65f56eeSaurel32         case SONIC_CAP0:
584a65f56eeSaurel32         case SONIC_SR:
585a65f56eeSaurel32         case SONIC_MDT:
586a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
587a65f56eeSaurel32             break;
588a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
589a65f56eeSaurel32         case SONIC_DCR:
590a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
59184689cbbSHervé Poussineau                 s->regs[reg] = data & 0xbfff;
592a65f56eeSaurel32             } else {
593a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
594a65f56eeSaurel32             }
595a65f56eeSaurel32             break;
596a65f56eeSaurel32         case SONIC_DCR2:
597a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
59884689cbbSHervé Poussineau                 s->regs[reg] = data & 0xf017;
599a65f56eeSaurel32             } else {
600a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
601a65f56eeSaurel32             }
602a65f56eeSaurel32             break;
603a65f56eeSaurel32         /* 12 lower bytes are Read Only */
604a65f56eeSaurel32         case SONIC_TCR:
60584689cbbSHervé Poussineau             s->regs[reg] = data & 0xf000;
606a65f56eeSaurel32             break;
607a65f56eeSaurel32         /* 9 lower bytes are Read Only */
608a65f56eeSaurel32         case SONIC_RCR:
60984689cbbSHervé Poussineau             s->regs[reg] = data & 0xffe0;
610a65f56eeSaurel32             break;
611a65f56eeSaurel32         /* Ignore most significant bit */
612a65f56eeSaurel32         case SONIC_IMR:
61384689cbbSHervé Poussineau             s->regs[reg] = data & 0x7fff;
614a65f56eeSaurel32             dp8393x_update_irq(s);
615a65f56eeSaurel32             break;
616a65f56eeSaurel32         /* Clear bits by writing 1 to them */
617a65f56eeSaurel32         case SONIC_ISR:
61884689cbbSHervé Poussineau             data &= s->regs[reg];
61984689cbbSHervé Poussineau             s->regs[reg] &= ~data;
62084689cbbSHervé Poussineau             if (data & SONIC_ISR_RBE) {
6213df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
622a65f56eeSaurel32             }
623a65f56eeSaurel32             dp8393x_update_irq(s);
6244594f93aSFam Zheng             if (dp8393x_can_receive(s->nic->ncs)) {
6254594f93aSFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
6264594f93aSFam Zheng             }
627a65f56eeSaurel32             break;
628a65f56eeSaurel32         /* Ignore least significant bit */
629a65f56eeSaurel32         case SONIC_RSA:
630a65f56eeSaurel32         case SONIC_REA:
631a65f56eeSaurel32         case SONIC_RRP:
632a65f56eeSaurel32         case SONIC_RWP:
63384689cbbSHervé Poussineau             s->regs[reg] = data & 0xfffe;
634a65f56eeSaurel32             break;
635a65f56eeSaurel32         /* Invert written value for some registers */
636a65f56eeSaurel32         case SONIC_CRCT:
637a65f56eeSaurel32         case SONIC_FAET:
638a65f56eeSaurel32         case SONIC_MPT:
63984689cbbSHervé Poussineau             s->regs[reg] = data ^ 0xffff;
640a65f56eeSaurel32             break;
641a65f56eeSaurel32         /* All other registers have no special contrainst */
642a65f56eeSaurel32         default:
64384689cbbSHervé Poussineau             s->regs[reg] = data;
644a65f56eeSaurel32     }
645a65f56eeSaurel32 
646a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6473df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
648a65f56eeSaurel32     }
649a65f56eeSaurel32 }
650a65f56eeSaurel32 
65184689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
65284689cbbSHervé Poussineau     .read = dp8393x_read,
65384689cbbSHervé Poussineau     .write = dp8393x_write,
65484689cbbSHervé Poussineau     .impl.min_access_size = 2,
65584689cbbSHervé Poussineau     .impl.max_access_size = 2,
65684689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
65784689cbbSHervé Poussineau };
65884689cbbSHervé Poussineau 
659a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
660a65f56eeSaurel32 {
661a65f56eeSaurel32     dp8393xState *s = opaque;
662a65f56eeSaurel32 
663a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
664a65f56eeSaurel32         return;
665a65f56eeSaurel32     }
666a65f56eeSaurel32 
667a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
668a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
6693df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
670a65f56eeSaurel32 
671a65f56eeSaurel32     /* Signal underflow */
672a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
673a65f56eeSaurel32     dp8393x_update_irq(s);
674a65f56eeSaurel32 }
675a65f56eeSaurel32 
6763df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
677a65f56eeSaurel32 {
678cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
679a65f56eeSaurel32 
680a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
681a65f56eeSaurel32         return 0;
682a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
683a65f56eeSaurel32         return 0;
684a65f56eeSaurel32     return 1;
685a65f56eeSaurel32 }
686a65f56eeSaurel32 
6873df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
6883df5de64SHervé Poussineau                                   int size)
689a65f56eeSaurel32 {
690a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
691a65f56eeSaurel32     int i;
692a65f56eeSaurel32 
693a65f56eeSaurel32     /* Check promiscuous mode */
694a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
695a65f56eeSaurel32         return 0;
696a65f56eeSaurel32     }
697a65f56eeSaurel32 
698a65f56eeSaurel32     /* Check multicast packets */
699a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
700a65f56eeSaurel32         return SONIC_RCR_MC;
701a65f56eeSaurel32     }
702a65f56eeSaurel32 
703a65f56eeSaurel32     /* Check broadcast */
704a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !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 
7213df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7223df5de64SHervé Poussineau                                size_t size)
723a65f56eeSaurel32 {
724cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
725a65f56eeSaurel32     uint16_t data[10];
726a65f56eeSaurel32     int packet_type;
727a65f56eeSaurel32     uint32_t available, address;
728a65f56eeSaurel32     int width, rx_len = size;
729a65f56eeSaurel32     uint32_t checksum;
730a65f56eeSaurel32 
731a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
732a65f56eeSaurel32 
733a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
734a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
735a65f56eeSaurel32 
7363df5de64SHervé Poussineau     packet_type = dp8393x_receive_filter(s, buf, size);
737a65f56eeSaurel32     if (packet_type < 0) {
738a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
7394f1c942bSMark McLoughlin         return -1;
740a65f56eeSaurel32     }
741a65f56eeSaurel32 
742a65f56eeSaurel32     /* XXX: Check byte ordering */
743a65f56eeSaurel32 
744a65f56eeSaurel32     /* Check for EOL */
745a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
746a65f56eeSaurel32         /* Are we still in resource exhaustion? */
747a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
748581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
749dd820513SHervé Poussineau         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
750dd820513SHervé Poussineau                          (uint8_t *)data, size, 0);
751a65f56eeSaurel32         if (data[0 * width] & 0x1) {
752a65f56eeSaurel32             /* Still EOL ; stop reception */
7534f1c942bSMark McLoughlin             return -1;
754a65f56eeSaurel32         } else {
755a65f56eeSaurel32             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
756a65f56eeSaurel32         }
757a65f56eeSaurel32     }
758a65f56eeSaurel32 
759a65f56eeSaurel32     /* Save current position */
760a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
761a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
762a65f56eeSaurel32 
763a65f56eeSaurel32     /* Calculate the ethernet checksum */
764a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
765a65f56eeSaurel32 
766a65f56eeSaurel32     /* Put packet into RBA */
767581f7b12SPeter Maydell     DPRINTF("Receive packet at %08x\n", dp8393x_crba(s));
768581f7b12SPeter Maydell     address = dp8393x_crba(s);
769dd820513SHervé Poussineau     address_space_rw(&s->as, address,
770dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
771a65f56eeSaurel32     address += rx_len;
772dd820513SHervé Poussineau     address_space_rw(&s->as, address,
773dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
774a65f56eeSaurel32     rx_len += 4;
775a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
776a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
777581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
778a65f56eeSaurel32     available -= rx_len / 2;
779a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
780a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
781a65f56eeSaurel32 
782a65f56eeSaurel32     /* Update status */
783581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
784a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
785a65f56eeSaurel32     }
786a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
787a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
788a65f56eeSaurel32     if (s->loopback_packet) {
789a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
790a65f56eeSaurel32         s->loopback_packet = 0;
791a65f56eeSaurel32     }
792a65f56eeSaurel32 
793a65f56eeSaurel32     /* Write status to memory */
794581f7b12SPeter Maydell     DPRINTF("Write status at %08x\n", dp8393x_crda(s));
795a65f56eeSaurel32     data[0 * width] = s->regs[SONIC_RCR]; /* status */
796a65f56eeSaurel32     data[1 * width] = rx_len; /* byte count */
797a65f56eeSaurel32     data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
798a65f56eeSaurel32     data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
799a65f56eeSaurel32     data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
800a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
801581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_crda(s),
802dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
803a65f56eeSaurel32 
804a65f56eeSaurel32     /* Move to next descriptor */
805a65f56eeSaurel32     size = sizeof(uint16_t) * width;
806581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
807dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
808a65f56eeSaurel32     s->regs[SONIC_LLFA] = data[0 * width];
809a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
810a65f56eeSaurel32         /* EOL detected */
811a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
812a65f56eeSaurel32     } else {
813a65f56eeSaurel32         data[0 * width] = 0; /* in_use */
814581f7b12SPeter Maydell         address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 6 * width,
815409b52bfSHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
816a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
817a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
818a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
819a65f56eeSaurel32 
820a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
821a65f56eeSaurel32             /* Read next RRA */
8223df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
823a65f56eeSaurel32         }
824a65f56eeSaurel32     }
825a65f56eeSaurel32 
826a65f56eeSaurel32     /* Done */
827a65f56eeSaurel32     dp8393x_update_irq(s);
8284f1c942bSMark McLoughlin 
8294f1c942bSMark McLoughlin     return size;
830a65f56eeSaurel32 }
831a65f56eeSaurel32 
832104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
833a65f56eeSaurel32 {
834104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
835bc72ad67SAlex Bligh     timer_del(s->watchdog);
836a65f56eeSaurel32 
837bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
838a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
839a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
840a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
841a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
842a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
843a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
844a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
845a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
846a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
847a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
848a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
849a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
850a65f56eeSaurel32 
851a65f56eeSaurel32     /* Network cable is connected */
852a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
853a65f56eeSaurel32 
854a65f56eeSaurel32     dp8393x_update_irq(s);
855a65f56eeSaurel32 }
856a65f56eeSaurel32 
85705f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
858f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
85905f41fe3SMark McLoughlin     .size = sizeof(NICState),
8603df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8613df5de64SHervé Poussineau     .receive = dp8393x_receive,
86205f41fe3SMark McLoughlin };
86305f41fe3SMark McLoughlin 
864104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
865a65f56eeSaurel32 {
866104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
867104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
868a65f56eeSaurel32 
869104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
87089ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
871104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
872104655a5SHervé Poussineau }
873a65f56eeSaurel32 
874104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
875104655a5SHervé Poussineau {
876104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
87789ae0ff9SHervé Poussineau     int i, checksum;
87889ae0ff9SHervé Poussineau     uint8_t *prom;
87952579c68SHervé Poussineau     Error *local_err = NULL;
880a65f56eeSaurel32 
881104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
882104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
883104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
884104655a5SHervé Poussineau 
885104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
886104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
887104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
888104655a5SHervé Poussineau 
889bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
890a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
89189ae0ff9SHervé Poussineau 
8928fad0a65SPeter Maydell     memory_region_init_ram(&s->prom, OBJECT(dev),
89352579c68SHervé Poussineau                            "dp8393x-prom", SONIC_PROM_SIZE, &local_err);
89452579c68SHervé Poussineau     if (local_err) {
89552579c68SHervé Poussineau         error_propagate(errp, local_err);
89652579c68SHervé Poussineau         return;
89752579c68SHervé Poussineau     }
89852579c68SHervé Poussineau     memory_region_set_readonly(&s->prom, true);
89989ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
90089ae0ff9SHervé Poussineau     checksum = 0;
90189ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
90289ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
90389ae0ff9SHervé Poussineau         checksum += prom[i];
90489ae0ff9SHervé Poussineau         if (checksum > 0xff) {
90589ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
90689ae0ff9SHervé Poussineau         }
90789ae0ff9SHervé Poussineau     }
90889ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
909a65f56eeSaurel32 }
910104655a5SHervé Poussineau 
9111670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9121670735dSHervé Poussineau     .name = "dp8393x",
9131670735dSHervé Poussineau     .version_id = 0,
9141670735dSHervé Poussineau     .minimum_version_id = 0,
9151670735dSHervé Poussineau     .fields = (VMStateField []) {
9161670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
9171670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
9181670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9191670735dSHervé Poussineau     }
9201670735dSHervé Poussineau };
9211670735dSHervé Poussineau 
922104655a5SHervé Poussineau static Property dp8393x_properties[] = {
923104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
924104655a5SHervé Poussineau     DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
925104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
926104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
927104655a5SHervé Poussineau };
928104655a5SHervé Poussineau 
929104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
930104655a5SHervé Poussineau {
931104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
932104655a5SHervé Poussineau 
933104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
934104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
935104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
9361670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
937104655a5SHervé Poussineau     dc->props = dp8393x_properties;
938f6351288SHervé Poussineau     /* Reason: dma_mr property can't be set */
939e90f2a8cSEduardo Habkost     dc->user_creatable = false;
940104655a5SHervé Poussineau }
941104655a5SHervé Poussineau 
942104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
943104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
944104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
945104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
946104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
947104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
948104655a5SHervé Poussineau };
949104655a5SHervé Poussineau 
950104655a5SHervé Poussineau static void dp8393x_register_types(void)
951104655a5SHervé Poussineau {
952104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
953104655a5SHervé Poussineau }
954104655a5SHervé Poussineau 
955104655a5SHervé Poussineau type_init(dp8393x_register_types)
956