xref: /qemu/hw/net/dp8393x.c (revision da34e65cb4025728566d6504a99916f6e7e1dd6a)
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"
21104655a5SHervé Poussineau #include "hw/sysbus.h"
22104655a5SHervé Poussineau #include "hw/devices.h"
231422e32dSPaolo Bonzini #include "net/net.h"
24*da34e65cSMarkus Armbruster #include "qapi/error.h"
25104655a5SHervé Poussineau #include "qemu/timer.h"
26f2f62c4dSHervé Poussineau #include <zlib.h>
27a65f56eeSaurel32 
28a65f56eeSaurel32 //#define DEBUG_SONIC
29a65f56eeSaurel32 
3089ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
31a65f56eeSaurel32 
32a65f56eeSaurel32 #ifdef DEBUG_SONIC
33001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
34001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
35a65f56eeSaurel32 static const char* reg_names[] = {
36a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
37a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
38a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
39a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
40a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
41a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
42a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
43a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
44a65f56eeSaurel32 #else
45001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
46a65f56eeSaurel32 #endif
47a65f56eeSaurel32 
48001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
49001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
50a65f56eeSaurel32 
51a65f56eeSaurel32 #define SONIC_CR     0x00
52a65f56eeSaurel32 #define SONIC_DCR    0x01
53a65f56eeSaurel32 #define SONIC_RCR    0x02
54a65f56eeSaurel32 #define SONIC_TCR    0x03
55a65f56eeSaurel32 #define SONIC_IMR    0x04
56a65f56eeSaurel32 #define SONIC_ISR    0x05
57a65f56eeSaurel32 #define SONIC_UTDA   0x06
58a65f56eeSaurel32 #define SONIC_CTDA   0x07
59a65f56eeSaurel32 #define SONIC_TPS    0x08
60a65f56eeSaurel32 #define SONIC_TFC    0x09
61a65f56eeSaurel32 #define SONIC_TSA0   0x0a
62a65f56eeSaurel32 #define SONIC_TSA1   0x0b
63a65f56eeSaurel32 #define SONIC_TFS    0x0c
64a65f56eeSaurel32 #define SONIC_URDA   0x0d
65a65f56eeSaurel32 #define SONIC_CRDA   0x0e
66a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
67a65f56eeSaurel32 #define SONIC_CRBA1  0x10
68a65f56eeSaurel32 #define SONIC_RBWC0  0x11
69a65f56eeSaurel32 #define SONIC_RBWC1  0x12
70a65f56eeSaurel32 #define SONIC_EOBC   0x13
71a65f56eeSaurel32 #define SONIC_URRA   0x14
72a65f56eeSaurel32 #define SONIC_RSA    0x15
73a65f56eeSaurel32 #define SONIC_REA    0x16
74a65f56eeSaurel32 #define SONIC_RRP    0x17
75a65f56eeSaurel32 #define SONIC_RWP    0x18
76a65f56eeSaurel32 #define SONIC_TRBA0  0x19
77a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
78a65f56eeSaurel32 #define SONIC_LLFA   0x1f
79a65f56eeSaurel32 #define SONIC_TTDA   0x20
80a65f56eeSaurel32 #define SONIC_CEP    0x21
81a65f56eeSaurel32 #define SONIC_CAP2   0x22
82a65f56eeSaurel32 #define SONIC_CAP1   0x23
83a65f56eeSaurel32 #define SONIC_CAP0   0x24
84a65f56eeSaurel32 #define SONIC_CE     0x25
85a65f56eeSaurel32 #define SONIC_CDP    0x26
86a65f56eeSaurel32 #define SONIC_CDC    0x27
87a65f56eeSaurel32 #define SONIC_SR     0x28
88a65f56eeSaurel32 #define SONIC_WT0    0x29
89a65f56eeSaurel32 #define SONIC_WT1    0x2a
90a65f56eeSaurel32 #define SONIC_RSC    0x2b
91a65f56eeSaurel32 #define SONIC_CRCT   0x2c
92a65f56eeSaurel32 #define SONIC_FAET   0x2d
93a65f56eeSaurel32 #define SONIC_MPT    0x2e
94a65f56eeSaurel32 #define SONIC_MDT    0x2f
95a65f56eeSaurel32 #define SONIC_DCR2   0x3f
96a65f56eeSaurel32 
97a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
98a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
99a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
100a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
101a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
102a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
103a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
104a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
105a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
106a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
107a65f56eeSaurel32 
108a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
109a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
110a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
111a65f56eeSaurel32 
112a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
113a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
114a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
115a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
116a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
117a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
118a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
119a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
120a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
121a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
122a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
123a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
124a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
125a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
126a65f56eeSaurel32 
127a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
128a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
129a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
130a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
131a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
132a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
133a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
134a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
135a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
136a65f56eeSaurel32 
137a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
138a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
139a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
140a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
141a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
142a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
143a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
144a65f56eeSaurel32 
145104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
146104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
147104655a5SHervé Poussineau 
148a65f56eeSaurel32 typedef struct dp8393xState {
149104655a5SHervé Poussineau     SysBusDevice parent_obj;
150104655a5SHervé Poussineau 
151a65f56eeSaurel32     /* Hardware */
152104655a5SHervé Poussineau     uint8_t it_shift;
153a65f56eeSaurel32     qemu_irq irq;
154a65f56eeSaurel32 #ifdef DEBUG_SONIC
155a65f56eeSaurel32     int irq_level;
156a65f56eeSaurel32 #endif
157a65f56eeSaurel32     QEMUTimer *watchdog;
158a65f56eeSaurel32     int64_t wt_last_update;
15905f41fe3SMark McLoughlin     NICConf conf;
16005f41fe3SMark McLoughlin     NICState *nic;
161024e5bb6SAvi Kivity     MemoryRegion mmio;
16289ae0ff9SHervé Poussineau     MemoryRegion prom;
163a65f56eeSaurel32 
164a65f56eeSaurel32     /* Registers */
165a65f56eeSaurel32     uint8_t cam[16][6];
166a65f56eeSaurel32     uint16_t regs[0x40];
167a65f56eeSaurel32 
168a65f56eeSaurel32     /* Temporaries */
169a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
170a65f56eeSaurel32     int loopback_packet;
171a65f56eeSaurel32 
172a65f56eeSaurel32     /* Memory access */
173104655a5SHervé Poussineau     void *dma_mr;
174dd820513SHervé Poussineau     AddressSpace as;
175a65f56eeSaurel32 } dp8393xState;
176a65f56eeSaurel32 
177a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
178a65f56eeSaurel32 {
179a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
180a65f56eeSaurel32 
181a65f56eeSaurel32 #ifdef DEBUG_SONIC
182a65f56eeSaurel32     if (level != s->irq_level) {
183a65f56eeSaurel32         s->irq_level = level;
184a65f56eeSaurel32         if (level) {
185a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
186a65f56eeSaurel32         } else {
187a65f56eeSaurel32             DPRINTF("lower irq\n");
188a65f56eeSaurel32         }
189a65f56eeSaurel32     }
190a65f56eeSaurel32 #endif
191a65f56eeSaurel32 
192a65f56eeSaurel32     qemu_set_irq(s->irq, level);
193a65f56eeSaurel32 }
194a65f56eeSaurel32 
1953df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
196a65f56eeSaurel32 {
197a65f56eeSaurel32     uint16_t data[8];
198a65f56eeSaurel32     int width, size;
199a65f56eeSaurel32     uint16_t index = 0;
200a65f56eeSaurel32 
201a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
202a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
203a65f56eeSaurel32 
204a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
205a65f56eeSaurel32         /* Fill current entry */
206dd820513SHervé Poussineau         address_space_rw(&s->as,
207a65f56eeSaurel32             (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
208dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
209a65f56eeSaurel32         s->cam[index][0] = data[1 * width] & 0xff;
210a65f56eeSaurel32         s->cam[index][1] = data[1 * width] >> 8;
211a65f56eeSaurel32         s->cam[index][2] = data[2 * width] & 0xff;
212a65f56eeSaurel32         s->cam[index][3] = data[2 * width] >> 8;
213a65f56eeSaurel32         s->cam[index][4] = data[3 * width] & 0xff;
214a65f56eeSaurel32         s->cam[index][5] = data[3 * width] >> 8;
215a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
216a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
217a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
218a65f56eeSaurel32         /* Move to next entry */
219a65f56eeSaurel32         s->regs[SONIC_CDC]--;
220a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
221a65f56eeSaurel32         index++;
222a65f56eeSaurel32     }
223a65f56eeSaurel32 
224a65f56eeSaurel32     /* Read CAM enable */
225dd820513SHervé Poussineau     address_space_rw(&s->as,
226a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
227dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
228a65f56eeSaurel32     s->regs[SONIC_CE] = data[0 * width];
229a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
230a65f56eeSaurel32 
231a65f56eeSaurel32     /* Done */
232a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
233a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
234a65f56eeSaurel32     dp8393x_update_irq(s);
235a65f56eeSaurel32 }
236a65f56eeSaurel32 
2373df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
238a65f56eeSaurel32 {
239a65f56eeSaurel32     uint16_t data[8];
240a65f56eeSaurel32     int width, size;
241a65f56eeSaurel32 
242a65f56eeSaurel32     /* Read memory */
243a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
244a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
245dd820513SHervé Poussineau     address_space_rw(&s->as,
246a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
247dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
248a65f56eeSaurel32 
249a65f56eeSaurel32     /* Update SONIC registers */
250a65f56eeSaurel32     s->regs[SONIC_CRBA0] = data[0 * width];
251a65f56eeSaurel32     s->regs[SONIC_CRBA1] = data[1 * width];
252a65f56eeSaurel32     s->regs[SONIC_RBWC0] = data[2 * width];
253a65f56eeSaurel32     s->regs[SONIC_RBWC1] = data[3 * width];
254a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
255a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
256a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
257a65f56eeSaurel32 
258a65f56eeSaurel32     /* Go to next entry */
259a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
260a65f56eeSaurel32 
261a65f56eeSaurel32     /* Handle wrap */
262a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
263a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
264a65f56eeSaurel32     }
265a65f56eeSaurel32 
266a65f56eeSaurel32     /* Check resource exhaustion */
267a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
268a65f56eeSaurel32     {
269a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
270a65f56eeSaurel32         dp8393x_update_irq(s);
271a65f56eeSaurel32     }
272a65f56eeSaurel32 
273a65f56eeSaurel32     /* Done */
274a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
275a65f56eeSaurel32 }
276a65f56eeSaurel32 
2773df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
278a65f56eeSaurel32 {
279bc72ad67SAlex Bligh     timer_del(s->watchdog);
280a65f56eeSaurel32 
281a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
282a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
283a65f56eeSaurel32 }
284a65f56eeSaurel32 
2853df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
286a65f56eeSaurel32 {
287a65f56eeSaurel32     uint32_t ticks;
288a65f56eeSaurel32     int64_t delay;
289a65f56eeSaurel32 
290a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
291bc72ad67SAlex Bligh         timer_del(s->watchdog);
292a65f56eeSaurel32         return;
293a65f56eeSaurel32     }
294a65f56eeSaurel32 
295a65f56eeSaurel32     ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
296bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2976ee093c9SJuan Quintela     delay = get_ticks_per_sec() * ticks / 5000000;
298bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
299a65f56eeSaurel32 }
300a65f56eeSaurel32 
3013df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
302a65f56eeSaurel32 {
303a65f56eeSaurel32     int64_t elapsed;
304a65f56eeSaurel32     uint32_t val;
305a65f56eeSaurel32 
306a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
307bc72ad67SAlex Bligh         timer_del(s->watchdog);
308a65f56eeSaurel32         return;
309a65f56eeSaurel32     }
310a65f56eeSaurel32 
311bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
312a65f56eeSaurel32     val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
313a65f56eeSaurel32     val -= elapsed / 5000000;
314a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
315a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3163df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
317a65f56eeSaurel32 
318a65f56eeSaurel32 }
319a65f56eeSaurel32 
3203df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
321a65f56eeSaurel32 {
322a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3233df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
324a65f56eeSaurel32 }
325a65f56eeSaurel32 
3263df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
327a65f56eeSaurel32 {
328a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3293df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
330a65f56eeSaurel32 }
331a65f56eeSaurel32 
3324594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc);
3334594f93aSFam Zheng 
3343df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
335a65f56eeSaurel32 {
336a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
3374594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
3384594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
3394594f93aSFam Zheng     }
340a65f56eeSaurel32 }
341a65f56eeSaurel32 
3423df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
343a65f56eeSaurel32 {
344a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
345a65f56eeSaurel32 }
346a65f56eeSaurel32 
3473df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
348a65f56eeSaurel32 {
349b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
350a65f56eeSaurel32     uint16_t data[12];
351a65f56eeSaurel32     int width, size;
352a65f56eeSaurel32     int tx_len, len;
353a65f56eeSaurel32     uint16_t i;
354a65f56eeSaurel32 
355a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
356a65f56eeSaurel32 
357a65f56eeSaurel32     while (1) {
358a65f56eeSaurel32         /* Read memory */
359a65f56eeSaurel32         DPRINTF("Transmit packet at %08x\n",
360a65f56eeSaurel32                 (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
361a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
362a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
363dd820513SHervé Poussineau         address_space_rw(&s->as,
364a65f56eeSaurel32             ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
365dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
366a65f56eeSaurel32         tx_len = 0;
367a65f56eeSaurel32 
368a65f56eeSaurel32         /* Update registers */
369a65f56eeSaurel32         s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
370a65f56eeSaurel32         s->regs[SONIC_TPS] = data[1 * width];
371a65f56eeSaurel32         s->regs[SONIC_TFC] = data[2 * width];
372a65f56eeSaurel32         s->regs[SONIC_TSA0] = data[3 * width];
373a65f56eeSaurel32         s->regs[SONIC_TSA1] = data[4 * width];
374a65f56eeSaurel32         s->regs[SONIC_TFS] = data[5 * width];
375a65f56eeSaurel32 
376a65f56eeSaurel32         /* Handle programmable interrupt */
377a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
378a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
379a65f56eeSaurel32         } else {
380a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
381a65f56eeSaurel32         }
382a65f56eeSaurel32 
383a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
384a65f56eeSaurel32             /* Append fragment */
385a65f56eeSaurel32             len = s->regs[SONIC_TFS];
386a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
387a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
388a65f56eeSaurel32             }
389dd820513SHervé Poussineau             address_space_rw(&s->as,
390a65f56eeSaurel32                 (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
391dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
392a65f56eeSaurel32             tx_len += len;
393a65f56eeSaurel32 
394a65f56eeSaurel32             i++;
395a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
396a65f56eeSaurel32                 /* Read next fragment details */
397a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
398dd820513SHervé Poussineau                 address_space_rw(&s->as,
399a65f56eeSaurel32                     ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
400dd820513SHervé Poussineau                     MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
401a65f56eeSaurel32                 s->regs[SONIC_TSA0] = data[0 * width];
402a65f56eeSaurel32                 s->regs[SONIC_TSA1] = data[1 * width];
403a65f56eeSaurel32                 s->regs[SONIC_TFS] = data[2 * width];
404a65f56eeSaurel32             }
405a65f56eeSaurel32         }
406a65f56eeSaurel32 
407a65f56eeSaurel32         /* Handle Ethernet checksum */
408a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
409a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
410a65f56eeSaurel32              * which don't have one */
411a65f56eeSaurel32         } else {
412a65f56eeSaurel32             /* Remove existing FCS */
413a65f56eeSaurel32             tx_len -= 4;
414a65f56eeSaurel32         }
415a65f56eeSaurel32 
416a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
417a65f56eeSaurel32             /* Loopback */
418a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
419b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
420a65f56eeSaurel32                 s->loopback_packet = 1;
421b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
422a65f56eeSaurel32             }
423a65f56eeSaurel32         } else {
424a65f56eeSaurel32             /* Transmit packet */
425b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
426a65f56eeSaurel32         }
427a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
428a65f56eeSaurel32 
429a65f56eeSaurel32         /* Write status */
430a65f56eeSaurel32         data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
431a65f56eeSaurel32         size = sizeof(uint16_t) * width;
432dd820513SHervé Poussineau         address_space_rw(&s->as,
433a65f56eeSaurel32             (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
434dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
435a65f56eeSaurel32 
436a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
437a65f56eeSaurel32             /* Read footer of packet */
438a65f56eeSaurel32             size = sizeof(uint16_t) * width;
439dd820513SHervé Poussineau             address_space_rw(&s->as,
440a65f56eeSaurel32                 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
441dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
442a65f56eeSaurel32             s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
443a65f56eeSaurel32             if (data[0 * width] & 0x1) {
444a65f56eeSaurel32                 /* EOL detected */
445a65f56eeSaurel32                 break;
446a65f56eeSaurel32             }
447a65f56eeSaurel32         }
448a65f56eeSaurel32     }
449a65f56eeSaurel32 
450a65f56eeSaurel32     /* Done */
451a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
452a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
453a65f56eeSaurel32     dp8393x_update_irq(s);
454a65f56eeSaurel32 }
455a65f56eeSaurel32 
4563df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
457a65f56eeSaurel32 {
458a65f56eeSaurel32     /* Nothing to do */
459a65f56eeSaurel32 }
460a65f56eeSaurel32 
4613df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
462a65f56eeSaurel32 {
463a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
464a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
465a65f56eeSaurel32         return;
466a65f56eeSaurel32     }
467a65f56eeSaurel32 
468a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
469a65f56eeSaurel32 
470a65f56eeSaurel32     if (command & SONIC_CR_HTX)
4713df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
472a65f56eeSaurel32     if (command & SONIC_CR_TXP)
4733df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
474a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
4753df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
476a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
4773df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
478a65f56eeSaurel32     if (command & SONIC_CR_STP)
4793df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
480a65f56eeSaurel32     if (command & SONIC_CR_ST)
4813df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
482a65f56eeSaurel32     if (command & SONIC_CR_RST)
4833df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
484a65f56eeSaurel32     if (command & SONIC_CR_RRRA)
4853df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
486a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
4873df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
488a65f56eeSaurel32 }
489a65f56eeSaurel32 
49084689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
491a65f56eeSaurel32 {
49284689cbbSHervé Poussineau     dp8393xState *s = opaque;
49384689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
494a65f56eeSaurel32     uint16_t val = 0;
495a65f56eeSaurel32 
496a65f56eeSaurel32     switch (reg) {
497a65f56eeSaurel32         /* Update data before reading it */
498a65f56eeSaurel32         case SONIC_WT0:
499a65f56eeSaurel32         case SONIC_WT1:
5003df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
501a65f56eeSaurel32             val = s->regs[reg];
502a65f56eeSaurel32             break;
503a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
504a65f56eeSaurel32         case SONIC_CAP2:
505a65f56eeSaurel32         case SONIC_CAP1:
506a65f56eeSaurel32         case SONIC_CAP0:
507a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
508a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
509a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
510a65f56eeSaurel32             }
511a65f56eeSaurel32             break;
512a65f56eeSaurel32         /* All other registers have no special contrainst */
513a65f56eeSaurel32         default:
514a65f56eeSaurel32             val = s->regs[reg];
515a65f56eeSaurel32     }
516a65f56eeSaurel32 
517a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
518a65f56eeSaurel32 
519a65f56eeSaurel32     return val;
520a65f56eeSaurel32 }
521a65f56eeSaurel32 
52284689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
52384689cbbSHervé Poussineau                           unsigned int size)
524a65f56eeSaurel32 {
52584689cbbSHervé Poussineau     dp8393xState *s = opaque;
52684689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
52784689cbbSHervé Poussineau 
52884689cbbSHervé Poussineau     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
529a65f56eeSaurel32 
530a65f56eeSaurel32     switch (reg) {
531a65f56eeSaurel32         /* Command register */
532a65f56eeSaurel32         case SONIC_CR:
5333df5de64SHervé Poussineau             dp8393x_do_command(s, data);
534a65f56eeSaurel32             break;
535a65f56eeSaurel32         /* Prevent write to read-only registers */
536a65f56eeSaurel32         case SONIC_CAP2:
537a65f56eeSaurel32         case SONIC_CAP1:
538a65f56eeSaurel32         case SONIC_CAP0:
539a65f56eeSaurel32         case SONIC_SR:
540a65f56eeSaurel32         case SONIC_MDT:
541a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
542a65f56eeSaurel32             break;
543a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
544a65f56eeSaurel32         case SONIC_DCR:
545a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
54684689cbbSHervé Poussineau                 s->regs[reg] = data & 0xbfff;
547a65f56eeSaurel32             } else {
548a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
549a65f56eeSaurel32             }
550a65f56eeSaurel32             break;
551a65f56eeSaurel32         case SONIC_DCR2:
552a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
55384689cbbSHervé Poussineau                 s->regs[reg] = data & 0xf017;
554a65f56eeSaurel32             } else {
555a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
556a65f56eeSaurel32             }
557a65f56eeSaurel32             break;
558a65f56eeSaurel32         /* 12 lower bytes are Read Only */
559a65f56eeSaurel32         case SONIC_TCR:
56084689cbbSHervé Poussineau             s->regs[reg] = data & 0xf000;
561a65f56eeSaurel32             break;
562a65f56eeSaurel32         /* 9 lower bytes are Read Only */
563a65f56eeSaurel32         case SONIC_RCR:
56484689cbbSHervé Poussineau             s->regs[reg] = data & 0xffe0;
565a65f56eeSaurel32             break;
566a65f56eeSaurel32         /* Ignore most significant bit */
567a65f56eeSaurel32         case SONIC_IMR:
56884689cbbSHervé Poussineau             s->regs[reg] = data & 0x7fff;
569a65f56eeSaurel32             dp8393x_update_irq(s);
570a65f56eeSaurel32             break;
571a65f56eeSaurel32         /* Clear bits by writing 1 to them */
572a65f56eeSaurel32         case SONIC_ISR:
57384689cbbSHervé Poussineau             data &= s->regs[reg];
57484689cbbSHervé Poussineau             s->regs[reg] &= ~data;
57584689cbbSHervé Poussineau             if (data & SONIC_ISR_RBE) {
5763df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
577a65f56eeSaurel32             }
578a65f56eeSaurel32             dp8393x_update_irq(s);
5794594f93aSFam Zheng             if (dp8393x_can_receive(s->nic->ncs)) {
5804594f93aSFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
5814594f93aSFam Zheng             }
582a65f56eeSaurel32             break;
583a65f56eeSaurel32         /* Ignore least significant bit */
584a65f56eeSaurel32         case SONIC_RSA:
585a65f56eeSaurel32         case SONIC_REA:
586a65f56eeSaurel32         case SONIC_RRP:
587a65f56eeSaurel32         case SONIC_RWP:
58884689cbbSHervé Poussineau             s->regs[reg] = data & 0xfffe;
589a65f56eeSaurel32             break;
590a65f56eeSaurel32         /* Invert written value for some registers */
591a65f56eeSaurel32         case SONIC_CRCT:
592a65f56eeSaurel32         case SONIC_FAET:
593a65f56eeSaurel32         case SONIC_MPT:
59484689cbbSHervé Poussineau             s->regs[reg] = data ^ 0xffff;
595a65f56eeSaurel32             break;
596a65f56eeSaurel32         /* All other registers have no special contrainst */
597a65f56eeSaurel32         default:
59884689cbbSHervé Poussineau             s->regs[reg] = data;
599a65f56eeSaurel32     }
600a65f56eeSaurel32 
601a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6023df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
603a65f56eeSaurel32     }
604a65f56eeSaurel32 }
605a65f56eeSaurel32 
60684689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
60784689cbbSHervé Poussineau     .read = dp8393x_read,
60884689cbbSHervé Poussineau     .write = dp8393x_write,
60984689cbbSHervé Poussineau     .impl.min_access_size = 2,
61084689cbbSHervé Poussineau     .impl.max_access_size = 2,
61184689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
61284689cbbSHervé Poussineau };
61384689cbbSHervé Poussineau 
614a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
615a65f56eeSaurel32 {
616a65f56eeSaurel32     dp8393xState *s = opaque;
617a65f56eeSaurel32 
618a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
619a65f56eeSaurel32         return;
620a65f56eeSaurel32     }
621a65f56eeSaurel32 
622a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
623a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
6243df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
625a65f56eeSaurel32 
626a65f56eeSaurel32     /* Signal underflow */
627a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
628a65f56eeSaurel32     dp8393x_update_irq(s);
629a65f56eeSaurel32 }
630a65f56eeSaurel32 
6313df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
632a65f56eeSaurel32 {
633cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
634a65f56eeSaurel32 
635a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
636a65f56eeSaurel32         return 0;
637a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
638a65f56eeSaurel32         return 0;
639a65f56eeSaurel32     return 1;
640a65f56eeSaurel32 }
641a65f56eeSaurel32 
6423df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
6433df5de64SHervé Poussineau                                   int size)
644a65f56eeSaurel32 {
645a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
646a65f56eeSaurel32     int i;
647a65f56eeSaurel32 
648a65f56eeSaurel32     /* Check promiscuous mode */
649a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
650a65f56eeSaurel32         return 0;
651a65f56eeSaurel32     }
652a65f56eeSaurel32 
653a65f56eeSaurel32     /* Check multicast packets */
654a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
655a65f56eeSaurel32         return SONIC_RCR_MC;
656a65f56eeSaurel32     }
657a65f56eeSaurel32 
658a65f56eeSaurel32     /* Check broadcast */
659a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
660a65f56eeSaurel32         return SONIC_RCR_BC;
661a65f56eeSaurel32     }
662a65f56eeSaurel32 
663a65f56eeSaurel32     /* Check CAM */
664a65f56eeSaurel32     for (i = 0; i < 16; i++) {
665a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
666a65f56eeSaurel32              /* Entry enabled */
667a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
668a65f56eeSaurel32                  return 0;
669a65f56eeSaurel32              }
670a65f56eeSaurel32         }
671a65f56eeSaurel32     }
672a65f56eeSaurel32 
673a65f56eeSaurel32     return -1;
674a65f56eeSaurel32 }
675a65f56eeSaurel32 
6763df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
6773df5de64SHervé Poussineau                                size_t size)
678a65f56eeSaurel32 {
679cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
680a65f56eeSaurel32     uint16_t data[10];
681a65f56eeSaurel32     int packet_type;
682a65f56eeSaurel32     uint32_t available, address;
683a65f56eeSaurel32     int width, rx_len = size;
684a65f56eeSaurel32     uint32_t checksum;
685a65f56eeSaurel32 
686a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
687a65f56eeSaurel32 
688a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
689a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
690a65f56eeSaurel32 
6913df5de64SHervé Poussineau     packet_type = dp8393x_receive_filter(s, buf, size);
692a65f56eeSaurel32     if (packet_type < 0) {
693a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
6944f1c942bSMark McLoughlin         return -1;
695a65f56eeSaurel32     }
696a65f56eeSaurel32 
697a65f56eeSaurel32     /* XXX: Check byte ordering */
698a65f56eeSaurel32 
699a65f56eeSaurel32     /* Check for EOL */
700a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
701a65f56eeSaurel32         /* Are we still in resource exhaustion? */
702a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
703a65f56eeSaurel32         address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
704dd820513SHervé Poussineau         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
705dd820513SHervé Poussineau                          (uint8_t *)data, size, 0);
706a65f56eeSaurel32         if (data[0 * width] & 0x1) {
707a65f56eeSaurel32             /* Still EOL ; stop reception */
7084f1c942bSMark McLoughlin             return -1;
709a65f56eeSaurel32         } else {
710a65f56eeSaurel32             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
711a65f56eeSaurel32         }
712a65f56eeSaurel32     }
713a65f56eeSaurel32 
714a65f56eeSaurel32     /* Save current position */
715a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
716a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
717a65f56eeSaurel32 
718a65f56eeSaurel32     /* Calculate the ethernet checksum */
719a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
720a65f56eeSaurel32 
721a65f56eeSaurel32     /* Put packet into RBA */
722a65f56eeSaurel32     DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
723a65f56eeSaurel32     address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
724dd820513SHervé Poussineau     address_space_rw(&s->as, address,
725dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
726a65f56eeSaurel32     address += rx_len;
727dd820513SHervé Poussineau     address_space_rw(&s->as, address,
728dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
729a65f56eeSaurel32     rx_len += 4;
730a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
731a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
732a65f56eeSaurel32     available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
733a65f56eeSaurel32     available -= rx_len / 2;
734a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
735a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
736a65f56eeSaurel32 
737a65f56eeSaurel32     /* Update status */
738a65f56eeSaurel32     if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) {
739a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
740a65f56eeSaurel32     }
741a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
742a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
743a65f56eeSaurel32     if (s->loopback_packet) {
744a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
745a65f56eeSaurel32         s->loopback_packet = 0;
746a65f56eeSaurel32     }
747a65f56eeSaurel32 
748a65f56eeSaurel32     /* Write status to memory */
749a65f56eeSaurel32     DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]);
750a65f56eeSaurel32     data[0 * width] = s->regs[SONIC_RCR]; /* status */
751a65f56eeSaurel32     data[1 * width] = rx_len; /* byte count */
752a65f56eeSaurel32     data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
753a65f56eeSaurel32     data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
754a65f56eeSaurel32     data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
755a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
756dd820513SHervé Poussineau     address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA],
757dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
758a65f56eeSaurel32 
759a65f56eeSaurel32     /* Move to next descriptor */
760a65f56eeSaurel32     size = sizeof(uint16_t) * width;
761dd820513SHervé Poussineau     address_space_rw(&s->as,
762a65f56eeSaurel32         ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
763dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
764a65f56eeSaurel32     s->regs[SONIC_LLFA] = data[0 * width];
765a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
766a65f56eeSaurel32         /* EOL detected */
767a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
768a65f56eeSaurel32     } else {
769a65f56eeSaurel32         data[0 * width] = 0; /* in_use */
770dd820513SHervé Poussineau         address_space_rw(&s->as,
771a65f56eeSaurel32             ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
772409b52bfSHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
773a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
774a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
775a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
776a65f56eeSaurel32 
777a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
778a65f56eeSaurel32             /* Read next RRA */
7793df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
780a65f56eeSaurel32         }
781a65f56eeSaurel32     }
782a65f56eeSaurel32 
783a65f56eeSaurel32     /* Done */
784a65f56eeSaurel32     dp8393x_update_irq(s);
7854f1c942bSMark McLoughlin 
7864f1c942bSMark McLoughlin     return size;
787a65f56eeSaurel32 }
788a65f56eeSaurel32 
789104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
790a65f56eeSaurel32 {
791104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
792bc72ad67SAlex Bligh     timer_del(s->watchdog);
793a65f56eeSaurel32 
794bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
795a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
796a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
797a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
798a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
799a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
800a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
801a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
802a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
803a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
804a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
805a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
806a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
807a65f56eeSaurel32 
808a65f56eeSaurel32     /* Network cable is connected */
809a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
810a65f56eeSaurel32 
811a65f56eeSaurel32     dp8393x_update_irq(s);
812a65f56eeSaurel32 }
813a65f56eeSaurel32 
81405f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
8152be64a68SLaszlo Ersek     .type = NET_CLIENT_OPTIONS_KIND_NIC,
81605f41fe3SMark McLoughlin     .size = sizeof(NICState),
8173df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8183df5de64SHervé Poussineau     .receive = dp8393x_receive,
81905f41fe3SMark McLoughlin };
82005f41fe3SMark McLoughlin 
821104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
822a65f56eeSaurel32 {
823104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
824104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
825a65f56eeSaurel32 
826104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
82789ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
828104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
829104655a5SHervé Poussineau }
830a65f56eeSaurel32 
831104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
832104655a5SHervé Poussineau {
833104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
83489ae0ff9SHervé Poussineau     int i, checksum;
83589ae0ff9SHervé Poussineau     uint8_t *prom;
83652579c68SHervé Poussineau     Error *local_err = NULL;
837a65f56eeSaurel32 
838104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
839104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
840104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
841104655a5SHervé Poussineau 
842104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
843104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
844104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
845104655a5SHervé Poussineau 
846bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
847a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
84889ae0ff9SHervé Poussineau 
84952579c68SHervé Poussineau     memory_region_init_ram(&s->prom, OBJECT(dev),
85052579c68SHervé Poussineau                            "dp8393x-prom", SONIC_PROM_SIZE, &local_err);
85152579c68SHervé Poussineau     if (local_err) {
85252579c68SHervé Poussineau         error_propagate(errp, local_err);
85352579c68SHervé Poussineau         return;
85452579c68SHervé Poussineau     }
85552579c68SHervé Poussineau     memory_region_set_readonly(&s->prom, true);
85689ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
85789ae0ff9SHervé Poussineau     checksum = 0;
85889ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
85989ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
86089ae0ff9SHervé Poussineau         checksum += prom[i];
86189ae0ff9SHervé Poussineau         if (checksum > 0xff) {
86289ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
86389ae0ff9SHervé Poussineau         }
86489ae0ff9SHervé Poussineau     }
86589ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
866a65f56eeSaurel32 }
867104655a5SHervé Poussineau 
8681670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
8691670735dSHervé Poussineau     .name = "dp8393x",
8701670735dSHervé Poussineau     .version_id = 0,
8711670735dSHervé Poussineau     .minimum_version_id = 0,
8721670735dSHervé Poussineau     .fields = (VMStateField []) {
8731670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
8741670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
8751670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
8761670735dSHervé Poussineau     }
8771670735dSHervé Poussineau };
8781670735dSHervé Poussineau 
879104655a5SHervé Poussineau static Property dp8393x_properties[] = {
880104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
881104655a5SHervé Poussineau     DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
882104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
883104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
884104655a5SHervé Poussineau };
885104655a5SHervé Poussineau 
886104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
887104655a5SHervé Poussineau {
888104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
889104655a5SHervé Poussineau 
890104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
891104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
892104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
8931670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
894104655a5SHervé Poussineau     dc->props = dp8393x_properties;
895f6351288SHervé Poussineau     /* Reason: dma_mr property can't be set */
896f6351288SHervé Poussineau     dc->cannot_instantiate_with_device_add_yet = true;
897104655a5SHervé Poussineau }
898104655a5SHervé Poussineau 
899104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
900104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
901104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
902104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
903104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
904104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
905104655a5SHervé Poussineau };
906104655a5SHervé Poussineau 
907104655a5SHervé Poussineau static void dp8393x_register_types(void)
908104655a5SHervé Poussineau {
909104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
910104655a5SHervé Poussineau }
911104655a5SHervé Poussineau 
912104655a5SHervé Poussineau type_init(dp8393x_register_types)
913