xref: /qemu/hw/net/dp8393x.c (revision f6351288b65130deb8102b17143f5d84f817a02a)
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 
20104655a5SHervé Poussineau #include "hw/sysbus.h"
21104655a5SHervé Poussineau #include "hw/devices.h"
221422e32dSPaolo Bonzini #include "net/net.h"
23104655a5SHervé Poussineau #include "qemu/timer.h"
24f2f62c4dSHervé Poussineau #include <zlib.h>
25a65f56eeSaurel32 
26a65f56eeSaurel32 //#define DEBUG_SONIC
27a65f56eeSaurel32 
2889ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
29a65f56eeSaurel32 
30a65f56eeSaurel32 #ifdef DEBUG_SONIC
31001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
32001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
33a65f56eeSaurel32 static const char* reg_names[] = {
34a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
35a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
36a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
37a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
38a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
39a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
40a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
41a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
42a65f56eeSaurel32 #else
43001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
44a65f56eeSaurel32 #endif
45a65f56eeSaurel32 
46001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
47001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
48a65f56eeSaurel32 
49a65f56eeSaurel32 #define SONIC_CR     0x00
50a65f56eeSaurel32 #define SONIC_DCR    0x01
51a65f56eeSaurel32 #define SONIC_RCR    0x02
52a65f56eeSaurel32 #define SONIC_TCR    0x03
53a65f56eeSaurel32 #define SONIC_IMR    0x04
54a65f56eeSaurel32 #define SONIC_ISR    0x05
55a65f56eeSaurel32 #define SONIC_UTDA   0x06
56a65f56eeSaurel32 #define SONIC_CTDA   0x07
57a65f56eeSaurel32 #define SONIC_TPS    0x08
58a65f56eeSaurel32 #define SONIC_TFC    0x09
59a65f56eeSaurel32 #define SONIC_TSA0   0x0a
60a65f56eeSaurel32 #define SONIC_TSA1   0x0b
61a65f56eeSaurel32 #define SONIC_TFS    0x0c
62a65f56eeSaurel32 #define SONIC_URDA   0x0d
63a65f56eeSaurel32 #define SONIC_CRDA   0x0e
64a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
65a65f56eeSaurel32 #define SONIC_CRBA1  0x10
66a65f56eeSaurel32 #define SONIC_RBWC0  0x11
67a65f56eeSaurel32 #define SONIC_RBWC1  0x12
68a65f56eeSaurel32 #define SONIC_EOBC   0x13
69a65f56eeSaurel32 #define SONIC_URRA   0x14
70a65f56eeSaurel32 #define SONIC_RSA    0x15
71a65f56eeSaurel32 #define SONIC_REA    0x16
72a65f56eeSaurel32 #define SONIC_RRP    0x17
73a65f56eeSaurel32 #define SONIC_RWP    0x18
74a65f56eeSaurel32 #define SONIC_TRBA0  0x19
75a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
76a65f56eeSaurel32 #define SONIC_LLFA   0x1f
77a65f56eeSaurel32 #define SONIC_TTDA   0x20
78a65f56eeSaurel32 #define SONIC_CEP    0x21
79a65f56eeSaurel32 #define SONIC_CAP2   0x22
80a65f56eeSaurel32 #define SONIC_CAP1   0x23
81a65f56eeSaurel32 #define SONIC_CAP0   0x24
82a65f56eeSaurel32 #define SONIC_CE     0x25
83a65f56eeSaurel32 #define SONIC_CDP    0x26
84a65f56eeSaurel32 #define SONIC_CDC    0x27
85a65f56eeSaurel32 #define SONIC_SR     0x28
86a65f56eeSaurel32 #define SONIC_WT0    0x29
87a65f56eeSaurel32 #define SONIC_WT1    0x2a
88a65f56eeSaurel32 #define SONIC_RSC    0x2b
89a65f56eeSaurel32 #define SONIC_CRCT   0x2c
90a65f56eeSaurel32 #define SONIC_FAET   0x2d
91a65f56eeSaurel32 #define SONIC_MPT    0x2e
92a65f56eeSaurel32 #define SONIC_MDT    0x2f
93a65f56eeSaurel32 #define SONIC_DCR2   0x3f
94a65f56eeSaurel32 
95a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
96a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
97a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
98a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
99a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
100a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
101a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
102a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
103a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
104a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
105a65f56eeSaurel32 
106a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
107a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
108a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
109a65f56eeSaurel32 
110a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
111a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
112a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
113a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
114a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
115a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
116a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
117a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
118a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
119a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
120a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
121a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
122a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
123a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
124a65f56eeSaurel32 
125a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
126a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
127a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
128a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
129a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
130a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
131a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
132a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
133a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
134a65f56eeSaurel32 
135a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
136a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
137a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
138a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
139a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
140a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
141a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
142a65f56eeSaurel32 
143104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
144104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
145104655a5SHervé Poussineau 
146a65f56eeSaurel32 typedef struct dp8393xState {
147104655a5SHervé Poussineau     SysBusDevice parent_obj;
148104655a5SHervé Poussineau 
149a65f56eeSaurel32     /* Hardware */
150104655a5SHervé Poussineau     uint8_t it_shift;
151a65f56eeSaurel32     qemu_irq irq;
152a65f56eeSaurel32 #ifdef DEBUG_SONIC
153a65f56eeSaurel32     int irq_level;
154a65f56eeSaurel32 #endif
155a65f56eeSaurel32     QEMUTimer *watchdog;
156a65f56eeSaurel32     int64_t wt_last_update;
15705f41fe3SMark McLoughlin     NICConf conf;
15805f41fe3SMark McLoughlin     NICState *nic;
159024e5bb6SAvi Kivity     MemoryRegion mmio;
16089ae0ff9SHervé Poussineau     MemoryRegion prom;
161a65f56eeSaurel32 
162a65f56eeSaurel32     /* Registers */
163a65f56eeSaurel32     uint8_t cam[16][6];
164a65f56eeSaurel32     uint16_t regs[0x40];
165a65f56eeSaurel32 
166a65f56eeSaurel32     /* Temporaries */
167a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
168a65f56eeSaurel32     int loopback_packet;
169a65f56eeSaurel32 
170a65f56eeSaurel32     /* Memory access */
171104655a5SHervé Poussineau     void *dma_mr;
172dd820513SHervé Poussineau     AddressSpace as;
173a65f56eeSaurel32 } dp8393xState;
174a65f56eeSaurel32 
175a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
176a65f56eeSaurel32 {
177a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
178a65f56eeSaurel32 
179a65f56eeSaurel32 #ifdef DEBUG_SONIC
180a65f56eeSaurel32     if (level != s->irq_level) {
181a65f56eeSaurel32         s->irq_level = level;
182a65f56eeSaurel32         if (level) {
183a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
184a65f56eeSaurel32         } else {
185a65f56eeSaurel32             DPRINTF("lower irq\n");
186a65f56eeSaurel32         }
187a65f56eeSaurel32     }
188a65f56eeSaurel32 #endif
189a65f56eeSaurel32 
190a65f56eeSaurel32     qemu_set_irq(s->irq, level);
191a65f56eeSaurel32 }
192a65f56eeSaurel32 
1933df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
194a65f56eeSaurel32 {
195a65f56eeSaurel32     uint16_t data[8];
196a65f56eeSaurel32     int width, size;
197a65f56eeSaurel32     uint16_t index = 0;
198a65f56eeSaurel32 
199a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
200a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
201a65f56eeSaurel32 
202a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
203a65f56eeSaurel32         /* Fill current entry */
204dd820513SHervé Poussineau         address_space_rw(&s->as,
205a65f56eeSaurel32             (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
206dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
207a65f56eeSaurel32         s->cam[index][0] = data[1 * width] & 0xff;
208a65f56eeSaurel32         s->cam[index][1] = data[1 * width] >> 8;
209a65f56eeSaurel32         s->cam[index][2] = data[2 * width] & 0xff;
210a65f56eeSaurel32         s->cam[index][3] = data[2 * width] >> 8;
211a65f56eeSaurel32         s->cam[index][4] = data[3 * width] & 0xff;
212a65f56eeSaurel32         s->cam[index][5] = data[3 * width] >> 8;
213a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
214a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
215a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
216a65f56eeSaurel32         /* Move to next entry */
217a65f56eeSaurel32         s->regs[SONIC_CDC]--;
218a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
219a65f56eeSaurel32         index++;
220a65f56eeSaurel32     }
221a65f56eeSaurel32 
222a65f56eeSaurel32     /* Read CAM enable */
223dd820513SHervé Poussineau     address_space_rw(&s->as,
224a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
225dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
226a65f56eeSaurel32     s->regs[SONIC_CE] = data[0 * width];
227a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
228a65f56eeSaurel32 
229a65f56eeSaurel32     /* Done */
230a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
231a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
232a65f56eeSaurel32     dp8393x_update_irq(s);
233a65f56eeSaurel32 }
234a65f56eeSaurel32 
2353df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
236a65f56eeSaurel32 {
237a65f56eeSaurel32     uint16_t data[8];
238a65f56eeSaurel32     int width, size;
239a65f56eeSaurel32 
240a65f56eeSaurel32     /* Read memory */
241a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
242a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
243dd820513SHervé Poussineau     address_space_rw(&s->as,
244a65f56eeSaurel32         (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
245dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
246a65f56eeSaurel32 
247a65f56eeSaurel32     /* Update SONIC registers */
248a65f56eeSaurel32     s->regs[SONIC_CRBA0] = data[0 * width];
249a65f56eeSaurel32     s->regs[SONIC_CRBA1] = data[1 * width];
250a65f56eeSaurel32     s->regs[SONIC_RBWC0] = data[2 * width];
251a65f56eeSaurel32     s->regs[SONIC_RBWC1] = data[3 * width];
252a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
253a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
254a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
255a65f56eeSaurel32 
256a65f56eeSaurel32     /* Go to next entry */
257a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
258a65f56eeSaurel32 
259a65f56eeSaurel32     /* Handle wrap */
260a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
261a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
262a65f56eeSaurel32     }
263a65f56eeSaurel32 
264a65f56eeSaurel32     /* Check resource exhaustion */
265a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
266a65f56eeSaurel32     {
267a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
268a65f56eeSaurel32         dp8393x_update_irq(s);
269a65f56eeSaurel32     }
270a65f56eeSaurel32 
271a65f56eeSaurel32     /* Done */
272a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
273a65f56eeSaurel32 }
274a65f56eeSaurel32 
2753df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
276a65f56eeSaurel32 {
277bc72ad67SAlex Bligh     timer_del(s->watchdog);
278a65f56eeSaurel32 
279a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
280a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
281a65f56eeSaurel32 }
282a65f56eeSaurel32 
2833df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
284a65f56eeSaurel32 {
285a65f56eeSaurel32     uint32_t ticks;
286a65f56eeSaurel32     int64_t delay;
287a65f56eeSaurel32 
288a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
289bc72ad67SAlex Bligh         timer_del(s->watchdog);
290a65f56eeSaurel32         return;
291a65f56eeSaurel32     }
292a65f56eeSaurel32 
293a65f56eeSaurel32     ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
294bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2956ee093c9SJuan Quintela     delay = get_ticks_per_sec() * ticks / 5000000;
296bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
297a65f56eeSaurel32 }
298a65f56eeSaurel32 
2993df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
300a65f56eeSaurel32 {
301a65f56eeSaurel32     int64_t elapsed;
302a65f56eeSaurel32     uint32_t val;
303a65f56eeSaurel32 
304a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
305bc72ad67SAlex Bligh         timer_del(s->watchdog);
306a65f56eeSaurel32         return;
307a65f56eeSaurel32     }
308a65f56eeSaurel32 
309bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
310a65f56eeSaurel32     val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
311a65f56eeSaurel32     val -= elapsed / 5000000;
312a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
313a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3143df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
315a65f56eeSaurel32 
316a65f56eeSaurel32 }
317a65f56eeSaurel32 
3183df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
319a65f56eeSaurel32 {
320a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3213df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
322a65f56eeSaurel32 }
323a65f56eeSaurel32 
3243df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
325a65f56eeSaurel32 {
326a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3273df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
328a65f56eeSaurel32 }
329a65f56eeSaurel32 
3304594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc);
3314594f93aSFam Zheng 
3323df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
333a65f56eeSaurel32 {
334a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
3354594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
3364594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
3374594f93aSFam Zheng     }
338a65f56eeSaurel32 }
339a65f56eeSaurel32 
3403df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
341a65f56eeSaurel32 {
342a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
343a65f56eeSaurel32 }
344a65f56eeSaurel32 
3453df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
346a65f56eeSaurel32 {
347b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
348a65f56eeSaurel32     uint16_t data[12];
349a65f56eeSaurel32     int width, size;
350a65f56eeSaurel32     int tx_len, len;
351a65f56eeSaurel32     uint16_t i;
352a65f56eeSaurel32 
353a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
354a65f56eeSaurel32 
355a65f56eeSaurel32     while (1) {
356a65f56eeSaurel32         /* Read memory */
357a65f56eeSaurel32         DPRINTF("Transmit packet at %08x\n",
358a65f56eeSaurel32                 (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
359a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
360a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
361dd820513SHervé Poussineau         address_space_rw(&s->as,
362a65f56eeSaurel32             ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
363dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
364a65f56eeSaurel32         tx_len = 0;
365a65f56eeSaurel32 
366a65f56eeSaurel32         /* Update registers */
367a65f56eeSaurel32         s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
368a65f56eeSaurel32         s->regs[SONIC_TPS] = data[1 * width];
369a65f56eeSaurel32         s->regs[SONIC_TFC] = data[2 * width];
370a65f56eeSaurel32         s->regs[SONIC_TSA0] = data[3 * width];
371a65f56eeSaurel32         s->regs[SONIC_TSA1] = data[4 * width];
372a65f56eeSaurel32         s->regs[SONIC_TFS] = data[5 * width];
373a65f56eeSaurel32 
374a65f56eeSaurel32         /* Handle programmable interrupt */
375a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
376a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
377a65f56eeSaurel32         } else {
378a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
379a65f56eeSaurel32         }
380a65f56eeSaurel32 
381a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
382a65f56eeSaurel32             /* Append fragment */
383a65f56eeSaurel32             len = s->regs[SONIC_TFS];
384a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
385a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
386a65f56eeSaurel32             }
387dd820513SHervé Poussineau             address_space_rw(&s->as,
388a65f56eeSaurel32                 (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
389dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
390a65f56eeSaurel32             tx_len += len;
391a65f56eeSaurel32 
392a65f56eeSaurel32             i++;
393a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
394a65f56eeSaurel32                 /* Read next fragment details */
395a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
396dd820513SHervé Poussineau                 address_space_rw(&s->as,
397a65f56eeSaurel32                     ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
398dd820513SHervé Poussineau                     MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
399a65f56eeSaurel32                 s->regs[SONIC_TSA0] = data[0 * width];
400a65f56eeSaurel32                 s->regs[SONIC_TSA1] = data[1 * width];
401a65f56eeSaurel32                 s->regs[SONIC_TFS] = data[2 * width];
402a65f56eeSaurel32             }
403a65f56eeSaurel32         }
404a65f56eeSaurel32 
405a65f56eeSaurel32         /* Handle Ethernet checksum */
406a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
407a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
408a65f56eeSaurel32              * which don't have one */
409a65f56eeSaurel32         } else {
410a65f56eeSaurel32             /* Remove existing FCS */
411a65f56eeSaurel32             tx_len -= 4;
412a65f56eeSaurel32         }
413a65f56eeSaurel32 
414a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
415a65f56eeSaurel32             /* Loopback */
416a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
417b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
418a65f56eeSaurel32                 s->loopback_packet = 1;
419b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
420a65f56eeSaurel32             }
421a65f56eeSaurel32         } else {
422a65f56eeSaurel32             /* Transmit packet */
423b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
424a65f56eeSaurel32         }
425a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
426a65f56eeSaurel32 
427a65f56eeSaurel32         /* Write status */
428a65f56eeSaurel32         data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
429a65f56eeSaurel32         size = sizeof(uint16_t) * width;
430dd820513SHervé Poussineau         address_space_rw(&s->as,
431a65f56eeSaurel32             (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
432dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
433a65f56eeSaurel32 
434a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
435a65f56eeSaurel32             /* Read footer of packet */
436a65f56eeSaurel32             size = sizeof(uint16_t) * width;
437dd820513SHervé Poussineau             address_space_rw(&s->as,
438a65f56eeSaurel32                 ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
439dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
440a65f56eeSaurel32             s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
441a65f56eeSaurel32             if (data[0 * width] & 0x1) {
442a65f56eeSaurel32                 /* EOL detected */
443a65f56eeSaurel32                 break;
444a65f56eeSaurel32             }
445a65f56eeSaurel32         }
446a65f56eeSaurel32     }
447a65f56eeSaurel32 
448a65f56eeSaurel32     /* Done */
449a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
450a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
451a65f56eeSaurel32     dp8393x_update_irq(s);
452a65f56eeSaurel32 }
453a65f56eeSaurel32 
4543df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
455a65f56eeSaurel32 {
456a65f56eeSaurel32     /* Nothing to do */
457a65f56eeSaurel32 }
458a65f56eeSaurel32 
4593df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
460a65f56eeSaurel32 {
461a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
462a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
463a65f56eeSaurel32         return;
464a65f56eeSaurel32     }
465a65f56eeSaurel32 
466a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
467a65f56eeSaurel32 
468a65f56eeSaurel32     if (command & SONIC_CR_HTX)
4693df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
470a65f56eeSaurel32     if (command & SONIC_CR_TXP)
4713df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
472a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
4733df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
474a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
4753df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
476a65f56eeSaurel32     if (command & SONIC_CR_STP)
4773df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
478a65f56eeSaurel32     if (command & SONIC_CR_ST)
4793df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
480a65f56eeSaurel32     if (command & SONIC_CR_RST)
4813df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
482a65f56eeSaurel32     if (command & SONIC_CR_RRRA)
4833df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
484a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
4853df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
486a65f56eeSaurel32 }
487a65f56eeSaurel32 
48884689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
489a65f56eeSaurel32 {
49084689cbbSHervé Poussineau     dp8393xState *s = opaque;
49184689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
492a65f56eeSaurel32     uint16_t val = 0;
493a65f56eeSaurel32 
494a65f56eeSaurel32     switch (reg) {
495a65f56eeSaurel32         /* Update data before reading it */
496a65f56eeSaurel32         case SONIC_WT0:
497a65f56eeSaurel32         case SONIC_WT1:
4983df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
499a65f56eeSaurel32             val = s->regs[reg];
500a65f56eeSaurel32             break;
501a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
502a65f56eeSaurel32         case SONIC_CAP2:
503a65f56eeSaurel32         case SONIC_CAP1:
504a65f56eeSaurel32         case SONIC_CAP0:
505a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
506a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
507a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
508a65f56eeSaurel32             }
509a65f56eeSaurel32             break;
510a65f56eeSaurel32         /* All other registers have no special contrainst */
511a65f56eeSaurel32         default:
512a65f56eeSaurel32             val = s->regs[reg];
513a65f56eeSaurel32     }
514a65f56eeSaurel32 
515a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
516a65f56eeSaurel32 
517a65f56eeSaurel32     return val;
518a65f56eeSaurel32 }
519a65f56eeSaurel32 
52084689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
52184689cbbSHervé Poussineau                           unsigned int size)
522a65f56eeSaurel32 {
52384689cbbSHervé Poussineau     dp8393xState *s = opaque;
52484689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
52584689cbbSHervé Poussineau 
52684689cbbSHervé Poussineau     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
527a65f56eeSaurel32 
528a65f56eeSaurel32     switch (reg) {
529a65f56eeSaurel32         /* Command register */
530a65f56eeSaurel32         case SONIC_CR:
5313df5de64SHervé Poussineau             dp8393x_do_command(s, data);
532a65f56eeSaurel32             break;
533a65f56eeSaurel32         /* Prevent write to read-only registers */
534a65f56eeSaurel32         case SONIC_CAP2:
535a65f56eeSaurel32         case SONIC_CAP1:
536a65f56eeSaurel32         case SONIC_CAP0:
537a65f56eeSaurel32         case SONIC_SR:
538a65f56eeSaurel32         case SONIC_MDT:
539a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
540a65f56eeSaurel32             break;
541a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
542a65f56eeSaurel32         case SONIC_DCR:
543a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
54484689cbbSHervé Poussineau                 s->regs[reg] = data & 0xbfff;
545a65f56eeSaurel32             } else {
546a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
547a65f56eeSaurel32             }
548a65f56eeSaurel32             break;
549a65f56eeSaurel32         case SONIC_DCR2:
550a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
55184689cbbSHervé Poussineau                 s->regs[reg] = data & 0xf017;
552a65f56eeSaurel32             } else {
553a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
554a65f56eeSaurel32             }
555a65f56eeSaurel32             break;
556a65f56eeSaurel32         /* 12 lower bytes are Read Only */
557a65f56eeSaurel32         case SONIC_TCR:
55884689cbbSHervé Poussineau             s->regs[reg] = data & 0xf000;
559a65f56eeSaurel32             break;
560a65f56eeSaurel32         /* 9 lower bytes are Read Only */
561a65f56eeSaurel32         case SONIC_RCR:
56284689cbbSHervé Poussineau             s->regs[reg] = data & 0xffe0;
563a65f56eeSaurel32             break;
564a65f56eeSaurel32         /* Ignore most significant bit */
565a65f56eeSaurel32         case SONIC_IMR:
56684689cbbSHervé Poussineau             s->regs[reg] = data & 0x7fff;
567a65f56eeSaurel32             dp8393x_update_irq(s);
568a65f56eeSaurel32             break;
569a65f56eeSaurel32         /* Clear bits by writing 1 to them */
570a65f56eeSaurel32         case SONIC_ISR:
57184689cbbSHervé Poussineau             data &= s->regs[reg];
57284689cbbSHervé Poussineau             s->regs[reg] &= ~data;
57384689cbbSHervé Poussineau             if (data & SONIC_ISR_RBE) {
5743df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
575a65f56eeSaurel32             }
576a65f56eeSaurel32             dp8393x_update_irq(s);
5774594f93aSFam Zheng             if (dp8393x_can_receive(s->nic->ncs)) {
5784594f93aSFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
5794594f93aSFam Zheng             }
580a65f56eeSaurel32             break;
581a65f56eeSaurel32         /* Ignore least significant bit */
582a65f56eeSaurel32         case SONIC_RSA:
583a65f56eeSaurel32         case SONIC_REA:
584a65f56eeSaurel32         case SONIC_RRP:
585a65f56eeSaurel32         case SONIC_RWP:
58684689cbbSHervé Poussineau             s->regs[reg] = data & 0xfffe;
587a65f56eeSaurel32             break;
588a65f56eeSaurel32         /* Invert written value for some registers */
589a65f56eeSaurel32         case SONIC_CRCT:
590a65f56eeSaurel32         case SONIC_FAET:
591a65f56eeSaurel32         case SONIC_MPT:
59284689cbbSHervé Poussineau             s->regs[reg] = data ^ 0xffff;
593a65f56eeSaurel32             break;
594a65f56eeSaurel32         /* All other registers have no special contrainst */
595a65f56eeSaurel32         default:
59684689cbbSHervé Poussineau             s->regs[reg] = data;
597a65f56eeSaurel32     }
598a65f56eeSaurel32 
599a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6003df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
601a65f56eeSaurel32     }
602a65f56eeSaurel32 }
603a65f56eeSaurel32 
60484689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
60584689cbbSHervé Poussineau     .read = dp8393x_read,
60684689cbbSHervé Poussineau     .write = dp8393x_write,
60784689cbbSHervé Poussineau     .impl.min_access_size = 2,
60884689cbbSHervé Poussineau     .impl.max_access_size = 2,
60984689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
61084689cbbSHervé Poussineau };
61184689cbbSHervé Poussineau 
612a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
613a65f56eeSaurel32 {
614a65f56eeSaurel32     dp8393xState *s = opaque;
615a65f56eeSaurel32 
616a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
617a65f56eeSaurel32         return;
618a65f56eeSaurel32     }
619a65f56eeSaurel32 
620a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
621a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
6223df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
623a65f56eeSaurel32 
624a65f56eeSaurel32     /* Signal underflow */
625a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
626a65f56eeSaurel32     dp8393x_update_irq(s);
627a65f56eeSaurel32 }
628a65f56eeSaurel32 
6293df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
630a65f56eeSaurel32 {
631cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
632a65f56eeSaurel32 
633a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
634a65f56eeSaurel32         return 0;
635a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
636a65f56eeSaurel32         return 0;
637a65f56eeSaurel32     return 1;
638a65f56eeSaurel32 }
639a65f56eeSaurel32 
6403df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
6413df5de64SHervé Poussineau                                   int size)
642a65f56eeSaurel32 {
643a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
644a65f56eeSaurel32     int i;
645a65f56eeSaurel32 
646a65f56eeSaurel32     /* Check for runt packet (remember that checksum is not there) */
647a65f56eeSaurel32     if (size < 64 - 4) {
648a65f56eeSaurel32         return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1;
649a65f56eeSaurel32     }
650a65f56eeSaurel32 
651a65f56eeSaurel32     /* Check promiscuous mode */
652a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
653a65f56eeSaurel32         return 0;
654a65f56eeSaurel32     }
655a65f56eeSaurel32 
656a65f56eeSaurel32     /* Check multicast packets */
657a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
658a65f56eeSaurel32         return SONIC_RCR_MC;
659a65f56eeSaurel32     }
660a65f56eeSaurel32 
661a65f56eeSaurel32     /* Check broadcast */
662a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
663a65f56eeSaurel32         return SONIC_RCR_BC;
664a65f56eeSaurel32     }
665a65f56eeSaurel32 
666a65f56eeSaurel32     /* Check CAM */
667a65f56eeSaurel32     for (i = 0; i < 16; i++) {
668a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
669a65f56eeSaurel32              /* Entry enabled */
670a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
671a65f56eeSaurel32                  return 0;
672a65f56eeSaurel32              }
673a65f56eeSaurel32         }
674a65f56eeSaurel32     }
675a65f56eeSaurel32 
676a65f56eeSaurel32     return -1;
677a65f56eeSaurel32 }
678a65f56eeSaurel32 
6793df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
6803df5de64SHervé Poussineau                                size_t size)
681a65f56eeSaurel32 {
682cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
683a65f56eeSaurel32     uint16_t data[10];
684a65f56eeSaurel32     int packet_type;
685a65f56eeSaurel32     uint32_t available, address;
686a65f56eeSaurel32     int width, rx_len = size;
687a65f56eeSaurel32     uint32_t checksum;
688a65f56eeSaurel32 
689a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
690a65f56eeSaurel32 
691a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
692a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
693a65f56eeSaurel32 
6943df5de64SHervé Poussineau     packet_type = dp8393x_receive_filter(s, buf, size);
695a65f56eeSaurel32     if (packet_type < 0) {
696a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
6974f1c942bSMark McLoughlin         return -1;
698a65f56eeSaurel32     }
699a65f56eeSaurel32 
700a65f56eeSaurel32     /* XXX: Check byte ordering */
701a65f56eeSaurel32 
702a65f56eeSaurel32     /* Check for EOL */
703a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
704a65f56eeSaurel32         /* Are we still in resource exhaustion? */
705a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
706a65f56eeSaurel32         address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
707dd820513SHervé Poussineau         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
708dd820513SHervé Poussineau                          (uint8_t *)data, size, 0);
709a65f56eeSaurel32         if (data[0 * width] & 0x1) {
710a65f56eeSaurel32             /* Still EOL ; stop reception */
7114f1c942bSMark McLoughlin             return -1;
712a65f56eeSaurel32         } else {
713a65f56eeSaurel32             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
714a65f56eeSaurel32         }
715a65f56eeSaurel32     }
716a65f56eeSaurel32 
717a65f56eeSaurel32     /* Save current position */
718a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
719a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
720a65f56eeSaurel32 
721a65f56eeSaurel32     /* Calculate the ethernet checksum */
722a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
723a65f56eeSaurel32 
724a65f56eeSaurel32     /* Put packet into RBA */
725a65f56eeSaurel32     DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
726a65f56eeSaurel32     address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
727dd820513SHervé Poussineau     address_space_rw(&s->as, address,
728dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
729a65f56eeSaurel32     address += rx_len;
730dd820513SHervé Poussineau     address_space_rw(&s->as, address,
731dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
732a65f56eeSaurel32     rx_len += 4;
733a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
734a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
735a65f56eeSaurel32     available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
736a65f56eeSaurel32     available -= rx_len / 2;
737a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
738a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
739a65f56eeSaurel32 
740a65f56eeSaurel32     /* Update status */
741a65f56eeSaurel32     if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) {
742a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
743a65f56eeSaurel32     }
744a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
745a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
746a65f56eeSaurel32     if (s->loopback_packet) {
747a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
748a65f56eeSaurel32         s->loopback_packet = 0;
749a65f56eeSaurel32     }
750a65f56eeSaurel32 
751a65f56eeSaurel32     /* Write status to memory */
752a65f56eeSaurel32     DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]);
753a65f56eeSaurel32     data[0 * width] = s->regs[SONIC_RCR]; /* status */
754a65f56eeSaurel32     data[1 * width] = rx_len; /* byte count */
755a65f56eeSaurel32     data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
756a65f56eeSaurel32     data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
757a65f56eeSaurel32     data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
758a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
759dd820513SHervé Poussineau     address_space_rw(&s->as, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA],
760dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
761a65f56eeSaurel32 
762a65f56eeSaurel32     /* Move to next descriptor */
763a65f56eeSaurel32     size = sizeof(uint16_t) * width;
764dd820513SHervé Poussineau     address_space_rw(&s->as,
765a65f56eeSaurel32         ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
766dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
767a65f56eeSaurel32     s->regs[SONIC_LLFA] = data[0 * width];
768a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
769a65f56eeSaurel32         /* EOL detected */
770a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
771a65f56eeSaurel32     } else {
772a65f56eeSaurel32         data[0 * width] = 0; /* in_use */
773dd820513SHervé Poussineau         address_space_rw(&s->as,
774a65f56eeSaurel32             ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
775409b52bfSHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
776a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
777a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
778a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
779a65f56eeSaurel32 
780a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
781a65f56eeSaurel32             /* Read next RRA */
7823df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
783a65f56eeSaurel32         }
784a65f56eeSaurel32     }
785a65f56eeSaurel32 
786a65f56eeSaurel32     /* Done */
787a65f56eeSaurel32     dp8393x_update_irq(s);
7884f1c942bSMark McLoughlin 
7894f1c942bSMark McLoughlin     return size;
790a65f56eeSaurel32 }
791a65f56eeSaurel32 
792104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
793a65f56eeSaurel32 {
794104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
795bc72ad67SAlex Bligh     timer_del(s->watchdog);
796a65f56eeSaurel32 
797bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
798a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
799a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
800a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
801a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
802a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
803a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
804a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
805a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
806a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
807a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
808a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
809a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
810a65f56eeSaurel32 
811a65f56eeSaurel32     /* Network cable is connected */
812a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
813a65f56eeSaurel32 
814a65f56eeSaurel32     dp8393x_update_irq(s);
815a65f56eeSaurel32 }
816a65f56eeSaurel32 
81705f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
8182be64a68SLaszlo Ersek     .type = NET_CLIENT_OPTIONS_KIND_NIC,
81905f41fe3SMark McLoughlin     .size = sizeof(NICState),
8203df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8213df5de64SHervé Poussineau     .receive = dp8393x_receive,
82205f41fe3SMark McLoughlin };
82305f41fe3SMark McLoughlin 
824104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
825a65f56eeSaurel32 {
826104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
827104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
828a65f56eeSaurel32 
829104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
83089ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
831104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
832104655a5SHervé Poussineau }
833a65f56eeSaurel32 
834104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
835104655a5SHervé Poussineau {
836104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
83789ae0ff9SHervé Poussineau     int i, checksum;
83889ae0ff9SHervé Poussineau     uint8_t *prom;
839a65f56eeSaurel32 
840104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
841104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
842104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
843104655a5SHervé Poussineau 
844104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
845104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
846104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
847104655a5SHervé Poussineau 
848bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
849a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
85089ae0ff9SHervé Poussineau 
85189ae0ff9SHervé Poussineau     memory_region_init_rom_device(&s->prom, OBJECT(dev), NULL, NULL,
85289ae0ff9SHervé Poussineau                                   "dp8393x-prom", SONIC_PROM_SIZE, NULL);
85389ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
85489ae0ff9SHervé Poussineau     checksum = 0;
85589ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
85689ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
85789ae0ff9SHervé Poussineau         checksum += prom[i];
85889ae0ff9SHervé Poussineau         if (checksum > 0xff) {
85989ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
86089ae0ff9SHervé Poussineau         }
86189ae0ff9SHervé Poussineau     }
86289ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
863a65f56eeSaurel32 }
864104655a5SHervé Poussineau 
8651670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
8661670735dSHervé Poussineau     .name = "dp8393x",
8671670735dSHervé Poussineau     .version_id = 0,
8681670735dSHervé Poussineau     .minimum_version_id = 0,
8691670735dSHervé Poussineau     .fields = (VMStateField []) {
8701670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
8711670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
8721670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
8731670735dSHervé Poussineau     }
8741670735dSHervé Poussineau };
8751670735dSHervé Poussineau 
876104655a5SHervé Poussineau static Property dp8393x_properties[] = {
877104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
878104655a5SHervé Poussineau     DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
879104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
880104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
881104655a5SHervé Poussineau };
882104655a5SHervé Poussineau 
883104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
884104655a5SHervé Poussineau {
885104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
886104655a5SHervé Poussineau 
887104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
888104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
889104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
8901670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
891104655a5SHervé Poussineau     dc->props = dp8393x_properties;
892*f6351288SHervé Poussineau     /* Reason: dma_mr property can't be set */
893*f6351288SHervé Poussineau     dc->cannot_instantiate_with_device_add_yet = true;
894104655a5SHervé Poussineau }
895104655a5SHervé Poussineau 
896104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
897104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
898104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
899104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
900104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
901104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
902104655a5SHervé Poussineau };
903104655a5SHervé Poussineau 
904104655a5SHervé Poussineau static void dp8393x_register_types(void)
905104655a5SHervé Poussineau {
906104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
907104655a5SHervé Poussineau }
908104655a5SHervé Poussineau 
909104655a5SHervé Poussineau type_init(dp8393x_register_types)
910