xref: /qemu/hw/net/dp8393x.c (revision 1ca82a8db03ea3c352d581753b22e8dac4ea8047)
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>
30db1015e9SEduardo Habkost #include "qom/object.h"
31a65f56eeSaurel32 
32*1ca82a8dSMark Cave-Ayland /* #define DEBUG_SONIC */
33a65f56eeSaurel32 
3489ae0ff9SHervé Poussineau #define SONIC_PROM_SIZE 0x1000
35a65f56eeSaurel32 
36a65f56eeSaurel32 #ifdef DEBUG_SONIC
37001faf32SBlue Swirl #define DPRINTF(fmt, ...) \
38001faf32SBlue Swirl do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
39a65f56eeSaurel32 static const char *reg_names[] = {
40a65f56eeSaurel32     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
41a65f56eeSaurel32     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
42a65f56eeSaurel32     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
43a65f56eeSaurel32     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
44a65f56eeSaurel32     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
45a65f56eeSaurel32     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
46a65f56eeSaurel32     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
47a65f56eeSaurel32     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
48a65f56eeSaurel32 #else
49001faf32SBlue Swirl #define DPRINTF(fmt, ...) do {} while (0)
50a65f56eeSaurel32 #endif
51a65f56eeSaurel32 
52001faf32SBlue Swirl #define SONIC_ERROR(fmt, ...) \
53001faf32SBlue Swirl do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
54a65f56eeSaurel32 
55a65f56eeSaurel32 #define SONIC_CR     0x00
56a65f56eeSaurel32 #define SONIC_DCR    0x01
57a65f56eeSaurel32 #define SONIC_RCR    0x02
58a65f56eeSaurel32 #define SONIC_TCR    0x03
59a65f56eeSaurel32 #define SONIC_IMR    0x04
60a65f56eeSaurel32 #define SONIC_ISR    0x05
61a65f56eeSaurel32 #define SONIC_UTDA   0x06
62a65f56eeSaurel32 #define SONIC_CTDA   0x07
63a65f56eeSaurel32 #define SONIC_TPS    0x08
64a65f56eeSaurel32 #define SONIC_TFC    0x09
65a65f56eeSaurel32 #define SONIC_TSA0   0x0a
66a65f56eeSaurel32 #define SONIC_TSA1   0x0b
67a65f56eeSaurel32 #define SONIC_TFS    0x0c
68a65f56eeSaurel32 #define SONIC_URDA   0x0d
69a65f56eeSaurel32 #define SONIC_CRDA   0x0e
70a65f56eeSaurel32 #define SONIC_CRBA0  0x0f
71a65f56eeSaurel32 #define SONIC_CRBA1  0x10
72a65f56eeSaurel32 #define SONIC_RBWC0  0x11
73a65f56eeSaurel32 #define SONIC_RBWC1  0x12
74a65f56eeSaurel32 #define SONIC_EOBC   0x13
75a65f56eeSaurel32 #define SONIC_URRA   0x14
76a65f56eeSaurel32 #define SONIC_RSA    0x15
77a65f56eeSaurel32 #define SONIC_REA    0x16
78a65f56eeSaurel32 #define SONIC_RRP    0x17
79a65f56eeSaurel32 #define SONIC_RWP    0x18
80a65f56eeSaurel32 #define SONIC_TRBA0  0x19
81a65f56eeSaurel32 #define SONIC_TRBA1  0x1a
82a65f56eeSaurel32 #define SONIC_LLFA   0x1f
83a65f56eeSaurel32 #define SONIC_TTDA   0x20
84a65f56eeSaurel32 #define SONIC_CEP    0x21
85a65f56eeSaurel32 #define SONIC_CAP2   0x22
86a65f56eeSaurel32 #define SONIC_CAP1   0x23
87a65f56eeSaurel32 #define SONIC_CAP0   0x24
88a65f56eeSaurel32 #define SONIC_CE     0x25
89a65f56eeSaurel32 #define SONIC_CDP    0x26
90a65f56eeSaurel32 #define SONIC_CDC    0x27
91a65f56eeSaurel32 #define SONIC_SR     0x28
92a65f56eeSaurel32 #define SONIC_WT0    0x29
93a65f56eeSaurel32 #define SONIC_WT1    0x2a
94a65f56eeSaurel32 #define SONIC_RSC    0x2b
95a65f56eeSaurel32 #define SONIC_CRCT   0x2c
96a65f56eeSaurel32 #define SONIC_FAET   0x2d
97a65f56eeSaurel32 #define SONIC_MPT    0x2e
98a65f56eeSaurel32 #define SONIC_MDT    0x2f
99a65f56eeSaurel32 #define SONIC_DCR2   0x3f
100a65f56eeSaurel32 
101a65f56eeSaurel32 #define SONIC_CR_HTX     0x0001
102a65f56eeSaurel32 #define SONIC_CR_TXP     0x0002
103a65f56eeSaurel32 #define SONIC_CR_RXDIS   0x0004
104a65f56eeSaurel32 #define SONIC_CR_RXEN    0x0008
105a65f56eeSaurel32 #define SONIC_CR_STP     0x0010
106a65f56eeSaurel32 #define SONIC_CR_ST      0x0020
107a65f56eeSaurel32 #define SONIC_CR_RST     0x0080
108a65f56eeSaurel32 #define SONIC_CR_RRRA    0x0100
109a65f56eeSaurel32 #define SONIC_CR_LCAM    0x0200
110a65f56eeSaurel32 #define SONIC_CR_MASK    0x03bf
111a65f56eeSaurel32 
112a65f56eeSaurel32 #define SONIC_DCR_DW     0x0020
113a65f56eeSaurel32 #define SONIC_DCR_LBR    0x2000
114a65f56eeSaurel32 #define SONIC_DCR_EXBUS  0x8000
115a65f56eeSaurel32 
116a65f56eeSaurel32 #define SONIC_RCR_PRX    0x0001
117a65f56eeSaurel32 #define SONIC_RCR_LBK    0x0002
118a65f56eeSaurel32 #define SONIC_RCR_FAER   0x0004
119a65f56eeSaurel32 #define SONIC_RCR_CRCR   0x0008
120a65f56eeSaurel32 #define SONIC_RCR_CRS    0x0020
121a65f56eeSaurel32 #define SONIC_RCR_LPKT   0x0040
122a65f56eeSaurel32 #define SONIC_RCR_BC     0x0080
123a65f56eeSaurel32 #define SONIC_RCR_MC     0x0100
124a65f56eeSaurel32 #define SONIC_RCR_LB0    0x0200
125a65f56eeSaurel32 #define SONIC_RCR_LB1    0x0400
126a65f56eeSaurel32 #define SONIC_RCR_AMC    0x0800
127a65f56eeSaurel32 #define SONIC_RCR_PRO    0x1000
128a65f56eeSaurel32 #define SONIC_RCR_BRD    0x2000
129a65f56eeSaurel32 #define SONIC_RCR_RNT    0x4000
130a65f56eeSaurel32 
131a65f56eeSaurel32 #define SONIC_TCR_PTX    0x0001
132a65f56eeSaurel32 #define SONIC_TCR_BCM    0x0002
133a65f56eeSaurel32 #define SONIC_TCR_FU     0x0004
134a65f56eeSaurel32 #define SONIC_TCR_EXC    0x0040
135a65f56eeSaurel32 #define SONIC_TCR_CRSL   0x0080
136a65f56eeSaurel32 #define SONIC_TCR_NCRS   0x0100
137a65f56eeSaurel32 #define SONIC_TCR_EXD    0x0400
138a65f56eeSaurel32 #define SONIC_TCR_CRCI   0x2000
139a65f56eeSaurel32 #define SONIC_TCR_PINT   0x8000
140a65f56eeSaurel32 
141ada74315SFinn Thain #define SONIC_ISR_RBAE   0x0010
142a65f56eeSaurel32 #define SONIC_ISR_RBE    0x0020
143a65f56eeSaurel32 #define SONIC_ISR_RDE    0x0040
144a65f56eeSaurel32 #define SONIC_ISR_TC     0x0080
145a65f56eeSaurel32 #define SONIC_ISR_TXDN   0x0200
146a65f56eeSaurel32 #define SONIC_ISR_PKTRX  0x0400
147a65f56eeSaurel32 #define SONIC_ISR_PINT   0x0800
148a65f56eeSaurel32 #define SONIC_ISR_LCD    0x1000
149a65f56eeSaurel32 
15088f632fbSFinn Thain #define SONIC_DESC_EOL   0x0001
15188f632fbSFinn Thain #define SONIC_DESC_ADDR  0xFFFE
15288f632fbSFinn Thain 
153104655a5SHervé Poussineau #define TYPE_DP8393X "dp8393x"
1548063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(dp8393xState, DP8393X)
155104655a5SHervé Poussineau 
156db1015e9SEduardo Habkost struct dp8393xState {
157104655a5SHervé Poussineau     SysBusDevice parent_obj;
158104655a5SHervé Poussineau 
159a65f56eeSaurel32     /* Hardware */
160104655a5SHervé Poussineau     uint8_t it_shift;
161be920841SLaurent Vivier     bool big_endian;
162c2279bd0SFinn Thain     bool last_rba_is_full;
163a65f56eeSaurel32     qemu_irq irq;
164a65f56eeSaurel32 #ifdef DEBUG_SONIC
165a65f56eeSaurel32     int irq_level;
166a65f56eeSaurel32 #endif
167a65f56eeSaurel32     QEMUTimer *watchdog;
168a65f56eeSaurel32     int64_t wt_last_update;
16905f41fe3SMark McLoughlin     NICConf conf;
17005f41fe3SMark McLoughlin     NICState *nic;
171024e5bb6SAvi Kivity     MemoryRegion mmio;
17289ae0ff9SHervé Poussineau     MemoryRegion prom;
173a65f56eeSaurel32 
174a65f56eeSaurel32     /* Registers */
175a65f56eeSaurel32     uint8_t cam[16][6];
176a65f56eeSaurel32     uint16_t regs[0x40];
177a65f56eeSaurel32 
178a65f56eeSaurel32     /* Temporaries */
179a65f56eeSaurel32     uint8_t tx_buffer[0x10000];
180af9f0be3SLaurent Vivier     uint16_t data[12];
181a65f56eeSaurel32     int loopback_packet;
182a65f56eeSaurel32 
183a65f56eeSaurel32     /* Memory access */
1843110ce81SMarc-André Lureau     MemoryRegion *dma_mr;
185dd820513SHervé Poussineau     AddressSpace as;
186db1015e9SEduardo Habkost };
187a65f56eeSaurel32 
188*1ca82a8dSMark Cave-Ayland /*
189*1ca82a8dSMark Cave-Ayland  * Accessor functions for values which are formed by
190581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
191581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
192581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
193581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
194581f7b12SPeter Maydell  */
195581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
196581f7b12SPeter Maydell {
197581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
198581f7b12SPeter Maydell }
199581f7b12SPeter Maydell 
200581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
201581f7b12SPeter Maydell {
202581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
203581f7b12SPeter Maydell }
204581f7b12SPeter Maydell 
205581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
206581f7b12SPeter Maydell {
20788f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
20888f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
209581f7b12SPeter Maydell }
210581f7b12SPeter Maydell 
211581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
212581f7b12SPeter Maydell {
213581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
214581f7b12SPeter Maydell }
215581f7b12SPeter Maydell 
216581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
217581f7b12SPeter Maydell {
218581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
219581f7b12SPeter Maydell }
220581f7b12SPeter Maydell 
221581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
222581f7b12SPeter Maydell {
223581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
224581f7b12SPeter Maydell }
225581f7b12SPeter Maydell 
226581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
227581f7b12SPeter Maydell {
22888f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
22988f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
230581f7b12SPeter Maydell }
231581f7b12SPeter Maydell 
232581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
233581f7b12SPeter Maydell {
234581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
235581f7b12SPeter Maydell }
236581f7b12SPeter Maydell 
237af9f0be3SLaurent Vivier static uint16_t dp8393x_get(dp8393xState *s, int width, int offset)
238be920841SLaurent Vivier {
239be920841SLaurent Vivier     uint16_t val;
240be920841SLaurent Vivier 
241be920841SLaurent Vivier     if (s->big_endian) {
242af9f0be3SLaurent Vivier         val = be16_to_cpu(s->data[offset * width + width - 1]);
243be920841SLaurent Vivier     } else {
244af9f0be3SLaurent Vivier         val = le16_to_cpu(s->data[offset * width]);
245be920841SLaurent Vivier     }
246be920841SLaurent Vivier     return val;
247be920841SLaurent Vivier }
248be920841SLaurent Vivier 
249af9f0be3SLaurent Vivier static void dp8393x_put(dp8393xState *s, int width, int offset,
250be920841SLaurent Vivier                         uint16_t val)
251be920841SLaurent Vivier {
252be920841SLaurent Vivier     if (s->big_endian) {
2533fe9a838SFinn Thain         if (width == 2) {
2543fe9a838SFinn Thain             s->data[offset * 2] = 0;
2553fe9a838SFinn Thain             s->data[offset * 2 + 1] = cpu_to_be16(val);
256be920841SLaurent Vivier         } else {
2573fe9a838SFinn Thain             s->data[offset] = cpu_to_be16(val);
2583fe9a838SFinn Thain         }
2593fe9a838SFinn Thain     } else {
2603fe9a838SFinn Thain         if (width == 2) {
2613fe9a838SFinn Thain             s->data[offset * 2] = cpu_to_le16(val);
2623fe9a838SFinn Thain             s->data[offset * 2 + 1] = 0;
2633fe9a838SFinn Thain         } else {
2643fe9a838SFinn Thain             s->data[offset] = cpu_to_le16(val);
2653fe9a838SFinn Thain         }
266be920841SLaurent Vivier     }
267be920841SLaurent Vivier }
268be920841SLaurent Vivier 
269a65f56eeSaurel32 static void dp8393x_update_irq(dp8393xState *s)
270a65f56eeSaurel32 {
271a65f56eeSaurel32     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
272a65f56eeSaurel32 
273a65f56eeSaurel32 #ifdef DEBUG_SONIC
274a65f56eeSaurel32     if (level != s->irq_level) {
275a65f56eeSaurel32         s->irq_level = level;
276a65f56eeSaurel32         if (level) {
277a65f56eeSaurel32             DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
278a65f56eeSaurel32         } else {
279a65f56eeSaurel32             DPRINTF("lower irq\n");
280a65f56eeSaurel32         }
281a65f56eeSaurel32     }
282a65f56eeSaurel32 #endif
283a65f56eeSaurel32 
284a65f56eeSaurel32     qemu_set_irq(s->irq, level);
285a65f56eeSaurel32 }
286a65f56eeSaurel32 
2873df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
288a65f56eeSaurel32 {
289a65f56eeSaurel32     int width, size;
290a65f56eeSaurel32     uint16_t index = 0;
291a65f56eeSaurel32 
292a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
293a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
294a65f56eeSaurel32 
295a65f56eeSaurel32     while (s->regs[SONIC_CDC] & 0x1f) {
296a65f56eeSaurel32         /* Fill current entry */
29719f70347SPeter Maydell         address_space_read(&s->as, dp8393x_cdp(s),
29819f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
299af9f0be3SLaurent Vivier         s->cam[index][0] = dp8393x_get(s, width, 1) & 0xff;
300af9f0be3SLaurent Vivier         s->cam[index][1] = dp8393x_get(s, width, 1) >> 8;
301af9f0be3SLaurent Vivier         s->cam[index][2] = dp8393x_get(s, width, 2) & 0xff;
302af9f0be3SLaurent Vivier         s->cam[index][3] = dp8393x_get(s, width, 2) >> 8;
303af9f0be3SLaurent Vivier         s->cam[index][4] = dp8393x_get(s, width, 3) & 0xff;
304af9f0be3SLaurent Vivier         s->cam[index][5] = dp8393x_get(s, width, 3) >> 8;
305a65f56eeSaurel32         DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
306a65f56eeSaurel32             s->cam[index][0], s->cam[index][1], s->cam[index][2],
307a65f56eeSaurel32             s->cam[index][3], s->cam[index][4], s->cam[index][5]);
308a65f56eeSaurel32         /* Move to next entry */
309a65f56eeSaurel32         s->regs[SONIC_CDC]--;
310a65f56eeSaurel32         s->regs[SONIC_CDP] += size;
311a65f56eeSaurel32         index++;
312a65f56eeSaurel32     }
313a65f56eeSaurel32 
314a65f56eeSaurel32     /* Read CAM enable */
31519f70347SPeter Maydell     address_space_read(&s->as, dp8393x_cdp(s),
31619f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
317af9f0be3SLaurent Vivier     s->regs[SONIC_CE] = dp8393x_get(s, width, 0);
318a65f56eeSaurel32     DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
319a65f56eeSaurel32 
320a65f56eeSaurel32     /* Done */
321a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
322a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
323a65f56eeSaurel32     dp8393x_update_irq(s);
324a65f56eeSaurel32 }
325a65f56eeSaurel32 
3263df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
327a65f56eeSaurel32 {
328a65f56eeSaurel32     int width, size;
329a65f56eeSaurel32 
330a65f56eeSaurel32     /* Read memory */
331a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
332a65f56eeSaurel32     size = sizeof(uint16_t) * 4 * width;
33319f70347SPeter Maydell     address_space_read(&s->as, dp8393x_rrp(s),
33419f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
335a65f56eeSaurel32 
336a65f56eeSaurel32     /* Update SONIC registers */
337af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA0] = dp8393x_get(s, width, 0);
338af9f0be3SLaurent Vivier     s->regs[SONIC_CRBA1] = dp8393x_get(s, width, 1);
339af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC0] = dp8393x_get(s, width, 2);
340af9f0be3SLaurent Vivier     s->regs[SONIC_RBWC1] = dp8393x_get(s, width, 3);
341a65f56eeSaurel32     DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
342a65f56eeSaurel32         s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
343a65f56eeSaurel32         s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
344a65f56eeSaurel32 
345a65f56eeSaurel32     /* Go to next entry */
346a65f56eeSaurel32     s->regs[SONIC_RRP] += size;
347a65f56eeSaurel32 
348a65f56eeSaurel32     /* Handle wrap */
349a65f56eeSaurel32     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
350a65f56eeSaurel32         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
351a65f56eeSaurel32     }
352a65f56eeSaurel32 
353c2279bd0SFinn Thain     /* Warn the host if CRBA now has the last available resource */
354*1ca82a8dSMark Cave-Ayland     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
355a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
356a65f56eeSaurel32         dp8393x_update_irq(s);
357a65f56eeSaurel32     }
358c2279bd0SFinn Thain 
359c2279bd0SFinn Thain     /* Allow packet reception */
360c2279bd0SFinn Thain     s->last_rba_is_full = false;
361a65f56eeSaurel32 }
362a65f56eeSaurel32 
3633df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
364a65f56eeSaurel32 {
365bc72ad67SAlex Bligh     timer_del(s->watchdog);
366a65f56eeSaurel32 
367*1ca82a8dSMark Cave-Ayland     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP |
368*1ca82a8dSMark Cave-Ayland                            SONIC_CR_HTX);
369a65f56eeSaurel32     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
370a65f56eeSaurel32 }
371a65f56eeSaurel32 
3723df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
373a65f56eeSaurel32 {
374a65f56eeSaurel32     uint32_t ticks;
375a65f56eeSaurel32     int64_t delay;
376a65f56eeSaurel32 
377a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
378bc72ad67SAlex Bligh         timer_del(s->watchdog);
379a65f56eeSaurel32         return;
380a65f56eeSaurel32     }
381a65f56eeSaurel32 
382581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
383bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
38473bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
385bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
386a65f56eeSaurel32 }
387a65f56eeSaurel32 
3883df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
389a65f56eeSaurel32 {
390a65f56eeSaurel32     int64_t elapsed;
391a65f56eeSaurel32     uint32_t val;
392a65f56eeSaurel32 
393a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
394bc72ad67SAlex Bligh         timer_del(s->watchdog);
395a65f56eeSaurel32         return;
396a65f56eeSaurel32     }
397a65f56eeSaurel32 
398bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
399581f7b12SPeter Maydell     val = dp8393x_wt(s);
400a65f56eeSaurel32     val -= elapsed / 5000000;
401a65f56eeSaurel32     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
402a65f56eeSaurel32     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
4033df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
404a65f56eeSaurel32 
405a65f56eeSaurel32 }
406a65f56eeSaurel32 
4073df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
408a65f56eeSaurel32 {
409a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
4103df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
411a65f56eeSaurel32 }
412a65f56eeSaurel32 
4133df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
414a65f56eeSaurel32 {
415a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
4163df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
417a65f56eeSaurel32 }
418a65f56eeSaurel32 
419b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc);
4204594f93aSFam Zheng 
4213df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
422a65f56eeSaurel32 {
423a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
4244594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
4254594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
4264594f93aSFam Zheng     }
427a65f56eeSaurel32 }
428a65f56eeSaurel32 
4293df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
430a65f56eeSaurel32 {
431a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
432a65f56eeSaurel32 }
433a65f56eeSaurel32 
4343df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
435a65f56eeSaurel32 {
436b356f76dSJason Wang     NetClientState *nc = qemu_get_queue(s->nic);
437a65f56eeSaurel32     int width, size;
438a65f56eeSaurel32     int tx_len, len;
439a65f56eeSaurel32     uint16_t i;
440a65f56eeSaurel32 
441a65f56eeSaurel32     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
442a65f56eeSaurel32 
443a65f56eeSaurel32     while (1) {
444a65f56eeSaurel32         /* Read memory */
445a65f56eeSaurel32         size = sizeof(uint16_t) * 6 * width;
446a65f56eeSaurel32         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
447581f7b12SPeter Maydell         DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
44819f70347SPeter Maydell         address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width,
44919f70347SPeter Maydell                            MEMTXATTRS_UNSPECIFIED, s->data, size);
450a65f56eeSaurel32         tx_len = 0;
451a65f56eeSaurel32 
452a65f56eeSaurel32         /* Update registers */
453af9f0be3SLaurent Vivier         s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000;
454af9f0be3SLaurent Vivier         s->regs[SONIC_TPS] = dp8393x_get(s, width, 1);
455af9f0be3SLaurent Vivier         s->regs[SONIC_TFC] = dp8393x_get(s, width, 2);
456af9f0be3SLaurent Vivier         s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3);
457af9f0be3SLaurent Vivier         s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4);
458af9f0be3SLaurent Vivier         s->regs[SONIC_TFS] = dp8393x_get(s, width, 5);
459a65f56eeSaurel32 
460a65f56eeSaurel32         /* Handle programmable interrupt */
461a65f56eeSaurel32         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
462a65f56eeSaurel32             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
463a65f56eeSaurel32         } else {
464a65f56eeSaurel32             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
465a65f56eeSaurel32         }
466a65f56eeSaurel32 
467a65f56eeSaurel32         for (i = 0; i < s->regs[SONIC_TFC]; ) {
468a65f56eeSaurel32             /* Append fragment */
469a65f56eeSaurel32             len = s->regs[SONIC_TFS];
470a65f56eeSaurel32             if (tx_len + len > sizeof(s->tx_buffer)) {
471a65f56eeSaurel32                 len = sizeof(s->tx_buffer) - tx_len;
472a65f56eeSaurel32             }
47319f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
47419f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
475a65f56eeSaurel32             tx_len += len;
476a65f56eeSaurel32 
477a65f56eeSaurel32             i++;
478a65f56eeSaurel32             if (i != s->regs[SONIC_TFC]) {
479a65f56eeSaurel32                 /* Read next fragment details */
480a65f56eeSaurel32                 size = sizeof(uint16_t) * 3 * width;
48119f70347SPeter Maydell                 address_space_read(&s->as,
48219f70347SPeter Maydell                                    dp8393x_ttda(s)
48319f70347SPeter Maydell                                    + sizeof(uint16_t) * width * (4 + 3 * i),
48419f70347SPeter Maydell                                    MEMTXATTRS_UNSPECIFIED, s->data,
48519f70347SPeter Maydell                                    size);
486af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0);
487af9f0be3SLaurent Vivier                 s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1);
488af9f0be3SLaurent Vivier                 s->regs[SONIC_TFS] = dp8393x_get(s, width, 2);
489a65f56eeSaurel32             }
490a65f56eeSaurel32         }
491a65f56eeSaurel32 
492a65f56eeSaurel32         /* Handle Ethernet checksum */
493a65f56eeSaurel32         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
494*1ca82a8dSMark Cave-Ayland             /*
495*1ca82a8dSMark Cave-Ayland              * Don't append FCS there, to look like slirp packets
496*1ca82a8dSMark Cave-Ayland              * which don't have one
497*1ca82a8dSMark Cave-Ayland              */
498a65f56eeSaurel32         } else {
499a65f56eeSaurel32             /* Remove existing FCS */
500a65f56eeSaurel32             tx_len -= 4;
501915976bdSMauro Matteo Cascella             if (tx_len < 0) {
502915976bdSMauro Matteo Cascella                 SONIC_ERROR("tx_len is %d\n", tx_len);
503915976bdSMauro Matteo Cascella                 break;
504915976bdSMauro Matteo Cascella             }
505a65f56eeSaurel32         }
506a65f56eeSaurel32 
507a65f56eeSaurel32         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
508a65f56eeSaurel32             /* Loopback */
509a65f56eeSaurel32             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
510b356f76dSJason Wang             if (nc->info->can_receive(nc)) {
511a65f56eeSaurel32                 s->loopback_packet = 1;
512331d2ac9SJason Wang                 qemu_receive_packet(nc, s->tx_buffer, tx_len);
513a65f56eeSaurel32             }
514a65f56eeSaurel32         } else {
515a65f56eeSaurel32             /* Transmit packet */
516b356f76dSJason Wang             qemu_send_packet(nc, s->tx_buffer, tx_len);
517a65f56eeSaurel32         }
518a65f56eeSaurel32         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
519a65f56eeSaurel32 
520a65f56eeSaurel32         /* Write status */
521af9f0be3SLaurent Vivier         dp8393x_put(s, width, 0,
522be920841SLaurent Vivier                     s->regs[SONIC_TCR] & 0x0fff); /* status */
523a65f56eeSaurel32         size = sizeof(uint16_t) * width;
52419f70347SPeter Maydell         address_space_write(&s->as, dp8393x_ttda(s),
52519f70347SPeter Maydell                             MEMTXATTRS_UNSPECIFIED, s->data, size);
526a65f56eeSaurel32 
527a65f56eeSaurel32         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
528a65f56eeSaurel32             /* Read footer of packet */
529a65f56eeSaurel32             size = sizeof(uint16_t) * width;
53019f70347SPeter Maydell             address_space_read(&s->as,
53119f70347SPeter Maydell                                dp8393x_ttda(s)
53219f70347SPeter Maydell                                + sizeof(uint16_t) * width
53319f70347SPeter Maydell                                  * (4 + 3 * s->regs[SONIC_TFC]),
53419f70347SPeter Maydell                                MEMTXATTRS_UNSPECIFIED, s->data,
53519f70347SPeter Maydell                                size);
536a0cf4297SFinn Thain             s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0);
537a0cf4297SFinn Thain             if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) {
538a65f56eeSaurel32                 /* EOL detected */
539a65f56eeSaurel32                 break;
540a65f56eeSaurel32             }
541a65f56eeSaurel32         }
542a65f56eeSaurel32     }
543a65f56eeSaurel32 
544a65f56eeSaurel32     /* Done */
545a65f56eeSaurel32     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
546a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
547a65f56eeSaurel32     dp8393x_update_irq(s);
548a65f56eeSaurel32 }
549a65f56eeSaurel32 
5503df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
551a65f56eeSaurel32 {
552a65f56eeSaurel32     /* Nothing to do */
553a65f56eeSaurel32 }
554a65f56eeSaurel32 
5553df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
556a65f56eeSaurel32 {
557a65f56eeSaurel32     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
558a65f56eeSaurel32         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
559a65f56eeSaurel32         return;
560a65f56eeSaurel32     }
561a65f56eeSaurel32 
562a65f56eeSaurel32     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
563a65f56eeSaurel32 
564*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_HTX) {
5653df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
566*1ca82a8dSMark Cave-Ayland     }
567*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_TXP) {
5683df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
569*1ca82a8dSMark Cave-Ayland     }
570*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXDIS) {
5713df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
572*1ca82a8dSMark Cave-Ayland     }
573*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXEN) {
5743df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
575*1ca82a8dSMark Cave-Ayland     }
576*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_STP) {
5773df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
578*1ca82a8dSMark Cave-Ayland     }
579*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_ST) {
5803df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
581*1ca82a8dSMark Cave-Ayland     }
582*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RST) {
5833df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
584*1ca82a8dSMark Cave-Ayland     }
585a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5863df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
587a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
588a3cce282SFinn Thain     }
589*1ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_LCAM) {
5903df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
591a65f56eeSaurel32     }
592*1ca82a8dSMark Cave-Ayland }
593a65f56eeSaurel32 
59484689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
595a65f56eeSaurel32 {
59684689cbbSHervé Poussineau     dp8393xState *s = opaque;
59784689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
598a65f56eeSaurel32     uint16_t val = 0;
599a65f56eeSaurel32 
600a65f56eeSaurel32     switch (reg) {
601a65f56eeSaurel32     /* Update data before reading it */
602a65f56eeSaurel32     case SONIC_WT0:
603a65f56eeSaurel32     case SONIC_WT1:
6043df5de64SHervé Poussineau         dp8393x_update_wt_regs(s);
605a65f56eeSaurel32         val = s->regs[reg];
606a65f56eeSaurel32         break;
607a65f56eeSaurel32     /* Accept read to some registers only when in reset mode */
608a65f56eeSaurel32     case SONIC_CAP2:
609a65f56eeSaurel32     case SONIC_CAP1:
610a65f56eeSaurel32     case SONIC_CAP0:
611a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
612a65f56eeSaurel32             val = s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg) + 1] << 8;
613a65f56eeSaurel32             val |= s->cam[s->regs[SONIC_CEP] & 0xf][2 * (SONIC_CAP0 - reg)];
614a65f56eeSaurel32         }
615a65f56eeSaurel32         break;
616*1ca82a8dSMark Cave-Ayland     /* All other registers have no special contraints */
617a65f56eeSaurel32     default:
618a65f56eeSaurel32         val = s->regs[reg];
619a65f56eeSaurel32     }
620a65f56eeSaurel32 
621a65f56eeSaurel32     DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
622a65f56eeSaurel32 
6233fe9a838SFinn Thain     return s->big_endian ? val << 16 : val;
624a65f56eeSaurel32 }
625a65f56eeSaurel32 
62684689cbbSHervé Poussineau static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data,
62784689cbbSHervé Poussineau                           unsigned int size)
628a65f56eeSaurel32 {
62984689cbbSHervé Poussineau     dp8393xState *s = opaque;
63084689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
6313fe9a838SFinn Thain     uint32_t val = s->big_endian ? data >> 16 : data;
63284689cbbSHervé Poussineau 
6333fe9a838SFinn Thain     DPRINTF("write 0x%04x to reg %s\n", (uint16_t)val, reg_names[reg]);
634a65f56eeSaurel32 
635a65f56eeSaurel32     switch (reg) {
636a65f56eeSaurel32     /* Command register */
637a65f56eeSaurel32     case SONIC_CR:
6383fe9a838SFinn Thain         dp8393x_do_command(s, val);
639a65f56eeSaurel32         break;
640a65f56eeSaurel32     /* Prevent write to read-only registers */
641a65f56eeSaurel32     case SONIC_CAP2:
642a65f56eeSaurel32     case SONIC_CAP1:
643a65f56eeSaurel32     case SONIC_CAP0:
644a65f56eeSaurel32     case SONIC_SR:
645a65f56eeSaurel32     case SONIC_MDT:
646a65f56eeSaurel32         DPRINTF("writing to reg %d invalid\n", reg);
647a65f56eeSaurel32         break;
648a65f56eeSaurel32     /* Accept write to some registers only when in reset mode */
649a65f56eeSaurel32     case SONIC_DCR:
650a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6513fe9a838SFinn Thain             s->regs[reg] = val & 0xbfff;
652a65f56eeSaurel32         } else {
653a65f56eeSaurel32             DPRINTF("writing to DCR invalid\n");
654a65f56eeSaurel32         }
655a65f56eeSaurel32         break;
656a65f56eeSaurel32     case SONIC_DCR2:
657a65f56eeSaurel32         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
6583fe9a838SFinn Thain             s->regs[reg] = val & 0xf017;
659a65f56eeSaurel32         } else {
660a65f56eeSaurel32             DPRINTF("writing to DCR2 invalid\n");
661a65f56eeSaurel32         }
662a65f56eeSaurel32         break;
663a65f56eeSaurel32     /* 12 lower bytes are Read Only */
664a65f56eeSaurel32     case SONIC_TCR:
6653fe9a838SFinn Thain         s->regs[reg] = val & 0xf000;
666a65f56eeSaurel32         break;
667a65f56eeSaurel32     /* 9 lower bytes are Read Only */
668a65f56eeSaurel32     case SONIC_RCR:
6693fe9a838SFinn Thain         s->regs[reg] = val & 0xffe0;
670a65f56eeSaurel32         break;
671a65f56eeSaurel32     /* Ignore most significant bit */
672a65f56eeSaurel32     case SONIC_IMR:
6733fe9a838SFinn Thain         s->regs[reg] = val & 0x7fff;
674a65f56eeSaurel32         dp8393x_update_irq(s);
675a65f56eeSaurel32         break;
676a65f56eeSaurel32     /* Clear bits by writing 1 to them */
677a65f56eeSaurel32     case SONIC_ISR:
6783fe9a838SFinn Thain         val &= s->regs[reg];
6793fe9a838SFinn Thain         s->regs[reg] &= ~val;
6803fe9a838SFinn Thain         if (val & SONIC_ISR_RBE) {
6813df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
682a65f56eeSaurel32         }
683a65f56eeSaurel32         dp8393x_update_irq(s);
684a65f56eeSaurel32         break;
685ea227027SFinn Thain     /* The guest is required to store aligned pointers here */
686a65f56eeSaurel32     case SONIC_RSA:
687a65f56eeSaurel32     case SONIC_REA:
688a65f56eeSaurel32     case SONIC_RRP:
689a65f56eeSaurel32     case SONIC_RWP:
690ea227027SFinn Thain         if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
691ea227027SFinn Thain             s->regs[reg] = val & 0xfffc;
692ea227027SFinn Thain         } else {
6933fe9a838SFinn Thain             s->regs[reg] = val & 0xfffe;
694ea227027SFinn Thain         }
695a65f56eeSaurel32         break;
696a65f56eeSaurel32     /* Invert written value for some registers */
697a65f56eeSaurel32     case SONIC_CRCT:
698a65f56eeSaurel32     case SONIC_FAET:
699a65f56eeSaurel32     case SONIC_MPT:
7003fe9a838SFinn Thain         s->regs[reg] = val ^ 0xffff;
701a65f56eeSaurel32         break;
702a65f56eeSaurel32     /* All other registers have no special contrainst */
703a65f56eeSaurel32     default:
7043fe9a838SFinn Thain         s->regs[reg] = val;
705a65f56eeSaurel32     }
706a65f56eeSaurel32 
707a65f56eeSaurel32     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
7083df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
709a65f56eeSaurel32     }
710a65f56eeSaurel32 }
711a65f56eeSaurel32 
71284689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
71384689cbbSHervé Poussineau     .read = dp8393x_read,
71484689cbbSHervé Poussineau     .write = dp8393x_write,
7153fe9a838SFinn Thain     .impl.min_access_size = 4,
7163fe9a838SFinn Thain     .impl.max_access_size = 4,
71784689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
71884689cbbSHervé Poussineau };
71984689cbbSHervé Poussineau 
720a65f56eeSaurel32 static void dp8393x_watchdog(void *opaque)
721a65f56eeSaurel32 {
722a65f56eeSaurel32     dp8393xState *s = opaque;
723a65f56eeSaurel32 
724a65f56eeSaurel32     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
725a65f56eeSaurel32         return;
726a65f56eeSaurel32     }
727a65f56eeSaurel32 
728a65f56eeSaurel32     s->regs[SONIC_WT1] = 0xffff;
729a65f56eeSaurel32     s->regs[SONIC_WT0] = 0xffff;
7303df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
731a65f56eeSaurel32 
732a65f56eeSaurel32     /* Signal underflow */
733a65f56eeSaurel32     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
734a65f56eeSaurel32     dp8393x_update_irq(s);
735a65f56eeSaurel32 }
736a65f56eeSaurel32 
737b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc)
738a65f56eeSaurel32 {
739cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
740a65f56eeSaurel32 
741b8c4b67eSPhilippe Mathieu-Daudé     return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN);
742a65f56eeSaurel32 }
743a65f56eeSaurel32 
7443df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
7453df5de64SHervé Poussineau                                   int size)
746a65f56eeSaurel32 {
747a65f56eeSaurel32     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
748a65f56eeSaurel32     int i;
749a65f56eeSaurel32 
750a65f56eeSaurel32     /* Check promiscuous mode */
751a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
752a65f56eeSaurel32         return 0;
753a65f56eeSaurel32     }
754a65f56eeSaurel32 
755a65f56eeSaurel32     /* Check multicast packets */
756a65f56eeSaurel32     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
757a65f56eeSaurel32         return SONIC_RCR_MC;
758a65f56eeSaurel32     }
759a65f56eeSaurel32 
760a65f56eeSaurel32     /* Check broadcast */
761*1ca82a8dSMark Cave-Ayland     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) &&
762*1ca82a8dSMark Cave-Ayland          !memcmp(buf, bcast, sizeof(bcast))) {
763a65f56eeSaurel32         return SONIC_RCR_BC;
764a65f56eeSaurel32     }
765a65f56eeSaurel32 
766a65f56eeSaurel32     /* Check CAM */
767a65f56eeSaurel32     for (i = 0; i < 16; i++) {
768a65f56eeSaurel32         if (s->regs[SONIC_CE] & (1 << i)) {
769a65f56eeSaurel32             /* Entry enabled */
770a65f56eeSaurel32             if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
771a65f56eeSaurel32                 return 0;
772a65f56eeSaurel32             }
773a65f56eeSaurel32         }
774a65f56eeSaurel32     }
775a65f56eeSaurel32 
776a65f56eeSaurel32     return -1;
777a65f56eeSaurel32 }
778a65f56eeSaurel32 
7793df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7809e3cd456SFinn Thain                                size_t pkt_size)
781a65f56eeSaurel32 {
782cc1f0f45SJason Wang     dp8393xState *s = qemu_get_nic_opaque(nc);
783a65f56eeSaurel32     int packet_type;
784a65f56eeSaurel32     uint32_t available, address;
785350e7d9aSFinn Thain     int width, rx_len, padded_len;
786a65f56eeSaurel32     uint32_t checksum;
7879e3cd456SFinn Thain     int size;
788a65f56eeSaurel32 
789a65f56eeSaurel32     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
790a65f56eeSaurel32         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
791a65f56eeSaurel32 
792c2279bd0SFinn Thain     if (s->last_rba_is_full) {
793c2279bd0SFinn Thain         return pkt_size;
794c2279bd0SFinn Thain     }
795c2279bd0SFinn Thain 
796350e7d9aSFinn Thain     rx_len = pkt_size + sizeof(checksum);
797350e7d9aSFinn Thain     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
798350e7d9aSFinn Thain         width = 2;
799350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 3) + 1;
800350e7d9aSFinn Thain     } else {
801350e7d9aSFinn Thain         width = 1;
802350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 1) + 1;
803350e7d9aSFinn Thain     }
804350e7d9aSFinn Thain 
805350e7d9aSFinn Thain     if (padded_len > dp8393x_rbwc(s) * 2) {
806ada74315SFinn Thain         DPRINTF("oversize packet, pkt_size is %d\n", pkt_size);
807ada74315SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_RBAE;
808ada74315SFinn Thain         dp8393x_update_irq(s);
809c2279bd0SFinn Thain         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
810c2279bd0SFinn Thain         goto done;
811ada74315SFinn Thain     }
812ada74315SFinn Thain 
8139e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
814a65f56eeSaurel32     if (packet_type < 0) {
815a65f56eeSaurel32         DPRINTF("packet not for netcard\n");
8164f1c942bSMark McLoughlin         return -1;
817a65f56eeSaurel32     }
818a65f56eeSaurel32 
819a65f56eeSaurel32     /* Check for EOL */
82088f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
821a65f56eeSaurel32         /* Are we still in resource exhaustion? */
822a65f56eeSaurel32         size = sizeof(uint16_t) * 1 * width;
823581f7b12SPeter Maydell         address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width;
82419f70347SPeter Maydell         address_space_read(&s->as, address, MEMTXATTRS_UNSPECIFIED,
82519f70347SPeter Maydell                            s->data, size);
8265b0c98fcSFinn Thain         s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
8275b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
828a65f56eeSaurel32             /* Still EOL ; stop reception */
8294f1c942bSMark McLoughlin             return -1;
830a65f56eeSaurel32         }
8315b0c98fcSFinn Thain         /* Link has been updated by host */
832d9fae131SFinn Thain 
833d9fae131SFinn Thain         /* Clear in_use */
834d9fae131SFinn Thain         size = sizeof(uint16_t) * width;
835d9fae131SFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
836d9fae131SFinn Thain         dp8393x_put(s, width, 0, 0);
837d9fae131SFinn Thain         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
838d9fae131SFinn Thain                          (uint8_t *)s->data, size, 1);
839d9fae131SFinn Thain 
840d9fae131SFinn Thain         /* Move to next descriptor */
8415b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
842d9fae131SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
843a65f56eeSaurel32     }
844a65f56eeSaurel32 
845a65f56eeSaurel32     /* Save current position */
846a65f56eeSaurel32     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
847a65f56eeSaurel32     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
848a65f56eeSaurel32 
849a65f56eeSaurel32     /* Calculate the ethernet checksum */
850350e7d9aSFinn Thain     checksum = cpu_to_le32(crc32(0, buf, pkt_size));
851a65f56eeSaurel32 
852a65f56eeSaurel32     /* Put packet into RBA */
853581f7b12SPeter Maydell     DPRINTF("Receive packet at %08x\n", dp8393x_crba(s));
854581f7b12SPeter Maydell     address = dp8393x_crba(s);
85519f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
856350e7d9aSFinn Thain                         buf, pkt_size);
857350e7d9aSFinn Thain     address += pkt_size;
858350e7d9aSFinn Thain 
859350e7d9aSFinn Thain     /* Put frame checksum into RBA */
86019f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
861350e7d9aSFinn Thain                         &checksum, sizeof(checksum));
862350e7d9aSFinn Thain     address += sizeof(checksum);
863350e7d9aSFinn Thain 
864350e7d9aSFinn Thain     /* Pad short packets to keep pointers aligned */
865350e7d9aSFinn Thain     if (rx_len < padded_len) {
866350e7d9aSFinn Thain         size = padded_len - rx_len;
867350e7d9aSFinn Thain         address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED,
868350e7d9aSFinn Thain             (uint8_t *)"\xFF\xFF\xFF", size, 1);
869350e7d9aSFinn Thain         address += size;
870350e7d9aSFinn Thain     }
871350e7d9aSFinn Thain 
872a65f56eeSaurel32     s->regs[SONIC_CRBA1] = address >> 16;
873a65f56eeSaurel32     s->regs[SONIC_CRBA0] = address & 0xffff;
874581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
875350e7d9aSFinn Thain     available -= padded_len >> 1;
876a65f56eeSaurel32     s->regs[SONIC_RBWC1] = available >> 16;
877a65f56eeSaurel32     s->regs[SONIC_RBWC0] = available & 0xffff;
878a65f56eeSaurel32 
879a65f56eeSaurel32     /* Update status */
880581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
881a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
882a65f56eeSaurel32     }
883a65f56eeSaurel32     s->regs[SONIC_RCR] |= packet_type;
884a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
885a65f56eeSaurel32     if (s->loopback_packet) {
886a65f56eeSaurel32         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
887a65f56eeSaurel32         s->loopback_packet = 0;
888a65f56eeSaurel32     }
889a65f56eeSaurel32 
890a65f56eeSaurel32     /* Write status to memory */
891581f7b12SPeter Maydell     DPRINTF("Write status at %08x\n", dp8393x_crda(s));
892af9f0be3SLaurent Vivier     dp8393x_put(s, width, 0, s->regs[SONIC_RCR]); /* status */
893af9f0be3SLaurent Vivier     dp8393x_put(s, width, 1, rx_len); /* byte count */
894af9f0be3SLaurent Vivier     dp8393x_put(s, width, 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
895af9f0be3SLaurent Vivier     dp8393x_put(s, width, 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
896af9f0be3SLaurent Vivier     dp8393x_put(s, width, 4, s->regs[SONIC_RSC]); /* seq_no */
897a65f56eeSaurel32     size = sizeof(uint16_t) * 5 * width;
89819f70347SPeter Maydell     address_space_write(&s->as, dp8393x_crda(s),
89919f70347SPeter Maydell                         MEMTXATTRS_UNSPECIFIED,
90019f70347SPeter Maydell                         s->data, size);
901a65f56eeSaurel32 
9025b0c98fcSFinn Thain     /* Check link field */
903a65f56eeSaurel32     size = sizeof(uint16_t) * width;
90419f70347SPeter Maydell     address_space_read(&s->as,
90519f70347SPeter Maydell                        dp8393x_crda(s) + sizeof(uint16_t) * 5 * width,
90619f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, s->data, size);
907af9f0be3SLaurent Vivier     s->regs[SONIC_LLFA] = dp8393x_get(s, width, 0);
90888f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
909a65f56eeSaurel32         /* EOL detected */
910a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
911a65f56eeSaurel32     } else {
91246ffee9aSFinn Thain         /* Clear in_use */
91346ffee9aSFinn Thain         size = sizeof(uint16_t) * width;
91446ffee9aSFinn Thain         address = dp8393x_crda(s) + sizeof(uint16_t) * 6 * width;
91546ffee9aSFinn Thain         dp8393x_put(s, width, 0, 0);
91646ffee9aSFinn Thain         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
91746ffee9aSFinn Thain                             s->data, size);
9185b0c98fcSFinn Thain 
9195b0c98fcSFinn Thain         /* Move to next descriptor */
920a65f56eeSaurel32         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
921a65f56eeSaurel32         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
92280b60673SFinn Thain     }
92380b60673SFinn Thain 
924c2279bd0SFinn Thain     dp8393x_update_irq(s);
925c2279bd0SFinn Thain 
92680b60673SFinn Thain     s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) |
92780b60673SFinn Thain                          ((s->regs[SONIC_RSC] + 1) & 0x00ff);
928a65f56eeSaurel32 
929c2279bd0SFinn Thain done:
930c2279bd0SFinn Thain 
931a65f56eeSaurel32     if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
932c2279bd0SFinn Thain         if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
933c2279bd0SFinn Thain             /* Stop packet reception */
934c2279bd0SFinn Thain             s->last_rba_is_full = true;
935c2279bd0SFinn Thain         } else {
936c2279bd0SFinn Thain             /* Read next resource */
9373df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
938a65f56eeSaurel32         }
939c2279bd0SFinn Thain     }
9404f1c942bSMark McLoughlin 
9419e3cd456SFinn Thain     return pkt_size;
942a65f56eeSaurel32 }
943a65f56eeSaurel32 
944104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
945a65f56eeSaurel32 {
946104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
947bc72ad67SAlex Bligh     timer_del(s->watchdog);
948a65f56eeSaurel32 
949bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
950083e21bbSFinn Thain     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */
951a65f56eeSaurel32     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
952a65f56eeSaurel32     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
953*1ca82a8dSMark Cave-Ayland     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD |
954*1ca82a8dSMark Cave-Ayland                             SONIC_RCR_RNT);
955a65f56eeSaurel32     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
956a65f56eeSaurel32     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
957a65f56eeSaurel32     s->regs[SONIC_IMR] = 0;
958a65f56eeSaurel32     s->regs[SONIC_ISR] = 0;
959a65f56eeSaurel32     s->regs[SONIC_DCR2] = 0;
960a65f56eeSaurel32     s->regs[SONIC_EOBC] = 0x02F8;
961a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
962a65f56eeSaurel32     s->regs[SONIC_CE] = 0;
963a65f56eeSaurel32     s->regs[SONIC_RSC] = 0;
964a65f56eeSaurel32 
965a65f56eeSaurel32     /* Network cable is connected */
966a65f56eeSaurel32     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
967a65f56eeSaurel32 
968a65f56eeSaurel32     dp8393x_update_irq(s);
969a65f56eeSaurel32 }
970a65f56eeSaurel32 
97105f41fe3SMark McLoughlin static NetClientInfo net_dp83932_info = {
972f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
97305f41fe3SMark McLoughlin     .size = sizeof(NICState),
9743df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
9753df5de64SHervé Poussineau     .receive = dp8393x_receive,
97605f41fe3SMark McLoughlin };
97705f41fe3SMark McLoughlin 
978104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
979a65f56eeSaurel32 {
980104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
981104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
982a65f56eeSaurel32 
983104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
98489ae0ff9SHervé Poussineau     sysbus_init_mmio(sbd, &s->prom);
985104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
986104655a5SHervé Poussineau }
987a65f56eeSaurel32 
988104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
989104655a5SHervé Poussineau {
990104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
99189ae0ff9SHervé Poussineau     int i, checksum;
99289ae0ff9SHervé Poussineau     uint8_t *prom;
99352579c68SHervé Poussineau     Error *local_err = NULL;
994a65f56eeSaurel32 
995104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
996104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
997104655a5SHervé Poussineau                           "dp8393x-regs", 0x40 << s->it_shift);
998104655a5SHervé Poussineau 
999104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
1000104655a5SHervé Poussineau                           object_get_typename(OBJECT(dev)), dev->id, s);
1001104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
1002104655a5SHervé Poussineau 
1003bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
100489ae0ff9SHervé Poussineau 
1005fcd3b085SPhilippe Mathieu-Daudé     memory_region_init_rom(&s->prom, OBJECT(dev), "dp8393x-prom",
1006fcd3b085SPhilippe Mathieu-Daudé                            SONIC_PROM_SIZE, &local_err);
100752579c68SHervé Poussineau     if (local_err) {
100852579c68SHervé Poussineau         error_propagate(errp, local_err);
100952579c68SHervé Poussineau         return;
101052579c68SHervé Poussineau     }
101189ae0ff9SHervé Poussineau     prom = memory_region_get_ram_ptr(&s->prom);
101289ae0ff9SHervé Poussineau     checksum = 0;
101389ae0ff9SHervé Poussineau     for (i = 0; i < 6; i++) {
101489ae0ff9SHervé Poussineau         prom[i] = s->conf.macaddr.a[i];
101589ae0ff9SHervé Poussineau         checksum += prom[i];
101689ae0ff9SHervé Poussineau         if (checksum > 0xff) {
101789ae0ff9SHervé Poussineau             checksum = (checksum + 1) & 0xff;
101889ae0ff9SHervé Poussineau         }
101989ae0ff9SHervé Poussineau     }
102089ae0ff9SHervé Poussineau     prom[7] = 0xff - checksum;
1021a65f56eeSaurel32 }
1022104655a5SHervé Poussineau 
10231670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
10241670735dSHervé Poussineau     .name = "dp8393x",
10251670735dSHervé Poussineau     .version_id = 0,
10261670735dSHervé Poussineau     .minimum_version_id = 0,
10271670735dSHervé Poussineau     .fields = (VMStateField []) {
10281670735dSHervé Poussineau         VMSTATE_BUFFER_UNSAFE(cam, dp8393xState, 0, 16 * 6),
10291670735dSHervé Poussineau         VMSTATE_UINT16_ARRAY(regs, dp8393xState, 0x40),
10301670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
10311670735dSHervé Poussineau     }
10321670735dSHervé Poussineau };
10331670735dSHervé Poussineau 
1034104655a5SHervé Poussineau static Property dp8393x_properties[] = {
1035104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
10363110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
10373110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
1038104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
1039be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
1040104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
1041104655a5SHervé Poussineau };
1042104655a5SHervé Poussineau 
1043104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
1044104655a5SHervé Poussineau {
1045104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
1046104655a5SHervé Poussineau 
1047104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
1048104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
1049104655a5SHervé Poussineau     dc->reset = dp8393x_reset;
10501670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
10514f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
1052104655a5SHervé Poussineau }
1053104655a5SHervé Poussineau 
1054104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
1055104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
1056104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
1057104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
1058104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
1059104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
1060104655a5SHervé Poussineau };
1061104655a5SHervé Poussineau 
1062104655a5SHervé Poussineau static void dp8393x_register_types(void)
1063104655a5SHervé Poussineau {
1064104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
1065104655a5SHervé Poussineau }
1066104655a5SHervé Poussineau 
1067104655a5SHervé Poussineau type_init(dp8393x_register_types)
1068