xref: /openbmc/qemu/hw/net/xilinx_axienet.c (revision 35e60bfdbc14ce31bba55cc82144f8a2a82ede68)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU model of Xilinx AXI-Ethernet.
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2011 Edgar E. Iglesias.
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
2449ab747fSPaolo Bonzini 
2549ab747fSPaolo Bonzini #include "hw/sysbus.h"
2649ab747fSPaolo Bonzini #include "qemu/log.h"
2749ab747fSPaolo Bonzini #include "net/net.h"
2849ab747fSPaolo Bonzini #include "net/checksum.h"
2949ab747fSPaolo Bonzini #include "qapi/qmp/qerror.h"
3049ab747fSPaolo Bonzini 
3149ab747fSPaolo Bonzini #include "hw/stream.h"
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini #define DPHY(x)
3449ab747fSPaolo Bonzini 
35f0e7a81cSPeter Crosthwaite #define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet"
3655b3e0c2SPeter Crosthwaite #define TYPE_XILINX_AXI_ENET_DATA_STREAM "xilinx-axienet-data-stream"
37f0e7a81cSPeter Crosthwaite 
38f0e7a81cSPeter Crosthwaite #define XILINX_AXI_ENET(obj) \
39f0e7a81cSPeter Crosthwaite      OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET)
40f0e7a81cSPeter Crosthwaite 
4155b3e0c2SPeter Crosthwaite #define XILINX_AXI_ENET_DATA_STREAM(obj) \
4255b3e0c2SPeter Crosthwaite      OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\
4355b3e0c2SPeter Crosthwaite      TYPE_XILINX_AXI_ENET_DATA_STREAM)
4455b3e0c2SPeter Crosthwaite 
4549ab747fSPaolo Bonzini /* Advertisement control register. */
4649ab747fSPaolo Bonzini #define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
4749ab747fSPaolo Bonzini #define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
4849ab747fSPaolo Bonzini #define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
4949ab747fSPaolo Bonzini #define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
5049ab747fSPaolo Bonzini 
5149ab747fSPaolo Bonzini struct PHY {
5249ab747fSPaolo Bonzini     uint32_t regs[32];
5349ab747fSPaolo Bonzini 
5449ab747fSPaolo Bonzini     int link;
5549ab747fSPaolo Bonzini 
5649ab747fSPaolo Bonzini     unsigned int (*read)(struct PHY *phy, unsigned int req);
5749ab747fSPaolo Bonzini     void (*write)(struct PHY *phy, unsigned int req,
5849ab747fSPaolo Bonzini                   unsigned int data);
5949ab747fSPaolo Bonzini };
6049ab747fSPaolo Bonzini 
6149ab747fSPaolo Bonzini static unsigned int tdk_read(struct PHY *phy, unsigned int req)
6249ab747fSPaolo Bonzini {
6349ab747fSPaolo Bonzini     int regnum;
6449ab747fSPaolo Bonzini     unsigned r = 0;
6549ab747fSPaolo Bonzini 
6649ab747fSPaolo Bonzini     regnum = req & 0x1f;
6749ab747fSPaolo Bonzini 
6849ab747fSPaolo Bonzini     switch (regnum) {
6949ab747fSPaolo Bonzini         case 1:
7049ab747fSPaolo Bonzini             if (!phy->link) {
7149ab747fSPaolo Bonzini                 break;
7249ab747fSPaolo Bonzini             }
7349ab747fSPaolo Bonzini             /* MR1.  */
7449ab747fSPaolo Bonzini             /* Speeds and modes.  */
7549ab747fSPaolo Bonzini             r |= (1 << 13) | (1 << 14);
7649ab747fSPaolo Bonzini             r |= (1 << 11) | (1 << 12);
7749ab747fSPaolo Bonzini             r |= (1 << 5); /* Autoneg complete.  */
7849ab747fSPaolo Bonzini             r |= (1 << 3); /* Autoneg able.  */
7949ab747fSPaolo Bonzini             r |= (1 << 2); /* link.  */
8049ab747fSPaolo Bonzini             r |= (1 << 1); /* link.  */
8149ab747fSPaolo Bonzini             break;
8249ab747fSPaolo Bonzini         case 5:
8349ab747fSPaolo Bonzini             /* Link partner ability.
8449ab747fSPaolo Bonzini                We are kind; always agree with whatever best mode
8549ab747fSPaolo Bonzini                the guest advertises.  */
8649ab747fSPaolo Bonzini             r = 1 << 14; /* Success.  */
8749ab747fSPaolo Bonzini             /* Copy advertised modes.  */
8849ab747fSPaolo Bonzini             r |= phy->regs[4] & (15 << 5);
8949ab747fSPaolo Bonzini             /* Autoneg support.  */
9049ab747fSPaolo Bonzini             r |= 1;
9149ab747fSPaolo Bonzini             break;
9249ab747fSPaolo Bonzini         case 17:
9349ab747fSPaolo Bonzini             /* Marvel PHY on many xilinx boards.  */
9449ab747fSPaolo Bonzini             r = 0x8000; /* 1000Mb  */
9549ab747fSPaolo Bonzini             break;
9649ab747fSPaolo Bonzini         case 18:
9749ab747fSPaolo Bonzini             {
9849ab747fSPaolo Bonzini                 /* Diagnostics reg.  */
9949ab747fSPaolo Bonzini                 int duplex = 0;
10049ab747fSPaolo Bonzini                 int speed_100 = 0;
10149ab747fSPaolo Bonzini 
10249ab747fSPaolo Bonzini                 if (!phy->link) {
10349ab747fSPaolo Bonzini                     break;
10449ab747fSPaolo Bonzini                 }
10549ab747fSPaolo Bonzini 
10649ab747fSPaolo Bonzini                 /* Are we advertising 100 half or 100 duplex ? */
10749ab747fSPaolo Bonzini                 speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
10849ab747fSPaolo Bonzini                 speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
10949ab747fSPaolo Bonzini 
11049ab747fSPaolo Bonzini                 /* Are we advertising 10 duplex or 100 duplex ? */
11149ab747fSPaolo Bonzini                 duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
11249ab747fSPaolo Bonzini                 duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
11349ab747fSPaolo Bonzini                 r = (speed_100 << 10) | (duplex << 11);
11449ab747fSPaolo Bonzini             }
11549ab747fSPaolo Bonzini             break;
11649ab747fSPaolo Bonzini 
11749ab747fSPaolo Bonzini         default:
11849ab747fSPaolo Bonzini             r = phy->regs[regnum];
11949ab747fSPaolo Bonzini             break;
12049ab747fSPaolo Bonzini     }
12149ab747fSPaolo Bonzini     DPHY(qemu_log("\n%s %x = reg[%d]\n", __func__, r, regnum));
12249ab747fSPaolo Bonzini     return r;
12349ab747fSPaolo Bonzini }
12449ab747fSPaolo Bonzini 
12549ab747fSPaolo Bonzini static void
12649ab747fSPaolo Bonzini tdk_write(struct PHY *phy, unsigned int req, unsigned int data)
12749ab747fSPaolo Bonzini {
12849ab747fSPaolo Bonzini     int regnum;
12949ab747fSPaolo Bonzini 
13049ab747fSPaolo Bonzini     regnum = req & 0x1f;
13149ab747fSPaolo Bonzini     DPHY(qemu_log("%s reg[%d] = %x\n", __func__, regnum, data));
13249ab747fSPaolo Bonzini     switch (regnum) {
13349ab747fSPaolo Bonzini         default:
13449ab747fSPaolo Bonzini             phy->regs[regnum] = data;
13549ab747fSPaolo Bonzini             break;
13649ab747fSPaolo Bonzini     }
13749ab747fSPaolo Bonzini }
13849ab747fSPaolo Bonzini 
13949ab747fSPaolo Bonzini static void
14049ab747fSPaolo Bonzini tdk_init(struct PHY *phy)
14149ab747fSPaolo Bonzini {
14249ab747fSPaolo Bonzini     phy->regs[0] = 0x3100;
14349ab747fSPaolo Bonzini     /* PHY Id.  */
14449ab747fSPaolo Bonzini     phy->regs[2] = 0x0300;
14549ab747fSPaolo Bonzini     phy->regs[3] = 0xe400;
14649ab747fSPaolo Bonzini     /* Autonegotiation advertisement reg.  */
14749ab747fSPaolo Bonzini     phy->regs[4] = 0x01E1;
14849ab747fSPaolo Bonzini     phy->link = 1;
14949ab747fSPaolo Bonzini 
15049ab747fSPaolo Bonzini     phy->read = tdk_read;
15149ab747fSPaolo Bonzini     phy->write = tdk_write;
15249ab747fSPaolo Bonzini }
15349ab747fSPaolo Bonzini 
15449ab747fSPaolo Bonzini struct MDIOBus {
15549ab747fSPaolo Bonzini     /* bus.  */
15649ab747fSPaolo Bonzini     int mdc;
15749ab747fSPaolo Bonzini     int mdio;
15849ab747fSPaolo Bonzini 
15949ab747fSPaolo Bonzini     /* decoder.  */
16049ab747fSPaolo Bonzini     enum {
16149ab747fSPaolo Bonzini         PREAMBLE,
16249ab747fSPaolo Bonzini         SOF,
16349ab747fSPaolo Bonzini         OPC,
16449ab747fSPaolo Bonzini         ADDR,
16549ab747fSPaolo Bonzini         REQ,
16649ab747fSPaolo Bonzini         TURNAROUND,
16749ab747fSPaolo Bonzini         DATA
16849ab747fSPaolo Bonzini     } state;
16949ab747fSPaolo Bonzini     unsigned int drive;
17049ab747fSPaolo Bonzini 
17149ab747fSPaolo Bonzini     unsigned int cnt;
17249ab747fSPaolo Bonzini     unsigned int addr;
17349ab747fSPaolo Bonzini     unsigned int opc;
17449ab747fSPaolo Bonzini     unsigned int req;
17549ab747fSPaolo Bonzini     unsigned int data;
17649ab747fSPaolo Bonzini 
17749ab747fSPaolo Bonzini     struct PHY *devs[32];
17849ab747fSPaolo Bonzini };
17949ab747fSPaolo Bonzini 
18049ab747fSPaolo Bonzini static void
18149ab747fSPaolo Bonzini mdio_attach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
18249ab747fSPaolo Bonzini {
18349ab747fSPaolo Bonzini     bus->devs[addr & 0x1f] = phy;
18449ab747fSPaolo Bonzini }
18549ab747fSPaolo Bonzini 
18649ab747fSPaolo Bonzini #ifdef USE_THIS_DEAD_CODE
18749ab747fSPaolo Bonzini static void
18849ab747fSPaolo Bonzini mdio_detach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
18949ab747fSPaolo Bonzini {
19049ab747fSPaolo Bonzini     bus->devs[addr & 0x1f] = NULL;
19149ab747fSPaolo Bonzini }
19249ab747fSPaolo Bonzini #endif
19349ab747fSPaolo Bonzini 
19449ab747fSPaolo Bonzini static uint16_t mdio_read_req(struct MDIOBus *bus, unsigned int addr,
19549ab747fSPaolo Bonzini                   unsigned int reg)
19649ab747fSPaolo Bonzini {
19749ab747fSPaolo Bonzini     struct PHY *phy;
19849ab747fSPaolo Bonzini     uint16_t data;
19949ab747fSPaolo Bonzini 
20049ab747fSPaolo Bonzini     phy = bus->devs[addr];
20149ab747fSPaolo Bonzini     if (phy && phy->read) {
20249ab747fSPaolo Bonzini         data = phy->read(phy, reg);
20349ab747fSPaolo Bonzini     } else {
20449ab747fSPaolo Bonzini         data = 0xffff;
20549ab747fSPaolo Bonzini     }
20649ab747fSPaolo Bonzini     DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
20749ab747fSPaolo Bonzini     return data;
20849ab747fSPaolo Bonzini }
20949ab747fSPaolo Bonzini 
21049ab747fSPaolo Bonzini static void mdio_write_req(struct MDIOBus *bus, unsigned int addr,
21149ab747fSPaolo Bonzini                unsigned int reg, uint16_t data)
21249ab747fSPaolo Bonzini {
21349ab747fSPaolo Bonzini     struct PHY *phy;
21449ab747fSPaolo Bonzini 
21549ab747fSPaolo Bonzini     DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
21649ab747fSPaolo Bonzini     phy = bus->devs[addr];
21749ab747fSPaolo Bonzini     if (phy && phy->write) {
21849ab747fSPaolo Bonzini         phy->write(phy, reg, data);
21949ab747fSPaolo Bonzini     }
22049ab747fSPaolo Bonzini }
22149ab747fSPaolo Bonzini 
22249ab747fSPaolo Bonzini #define DENET(x)
22349ab747fSPaolo Bonzini 
22449ab747fSPaolo Bonzini #define R_RAF      (0x000 / 4)
22549ab747fSPaolo Bonzini enum {
22649ab747fSPaolo Bonzini     RAF_MCAST_REJ = (1 << 1),
22749ab747fSPaolo Bonzini     RAF_BCAST_REJ = (1 << 2),
22849ab747fSPaolo Bonzini     RAF_EMCF_EN = (1 << 12),
22949ab747fSPaolo Bonzini     RAF_NEWFUNC_EN = (1 << 11)
23049ab747fSPaolo Bonzini };
23149ab747fSPaolo Bonzini 
23249ab747fSPaolo Bonzini #define R_IS       (0x00C / 4)
23349ab747fSPaolo Bonzini enum {
23449ab747fSPaolo Bonzini     IS_HARD_ACCESS_COMPLETE = 1,
23549ab747fSPaolo Bonzini     IS_AUTONEG = (1 << 1),
23649ab747fSPaolo Bonzini     IS_RX_COMPLETE = (1 << 2),
23749ab747fSPaolo Bonzini     IS_RX_REJECT = (1 << 3),
23849ab747fSPaolo Bonzini     IS_TX_COMPLETE = (1 << 5),
23949ab747fSPaolo Bonzini     IS_RX_DCM_LOCK = (1 << 6),
24049ab747fSPaolo Bonzini     IS_MGM_RDY = (1 << 7),
24149ab747fSPaolo Bonzini     IS_PHY_RST_DONE = (1 << 8),
24249ab747fSPaolo Bonzini };
24349ab747fSPaolo Bonzini 
24449ab747fSPaolo Bonzini #define R_IP       (0x010 / 4)
24549ab747fSPaolo Bonzini #define R_IE       (0x014 / 4)
24649ab747fSPaolo Bonzini #define R_UAWL     (0x020 / 4)
24749ab747fSPaolo Bonzini #define R_UAWU     (0x024 / 4)
24849ab747fSPaolo Bonzini #define R_PPST     (0x030 / 4)
24949ab747fSPaolo Bonzini enum {
25049ab747fSPaolo Bonzini     PPST_LINKSTATUS = (1 << 0),
25149ab747fSPaolo Bonzini     PPST_PHY_LINKSTATUS = (1 << 7),
25249ab747fSPaolo Bonzini };
25349ab747fSPaolo Bonzini 
25449ab747fSPaolo Bonzini #define R_STATS_RX_BYTESL (0x200 / 4)
25549ab747fSPaolo Bonzini #define R_STATS_RX_BYTESH (0x204 / 4)
25649ab747fSPaolo Bonzini #define R_STATS_TX_BYTESL (0x208 / 4)
25749ab747fSPaolo Bonzini #define R_STATS_TX_BYTESH (0x20C / 4)
25849ab747fSPaolo Bonzini #define R_STATS_RXL       (0x290 / 4)
25949ab747fSPaolo Bonzini #define R_STATS_RXH       (0x294 / 4)
26049ab747fSPaolo Bonzini #define R_STATS_RX_BCASTL (0x2a0 / 4)
26149ab747fSPaolo Bonzini #define R_STATS_RX_BCASTH (0x2a4 / 4)
26249ab747fSPaolo Bonzini #define R_STATS_RX_MCASTL (0x2a8 / 4)
26349ab747fSPaolo Bonzini #define R_STATS_RX_MCASTH (0x2ac / 4)
26449ab747fSPaolo Bonzini 
26549ab747fSPaolo Bonzini #define R_RCW0     (0x400 / 4)
26649ab747fSPaolo Bonzini #define R_RCW1     (0x404 / 4)
26749ab747fSPaolo Bonzini enum {
26849ab747fSPaolo Bonzini     RCW1_VLAN = (1 << 27),
26949ab747fSPaolo Bonzini     RCW1_RX   = (1 << 28),
27049ab747fSPaolo Bonzini     RCW1_FCS  = (1 << 29),
27149ab747fSPaolo Bonzini     RCW1_JUM  = (1 << 30),
27249ab747fSPaolo Bonzini     RCW1_RST  = (1 << 31),
27349ab747fSPaolo Bonzini };
27449ab747fSPaolo Bonzini 
27549ab747fSPaolo Bonzini #define R_TC       (0x408 / 4)
27649ab747fSPaolo Bonzini enum {
27749ab747fSPaolo Bonzini     TC_VLAN = (1 << 27),
27849ab747fSPaolo Bonzini     TC_TX   = (1 << 28),
27949ab747fSPaolo Bonzini     TC_FCS  = (1 << 29),
28049ab747fSPaolo Bonzini     TC_JUM  = (1 << 30),
28149ab747fSPaolo Bonzini     TC_RST  = (1 << 31),
28249ab747fSPaolo Bonzini };
28349ab747fSPaolo Bonzini 
28449ab747fSPaolo Bonzini #define R_EMMC     (0x410 / 4)
28549ab747fSPaolo Bonzini enum {
28649ab747fSPaolo Bonzini     EMMC_LINKSPEED_10MB = (0 << 30),
28749ab747fSPaolo Bonzini     EMMC_LINKSPEED_100MB = (1 << 30),
28849ab747fSPaolo Bonzini     EMMC_LINKSPEED_1000MB = (2 << 30),
28949ab747fSPaolo Bonzini };
29049ab747fSPaolo Bonzini 
29149ab747fSPaolo Bonzini #define R_PHYC     (0x414 / 4)
29249ab747fSPaolo Bonzini 
29349ab747fSPaolo Bonzini #define R_MC       (0x500 / 4)
29449ab747fSPaolo Bonzini #define MC_EN      (1 << 6)
29549ab747fSPaolo Bonzini 
29649ab747fSPaolo Bonzini #define R_MCR      (0x504 / 4)
29749ab747fSPaolo Bonzini #define R_MWD      (0x508 / 4)
29849ab747fSPaolo Bonzini #define R_MRD      (0x50c / 4)
29949ab747fSPaolo Bonzini #define R_MIS      (0x600 / 4)
30049ab747fSPaolo Bonzini #define R_MIP      (0x620 / 4)
30149ab747fSPaolo Bonzini #define R_MIE      (0x640 / 4)
30249ab747fSPaolo Bonzini #define R_MIC      (0x640 / 4)
30349ab747fSPaolo Bonzini 
30449ab747fSPaolo Bonzini #define R_UAW0     (0x700 / 4)
30549ab747fSPaolo Bonzini #define R_UAW1     (0x704 / 4)
30649ab747fSPaolo Bonzini #define R_FMI      (0x708 / 4)
30749ab747fSPaolo Bonzini #define R_AF0      (0x710 / 4)
30849ab747fSPaolo Bonzini #define R_AF1      (0x714 / 4)
30949ab747fSPaolo Bonzini #define R_MAX      (0x34 / 4)
31049ab747fSPaolo Bonzini 
31149ab747fSPaolo Bonzini /* Indirect registers.  */
31249ab747fSPaolo Bonzini struct TEMAC  {
31349ab747fSPaolo Bonzini     struct MDIOBus mdio_bus;
31449ab747fSPaolo Bonzini     struct PHY phy;
31549ab747fSPaolo Bonzini 
31649ab747fSPaolo Bonzini     void *parent;
31749ab747fSPaolo Bonzini };
31849ab747fSPaolo Bonzini 
31955b3e0c2SPeter Crosthwaite typedef struct XilinxAXIEnetStreamSlave XilinxAXIEnetStreamSlave;
320545129e5SPeter Crosthwaite typedef struct XilinxAXIEnet XilinxAXIEnet;
321545129e5SPeter Crosthwaite 
32255b3e0c2SPeter Crosthwaite struct XilinxAXIEnetStreamSlave {
32355b3e0c2SPeter Crosthwaite     Object parent;
32455b3e0c2SPeter Crosthwaite 
32555b3e0c2SPeter Crosthwaite     struct XilinxAXIEnet *enet;
32655b3e0c2SPeter Crosthwaite } ;
32755b3e0c2SPeter Crosthwaite 
32849ab747fSPaolo Bonzini struct XilinxAXIEnet {
32949ab747fSPaolo Bonzini     SysBusDevice busdev;
33049ab747fSPaolo Bonzini     MemoryRegion iomem;
33149ab747fSPaolo Bonzini     qemu_irq irq;
33249ab747fSPaolo Bonzini     StreamSlave *tx_dev;
33355b3e0c2SPeter Crosthwaite     XilinxAXIEnetStreamSlave rx_data_dev;
33449ab747fSPaolo Bonzini     NICState *nic;
33549ab747fSPaolo Bonzini     NICConf conf;
33649ab747fSPaolo Bonzini 
33749ab747fSPaolo Bonzini 
33849ab747fSPaolo Bonzini     uint32_t c_rxmem;
33949ab747fSPaolo Bonzini     uint32_t c_txmem;
34049ab747fSPaolo Bonzini     uint32_t c_phyaddr;
34149ab747fSPaolo Bonzini 
34249ab747fSPaolo Bonzini     struct TEMAC TEMAC;
34349ab747fSPaolo Bonzini 
34449ab747fSPaolo Bonzini     /* MII regs.  */
34549ab747fSPaolo Bonzini     union {
34649ab747fSPaolo Bonzini         uint32_t regs[4];
34749ab747fSPaolo Bonzini         struct {
34849ab747fSPaolo Bonzini             uint32_t mc;
34949ab747fSPaolo Bonzini             uint32_t mcr;
35049ab747fSPaolo Bonzini             uint32_t mwd;
35149ab747fSPaolo Bonzini             uint32_t mrd;
35249ab747fSPaolo Bonzini         };
35349ab747fSPaolo Bonzini     } mii;
35449ab747fSPaolo Bonzini 
35549ab747fSPaolo Bonzini     struct {
35649ab747fSPaolo Bonzini         uint64_t rx_bytes;
35749ab747fSPaolo Bonzini         uint64_t tx_bytes;
35849ab747fSPaolo Bonzini 
35949ab747fSPaolo Bonzini         uint64_t rx;
36049ab747fSPaolo Bonzini         uint64_t rx_bcast;
36149ab747fSPaolo Bonzini         uint64_t rx_mcast;
36249ab747fSPaolo Bonzini     } stats;
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini     /* Receive configuration words.  */
36549ab747fSPaolo Bonzini     uint32_t rcw[2];
36649ab747fSPaolo Bonzini     /* Transmit config.  */
36749ab747fSPaolo Bonzini     uint32_t tc;
36849ab747fSPaolo Bonzini     uint32_t emmc;
36949ab747fSPaolo Bonzini     uint32_t phyc;
37049ab747fSPaolo Bonzini 
37149ab747fSPaolo Bonzini     /* Unicast Address Word.  */
37249ab747fSPaolo Bonzini     uint32_t uaw[2];
37349ab747fSPaolo Bonzini     /* Unicast address filter used with extended mcast.  */
37449ab747fSPaolo Bonzini     uint32_t ext_uaw[2];
37549ab747fSPaolo Bonzini     uint32_t fmi;
37649ab747fSPaolo Bonzini 
37749ab747fSPaolo Bonzini     uint32_t regs[R_MAX];
37849ab747fSPaolo Bonzini 
37949ab747fSPaolo Bonzini     /* Multicast filter addrs.  */
38049ab747fSPaolo Bonzini     uint32_t maddr[4][2];
38149ab747fSPaolo Bonzini     /* 32K x 1 lookup filter.  */
38249ab747fSPaolo Bonzini     uint32_t ext_mtable[1024];
38349ab747fSPaolo Bonzini 
38449ab747fSPaolo Bonzini 
38549ab747fSPaolo Bonzini     uint8_t *rxmem;
38649ab747fSPaolo Bonzini };
38749ab747fSPaolo Bonzini 
388545129e5SPeter Crosthwaite static void axienet_rx_reset(XilinxAXIEnet *s)
38949ab747fSPaolo Bonzini {
39049ab747fSPaolo Bonzini     s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN;
39149ab747fSPaolo Bonzini }
39249ab747fSPaolo Bonzini 
393545129e5SPeter Crosthwaite static void axienet_tx_reset(XilinxAXIEnet *s)
39449ab747fSPaolo Bonzini {
39549ab747fSPaolo Bonzini     s->tc = TC_JUM | TC_TX | TC_VLAN;
39649ab747fSPaolo Bonzini }
39749ab747fSPaolo Bonzini 
398545129e5SPeter Crosthwaite static inline int axienet_rx_resetting(XilinxAXIEnet *s)
39949ab747fSPaolo Bonzini {
40049ab747fSPaolo Bonzini     return s->rcw[1] & RCW1_RST;
40149ab747fSPaolo Bonzini }
40249ab747fSPaolo Bonzini 
403545129e5SPeter Crosthwaite static inline int axienet_rx_enabled(XilinxAXIEnet *s)
40449ab747fSPaolo Bonzini {
40549ab747fSPaolo Bonzini     return s->rcw[1] & RCW1_RX;
40649ab747fSPaolo Bonzini }
40749ab747fSPaolo Bonzini 
408545129e5SPeter Crosthwaite static inline int axienet_extmcf_enabled(XilinxAXIEnet *s)
40949ab747fSPaolo Bonzini {
41049ab747fSPaolo Bonzini     return !!(s->regs[R_RAF] & RAF_EMCF_EN);
41149ab747fSPaolo Bonzini }
41249ab747fSPaolo Bonzini 
413545129e5SPeter Crosthwaite static inline int axienet_newfunc_enabled(XilinxAXIEnet *s)
41449ab747fSPaolo Bonzini {
41549ab747fSPaolo Bonzini     return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN);
41649ab747fSPaolo Bonzini }
41749ab747fSPaolo Bonzini 
4189ee0ceb7SPeter Crosthwaite static void xilinx_axienet_reset(DeviceState *d)
41949ab747fSPaolo Bonzini {
4209ee0ceb7SPeter Crosthwaite     XilinxAXIEnet *s = XILINX_AXI_ENET(d);
4219ee0ceb7SPeter Crosthwaite 
42249ab747fSPaolo Bonzini     axienet_rx_reset(s);
42349ab747fSPaolo Bonzini     axienet_tx_reset(s);
42449ab747fSPaolo Bonzini 
42549ab747fSPaolo Bonzini     s->regs[R_PPST] = PPST_LINKSTATUS | PPST_PHY_LINKSTATUS;
42649ab747fSPaolo Bonzini     s->regs[R_IS] = IS_AUTONEG | IS_RX_DCM_LOCK | IS_MGM_RDY | IS_PHY_RST_DONE;
42749ab747fSPaolo Bonzini 
42849ab747fSPaolo Bonzini     s->emmc = EMMC_LINKSPEED_100MB;
42949ab747fSPaolo Bonzini }
43049ab747fSPaolo Bonzini 
431545129e5SPeter Crosthwaite static void enet_update_irq(XilinxAXIEnet *s)
43249ab747fSPaolo Bonzini {
43349ab747fSPaolo Bonzini     s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE];
43449ab747fSPaolo Bonzini     qemu_set_irq(s->irq, !!s->regs[R_IP]);
43549ab747fSPaolo Bonzini }
43649ab747fSPaolo Bonzini 
43749ab747fSPaolo Bonzini static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
43849ab747fSPaolo Bonzini {
439545129e5SPeter Crosthwaite     XilinxAXIEnet *s = opaque;
44049ab747fSPaolo Bonzini     uint32_t r = 0;
44149ab747fSPaolo Bonzini     addr >>= 2;
44249ab747fSPaolo Bonzini 
44349ab747fSPaolo Bonzini     switch (addr) {
44449ab747fSPaolo Bonzini         case R_RCW0:
44549ab747fSPaolo Bonzini         case R_RCW1:
44649ab747fSPaolo Bonzini             r = s->rcw[addr & 1];
44749ab747fSPaolo Bonzini             break;
44849ab747fSPaolo Bonzini 
44949ab747fSPaolo Bonzini         case R_TC:
45049ab747fSPaolo Bonzini             r = s->tc;
45149ab747fSPaolo Bonzini             break;
45249ab747fSPaolo Bonzini 
45349ab747fSPaolo Bonzini         case R_EMMC:
45449ab747fSPaolo Bonzini             r = s->emmc;
45549ab747fSPaolo Bonzini             break;
45649ab747fSPaolo Bonzini 
45749ab747fSPaolo Bonzini         case R_PHYC:
45849ab747fSPaolo Bonzini             r = s->phyc;
45949ab747fSPaolo Bonzini             break;
46049ab747fSPaolo Bonzini 
46149ab747fSPaolo Bonzini         case R_MCR:
46249ab747fSPaolo Bonzini             r = s->mii.regs[addr & 3] | (1 << 7); /* Always ready.  */
46349ab747fSPaolo Bonzini             break;
46449ab747fSPaolo Bonzini 
46549ab747fSPaolo Bonzini         case R_STATS_RX_BYTESL:
46649ab747fSPaolo Bonzini         case R_STATS_RX_BYTESH:
46749ab747fSPaolo Bonzini             r = s->stats.rx_bytes >> (32 * (addr & 1));
46849ab747fSPaolo Bonzini             break;
46949ab747fSPaolo Bonzini 
47049ab747fSPaolo Bonzini         case R_STATS_TX_BYTESL:
47149ab747fSPaolo Bonzini         case R_STATS_TX_BYTESH:
47249ab747fSPaolo Bonzini             r = s->stats.tx_bytes >> (32 * (addr & 1));
47349ab747fSPaolo Bonzini             break;
47449ab747fSPaolo Bonzini 
47549ab747fSPaolo Bonzini         case R_STATS_RXL:
47649ab747fSPaolo Bonzini         case R_STATS_RXH:
47749ab747fSPaolo Bonzini             r = s->stats.rx >> (32 * (addr & 1));
47849ab747fSPaolo Bonzini             break;
47949ab747fSPaolo Bonzini         case R_STATS_RX_BCASTL:
48049ab747fSPaolo Bonzini         case R_STATS_RX_BCASTH:
48149ab747fSPaolo Bonzini             r = s->stats.rx_bcast >> (32 * (addr & 1));
48249ab747fSPaolo Bonzini             break;
48349ab747fSPaolo Bonzini         case R_STATS_RX_MCASTL:
48449ab747fSPaolo Bonzini         case R_STATS_RX_MCASTH:
48549ab747fSPaolo Bonzini             r = s->stats.rx_mcast >> (32 * (addr & 1));
48649ab747fSPaolo Bonzini             break;
48749ab747fSPaolo Bonzini 
48849ab747fSPaolo Bonzini         case R_MC:
48949ab747fSPaolo Bonzini         case R_MWD:
49049ab747fSPaolo Bonzini         case R_MRD:
49149ab747fSPaolo Bonzini             r = s->mii.regs[addr & 3];
49249ab747fSPaolo Bonzini             break;
49349ab747fSPaolo Bonzini 
49449ab747fSPaolo Bonzini         case R_UAW0:
49549ab747fSPaolo Bonzini         case R_UAW1:
49649ab747fSPaolo Bonzini             r = s->uaw[addr & 1];
49749ab747fSPaolo Bonzini             break;
49849ab747fSPaolo Bonzini 
49949ab747fSPaolo Bonzini         case R_UAWU:
50049ab747fSPaolo Bonzini         case R_UAWL:
50149ab747fSPaolo Bonzini             r = s->ext_uaw[addr & 1];
50249ab747fSPaolo Bonzini             break;
50349ab747fSPaolo Bonzini 
50449ab747fSPaolo Bonzini         case R_FMI:
50549ab747fSPaolo Bonzini             r = s->fmi;
50649ab747fSPaolo Bonzini             break;
50749ab747fSPaolo Bonzini 
50849ab747fSPaolo Bonzini         case R_AF0:
50949ab747fSPaolo Bonzini         case R_AF1:
51049ab747fSPaolo Bonzini             r = s->maddr[s->fmi & 3][addr & 1];
51149ab747fSPaolo Bonzini             break;
51249ab747fSPaolo Bonzini 
51349ab747fSPaolo Bonzini         case 0x8000 ... 0x83ff:
51449ab747fSPaolo Bonzini             r = s->ext_mtable[addr - 0x8000];
51549ab747fSPaolo Bonzini             break;
51649ab747fSPaolo Bonzini 
51749ab747fSPaolo Bonzini         default:
51849ab747fSPaolo Bonzini             if (addr < ARRAY_SIZE(s->regs)) {
51949ab747fSPaolo Bonzini                 r = s->regs[addr];
52049ab747fSPaolo Bonzini             }
52149ab747fSPaolo Bonzini             DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
52249ab747fSPaolo Bonzini                             __func__, addr * 4, r));
52349ab747fSPaolo Bonzini             break;
52449ab747fSPaolo Bonzini     }
52549ab747fSPaolo Bonzini     return r;
52649ab747fSPaolo Bonzini }
52749ab747fSPaolo Bonzini 
52849ab747fSPaolo Bonzini static void enet_write(void *opaque, hwaddr addr,
52949ab747fSPaolo Bonzini                        uint64_t value, unsigned size)
53049ab747fSPaolo Bonzini {
531545129e5SPeter Crosthwaite     XilinxAXIEnet *s = opaque;
53249ab747fSPaolo Bonzini     struct TEMAC *t = &s->TEMAC;
53349ab747fSPaolo Bonzini 
53449ab747fSPaolo Bonzini     addr >>= 2;
53549ab747fSPaolo Bonzini     switch (addr) {
53649ab747fSPaolo Bonzini         case R_RCW0:
53749ab747fSPaolo Bonzini         case R_RCW1:
53849ab747fSPaolo Bonzini             s->rcw[addr & 1] = value;
53949ab747fSPaolo Bonzini             if ((addr & 1) && value & RCW1_RST) {
54049ab747fSPaolo Bonzini                 axienet_rx_reset(s);
54149ab747fSPaolo Bonzini             } else {
54249ab747fSPaolo Bonzini                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
54349ab747fSPaolo Bonzini             }
54449ab747fSPaolo Bonzini             break;
54549ab747fSPaolo Bonzini 
54649ab747fSPaolo Bonzini         case R_TC:
54749ab747fSPaolo Bonzini             s->tc = value;
54849ab747fSPaolo Bonzini             if (value & TC_RST) {
54949ab747fSPaolo Bonzini                 axienet_tx_reset(s);
55049ab747fSPaolo Bonzini             }
55149ab747fSPaolo Bonzini             break;
55249ab747fSPaolo Bonzini 
55349ab747fSPaolo Bonzini         case R_EMMC:
55449ab747fSPaolo Bonzini             s->emmc = value;
55549ab747fSPaolo Bonzini             break;
55649ab747fSPaolo Bonzini 
55749ab747fSPaolo Bonzini         case R_PHYC:
55849ab747fSPaolo Bonzini             s->phyc = value;
55949ab747fSPaolo Bonzini             break;
56049ab747fSPaolo Bonzini 
56149ab747fSPaolo Bonzini         case R_MC:
56249ab747fSPaolo Bonzini              value &= ((1 < 7) - 1);
56349ab747fSPaolo Bonzini 
56449ab747fSPaolo Bonzini              /* Enable the MII.  */
56549ab747fSPaolo Bonzini              if (value & MC_EN) {
56649ab747fSPaolo Bonzini                  unsigned int miiclkdiv = value & ((1 << 6) - 1);
56749ab747fSPaolo Bonzini                  if (!miiclkdiv) {
56849ab747fSPaolo Bonzini                      qemu_log("AXIENET: MDIO enabled but MDIOCLK is zero!\n");
56949ab747fSPaolo Bonzini                  }
57049ab747fSPaolo Bonzini              }
57149ab747fSPaolo Bonzini              s->mii.mc = value;
57249ab747fSPaolo Bonzini              break;
57349ab747fSPaolo Bonzini 
57449ab747fSPaolo Bonzini         case R_MCR: {
57549ab747fSPaolo Bonzini              unsigned int phyaddr = (value >> 24) & 0x1f;
57649ab747fSPaolo Bonzini              unsigned int regaddr = (value >> 16) & 0x1f;
57749ab747fSPaolo Bonzini              unsigned int op = (value >> 14) & 3;
57849ab747fSPaolo Bonzini              unsigned int initiate = (value >> 11) & 1;
57949ab747fSPaolo Bonzini 
58049ab747fSPaolo Bonzini              if (initiate) {
58149ab747fSPaolo Bonzini                  if (op == 1) {
58249ab747fSPaolo Bonzini                      mdio_write_req(&t->mdio_bus, phyaddr, regaddr, s->mii.mwd);
58349ab747fSPaolo Bonzini                  } else if (op == 2) {
58449ab747fSPaolo Bonzini                      s->mii.mrd = mdio_read_req(&t->mdio_bus, phyaddr, regaddr);
58549ab747fSPaolo Bonzini                  } else {
58649ab747fSPaolo Bonzini                      qemu_log("AXIENET: invalid MDIOBus OP=%d\n", op);
58749ab747fSPaolo Bonzini                  }
58849ab747fSPaolo Bonzini              }
58949ab747fSPaolo Bonzini              s->mii.mcr = value;
59049ab747fSPaolo Bonzini              break;
59149ab747fSPaolo Bonzini         }
59249ab747fSPaolo Bonzini 
59349ab747fSPaolo Bonzini         case R_MWD:
59449ab747fSPaolo Bonzini         case R_MRD:
59549ab747fSPaolo Bonzini              s->mii.regs[addr & 3] = value;
59649ab747fSPaolo Bonzini              break;
59749ab747fSPaolo Bonzini 
59849ab747fSPaolo Bonzini 
59949ab747fSPaolo Bonzini         case R_UAW0:
60049ab747fSPaolo Bonzini         case R_UAW1:
60149ab747fSPaolo Bonzini             s->uaw[addr & 1] = value;
60249ab747fSPaolo Bonzini             break;
60349ab747fSPaolo Bonzini 
60449ab747fSPaolo Bonzini         case R_UAWL:
60549ab747fSPaolo Bonzini         case R_UAWU:
60649ab747fSPaolo Bonzini             s->ext_uaw[addr & 1] = value;
60749ab747fSPaolo Bonzini             break;
60849ab747fSPaolo Bonzini 
60949ab747fSPaolo Bonzini         case R_FMI:
61049ab747fSPaolo Bonzini             s->fmi = value;
61149ab747fSPaolo Bonzini             break;
61249ab747fSPaolo Bonzini 
61349ab747fSPaolo Bonzini         case R_AF0:
61449ab747fSPaolo Bonzini         case R_AF1:
61549ab747fSPaolo Bonzini             s->maddr[s->fmi & 3][addr & 1] = value;
61649ab747fSPaolo Bonzini             break;
61749ab747fSPaolo Bonzini 
61849ab747fSPaolo Bonzini         case R_IS:
61949ab747fSPaolo Bonzini             s->regs[addr] &= ~value;
62049ab747fSPaolo Bonzini             break;
62149ab747fSPaolo Bonzini 
62249ab747fSPaolo Bonzini         case 0x8000 ... 0x83ff:
62349ab747fSPaolo Bonzini             s->ext_mtable[addr - 0x8000] = value;
62449ab747fSPaolo Bonzini             break;
62549ab747fSPaolo Bonzini 
62649ab747fSPaolo Bonzini         default:
62749ab747fSPaolo Bonzini             DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
62849ab747fSPaolo Bonzini                            __func__, addr * 4, (unsigned)value));
62949ab747fSPaolo Bonzini             if (addr < ARRAY_SIZE(s->regs)) {
63049ab747fSPaolo Bonzini                 s->regs[addr] = value;
63149ab747fSPaolo Bonzini             }
63249ab747fSPaolo Bonzini             break;
63349ab747fSPaolo Bonzini     }
63449ab747fSPaolo Bonzini     enet_update_irq(s);
63549ab747fSPaolo Bonzini }
63649ab747fSPaolo Bonzini 
63749ab747fSPaolo Bonzini static const MemoryRegionOps enet_ops = {
63849ab747fSPaolo Bonzini     .read = enet_read,
63949ab747fSPaolo Bonzini     .write = enet_write,
64049ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
64149ab747fSPaolo Bonzini };
64249ab747fSPaolo Bonzini 
64349ab747fSPaolo Bonzini static int eth_can_rx(NetClientState *nc)
64449ab747fSPaolo Bonzini {
645545129e5SPeter Crosthwaite     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
64649ab747fSPaolo Bonzini 
64749ab747fSPaolo Bonzini     /* RX enabled?  */
64849ab747fSPaolo Bonzini     return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
64949ab747fSPaolo Bonzini }
65049ab747fSPaolo Bonzini 
65149ab747fSPaolo Bonzini static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
65249ab747fSPaolo Bonzini {
65349ab747fSPaolo Bonzini     int match = 1;
65449ab747fSPaolo Bonzini 
65549ab747fSPaolo Bonzini     if (memcmp(buf, &f0, 4)) {
65649ab747fSPaolo Bonzini         match = 0;
65749ab747fSPaolo Bonzini     }
65849ab747fSPaolo Bonzini 
65949ab747fSPaolo Bonzini     if (buf[4] != (f1 & 0xff) || buf[5] != ((f1 >> 8) & 0xff)) {
66049ab747fSPaolo Bonzini         match = 0;
66149ab747fSPaolo Bonzini     }
66249ab747fSPaolo Bonzini 
66349ab747fSPaolo Bonzini     return match;
66449ab747fSPaolo Bonzini }
66549ab747fSPaolo Bonzini 
66649ab747fSPaolo Bonzini static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
66749ab747fSPaolo Bonzini {
668545129e5SPeter Crosthwaite     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
66949ab747fSPaolo Bonzini     static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
67049ab747fSPaolo Bonzini                                               0xff, 0xff, 0xff};
67149ab747fSPaolo Bonzini     static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
67249ab747fSPaolo Bonzini     uint32_t app[6] = {0};
67349ab747fSPaolo Bonzini     int promisc = s->fmi & (1 << 31);
67449ab747fSPaolo Bonzini     int unicast, broadcast, multicast, ip_multicast = 0;
67549ab747fSPaolo Bonzini     uint32_t csum32;
67649ab747fSPaolo Bonzini     uint16_t csum16;
67749ab747fSPaolo Bonzini     int i;
67849ab747fSPaolo Bonzini 
67949ab747fSPaolo Bonzini     DENET(qemu_log("%s: %zd bytes\n", __func__, size));
68049ab747fSPaolo Bonzini 
68149ab747fSPaolo Bonzini     unicast = ~buf[0] & 0x1;
68249ab747fSPaolo Bonzini     broadcast = memcmp(buf, sa_bcast, 6) == 0;
68349ab747fSPaolo Bonzini     multicast = !unicast && !broadcast;
68449ab747fSPaolo Bonzini     if (multicast && (memcmp(sa_ipmcast, buf, sizeof sa_ipmcast) == 0)) {
68549ab747fSPaolo Bonzini         ip_multicast = 1;
68649ab747fSPaolo Bonzini     }
68749ab747fSPaolo Bonzini 
68849ab747fSPaolo Bonzini     /* Jumbo or vlan sizes ?  */
68949ab747fSPaolo Bonzini     if (!(s->rcw[1] & RCW1_JUM)) {
69049ab747fSPaolo Bonzini         if (size > 1518 && size <= 1522 && !(s->rcw[1] & RCW1_VLAN)) {
69149ab747fSPaolo Bonzini             return size;
69249ab747fSPaolo Bonzini         }
69349ab747fSPaolo Bonzini     }
69449ab747fSPaolo Bonzini 
69549ab747fSPaolo Bonzini     /* Basic Address filters.  If you want to use the extended filters
69649ab747fSPaolo Bonzini        you'll generally have to place the ethernet mac into promiscuous mode
69749ab747fSPaolo Bonzini        to avoid the basic filtering from dropping most frames.  */
69849ab747fSPaolo Bonzini     if (!promisc) {
69949ab747fSPaolo Bonzini         if (unicast) {
70049ab747fSPaolo Bonzini             if (!enet_match_addr(buf, s->uaw[0], s->uaw[1])) {
70149ab747fSPaolo Bonzini                 return size;
70249ab747fSPaolo Bonzini             }
70349ab747fSPaolo Bonzini         } else {
70449ab747fSPaolo Bonzini             if (broadcast) {
70549ab747fSPaolo Bonzini                 /* Broadcast.  */
70649ab747fSPaolo Bonzini                 if (s->regs[R_RAF] & RAF_BCAST_REJ) {
70749ab747fSPaolo Bonzini                     return size;
70849ab747fSPaolo Bonzini                 }
70949ab747fSPaolo Bonzini             } else {
71049ab747fSPaolo Bonzini                 int drop = 1;
71149ab747fSPaolo Bonzini 
71249ab747fSPaolo Bonzini                 /* Multicast.  */
71349ab747fSPaolo Bonzini                 if (s->regs[R_RAF] & RAF_MCAST_REJ) {
71449ab747fSPaolo Bonzini                     return size;
71549ab747fSPaolo Bonzini                 }
71649ab747fSPaolo Bonzini 
71749ab747fSPaolo Bonzini                 for (i = 0; i < 4; i++) {
71849ab747fSPaolo Bonzini                     if (enet_match_addr(buf, s->maddr[i][0], s->maddr[i][1])) {
71949ab747fSPaolo Bonzini                         drop = 0;
72049ab747fSPaolo Bonzini                         break;
72149ab747fSPaolo Bonzini                     }
72249ab747fSPaolo Bonzini                 }
72349ab747fSPaolo Bonzini 
72449ab747fSPaolo Bonzini                 if (drop) {
72549ab747fSPaolo Bonzini                     return size;
72649ab747fSPaolo Bonzini                 }
72749ab747fSPaolo Bonzini             }
72849ab747fSPaolo Bonzini         }
72949ab747fSPaolo Bonzini     }
73049ab747fSPaolo Bonzini 
73149ab747fSPaolo Bonzini     /* Extended mcast filtering enabled?  */
73249ab747fSPaolo Bonzini     if (axienet_newfunc_enabled(s) && axienet_extmcf_enabled(s)) {
73349ab747fSPaolo Bonzini         if (unicast) {
73449ab747fSPaolo Bonzini             if (!enet_match_addr(buf, s->ext_uaw[0], s->ext_uaw[1])) {
73549ab747fSPaolo Bonzini                 return size;
73649ab747fSPaolo Bonzini             }
73749ab747fSPaolo Bonzini         } else {
73849ab747fSPaolo Bonzini             if (broadcast) {
73949ab747fSPaolo Bonzini                 /* Broadcast. ???  */
74049ab747fSPaolo Bonzini                 if (s->regs[R_RAF] & RAF_BCAST_REJ) {
74149ab747fSPaolo Bonzini                     return size;
74249ab747fSPaolo Bonzini                 }
74349ab747fSPaolo Bonzini             } else {
74449ab747fSPaolo Bonzini                 int idx, bit;
74549ab747fSPaolo Bonzini 
74649ab747fSPaolo Bonzini                 /* Multicast.  */
74749ab747fSPaolo Bonzini                 if (!memcmp(buf, sa_ipmcast, 3)) {
74849ab747fSPaolo Bonzini                     return size;
74949ab747fSPaolo Bonzini                 }
75049ab747fSPaolo Bonzini 
75149ab747fSPaolo Bonzini                 idx  = (buf[4] & 0x7f) << 8;
75249ab747fSPaolo Bonzini                 idx |= buf[5];
75349ab747fSPaolo Bonzini 
75449ab747fSPaolo Bonzini                 bit = 1 << (idx & 0x1f);
75549ab747fSPaolo Bonzini                 idx >>= 5;
75649ab747fSPaolo Bonzini 
75749ab747fSPaolo Bonzini                 if (!(s->ext_mtable[idx] & bit)) {
75849ab747fSPaolo Bonzini                     return size;
75949ab747fSPaolo Bonzini                 }
76049ab747fSPaolo Bonzini             }
76149ab747fSPaolo Bonzini         }
76249ab747fSPaolo Bonzini     }
76349ab747fSPaolo Bonzini 
76449ab747fSPaolo Bonzini     if (size < 12) {
76549ab747fSPaolo Bonzini         s->regs[R_IS] |= IS_RX_REJECT;
76649ab747fSPaolo Bonzini         enet_update_irq(s);
76749ab747fSPaolo Bonzini         return -1;
76849ab747fSPaolo Bonzini     }
76949ab747fSPaolo Bonzini 
77049ab747fSPaolo Bonzini     if (size > (s->c_rxmem - 4)) {
77149ab747fSPaolo Bonzini         size = s->c_rxmem - 4;
77249ab747fSPaolo Bonzini     }
77349ab747fSPaolo Bonzini 
77449ab747fSPaolo Bonzini     memcpy(s->rxmem, buf, size);
77549ab747fSPaolo Bonzini     memset(s->rxmem + size, 0, 4); /* Clear the FCS.  */
77649ab747fSPaolo Bonzini 
77749ab747fSPaolo Bonzini     if (s->rcw[1] & RCW1_FCS) {
77849ab747fSPaolo Bonzini         size += 4; /* fcs is inband.  */
77949ab747fSPaolo Bonzini     }
78049ab747fSPaolo Bonzini 
78149ab747fSPaolo Bonzini     app[0] = 5 << 28;
78249ab747fSPaolo Bonzini     csum32 = net_checksum_add(size - 14, (uint8_t *)s->rxmem + 14);
78349ab747fSPaolo Bonzini     /* Fold it once.  */
78449ab747fSPaolo Bonzini     csum32 = (csum32 & 0xffff) + (csum32 >> 16);
78549ab747fSPaolo Bonzini     /* And twice to get rid of possible carries.  */
78649ab747fSPaolo Bonzini     csum16 = (csum32 & 0xffff) + (csum32 >> 16);
78749ab747fSPaolo Bonzini     app[3] = csum16;
78849ab747fSPaolo Bonzini     app[4] = size & 0xffff;
78949ab747fSPaolo Bonzini 
79049ab747fSPaolo Bonzini     s->stats.rx_bytes += size;
79149ab747fSPaolo Bonzini     s->stats.rx++;
79249ab747fSPaolo Bonzini     if (multicast) {
79349ab747fSPaolo Bonzini         s->stats.rx_mcast++;
79449ab747fSPaolo Bonzini         app[2] |= 1 | (ip_multicast << 1);
79549ab747fSPaolo Bonzini     } else if (broadcast) {
79649ab747fSPaolo Bonzini         s->stats.rx_bcast++;
79749ab747fSPaolo Bonzini         app[2] |= 1 << 3;
79849ab747fSPaolo Bonzini     }
79949ab747fSPaolo Bonzini 
80049ab747fSPaolo Bonzini     /* Good frame.  */
80149ab747fSPaolo Bonzini     app[2] |= 1 << 6;
80249ab747fSPaolo Bonzini 
80349ab747fSPaolo Bonzini     stream_push(s->tx_dev, (void *)s->rxmem, size, app);
80449ab747fSPaolo Bonzini 
80549ab747fSPaolo Bonzini     s->regs[R_IS] |= IS_RX_COMPLETE;
80649ab747fSPaolo Bonzini     enet_update_irq(s);
80749ab747fSPaolo Bonzini     return size;
80849ab747fSPaolo Bonzini }
80949ab747fSPaolo Bonzini 
81049ab747fSPaolo Bonzini static void eth_cleanup(NetClientState *nc)
81149ab747fSPaolo Bonzini {
81249ab747fSPaolo Bonzini     /* FIXME.  */
813545129e5SPeter Crosthwaite     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
81449ab747fSPaolo Bonzini     g_free(s->rxmem);
81549ab747fSPaolo Bonzini     g_free(s);
81649ab747fSPaolo Bonzini }
81749ab747fSPaolo Bonzini 
818*35e60bfdSPeter Crosthwaite static size_t
81955b3e0c2SPeter Crosthwaite xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
82055b3e0c2SPeter Crosthwaite                                 uint32_t *hdr)
82149ab747fSPaolo Bonzini {
82255b3e0c2SPeter Crosthwaite     XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(obj);
82355b3e0c2SPeter Crosthwaite     XilinxAXIEnet *s = ds->enet;
82449ab747fSPaolo Bonzini 
82549ab747fSPaolo Bonzini     /* TX enable ?  */
82649ab747fSPaolo Bonzini     if (!(s->tc & TC_TX)) {
827*35e60bfdSPeter Crosthwaite         return size;
82849ab747fSPaolo Bonzini     }
82949ab747fSPaolo Bonzini 
83049ab747fSPaolo Bonzini     /* Jumbo or vlan sizes ?  */
83149ab747fSPaolo Bonzini     if (!(s->tc & TC_JUM)) {
83249ab747fSPaolo Bonzini         if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) {
833*35e60bfdSPeter Crosthwaite             return size;
83449ab747fSPaolo Bonzini         }
83549ab747fSPaolo Bonzini     }
83649ab747fSPaolo Bonzini 
83749ab747fSPaolo Bonzini     if (hdr[0] & 1) {
83849ab747fSPaolo Bonzini         unsigned int start_off = hdr[1] >> 16;
83949ab747fSPaolo Bonzini         unsigned int write_off = hdr[1] & 0xffff;
84049ab747fSPaolo Bonzini         uint32_t tmp_csum;
84149ab747fSPaolo Bonzini         uint16_t csum;
84249ab747fSPaolo Bonzini 
84349ab747fSPaolo Bonzini         tmp_csum = net_checksum_add(size - start_off,
84449ab747fSPaolo Bonzini                                     (uint8_t *)buf + start_off);
84549ab747fSPaolo Bonzini         /* Accumulate the seed.  */
84649ab747fSPaolo Bonzini         tmp_csum += hdr[2] & 0xffff;
84749ab747fSPaolo Bonzini 
84849ab747fSPaolo Bonzini         /* Fold the 32bit partial checksum.  */
84949ab747fSPaolo Bonzini         csum = net_checksum_finish(tmp_csum);
85049ab747fSPaolo Bonzini 
85149ab747fSPaolo Bonzini         /* Writeback.  */
85249ab747fSPaolo Bonzini         buf[write_off] = csum >> 8;
85349ab747fSPaolo Bonzini         buf[write_off + 1] = csum & 0xff;
85449ab747fSPaolo Bonzini     }
85549ab747fSPaolo Bonzini 
85649ab747fSPaolo Bonzini     qemu_send_packet(qemu_get_queue(s->nic), buf, size);
85749ab747fSPaolo Bonzini 
85849ab747fSPaolo Bonzini     s->stats.tx_bytes += size;
85949ab747fSPaolo Bonzini     s->regs[R_IS] |= IS_TX_COMPLETE;
86049ab747fSPaolo Bonzini     enet_update_irq(s);
861*35e60bfdSPeter Crosthwaite 
862*35e60bfdSPeter Crosthwaite     return size;
86349ab747fSPaolo Bonzini }
86449ab747fSPaolo Bonzini 
86549ab747fSPaolo Bonzini static NetClientInfo net_xilinx_enet_info = {
86649ab747fSPaolo Bonzini     .type = NET_CLIENT_OPTIONS_KIND_NIC,
86749ab747fSPaolo Bonzini     .size = sizeof(NICState),
86849ab747fSPaolo Bonzini     .can_receive = eth_can_rx,
86949ab747fSPaolo Bonzini     .receive = eth_rx,
87049ab747fSPaolo Bonzini     .cleanup = eth_cleanup,
87149ab747fSPaolo Bonzini };
87249ab747fSPaolo Bonzini 
873b2d9dfe9SPeter Crosthwaite static void xilinx_enet_realize(DeviceState *dev, Error **errp)
87449ab747fSPaolo Bonzini {
875f0e7a81cSPeter Crosthwaite     XilinxAXIEnet *s = XILINX_AXI_ENET(dev);
87655b3e0c2SPeter Crosthwaite     XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(&s->rx_data_dev);
87755b3e0c2SPeter Crosthwaite     Error *local_errp = NULL;
87855b3e0c2SPeter Crosthwaite 
87955b3e0c2SPeter Crosthwaite     object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet",
88055b3e0c2SPeter Crosthwaite                              (Object **) &ds->enet, &local_errp);
88155b3e0c2SPeter Crosthwaite     if (local_errp) {
88255b3e0c2SPeter Crosthwaite         goto xilinx_enet_realize_fail;
88355b3e0c2SPeter Crosthwaite     }
88455b3e0c2SPeter Crosthwaite     object_property_set_link(OBJECT(ds), OBJECT(s), "enet", &local_errp);
88555b3e0c2SPeter Crosthwaite     if (local_errp) {
88655b3e0c2SPeter Crosthwaite         goto xilinx_enet_realize_fail;
88755b3e0c2SPeter Crosthwaite     }
88849ab747fSPaolo Bonzini 
88949ab747fSPaolo Bonzini     qemu_macaddr_default_if_unset(&s->conf.macaddr);
89049ab747fSPaolo Bonzini     s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
891b2d9dfe9SPeter Crosthwaite                           object_get_typename(OBJECT(dev)), dev->id, s);
89249ab747fSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
89349ab747fSPaolo Bonzini 
89449ab747fSPaolo Bonzini     tdk_init(&s->TEMAC.phy);
89549ab747fSPaolo Bonzini     mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr);
89649ab747fSPaolo Bonzini 
89749ab747fSPaolo Bonzini     s->TEMAC.parent = s;
89849ab747fSPaolo Bonzini 
89949ab747fSPaolo Bonzini     s->rxmem = g_malloc(s->c_rxmem);
90055b3e0c2SPeter Crosthwaite     return;
90155b3e0c2SPeter Crosthwaite 
90255b3e0c2SPeter Crosthwaite xilinx_enet_realize_fail:
90355b3e0c2SPeter Crosthwaite     if (!*errp) {
90455b3e0c2SPeter Crosthwaite         *errp = local_errp;
90555b3e0c2SPeter Crosthwaite     }
90649ab747fSPaolo Bonzini }
90749ab747fSPaolo Bonzini 
908b2d9dfe9SPeter Crosthwaite static void xilinx_enet_init(Object *obj)
90949ab747fSPaolo Bonzini {
910f0e7a81cSPeter Crosthwaite     XilinxAXIEnet *s = XILINX_AXI_ENET(obj);
911b2d9dfe9SPeter Crosthwaite     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
91249ab747fSPaolo Bonzini     Error *errp = NULL;
91349ab747fSPaolo Bonzini 
91449ab747fSPaolo Bonzini     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
91549ab747fSPaolo Bonzini                              (Object **) &s->tx_dev, &errp);
91649ab747fSPaolo Bonzini     assert_no_error(errp);
917b2d9dfe9SPeter Crosthwaite 
91855b3e0c2SPeter Crosthwaite     object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_ENET_DATA_STREAM);
91955b3e0c2SPeter Crosthwaite     object_property_add_child(OBJECT(s), "axistream-connected-target",
92055b3e0c2SPeter Crosthwaite                               (Object *)&s->rx_data_dev, &errp);
92155b3e0c2SPeter Crosthwaite     assert_no_error(errp);
92255b3e0c2SPeter Crosthwaite 
923b2d9dfe9SPeter Crosthwaite     sysbus_init_irq(sbd, &s->irq);
924b2d9dfe9SPeter Crosthwaite 
925b2d9dfe9SPeter Crosthwaite     memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000);
926b2d9dfe9SPeter Crosthwaite     sysbus_init_mmio(sbd, &s->iomem);
92749ab747fSPaolo Bonzini }
92849ab747fSPaolo Bonzini 
92949ab747fSPaolo Bonzini static Property xilinx_enet_properties[] = {
930545129e5SPeter Crosthwaite     DEFINE_PROP_UINT32("phyaddr", XilinxAXIEnet, c_phyaddr, 7),
931545129e5SPeter Crosthwaite     DEFINE_PROP_UINT32("rxmem", XilinxAXIEnet, c_rxmem, 0x1000),
932545129e5SPeter Crosthwaite     DEFINE_PROP_UINT32("txmem", XilinxAXIEnet, c_txmem, 0x1000),
933545129e5SPeter Crosthwaite     DEFINE_NIC_PROPERTIES(XilinxAXIEnet, conf),
93449ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
93549ab747fSPaolo Bonzini };
93649ab747fSPaolo Bonzini 
93749ab747fSPaolo Bonzini static void xilinx_enet_class_init(ObjectClass *klass, void *data)
93849ab747fSPaolo Bonzini {
93949ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
94049ab747fSPaolo Bonzini 
941b2d9dfe9SPeter Crosthwaite     dc->realize = xilinx_enet_realize;
94249ab747fSPaolo Bonzini     dc->props = xilinx_enet_properties;
9439ee0ceb7SPeter Crosthwaite     dc->reset = xilinx_axienet_reset;
94455b3e0c2SPeter Crosthwaite }
94555b3e0c2SPeter Crosthwaite 
94655b3e0c2SPeter Crosthwaite static void xilinx_enet_stream_class_init(ObjectClass *klass, void *data)
94755b3e0c2SPeter Crosthwaite {
94855b3e0c2SPeter Crosthwaite     StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
94955b3e0c2SPeter Crosthwaite 
95055b3e0c2SPeter Crosthwaite     ssc->push = data;
95149ab747fSPaolo Bonzini }
95249ab747fSPaolo Bonzini 
95349ab747fSPaolo Bonzini static const TypeInfo xilinx_enet_info = {
954f0e7a81cSPeter Crosthwaite     .name          = TYPE_XILINX_AXI_ENET,
95549ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
956545129e5SPeter Crosthwaite     .instance_size = sizeof(XilinxAXIEnet),
95749ab747fSPaolo Bonzini     .class_init    = xilinx_enet_class_init,
958b2d9dfe9SPeter Crosthwaite     .instance_init = xilinx_enet_init,
95955b3e0c2SPeter Crosthwaite };
96055b3e0c2SPeter Crosthwaite 
96155b3e0c2SPeter Crosthwaite static const TypeInfo xilinx_enet_data_stream_info = {
96255b3e0c2SPeter Crosthwaite     .name          = TYPE_XILINX_AXI_ENET_DATA_STREAM,
96355b3e0c2SPeter Crosthwaite     .parent        = TYPE_OBJECT,
96455b3e0c2SPeter Crosthwaite     .instance_size = sizeof(struct XilinxAXIEnetStreamSlave),
96555b3e0c2SPeter Crosthwaite     .class_init    = xilinx_enet_stream_class_init,
96655b3e0c2SPeter Crosthwaite     .class_data    = xilinx_axienet_data_stream_push,
96749ab747fSPaolo Bonzini     .interfaces = (InterfaceInfo[]) {
96849ab747fSPaolo Bonzini             { TYPE_STREAM_SLAVE },
96949ab747fSPaolo Bonzini             { }
97049ab747fSPaolo Bonzini     }
97149ab747fSPaolo Bonzini };
97249ab747fSPaolo Bonzini 
97349ab747fSPaolo Bonzini static void xilinx_enet_register_types(void)
97449ab747fSPaolo Bonzini {
97549ab747fSPaolo Bonzini     type_register_static(&xilinx_enet_info);
97655b3e0c2SPeter Crosthwaite     type_register_static(&xilinx_enet_data_stream_info);
97749ab747fSPaolo Bonzini }
97849ab747fSPaolo Bonzini 
97949ab747fSPaolo Bonzini type_init(xilinx_enet_register_types)
980