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