xref: /openbmc/qemu/hw/net/dp8393x.c (revision a53b931645183bd0c15dd19ae0708fc3c81ecf1d)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU NS SONIC DP8393x netcard
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2008-2009 Herve Poussineau
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * This program is free software; you can redistribute it and/or
749ab747fSPaolo Bonzini  * modify it under the terms of the GNU General Public License as
849ab747fSPaolo Bonzini  * published by the Free Software Foundation; either version 2 of
949ab747fSPaolo Bonzini  * the License, or (at your option) any later version.
1049ab747fSPaolo Bonzini  *
1149ab747fSPaolo Bonzini  * This program is distributed in the hope that it will be useful,
1249ab747fSPaolo Bonzini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1349ab747fSPaolo Bonzini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1449ab747fSPaolo Bonzini  * GNU General Public License for more details.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * You should have received a copy of the GNU General Public License along
1749ab747fSPaolo Bonzini  * with this program; if not, see <http://www.gnu.org/licenses/>.
1849ab747fSPaolo Bonzini  */
1949ab747fSPaolo Bonzini 
20e8d40465SPeter Maydell #include "qemu/osdep.h"
2164552b6bSMarkus Armbruster #include "hw/irq.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
232db48d03SMark Cave-Ayland #include "hw/net/dp8393x.h"
24104655a5SHervé Poussineau #include "hw/sysbus.h"
25d6454270SMarkus Armbruster #include "migration/vmstate.h"
2649ab747fSPaolo Bonzini #include "net/net.h"
27da34e65cSMarkus Armbruster #include "qapi/error.h"
280b8fa32fSMarkus Armbruster #include "qemu/module.h"
29104655a5SHervé Poussineau #include "qemu/timer.h"
30*5691f477SMichael Tokarev #include <zlib.h> /* for crc32 */
31db1015e9SEduardo Habkost #include "qom/object.h"
32c0af04a4SMark Cave-Ayland #include "trace.h"
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini static const char *reg_names[] = {
3549ab747fSPaolo Bonzini     "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
3649ab747fSPaolo Bonzini     "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
3749ab747fSPaolo Bonzini     "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
3849ab747fSPaolo Bonzini     "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
3949ab747fSPaolo Bonzini     "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
4049ab747fSPaolo Bonzini     "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
4149ab747fSPaolo Bonzini     "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
4249ab747fSPaolo Bonzini     "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
4349ab747fSPaolo Bonzini 
4449ab747fSPaolo Bonzini #define SONIC_CR     0x00
4549ab747fSPaolo Bonzini #define SONIC_DCR    0x01
4649ab747fSPaolo Bonzini #define SONIC_RCR    0x02
4749ab747fSPaolo Bonzini #define SONIC_TCR    0x03
4849ab747fSPaolo Bonzini #define SONIC_IMR    0x04
4949ab747fSPaolo Bonzini #define SONIC_ISR    0x05
5049ab747fSPaolo Bonzini #define SONIC_UTDA   0x06
5149ab747fSPaolo Bonzini #define SONIC_CTDA   0x07
5249ab747fSPaolo Bonzini #define SONIC_TPS    0x08
5349ab747fSPaolo Bonzini #define SONIC_TFC    0x09
5449ab747fSPaolo Bonzini #define SONIC_TSA0   0x0a
5549ab747fSPaolo Bonzini #define SONIC_TSA1   0x0b
5649ab747fSPaolo Bonzini #define SONIC_TFS    0x0c
5749ab747fSPaolo Bonzini #define SONIC_URDA   0x0d
5849ab747fSPaolo Bonzini #define SONIC_CRDA   0x0e
5949ab747fSPaolo Bonzini #define SONIC_CRBA0  0x0f
6049ab747fSPaolo Bonzini #define SONIC_CRBA1  0x10
6149ab747fSPaolo Bonzini #define SONIC_RBWC0  0x11
6249ab747fSPaolo Bonzini #define SONIC_RBWC1  0x12
6349ab747fSPaolo Bonzini #define SONIC_EOBC   0x13
6449ab747fSPaolo Bonzini #define SONIC_URRA   0x14
6549ab747fSPaolo Bonzini #define SONIC_RSA    0x15
6649ab747fSPaolo Bonzini #define SONIC_REA    0x16
6749ab747fSPaolo Bonzini #define SONIC_RRP    0x17
6849ab747fSPaolo Bonzini #define SONIC_RWP    0x18
6949ab747fSPaolo Bonzini #define SONIC_TRBA0  0x19
7049ab747fSPaolo Bonzini #define SONIC_TRBA1  0x1a
7149ab747fSPaolo Bonzini #define SONIC_LLFA   0x1f
7249ab747fSPaolo Bonzini #define SONIC_TTDA   0x20
7349ab747fSPaolo Bonzini #define SONIC_CEP    0x21
7449ab747fSPaolo Bonzini #define SONIC_CAP2   0x22
7549ab747fSPaolo Bonzini #define SONIC_CAP1   0x23
7649ab747fSPaolo Bonzini #define SONIC_CAP0   0x24
7749ab747fSPaolo Bonzini #define SONIC_CE     0x25
7849ab747fSPaolo Bonzini #define SONIC_CDP    0x26
7949ab747fSPaolo Bonzini #define SONIC_CDC    0x27
8049ab747fSPaolo Bonzini #define SONIC_SR     0x28
8149ab747fSPaolo Bonzini #define SONIC_WT0    0x29
8249ab747fSPaolo Bonzini #define SONIC_WT1    0x2a
8349ab747fSPaolo Bonzini #define SONIC_RSC    0x2b
8449ab747fSPaolo Bonzini #define SONIC_CRCT   0x2c
8549ab747fSPaolo Bonzini #define SONIC_FAET   0x2d
8649ab747fSPaolo Bonzini #define SONIC_MPT    0x2e
8749ab747fSPaolo Bonzini #define SONIC_MDT    0x2f
8849ab747fSPaolo Bonzini #define SONIC_DCR2   0x3f
8949ab747fSPaolo Bonzini 
9049ab747fSPaolo Bonzini #define SONIC_CR_HTX     0x0001
9149ab747fSPaolo Bonzini #define SONIC_CR_TXP     0x0002
9249ab747fSPaolo Bonzini #define SONIC_CR_RXDIS   0x0004
9349ab747fSPaolo Bonzini #define SONIC_CR_RXEN    0x0008
9449ab747fSPaolo Bonzini #define SONIC_CR_STP     0x0010
9549ab747fSPaolo Bonzini #define SONIC_CR_ST      0x0020
9649ab747fSPaolo Bonzini #define SONIC_CR_RST     0x0080
9749ab747fSPaolo Bonzini #define SONIC_CR_RRRA    0x0100
9849ab747fSPaolo Bonzini #define SONIC_CR_LCAM    0x0200
9949ab747fSPaolo Bonzini #define SONIC_CR_MASK    0x03bf
10049ab747fSPaolo Bonzini 
10149ab747fSPaolo Bonzini #define SONIC_DCR_DW     0x0020
10249ab747fSPaolo Bonzini #define SONIC_DCR_LBR    0x2000
10349ab747fSPaolo Bonzini #define SONIC_DCR_EXBUS  0x8000
10449ab747fSPaolo Bonzini 
10549ab747fSPaolo Bonzini #define SONIC_RCR_PRX    0x0001
10649ab747fSPaolo Bonzini #define SONIC_RCR_LBK    0x0002
10749ab747fSPaolo Bonzini #define SONIC_RCR_FAER   0x0004
10849ab747fSPaolo Bonzini #define SONIC_RCR_CRCR   0x0008
10949ab747fSPaolo Bonzini #define SONIC_RCR_CRS    0x0020
11049ab747fSPaolo Bonzini #define SONIC_RCR_LPKT   0x0040
11149ab747fSPaolo Bonzini #define SONIC_RCR_BC     0x0080
11249ab747fSPaolo Bonzini #define SONIC_RCR_MC     0x0100
11349ab747fSPaolo Bonzini #define SONIC_RCR_LB0    0x0200
11449ab747fSPaolo Bonzini #define SONIC_RCR_LB1    0x0400
11549ab747fSPaolo Bonzini #define SONIC_RCR_AMC    0x0800
11649ab747fSPaolo Bonzini #define SONIC_RCR_PRO    0x1000
11749ab747fSPaolo Bonzini #define SONIC_RCR_BRD    0x2000
11849ab747fSPaolo Bonzini #define SONIC_RCR_RNT    0x4000
11949ab747fSPaolo Bonzini 
12049ab747fSPaolo Bonzini #define SONIC_TCR_PTX    0x0001
12149ab747fSPaolo Bonzini #define SONIC_TCR_BCM    0x0002
12249ab747fSPaolo Bonzini #define SONIC_TCR_FU     0x0004
12349ab747fSPaolo Bonzini #define SONIC_TCR_EXC    0x0040
12449ab747fSPaolo Bonzini #define SONIC_TCR_CRSL   0x0080
12549ab747fSPaolo Bonzini #define SONIC_TCR_NCRS   0x0100
12649ab747fSPaolo Bonzini #define SONIC_TCR_EXD    0x0400
12749ab747fSPaolo Bonzini #define SONIC_TCR_CRCI   0x2000
12849ab747fSPaolo Bonzini #define SONIC_TCR_PINT   0x8000
12949ab747fSPaolo Bonzini 
130ada74315SFinn Thain #define SONIC_ISR_RBAE   0x0010
13149ab747fSPaolo Bonzini #define SONIC_ISR_RBE    0x0020
13249ab747fSPaolo Bonzini #define SONIC_ISR_RDE    0x0040
13349ab747fSPaolo Bonzini #define SONIC_ISR_TC     0x0080
13449ab747fSPaolo Bonzini #define SONIC_ISR_TXDN   0x0200
13549ab747fSPaolo Bonzini #define SONIC_ISR_PKTRX  0x0400
13649ab747fSPaolo Bonzini #define SONIC_ISR_PINT   0x0800
13749ab747fSPaolo Bonzini #define SONIC_ISR_LCD    0x1000
13849ab747fSPaolo Bonzini 
13988f632fbSFinn Thain #define SONIC_DESC_EOL   0x0001
14088f632fbSFinn Thain #define SONIC_DESC_ADDR  0xFFFE
14188f632fbSFinn Thain 
14249ab747fSPaolo Bonzini 
1431ca82a8dSMark Cave-Ayland /*
1441ca82a8dSMark Cave-Ayland  * Accessor functions for values which are formed by
145581f7b12SPeter Maydell  * concatenating two 16 bit device registers. By putting these
146581f7b12SPeter Maydell  * in their own functions with a uint32_t return type we avoid the
147581f7b12SPeter Maydell  * pitfall of implicit sign extension where ((x << 16) | y) is a
148581f7b12SPeter Maydell  * signed 32 bit integer that might get sign-extended to a 64 bit integer.
149581f7b12SPeter Maydell  */
dp8393x_cdp(dp8393xState * s)150581f7b12SPeter Maydell static uint32_t dp8393x_cdp(dp8393xState *s)
151581f7b12SPeter Maydell {
152581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP];
153581f7b12SPeter Maydell }
154581f7b12SPeter Maydell 
dp8393x_crba(dp8393xState * s)155581f7b12SPeter Maydell static uint32_t dp8393x_crba(dp8393xState *s)
156581f7b12SPeter Maydell {
157581f7b12SPeter Maydell     return (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
158581f7b12SPeter Maydell }
159581f7b12SPeter Maydell 
dp8393x_crda(dp8393xState * s)160581f7b12SPeter Maydell static uint32_t dp8393x_crda(dp8393xState *s)
161581f7b12SPeter Maydell {
16288f632fbSFinn Thain     return (s->regs[SONIC_URDA] << 16) |
16388f632fbSFinn Thain            (s->regs[SONIC_CRDA] & SONIC_DESC_ADDR);
164581f7b12SPeter Maydell }
165581f7b12SPeter Maydell 
dp8393x_rbwc(dp8393xState * s)166581f7b12SPeter Maydell static uint32_t dp8393x_rbwc(dp8393xState *s)
167581f7b12SPeter Maydell {
168581f7b12SPeter Maydell     return (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
169581f7b12SPeter Maydell }
170581f7b12SPeter Maydell 
dp8393x_rrp(dp8393xState * s)171581f7b12SPeter Maydell static uint32_t dp8393x_rrp(dp8393xState *s)
172581f7b12SPeter Maydell {
173581f7b12SPeter Maydell     return (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP];
174581f7b12SPeter Maydell }
175581f7b12SPeter Maydell 
dp8393x_tsa(dp8393xState * s)176581f7b12SPeter Maydell static uint32_t dp8393x_tsa(dp8393xState *s)
177581f7b12SPeter Maydell {
178581f7b12SPeter Maydell     return (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0];
179581f7b12SPeter Maydell }
180581f7b12SPeter Maydell 
dp8393x_ttda(dp8393xState * s)181581f7b12SPeter Maydell static uint32_t dp8393x_ttda(dp8393xState *s)
182581f7b12SPeter Maydell {
18388f632fbSFinn Thain     return (s->regs[SONIC_UTDA] << 16) |
18488f632fbSFinn Thain            (s->regs[SONIC_TTDA] & SONIC_DESC_ADDR);
185581f7b12SPeter Maydell }
186581f7b12SPeter Maydell 
dp8393x_wt(dp8393xState * s)187581f7b12SPeter Maydell static uint32_t dp8393x_wt(dp8393xState *s)
188581f7b12SPeter Maydell {
189581f7b12SPeter Maydell     return s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
190581f7b12SPeter Maydell }
191581f7b12SPeter Maydell 
dp8393x_get(dp8393xState * s,hwaddr addr,int offset)19282adabf7SPhilippe Mathieu-Daudé static uint16_t dp8393x_get(dp8393xState *s, hwaddr addr, int offset)
193be920841SLaurent Vivier {
19482adabf7SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
195be920841SLaurent Vivier     uint16_t val;
196be920841SLaurent Vivier 
19782adabf7SPhilippe Mathieu-Daudé     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
19882adabf7SPhilippe Mathieu-Daudé         addr += offset << 2;
199be920841SLaurent Vivier         if (s->big_endian) {
20082adabf7SPhilippe Mathieu-Daudé             val = address_space_ldl_be(&s->as, addr, attrs, NULL);
201be920841SLaurent Vivier         } else {
20282adabf7SPhilippe Mathieu-Daudé             val = address_space_ldl_le(&s->as, addr, attrs, NULL);
203be920841SLaurent Vivier         }
20482adabf7SPhilippe Mathieu-Daudé     } else {
20582adabf7SPhilippe Mathieu-Daudé         addr += offset << 1;
20682adabf7SPhilippe Mathieu-Daudé         if (s->big_endian) {
20782adabf7SPhilippe Mathieu-Daudé             val = address_space_lduw_be(&s->as, addr, attrs, NULL);
20882adabf7SPhilippe Mathieu-Daudé         } else {
20982adabf7SPhilippe Mathieu-Daudé             val = address_space_lduw_le(&s->as, addr, attrs, NULL);
21082adabf7SPhilippe Mathieu-Daudé         }
21182adabf7SPhilippe Mathieu-Daudé     }
21282adabf7SPhilippe Mathieu-Daudé 
213be920841SLaurent Vivier     return val;
214be920841SLaurent Vivier }
215be920841SLaurent Vivier 
dp8393x_put(dp8393xState * s,hwaddr addr,int offset,uint16_t val)21682adabf7SPhilippe Mathieu-Daudé static void dp8393x_put(dp8393xState *s,
21782adabf7SPhilippe Mathieu-Daudé                         hwaddr addr, int offset, uint16_t val)
218be920841SLaurent Vivier {
21982adabf7SPhilippe Mathieu-Daudé     const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
22082adabf7SPhilippe Mathieu-Daudé 
22182adabf7SPhilippe Mathieu-Daudé     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
22282adabf7SPhilippe Mathieu-Daudé         addr += offset << 2;
223be920841SLaurent Vivier         if (s->big_endian) {
22482adabf7SPhilippe Mathieu-Daudé             address_space_stl_be(&s->as, addr, val, attrs, NULL);
225be920841SLaurent Vivier         } else {
22682adabf7SPhilippe Mathieu-Daudé             address_space_stl_le(&s->as, addr, val, attrs, NULL);
2273fe9a838SFinn Thain         }
2283fe9a838SFinn Thain     } else {
22982adabf7SPhilippe Mathieu-Daudé         addr += offset << 1;
23082adabf7SPhilippe Mathieu-Daudé         if (s->big_endian) {
23182adabf7SPhilippe Mathieu-Daudé             address_space_stw_be(&s->as, addr, val, attrs, NULL);
2323fe9a838SFinn Thain         } else {
23382adabf7SPhilippe Mathieu-Daudé             address_space_stw_le(&s->as, addr, val, attrs, NULL);
2343fe9a838SFinn Thain         }
235be920841SLaurent Vivier     }
236be920841SLaurent Vivier }
237be920841SLaurent Vivier 
dp8393x_update_irq(dp8393xState * s)23849ab747fSPaolo Bonzini static void dp8393x_update_irq(dp8393xState *s)
23949ab747fSPaolo Bonzini {
24049ab747fSPaolo Bonzini     int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
24149ab747fSPaolo Bonzini 
24249ab747fSPaolo Bonzini     if (level != s->irq_level) {
24349ab747fSPaolo Bonzini         s->irq_level = level;
24449ab747fSPaolo Bonzini         if (level) {
245c0af04a4SMark Cave-Ayland             trace_dp8393x_raise_irq(s->regs[SONIC_ISR]);
24649ab747fSPaolo Bonzini         } else {
247c0af04a4SMark Cave-Ayland             trace_dp8393x_lower_irq();
24849ab747fSPaolo Bonzini         }
24949ab747fSPaolo Bonzini     }
25049ab747fSPaolo Bonzini 
25149ab747fSPaolo Bonzini     qemu_set_irq(s->irq, level);
25249ab747fSPaolo Bonzini }
25349ab747fSPaolo Bonzini 
dp8393x_do_load_cam(dp8393xState * s)2543df5de64SHervé Poussineau static void dp8393x_do_load_cam(dp8393xState *s)
25549ab747fSPaolo Bonzini {
25649ab747fSPaolo Bonzini     int width, size;
25785e411d7SMark Cave-Ayland     uint16_t index;
25849ab747fSPaolo Bonzini 
25949ab747fSPaolo Bonzini     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
26049ab747fSPaolo Bonzini     size = sizeof(uint16_t) * 4 * width;
26149ab747fSPaolo Bonzini 
26249ab747fSPaolo Bonzini     while (s->regs[SONIC_CDC] & 0x1f) {
26349ab747fSPaolo Bonzini         /* Fill current entry */
26482adabf7SPhilippe Mathieu-Daudé         index = dp8393x_get(s, dp8393x_cdp(s), 0) & 0xf;
26582adabf7SPhilippe Mathieu-Daudé         s->cam[index][0] = dp8393x_get(s, dp8393x_cdp(s), 1);
26682adabf7SPhilippe Mathieu-Daudé         s->cam[index][1] = dp8393x_get(s, dp8393x_cdp(s), 2);
26782adabf7SPhilippe Mathieu-Daudé         s->cam[index][2] = dp8393x_get(s, dp8393x_cdp(s), 3);
2688ac2ffb5SPhilippe Mathieu-Daudé         trace_dp8393x_load_cam(index,
2698ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][0] >> 8, s->cam[index][0] & 0xff,
2708ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][1] >> 8, s->cam[index][1] & 0xff,
2718ac2ffb5SPhilippe Mathieu-Daudé                                s->cam[index][2] >> 8, s->cam[index][2] & 0xff);
27249ab747fSPaolo Bonzini         /* Move to next entry */
27349ab747fSPaolo Bonzini         s->regs[SONIC_CDC]--;
27449ab747fSPaolo Bonzini         s->regs[SONIC_CDP] += size;
27549ab747fSPaolo Bonzini     }
27649ab747fSPaolo Bonzini 
27749ab747fSPaolo Bonzini     /* Read CAM enable */
27882adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_CE] = dp8393x_get(s, dp8393x_cdp(s), 0);
279c0af04a4SMark Cave-Ayland     trace_dp8393x_load_cam_done(s->regs[SONIC_CE]);
28049ab747fSPaolo Bonzini 
28149ab747fSPaolo Bonzini     /* Done */
28249ab747fSPaolo Bonzini     s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
28349ab747fSPaolo Bonzini     s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
28449ab747fSPaolo Bonzini     dp8393x_update_irq(s);
28549ab747fSPaolo Bonzini }
28649ab747fSPaolo Bonzini 
dp8393x_do_read_rra(dp8393xState * s)2873df5de64SHervé Poussineau static void dp8393x_do_read_rra(dp8393xState *s)
28849ab747fSPaolo Bonzini {
28949ab747fSPaolo Bonzini     int width, size;
29049ab747fSPaolo Bonzini 
29149ab747fSPaolo Bonzini     /* Read memory */
29249ab747fSPaolo Bonzini     width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
29349ab747fSPaolo Bonzini     size = sizeof(uint16_t) * 4 * width;
29449ab747fSPaolo Bonzini 
29549ab747fSPaolo Bonzini     /* Update SONIC registers */
29682adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_CRBA0] = dp8393x_get(s, dp8393x_rrp(s), 0);
29782adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_CRBA1] = dp8393x_get(s, dp8393x_rrp(s), 1);
29882adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_RBWC0] = dp8393x_get(s, dp8393x_rrp(s), 2);
29982adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_RBWC1] = dp8393x_get(s, dp8393x_rrp(s), 3);
300c0af04a4SMark Cave-Ayland     trace_dp8393x_read_rra_regs(s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
30149ab747fSPaolo Bonzini                                 s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
30249ab747fSPaolo Bonzini 
30349ab747fSPaolo Bonzini     /* Go to next entry */
30449ab747fSPaolo Bonzini     s->regs[SONIC_RRP] += size;
30549ab747fSPaolo Bonzini 
30649ab747fSPaolo Bonzini     /* Handle wrap */
30749ab747fSPaolo Bonzini     if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
30849ab747fSPaolo Bonzini         s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
30949ab747fSPaolo Bonzini     }
31049ab747fSPaolo Bonzini 
311c2279bd0SFinn Thain     /* Warn the host if CRBA now has the last available resource */
3121ca82a8dSMark Cave-Ayland     if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
31349ab747fSPaolo Bonzini         s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
31449ab747fSPaolo Bonzini         dp8393x_update_irq(s);
31549ab747fSPaolo Bonzini     }
316c2279bd0SFinn Thain 
317c2279bd0SFinn Thain     /* Allow packet reception */
318c2279bd0SFinn Thain     s->last_rba_is_full = false;
31949ab747fSPaolo Bonzini }
32049ab747fSPaolo Bonzini 
dp8393x_do_software_reset(dp8393xState * s)3213df5de64SHervé Poussineau static void dp8393x_do_software_reset(dp8393xState *s)
32249ab747fSPaolo Bonzini {
323bc72ad67SAlex Bligh     timer_del(s->watchdog);
32449ab747fSPaolo Bonzini 
3251ca82a8dSMark Cave-Ayland     s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP |
3261ca82a8dSMark Cave-Ayland                            SONIC_CR_HTX);
32749ab747fSPaolo Bonzini     s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
32849ab747fSPaolo Bonzini }
32949ab747fSPaolo Bonzini 
dp8393x_set_next_tick(dp8393xState * s)3303df5de64SHervé Poussineau static void dp8393x_set_next_tick(dp8393xState *s)
33149ab747fSPaolo Bonzini {
33249ab747fSPaolo Bonzini     uint32_t ticks;
33349ab747fSPaolo Bonzini     int64_t delay;
33449ab747fSPaolo Bonzini 
33549ab747fSPaolo Bonzini     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
336bc72ad67SAlex Bligh         timer_del(s->watchdog);
33749ab747fSPaolo Bonzini         return;
33849ab747fSPaolo Bonzini     }
33949ab747fSPaolo Bonzini 
340581f7b12SPeter Maydell     ticks = dp8393x_wt(s);
341bc72ad67SAlex Bligh     s->wt_last_update = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
34273bcb24dSRutuja Shah     delay = NANOSECONDS_PER_SECOND * ticks / 5000000;
343bc72ad67SAlex Bligh     timer_mod(s->watchdog, s->wt_last_update + delay);
34449ab747fSPaolo Bonzini }
34549ab747fSPaolo Bonzini 
dp8393x_update_wt_regs(dp8393xState * s)3463df5de64SHervé Poussineau static void dp8393x_update_wt_regs(dp8393xState *s)
34749ab747fSPaolo Bonzini {
34849ab747fSPaolo Bonzini     int64_t elapsed;
34949ab747fSPaolo Bonzini     uint32_t val;
35049ab747fSPaolo Bonzini 
35149ab747fSPaolo Bonzini     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
352bc72ad67SAlex Bligh         timer_del(s->watchdog);
35349ab747fSPaolo Bonzini         return;
35449ab747fSPaolo Bonzini     }
35549ab747fSPaolo Bonzini 
356bc72ad67SAlex Bligh     elapsed = s->wt_last_update - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
357581f7b12SPeter Maydell     val = dp8393x_wt(s);
35849ab747fSPaolo Bonzini     val -= elapsed / 5000000;
35949ab747fSPaolo Bonzini     s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
36049ab747fSPaolo Bonzini     s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
3613df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
36249ab747fSPaolo Bonzini 
36349ab747fSPaolo Bonzini }
36449ab747fSPaolo Bonzini 
dp8393x_do_start_timer(dp8393xState * s)3653df5de64SHervé Poussineau static void dp8393x_do_start_timer(dp8393xState *s)
36649ab747fSPaolo Bonzini {
36749ab747fSPaolo Bonzini     s->regs[SONIC_CR] &= ~SONIC_CR_STP;
3683df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
36949ab747fSPaolo Bonzini }
37049ab747fSPaolo Bonzini 
dp8393x_do_stop_timer(dp8393xState * s)3713df5de64SHervé Poussineau static void dp8393x_do_stop_timer(dp8393xState *s)
37249ab747fSPaolo Bonzini {
37349ab747fSPaolo Bonzini     s->regs[SONIC_CR] &= ~SONIC_CR_ST;
3743df5de64SHervé Poussineau     dp8393x_update_wt_regs(s);
37549ab747fSPaolo Bonzini }
37649ab747fSPaolo Bonzini 
377b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc);
3784594f93aSFam Zheng 
dp8393x_do_receiver_enable(dp8393xState * s)3793df5de64SHervé Poussineau static void dp8393x_do_receiver_enable(dp8393xState *s)
38049ab747fSPaolo Bonzini {
38149ab747fSPaolo Bonzini     s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
3824594f93aSFam Zheng     if (dp8393x_can_receive(s->nic->ncs)) {
3834594f93aSFam Zheng         qemu_flush_queued_packets(qemu_get_queue(s->nic));
3844594f93aSFam Zheng     }
38549ab747fSPaolo Bonzini }
38649ab747fSPaolo Bonzini 
dp8393x_do_receiver_disable(dp8393xState * s)3873df5de64SHervé Poussineau static void dp8393x_do_receiver_disable(dp8393xState *s)
38849ab747fSPaolo Bonzini {
38949ab747fSPaolo Bonzini     s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
39049ab747fSPaolo Bonzini }
39149ab747fSPaolo Bonzini 
dp8393x_do_transmit_packets(dp8393xState * s)3923df5de64SHervé Poussineau static void dp8393x_do_transmit_packets(dp8393xState *s)
39349ab747fSPaolo Bonzini {
39449ab747fSPaolo Bonzini     NetClientState *nc = qemu_get_queue(s->nic);
39549ab747fSPaolo Bonzini     int tx_len, len;
39649ab747fSPaolo Bonzini     uint16_t i;
39749ab747fSPaolo Bonzini 
39849ab747fSPaolo Bonzini     while (1) {
39949ab747fSPaolo Bonzini         /* Read memory */
40049ab747fSPaolo Bonzini         s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
401c0af04a4SMark Cave-Ayland         trace_dp8393x_transmit_packet(dp8393x_ttda(s));
40249ab747fSPaolo Bonzini         tx_len = 0;
40349ab747fSPaolo Bonzini 
40449ab747fSPaolo Bonzini         /* Update registers */
40582adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TCR] = dp8393x_get(s, dp8393x_ttda(s), 1) & 0xf000;
40682adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TPS] = dp8393x_get(s, dp8393x_ttda(s), 2);
40782adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TFC] = dp8393x_get(s, dp8393x_ttda(s), 3);
40882adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TSA0] = dp8393x_get(s, dp8393x_ttda(s), 4);
40982adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TSA1] = dp8393x_get(s, dp8393x_ttda(s), 5);
41082adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_TFS] = dp8393x_get(s, dp8393x_ttda(s), 6);
41149ab747fSPaolo Bonzini 
41249ab747fSPaolo Bonzini         /* Handle programmable interrupt */
41349ab747fSPaolo Bonzini         if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
41449ab747fSPaolo Bonzini             s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
41549ab747fSPaolo Bonzini         } else {
41649ab747fSPaolo Bonzini             s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
41749ab747fSPaolo Bonzini         }
41849ab747fSPaolo Bonzini 
41949ab747fSPaolo Bonzini         for (i = 0; i < s->regs[SONIC_TFC]; ) {
42049ab747fSPaolo Bonzini             /* Append fragment */
42149ab747fSPaolo Bonzini             len = s->regs[SONIC_TFS];
42249ab747fSPaolo Bonzini             if (tx_len + len > sizeof(s->tx_buffer)) {
42349ab747fSPaolo Bonzini                 len = sizeof(s->tx_buffer) - tx_len;
42449ab747fSPaolo Bonzini             }
42519f70347SPeter Maydell             address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
42619f70347SPeter Maydell                                &s->tx_buffer[tx_len], len);
42749ab747fSPaolo Bonzini             tx_len += len;
42849ab747fSPaolo Bonzini 
42949ab747fSPaolo Bonzini             i++;
43049ab747fSPaolo Bonzini             if (i != s->regs[SONIC_TFC]) {
43149ab747fSPaolo Bonzini                 /* Read next fragment details */
43282adabf7SPhilippe Mathieu-Daudé                 s->regs[SONIC_TSA0] = dp8393x_get(s, dp8393x_ttda(s),
43382adabf7SPhilippe Mathieu-Daudé                                                   4 + 3 * i);
43482adabf7SPhilippe Mathieu-Daudé                 s->regs[SONIC_TSA1] = dp8393x_get(s, dp8393x_ttda(s),
43582adabf7SPhilippe Mathieu-Daudé                                                   5 + 3 * i);
43682adabf7SPhilippe Mathieu-Daudé                 s->regs[SONIC_TFS] = dp8393x_get(s, dp8393x_ttda(s),
43782adabf7SPhilippe Mathieu-Daudé                                                  6 + 3 * i);
43849ab747fSPaolo Bonzini             }
43949ab747fSPaolo Bonzini         }
44049ab747fSPaolo Bonzini 
44149ab747fSPaolo Bonzini         /* Handle Ethernet checksum */
44249ab747fSPaolo Bonzini         if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
4431ca82a8dSMark Cave-Ayland             /*
4441ca82a8dSMark Cave-Ayland              * Don't append FCS there, to look like slirp packets
4451ca82a8dSMark Cave-Ayland              * which don't have one
4461ca82a8dSMark Cave-Ayland              */
44749ab747fSPaolo Bonzini         } else {
44849ab747fSPaolo Bonzini             /* Remove existing FCS */
44949ab747fSPaolo Bonzini             tx_len -= 4;
450915976bdSMauro Matteo Cascella             if (tx_len < 0) {
451c0af04a4SMark Cave-Ayland                 trace_dp8393x_transmit_txlen_error(tx_len);
452915976bdSMauro Matteo Cascella                 break;
453915976bdSMauro Matteo Cascella             }
45449ab747fSPaolo Bonzini         }
45549ab747fSPaolo Bonzini 
45649ab747fSPaolo Bonzini         if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
45749ab747fSPaolo Bonzini             /* Loopback */
45849ab747fSPaolo Bonzini             s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
45949ab747fSPaolo Bonzini             if (nc->info->can_receive(nc)) {
46049ab747fSPaolo Bonzini                 s->loopback_packet = 1;
461331d2ac9SJason Wang                 qemu_receive_packet(nc, s->tx_buffer, tx_len);
46249ab747fSPaolo Bonzini             }
46349ab747fSPaolo Bonzini         } else {
46449ab747fSPaolo Bonzini             /* Transmit packet */
46549ab747fSPaolo Bonzini             qemu_send_packet(nc, s->tx_buffer, tx_len);
46649ab747fSPaolo Bonzini         }
46749ab747fSPaolo Bonzini         s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
46849ab747fSPaolo Bonzini 
46949ab747fSPaolo Bonzini         /* Write status */
47082adabf7SPhilippe Mathieu-Daudé         dp8393x_put(s, dp8393x_ttda(s), 0, s->regs[SONIC_TCR] & 0x0fff);
47149ab747fSPaolo Bonzini 
47249ab747fSPaolo Bonzini         if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
47349ab747fSPaolo Bonzini             /* Read footer of packet */
47482adabf7SPhilippe Mathieu-Daudé             s->regs[SONIC_CTDA] = dp8393x_get(s, dp8393x_ttda(s),
47582adabf7SPhilippe Mathieu-Daudé                                               4 + 3 * s->regs[SONIC_TFC]);
476a0cf4297SFinn Thain             if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) {
47749ab747fSPaolo Bonzini                 /* EOL detected */
47849ab747fSPaolo Bonzini                 break;
47949ab747fSPaolo Bonzini             }
48049ab747fSPaolo Bonzini         }
48149ab747fSPaolo Bonzini     }
48249ab747fSPaolo Bonzini 
48349ab747fSPaolo Bonzini     /* Done */
48449ab747fSPaolo Bonzini     s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
48549ab747fSPaolo Bonzini     s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
48649ab747fSPaolo Bonzini     dp8393x_update_irq(s);
48749ab747fSPaolo Bonzini }
48849ab747fSPaolo Bonzini 
dp8393x_do_halt_transmission(dp8393xState * s)4893df5de64SHervé Poussineau static void dp8393x_do_halt_transmission(dp8393xState *s)
49049ab747fSPaolo Bonzini {
49149ab747fSPaolo Bonzini     /* Nothing to do */
49249ab747fSPaolo Bonzini }
49349ab747fSPaolo Bonzini 
dp8393x_do_command(dp8393xState * s,uint16_t command)4943df5de64SHervé Poussineau static void dp8393x_do_command(dp8393xState *s, uint16_t command)
49549ab747fSPaolo Bonzini {
49649ab747fSPaolo Bonzini     if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
49749ab747fSPaolo Bonzini         s->regs[SONIC_CR] &= ~SONIC_CR_RST;
49849ab747fSPaolo Bonzini         return;
49949ab747fSPaolo Bonzini     }
50049ab747fSPaolo Bonzini 
50149ab747fSPaolo Bonzini     s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
50249ab747fSPaolo Bonzini 
5031ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_HTX) {
5043df5de64SHervé Poussineau         dp8393x_do_halt_transmission(s);
5051ca82a8dSMark Cave-Ayland     }
5061ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_TXP) {
5073df5de64SHervé Poussineau         dp8393x_do_transmit_packets(s);
5081ca82a8dSMark Cave-Ayland     }
5091ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXDIS) {
5103df5de64SHervé Poussineau         dp8393x_do_receiver_disable(s);
5111ca82a8dSMark Cave-Ayland     }
5121ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RXEN) {
5133df5de64SHervé Poussineau         dp8393x_do_receiver_enable(s);
5141ca82a8dSMark Cave-Ayland     }
5151ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_STP) {
5163df5de64SHervé Poussineau         dp8393x_do_stop_timer(s);
5171ca82a8dSMark Cave-Ayland     }
5181ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_ST) {
5193df5de64SHervé Poussineau         dp8393x_do_start_timer(s);
5201ca82a8dSMark Cave-Ayland     }
5211ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_RST) {
5223df5de64SHervé Poussineau         dp8393x_do_software_reset(s);
5231ca82a8dSMark Cave-Ayland     }
524a3cce282SFinn Thain     if (command & SONIC_CR_RRRA) {
5253df5de64SHervé Poussineau         dp8393x_do_read_rra(s);
526a3cce282SFinn Thain         s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
527a3cce282SFinn Thain     }
5281ca82a8dSMark Cave-Ayland     if (command & SONIC_CR_LCAM) {
5293df5de64SHervé Poussineau         dp8393x_do_load_cam(s);
53049ab747fSPaolo Bonzini     }
5311ca82a8dSMark Cave-Ayland }
53249ab747fSPaolo Bonzini 
dp8393x_read(void * opaque,hwaddr addr,unsigned int size)53384689cbbSHervé Poussineau static uint64_t dp8393x_read(void *opaque, hwaddr addr, unsigned int size)
53449ab747fSPaolo Bonzini {
53584689cbbSHervé Poussineau     dp8393xState *s = opaque;
53684689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
53749ab747fSPaolo Bonzini     uint16_t val = 0;
53849ab747fSPaolo Bonzini 
53949ab747fSPaolo Bonzini     switch (reg) {
54049ab747fSPaolo Bonzini     /* Update data before reading it */
54149ab747fSPaolo Bonzini     case SONIC_WT0:
54249ab747fSPaolo Bonzini     case SONIC_WT1:
5433df5de64SHervé Poussineau         dp8393x_update_wt_regs(s);
54449ab747fSPaolo Bonzini         val = s->regs[reg];
54549ab747fSPaolo Bonzini         break;
54649ab747fSPaolo Bonzini     /* Accept read to some registers only when in reset mode */
54749ab747fSPaolo Bonzini     case SONIC_CAP2:
54849ab747fSPaolo Bonzini     case SONIC_CAP1:
54949ab747fSPaolo Bonzini     case SONIC_CAP0:
55049ab747fSPaolo Bonzini         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
5518ac2ffb5SPhilippe Mathieu-Daudé             val = s->cam[s->regs[SONIC_CEP] & 0xf][SONIC_CAP0 - reg];
55249ab747fSPaolo Bonzini         }
55349ab747fSPaolo Bonzini         break;
5542431f4f1SMichael Tokarev     /* All other registers have no special constraints */
55549ab747fSPaolo Bonzini     default:
55649ab747fSPaolo Bonzini         val = s->regs[reg];
55749ab747fSPaolo Bonzini     }
55849ab747fSPaolo Bonzini 
559c0af04a4SMark Cave-Ayland     trace_dp8393x_read(reg, reg_names[reg], val, size);
56049ab747fSPaolo Bonzini 
56139d9919fSMark Cave-Ayland     return val;
56249ab747fSPaolo Bonzini }
56349ab747fSPaolo Bonzini 
dp8393x_write(void * opaque,hwaddr addr,uint64_t val,unsigned int size)56439d9919fSMark Cave-Ayland static void dp8393x_write(void *opaque, hwaddr addr, uint64_t val,
56584689cbbSHervé Poussineau                           unsigned int size)
56649ab747fSPaolo Bonzini {
56784689cbbSHervé Poussineau     dp8393xState *s = opaque;
56884689cbbSHervé Poussineau     int reg = addr >> s->it_shift;
56984689cbbSHervé Poussineau 
570c0af04a4SMark Cave-Ayland     trace_dp8393x_write(reg, reg_names[reg], val, size);
57149ab747fSPaolo Bonzini 
57249ab747fSPaolo Bonzini     switch (reg) {
57349ab747fSPaolo Bonzini     /* Command register */
57449ab747fSPaolo Bonzini     case SONIC_CR:
5753fe9a838SFinn Thain         dp8393x_do_command(s, val);
57649ab747fSPaolo Bonzini         break;
57749ab747fSPaolo Bonzini     /* Prevent write to read-only registers */
57849ab747fSPaolo Bonzini     case SONIC_CAP2:
57949ab747fSPaolo Bonzini     case SONIC_CAP1:
58049ab747fSPaolo Bonzini     case SONIC_CAP0:
58149ab747fSPaolo Bonzini     case SONIC_SR:
58249ab747fSPaolo Bonzini     case SONIC_MDT:
583c0af04a4SMark Cave-Ayland         trace_dp8393x_write_invalid(reg);
58449ab747fSPaolo Bonzini         break;
58549ab747fSPaolo Bonzini     /* Accept write to some registers only when in reset mode */
58649ab747fSPaolo Bonzini     case SONIC_DCR:
58749ab747fSPaolo Bonzini         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
5883fe9a838SFinn Thain             s->regs[reg] = val & 0xbfff;
58949ab747fSPaolo Bonzini         } else {
590c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR");
59149ab747fSPaolo Bonzini         }
59249ab747fSPaolo Bonzini         break;
59349ab747fSPaolo Bonzini     case SONIC_DCR2:
59449ab747fSPaolo Bonzini         if (s->regs[SONIC_CR] & SONIC_CR_RST) {
5953fe9a838SFinn Thain             s->regs[reg] = val & 0xf017;
59649ab747fSPaolo Bonzini         } else {
597c0af04a4SMark Cave-Ayland             trace_dp8393x_write_invalid_dcr("DCR2");
59849ab747fSPaolo Bonzini         }
59949ab747fSPaolo Bonzini         break;
60049ab747fSPaolo Bonzini     /* 12 lower bytes are Read Only */
60149ab747fSPaolo Bonzini     case SONIC_TCR:
6023fe9a838SFinn Thain         s->regs[reg] = val & 0xf000;
60349ab747fSPaolo Bonzini         break;
60449ab747fSPaolo Bonzini     /* 9 lower bytes are Read Only */
60549ab747fSPaolo Bonzini     case SONIC_RCR:
6063fe9a838SFinn Thain         s->regs[reg] = val & 0xffe0;
60749ab747fSPaolo Bonzini         break;
60849ab747fSPaolo Bonzini     /* Ignore most significant bit */
60949ab747fSPaolo Bonzini     case SONIC_IMR:
6103fe9a838SFinn Thain         s->regs[reg] = val & 0x7fff;
61149ab747fSPaolo Bonzini         dp8393x_update_irq(s);
61249ab747fSPaolo Bonzini         break;
61349ab747fSPaolo Bonzini     /* Clear bits by writing 1 to them */
61449ab747fSPaolo Bonzini     case SONIC_ISR:
6153fe9a838SFinn Thain         val &= s->regs[reg];
6163fe9a838SFinn Thain         s->regs[reg] &= ~val;
6173fe9a838SFinn Thain         if (val & SONIC_ISR_RBE) {
6183df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
61949ab747fSPaolo Bonzini         }
62049ab747fSPaolo Bonzini         dp8393x_update_irq(s);
62149ab747fSPaolo Bonzini         break;
622ea227027SFinn Thain     /* The guest is required to store aligned pointers here */
62349ab747fSPaolo Bonzini     case SONIC_RSA:
62449ab747fSPaolo Bonzini     case SONIC_REA:
62549ab747fSPaolo Bonzini     case SONIC_RRP:
62649ab747fSPaolo Bonzini     case SONIC_RWP:
627ea227027SFinn Thain         if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
628ea227027SFinn Thain             s->regs[reg] = val & 0xfffc;
629ea227027SFinn Thain         } else {
6303fe9a838SFinn Thain             s->regs[reg] = val & 0xfffe;
631ea227027SFinn Thain         }
63249ab747fSPaolo Bonzini         break;
63349ab747fSPaolo Bonzini     /* Invert written value for some registers */
63449ab747fSPaolo Bonzini     case SONIC_CRCT:
63549ab747fSPaolo Bonzini     case SONIC_FAET:
63649ab747fSPaolo Bonzini     case SONIC_MPT:
6373fe9a838SFinn Thain         s->regs[reg] = val ^ 0xffff;
63849ab747fSPaolo Bonzini         break;
63949ab747fSPaolo Bonzini     /* All other registers have no special contrainst */
64049ab747fSPaolo Bonzini     default:
6413fe9a838SFinn Thain         s->regs[reg] = val;
64249ab747fSPaolo Bonzini     }
64349ab747fSPaolo Bonzini 
64449ab747fSPaolo Bonzini     if (reg == SONIC_WT0 || reg == SONIC_WT1) {
6453df5de64SHervé Poussineau         dp8393x_set_next_tick(s);
64649ab747fSPaolo Bonzini     }
64749ab747fSPaolo Bonzini }
64849ab747fSPaolo Bonzini 
64939d9919fSMark Cave-Ayland /*
65039d9919fSMark Cave-Ayland  * Since .impl.max_access_size is effectively controlled by the it_shift
65139d9919fSMark Cave-Ayland  * property, leave it unspecified for now to allow the memory API to
65239d9919fSMark Cave-Ayland  * correctly zero extend the 16-bit register values to the access size up to and
65339d9919fSMark Cave-Ayland  * including it_shift.
65439d9919fSMark Cave-Ayland  */
65584689cbbSHervé Poussineau static const MemoryRegionOps dp8393x_ops = {
65684689cbbSHervé Poussineau     .read = dp8393x_read,
65784689cbbSHervé Poussineau     .write = dp8393x_write,
65839d9919fSMark Cave-Ayland     .impl.min_access_size = 2,
65984689cbbSHervé Poussineau     .endianness = DEVICE_NATIVE_ENDIAN,
66084689cbbSHervé Poussineau };
66184689cbbSHervé Poussineau 
dp8393x_watchdog(void * opaque)66249ab747fSPaolo Bonzini static void dp8393x_watchdog(void *opaque)
66349ab747fSPaolo Bonzini {
66449ab747fSPaolo Bonzini     dp8393xState *s = opaque;
66549ab747fSPaolo Bonzini 
66649ab747fSPaolo Bonzini     if (s->regs[SONIC_CR] & SONIC_CR_STP) {
66749ab747fSPaolo Bonzini         return;
66849ab747fSPaolo Bonzini     }
66949ab747fSPaolo Bonzini 
67049ab747fSPaolo Bonzini     s->regs[SONIC_WT1] = 0xffff;
67149ab747fSPaolo Bonzini     s->regs[SONIC_WT0] = 0xffff;
6723df5de64SHervé Poussineau     dp8393x_set_next_tick(s);
67349ab747fSPaolo Bonzini 
67449ab747fSPaolo Bonzini     /* Signal underflow */
67549ab747fSPaolo Bonzini     s->regs[SONIC_ISR] |= SONIC_ISR_TC;
67649ab747fSPaolo Bonzini     dp8393x_update_irq(s);
67749ab747fSPaolo Bonzini }
67849ab747fSPaolo Bonzini 
dp8393x_can_receive(NetClientState * nc)679b8c4b67eSPhilippe Mathieu-Daudé static bool dp8393x_can_receive(NetClientState *nc)
68049ab747fSPaolo Bonzini {
68149ab747fSPaolo Bonzini     dp8393xState *s = qemu_get_nic_opaque(nc);
68249ab747fSPaolo Bonzini 
683b8c4b67eSPhilippe Mathieu-Daudé     return !!(s->regs[SONIC_CR] & SONIC_CR_RXEN);
68449ab747fSPaolo Bonzini }
68549ab747fSPaolo Bonzini 
dp8393x_receive_filter(dp8393xState * s,const uint8_t * buf,int size)6863df5de64SHervé Poussineau static int dp8393x_receive_filter(dp8393xState *s, const uint8_t * buf,
6873df5de64SHervé Poussineau                                   int size)
68849ab747fSPaolo Bonzini {
68949ab747fSPaolo Bonzini     static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
69049ab747fSPaolo Bonzini     int i;
69149ab747fSPaolo Bonzini 
69249ab747fSPaolo Bonzini     /* Check promiscuous mode */
69349ab747fSPaolo Bonzini     if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
69449ab747fSPaolo Bonzini         return 0;
69549ab747fSPaolo Bonzini     }
69649ab747fSPaolo Bonzini 
69749ab747fSPaolo Bonzini     /* Check multicast packets */
69849ab747fSPaolo Bonzini     if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
69949ab747fSPaolo Bonzini         return SONIC_RCR_MC;
70049ab747fSPaolo Bonzini     }
70149ab747fSPaolo Bonzini 
70249ab747fSPaolo Bonzini     /* Check broadcast */
7031ca82a8dSMark Cave-Ayland     if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) &&
7041ca82a8dSMark Cave-Ayland          !memcmp(buf, bcast, sizeof(bcast))) {
70549ab747fSPaolo Bonzini         return SONIC_RCR_BC;
70649ab747fSPaolo Bonzini     }
70749ab747fSPaolo Bonzini 
70849ab747fSPaolo Bonzini     /* Check CAM */
70949ab747fSPaolo Bonzini     for (i = 0; i < 16; i++) {
71049ab747fSPaolo Bonzini         if (s->regs[SONIC_CE] & (1 << i)) {
71149ab747fSPaolo Bonzini             /* Entry enabled */
71249ab747fSPaolo Bonzini             if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
71349ab747fSPaolo Bonzini                 return 0;
71449ab747fSPaolo Bonzini             }
71549ab747fSPaolo Bonzini         }
71649ab747fSPaolo Bonzini     }
71749ab747fSPaolo Bonzini 
71849ab747fSPaolo Bonzini     return -1;
71949ab747fSPaolo Bonzini }
72049ab747fSPaolo Bonzini 
dp8393x_receive(NetClientState * nc,const uint8_t * buf,size_t pkt_size)7213df5de64SHervé Poussineau static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf,
7229e3cd456SFinn Thain                                size_t pkt_size)
72349ab747fSPaolo Bonzini {
72449ab747fSPaolo Bonzini     dp8393xState *s = qemu_get_nic_opaque(nc);
72549ab747fSPaolo Bonzini     int packet_type;
72649ab747fSPaolo Bonzini     uint32_t available, address;
72782adabf7SPhilippe Mathieu-Daudé     int rx_len, padded_len;
72849ab747fSPaolo Bonzini     uint32_t checksum;
7299e3cd456SFinn Thain     int size;
73049ab747fSPaolo Bonzini 
73149ab747fSPaolo Bonzini     s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
73249ab747fSPaolo Bonzini         SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
73349ab747fSPaolo Bonzini 
734c2279bd0SFinn Thain     if (s->last_rba_is_full) {
735c2279bd0SFinn Thain         return pkt_size;
736c2279bd0SFinn Thain     }
737c2279bd0SFinn Thain 
738350e7d9aSFinn Thain     rx_len = pkt_size + sizeof(checksum);
739350e7d9aSFinn Thain     if (s->regs[SONIC_DCR] & SONIC_DCR_DW) {
740350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 3) + 1;
741350e7d9aSFinn Thain     } else {
742350e7d9aSFinn Thain         padded_len = ((rx_len - 1) | 1) + 1;
743350e7d9aSFinn Thain     }
744350e7d9aSFinn Thain 
745350e7d9aSFinn Thain     if (padded_len > dp8393x_rbwc(s) * 2) {
746c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_oversize(pkt_size);
747ada74315SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_RBAE;
748ada74315SFinn Thain         dp8393x_update_irq(s);
749c2279bd0SFinn Thain         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
750c2279bd0SFinn Thain         goto done;
751ada74315SFinn Thain     }
752ada74315SFinn Thain 
7539e3cd456SFinn Thain     packet_type = dp8393x_receive_filter(s, buf, pkt_size);
75449ab747fSPaolo Bonzini     if (packet_type < 0) {
755c0af04a4SMark Cave-Ayland         trace_dp8393x_receive_not_netcard();
75649ab747fSPaolo Bonzini         return -1;
75749ab747fSPaolo Bonzini     }
75849ab747fSPaolo Bonzini 
75949ab747fSPaolo Bonzini     /* Check for EOL */
76088f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
76149ab747fSPaolo Bonzini         /* Are we still in resource exhaustion? */
76282adabf7SPhilippe Mathieu-Daudé         s->regs[SONIC_LLFA] = dp8393x_get(s, dp8393x_crda(s), 5);
7635b0c98fcSFinn Thain         if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
76449ab747fSPaolo Bonzini             /* Still EOL ; stop reception */
76549ab747fSPaolo Bonzini             return -1;
76649ab747fSPaolo Bonzini         }
7675b0c98fcSFinn Thain         /* Link has been updated by host */
768d9fae131SFinn Thain 
769d9fae131SFinn Thain         /* Clear in_use */
77082adabf7SPhilippe Mathieu-Daudé         dp8393x_put(s, dp8393x_crda(s), 6, 0x0000);
771d9fae131SFinn Thain 
772d9fae131SFinn Thain         /* Move to next descriptor */
7735b0c98fcSFinn Thain         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
774d9fae131SFinn Thain         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
77549ab747fSPaolo Bonzini     }
77649ab747fSPaolo Bonzini 
77749ab747fSPaolo Bonzini     /* Save current position */
77849ab747fSPaolo Bonzini     s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
77949ab747fSPaolo Bonzini     s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
78049ab747fSPaolo Bonzini 
78149ab747fSPaolo Bonzini     /* Calculate the ethernet checksum */
782350e7d9aSFinn Thain     checksum = cpu_to_le32(crc32(0, buf, pkt_size));
78349ab747fSPaolo Bonzini 
78449ab747fSPaolo Bonzini     /* Put packet into RBA */
785c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_packet(dp8393x_crba(s));
786581f7b12SPeter Maydell     address = dp8393x_crba(s);
78719f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
788350e7d9aSFinn Thain                         buf, pkt_size);
789350e7d9aSFinn Thain     address += pkt_size;
790350e7d9aSFinn Thain 
791350e7d9aSFinn Thain     /* Put frame checksum into RBA */
79219f70347SPeter Maydell     address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
793350e7d9aSFinn Thain                         &checksum, sizeof(checksum));
794350e7d9aSFinn Thain     address += sizeof(checksum);
795350e7d9aSFinn Thain 
796350e7d9aSFinn Thain     /* Pad short packets to keep pointers aligned */
797350e7d9aSFinn Thain     if (rx_len < padded_len) {
798350e7d9aSFinn Thain         size = padded_len - rx_len;
799197ade0dSPhilippe Mathieu-Daudé         address_space_write(&s->as, address, MEMTXATTRS_UNSPECIFIED,
800197ade0dSPhilippe Mathieu-Daudé                             "\xFF\xFF\xFF", size);
801350e7d9aSFinn Thain         address += size;
802350e7d9aSFinn Thain     }
803350e7d9aSFinn Thain 
80449ab747fSPaolo Bonzini     s->regs[SONIC_CRBA1] = address >> 16;
80549ab747fSPaolo Bonzini     s->regs[SONIC_CRBA0] = address & 0xffff;
806581f7b12SPeter Maydell     available = dp8393x_rbwc(s);
807350e7d9aSFinn Thain     available -= padded_len >> 1;
80849ab747fSPaolo Bonzini     s->regs[SONIC_RBWC1] = available >> 16;
80949ab747fSPaolo Bonzini     s->regs[SONIC_RBWC0] = available & 0xffff;
81049ab747fSPaolo Bonzini 
81149ab747fSPaolo Bonzini     /* Update status */
812581f7b12SPeter Maydell     if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) {
81349ab747fSPaolo Bonzini         s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
81449ab747fSPaolo Bonzini     }
81549ab747fSPaolo Bonzini     s->regs[SONIC_RCR] |= packet_type;
81649ab747fSPaolo Bonzini     s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
81749ab747fSPaolo Bonzini     if (s->loopback_packet) {
81849ab747fSPaolo Bonzini         s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
81949ab747fSPaolo Bonzini         s->loopback_packet = 0;
82049ab747fSPaolo Bonzini     }
82149ab747fSPaolo Bonzini 
82249ab747fSPaolo Bonzini     /* Write status to memory */
823c0af04a4SMark Cave-Ayland     trace_dp8393x_receive_write_status(dp8393x_crda(s));
82482adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 0, s->regs[SONIC_RCR]); /* status */
82582adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 1, rx_len); /* byte count */
82682adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 2, s->regs[SONIC_TRBA0]); /* pkt_ptr0 */
82782adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 3, s->regs[SONIC_TRBA1]); /* pkt_ptr1 */
82882adabf7SPhilippe Mathieu-Daudé     dp8393x_put(s, dp8393x_crda(s), 4, s->regs[SONIC_RSC]); /* seq_no */
82949ab747fSPaolo Bonzini 
8305b0c98fcSFinn Thain     /* Check link field */
83182adabf7SPhilippe Mathieu-Daudé     s->regs[SONIC_LLFA] = dp8393x_get(s, dp8393x_crda(s), 5);
83288f632fbSFinn Thain     if (s->regs[SONIC_LLFA] & SONIC_DESC_EOL) {
83349ab747fSPaolo Bonzini         /* EOL detected */
83449ab747fSPaolo Bonzini         s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
83549ab747fSPaolo Bonzini     } else {
83646ffee9aSFinn Thain         /* Clear in_use */
83782adabf7SPhilippe Mathieu-Daudé         dp8393x_put(s, dp8393x_crda(s), 6, 0x0000);
8385b0c98fcSFinn Thain 
8395b0c98fcSFinn Thain         /* Move to next descriptor */
84049ab747fSPaolo Bonzini         s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
84149ab747fSPaolo Bonzini         s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
84280b60673SFinn Thain     }
84380b60673SFinn Thain 
844c2279bd0SFinn Thain     dp8393x_update_irq(s);
845c2279bd0SFinn Thain 
84680b60673SFinn Thain     s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) |
84780b60673SFinn Thain                          ((s->regs[SONIC_RSC] + 1) & 0x00ff);
84849ab747fSPaolo Bonzini 
849c2279bd0SFinn Thain done:
850c2279bd0SFinn Thain 
85149ab747fSPaolo Bonzini     if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
852c2279bd0SFinn Thain         if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) {
853c2279bd0SFinn Thain             /* Stop packet reception */
854c2279bd0SFinn Thain             s->last_rba_is_full = true;
855c2279bd0SFinn Thain         } else {
856c2279bd0SFinn Thain             /* Read next resource */
8573df5de64SHervé Poussineau             dp8393x_do_read_rra(s);
85849ab747fSPaolo Bonzini         }
859c2279bd0SFinn Thain     }
86049ab747fSPaolo Bonzini 
8619e3cd456SFinn Thain     return pkt_size;
86249ab747fSPaolo Bonzini }
86349ab747fSPaolo Bonzini 
dp8393x_reset(DeviceState * dev)864104655a5SHervé Poussineau static void dp8393x_reset(DeviceState *dev)
86549ab747fSPaolo Bonzini {
866104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
867bc72ad67SAlex Bligh     timer_del(s->watchdog);
86849ab747fSPaolo Bonzini 
869bd8f1ebcSHervé Poussineau     memset(s->regs, 0, sizeof(s->regs));
870083e21bbSFinn Thain     s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux/mips */
87149ab747fSPaolo Bonzini     s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
87249ab747fSPaolo Bonzini     s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
8731ca82a8dSMark Cave-Ayland     s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD |
8741ca82a8dSMark Cave-Ayland                             SONIC_RCR_RNT);
87549ab747fSPaolo Bonzini     s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
87649ab747fSPaolo Bonzini     s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
87749ab747fSPaolo Bonzini     s->regs[SONIC_IMR] = 0;
87849ab747fSPaolo Bonzini     s->regs[SONIC_ISR] = 0;
87949ab747fSPaolo Bonzini     s->regs[SONIC_DCR2] = 0;
88049ab747fSPaolo Bonzini     s->regs[SONIC_EOBC] = 0x02F8;
88149ab747fSPaolo Bonzini     s->regs[SONIC_RSC] = 0;
88249ab747fSPaolo Bonzini     s->regs[SONIC_CE] = 0;
88349ab747fSPaolo Bonzini     s->regs[SONIC_RSC] = 0;
88449ab747fSPaolo Bonzini 
88549ab747fSPaolo Bonzini     /* Network cable is connected */
88649ab747fSPaolo Bonzini     s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
88749ab747fSPaolo Bonzini 
88849ab747fSPaolo Bonzini     dp8393x_update_irq(s);
88949ab747fSPaolo Bonzini }
89049ab747fSPaolo Bonzini 
89149ab747fSPaolo Bonzini static NetClientInfo net_dp83932_info = {
892f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
89349ab747fSPaolo Bonzini     .size = sizeof(NICState),
8943df5de64SHervé Poussineau     .can_receive = dp8393x_can_receive,
8953df5de64SHervé Poussineau     .receive = dp8393x_receive,
89649ab747fSPaolo Bonzini };
89749ab747fSPaolo Bonzini 
dp8393x_instance_init(Object * obj)898104655a5SHervé Poussineau static void dp8393x_instance_init(Object *obj)
89949ab747fSPaolo Bonzini {
900104655a5SHervé Poussineau     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
901104655a5SHervé Poussineau     dp8393xState *s = DP8393X(obj);
90249ab747fSPaolo Bonzini 
903104655a5SHervé Poussineau     sysbus_init_mmio(sbd, &s->mmio);
904104655a5SHervé Poussineau     sysbus_init_irq(sbd, &s->irq);
905104655a5SHervé Poussineau }
90649ab747fSPaolo Bonzini 
dp8393x_realize(DeviceState * dev,Error ** errp)907104655a5SHervé Poussineau static void dp8393x_realize(DeviceState *dev, Error **errp)
908104655a5SHervé Poussineau {
909104655a5SHervé Poussineau     dp8393xState *s = DP8393X(dev);
91049ab747fSPaolo Bonzini 
911104655a5SHervé Poussineau     address_space_init(&s->as, s->dma_mr, "dp8393x");
912104655a5SHervé Poussineau     memory_region_init_io(&s->mmio, OBJECT(dev), &dp8393x_ops, s,
91367b38ddfSPhilippe Mathieu-Daudé                           "dp8393x-regs", SONIC_REG_COUNT << s->it_shift);
914104655a5SHervé Poussineau 
915104655a5SHervé Poussineau     s->nic = qemu_new_nic(&net_dp83932_info, &s->conf,
9167d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), dev->id,
9177d0fefdfSAkihiko Odaki                           &dev->mem_reentrancy_guard, s);
918104655a5SHervé Poussineau     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
919104655a5SHervé Poussineau 
920bc72ad67SAlex Bligh     s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s);
92149ab747fSPaolo Bonzini }
922104655a5SHervé Poussineau 
9231670735dSHervé Poussineau static const VMStateDescription vmstate_dp8393x = {
9241670735dSHervé Poussineau     .name = "dp8393x",
9258ac2ffb5SPhilippe Mathieu-Daudé     .version_id = 1,
9268ac2ffb5SPhilippe Mathieu-Daudé     .minimum_version_id = 1,
9271de81b42SRichard Henderson     .fields = (const VMStateField []) {
9288ac2ffb5SPhilippe Mathieu-Daudé         VMSTATE_UINT16_2DARRAY(cam, dp8393xState, 16, 3),
92967b38ddfSPhilippe Mathieu-Daudé         VMSTATE_UINT16_ARRAY(regs, dp8393xState, SONIC_REG_COUNT),
9301670735dSHervé Poussineau         VMSTATE_END_OF_LIST()
9311670735dSHervé Poussineau     }
9321670735dSHervé Poussineau };
9331670735dSHervé Poussineau 
934104655a5SHervé Poussineau static Property dp8393x_properties[] = {
935104655a5SHervé Poussineau     DEFINE_NIC_PROPERTIES(dp8393xState, conf),
9363110ce81SMarc-André Lureau     DEFINE_PROP_LINK("dma_mr", dp8393xState, dma_mr,
9373110ce81SMarc-André Lureau                      TYPE_MEMORY_REGION, MemoryRegion *),
938104655a5SHervé Poussineau     DEFINE_PROP_UINT8("it_shift", dp8393xState, it_shift, 0),
939be920841SLaurent Vivier     DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false),
940104655a5SHervé Poussineau     DEFINE_PROP_END_OF_LIST(),
941104655a5SHervé Poussineau };
942104655a5SHervé Poussineau 
dp8393x_class_init(ObjectClass * klass,void * data)943104655a5SHervé Poussineau static void dp8393x_class_init(ObjectClass *klass, void *data)
944104655a5SHervé Poussineau {
945104655a5SHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
946104655a5SHervé Poussineau 
947104655a5SHervé Poussineau     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
948104655a5SHervé Poussineau     dc->realize = dp8393x_realize;
949e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, dp8393x_reset);
9501670735dSHervé Poussineau     dc->vmsd = &vmstate_dp8393x;
9514f67d30bSMarc-André Lureau     device_class_set_props(dc, dp8393x_properties);
952104655a5SHervé Poussineau }
953104655a5SHervé Poussineau 
954104655a5SHervé Poussineau static const TypeInfo dp8393x_info = {
955104655a5SHervé Poussineau     .name          = TYPE_DP8393X,
956104655a5SHervé Poussineau     .parent        = TYPE_SYS_BUS_DEVICE,
957104655a5SHervé Poussineau     .instance_size = sizeof(dp8393xState),
958104655a5SHervé Poussineau     .instance_init = dp8393x_instance_init,
959104655a5SHervé Poussineau     .class_init    = dp8393x_class_init,
960104655a5SHervé Poussineau };
961104655a5SHervé Poussineau 
dp8393x_register_types(void)962104655a5SHervé Poussineau static void dp8393x_register_types(void)
963104655a5SHervé Poussineau {
964104655a5SHervé Poussineau     type_register_static(&dp8393x_info);
965104655a5SHervé Poussineau }
966104655a5SHervé Poussineau 
967104655a5SHervé Poussineau type_init(dp8393x_register_types)
968