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