xref: /qemu/hw/net/xilinx_axienet.c (revision f0e7a81c0ca122126e92a9f06b9599f04a0eaebb)
193f1e401SEdgar E. Iglesias /*
293f1e401SEdgar E. Iglesias  * QEMU model of Xilinx AXI-Ethernet.
393f1e401SEdgar E. Iglesias  *
493f1e401SEdgar E. Iglesias  * Copyright (c) 2011 Edgar E. Iglesias.
593f1e401SEdgar E. Iglesias  *
693f1e401SEdgar E. Iglesias  * Permission is hereby granted, free of charge, to any person obtaining a copy
793f1e401SEdgar E. Iglesias  * of this software and associated documentation files (the "Software"), to deal
893f1e401SEdgar E. Iglesias  * in the Software without restriction, including without limitation the rights
993f1e401SEdgar E. Iglesias  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1093f1e401SEdgar E. Iglesias  * copies of the Software, and to permit persons to whom the Software is
1193f1e401SEdgar E. Iglesias  * furnished to do so, subject to the following conditions:
1293f1e401SEdgar E. Iglesias  *
1393f1e401SEdgar E. Iglesias  * The above copyright notice and this permission notice shall be included in
1493f1e401SEdgar E. Iglesias  * all copies or substantial portions of the Software.
1593f1e401SEdgar E. Iglesias  *
1693f1e401SEdgar E. Iglesias  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1793f1e401SEdgar E. Iglesias  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1893f1e401SEdgar E. Iglesias  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1993f1e401SEdgar E. Iglesias  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2093f1e401SEdgar E. Iglesias  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2193f1e401SEdgar E. Iglesias  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2293f1e401SEdgar E. Iglesias  * THE SOFTWARE.
2393f1e401SEdgar E. Iglesias  */
2493f1e401SEdgar E. Iglesias 
2583c9f4caSPaolo Bonzini #include "hw/sysbus.h"
261de7afc9SPaolo Bonzini #include "qemu/log.h"
271422e32dSPaolo Bonzini #include "net/net.h"
2893f1e401SEdgar E. Iglesias #include "net/checksum.h"
29b4a42f81SPaolo Bonzini #include "qapi/qmp/qerror.h"
3093f1e401SEdgar E. Iglesias 
3183c9f4caSPaolo Bonzini #include "hw/stream.h"
3293f1e401SEdgar E. Iglesias 
3393f1e401SEdgar E. Iglesias #define DPHY(x)
3493f1e401SEdgar E. Iglesias 
35*f0e7a81cSPeter Crosthwaite #define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet"
36*f0e7a81cSPeter Crosthwaite 
37*f0e7a81cSPeter Crosthwaite #define XILINX_AXI_ENET(obj) \
38*f0e7a81cSPeter Crosthwaite      OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET)
39*f0e7a81cSPeter Crosthwaite 
4093f1e401SEdgar E. Iglesias /* Advertisement control register. */
4193f1e401SEdgar E. Iglesias #define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
4293f1e401SEdgar E. Iglesias #define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
4393f1e401SEdgar E. Iglesias #define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
4493f1e401SEdgar E. Iglesias #define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
4593f1e401SEdgar E. Iglesias 
4693f1e401SEdgar E. Iglesias struct PHY {
4793f1e401SEdgar E. Iglesias     uint32_t regs[32];
4893f1e401SEdgar E. Iglesias 
4993f1e401SEdgar E. Iglesias     int link;
5093f1e401SEdgar E. Iglesias 
5193f1e401SEdgar E. Iglesias     unsigned int (*read)(struct PHY *phy, unsigned int req);
5293f1e401SEdgar E. Iglesias     void (*write)(struct PHY *phy, unsigned int req,
5393f1e401SEdgar E. Iglesias                   unsigned int data);
5493f1e401SEdgar E. Iglesias };
5593f1e401SEdgar E. Iglesias 
5693f1e401SEdgar E. Iglesias static unsigned int tdk_read(struct PHY *phy, unsigned int req)
5793f1e401SEdgar E. Iglesias {
5893f1e401SEdgar E. Iglesias     int regnum;
5993f1e401SEdgar E. Iglesias     unsigned r = 0;
6093f1e401SEdgar E. Iglesias 
6193f1e401SEdgar E. Iglesias     regnum = req & 0x1f;
6293f1e401SEdgar E. Iglesias 
6393f1e401SEdgar E. Iglesias     switch (regnum) {
6493f1e401SEdgar E. Iglesias         case 1:
6593f1e401SEdgar E. Iglesias             if (!phy->link) {
6693f1e401SEdgar E. Iglesias                 break;
6793f1e401SEdgar E. Iglesias             }
6893f1e401SEdgar E. Iglesias             /* MR1.  */
6993f1e401SEdgar E. Iglesias             /* Speeds and modes.  */
7093f1e401SEdgar E. Iglesias             r |= (1 << 13) | (1 << 14);
7193f1e401SEdgar E. Iglesias             r |= (1 << 11) | (1 << 12);
7293f1e401SEdgar E. Iglesias             r |= (1 << 5); /* Autoneg complete.  */
7393f1e401SEdgar E. Iglesias             r |= (1 << 3); /* Autoneg able.  */
7493f1e401SEdgar E. Iglesias             r |= (1 << 2); /* link.  */
7593f1e401SEdgar E. Iglesias             r |= (1 << 1); /* link.  */
7693f1e401SEdgar E. Iglesias             break;
7793f1e401SEdgar E. Iglesias         case 5:
7893f1e401SEdgar E. Iglesias             /* Link partner ability.
7993f1e401SEdgar E. Iglesias                We are kind; always agree with whatever best mode
8093f1e401SEdgar E. Iglesias                the guest advertises.  */
8193f1e401SEdgar E. Iglesias             r = 1 << 14; /* Success.  */
8293f1e401SEdgar E. Iglesias             /* Copy advertised modes.  */
8393f1e401SEdgar E. Iglesias             r |= phy->regs[4] & (15 << 5);
8493f1e401SEdgar E. Iglesias             /* Autoneg support.  */
8593f1e401SEdgar E. Iglesias             r |= 1;
8693f1e401SEdgar E. Iglesias             break;
8793f1e401SEdgar E. Iglesias         case 17:
8893f1e401SEdgar E. Iglesias             /* Marvel PHY on many xilinx boards.  */
8993f1e401SEdgar E. Iglesias             r = 0x8000; /* 1000Mb  */
9093f1e401SEdgar E. Iglesias             break;
9193f1e401SEdgar E. Iglesias         case 18:
9293f1e401SEdgar E. Iglesias             {
9393f1e401SEdgar E. Iglesias                 /* Diagnostics reg.  */
9493f1e401SEdgar E. Iglesias                 int duplex = 0;
9593f1e401SEdgar E. Iglesias                 int speed_100 = 0;
9693f1e401SEdgar E. Iglesias 
9793f1e401SEdgar E. Iglesias                 if (!phy->link) {
9893f1e401SEdgar E. Iglesias                     break;
9993f1e401SEdgar E. Iglesias                 }
10093f1e401SEdgar E. Iglesias 
10193f1e401SEdgar E. Iglesias                 /* Are we advertising 100 half or 100 duplex ? */
10293f1e401SEdgar E. Iglesias                 speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
10393f1e401SEdgar E. Iglesias                 speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
10493f1e401SEdgar E. Iglesias 
10593f1e401SEdgar E. Iglesias                 /* Are we advertising 10 duplex or 100 duplex ? */
10693f1e401SEdgar E. Iglesias                 duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
10793f1e401SEdgar E. Iglesias                 duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
10893f1e401SEdgar E. Iglesias                 r = (speed_100 << 10) | (duplex << 11);
10993f1e401SEdgar E. Iglesias             }
11093f1e401SEdgar E. Iglesias             break;
11193f1e401SEdgar E. Iglesias 
11293f1e401SEdgar E. Iglesias         default:
11393f1e401SEdgar E. Iglesias             r = phy->regs[regnum];
11493f1e401SEdgar E. Iglesias             break;
11593f1e401SEdgar E. Iglesias     }
11693f1e401SEdgar E. Iglesias     DPHY(qemu_log("\n%s %x = reg[%d]\n", __func__, r, regnum));
11793f1e401SEdgar E. Iglesias     return r;
11893f1e401SEdgar E. Iglesias }
11993f1e401SEdgar E. Iglesias 
12093f1e401SEdgar E. Iglesias static void
12193f1e401SEdgar E. Iglesias tdk_write(struct PHY *phy, unsigned int req, unsigned int data)
12293f1e401SEdgar E. Iglesias {
12393f1e401SEdgar E. Iglesias     int regnum;
12493f1e401SEdgar E. Iglesias 
12593f1e401SEdgar E. Iglesias     regnum = req & 0x1f;
12693f1e401SEdgar E. Iglesias     DPHY(qemu_log("%s reg[%d] = %x\n", __func__, regnum, data));
12793f1e401SEdgar E. Iglesias     switch (regnum) {
12893f1e401SEdgar E. Iglesias         default:
12993f1e401SEdgar E. Iglesias             phy->regs[regnum] = data;
13093f1e401SEdgar E. Iglesias             break;
13193f1e401SEdgar E. Iglesias     }
13293f1e401SEdgar E. Iglesias }
13393f1e401SEdgar E. Iglesias 
13493f1e401SEdgar E. Iglesias static void
13593f1e401SEdgar E. Iglesias tdk_init(struct PHY *phy)
13693f1e401SEdgar E. Iglesias {
13793f1e401SEdgar E. Iglesias     phy->regs[0] = 0x3100;
13893f1e401SEdgar E. Iglesias     /* PHY Id.  */
13993f1e401SEdgar E. Iglesias     phy->regs[2] = 0x0300;
14093f1e401SEdgar E. Iglesias     phy->regs[3] = 0xe400;
14193f1e401SEdgar E. Iglesias     /* Autonegotiation advertisement reg.  */
14293f1e401SEdgar E. Iglesias     phy->regs[4] = 0x01E1;
14393f1e401SEdgar E. Iglesias     phy->link = 1;
14493f1e401SEdgar E. Iglesias 
14593f1e401SEdgar E. Iglesias     phy->read = tdk_read;
14693f1e401SEdgar E. Iglesias     phy->write = tdk_write;
14793f1e401SEdgar E. Iglesias }
14893f1e401SEdgar E. Iglesias 
14993f1e401SEdgar E. Iglesias struct MDIOBus {
15093f1e401SEdgar E. Iglesias     /* bus.  */
15193f1e401SEdgar E. Iglesias     int mdc;
15293f1e401SEdgar E. Iglesias     int mdio;
15393f1e401SEdgar E. Iglesias 
15493f1e401SEdgar E. Iglesias     /* decoder.  */
15593f1e401SEdgar E. Iglesias     enum {
15693f1e401SEdgar E. Iglesias         PREAMBLE,
15793f1e401SEdgar E. Iglesias         SOF,
15893f1e401SEdgar E. Iglesias         OPC,
15993f1e401SEdgar E. Iglesias         ADDR,
16093f1e401SEdgar E. Iglesias         REQ,
16193f1e401SEdgar E. Iglesias         TURNAROUND,
16293f1e401SEdgar E. Iglesias         DATA
16393f1e401SEdgar E. Iglesias     } state;
16493f1e401SEdgar E. Iglesias     unsigned int drive;
16593f1e401SEdgar E. Iglesias 
16693f1e401SEdgar E. Iglesias     unsigned int cnt;
16793f1e401SEdgar E. Iglesias     unsigned int addr;
16893f1e401SEdgar E. Iglesias     unsigned int opc;
16993f1e401SEdgar E. Iglesias     unsigned int req;
17093f1e401SEdgar E. Iglesias     unsigned int data;
17193f1e401SEdgar E. Iglesias 
17293f1e401SEdgar E. Iglesias     struct PHY *devs[32];
17393f1e401SEdgar E. Iglesias };
17493f1e401SEdgar E. Iglesias 
17593f1e401SEdgar E. Iglesias static void
17693f1e401SEdgar E. Iglesias mdio_attach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
17793f1e401SEdgar E. Iglesias {
17893f1e401SEdgar E. Iglesias     bus->devs[addr & 0x1f] = phy;
17993f1e401SEdgar E. Iglesias }
18093f1e401SEdgar E. Iglesias 
18193f1e401SEdgar E. Iglesias #ifdef USE_THIS_DEAD_CODE
18293f1e401SEdgar E. Iglesias static void
18393f1e401SEdgar E. Iglesias mdio_detach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
18493f1e401SEdgar E. Iglesias {
18593f1e401SEdgar E. Iglesias     bus->devs[addr & 0x1f] = NULL;
18693f1e401SEdgar E. Iglesias }
18793f1e401SEdgar E. Iglesias #endif
18893f1e401SEdgar E. Iglesias 
18993f1e401SEdgar E. Iglesias static uint16_t mdio_read_req(struct MDIOBus *bus, unsigned int addr,
19093f1e401SEdgar E. Iglesias                   unsigned int reg)
19193f1e401SEdgar E. Iglesias {
19293f1e401SEdgar E. Iglesias     struct PHY *phy;
19393f1e401SEdgar E. Iglesias     uint16_t data;
19493f1e401SEdgar E. Iglesias 
19593f1e401SEdgar E. Iglesias     phy = bus->devs[addr];
19693f1e401SEdgar E. Iglesias     if (phy && phy->read) {
19793f1e401SEdgar E. Iglesias         data = phy->read(phy, reg);
19893f1e401SEdgar E. Iglesias     } else {
19993f1e401SEdgar E. Iglesias         data = 0xffff;
20093f1e401SEdgar E. Iglesias     }
20193f1e401SEdgar E. Iglesias     DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
20293f1e401SEdgar E. Iglesias     return data;
20393f1e401SEdgar E. Iglesias }
20493f1e401SEdgar E. Iglesias 
20593f1e401SEdgar E. Iglesias static void mdio_write_req(struct MDIOBus *bus, unsigned int addr,
20693f1e401SEdgar E. Iglesias                unsigned int reg, uint16_t data)
20793f1e401SEdgar E. Iglesias {
20893f1e401SEdgar E. Iglesias     struct PHY *phy;
20993f1e401SEdgar E. Iglesias 
21093f1e401SEdgar E. Iglesias     DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
21193f1e401SEdgar E. Iglesias     phy = bus->devs[addr];
21293f1e401SEdgar E. Iglesias     if (phy && phy->write) {
21393f1e401SEdgar E. Iglesias         phy->write(phy, reg, data);
21493f1e401SEdgar E. Iglesias     }
21593f1e401SEdgar E. Iglesias }
21693f1e401SEdgar E. Iglesias 
21793f1e401SEdgar E. Iglesias #define DENET(x)
21893f1e401SEdgar E. Iglesias 
21993f1e401SEdgar E. Iglesias #define R_RAF      (0x000 / 4)
22093f1e401SEdgar E. Iglesias enum {
22193f1e401SEdgar E. Iglesias     RAF_MCAST_REJ = (1 << 1),
22293f1e401SEdgar E. Iglesias     RAF_BCAST_REJ = (1 << 2),
22393f1e401SEdgar E. Iglesias     RAF_EMCF_EN = (1 << 12),
22493f1e401SEdgar E. Iglesias     RAF_NEWFUNC_EN = (1 << 11)
22593f1e401SEdgar E. Iglesias };
22693f1e401SEdgar E. Iglesias 
22793f1e401SEdgar E. Iglesias #define R_IS       (0x00C / 4)
22893f1e401SEdgar E. Iglesias enum {
22993f1e401SEdgar E. Iglesias     IS_HARD_ACCESS_COMPLETE = 1,
23093f1e401SEdgar E. Iglesias     IS_AUTONEG = (1 << 1),
23193f1e401SEdgar E. Iglesias     IS_RX_COMPLETE = (1 << 2),
23293f1e401SEdgar E. Iglesias     IS_RX_REJECT = (1 << 3),
23393f1e401SEdgar E. Iglesias     IS_TX_COMPLETE = (1 << 5),
23493f1e401SEdgar E. Iglesias     IS_RX_DCM_LOCK = (1 << 6),
23593f1e401SEdgar E. Iglesias     IS_MGM_RDY = (1 << 7),
23693f1e401SEdgar E. Iglesias     IS_PHY_RST_DONE = (1 << 8),
23793f1e401SEdgar E. Iglesias };
23893f1e401SEdgar E. Iglesias 
23993f1e401SEdgar E. Iglesias #define R_IP       (0x010 / 4)
24093f1e401SEdgar E. Iglesias #define R_IE       (0x014 / 4)
24193f1e401SEdgar E. Iglesias #define R_UAWL     (0x020 / 4)
24293f1e401SEdgar E. Iglesias #define R_UAWU     (0x024 / 4)
24393f1e401SEdgar E. Iglesias #define R_PPST     (0x030 / 4)
24493f1e401SEdgar E. Iglesias enum {
24593f1e401SEdgar E. Iglesias     PPST_LINKSTATUS = (1 << 0),
24693f1e401SEdgar E. Iglesias     PPST_PHY_LINKSTATUS = (1 << 7),
24793f1e401SEdgar E. Iglesias };
24893f1e401SEdgar E. Iglesias 
24993f1e401SEdgar E. Iglesias #define R_STATS_RX_BYTESL (0x200 / 4)
25093f1e401SEdgar E. Iglesias #define R_STATS_RX_BYTESH (0x204 / 4)
25193f1e401SEdgar E. Iglesias #define R_STATS_TX_BYTESL (0x208 / 4)
25293f1e401SEdgar E. Iglesias #define R_STATS_TX_BYTESH (0x20C / 4)
25393f1e401SEdgar E. Iglesias #define R_STATS_RXL       (0x290 / 4)
25493f1e401SEdgar E. Iglesias #define R_STATS_RXH       (0x294 / 4)
25593f1e401SEdgar E. Iglesias #define R_STATS_RX_BCASTL (0x2a0 / 4)
25693f1e401SEdgar E. Iglesias #define R_STATS_RX_BCASTH (0x2a4 / 4)
25793f1e401SEdgar E. Iglesias #define R_STATS_RX_MCASTL (0x2a8 / 4)
25893f1e401SEdgar E. Iglesias #define R_STATS_RX_MCASTH (0x2ac / 4)
25993f1e401SEdgar E. Iglesias 
26093f1e401SEdgar E. Iglesias #define R_RCW0     (0x400 / 4)
26193f1e401SEdgar E. Iglesias #define R_RCW1     (0x404 / 4)
26293f1e401SEdgar E. Iglesias enum {
26393f1e401SEdgar E. Iglesias     RCW1_VLAN = (1 << 27),
26493f1e401SEdgar E. Iglesias     RCW1_RX   = (1 << 28),
26593f1e401SEdgar E. Iglesias     RCW1_FCS  = (1 << 29),
26693f1e401SEdgar E. Iglesias     RCW1_JUM  = (1 << 30),
26793f1e401SEdgar E. Iglesias     RCW1_RST  = (1 << 31),
26893f1e401SEdgar E. Iglesias };
26993f1e401SEdgar E. Iglesias 
27093f1e401SEdgar E. Iglesias #define R_TC       (0x408 / 4)
27193f1e401SEdgar E. Iglesias enum {
27293f1e401SEdgar E. Iglesias     TC_VLAN = (1 << 27),
27393f1e401SEdgar E. Iglesias     TC_TX   = (1 << 28),
27493f1e401SEdgar E. Iglesias     TC_FCS  = (1 << 29),
27593f1e401SEdgar E. Iglesias     TC_JUM  = (1 << 30),
27693f1e401SEdgar E. Iglesias     TC_RST  = (1 << 31),
27793f1e401SEdgar E. Iglesias };
27893f1e401SEdgar E. Iglesias 
27993f1e401SEdgar E. Iglesias #define R_EMMC     (0x410 / 4)
28093f1e401SEdgar E. Iglesias enum {
28193f1e401SEdgar E. Iglesias     EMMC_LINKSPEED_10MB = (0 << 30),
28293f1e401SEdgar E. Iglesias     EMMC_LINKSPEED_100MB = (1 << 30),
28393f1e401SEdgar E. Iglesias     EMMC_LINKSPEED_1000MB = (2 << 30),
28493f1e401SEdgar E. Iglesias };
28593f1e401SEdgar E. Iglesias 
28693f1e401SEdgar E. Iglesias #define R_PHYC     (0x414 / 4)
28793f1e401SEdgar E. Iglesias 
28893f1e401SEdgar E. Iglesias #define R_MC       (0x500 / 4)
28993f1e401SEdgar E. Iglesias #define MC_EN      (1 << 6)
29093f1e401SEdgar E. Iglesias 
29193f1e401SEdgar E. Iglesias #define R_MCR      (0x504 / 4)
29293f1e401SEdgar E. Iglesias #define R_MWD      (0x508 / 4)
29393f1e401SEdgar E. Iglesias #define R_MRD      (0x50c / 4)
29493f1e401SEdgar E. Iglesias #define R_MIS      (0x600 / 4)
29593f1e401SEdgar E. Iglesias #define R_MIP      (0x620 / 4)
29693f1e401SEdgar E. Iglesias #define R_MIE      (0x640 / 4)
29793f1e401SEdgar E. Iglesias #define R_MIC      (0x640 / 4)
29893f1e401SEdgar E. Iglesias 
29993f1e401SEdgar E. Iglesias #define R_UAW0     (0x700 / 4)
30093f1e401SEdgar E. Iglesias #define R_UAW1     (0x704 / 4)
30193f1e401SEdgar E. Iglesias #define R_FMI      (0x708 / 4)
30293f1e401SEdgar E. Iglesias #define R_AF0      (0x710 / 4)
30393f1e401SEdgar E. Iglesias #define R_AF1      (0x714 / 4)
30493f1e401SEdgar E. Iglesias #define R_MAX      (0x34 / 4)
30593f1e401SEdgar E. Iglesias 
30693f1e401SEdgar E. Iglesias /* Indirect registers.  */
30793f1e401SEdgar E. Iglesias struct TEMAC  {
30893f1e401SEdgar E. Iglesias     struct MDIOBus mdio_bus;
30993f1e401SEdgar E. Iglesias     struct PHY phy;
31093f1e401SEdgar E. Iglesias 
31193f1e401SEdgar E. Iglesias     void *parent;
31293f1e401SEdgar E. Iglesias };
31393f1e401SEdgar E. Iglesias 
314545129e5SPeter Crosthwaite typedef struct XilinxAXIEnet XilinxAXIEnet;
315545129e5SPeter Crosthwaite 
31693f1e401SEdgar E. Iglesias struct XilinxAXIEnet {
31793f1e401SEdgar E. Iglesias     SysBusDevice busdev;
3180dc31f3bSAvi Kivity     MemoryRegion iomem;
31993f1e401SEdgar E. Iglesias     qemu_irq irq;
320669b4983SPeter A. G. Crosthwaite     StreamSlave *tx_dev;
32193f1e401SEdgar E. Iglesias     NICState *nic;
32293f1e401SEdgar E. Iglesias     NICConf conf;
32393f1e401SEdgar E. Iglesias 
32493f1e401SEdgar E. Iglesias 
32593f1e401SEdgar E. Iglesias     uint32_t c_rxmem;
32693f1e401SEdgar E. Iglesias     uint32_t c_txmem;
32793f1e401SEdgar E. Iglesias     uint32_t c_phyaddr;
32893f1e401SEdgar E. Iglesias 
32993f1e401SEdgar E. Iglesias     struct TEMAC TEMAC;
33093f1e401SEdgar E. Iglesias 
33193f1e401SEdgar E. Iglesias     /* MII regs.  */
33293f1e401SEdgar E. Iglesias     union {
33393f1e401SEdgar E. Iglesias         uint32_t regs[4];
33493f1e401SEdgar E. Iglesias         struct {
33593f1e401SEdgar E. Iglesias             uint32_t mc;
33693f1e401SEdgar E. Iglesias             uint32_t mcr;
33793f1e401SEdgar E. Iglesias             uint32_t mwd;
33893f1e401SEdgar E. Iglesias             uint32_t mrd;
33993f1e401SEdgar E. Iglesias         };
34093f1e401SEdgar E. Iglesias     } mii;
34193f1e401SEdgar E. Iglesias 
34293f1e401SEdgar E. Iglesias     struct {
34393f1e401SEdgar E. Iglesias         uint64_t rx_bytes;
34493f1e401SEdgar E. Iglesias         uint64_t tx_bytes;
34593f1e401SEdgar E. Iglesias 
34693f1e401SEdgar E. Iglesias         uint64_t rx;
34793f1e401SEdgar E. Iglesias         uint64_t rx_bcast;
34893f1e401SEdgar E. Iglesias         uint64_t rx_mcast;
34993f1e401SEdgar E. Iglesias     } stats;
35093f1e401SEdgar E. Iglesias 
35193f1e401SEdgar E. Iglesias     /* Receive configuration words.  */
35293f1e401SEdgar E. Iglesias     uint32_t rcw[2];
35393f1e401SEdgar E. Iglesias     /* Transmit config.  */
35493f1e401SEdgar E. Iglesias     uint32_t tc;
35593f1e401SEdgar E. Iglesias     uint32_t emmc;
35693f1e401SEdgar E. Iglesias     uint32_t phyc;
35793f1e401SEdgar E. Iglesias 
35893f1e401SEdgar E. Iglesias     /* Unicast Address Word.  */
35993f1e401SEdgar E. Iglesias     uint32_t uaw[2];
36093f1e401SEdgar E. Iglesias     /* Unicast address filter used with extended mcast.  */
36193f1e401SEdgar E. Iglesias     uint32_t ext_uaw[2];
36293f1e401SEdgar E. Iglesias     uint32_t fmi;
36393f1e401SEdgar E. Iglesias 
36493f1e401SEdgar E. Iglesias     uint32_t regs[R_MAX];
36593f1e401SEdgar E. Iglesias 
36693f1e401SEdgar E. Iglesias     /* Multicast filter addrs.  */
36793f1e401SEdgar E. Iglesias     uint32_t maddr[4][2];
36893f1e401SEdgar E. Iglesias     /* 32K x 1 lookup filter.  */
36993f1e401SEdgar E. Iglesias     uint32_t ext_mtable[1024];
37093f1e401SEdgar E. Iglesias 
37193f1e401SEdgar E. Iglesias 
37293f1e401SEdgar E. Iglesias     uint8_t *rxmem;
37393f1e401SEdgar E. Iglesias };
37493f1e401SEdgar E. Iglesias 
375545129e5SPeter Crosthwaite static void axienet_rx_reset(XilinxAXIEnet *s)
37693f1e401SEdgar E. Iglesias {
37793f1e401SEdgar E. Iglesias     s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN;
37893f1e401SEdgar E. Iglesias }
37993f1e401SEdgar E. Iglesias 
380545129e5SPeter Crosthwaite static void axienet_tx_reset(XilinxAXIEnet *s)
38193f1e401SEdgar E. Iglesias {
38293f1e401SEdgar E. Iglesias     s->tc = TC_JUM | TC_TX | TC_VLAN;
38393f1e401SEdgar E. Iglesias }
38493f1e401SEdgar E. Iglesias 
385545129e5SPeter Crosthwaite static inline int axienet_rx_resetting(XilinxAXIEnet *s)
38693f1e401SEdgar E. Iglesias {
38793f1e401SEdgar E. Iglesias     return s->rcw[1] & RCW1_RST;
38893f1e401SEdgar E. Iglesias }
38993f1e401SEdgar E. Iglesias 
390545129e5SPeter Crosthwaite static inline int axienet_rx_enabled(XilinxAXIEnet *s)
39193f1e401SEdgar E. Iglesias {
39293f1e401SEdgar E. Iglesias     return s->rcw[1] & RCW1_RX;
39393f1e401SEdgar E. Iglesias }
39493f1e401SEdgar E. Iglesias 
395545129e5SPeter Crosthwaite static inline int axienet_extmcf_enabled(XilinxAXIEnet *s)
39693f1e401SEdgar E. Iglesias {
39793f1e401SEdgar E. Iglesias     return !!(s->regs[R_RAF] & RAF_EMCF_EN);
39893f1e401SEdgar E. Iglesias }
39993f1e401SEdgar E. Iglesias 
400545129e5SPeter Crosthwaite static inline int axienet_newfunc_enabled(XilinxAXIEnet *s)
40193f1e401SEdgar E. Iglesias {
40293f1e401SEdgar E. Iglesias     return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN);
40393f1e401SEdgar E. Iglesias }
40493f1e401SEdgar E. Iglesias 
405545129e5SPeter Crosthwaite static void axienet_reset(XilinxAXIEnet *s)
40693f1e401SEdgar E. Iglesias {
40793f1e401SEdgar E. Iglesias     axienet_rx_reset(s);
40893f1e401SEdgar E. Iglesias     axienet_tx_reset(s);
40993f1e401SEdgar E. Iglesias 
41093f1e401SEdgar E. Iglesias     s->regs[R_PPST] = PPST_LINKSTATUS | PPST_PHY_LINKSTATUS;
41193f1e401SEdgar E. Iglesias     s->regs[R_IS] = IS_AUTONEG | IS_RX_DCM_LOCK | IS_MGM_RDY | IS_PHY_RST_DONE;
41293f1e401SEdgar E. Iglesias 
41393f1e401SEdgar E. Iglesias     s->emmc = EMMC_LINKSPEED_100MB;
41493f1e401SEdgar E. Iglesias }
41593f1e401SEdgar E. Iglesias 
416545129e5SPeter Crosthwaite static void enet_update_irq(XilinxAXIEnet *s)
41793f1e401SEdgar E. Iglesias {
41893f1e401SEdgar E. Iglesias     s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE];
41993f1e401SEdgar E. Iglesias     qemu_set_irq(s->irq, !!s->regs[R_IP]);
42093f1e401SEdgar E. Iglesias }
42193f1e401SEdgar E. Iglesias 
422a8170e5eSAvi Kivity static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
42393f1e401SEdgar E. Iglesias {
424545129e5SPeter Crosthwaite     XilinxAXIEnet *s = opaque;
42593f1e401SEdgar E. Iglesias     uint32_t r = 0;
42693f1e401SEdgar E. Iglesias     addr >>= 2;
42793f1e401SEdgar E. Iglesias 
42893f1e401SEdgar E. Iglesias     switch (addr) {
42993f1e401SEdgar E. Iglesias         case R_RCW0:
43093f1e401SEdgar E. Iglesias         case R_RCW1:
43193f1e401SEdgar E. Iglesias             r = s->rcw[addr & 1];
43293f1e401SEdgar E. Iglesias             break;
43393f1e401SEdgar E. Iglesias 
43493f1e401SEdgar E. Iglesias         case R_TC:
43593f1e401SEdgar E. Iglesias             r = s->tc;
43693f1e401SEdgar E. Iglesias             break;
43793f1e401SEdgar E. Iglesias 
43893f1e401SEdgar E. Iglesias         case R_EMMC:
43993f1e401SEdgar E. Iglesias             r = s->emmc;
44093f1e401SEdgar E. Iglesias             break;
44193f1e401SEdgar E. Iglesias 
44293f1e401SEdgar E. Iglesias         case R_PHYC:
44393f1e401SEdgar E. Iglesias             r = s->phyc;
44493f1e401SEdgar E. Iglesias             break;
44593f1e401SEdgar E. Iglesias 
44693f1e401SEdgar E. Iglesias         case R_MCR:
44793f1e401SEdgar E. Iglesias             r = s->mii.regs[addr & 3] | (1 << 7); /* Always ready.  */
44893f1e401SEdgar E. Iglesias             break;
44993f1e401SEdgar E. Iglesias 
45093f1e401SEdgar E. Iglesias         case R_STATS_RX_BYTESL:
45193f1e401SEdgar E. Iglesias         case R_STATS_RX_BYTESH:
45293f1e401SEdgar E. Iglesias             r = s->stats.rx_bytes >> (32 * (addr & 1));
45393f1e401SEdgar E. Iglesias             break;
45493f1e401SEdgar E. Iglesias 
45593f1e401SEdgar E. Iglesias         case R_STATS_TX_BYTESL:
45693f1e401SEdgar E. Iglesias         case R_STATS_TX_BYTESH:
45793f1e401SEdgar E. Iglesias             r = s->stats.tx_bytes >> (32 * (addr & 1));
45893f1e401SEdgar E. Iglesias             break;
45993f1e401SEdgar E. Iglesias 
46093f1e401SEdgar E. Iglesias         case R_STATS_RXL:
46193f1e401SEdgar E. Iglesias         case R_STATS_RXH:
46293f1e401SEdgar E. Iglesias             r = s->stats.rx >> (32 * (addr & 1));
46393f1e401SEdgar E. Iglesias             break;
46493f1e401SEdgar E. Iglesias         case R_STATS_RX_BCASTL:
46593f1e401SEdgar E. Iglesias         case R_STATS_RX_BCASTH:
46693f1e401SEdgar E. Iglesias             r = s->stats.rx_bcast >> (32 * (addr & 1));
46793f1e401SEdgar E. Iglesias             break;
46893f1e401SEdgar E. Iglesias         case R_STATS_RX_MCASTL:
46993f1e401SEdgar E. Iglesias         case R_STATS_RX_MCASTH:
47093f1e401SEdgar E. Iglesias             r = s->stats.rx_mcast >> (32 * (addr & 1));
47193f1e401SEdgar E. Iglesias             break;
47293f1e401SEdgar E. Iglesias 
47393f1e401SEdgar E. Iglesias         case R_MC:
47493f1e401SEdgar E. Iglesias         case R_MWD:
47593f1e401SEdgar E. Iglesias         case R_MRD:
47693f1e401SEdgar E. Iglesias             r = s->mii.regs[addr & 3];
47793f1e401SEdgar E. Iglesias             break;
47893f1e401SEdgar E. Iglesias 
47993f1e401SEdgar E. Iglesias         case R_UAW0:
48093f1e401SEdgar E. Iglesias         case R_UAW1:
48193f1e401SEdgar E. Iglesias             r = s->uaw[addr & 1];
48293f1e401SEdgar E. Iglesias             break;
48393f1e401SEdgar E. Iglesias 
48493f1e401SEdgar E. Iglesias         case R_UAWU:
48593f1e401SEdgar E. Iglesias         case R_UAWL:
48693f1e401SEdgar E. Iglesias             r = s->ext_uaw[addr & 1];
48793f1e401SEdgar E. Iglesias             break;
48893f1e401SEdgar E. Iglesias 
48993f1e401SEdgar E. Iglesias         case R_FMI:
49093f1e401SEdgar E. Iglesias             r = s->fmi;
49193f1e401SEdgar E. Iglesias             break;
49293f1e401SEdgar E. Iglesias 
49393f1e401SEdgar E. Iglesias         case R_AF0:
49493f1e401SEdgar E. Iglesias         case R_AF1:
49593f1e401SEdgar E. Iglesias             r = s->maddr[s->fmi & 3][addr & 1];
49693f1e401SEdgar E. Iglesias             break;
49793f1e401SEdgar E. Iglesias 
49893f1e401SEdgar E. Iglesias         case 0x8000 ... 0x83ff:
49993f1e401SEdgar E. Iglesias             r = s->ext_mtable[addr - 0x8000];
50093f1e401SEdgar E. Iglesias             break;
50193f1e401SEdgar E. Iglesias 
50293f1e401SEdgar E. Iglesias         default:
50393f1e401SEdgar E. Iglesias             if (addr < ARRAY_SIZE(s->regs)) {
50493f1e401SEdgar E. Iglesias                 r = s->regs[addr];
50593f1e401SEdgar E. Iglesias             }
50693f1e401SEdgar E. Iglesias             DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
50793f1e401SEdgar E. Iglesias                             __func__, addr * 4, r));
50893f1e401SEdgar E. Iglesias             break;
50993f1e401SEdgar E. Iglesias     }
51093f1e401SEdgar E. Iglesias     return r;
51193f1e401SEdgar E. Iglesias }
51293f1e401SEdgar E. Iglesias 
513a8170e5eSAvi Kivity static void enet_write(void *opaque, hwaddr addr,
5140dc31f3bSAvi Kivity                        uint64_t value, unsigned size)
51593f1e401SEdgar E. Iglesias {
516545129e5SPeter Crosthwaite     XilinxAXIEnet *s = opaque;
51793f1e401SEdgar E. Iglesias     struct TEMAC *t = &s->TEMAC;
51893f1e401SEdgar E. Iglesias 
51993f1e401SEdgar E. Iglesias     addr >>= 2;
52093f1e401SEdgar E. Iglesias     switch (addr) {
52193f1e401SEdgar E. Iglesias         case R_RCW0:
52293f1e401SEdgar E. Iglesias         case R_RCW1:
52393f1e401SEdgar E. Iglesias             s->rcw[addr & 1] = value;
52493f1e401SEdgar E. Iglesias             if ((addr & 1) && value & RCW1_RST) {
52593f1e401SEdgar E. Iglesias                 axienet_rx_reset(s);
5264dbb9ed3SPeter Crosthwaite             } else {
5274dbb9ed3SPeter Crosthwaite                 qemu_flush_queued_packets(qemu_get_queue(s->nic));
52893f1e401SEdgar E. Iglesias             }
52993f1e401SEdgar E. Iglesias             break;
53093f1e401SEdgar E. Iglesias 
53193f1e401SEdgar E. Iglesias         case R_TC:
53293f1e401SEdgar E. Iglesias             s->tc = value;
53393f1e401SEdgar E. Iglesias             if (value & TC_RST) {
53493f1e401SEdgar E. Iglesias                 axienet_tx_reset(s);
53593f1e401SEdgar E. Iglesias             }
53693f1e401SEdgar E. Iglesias             break;
53793f1e401SEdgar E. Iglesias 
53893f1e401SEdgar E. Iglesias         case R_EMMC:
53993f1e401SEdgar E. Iglesias             s->emmc = value;
54093f1e401SEdgar E. Iglesias             break;
54193f1e401SEdgar E. Iglesias 
54293f1e401SEdgar E. Iglesias         case R_PHYC:
54393f1e401SEdgar E. Iglesias             s->phyc = value;
54493f1e401SEdgar E. Iglesias             break;
54593f1e401SEdgar E. Iglesias 
54693f1e401SEdgar E. Iglesias         case R_MC:
54793f1e401SEdgar E. Iglesias              value &= ((1 < 7) - 1);
54893f1e401SEdgar E. Iglesias 
54993f1e401SEdgar E. Iglesias              /* Enable the MII.  */
55093f1e401SEdgar E. Iglesias              if (value & MC_EN) {
55193f1e401SEdgar E. Iglesias                  unsigned int miiclkdiv = value & ((1 << 6) - 1);
55293f1e401SEdgar E. Iglesias                  if (!miiclkdiv) {
55393f1e401SEdgar E. Iglesias                      qemu_log("AXIENET: MDIO enabled but MDIOCLK is zero!\n");
55493f1e401SEdgar E. Iglesias                  }
55593f1e401SEdgar E. Iglesias              }
55693f1e401SEdgar E. Iglesias              s->mii.mc = value;
55793f1e401SEdgar E. Iglesias              break;
55893f1e401SEdgar E. Iglesias 
55993f1e401SEdgar E. Iglesias         case R_MCR: {
56093f1e401SEdgar E. Iglesias              unsigned int phyaddr = (value >> 24) & 0x1f;
56193f1e401SEdgar E. Iglesias              unsigned int regaddr = (value >> 16) & 0x1f;
56293f1e401SEdgar E. Iglesias              unsigned int op = (value >> 14) & 3;
56393f1e401SEdgar E. Iglesias              unsigned int initiate = (value >> 11) & 1;
56493f1e401SEdgar E. Iglesias 
56593f1e401SEdgar E. Iglesias              if (initiate) {
56693f1e401SEdgar E. Iglesias                  if (op == 1) {
56793f1e401SEdgar E. Iglesias                      mdio_write_req(&t->mdio_bus, phyaddr, regaddr, s->mii.mwd);
56893f1e401SEdgar E. Iglesias                  } else if (op == 2) {
56993f1e401SEdgar E. Iglesias                      s->mii.mrd = mdio_read_req(&t->mdio_bus, phyaddr, regaddr);
57093f1e401SEdgar E. Iglesias                  } else {
57193f1e401SEdgar E. Iglesias                      qemu_log("AXIENET: invalid MDIOBus OP=%d\n", op);
57293f1e401SEdgar E. Iglesias                  }
57393f1e401SEdgar E. Iglesias              }
57493f1e401SEdgar E. Iglesias              s->mii.mcr = value;
57593f1e401SEdgar E. Iglesias              break;
57693f1e401SEdgar E. Iglesias         }
57793f1e401SEdgar E. Iglesias 
57893f1e401SEdgar E. Iglesias         case R_MWD:
57993f1e401SEdgar E. Iglesias         case R_MRD:
58093f1e401SEdgar E. Iglesias              s->mii.regs[addr & 3] = value;
58193f1e401SEdgar E. Iglesias              break;
58293f1e401SEdgar E. Iglesias 
58393f1e401SEdgar E. Iglesias 
58493f1e401SEdgar E. Iglesias         case R_UAW0:
58593f1e401SEdgar E. Iglesias         case R_UAW1:
58693f1e401SEdgar E. Iglesias             s->uaw[addr & 1] = value;
58793f1e401SEdgar E. Iglesias             break;
58893f1e401SEdgar E. Iglesias 
58993f1e401SEdgar E. Iglesias         case R_UAWL:
59093f1e401SEdgar E. Iglesias         case R_UAWU:
59193f1e401SEdgar E. Iglesias             s->ext_uaw[addr & 1] = value;
59293f1e401SEdgar E. Iglesias             break;
59393f1e401SEdgar E. Iglesias 
59493f1e401SEdgar E. Iglesias         case R_FMI:
59593f1e401SEdgar E. Iglesias             s->fmi = value;
59693f1e401SEdgar E. Iglesias             break;
59793f1e401SEdgar E. Iglesias 
59893f1e401SEdgar E. Iglesias         case R_AF0:
59993f1e401SEdgar E. Iglesias         case R_AF1:
60093f1e401SEdgar E. Iglesias             s->maddr[s->fmi & 3][addr & 1] = value;
60193f1e401SEdgar E. Iglesias             break;
60293f1e401SEdgar E. Iglesias 
603d4d230daSPeter Crosthwaite         case R_IS:
604d4d230daSPeter Crosthwaite             s->regs[addr] &= ~value;
605d4d230daSPeter Crosthwaite             break;
606d4d230daSPeter Crosthwaite 
60793f1e401SEdgar E. Iglesias         case 0x8000 ... 0x83ff:
60893f1e401SEdgar E. Iglesias             s->ext_mtable[addr - 0x8000] = value;
60993f1e401SEdgar E. Iglesias             break;
61093f1e401SEdgar E. Iglesias 
61193f1e401SEdgar E. Iglesias         default:
61293f1e401SEdgar E. Iglesias             DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
6130dc31f3bSAvi Kivity                            __func__, addr * 4, (unsigned)value));
61493f1e401SEdgar E. Iglesias             if (addr < ARRAY_SIZE(s->regs)) {
61593f1e401SEdgar E. Iglesias                 s->regs[addr] = value;
61693f1e401SEdgar E. Iglesias             }
61793f1e401SEdgar E. Iglesias             break;
61893f1e401SEdgar E. Iglesias     }
61993f1e401SEdgar E. Iglesias     enet_update_irq(s);
62093f1e401SEdgar E. Iglesias }
62193f1e401SEdgar E. Iglesias 
6220dc31f3bSAvi Kivity static const MemoryRegionOps enet_ops = {
6230dc31f3bSAvi Kivity     .read = enet_read,
6240dc31f3bSAvi Kivity     .write = enet_write,
6250dc31f3bSAvi Kivity     .endianness = DEVICE_LITTLE_ENDIAN,
62693f1e401SEdgar E. Iglesias };
62793f1e401SEdgar E. Iglesias 
6284e68f7a0SStefan Hajnoczi static int eth_can_rx(NetClientState *nc)
62993f1e401SEdgar E. Iglesias {
630545129e5SPeter Crosthwaite     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
63193f1e401SEdgar E. Iglesias 
63293f1e401SEdgar E. Iglesias     /* RX enabled?  */
63393f1e401SEdgar E. Iglesias     return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
63493f1e401SEdgar E. Iglesias }
63593f1e401SEdgar E. Iglesias 
63693f1e401SEdgar E. Iglesias static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
63793f1e401SEdgar E. Iglesias {
63893f1e401SEdgar E. Iglesias     int match = 1;
63993f1e401SEdgar E. Iglesias 
64093f1e401SEdgar E. Iglesias     if (memcmp(buf, &f0, 4)) {
64193f1e401SEdgar E. Iglesias         match = 0;
64293f1e401SEdgar E. Iglesias     }
64393f1e401SEdgar E. Iglesias 
64493f1e401SEdgar E. Iglesias     if (buf[4] != (f1 & 0xff) || buf[5] != ((f1 >> 8) & 0xff)) {
64593f1e401SEdgar E. Iglesias         match = 0;
64693f1e401SEdgar E. Iglesias     }
64793f1e401SEdgar E. Iglesias 
64893f1e401SEdgar E. Iglesias     return match;
64993f1e401SEdgar E. Iglesias }
65093f1e401SEdgar E. Iglesias 
6514e68f7a0SStefan Hajnoczi static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
65293f1e401SEdgar E. Iglesias {
653545129e5SPeter Crosthwaite     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
65493f1e401SEdgar E. Iglesias     static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
65593f1e401SEdgar E. Iglesias                                               0xff, 0xff, 0xff};
65693f1e401SEdgar E. Iglesias     static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
65793f1e401SEdgar E. Iglesias     uint32_t app[6] = {0};
65893f1e401SEdgar E. Iglesias     int promisc = s->fmi & (1 << 31);
65993f1e401SEdgar E. Iglesias     int unicast, broadcast, multicast, ip_multicast = 0;
66093f1e401SEdgar E. Iglesias     uint32_t csum32;
66193f1e401SEdgar E. Iglesias     uint16_t csum16;
66293f1e401SEdgar E. Iglesias     int i;
66393f1e401SEdgar E. Iglesias 
66493f1e401SEdgar E. Iglesias     DENET(qemu_log("%s: %zd bytes\n", __func__, size));
66593f1e401SEdgar E. Iglesias 
66693f1e401SEdgar E. Iglesias     unicast = ~buf[0] & 0x1;
66793f1e401SEdgar E. Iglesias     broadcast = memcmp(buf, sa_bcast, 6) == 0;
66893f1e401SEdgar E. Iglesias     multicast = !unicast && !broadcast;
66993f1e401SEdgar E. Iglesias     if (multicast && (memcmp(sa_ipmcast, buf, sizeof sa_ipmcast) == 0)) {
67093f1e401SEdgar E. Iglesias         ip_multicast = 1;
67193f1e401SEdgar E. Iglesias     }
67293f1e401SEdgar E. Iglesias 
67393f1e401SEdgar E. Iglesias     /* Jumbo or vlan sizes ?  */
67493f1e401SEdgar E. Iglesias     if (!(s->rcw[1] & RCW1_JUM)) {
67593f1e401SEdgar E. Iglesias         if (size > 1518 && size <= 1522 && !(s->rcw[1] & RCW1_VLAN)) {
67693f1e401SEdgar E. Iglesias             return size;
67793f1e401SEdgar E. Iglesias         }
67893f1e401SEdgar E. Iglesias     }
67993f1e401SEdgar E. Iglesias 
68093f1e401SEdgar E. Iglesias     /* Basic Address filters.  If you want to use the extended filters
68193f1e401SEdgar E. Iglesias        you'll generally have to place the ethernet mac into promiscuous mode
68293f1e401SEdgar E. Iglesias        to avoid the basic filtering from dropping most frames.  */
68393f1e401SEdgar E. Iglesias     if (!promisc) {
68493f1e401SEdgar E. Iglesias         if (unicast) {
68593f1e401SEdgar E. Iglesias             if (!enet_match_addr(buf, s->uaw[0], s->uaw[1])) {
68693f1e401SEdgar E. Iglesias                 return size;
68793f1e401SEdgar E. Iglesias             }
68893f1e401SEdgar E. Iglesias         } else {
68993f1e401SEdgar E. Iglesias             if (broadcast) {
69093f1e401SEdgar E. Iglesias                 /* Broadcast.  */
69193f1e401SEdgar E. Iglesias                 if (s->regs[R_RAF] & RAF_BCAST_REJ) {
69293f1e401SEdgar E. Iglesias                     return size;
69393f1e401SEdgar E. Iglesias                 }
69493f1e401SEdgar E. Iglesias             } else {
69593f1e401SEdgar E. Iglesias                 int drop = 1;
69693f1e401SEdgar E. Iglesias 
69793f1e401SEdgar E. Iglesias                 /* Multicast.  */
69893f1e401SEdgar E. Iglesias                 if (s->regs[R_RAF] & RAF_MCAST_REJ) {
69993f1e401SEdgar E. Iglesias                     return size;
70093f1e401SEdgar E. Iglesias                 }
70193f1e401SEdgar E. Iglesias 
70293f1e401SEdgar E. Iglesias                 for (i = 0; i < 4; i++) {
70393f1e401SEdgar E. Iglesias                     if (enet_match_addr(buf, s->maddr[i][0], s->maddr[i][1])) {
70493f1e401SEdgar E. Iglesias                         drop = 0;
70593f1e401SEdgar E. Iglesias                         break;
70693f1e401SEdgar E. Iglesias                     }
70793f1e401SEdgar E. Iglesias                 }
70893f1e401SEdgar E. Iglesias 
70993f1e401SEdgar E. Iglesias                 if (drop) {
71093f1e401SEdgar E. Iglesias                     return size;
71193f1e401SEdgar E. Iglesias                 }
71293f1e401SEdgar E. Iglesias             }
71393f1e401SEdgar E. Iglesias         }
71493f1e401SEdgar E. Iglesias     }
71593f1e401SEdgar E. Iglesias 
71693f1e401SEdgar E. Iglesias     /* Extended mcast filtering enabled?  */
71793f1e401SEdgar E. Iglesias     if (axienet_newfunc_enabled(s) && axienet_extmcf_enabled(s)) {
71893f1e401SEdgar E. Iglesias         if (unicast) {
71993f1e401SEdgar E. Iglesias             if (!enet_match_addr(buf, s->ext_uaw[0], s->ext_uaw[1])) {
72093f1e401SEdgar E. Iglesias                 return size;
72193f1e401SEdgar E. Iglesias             }
72293f1e401SEdgar E. Iglesias         } else {
72393f1e401SEdgar E. Iglesias             if (broadcast) {
72493f1e401SEdgar E. Iglesias                 /* Broadcast. ???  */
72593f1e401SEdgar E. Iglesias                 if (s->regs[R_RAF] & RAF_BCAST_REJ) {
72693f1e401SEdgar E. Iglesias                     return size;
72793f1e401SEdgar E. Iglesias                 }
72893f1e401SEdgar E. Iglesias             } else {
72993f1e401SEdgar E. Iglesias                 int idx, bit;
73093f1e401SEdgar E. Iglesias 
73193f1e401SEdgar E. Iglesias                 /* Multicast.  */
73293f1e401SEdgar E. Iglesias                 if (!memcmp(buf, sa_ipmcast, 3)) {
73393f1e401SEdgar E. Iglesias                     return size;
73493f1e401SEdgar E. Iglesias                 }
73593f1e401SEdgar E. Iglesias 
73693f1e401SEdgar E. Iglesias                 idx  = (buf[4] & 0x7f) << 8;
73793f1e401SEdgar E. Iglesias                 idx |= buf[5];
73893f1e401SEdgar E. Iglesias 
73993f1e401SEdgar E. Iglesias                 bit = 1 << (idx & 0x1f);
74093f1e401SEdgar E. Iglesias                 idx >>= 5;
74193f1e401SEdgar E. Iglesias 
74293f1e401SEdgar E. Iglesias                 if (!(s->ext_mtable[idx] & bit)) {
74393f1e401SEdgar E. Iglesias                     return size;
74493f1e401SEdgar E. Iglesias                 }
74593f1e401SEdgar E. Iglesias             }
74693f1e401SEdgar E. Iglesias         }
74793f1e401SEdgar E. Iglesias     }
74893f1e401SEdgar E. Iglesias 
74993f1e401SEdgar E. Iglesias     if (size < 12) {
75093f1e401SEdgar E. Iglesias         s->regs[R_IS] |= IS_RX_REJECT;
75193f1e401SEdgar E. Iglesias         enet_update_irq(s);
75293f1e401SEdgar E. Iglesias         return -1;
75393f1e401SEdgar E. Iglesias     }
75493f1e401SEdgar E. Iglesias 
75593f1e401SEdgar E. Iglesias     if (size > (s->c_rxmem - 4)) {
75693f1e401SEdgar E. Iglesias         size = s->c_rxmem - 4;
75793f1e401SEdgar E. Iglesias     }
75893f1e401SEdgar E. Iglesias 
75993f1e401SEdgar E. Iglesias     memcpy(s->rxmem, buf, size);
76093f1e401SEdgar E. Iglesias     memset(s->rxmem + size, 0, 4); /* Clear the FCS.  */
76193f1e401SEdgar E. Iglesias 
76293f1e401SEdgar E. Iglesias     if (s->rcw[1] & RCW1_FCS) {
76393f1e401SEdgar E. Iglesias         size += 4; /* fcs is inband.  */
76493f1e401SEdgar E. Iglesias     }
76593f1e401SEdgar E. Iglesias 
76693f1e401SEdgar E. Iglesias     app[0] = 5 << 28;
76793f1e401SEdgar E. Iglesias     csum32 = net_checksum_add(size - 14, (uint8_t *)s->rxmem + 14);
76893f1e401SEdgar E. Iglesias     /* Fold it once.  */
76993f1e401SEdgar E. Iglesias     csum32 = (csum32 & 0xffff) + (csum32 >> 16);
77093f1e401SEdgar E. Iglesias     /* And twice to get rid of possible carries.  */
77193f1e401SEdgar E. Iglesias     csum16 = (csum32 & 0xffff) + (csum32 >> 16);
77293f1e401SEdgar E. Iglesias     app[3] = csum16;
77393f1e401SEdgar E. Iglesias     app[4] = size & 0xffff;
77493f1e401SEdgar E. Iglesias 
77593f1e401SEdgar E. Iglesias     s->stats.rx_bytes += size;
77693f1e401SEdgar E. Iglesias     s->stats.rx++;
77793f1e401SEdgar E. Iglesias     if (multicast) {
77893f1e401SEdgar E. Iglesias         s->stats.rx_mcast++;
77993f1e401SEdgar E. Iglesias         app[2] |= 1 | (ip_multicast << 1);
78093f1e401SEdgar E. Iglesias     } else if (broadcast) {
78193f1e401SEdgar E. Iglesias         s->stats.rx_bcast++;
78293f1e401SEdgar E. Iglesias         app[2] |= 1 << 3;
78393f1e401SEdgar E. Iglesias     }
78493f1e401SEdgar E. Iglesias 
78593f1e401SEdgar E. Iglesias     /* Good frame.  */
78693f1e401SEdgar E. Iglesias     app[2] |= 1 << 6;
78793f1e401SEdgar E. Iglesias 
788669b4983SPeter A. G. Crosthwaite     stream_push(s->tx_dev, (void *)s->rxmem, size, app);
78993f1e401SEdgar E. Iglesias 
79093f1e401SEdgar E. Iglesias     s->regs[R_IS] |= IS_RX_COMPLETE;
79193f1e401SEdgar E. Iglesias     enet_update_irq(s);
79293f1e401SEdgar E. Iglesias     return size;
79393f1e401SEdgar E. Iglesias }
79493f1e401SEdgar E. Iglesias 
7954e68f7a0SStefan Hajnoczi static void eth_cleanup(NetClientState *nc)
79693f1e401SEdgar E. Iglesias {
79793f1e401SEdgar E. Iglesias     /* FIXME.  */
798545129e5SPeter Crosthwaite     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
7997267c094SAnthony Liguori     g_free(s->rxmem);
8007267c094SAnthony Liguori     g_free(s);
80193f1e401SEdgar E. Iglesias }
80293f1e401SEdgar E. Iglesias 
80393f1e401SEdgar E. Iglesias static void
804669b4983SPeter A. G. Crosthwaite axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr)
80593f1e401SEdgar E. Iglesias {
806545129e5SPeter Crosthwaite     XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
80793f1e401SEdgar E. Iglesias 
80893f1e401SEdgar E. Iglesias     /* TX enable ?  */
80993f1e401SEdgar E. Iglesias     if (!(s->tc & TC_TX)) {
81093f1e401SEdgar E. Iglesias         return;
81193f1e401SEdgar E. Iglesias     }
81293f1e401SEdgar E. Iglesias 
81393f1e401SEdgar E. Iglesias     /* Jumbo or vlan sizes ?  */
81493f1e401SEdgar E. Iglesias     if (!(s->tc & TC_JUM)) {
81593f1e401SEdgar E. Iglesias         if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) {
81693f1e401SEdgar E. Iglesias             return;
81793f1e401SEdgar E. Iglesias         }
81893f1e401SEdgar E. Iglesias     }
81993f1e401SEdgar E. Iglesias 
82093f1e401SEdgar E. Iglesias     if (hdr[0] & 1) {
82193f1e401SEdgar E. Iglesias         unsigned int start_off = hdr[1] >> 16;
82293f1e401SEdgar E. Iglesias         unsigned int write_off = hdr[1] & 0xffff;
82393f1e401SEdgar E. Iglesias         uint32_t tmp_csum;
82493f1e401SEdgar E. Iglesias         uint16_t csum;
82593f1e401SEdgar E. Iglesias 
82693f1e401SEdgar E. Iglesias         tmp_csum = net_checksum_add(size - start_off,
82793f1e401SEdgar E. Iglesias                                     (uint8_t *)buf + start_off);
82893f1e401SEdgar E. Iglesias         /* Accumulate the seed.  */
82993f1e401SEdgar E. Iglesias         tmp_csum += hdr[2] & 0xffff;
83093f1e401SEdgar E. Iglesias 
83193f1e401SEdgar E. Iglesias         /* Fold the 32bit partial checksum.  */
83293f1e401SEdgar E. Iglesias         csum = net_checksum_finish(tmp_csum);
83393f1e401SEdgar E. Iglesias 
83493f1e401SEdgar E. Iglesias         /* Writeback.  */
83593f1e401SEdgar E. Iglesias         buf[write_off] = csum >> 8;
83693f1e401SEdgar E. Iglesias         buf[write_off + 1] = csum & 0xff;
83793f1e401SEdgar E. Iglesias     }
83893f1e401SEdgar E. Iglesias 
839b356f76dSJason Wang     qemu_send_packet(qemu_get_queue(s->nic), buf, size);
84093f1e401SEdgar E. Iglesias 
84193f1e401SEdgar E. Iglesias     s->stats.tx_bytes += size;
84293f1e401SEdgar E. Iglesias     s->regs[R_IS] |= IS_TX_COMPLETE;
84393f1e401SEdgar E. Iglesias     enet_update_irq(s);
84493f1e401SEdgar E. Iglesias }
84593f1e401SEdgar E. Iglesias 
84693f1e401SEdgar E. Iglesias static NetClientInfo net_xilinx_enet_info = {
8472be64a68SLaszlo Ersek     .type = NET_CLIENT_OPTIONS_KIND_NIC,
84893f1e401SEdgar E. Iglesias     .size = sizeof(NICState),
84993f1e401SEdgar E. Iglesias     .can_receive = eth_can_rx,
85093f1e401SEdgar E. Iglesias     .receive = eth_rx,
85193f1e401SEdgar E. Iglesias     .cleanup = eth_cleanup,
85293f1e401SEdgar E. Iglesias };
85393f1e401SEdgar E. Iglesias 
85493f1e401SEdgar E. Iglesias static int xilinx_enet_init(SysBusDevice *dev)
85593f1e401SEdgar E. Iglesias {
856*f0e7a81cSPeter Crosthwaite     XilinxAXIEnet *s = XILINX_AXI_ENET(dev);
85793f1e401SEdgar E. Iglesias 
85893f1e401SEdgar E. Iglesias     sysbus_init_irq(dev, &s->irq);
85993f1e401SEdgar E. Iglesias 
8600dc31f3bSAvi Kivity     memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000);
861750ecd44SAvi Kivity     sysbus_init_mmio(dev, &s->iomem);
86293f1e401SEdgar E. Iglesias 
86393f1e401SEdgar E. Iglesias     qemu_macaddr_default_if_unset(&s->conf.macaddr);
86493f1e401SEdgar E. Iglesias     s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
865f79f2bfcSAnthony Liguori                           object_get_typename(OBJECT(dev)), dev->qdev.id, s);
866b356f76dSJason Wang     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
86793f1e401SEdgar E. Iglesias 
86893f1e401SEdgar E. Iglesias     tdk_init(&s->TEMAC.phy);
86993f1e401SEdgar E. Iglesias     mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr);
87093f1e401SEdgar E. Iglesias 
87193f1e401SEdgar E. Iglesias     s->TEMAC.parent = s;
87293f1e401SEdgar E. Iglesias 
8737267c094SAnthony Liguori     s->rxmem = g_malloc(s->c_rxmem);
87493f1e401SEdgar E. Iglesias     axienet_reset(s);
87593f1e401SEdgar E. Iglesias 
87693f1e401SEdgar E. Iglesias     return 0;
87793f1e401SEdgar E. Iglesias }
87893f1e401SEdgar E. Iglesias 
879669b4983SPeter A. G. Crosthwaite static void xilinx_enet_initfn(Object *obj)
880669b4983SPeter A. G. Crosthwaite {
881*f0e7a81cSPeter Crosthwaite     XilinxAXIEnet *s = XILINX_AXI_ENET(obj);
882b15aaca4SPeter Crosthwaite     Error *errp = NULL;
883669b4983SPeter A. G. Crosthwaite 
884669b4983SPeter A. G. Crosthwaite     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
885b15aaca4SPeter Crosthwaite                              (Object **) &s->tx_dev, &errp);
886b15aaca4SPeter Crosthwaite     assert_no_error(errp);
887669b4983SPeter A. G. Crosthwaite }
888669b4983SPeter A. G. Crosthwaite 
889999e12bbSAnthony Liguori static Property xilinx_enet_properties[] = {
890545129e5SPeter Crosthwaite     DEFINE_PROP_UINT32("phyaddr", XilinxAXIEnet, c_phyaddr, 7),
891545129e5SPeter Crosthwaite     DEFINE_PROP_UINT32("rxmem", XilinxAXIEnet, c_rxmem, 0x1000),
892545129e5SPeter Crosthwaite     DEFINE_PROP_UINT32("txmem", XilinxAXIEnet, c_txmem, 0x1000),
893545129e5SPeter Crosthwaite     DEFINE_NIC_PROPERTIES(XilinxAXIEnet, conf),
89493f1e401SEdgar E. Iglesias     DEFINE_PROP_END_OF_LIST(),
895999e12bbSAnthony Liguori };
896999e12bbSAnthony Liguori 
897999e12bbSAnthony Liguori static void xilinx_enet_class_init(ObjectClass *klass, void *data)
898999e12bbSAnthony Liguori {
89939bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
900999e12bbSAnthony Liguori     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
901669b4983SPeter A. G. Crosthwaite     StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
902999e12bbSAnthony Liguori 
903999e12bbSAnthony Liguori     k->init = xilinx_enet_init;
90439bffca2SAnthony Liguori     dc->props = xilinx_enet_properties;
905669b4983SPeter A. G. Crosthwaite     ssc->push = axienet_stream_push;
90693f1e401SEdgar E. Iglesias }
907999e12bbSAnthony Liguori 
9088c43a6f0SAndreas Färber static const TypeInfo xilinx_enet_info = {
909*f0e7a81cSPeter Crosthwaite     .name          = TYPE_XILINX_AXI_ENET,
91039bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
911545129e5SPeter Crosthwaite     .instance_size = sizeof(XilinxAXIEnet),
912999e12bbSAnthony Liguori     .class_init    = xilinx_enet_class_init,
913669b4983SPeter A. G. Crosthwaite     .instance_init = xilinx_enet_initfn,
914669b4983SPeter A. G. Crosthwaite     .interfaces = (InterfaceInfo[]) {
915669b4983SPeter A. G. Crosthwaite             { TYPE_STREAM_SLAVE },
916669b4983SPeter A. G. Crosthwaite             { }
917669b4983SPeter A. G. Crosthwaite     }
91893f1e401SEdgar E. Iglesias };
91983f7d43aSAndreas Färber 
92083f7d43aSAndreas Färber static void xilinx_enet_register_types(void)
92193f1e401SEdgar E. Iglesias {
92239bffca2SAnthony Liguori     type_register_static(&xilinx_enet_info);
92393f1e401SEdgar E. Iglesias }
92493f1e401SEdgar E. Iglesias 
92583f7d43aSAndreas Färber type_init(xilinx_enet_register_types)
926