xref: /qemu/hw/net/dp8393x.c (revision be9208419865bc653f869e9b8cc22bdcbd9429fb)
1a65f56eeSaurel32 /*
2a65f56eeSaurel32  * QEMU NS SONIC DP8393x netcard
3a65f56eeSaurel32  *
4a65f56eeSaurel32  * Copyright (c) 2008-2009 Herve Poussineau
5a65f56eeSaurel32  *
6a65f56eeSaurel32  * This program is free software; you can redistribute it and/or
7a65f56eeSaurel32  * modify it under the terms of the GNU General Public License as
8a65f56eeSaurel32  * published by the Free Software Foundation; either version 2 of
9a65f56eeSaurel32  * the License, or (at your option) any later version.
10a65f56eeSaurel32  *
11a65f56eeSaurel32  * This program is distributed in the hope that it will be useful,
12a65f56eeSaurel32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13a65f56eeSaurel32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14a65f56eeSaurel32  * GNU General Public License for more details.
15a65f56eeSaurel32  *
16a65f56eeSaurel32  * You should have received a copy of the GNU General Public License along
178167ee88SBlue Swirl  * with this program; if not, see <http://www.gnu.org/licenses/>.
18a65f56eeSaurel32  */
19a65f56eeSaurel32 
20e8d40465SPeter Maydell #include "qemu/osdep.h"
2164552b6bSMarkus Armbruster #include "hw/irq.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
23104655a5SHervé Poussineau #include "hw/sysbus.h"
24d6454270SMarkus Armbruster #include "migration/vmstate.h"
251422e32dSPaolo Bonzini #include "net/net.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
28104655a5SHervé Poussineau #include "qemu/timer.h"
29f2f62c4dSHervé Poussineau #include <zlib.h>
30a65f56eeSaurel32 
31a65f56eeSaurel32 //#define DEBUG_SONIC
32a65f56eeSaurel32 
3389ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
34a65f56eeSaurel32 
35a65f56eeSaurel32 #ifdef DEBUG_SONIC
36001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
37001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
38a65f56eeSaurel32 static const char* reg_names[] = {
39a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
40a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
41a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
42a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
43a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
44a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
45a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
46a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
47a65f56eeSaurel32 #else
48001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
49a65f56eeSaurel32 #endif
50a65f56eeSaurel32 
51001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
52001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
53a65f56eeSaurel32 
54a65f56eeSaurel32 #define SONIC_CR     0x00
55a65f56eeSaurel32 #define SONIC_DCR    0x01
56a65f56eeSaurel32 #define SONIC_RCR    0x02
57a65f56eeSaurel32 #define SONIC_TCR    0x03
58a65f56eeSaurel32 #define SONIC_IMR    0x04
59a65f56eeSaurel32 #define SONIC_ISR    0x05
60a65f56eeSaurel32 #define SONIC_UTDA   0x06
61a65f56eeSaurel32 #define SONIC_CTDA   0x07
62a65f56eeSaurel32 #define SONIC_TPS    0x08
63a65f56eeSaurel32 #define SONIC_TFC    0x09
64a65f56eeSaurel32 #define SONIC_TSA0   0x0a
65a65f56eeSaurel32 #define SONIC_TSA1   0x0b
66a65f56eeSaurel32 #define SONIC_TFS    0x0c
67a65f56eeSaurel32 #define SONIC_URDA   0x0d
68a65f56eeSaurel32 #define SONIC_CRDA   0x0e
69a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
70a65f56eeSaurel32 #define SONIC_CRBA1  0x10
71a65f56eeSaurel32 #define SONIC_RBWC0  0x11
72a65f56eeSaurel32 #define SONIC_RBWC1  0x12
73a65f56eeSaurel32 #define SONIC_EOBC   0x13
74a65f56eeSaurel32 #define SONIC_URRA   0x14
75a65f56eeSaurel32 #define SONIC_RSA    0x15
76a65f56eeSaurel32 #define SONIC_REA    0x16
77a65f56eeSaurel32 #define SONIC_RRP    0x17
78a65f56eeSaurel32 #define SONIC_RWP    0x18
79a65f56eeSaurel32 #define SONIC_TRBA0  0x19
80a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
81a65f56eeSaurel32 #define SONIC_LLFA   0x1f
82a65f56eeSaurel32 #define SONIC_TTDA   0x20
83a65f56eeSaurel32 #define SONIC_CEP    0x21
84a65f56eeSaurel32 #define SONIC_CAP2   0x22
85a65f56eeSaurel32 #define SONIC_CAP1   0x23
86a65f56eeSaurel32 #define SONIC_CAP0   0x24
87a65f56eeSaurel32 #define SONIC_CE     0x25
88a65f56eeSaurel32 #define SONIC_CDP    0x26
89a65f56eeSaurel32 #define SONIC_CDC    0x27
90a65f56eeSaurel32 #define SONIC_SR     0x28
91a65f56eeSaurel32 #define SONIC_WT0    0x29
92a65f56eeSaurel32 #define SONIC_WT1    0x2a
93a65f56eeSaurel32 #define SONIC_RSC    0x2b
94a65f56eeSaurel32 #define SONIC_CRCT   0x2c
95a65f56eeSaurel32 #define SONIC_FAET   0x2d
96a65f56eeSaurel32 #define SONIC_MPT    0x2e
97a65f56eeSaurel32 #define SONIC_MDT    0x2f
98a65f56eeSaurel32 #define SONIC_DCR2   0x3f
99a65f56eeSaurel32 
100a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
101a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
102a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
103a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
104a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
105a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
106a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
107a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
108a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
109a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
110a65f56eeSaurel32 
111a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
112a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
113a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
114a65f56eeSaurel32 
115a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
116a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
117a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
118a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
119a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
120a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
121a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
122a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
123a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
124a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
125a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
126a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
127a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
128a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
129a65f56eeSaurel32 
130a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
131a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
132a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
133a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
134a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
135a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
136a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
137a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
138a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
139a65f56eeSaurel32 
140a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
141a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
142a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
143a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
144a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
145a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
146a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
147a65f56eeSaurel32 
148104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
149104655a5SHervé Poussineau #define DP8393X(obj) OBJECT_CHECK(dp8393xState, (obj), TYPE_DP8393X)
150104655a5SHervé Poussineau 
151a65f56eeSaurel32 typedef struct dp8393xState {
152104655a5SHervé Poussineau     SysBusDevice parent_obj;
153104655a5SHervé Poussineau 
154a65f56eeSaurel32     /* Hardware */
155104655a5SHervé Poussineau     uint8_t it_shift;
156*be920841SLaurent Vivier     bool big_endian;
157a65f56eeSaurel32     qemu_irq irq;
158a65f56eeSaurel32 #ifdef DEBUG_SONIC
159a65f56eeSaurel32     int irq_level;
160a65f56eeSaurel32 #endif
161a65f56eeSaurel32     QEMUTimer *watchdog;
162a65f56eeSaurel32     int64_t wt_last_update;
16305f41fe3SMark McLoughlin     NICConf conf;
16405f41fe3SMark McLoughlin     NICState *nic;
165024e5bb6SAvi Kivity     MemoryRegion mmio;
16689ae0ff9SHervé Poussineau     MemoryRegion prom;
167a65f56eeSaurel32 
168a65f56eeSaurel32     /* Registers */
169a65f56eeSaurel32     uint8_t cam[16][6];
170a65f56eeSaurel32     uint16_t regs[0x40];
171a65f56eeSaurel32 
172a65f56eeSaurel32     /* Temporaries */
173a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
174a65f56eeSaurel32     int loopback_packet;
175a65f56eeSaurel32 
176a65f56eeSaurel32     /* Memory access */
177104655a5SHervé Poussineau     void *dma_mr;
178dd820513SHervé Poussineau     AddressSpace as;
179a65f56eeSaurel32 } dp8393xState;
180a65f56eeSaurel32 
181581f7b12SPeter Maydell /* Accessor functions for values which are formed by
182581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
183581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
184581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
185581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
186581f7b12SPeter Maydell  */
187581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
188581f7b12SPeter Maydell {
189581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
190581f7b12SPeter Maydell }
191581f7b12SPeter Maydell 
192581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
193581f7b12SPeter Maydell {
194581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
195581f7b12SPeter Maydell }
196581f7b12SPeter Maydell 
197581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
198581f7b12SPeter Maydell {
199581f7b12SPeter Maydell     return (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA];
200581f7b12SPeter Maydell }
201581f7b12SPeter Maydell 
202581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
203581f7b12SPeter Maydell {
204581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
205581f7b12SPeter Maydell }
206581f7b12SPeter Maydell 
207581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
208581f7b12SPeter Maydell {
209581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
210581f7b12SPeter Maydell }
211581f7b12SPeter Maydell 
212581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
213581f7b12SPeter Maydell {
214581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
215581f7b12SPeter Maydell }
216581f7b12SPeter Maydell 
217581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
218581f7b12SPeter Maydell {
219581f7b12SPeter Maydell     return (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA];
220581f7b12SPeter Maydell }
221581f7b12SPeter Maydell 
222581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
223581f7b12SPeter Maydell {
224581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
225581f7b12SPeter Maydell }
226581f7b12SPeter Maydell 
227*be920841SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, uint16_t *base,
228*be920841SLaurent Vivier                             int offset)
229*be920841SLaurent Vivier {
230*be920841SLaurent Vivier     uint16_t val;
231*be920841SLaurent Vivier 
232*be920841SLaurent Vivier     if (s->big_endian) {
233*be920841SLaurent Vivier         val = be16_to_cpu(base[offset * width + width - 1]);
234*be920841SLaurent Vivier     } else {
235*be920841SLaurent Vivier         val = le16_to_cpu(base[offset * width]);
236*be920841SLaurent Vivier     }
237*be920841SLaurent Vivier     return val;
238*be920841SLaurent Vivier }
239*be920841SLaurent Vivier 
240*be920841SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, uint16_t *base, int offset,
241*be920841SLaurent Vivier                         uint16_t val)
242*be920841SLaurent Vivier {
243*be920841SLaurent Vivier     if (s->big_endian) {
244*be920841SLaurent Vivier         base[offset * width + width - 1] = cpu_to_be16(val);
245*be920841SLaurent Vivier     } else {
246*be920841SLaurent Vivier         base[offset * width] = cpu_to_le16(val);
247*be920841SLaurent Vivier     }
248*be920841SLaurent Vivier }
249*be920841SLaurent Vivier 
250a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
251a65f56eeSaurel32 {
252a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
253a65f56eeSaurel32 
254a65f56eeSaurel32 #ifdef DEBUG_SONIC
255a65f56eeSaurel32     if (level != s->irq_level) {
256a65f56eeSaurel32         s->irq_level = level;
257a65f56eeSaurel32         if (level) {
258a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
259a65f56eeSaurel32         } else {
260a65f56eeSaurel32             DPRINTF("lower irq\n");
261a65f56eeSaurel32         }
262a65f56eeSaurel32     }
263a65f56eeSaurel32 #endif
264a65f56eeSaurel32 
265a65f56eeSaurel32     qemu_set_irq(s->irq, level);
266a65f56eeSaurel32 }
267a65f56eeSaurel32 
2683df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
269a65f56eeSaurel32 {
270a65f56eeSaurel32     uint16_t data[8];
271a65f56eeSaurel32     int width, size;
272a65f56eeSaurel32     uint16_t index = 0;
273a65f56eeSaurel32 
274a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
275a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
276a65f56eeSaurel32 
277a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
278a65f56eeSaurel32         /* Fill current entry */
279581f7b12SPeter Maydell         address_space_rw(&s->as, dp8393x_cdp(s),
280dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
281*be920841SLaurent Vivier         s->cam[index][0] = dp8393x_get(s, width, data, 1) & 0xff;
282*be920841SLaurent Vivier         s->cam[index][1] = dp8393x_get(s, width, data, 1) >> 8;
283*be920841SLaurent Vivier         s->cam[index][2] = dp8393x_get(s, width, data, 2) & 0xff;
284*be920841SLaurent Vivier         s->cam[index][3] = dp8393x_get(s, width, data, 2) >> 8;
285*be920841SLaurent Vivier         s->cam[index][4] = dp8393x_get(s, width, data, 3) & 0xff;
286*be920841SLaurent Vivier         s->cam[index][5] = dp8393x_get(s, width, data, 3) >> 8;
287a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
288a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
289a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
290a65f56eeSaurel32         /* Move to next entry */
291a65f56eeSaurel32         s->regs[SONIC_CDC]--;
292a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
293a65f56eeSaurel32         index++;
294a65f56eeSaurel32     }
295a65f56eeSaurel32 
296a65f56eeSaurel32     /* Read CAM enable */
297581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_cdp(s),
298dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
299*be920841SLaurent Vivier     s->regs[SONIC_CE] = dp8393x_get(s, width, data, 0);
300a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
301a65f56eeSaurel32 
302a65f56eeSaurel32     /* Done */
303a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
304a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
305a65f56eeSaurel32     dp8393x_update_irq(s);
306a65f56eeSaurel32 }
307a65f56eeSaurel32 
3083df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
309a65f56eeSaurel32 {
310a65f56eeSaurel32     uint16_t data[8];
311a65f56eeSaurel32     int width, size;
312a65f56eeSaurel32 
313a65f56eeSaurel32     /* Read memory */
314a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
315a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
316581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_rrp(s),
317dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
318a65f56eeSaurel32 
319a65f56eeSaurel32     /* Update SONIC registers */
320*be920841SLaurent Vivier     s->regs[SONIC_CRBA0] = dp8393x_get(s, width, data, 0);
321*be920841SLaurent Vivier     s->regs[SONIC_CRBA1] = dp8393x_get(s, width, data, 1);
322*be920841SLaurent Vivier     s->regs[SONIC_RBWC0] = dp8393x_get(s, width, data, 2);
323*be920841SLaurent Vivier     s->regs[SONIC_RBWC1] = dp8393x_get(s, width, data, 3);
324a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
325a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
326a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
327a65f56eeSaurel32 
328a65f56eeSaurel32     /* Go to next entry */
329a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
330a65f56eeSaurel32 
331a65f56eeSaurel32     /* Handle wrap */
332a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
333a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
334a65f56eeSaurel32     }
335a65f56eeSaurel32 
336a65f56eeSaurel32     /* Check resource exhaustion */
337a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
338a65f56eeSaurel32     {
339a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
340a65f56eeSaurel32         dp8393x_update_irq(s);
341a65f56eeSaurel32     }
342a65f56eeSaurel32 
343a65f56eeSaurel32     /* Done */
344a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
345a65f56eeSaurel32 }
346a65f56eeSaurel32 
3473df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
348a65f56eeSaurel32 {
349bc72ad67SAlex Bligh     timer_del(s->watchdog);
350a65f56eeSaurel32 
351a65f56eeSaurel32     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
352a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
353a65f56eeSaurel32 }
354a65f56eeSaurel32 
3553df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
356a65f56eeSaurel32 {
357a65f56eeSaurel32     uint32_t ticks;
358a65f56eeSaurel32     int64_t delay;
359a65f56eeSaurel32 
360a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
361bc72ad67SAlex Bligh         timer_del(s->watchdog);
362a65f56eeSaurel32         return;
363a65f56eeSaurel32     }
364a65f56eeSaurel32 
365581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
366bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
36773bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
368bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
369a65f56eeSaurel32 }
370a65f56eeSaurel32 
3713df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
372a65f56eeSaurel32 {
373a65f56eeSaurel32     int64_t elapsed;
374a65f56eeSaurel32     uint32_t val;
375a65f56eeSaurel32 
376a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
377bc72ad67SAlex Bligh         timer_del(s->watchdog);
378a65f56eeSaurel32         return;
379a65f56eeSaurel32     }
380a65f56eeSaurel32 
381bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
382581f7b12SPeter Maydell     val = dp8393x_wt(s);
383a65f56eeSaurel32     val -= elapsed / 5000000;
384a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
385a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3863df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
387a65f56eeSaurel32 
388a65f56eeSaurel32 }
389a65f56eeSaurel32 
3903df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
391a65f56eeSaurel32 {
392a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3933df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
394a65f56eeSaurel32 }
395a65f56eeSaurel32 
3963df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
397a65f56eeSaurel32 {
398a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3993df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
400a65f56eeSaurel32 }
401a65f56eeSaurel32 
4024594f93aSFam Zheng static int dp8393x_can_receive(NetClientState *nc);
4034594f93aSFam Zheng 
4043df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
405a65f56eeSaurel32 {
406a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
4074594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
4084594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
4094594f93aSFam Zheng     }
410a65f56eeSaurel32 }
411a65f56eeSaurel32 
4123df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
413a65f56eeSaurel32 {
414a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
415a65f56eeSaurel32 }
416a65f56eeSaurel32 
4173df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
418a65f56eeSaurel32 {
419b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
420a65f56eeSaurel32     uint16_t data[12];
421a65f56eeSaurel32     int width, size;
422a65f56eeSaurel32     int tx_len, len;
423a65f56eeSaurel32     uint16_t i;
424a65f56eeSaurel32 
425a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
426a65f56eeSaurel32 
427a65f56eeSaurel32     while (1) {
428a65f56eeSaurel32         /* Read memory */
429a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
430a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
431581f7b12SPeter Maydell         DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
432dd820513SHervé Poussineau         address_space_rw(&s->as,
433581f7b12SPeter Maydell             dp8393x_ttda(s) + sizeof(uint16_t) * width,
434dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
435a65f56eeSaurel32         tx_len = 0;
436a65f56eeSaurel32 
437a65f56eeSaurel32         /* Update registers */
438*be920841SLaurent Vivier         s->regs[SONIC_TCR] = dp8393x_get(s, width, data, 0) & 0xf000;
439*be920841SLaurent Vivier         s->regs[SONIC_TPS] = dp8393x_get(s, width, data, 1);
440*be920841SLaurent Vivier         s->regs[SONIC_TFC] = dp8393x_get(s, width, data, 2);
441*be920841SLaurent Vivier         s->regs[SONIC_TSA0] = dp8393x_get(s, width, data, 3);
442*be920841SLaurent Vivier         s->regs[SONIC_TSA1] = dp8393x_get(s, width, data, 4);
443*be920841SLaurent Vivier         s->regs[SONIC_TFS] = dp8393x_get(s, width, data, 5);
444a65f56eeSaurel32 
445a65f56eeSaurel32         /* Handle programmable interrupt */
446a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
447a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
448a65f56eeSaurel32         } else {
449a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
450a65f56eeSaurel32         }
451a65f56eeSaurel32 
452a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
453a65f56eeSaurel32             /* Append fragment */
454a65f56eeSaurel32             len = s->regs[SONIC_TFS];
455a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
456a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
457a65f56eeSaurel32             }
458581f7b12SPeter Maydell             address_space_rw(&s->as, dp8393x_tsa(s),
459dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0);
460a65f56eeSaurel32             tx_len += len;
461a65f56eeSaurel32 
462a65f56eeSaurel32             i++;
463a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
464a65f56eeSaurel32                 /* Read next fragment details */
465a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
466dd820513SHervé Poussineau                 address_space_rw(&s->as,
467581f7b12SPeter Maydell                     dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width,
468dd820513SHervé Poussineau                     MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
469*be920841SLaurent Vivier                 s->regs[SONIC_TSA0] = dp8393x_get(s, width, data, 0);
470*be920841SLaurent Vivier                 s->regs[SONIC_TSA1] = dp8393x_get(s, width, data, 1);
471*be920841SLaurent Vivier                 s->regs[SONIC_TFS] = dp8393x_get(s, width, data, 2);
472a65f56eeSaurel32             }
473a65f56eeSaurel32         }
474a65f56eeSaurel32 
475a65f56eeSaurel32         /* Handle Ethernet checksum */
476a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
477a65f56eeSaurel32             /* Don't append FCS there, to look like slirp packets
478a65f56eeSaurel32              * which don't have one */
479a65f56eeSaurel32         } else {
480a65f56eeSaurel32             /* Remove existing FCS */
481a65f56eeSaurel32             tx_len -= 4;
482a65f56eeSaurel32         }
483a65f56eeSaurel32 
484a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
485a65f56eeSaurel32             /* Loopback */
486a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
487b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
488a65f56eeSaurel32                 s->loopback_packet = 1;
489b356f76dSJason Wang                 nc->info->receive(nc, s->tx_buffer, tx_len);
490a65f56eeSaurel32             }
491a65f56eeSaurel32         } else {
492a65f56eeSaurel32             /* Transmit packet */
493b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
494a65f56eeSaurel32         }
495a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
496a65f56eeSaurel32 
497a65f56eeSaurel32         /* Write status */
498*be920841SLaurent Vivier         dp8393x_put(s, width, data, 0,
499*be920841SLaurent Vivier                     s->regs[SONIC_TCR] & 0x0fff); /* status */
500a65f56eeSaurel32         size = sizeof(uint16_t) * width;
501dd820513SHervé Poussineau         address_space_rw(&s->as,
502581f7b12SPeter Maydell             dp8393x_ttda(s),
503dd820513SHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
504a65f56eeSaurel32 
505a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
506a65f56eeSaurel32             /* Read footer of packet */
507a65f56eeSaurel32             size = sizeof(uint16_t) * width;
508dd820513SHervé Poussineau             address_space_rw(&s->as,
509581f7b12SPeter Maydell                 dp8393x_ttda(s) +
510581f7b12SPeter Maydell                              sizeof(uint16_t) *
511581f7b12SPeter Maydell                              (4 + 3 * s->regs[SONIC_TFC]) * width,
512dd820513SHervé Poussineau                 MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
513*be920841SLaurent Vivier             s->regs[SONIC_CTDA] = dp8393x_get(s, width, data, 0) & ~0x1;
514*be920841SLaurent Vivier             if (dp8393x_get(s, width, data, 0) & 0x1) {
515a65f56eeSaurel32                 /* EOL detected */
516a65f56eeSaurel32                 break;
517a65f56eeSaurel32             }
518a65f56eeSaurel32         }
519a65f56eeSaurel32     }
520a65f56eeSaurel32 
521a65f56eeSaurel32     /* Done */
522a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
523a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
524a65f56eeSaurel32     dp8393x_update_irq(s);
525a65f56eeSaurel32 }
526a65f56eeSaurel32 
5273df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
528a65f56eeSaurel32 {
529a65f56eeSaurel32     /* Nothing to do */
530a65f56eeSaurel32 }
531a65f56eeSaurel32 
5323df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
533a65f56eeSaurel32 {
534a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
535a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
536a65f56eeSaurel32         return;
537a65f56eeSaurel32     }
538a65f56eeSaurel32 
539a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
540a65f56eeSaurel32 
541a65f56eeSaurel32     if (command & SONIC_CR_HTX)
5423df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
543a65f56eeSaurel32     if (command & SONIC_CR_TXP)
5443df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
545a65f56eeSaurel32     if (command & SONIC_CR_RXDIS)
5463df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
547a65f56eeSaurel32     if (command & SONIC_CR_RXEN)
5483df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
549a65f56eeSaurel32     if (command & SONIC_CR_STP)
5503df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
551a65f56eeSaurel32     if (command & SONIC_CR_ST)
5523df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
553a65f56eeSaurel32     if (command & SONIC_CR_RST)
5543df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
555a65f56eeSaurel32     if (command & SONIC_CR_RRRA)
5563df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
557a65f56eeSaurel32     if (command & SONIC_CR_LCAM)
5583df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
559a65f56eeSaurel32 }
560a65f56eeSaurel32 
56184689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
562a65f56eeSaurel32 {
56384689cbbSHervé Poussineau     dp8393xState *s = opaque;
56484689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
565a65f56eeSaurel32     uint16_t val = 0;
566a65f56eeSaurel32 
567a65f56eeSaurel32     switch (reg) {
568a65f56eeSaurel32         /* Update data before reading it */
569a65f56eeSaurel32         case SONIC_WT0:
570a65f56eeSaurel32         case SONIC_WT1:
5713df5de64SHervé Poussineau             dp8393x_update_wt_regs(s);
572a65f56eeSaurel32             val = s->regs[reg];
573a65f56eeSaurel32             break;
574a65f56eeSaurel32         /* Accept read to some registers only when in reset mode */
575a65f56eeSaurel32         case SONIC_CAP2:
576a65f56eeSaurel32         case SONIC_CAP1:
577a65f56eeSaurel32         case SONIC_CAP0:
578a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
579a65f56eeSaurel32                 val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
580a65f56eeSaurel32                 val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
581a65f56eeSaurel32             }
582a65f56eeSaurel32             break;
583a65f56eeSaurel32         /* All other registers have no special contrainst */
584a65f56eeSaurel32         default:
585a65f56eeSaurel32             val = s->regs[reg];
586a65f56eeSaurel32     }
587a65f56eeSaurel32 
588a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
589a65f56eeSaurel32 
590a65f56eeSaurel32     return val;
591a65f56eeSaurel32 }
592a65f56eeSaurel32 
59384689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
59484689cbbSHervé Poussineau                           unsigned int size)
595a65f56eeSaurel32 {
59684689cbbSHervé Poussineau     dp8393xState *s = opaque;
59784689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
59884689cbbSHervé Poussineau 
59984689cbbSHervé Poussineau     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]);
600a65f56eeSaurel32 
601a65f56eeSaurel32     switch (reg) {
602a65f56eeSaurel32         /* Command register */
603a65f56eeSaurel32         case SONIC_CR:
6043df5de64SHervé Poussineau             dp8393x_do_command(s, data);
605a65f56eeSaurel32             break;
606a65f56eeSaurel32         /* Prevent write to read-only registers */
607a65f56eeSaurel32         case SONIC_CAP2:
608a65f56eeSaurel32         case SONIC_CAP1:
609a65f56eeSaurel32         case SONIC_CAP0:
610a65f56eeSaurel32         case SONIC_SR:
611a65f56eeSaurel32         case SONIC_MDT:
612a65f56eeSaurel32             DPRINTF("writing to reg %d invalid\n", reg);
613a65f56eeSaurel32             break;
614a65f56eeSaurel32         /* Accept write to some registers only when in reset mode */
615a65f56eeSaurel32         case SONIC_DCR:
616a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
61784689cbbSHervé Poussineau                 s->regs[reg] = data & 0xbfff;
618a65f56eeSaurel32             } else {
619a65f56eeSaurel32                 DPRINTF("writing to DCR invalid\n");
620a65f56eeSaurel32             }
621a65f56eeSaurel32             break;
622a65f56eeSaurel32         case SONIC_DCR2:
623a65f56eeSaurel32             if (s->regs[SONIC_CR] & SONIC_CR_RST) {
62484689cbbSHervé Poussineau                 s->regs[reg] = data & 0xf017;
625a65f56eeSaurel32             } else {
626a65f56eeSaurel32                 DPRINTF("writing to DCR2 invalid\n");
627a65f56eeSaurel32             }
628a65f56eeSaurel32             break;
629a65f56eeSaurel32         /* 12 lower bytes are Read Only */
630a65f56eeSaurel32         case SONIC_TCR:
63184689cbbSHervé Poussineau             s->regs[reg] = data & 0xf000;
632a65f56eeSaurel32             break;
633a65f56eeSaurel32         /* 9 lower bytes are Read Only */
634a65f56eeSaurel32         case SONIC_RCR:
63584689cbbSHervé Poussineau             s->regs[reg] = data & 0xffe0;
636a65f56eeSaurel32             break;
637a65f56eeSaurel32         /* Ignore most significant bit */
638a65f56eeSaurel32         case SONIC_IMR:
63984689cbbSHervé Poussineau             s->regs[reg] = data & 0x7fff;
640a65f56eeSaurel32             dp8393x_update_irq(s);
641a65f56eeSaurel32             break;
642a65f56eeSaurel32         /* Clear bits by writing 1 to them */
643a65f56eeSaurel32         case SONIC_ISR:
64484689cbbSHervé Poussineau             data &= s->regs[reg];
64584689cbbSHervé Poussineau             s->regs[reg] &= ~data;
64684689cbbSHervé Poussineau             if (data & SONIC_ISR_RBE) {
6473df5de64SHervé Poussineau                 dp8393x_do_read_rra(s);
648a65f56eeSaurel32             }
649a65f56eeSaurel32             dp8393x_update_irq(s);
6504594f93aSFam Zheng             if (dp8393x_can_receive(s->nic->ncs)) {
6514594f93aSFam Zheng                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
6524594f93aSFam Zheng             }
653a65f56eeSaurel32             break;
654a65f56eeSaurel32         /* Ignore least significant bit */
655a65f56eeSaurel32         case SONIC_RSA:
656a65f56eeSaurel32         case SONIC_REA:
657a65f56eeSaurel32         case SONIC_RRP:
658a65f56eeSaurel32         case SONIC_RWP:
65984689cbbSHervé Poussineau             s->regs[reg] = data & 0xfffe;
660a65f56eeSaurel32             break;
661a65f56eeSaurel32         /* Invert written value for some registers */
662a65f56eeSaurel32         case SONIC_CRCT:
663a65f56eeSaurel32         case SONIC_FAET:
664a65f56eeSaurel32         case SONIC_MPT:
66584689cbbSHervé Poussineau             s->regs[reg] = data ^ 0xffff;
666a65f56eeSaurel32             break;
667a65f56eeSaurel32         /* All other registers have no special contrainst */
668a65f56eeSaurel32         default:
66984689cbbSHervé Poussineau             s->regs[reg] = data;
670a65f56eeSaurel32     }
671a65f56eeSaurel32 
672a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6733df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
674a65f56eeSaurel32     }
675a65f56eeSaurel32 }
676a65f56eeSaurel32 
67784689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
67884689cbbSHervé Poussineau     .read = dp8393x_read,
67984689cbbSHervé Poussineau     .write = dp8393x_write,
68084689cbbSHervé Poussineau     .impl.min_access_size = 2,
68184689cbbSHervé Poussineau     .impl.max_access_size = 2,
68284689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
68384689cbbSHervé Poussineau };
68484689cbbSHervé Poussineau 
685a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
686a65f56eeSaurel32 {
687a65f56eeSaurel32     dp8393xState *s = opaque;
688a65f56eeSaurel32 
689a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
690a65f56eeSaurel32         return;
691a65f56eeSaurel32     }
692a65f56eeSaurel32 
693a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
694a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
6953df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
696a65f56eeSaurel32 
697a65f56eeSaurel32     /* Signal underflow */
698a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
699a65f56eeSaurel32     dp8393x_update_irq(s);
700a65f56eeSaurel32 }
701a65f56eeSaurel32 
7023df5de64SHervé Poussineau static int dp8393x_can_receive(NetClientState *nc)
703a65f56eeSaurel32 {
704cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
705a65f56eeSaurel32 
706a65f56eeSaurel32     if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
707a65f56eeSaurel32         return 0;
708a65f56eeSaurel32     if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
709a65f56eeSaurel32         return 0;
710a65f56eeSaurel32     return 1;
711a65f56eeSaurel32 }
712a65f56eeSaurel32 
7133df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
7143df5de64SHervé Poussineau                                   int size)
715a65f56eeSaurel32 {
716a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
717a65f56eeSaurel32     int i;
718a65f56eeSaurel32 
719a65f56eeSaurel32     /* Check promiscuous mode */
720a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
721a65f56eeSaurel32         return 0;
722a65f56eeSaurel32     }
723a65f56eeSaurel32 
724a65f56eeSaurel32     /* Check multicast packets */
725a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
726a65f56eeSaurel32         return SONIC_RCR_MC;
727a65f56eeSaurel32     }
728a65f56eeSaurel32 
729a65f56eeSaurel32     /* Check broadcast */
730a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
731a65f56eeSaurel32         return SONIC_RCR_BC;
732a65f56eeSaurel32     }
733a65f56eeSaurel32 
734a65f56eeSaurel32     /* Check CAM */
735a65f56eeSaurel32     for (i = 0; i < 16; i++) {
736a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
737a65f56eeSaurel32              /* Entry enabled */
738a65f56eeSaurel32              if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
739a65f56eeSaurel32                  return 0;
740a65f56eeSaurel32              }
741a65f56eeSaurel32         }
742a65f56eeSaurel32     }
743a65f56eeSaurel32 
744a65f56eeSaurel32     return -1;
745a65f56eeSaurel32 }
746a65f56eeSaurel32 
7473df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7483df5de64SHervé Poussineau                                size_t size)
749a65f56eeSaurel32 {
750cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
751a65f56eeSaurel32     uint16_t data[10];
752a65f56eeSaurel32     int packet_type;
753a65f56eeSaurel32     uint32_t available, address;
754a65f56eeSaurel32     int width, rx_len = size;
755a65f56eeSaurel32     uint32_t checksum;
756a65f56eeSaurel32 
757a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
758a65f56eeSaurel32 
759a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
760a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
761a65f56eeSaurel32 
7623df5de64SHervé Poussineau     packet_type = dp8393x_receive_filter(s, buf, size);
763a65f56eeSaurel32     if (packet_type < 0) {
764a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
7654f1c942bSMark McLoughlin         return -1;
766a65f56eeSaurel32     }
767a65f56eeSaurel32 
768a65f56eeSaurel32     /* XXX: Check byte ordering */
769a65f56eeSaurel32 
770a65f56eeSaurel32     /* Check for EOL */
771a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
772a65f56eeSaurel32         /* Are we still in resource exhaustion? */
773a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
774581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
775dd820513SHervé Poussineau         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
776dd820513SHervé Poussineau                          (uint8_t *)data, size, 0);
777*be920841SLaurent Vivier         if (dp8393x_get(s, width, data, 0) & 0x1) {
778a65f56eeSaurel32             /* Still EOL ; stop reception */
7794f1c942bSMark McLoughlin             return -1;
780a65f56eeSaurel32         } else {
781a65f56eeSaurel32             s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
782a65f56eeSaurel32         }
783a65f56eeSaurel32     }
784a65f56eeSaurel32 
785a65f56eeSaurel32     /* Save current position */
786a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
787a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
788a65f56eeSaurel32 
789a65f56eeSaurel32     /* Calculate the ethernet checksum */
790a65f56eeSaurel32     checksum = cpu_to_le32(crc32(0, buf, rx_len));
791a65f56eeSaurel32 
792a65f56eeSaurel32     /* Put packet into RBA */
793581f7b12SPeter Maydell     DPRINTF("Receive packet at %08x\n", dp8393x_crba(s));
794581f7b12SPeter Maydell     address = dp8393x_crba(s);
795dd820513SHervé Poussineau     address_space_rw(&s->as, address,
796dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1);
797a65f56eeSaurel32     address += rx_len;
798dd820513SHervé Poussineau     address_space_rw(&s->as, address,
799dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1);
800a65f56eeSaurel32     rx_len += 4;
801a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
802a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
803581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
804a65f56eeSaurel32     available -= rx_len / 2;
805a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
806a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
807a65f56eeSaurel32 
808a65f56eeSaurel32     /* Update status */
809581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
810a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
811a65f56eeSaurel32     }
812a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
813a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
814a65f56eeSaurel32     if (s->loopback_packet) {
815a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
816a65f56eeSaurel32         s->loopback_packet = 0;
817a65f56eeSaurel32     }
818a65f56eeSaurel32 
819a65f56eeSaurel32     /* Write status to memory */
820581f7b12SPeter Maydell     DPRINTF("Write status at %08x\n", dp8393x_crda(s));
821*be920841SLaurent Vivier     dp8393x_put(s, width, data, 0, s->regs[SONIC_RCR]); /* status */
822*be920841SLaurent Vivier     dp8393x_put(s, width, data, 1, rx_len); /* byte count */
823*be920841SLaurent Vivier     dp8393x_put(s, width, data, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
824*be920841SLaurent Vivier     dp8393x_put(s, width, data, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
825*be920841SLaurent Vivier     dp8393x_put(s, width, data, 4, s->regs[SONIC_RSC]); /* seq_no */
826a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
827581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_crda(s),
828dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1);
829a65f56eeSaurel32 
830a65f56eeSaurel32     /* Move to next descriptor */
831a65f56eeSaurel32     size = sizeof(uint16_t) * width;
832581f7b12SPeter Maydell     address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
833dd820513SHervé Poussineau         MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0);
834*be920841SLaurent Vivier     s->regs[SONIC_LLFA] = dp8393x_get(s, width, data, 0);
835a65f56eeSaurel32     if (s->regs[SONIC_LLFA] & 0x1) {
836a65f56eeSaurel32         /* EOL detected */
837a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
838a65f56eeSaurel32     } else {
839*be920841SLaurent Vivier         dp8393x_put(s, width, data, 0, 0); /* in_use */
840581f7b12SPeter Maydell         address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 6 * width,
841409b52bfSHervé Poussineau             MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1);
842a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
843a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
844a65f56eeSaurel32         s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
845a65f56eeSaurel32 
846a65f56eeSaurel32         if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
847a65f56eeSaurel32             /* Read next RRA */
8483df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
849a65f56eeSaurel32         }
850a65f56eeSaurel32     }
851a65f56eeSaurel32 
852a65f56eeSaurel32     /* Done */
853a65f56eeSaurel32     dp8393x_update_irq(s);
8544f1c942bSMark McLoughlin 
8554f1c942bSMark McLoughlin     return size;
856a65f56eeSaurel32 }
857a65f56eeSaurel32 
858104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
859a65f56eeSaurel32 {
860104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
861bc72ad67SAlex Bligh     timer_del(s->watchdog);
862a65f56eeSaurel32 
863bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
864a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
865a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
866a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
867a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
868a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
869a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
870a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
871a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
872a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
873a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
874a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
875a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
876a65f56eeSaurel32 
877a65f56eeSaurel32     /* Network cable is connected */
878a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
879a65f56eeSaurel32 
880a65f56eeSaurel32     dp8393x_update_irq(s);
881a65f56eeSaurel32 }
882a65f56eeSaurel32 
88305f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
884f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
88505f41fe3SMark McLoughlin     .size = sizeof(NICState),
8863df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8873df5de64SHervé Poussineau     .receive = dp8393x_receive,
88805f41fe3SMark McLoughlin };
88905f41fe3SMark McLoughlin 
890104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
891a65f56eeSaurel32 {
892104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
893104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
894a65f56eeSaurel32 
895104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
89689ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
897104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
898104655a5SHervé Poussineau }
899a65f56eeSaurel32 
900104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
901104655a5SHervé Poussineau {
902104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
90389ae0ff9SHervé Poussineau     int i, checksum;
90489ae0ff9SHervé Poussineau     uint8_t *prom;
90552579c68SHervé Poussineau     Error *local_err = NULL;
906a65f56eeSaurel32 
907104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
908104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
909104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
910104655a5SHervé Poussineau 
911104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
912104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
913104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
914104655a5SHervé Poussineau 
915bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
916a65f56eeSaurel32     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
91789ae0ff9SHervé Poussineau 
9188fad0a65SPeter Maydell     memory_region_init_ram(&s->prom, OBJECT(dev),
91952579c68SHervé Poussineau                            "dp8393x-prom", SONIC_PROM_SIZE, &local_err);
92052579c68SHervé Poussineau     if (local_err) {
92152579c68SHervé Poussineau         error_propagate(errp, local_err);
92252579c68SHervé Poussineau         return;
92352579c68SHervé Poussineau     }
92452579c68SHervé Poussineau     memory_region_set_readonly(&s->prom, true);
92589ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
92689ae0ff9SHervé Poussineau     checksum = 0;
92789ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
92889ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
92989ae0ff9SHervé Poussineau         checksum += prom[i];
93089ae0ff9SHervé Poussineau         if (checksum > 0xff) {
93189ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
93289ae0ff9SHervé Poussineau         }
93389ae0ff9SHervé Poussineau     }
93489ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
935a65f56eeSaurel32 }
936104655a5SHervé Poussineau 
9371670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9381670735dSHervé Poussineau     .name = "dp8393x",
9391670735dSHervé Poussineau     .version_id = 0,
9401670735dSHervé Poussineau     .minimum_version_id = 0,
9411670735dSHervé Poussineau     .fields = (VMStateField []) {
9421670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
9431670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
9441670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9451670735dSHervé Poussineau     }
9461670735dSHervé Poussineau };
9471670735dSHervé Poussineau 
948104655a5SHervé Poussineau static Property dp8393x_properties[] = {
949104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
950104655a5SHervé Poussineau     DEFINE_PROP_PTR("dma_mr", dp8393xState, dma_mr),
951104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
952*be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
953104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
954104655a5SHervé Poussineau };
955104655a5SHervé Poussineau 
956104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
957104655a5SHervé Poussineau {
958104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
959104655a5SHervé Poussineau 
960104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
961104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
962104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
9631670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
964104655a5SHervé Poussineau     dc->props = dp8393x_properties;
965f6351288SHervé Poussineau     /* Reason: dma_mr property can't be set */
966e90f2a8cSEduardo Habkost     dc->user_creatable = false;
967104655a5SHervé Poussineau }
968104655a5SHervé Poussineau 
969104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
970104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
971104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
972104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
973104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
974104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
975104655a5SHervé Poussineau };
976104655a5SHervé Poussineau 
977104655a5SHervé Poussineau static void dp8393x_register_types(void)
978104655a5SHervé Poussineau {
979104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
980104655a5SHervé Poussineau }
981104655a5SHervé Poussineau 
982104655a5SHervé Poussineau type_init(dp8393x_register_types)
983